Skip to main content

Command Palette

Search for a command to run...

법률 AI 검색 실험기 (5) — Query Rewriting: 프롬프트 진화와 subQuery 실험

Updated
6 min read

법률 QA 시스템의 검색 품질을 끌어올리기 위해 query rewriting 프롬프트를 반복 개선하고, sub-query decomposition까지 도입해 본 실험 기록이다. 결론부터 말하면, 프롬프트 개선은 효과가 있었지만 한계가 명확했고, sub-query 전략은 기대만큼의 돌파구가 되지 못했다.


배경: V2까지의 상황

이전 글에서 다뤘듯이, prerewriter V2는 Gemini 2.5 Flash-Lite 모델 기준으로 raw query 대비 오히려 성능이 떨어지는 경우가 많았다. 쿼리를 다시 써주는 것이 항상 좋은 것은 아니라는 교훈을 얻은 셈이다. 그래서 V3에서는 프롬프트 자체를 근본적으로 재설계했다.

한편 당시 최고 baseline은 pplx-embed-v1-4b 임베딩 모델에 raw query를 그대로 넣는 조합이었다. Recall@50 기준 30/31, Full Recall@50 기준 10/11. prerewriter가 이 baseline을 넘지 못하면 존재 의의가 없는 상황이었다.

참고: 이 30/31 수치는 초기 평가 기준 기준이다. 이후 multi-2 문항의 정답 정의를 재검토하면서(민법 766조 → 756조) benchmark를 수정했고, graph 검색과 결합하여 최종 31/31을 달성했다. 자세한 내용은 이후 "1차 아키텍처 확정: 실험에서 운영으로" 편에서 다룬다.


V3: 프롬프트 개선의 효과

V3 프롬프트는 2026년 3월 9일에 prerewriter-unified-v3.ts로 구현해서 평가했다. 모델은 동일하게 Gemini 2.5 Flash-Lite, 임베딩 5종 전체에 대해 복수정답 질문셋으로 측정했다.

핵심 결과 (Recall@50 / Full Recall@50)

임베딩 모델rawV3 prerewrite
bge-m328/31, 8/1128/31, 8/11
pplx-embed-v1-0.6b28/31, 8/1129/31, 9/11
pplx-embed-v1-4b30/31, 10/1129/31, 9/11
pplx-embed-context-v1-0.6b26/31, 6/1128/31, 8/11
pplx-embed-context-v1-4b28/31, 8/1127/31, 7/11

V2 대비 확실한 개선이 있었다. V2는 raw보다 성능을 깎아먹는 경우가 잦았는데, V3는 최소한 그런 참사는 없었다. 특히 두 가지 임베딩에서 의미 있는 향상을 확인했다.

  • pplx-embed-v1-0.6b: 28/31에서 29/31로, Full Recall도 8/11에서 9/11로 상승
  • pplx-embed-context-v1-0.6b: 26/31에서 28/31로, Full Recall은 6/11에서 8/11로 대폭 상승

다만 주의할 점이 있었다. 최고 성능 임베딩인 pplx-embed-v1-4b에서는 오히려 V3가 raw보다 약간 낮았다. 그리고 평균 latency가 +1.3~1.4초 증가했다. 검색 한 건당 1.5초가 추가되는 것은 운영 환경에서 무시할 수 없는 비용이다.

V3의 판단: 프롬프트 방향은 맞다. V2보다 확실히 낫다. 그러나 전체 최고 baseline(pplx-embed-v1-4b raw)을 대체하기에는 아직 부족하다.


V4: sub-query decomposition 도입

V3에서 단일 쿼리 rewriting의 한계를 체감한 뒤, 다음 시도로 sub-query decomposition을 도입했다. 이것이 V4다.

sub-query decomposition이란

RAG 시스템에서 널리 연구되는 기법으로, 복잡한 질문을 더 단순한 하위 질문들로 분해한 뒤 각각에 대해 검색을 수행하고 결과를 병합하는 방식이다. 최근 연구들에 따르면, multi-hop 질문에서 관련 사실이 여러 문서에 분산되어 있을 때 표준 RAG가 충분한 정보를 검색하지 못하는 문제를 해결하기 위해 고안되었다. Question Decomposition for Retrieval-Augmented Generation (ACL 2025) 연구에서는 질문 분해와 reranking을 결합했을 때 MRR@10 기준 +36.7%, 답변 F1 기준 +11.6%의 개선을 보고하기도 했다.

법률 QA에서도 이 접근이 유효할 것이라는 가설이 있었다. "임대차 계약 해지 시 보증금 반환 절차와 기한은?"처럼 여러 축(해지 절차, 보증금 반환, 기한)을 동시에 묻는 질문이 많기 때문이다. 각 축별로 검색하면 놓치는 조문이 줄지 않을까.

V4 설계

V4 프롬프트(prerewriter-unified-v4.ts)는 기존 단일 searchQuery 외에 구조화된 출력을 도입했다.

  • queryType: 질문 유형 분류
  • vector.subQueries: 하위 질문 목록
  • vector.keywords: 검색 키워드
  • graph.keywords: 그래프 검색용 키워드
  • graph.lawNames: 관련 법률명

검색 시에는 메인 searchQuery와 각 subQuery를 개별 검색한 뒤 RRF(Reciprocal Rank Fusion)로 병합했다. 그래프 검색은 이번 실험에서 켜지 않았고, vector 경로만 평가했다.

결과 (Recall@50 / Full Recall@50)

임베딩 모델rawV3V4
bge-m328/31, 8/1128/31, 8/1127/31, 7/11
pplx-embed-v1-0.6b28/31, 8/1129/31, 9/1129/31, 9/11
pplx-embed-v1-4b30/31, 10/1129/31, 9/1129/31, 9/11
pplx-embed-context-v1-0.6b26/31, 6/1128/31, 8/1127/31, 7/11
pplx-embed-context-v1-4b28/31, 8/1127/31, 7/1128/31, 8/11

sub-query를 실제로 검색 병합에 넣어봤지만, V3 대비 전반적으로 나아지지 않았다. 최고치가 같은 경우도 있었지만 Recall@10 같은 초기 정밀도 지표에서는 오히려 크게 악화됐다.

특히 pplx-embed-v1-4b의 Recall@10이 V3의 21/31에서 V4의 14/31로 떨어졌다. sub-query들이 오히려 노이즈를 끌어들여 상위 랭킹을 흐트러뜨린 것이다. Latency도 ~2.2초까지 올라가 V3의 ~1.5초보다 더 나빠졌다.


세 버전 비교: 무엇을 배웠나

V2에서 V4까지의 여정을 요약하면 이렇다.

버전접근최고 Recall@50최고 Full@50Latency 추가판정
raw (baseline)없음30/3110/110ms최고
V2기본 rewritingraw 이하raw 이하+1.2s실패
V3프롬프트 재설계29/319/11+1.4s부분 개선
V4sub-query + RRF 병합29/319/11+2.2s개선 없음

핵심 발견은 세 가지다.

첫째, 프롬프트 품질은 중요하다. V2에서 V3로의 개선은 프롬프트 설계만으로도 retrieval 품질을 의미 있게 올릴 수 있음을 보여줬다. "잘 쓴" rewriting은 약한 임베딩 모델의 성능을 끌어올리는 효과가 있었다.

둘째, sub-query decomposition은 만능이 아니다. 학술 연구에서 보고된 큰 폭의 개선이 우리 도메인에서는 재현되지 않았다. 분해된 하위 질문들이 오히려 노이즈를 증폭시키는 현상이 관찰됐다. 이전에 clean 실험에서도 few-shot 치팅 조건에서만 좋아 보이고 일반화 조건에서는 하락했던 이력이 있었는데, V4에서도 같은 패턴이 반복됐다.

셋째, prerewriter 단독으로는 retrieval miss를 해결할 수 없다. 어떤 프롬프트를 써도 pplx-embed-v1-4b raw의 30/31, 10/11을 넘지 못했다. 문제의 본질이 "쿼리를 얼마나 잘 다시 쓰느냐"가 아니라 "Top-50 안에 아예 들어오지 못하는 조문을 어떻게 회수하느냐"에 있다는 결론에 도달했다.


한계 인식과 다음 가설

이 실험들을 거치며 팀 내 토론에서 도출한 핵심 인식은 이렇다: 현재 병목은 selection miss가 아니라 retrieval miss다. selection miss(Top-50 안에 있는데 최종 답변에서 빠지는 것)는 이미 상당 부분 완화됐고, 진짜 문제는 Top-50 안에 아예 들어오지 않는 조문이었다.

이 관점에서 네 가지 다음 가설을 세웠다.

1. 임베딩 모델 재평가 (우선순위 1위)

retrieval quality를 직접 건드리는 가장 근본적인 접근이다. 기존 임베딩 평가가 단일정답 위주였기 때문에, 복수정답 Recall@20/50과 holdout domain noise 기준으로 다시 평가하면 더 나은 모델이 있을 수 있다. 만약 K20에서 현재 K50 수준 recall이 나오면 노이즈 감소, selection miss 감소, retrieval miss 완화까지 기대할 수 있다.

2. retrieval-stage 웹검색 보조 (우선순위 2위)

웹검색을 답변 단계가 아니라 검색 단계의 병렬 레인으로 사용하는 것이다. 과거 실험에서 retrieval-stage web augment가 Recall@50을 29/31에서 30/31로, Full Recall을 9/11에서 10/11로 올린 적이 있었다. 웹에서 법률명, 절차명, 숨은 facet 같은 힌트를 먼저 얻고 이를 내부 DB 문서로 다시 매핑/검증하는 구조다.

3. 원문 유지형 query 분할 (우선순위 3위)

sub-query decomposition 자체를 버리는 것은 아니다. 다만 원문 query를 대체하는 방식이 아니라, 원문 검색 결과에 추가 후보를 확장하는 보조 수단으로만 한정해야 한다는 교훈을 얻었다. V4의 실패는 "분해 결과로 원문을 대체한" 설계에서 비롯된 측면이 크다.

4. Graph DB (Neo4j) 도입 (우선순위 4위)

장기적으로는 검토 가치가 있지만, 지금 당장 정확도 개선 카드로 보기는 어렵다. 현재 문제는 저장소가 MongoDB인지 Neo4j인지가 아니라, 그래프가 "조문과 개념", "조문과 절차", "조문과 기간/요건/효과" 같은 의미 관계를 알고 있는지의 문제이기 때문이다.


돌아보며

prerewriter V3, V4 실험을 통해 query rewriting의 가능성과 한계를 동시에 확인했다. 프롬프트 개선은 분명히 효과가 있었고, sub-query decomposition이라는 학술적으로 검증된 기법도 직접 시도해 봤다. 그러나 우리 도메인에서는 기대한 만큼의 돌파구가 되지 못했다.

가장 큰 수확은 "문제의 본질이 어디에 있는지"를 명확히 한 것이다. prerewriter를 아무리 고도화해도 임베딩 모델의 retrieval quality라는 천장을 넘을 수 없다. 다음 단계는 그 천장 자체를 올리는 작업이 되어야 한다.


참고 자료:

More from this blog

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

전처리 파이프라인을 "마무리"한다는 것 RAG 파이프라인에서 전처리(query preparation)는 사용자 질문과 검색 엔진 사이의 번역 계층이다. 질문을 그대로 벡터 검색에 넣는 것과, 질문을 구조화하고 어떤 소스를 열지 먼저 정하는 것은 검색 품질에서 체감할 수 있는 차이를 만든다. 이 프로젝트에서는 법률 QA를 다루고 있고, 검색 대상이 조문, 판례, 유권해석, 행정심판 등 8개 소스 레인에 걸쳐 있다. 그만큼 전처리 단계가 감당해야 ...

Apr 28, 20265 min read3

법률 AI 검색 실험기 (9) — Source Router: 8개 컬렉션을 지능적으로 라우팅하기

법률 QA 시스템을 만들면서 가장 먼저 부딪힌 현실이 있다. 우리가 다루는 법률 데이터는 하나의 벡터 DB에 넣고 검색하면 끝나는 구조가 아니라는 것이다. 법령 조문, 판례, 공식 법령해석, 부처 실무 해석, 행정심판 재결례, 헌재 결정, 위원회 결정, 지자체 조례까지 --- 성격이 완전히 다른 8개 컬렉션, 총 300만 건 이상의 문서가 Qdrant에 올라가 있다. 이 글에서는 사용자 질문 하나가 들어왔을 때 어떤 컬렉션을 열고, 어떤 컬렉션...

Apr 26, 20266 min read31

법률 AI 검색 실험기 (8) — 1차 아키텍처 확정: 실험에서 운영으로

실험이 끝나는 순간은 생각보다 조용하다. 극적인 성능 점프가 아니라, "더 이상 구조를 바꿔도 의미 있는 차이가 나지 않는다"는 판단이 쌓이면서 자연스럽게 온다. 법률 검색 서비스의 RAG 파이프라인을 약 한 달간 실험한 끝에, 나는 1차 아키텍처를 확정하고 운영 전환 준비에 들어갔다. 이 글은 그 과정에서 내린 결정들과 그 이유를 정리한 기록이다. 최종 아키텍처: 여섯 단계의 파이프라인 확정된 구조는 다음과 같다. 질문 -> PreRewri...

Apr 21, 20266 min read5

법률 AI 검색 실험기 (7) — Graph RAG 도입기: Neo4j로 조문 간 의미 관계 구축

법률 QA 시스템을 만들면서 벡터 검색의 근본적인 한계에 부딪혔다. 사용자가 "부당해고 당했는데 어떻게 하나요?"라고 물으면 근로기준법 28조(구제신청)는 잘 찾는데, 함께 알아야 하는 23조(해고제한)와 26조(해고예고)는 Top-50에도 들어오지 않았다. 벡터 유사도만으로는 풀 수 없는 문제였고, 그래프 기반 확장이 필요했다. 이 글은 Neo4j를 도입해 법률 조문 간 의미 관계 그래프를 구축하고, 실제로 놓친 조문을 회수하기까지의 과정을 ...

Apr 18, 20267 min read5

법률 AI 검색 실험기 (6) — 하이브리드 검색과 쿼리 분해 실험기

벡터 검색만으로 단답형 질문은 거의 100% recall을 달성했다. 그런데 "부당해고 구제절차와 관련 판례"처럼 정답이 여러 개인 복수정답 질문에서는 벡터 검색이 한계를 드러냈다. 정답 조문 중 일부만 Top-10에 들어오고, 나머지는 빠지는 문제가 반복됐다. 자연스럽게 다음 질문이 떠올랐다. 벡터만으로 안 되면 키워드를 섞으면 어떨까? 업계의 하이브리드 검색 흐름 하이브리드 검색은 RAG 파이프라인에서 이미 표준에 가까운 접근법이 됐다. D...

Apr 16, 20265 min read18
D

Dongjun's Blog

22 posts