본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 29. 02:39

Gemini를 사용하여 100개 이상의 USCIS 행정 항소국(AAO) 결정 사례를 바탕으로 RAG 시스템을 구축한 방법

요약

Gemini를 활용하여 107개의 USCIS 행정 항소국(AAO) 결정 사례를 기반으로 한 법률 특화 RAG 시스템 구축 과정을 다룹니다. 데이터의 선택 편향 문제를 해결하기 위해 승인 확률 대신 강도 지표를 사용하고, 하이브리드 검색과 카테고리 격리를 통해 정확도를 높이는 설계 전략을 설명합니다.

핵심 포인트

  • 실제 AAO 판결 데이터를 활용한 고품질 RAG 파이프라인 구축
  • 거절 사례 중심의 코퍼스 편향을 인지하고 설계에 반영
  • 승인 확률 대신 구체적 근거와 강도 지표를 제공하는 방식 채택
  • 하이브리드 검색과 하드 필터를 통한 교차 오염 방지

EB-1A 청원(petition)에 대한 USCIS 거절률이 1년 만에 25.6%에서 46.6%로 거의 두 배 가까이 증가했습니다. NIW 거절률은 64.3%에 달했습니다. 이민 변호사들은 대부분의 신청자가 감당하기 어려운 5,000달러에서 15,000달러 사이의 사례 준비 비용을 청구합니다.

저는 USCIS가 실제로 검토하는 방식과 동일하게 비자 청원을 분석하는 이민 사례 준비 플랫폼인 PetitionIQ를 구축하고 있습니다. 이 플랫폼의 핵심은 107개의 실제 USCIS 행정 항소국 (AAO, Administrative Appeals Office) 비선례 결정(non-precedent decisions)에 대한 RAG (Retrieval-Augmented Generation, 검색 증강 생성) 파이프라인입니다. 이는 일반적인 법률 지식이나 LLM (Large Language Model, 거대 언어 모델) 학습 데이터가 아니라, 완전한 출처(provenance)를 가진 실제 판결 결과입니다.

이 포스트에서는 RAG 시스템의 모든 설계 결정 과정을 살펴봅. 왜 코퍼스(corpus, 말뭉치)에 편향이 있는지와 이를 어떻게 처리하는지, 왜 카테고리 격리(category isolation)가 생각보다 중요한지, 그리고 하드 필터(hard filters)를 활용한 하이브리드 검색(hybrid retrieval)이 법률 AI를 위험하게 만드는 교차 오염(cross-contamination)을 어떻게 방지하는지에 대해 다룹니다.

왜 AAO 결정인가?

행정 항소국(AAO)은 uscis.gov에 비선례 결정(non-precedent decisions)을 게시합니다. 이것들은 실제 판결 결과입니다. 즉, 누군가가 I-140 청원을 제출했다가 거절당한 후 항소한 사례들입니다. AAO는 항소를 인용(sustained, 거절을 뒤집음)하거나, 기각(dismissed, 거절을 유지)하거나, 또는 환송(remanded, 추가 검토를 위해 다시 보냄)합니다.

이 코퍼스는 USCIS가 각 기준에 대해 증거를 어떻게 평가하는지를 정확히 보여주기 때문에 매우 가치 있습니다. 추상적으로 법이 무엇을 말하는지가 아니라, 심사관들이 실제 사례에 법을 어떻게 적용하는지를 보여줍니다. AAO가 "신청자의 해당 분야 학술지 3편 게재는 칭찬할 만하지만, 수혜자의 업무가 주요한 의미를 갖는 독창적인 기여를 구성한다는 점을 입증하지는 못한다"라고 작성할 때, 이는 그 어떤 LLM 학습으로도 포착할 수 없는 데이터 포인트입니다.

하지만 이 코퍼스에는 근본적인 문제가 있습니다.

코퍼스 편향 문제

AAO 결정은 거절에 대한 항소 결과입니다. 깨끗한 승인(approvals) 사례는 이 데이터셋에 절대 나타나지 않습니다. 만약 누군가가 EB-1A 청원을 제출하여 승인을 받았다면, 이에 대한 AAO 기록은 존재하지 않습니다.

이는 말하자면 코퍼스(Corpus)가 거절 사례에 편향된 선택 편향(Selection bias)을 가지고 있음을 의미합니다. 만약 제가 이 데이터를 바탕으로 단순하게 학습하는 시스템을 구축했다면, 승인된 사례는 전혀 보지 못하고 거절된 사례만 보게 되므로 거의 아무것도 승인되지 않는다는 결론을 내렸을 것입니다.

설계 결정: PetitionIQ는 승인 확률을 절대 출력하지 않습니다.

"귀하의 승인 확률은 73%입니다"라거나 "유사한 사례를 바탕으로 볼 때, 귀하의 가능성은 높습니다"와 같은 표현은 사용하지 않습니다. 대신 시스템은 강도 지표(Strong, Moderate, Weak)를 사용하며, 증거가 특정 기준을 충족하거나 충족하지 못하는 이유를 설명하기 위해 구체적인 AAO 결정 사례를 인용합니다. 모든 응답에는 AAO 코퍼스가 거절에 대한 항소 사례만을 포함하고 있음을 설명하는 코퍼스 편향 공개(Corpus bias disclosure) 문구가 포함됩니다.

이는 제가 숨기려는 한계가 아닙니다. 오히려 강조하고자 하는 설계상의 제약 사항입니다. 편향된 데이터를 다룰 때 정직한 방법은 편향을 거짓된 확신으로 덮어버리는 것이 아니라, 그 편향에 대해 투명하게 공개하는 것입니다.

크롤링 파이프라인 (The crawl pipeline)

AAO는 uscis.gov에 결정 사례를 카테고리와 연도별로 정리하여 PDF 형식으로 게시합니다. 크롤러(Crawler)는 다음과 같이 작동하는, 속도 제한(Rate-limited)이 적용된 정중한 스크래퍼(Scraper)입니다:

  1. USCIS 웹사이트의 디렉토리 리스팅을 통해 PDF를 발견합니다.
  2. 디렉토리 리스팅을 사용할 수 없는 경우 후보 URL 탐색(Candidate URL probing) 방식으로 전환합니다 (AAO 파일명은 JAN162026_01B2203.pdf와 같이 예측 가능한 패턴을 따릅니다).
  3. 요청 사이에 2초의 속도 제한을 두어 각 PDF를 다운로드합니다.
  4. pdfplumber를 사용하여 텍스트를 추출합니다.
  5. 멱등성(Idempotent)을 유지하는 매니페스트(Manifest)를 관리하여 재실행 시 중복 다운로드를 방지합니다.

현재 코퍼스: 4개의 비자 카테고리에 걸친 107개의 결정 사례 (EB-1A: 44개, EB-2 NIW: 54개, EB-1B: 4개, O-1A: 5개), 총 단어 수는 262,778개입니다.

# 정중한 속도 제한 (Polite rate limiting)
class RateLimiter:
    def __init__(self, min_interval=2.0):
...

Gemini 구조화된 추출 (Gemini structured extraction)

가공되지 않은 AAO 결정 텍스트는 매우 무질서합니다. 심사관마다 작성 방식이 다르고, 형식이 다양하며, 동일한 기준이 결정문의 여러 섹션에 걸쳐 논의될 수 있습니다. 저는 각 결정 사례에서 구조화된 데이터(Structured data)를 추출하기 위해 Gemini 2.5 Flash를 사용합니다:

  • Category (EB-1A, EB-1B, EB-2 NIW, O-1A)
  • Outcome (sustained, dismissed, remanded)
  • Criteria findings - 어떤 기준(criteria)이 주장되었고, 어떤 기준이 충족되었으며, 각 기준에 대한 AAO의 논거(reasoning)는 무엇인지
  • Field of endeavor - 청원인(petitioner)이 종사한 분야
  • Confidence score - 추출 결과에 대한 신뢰도

추출 과정에서는 엄격한 Pydantic 스키마(schema)를 사용하는 JSON 응답 모드(JSON response mode)를 활용합니다. 유효성 검사(validation)에 실패한 결정 사례(주로 Gemini가 필수 불리언(boolean) 필드에 대해 null을 반환한 경우)는 잘못된 데이터로 포함하는 대신 격리(quarantined) 처리합니다.

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=prompt,
...

총 107개의 결정 사례 중 93개는 성공적으로 추출되었고 14개는 격리되었습니다. 격리된 결정 사례들은 주로 Dhanasar prong 분석이 스키마에 깔끔하게 매핑되지 않은 NIW 사례들이었습니다. 저는 잘못된 추출 데이터를 포함하느니 차라리 코퍼스(corpus)의 13%를 잃는 쪽을 택하겠습니다.

카테고리 격리(Category isolation)가 중요한 이유

이것은 대부분의 법률 AI 도구들이 가장 잘못하고 있는 설계 결정(design decision)입니다.

O-1A(예술/과학/비즈니스 분야의 탁월한 능력)와 EB-1A(영주권을 위한 탁월한 능력)는 거의 동일한 기준(criteria) 텍스트를 공유합니다. 두 경우 모두 "수상(awards)", "출판물(published material)", "독창적 기여(original contributions)" 등을 언급합니다. 하지만 이들은 서로 다른 법적 기준(legal standards)을 적용합니다. O-1A는 "차별성(distinction)" 기준을 사용하는 반면, EB-1A는 더 높은 수준인 "지속적인 국가적 또는 국제적 명성(sustained national or international acclaim)" 기준을 사용합니다. O-1A를 충족하는 동일한 증거라도 EB-1A를 충족하지 못할 수 있습니다.

만약 사용자가 O-1A에 대해 질문했을 때 검색 시스템(retrieval system)이 EB-1A의 논거를 반환한다면, 텍스트가 관련 있어 보일지라도 그 분석은 틀린 것입니다. 기준의 명칭도 일치하고 증거 유형도 일치하지만, 법적 기준이 다르기 때문입니다.

설계 결정: 카테고리는 소프트 시그널(soft signal)이 아니라 하드 필터(hard filter)입니다.

사용자가 O-1A에 대한 분석을 요청하면, 검색 시스템은 O-1A로 태그된 청크(chunk)만을 반환합니다. 의미론적 유사성(semantic similarity)에 관계없이 EB-1A 청크는 단 하나도 유출되지 않습니다.

def retrieve(query, category, top_k=10, store=None):
    # 하드 필터(Hard filter): 요청된 카테고리와 일치하는 청크(chunk)만 반환
    category_chunks = [
...

저는 카테고리 간 유출(cross-category leakage)을 구체적으로 점검하는 평가 테스트(eval test)를 작성했습니다. 이 테스트는 O-1A 기준을 쿼리하여 결과에 EB-1A 청크가 단 하나도 나타나지 않는지 확인합니다. 이 테스트는 모든 빌드(build) 시마다 실행됩니다.

[PASS] category_leakage - 카테고리 간 오염 없음

토큰 윈도우가 아닌 기준(criterion)별 청킹

대부분의 RAG 튜토리얼은 고정된 토큰 윈도우(token window)를 기준으로 청킹합니다. 예를 들어, 100 토큰의 오버랩(overlap)을 가진 500 토큰 단위로 나누는 방식입니다. 이는 법률 문서에는 맞지 않는 방식입니다.

AAO 결정문은 기준(criteria)을 중심으로 구조화되어 있습니다. 심사관은 한 섹션에서는 "수상 경력(Awards)" 기준을 평가하고, 다른 섹션에서는 "독창적 기여(Original Contributions)" 기준을 평가합니다. 기준 분석의 중간에서 청크를 잘라버리면 추론 단위(reasoning unit)가 깨지게 됩니다.

PetitionIQ는 기준 섹션별로 청킹합니다. 각 청크는 하나의 결정문에서 하나의 기준에 대한 완전한 법적 추론 단위를 나타냅니다. 각 청크는 다음과 같은 전체 메타데이터(metadata)를 포함합니다.

@dataclass
class Chunk:
    id: str              # 고유 청크 ID
...

현재 인덱스에는 361개의 청크(사례 청크 327개 + 규정 텍스트의 권위 청크 34개)가 포함되어 있습니다.

하이브리드 검색(Hybrid retrieval): 코사인 유사도 + TF-IDF + RRF

순수 의미론적 검색(semantic search)은 중요한 법률 용어를 놓칠 수 있습니다. 사용자가 "Kazarian 2단계 분석(Kazarian two-step analysis)"에 대해 질문할 때, 의미론적 유사성(semantic similarity)만 따지면 "평가 프레임워크(evaluation framework)"에 관한 청크가 Kazarian을 직접 언급하는 청크보다 더 높게 순위가 매겨질 수 있습니다. 반대로 순수 키워드 검색은 의미론적 의미를 놓칩니다. "연구가 분야에 미치는 영향(impact of research on the field)"에 대한 질문은 정확한 단어가 일치하지 않더라도 "중대한 의미를 갖는 독창적 기여(original contributions of major significance)"에 관한 청크와 매칭되어야 합니다.

PetitionIQ는 하이브리드 검색(hybrid retrieval)을 사용합니다:

  1. 코사인 유사도 (Cosine similarity): 의미론적 매칭을 위해 gemini-embedding-001 임베딩(3072 차원)을 사용합니다.
  2. TF-IDF: 용어 가중치(term weighting)를 적용한 키워드 매칭을 위해 사용합니다.
  3. 상호 순위 결합 (Reciprocal Rank Fusion, RRF): 두 개의 순위 리스트를 하나의 결과로 결합하기 위해 사용합니다.
def reciprocal_rank_fusion(ranked_lists, k=60):
    scores = {}
    for ranked_list in ranked_lists:
...

RRF는 단순하며 효과적입니다. 의미론적 점수 (semantic scores)와 키워드 점수 (keyword scores) 사이의 가중치를 조정할 필요가 없으며, 두 방식 간의 점수 분포 차이에도 견고합니다.

권위 코퍼스 (The authority corpus)

사례 청크 (case chunks) 외에도, 검색 시스템에는 권위 코퍼스 (authority corpus)가 포함됩니다: 규정 텍스트 34개 청크, USCIS 정책 매뉴얼 (Policy Manual) 발췌본, 그리고 주요 선례 결정 요약본 (Kazarian v. USCIS, Dhanasar, Chawathe)입니다. 이들은 사례 청크가 해석되는 기준이 되는 법적 프레임워크를 제공합니다.

권위 청크 (Authority chunks)는 항상 사례 청크와 함께 검색 결과에 포함됩니다. 생성기 (generator)는 근거 있는 분석을 생성하기 위해 두 가지를 모두 사용합니다: "[authority]에 따른 Kazarian 2단계 프레임워크에 따라, [decision_id]의 AAO는 ...라고 판단했습니다."

인용을 포함한 생성 (Generation with citations)

생성된 분석의 모든 주장은 특정 출처를 인용합니다. 단순히 "AAO 선례에 근거하여"라고 하는 대신, uscis.gov의 원본 PDF로 연결되는 클릭 가능한 링크와 함께 "[AAO-JAN162026_01B2203]"와 같이 표기합니다.

생성 프롬프트 (generation prompt)는 이 사항에 대해 엄격합니다:

  • 모든 사실적 주장은 검색된 청크를 참조해야 함
  • 승인 확률 (approval probabilities) 언급 금지
  • 모든 응답에 코퍼스 편향 (corpus bias) 공개
  • 결론을 내리기에 증거가 불충분한 경우, 이를 명시할 것

평가 스위트 (The eval suite)

모든 빌드 시 네 가지 테스트가 실행됩니다:

  1. category_leakage - O-1A 쿼리를 실행하여 결과에 EB-1A 청크가 포함되지 않았는지 확인
  2. probability_leak - 응답을 생성하여 승인 확률 관련 표현이 나타나지 않는지 확인
  3. probability_pattern_validation - 패턴 탐지기가 확률 관련 표현이 존재할 때 이를 제대로 잡아내는지 테스트
  4. retrieval_recall - 알려진 쿼리에 대해 관련 청크가 실제로 검색되는지 확인
[PASS] category_leakage     - 카테고리 간 교차 오염 없음
[PASS] probability_leak     - 승인 확률 관련 표현 미감지
[PASS] probability_patterns - 금지된 패턴을 정확히 포착함
...

현재 361개 청크 인덱스에서 네 가지 테스트 모두 통과했습니다.

이것이 가능하게 하는 것

이 RAG (Retrieval-Augmented Generation) 시스템은 PetitionIQ의 심층 분석 (deep analysis) 기능을 구동합니다. 사용자가 자신의 비자 카테고리에 대해 심층 분석을 실행하면, 시스템은 다음과 같이 작동합니다:

  1. gemini-embedding-001을 사용하여 쿼리를 임베딩 (Embed) 합니다.
  2. 하이브리드 검색 (hybrid search)을 통해 상위 청크 (chunks)를 검색합니다 (사용자의 카테고리로 하드 필터링 적용).
  3. 검색된 청크와 권위 있는 코퍼스 (authority corpus)를 Gemini 2.5 Flash에 전달합니다.
  4. AAO 결정 인용을 포함하여 기준별 분석을 생성합니다.
  5. 코퍼스 편향 (corpus bias) 공시를 포함합니다.

전체 파이프라인은 Google Cloud에서 실행됩니다: Gemini 호출 및 임베딩을 위한 Vertex AI, FastAPI 백엔드를 위한 Cloud Run, 데이터 지속성을 위한 Firestore를 사용합니다.

내가 배운 점

편향 완화보다 편향 투명성이 더 중요합니다. AAO 코퍼스의 선택 편향 (selection bias)을 "교정"하려고 시간을 보냈지만, 결국 가장 정직한 방법은 사용자에게 이를 그대로 알리는 것이라는 점을 깨달았습니다. 모든 응답에는 "이 분석은 AAO 항소 결정에 기반하며, 이는 거절되어 항소된 사례만을 포함합니다. 승인 패턴은 포함되어 있지 않습니다."라는 문구가 포함됩니다.

안전이 중요한 검색에서는 소프트 시그널 (soft signals)보다 하드 필터 (hard filters)가 더 효과적입니다. 법률 분석에서 잘못된 카테고리의 논거를 반환하는 것은 단순히 "관련성이 낮은" 결과가 아니라, 적극적으로 오도하는 결과입니다. 평가 테스트 (eval tests)를 동반한 하드 카테고리 필터링만이 제가 신뢰할 수 있는 유일한 접근 방식입니다.

토큰 수(token count)가 아니라 추론 단위(reasoning unit)별로 청킹하세요. 법률적 추론에는 자연스러운 경계가 있습니다. 이를 존중해야 합니다.

평가 스위트 (eval suite)부터 시작하세요. 저는 검색 시스템을 구축하기 전에 네 가지 평가 테스트를 먼저 작성했습니다. 이 테스트들이 시스템이 충족해야 할 계약 (contract)을 정의했습니다. 모든 설계 결정은 이 테스트들을 통해 검증되었습니다.

PetitionIQ는 petitioniq.io에서 확인할 수 있습니다. 5개 카테고리에 대해 무료 다중 비자 분석을 제공합니다. RAG 기반 심층 분석, 제출 전 일관성 감사 (consistency audit), 문서 생성, 그리고 RFE (Request for Evidence) 대응 모듈은 유료 플랜에서 사용할 수 있습니다.

이 프로젝트는 Build with Gemini XPRIZE를 위해 Gemini 2.5 Flash + gemini-embedding-001 + Google Cloud Run + Firestore를 기반으로 완전히 구축되었습니다.

전체 코드베이스는 github.com/4KInc/petitioniq에서 확인할 수 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0