Skip to main content

Command Palette

Search for a command to run...

법률 AI 검색 실험기 (4) — Query Rewriting: Prerewriter 도입과 모델 비교

Updated
6 min read

벡터 검색 성능을 올리는 가장 쉬운 방법

RAG 파이프라인에서 retrieval 성능이 안 나올 때 가장 먼저 떠오르는 선택지는 보통 두 가지다. 임베딩 모델을 바꾸거나, 쿼리를 바꾸거나. 임베딩 모델 비교는 이미 별도로 진행했고, 이번에는 후자를 건드릴 차례였다.

업계에서는 이 접근을 보통 query rewriting이라고 부른다. 사용자의 원문 질문을 검색에 더 유리한 형태로 변환하는 것이다. Microsoft의 RAG 기법 정리 문서에서는 query rewriting을 "pre-retrieval" 단계의 핵심 기법으로 분류하고 있고, 최근 연구(arxiv 2501.07391)에서도 query expansion과 rewriting이 retrieval 품질에 미치는 영향을 체계적으로 조사하고 있다. HyDE(Hypothetical Document Embedding)처럼 가상 문서를 생성해서 검색하는 방법도 있고, Step-Back Prompting처럼 질문을 더 일반적인 형태로 바꾸는 접근도 있다.

내가 만든 파이프라인에서는 이 단계를 prerewriter라고 부른다. 검색 전에 쿼리를 다시 쓴다는 뜻 그대로다. 이번 글에서는 prerewriter V2의 설계 의도와, 네 가지 LLM 모델로 비교 실험한 결과를 정리한다.

Prerewriter V2 설계 의도

Prerewriter V1은 단순한 rephrasing이었다. V2에서 바꾼 핵심 설계는 크게 세 가지다.

첫째, 복수 정답을 더 강하게 고려한다. 법률 QA에서는 하나의 질문이 여러 조문, 여러 법률에 걸쳐 답을 가지는 경우가 많다. "상속 포기 절차와 그 효과는?"이라는 질문 하나에도 민법의 여러 조문이 관련된다. Prerewriter V2는 하나의 질문 안에서 여러 축--사람, 기관, 관계, 권리, 책임, 절차, 금액, 기간, 숫자--을 보존하도록 프롬프트를 설계했다.

둘째, 출력을 용도별로 분리한다. 단일 프롬프트 안에서 벡터 검색용과 그래프 검색용 출력을 함께 생성한다.

  • vector.searchQuery: 벡터 검색에 넣을 재작성된 쿼리
  • vector.keywords: 벡터 검색 보조 키워드
  • graph.keywords: 그래프 탐색용 키워드
  • graph.lawNames: 그래프 탐색에 쓸 법률명

다만, 이번 실험에서는 vector 출력만 사용했다. 실제 검색은 벡터 DB의 hybrid(dense + sparse) 검색만 돌렸고, 그래프 검색은 사용하지 않았다. 그래프 검색 쪽은 별도 실험으로 분리할 예정이었다.

셋째, 기존 코드와 실험을 건드리지 않는다. 새 prerewriter만 별도로 추가하고, baseline(raw, 즉 원문 그대로 검색)과의 비교를 항상 유지했다.

실험 설계

질문셋은 전체 84개를 사용했다.

카테고리문항 수
direct20
scenario20
tax17
multi11
holdout16

임베딩 모델은 5종 전체를 돌렸다: bge-m3, pplx-embed-v1-0.6b, pplx-embed-v1-4b, pplx-embed-context-v1-0.6b, pplx-embed-context-v1-4b.

Prerewriter 모델은 두 차례에 걸쳐 총 4종을 비교했다.

  • 1차 실험: gemini-2.5-flash-lite, gpt-5-mini
  • 2차 실험: gemini-2.5-flash, gpt-4.1-mini

평가 지표는 복수 정답 recall을 중심으로 봤다. @K는 상위 K개 청크 안에 정답 조문이 몇 개 포함되었는지, F@K는 복수 정답이 모두 포함된 질문("full match")이 몇 개인지를 뜻한다. 예를 들어 multi 카테고리에서 @50 = 30/31이라면, 상위 50개 청크 안에 전체 31개 정답 중 30개가 포함되었다는 의미다. F@50 = 10/11이면 11개 multi 질문 중 10개가 모든 정답을 상위 50개 안에서 찾았다는 뜻이다.

1차 실험: Gemini Flash Lite vs GPT-5 mini

결과 요약 (multi 카테고리, 복수 정답 기준)

임베딩모드@10@30@50F@50평균 지연
pplx-v1-4braw19/3126/3130/3110/11324ms
pplx-v1-4bgemini-flash-lite19/3125/3128/318/111,755ms
pplx-v1-4bgpt-5-mini21/3129/3130/3110/115,387ms
pplx-v1-0.6braw17/3126/3128/318/11323ms
pplx-v1-0.6bgpt-5-mini16/3129/3129/319/115,383ms
pplx-context-v1-0.6braw16/3124/3126/316/11326ms
pplx-context-v1-0.6bgpt-5-mini17/3127/3129/319/115,394ms

해석

Gemini 2.5 Flash Lite는 전반적으로 실패였다. Raw보다 분명히 떨어지는 경우가 많았고, 특히 bge-m3pplx-v1-4b에서 손해가 컸다. 쿼리를 재작성했는데 오히려 원문보다 검색 품질이 나빠진 것이다.

GPT-5 mini는 일부 임베딩에서 실제로 개선을 보였다. 주목할 만한 변화를 정리하면:

  • pplx-embed-v1-0.6b: @50이 28/31에서 29/31로, F@50이 8/11에서 9/11로 상승
  • pplx-embed-context-v1-0.6b: @50이 26/31에서 29/31로, F@50이 6/11에서 9/11로 상승. 이건 꽤 큰 점프다.
  • pplx-embed-context-v1-4b: @50이 28/31에서 29/31로, F@50이 8/11에서 9/11로 상승

다만 최고 성능 조합인 pplx-embed-v1-4b에서는 @50 기준 30/31로 raw와 동일했다. @30, @40에서는 개선이 있었지만 천장을 뚫지는 못했다.

가장 큰 문제는 속도였다. GPT-5 mini prerewriter를 붙이면 multi 기준 5.2초에서 5.5초가 걸렸다. Raw는 0.16초에서 0.33초다. Retrieval 단계 하나에 5초를 더 쓰는 건 운영 환경에서 받아들이기 어렵다.

2차 실험: Gemini Flash vs GPT-4.1 mini

1차에서 flash-lite가 너무 약했기 때문에 상위 모델인 gemini-2.5-flash를 추가했고, GPT 쪽에서는 비용/속도 절충을 위해 gpt-4.1-mini를 넣었다.

결과 요약 (multi 카테고리, 복수 정답 기준)

임베딩모드@10@30@50F@50평균 지연
pplx-v1-4braw19/3126/3130/3110/11376ms
pplx-v1-4bgemini-flash18/3126/3129/319/113,613ms
pplx-v1-4bgpt-4.1-mini20/3126/3128/318/112,894ms
pplx-v1-0.6bgemini-flash19/3128/3128/318/113,601ms
pplx-context-v1-4bgpt-4.1-mini17/3124/3129/319/112,835ms

해석

Gemini 2.5 Flash는 Flash Lite보다 확실히 나았다. 특히 pplx-v1-0.6b에서 @20, @30이 꽤 올라갔다. 하지만 raw를 확실히 뒤집는 수준은 아니었다. 지연도 3.5초에서 3.6초대로 여전히 높았다.

GPT-4.1 mini는 GPT-5 mini보다 빨랐지만, 성능은 약했다. 지연이 2.7초에서 2.9초로 GPT-5 mini 대비 절반 가까이 줄었는데, 그만큼 rewriting 품질도 떨어진 것으로 보인다. 일부 임베딩에서 @40, @50은 괜찮았지만, 전체 최고 조합을 만들지는 못했다.

네 모델을 종합하면

모든 실험을 관통하는 결론은 명확했다.

순수 retrieval 최고 성능은 여전히 pplx-embed-v1-4b + raw(원문 그대로)였다. Multi @50 = 30/31, F@50 = 10/11. 어떤 prerewriter를 붙여도 이 조합을 일관되게 넘지 못했다.

네 모델의 포지션을 정리하면 이렇다:

모델성능 개선지연종합 판단
gemini-2.5-flash-literaw보다 하락~1.7s탈락
gemini-2.5-flashflash-lite보다 개선, raw 미달~3.5s보류
gpt-4.1-mini부분 개선, gpt-5-mini 미달~2.8s보류
gpt-5-mini일부 임베딩에서 유의미 개선~5.3s연구 후보

속도 대비 품질이 가장 좋았던 모델은 gpt-5-mini다. 특히 중소형 임베딩(pplx-embed-v1-0.6b, pplx-embed-context-v1-0.6b)과 조합했을 때 F@50 기준 6/11에서 9/11로 올라가는 등 눈에 띄는 개선이 있었다. 하지만 가장 강한 임베딩(pplx-embed-v1-4b)과 조합하면 이미 raw가 충분히 좋아서 prerewriter의 추가 가치가 제한적이었다.

Gemini 계열은 이 프롬프트 설계와 궁합이 안 맞았다. Flash Lite는 아예 역효과였고, Flash도 기대만큼은 아니었다. 모델 자체의 문제인지 프롬프트 최적화 부족인지는 이 실험만으로는 단정할 수 없다.

이 실험에서 확인한 것

Query rewriting이 RAG에서 유효한 기법이라는 건 업계 전반의 합의다. 하지만 "항상 좋아진다"는 보장은 없다. 이번 실험에서 확인한 결론은 세 가지다.

1. 강한 임베딩 + 원문이 약한 임베딩 + rewriting을 이긴다. pplx-embed-v1-4b raw가 어떤 prerewriter 조합보다 좋거나 동등했다. 임베딩 모델의 기본 역량이 충분하면 쿼리를 손대지 않는 것이 더 나을 수 있다.

2. Rewriting은 약한 임베딩을 보완하는 데 더 효과적이다. GPT-5 mini prerewriter가 가장 큰 효과를 보인 건 0.6b급 소형 임베딩과의 조합이었다. 임베딩 모델의 표현력이 부족한 부분을 쿼리 쪽에서 보완한 결과다.

3. 속도 비용을 무시할 수 없다. Prerewriter를 붙이면 retrieval 지연이 10배에서 16배로 뛴다. 후속 단계(answer generation, verification)의 지연까지 합치면 전체 파이프라인 응답 시간에 미치는 영향이 상당하다. 운영 환경에서는 이 trade-off를 무시할 수 없다.

결론적으로, prerewriter는 운영 기본값으로 채택하기에는 아직 설득력이 부족했다. 하지만 "쿼리를 바꾸면 검색이 달라진다"는 가능성 자체는 확인했고, 특히 소형 임베딩 환경이나 복수 정답 recall이 중요한 시나리오에서는 여전히 연구할 가치가 있다. 다음 단계에서는 source routing과 결합하거나, prerewriter의 프롬프트 자체를 더 정교하게 다듬는 방향을 검토할 예정이다.

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