Skip to main content

Command Palette

Search for a command to run...

법률 AI 검색 실험기 (10) — Query Prep 마무리: 무엇을 남기고 무엇을 버렸나

Published
5 min read

전처리 파이프라인을 "마무리"한다는 것

RAG 파이프라인에서 전처리(query preparation)는 사용자 질문과 검색 엔진 사이의 번역 계층이다. 질문을 그대로 벡터 검색에 넣는 것과, 질문을 구조화하고 어떤 소스를 열지 먼저 정하는 것은 검색 품질에서 체감할 수 있는 차이를 만든다.

이 프로젝트에서는 법률 QA를 다루고 있고, 검색 대상이 조문, 판례, 유권해석, 행정심판 등 8개 소스 레인에 걸쳐 있다. 그만큼 전처리 단계가 감당해야 할 범위가 넓었다. 처음에는 세 축을 세웠다.

  • prerewriter: 질문을 구조화하고 검색 힌트를 생성
  • source-router: 어떤 소스 레인을 활성화할지 결정
  • filter: payload 기준으로 검색 범위를 추가로 좁힘

세 축 모두를 운영 가능한 상태로 올리는 것이 원래 목표였다. 하지만 결론적으로 두 축만 남기고 하나는 보류했다. 이 글은 그 결정의 과정과 근거, 그리고 "마무리"라는 것이 실제로 무엇을 의미하는지를 정리한다.


최종 운영 구조: prerewriter + source-router

현재 query-prep의 운영 구조는 다음과 같다.

질문
-> prerewriter
-> source-router
-> query-prep handoff
-> retrieval
-> merge / rerank
-> answer

prerewriter는 raw 질문을 대체하지 않으면서, retrieval에 필요한 구조화된 힌트를 만든다. queryType, 벡터 검색용 searchQuery와 subQueries, 키워드, 그래프 검색용 법률명 등이 여기서 나온다. 원래 질문을 버리지 않는다는 점이 중요한데, 전처리가 잘못될 경우에도 원본이 살아 있으므로 fallback이 가능하다.

source-router는 8개 소스 레인 중 어떤 것을 활성화할지 정한다. recall-priority 기준까지 반영한 실험을 거쳐 v1_6이 최종 선택됐다. 예를 들어 "국가공무원법상 징계 관련 판례"라는 질문이 들어오면, 조문 레인과 판례 레인을 동시에 열되 해석례나 위원회 결정은 열지 않는 식의 판단을 한다.

이 두 축의 조합만으로도 검색 단계에 넘길 계획(retrieval plan)은 충분히 만들어진다. 중요한 것은 downstream이 prerewriter와 router의 내부 구현을 알 필요가 없다는 점이다. 다음 단계는 buildQueryPrepHandoff라는 하나의 진입점만 보면 된다.


Filter를 보류한 이유

세 번째 축인 filter는 보류했다. "아직 안 만들었다"가 아니라 "만들 수 있지만 지금은 빼는 것이 낫다"는 판단이었다. 이유는 크게 세 가지다.

1. 실제 payload와 설계가 맞지 않았다

filter의 원래 역할은 Qdrant payload filter로 이어지는 것이었다. 처음 설계할 때는 lawNames, regions, institutions 같은 공통 필드를 모든 소스에 걸쳐 사용할 수 있으리라 가정했다.

하지만 실제 payload를 까보니 현실은 달랐다.

  • 조문(law_articles)은 lawName 필드가 있어 자연스럽게 filter 가능
  • 조례(ordinance_articles)는 지역이 중요하지만 payload에 region 필드 자체가 없음
  • 판례(precedents)는 courtName, caseType, referenceLaws가 더 자연스러운 filter 후보
  • 유권해석, 행정심판, 헌재결정 등은 titlesummary 정도만 있어 filter를 걸 근거가 부족

8개 레인에 공통 스키마를 씌우려 했지만, 실제로는 소스마다 쓸 수 있는 필드가 완전히 다른 상황이었다. 추상 스키마를 먼저 만들고 payload를 나중에 보는 순서가 거꾸로였던 셈이다.

2. 잘못된 hard filter는 recall을 직접 떨어뜨린다

법률 검색에서 filter는 양날의 검이다. 정확한 filter는 noise를 줄여주지만, 잘못된 hard filter는 정답 문서를 아예 검색 결과에서 빼버린다. 특히 판례, 유권해석, 행정심판처럼 메타데이터가 빈약한 소스에서 hard filter를 거는 것은 retrieval miss를 직접 만드는 행위다.

2025년 이후의 RAG 파이프라인 설계에서도 이 점은 공통적으로 언급된다. query expansion이나 다중 paraphrase를 통해 검색 범위를 넓히는 접근이 일반적인 추세이고, filter로 범위를 좁히는 것은 충분한 recall이 확보된 이후에 정밀하게 적용하는 것이 권장된다. 잘못된 전처리가 RAG 실패의 주요 원인이라는 분석도 여러 연구에서 반복적으로 나온다.

3. router가 이미 1차 필터 역할을 하고 있다

source-router가 레인을 선택하는 것 자체가 넓은 의미의 filtering이다. 8개 레인 전부를 여는 것이 아니라, 질문에 맞는 레인만 활성화하므로 불필요한 소스에서의 noise는 이미 상당 부분 걸러진다. 여기에 payload filter까지 추가하면 이득보다 위험이 더 크다고 봤다.

결론적으로, filter를 보류한 것은 "filter가 불필요하다"는 판단이 아니라 "지금 상태에서 안전하게 붙일 수 있는 기반이 갖춰지지 않았다"는 판단이다. 나중에 다시 시작한다면 law_articleslawName filter부터, 레인별로 하나씩 시작하는 것이 맞다.


인계 기준 설계: 모듈형 handoff

query-prep을 마무리하면서 가장 신경 쓴 부분은 downstream과의 경계를 어떻게 그을 것인가였다. 문서로만 "이 단계는 끝났다"고 적어두면, 다음 단계에서 결국 내부 구조를 다시 들여다보게 된다.

그래서 buildQueryPrepHandoff라는 함수를 실제 진입점으로 만들었다. 이 함수는 내부적으로 prerewriter와 source-router를 실행하고, 그 결과를 하나의 handoff 객체로 합쳐서 반환한다.

downstream이 받는 계약은 이것이다.

  • queryType: 질문 유형
  • vector.searchQuery, vector.subQueries, vector.keywords: 벡터 검색 힌트
  • graph.keywords, graph.lawNames: 그래프 검색 힌트
  • sourceHints: 활성화할 소스 레인 목록
  • confidence: 전처리 신뢰도

filterPlan은 계약에 optional로 존재하지만 기본적으로 포함하지 않는다.

이 구조의 핵심은 모듈 merge가 아니라 출력 계약 merge라는 점이다. prerewriter와 router는 내부적으로 여전히 분리되어 있고, 각각의 버전을 독립적으로 교체할 수 있다. 하지만 downstream은 그 내부 구조를 알 필요 없이 handoff 하나만 보면 된다.


"완료"의 기준은 무엇인가

소프트웨어에서 "완료"라는 단어는 항상 조심스럽다. 특히 실험 기반 프로젝트에서는 더 그렇다. 여기서 query-prep의 "완료"는 다음을 의미한다.

완료인 것:

  • prerewriter와 source-router의 운영 버전 선정
  • downstream에 넘길 출력 계약 고정
  • handoff 모듈의 코드 구현
  • filter 보류 결정과 그 근거 문서화

완료가 아닌 것:

  • filter를 영구적으로 폐기한 것
  • prerewriter나 router를 다시는 건드리지 않겠다는 것
  • 전처리 파이프라인 전체의 최적화가 끝난 것

즉, "이 단계에서 더 실험하는 것보다 다음 단계로 넘어가는 것이 전체 시스템에 더 이롭다"는 판단이 완료의 기준이었다. query-prep 내부를 계속 다듬는 것보다, retrieval과 answer까지의 end-to-end 흐름을 먼저 검증하는 편이 병목을 더 빠르게 찾을 수 있다.


교훈: 완벽보다 운영 가능한 구조

이 과정에서 몇 가지 배운 것을 정리한다.

첫째, payload를 먼저 보고 설계해야 한다. filter 설계를 추상 스키마에서 시작한 것이 가장 큰 실수였다. "어떤 필드를 추출할지"보다 "실제로 어떤 필드가 존재하고 filter로 쓸 수 있는지"를 먼저 봤어야 했다. 설계가 코드보다 앞서가면, 나중에 코드가 설계를 못 따라간다.

둘째, 빼는 것도 결정이다. filter를 보류한 것은 소극적인 선택처럼 보일 수 있다. 하지만 실제로는 "recall risk를 감수하면서 불완전한 filter를 유지하는 것"과 "filter 없이 넓게 회수하는 것" 사이의 능동적 선택이었다. 특히 법률 도메인에서 검색 누락은 답변 품질에 치명적이므로, recall 확보가 precision보다 우선이라는 판단에는 지금도 변함이 없다.

셋째, 인계 가능한 상태가 완료의 진짜 기준이다. 문서만 있고 코드가 없으면 다음 단계에서 다시 내부를 파야 한다. 반대로 코드만 있고 계약이 명확하지 않으면 통합할 때 혼란이 생긴다. handoff 모듈과 출력 계약 문서를 함께 만든 것이 이번 마무리에서 가장 유용한 작업이었다.

넷째, 실험 프로젝트에서의 "완료"는 snapshot이다. 지금 시점에서 가장 합리적인 고정점을 찍은 것이지, 영구적인 결론을 내린 것이 아니다. filter는 source별 payload enrichment가 진행되면 다시 열릴 것이고, prerewriter나 router도 downstream 검증 결과에 따라 조정될 수 있다.


다음은 어디로

query-prep을 마무리한 이상, 다음은 이 결과를 실제로 쓰는 쪽이다. retrieval에 handoff를 연결하고, merge와 rerank를 거쳐 answer까지 이어지는 end-to-end 흐름을 검증해야 한다.

전처리를 오래 다듬는 것보다, 전체 파이프라인을 한 번이라도 끝까지 돌려보는 것이 지금 시점에서는 더 가치 있다. 부분 최적화에 매몰되면 전체 시스템의 병목을 놓치기 쉽다.

결국 query-prep은 "질문 구조화 + source lane 결정"이라는 역할로 정리됐다. 크지 않은 역할처럼 보일 수 있지만, 이 두 가지가 안정적으로 동작한다는 확신이 있어야 그 뒤의 모든 단계가 의미를 갖는다. 기초가 흔들리면 그 위에 무엇을 쌓아도 불안하다.


Sources:

More from this blog

내 문서를 읽는 작은 에이전트를 다시 만들며

내 사이트 dongjun.win에 붙어 있던 작은 AI 어시스턴트를 최근에 다시 손봤다. 방문자가 AI 어시스턴트 페이지에서 질문을 던지면, 내 이력서와 프로젝트 문서, 강점 진단, 리더십 리포트, 버크만(Birkman) 리포트를 바탕으로 답하는 기능이다. 겉으로는 단순하다. "최근 프로젝트는?", "어떤 기술을 쓰나요?", "일하는 방식은 어떤가요?" 같

May 27, 202610 min read

내 실생활에 AI 더하기 (1) — 사진, 영상 하이라이트 만들기

폰 사진 앱을 켜다가 여행 영상 폴더 앞에서 매번 멈춘다. 문제는 "안 본다"가 아니라 "안 보게 된다"였다. 분명히 좋아서 찍었는데, 시간이 지나니 불필요한 컷이 너무 많아서 다시 들어가기가 부담스러운 폴더가 된다. 핵심 장면만 추린 2~3분짜리 메모리 필름이 있다면 한 번에 그 시간을 다시 만날 수 있을 것 같았다. 업무에서는 AI를 매일 많이 쓴다.

May 13, 202611 min read3

법률 AI 검색 실험기 (12) — Lane-based Retrieval 설계와 전체 회고

법률 QA 검색기를 만들면서 거쳐 온 설계 여정의 마지막 이야기다. 벡터 검색의 한계를 마주한 순간부터, 임베딩 선택, selector, rewriter, graph, source-router, 그리고 lane-based retrieval까지. 이 글에서는 최종 단계인 lane 구조 설계를 정리하고, 시리즈 전체를 돌아본다. 검색기 운영 설계의 최종 단계 query-prep 단계를 마무리하면서 자연스럽게 다음 질문이 떠올랐다. prerewri...

May 11, 20266 min read7

법률 AI 검색 실험기 (11) — 오답 분석: 법률 RAG는 왜 자신 있게 틀리는가

틀린 답 하나가 열어준 토끼굴 "중소기업 특별세액감면이 최저한세 적용 대상인가요?" 단순해 보이는 질문이었다. 법령 QA 시스템은 자신 있게 답했다. "조세특례제한법 제132조가 해당 감면 조문을 열거하므로, 최저한세 적용 대상입니다." 조문 번호도 있고, 논리 구조도 있고, 결론도 명확했다. 문제는 하나뿐이었다. 틀렸다는 것. 실제로 제132조의 열거 조문과 해당 감면 조문의 관계를 확인하면, 시스템이 내린 결론과 실제 적용이 달랐다. 세무 ...

May 5, 20264 min read17
D

Dongjun's Blog

28 posts