[LLM Wiki 16] Query Expansion: 짧은 질문을 검색 가능한 여러 표현으로 바꾸는 기술
Query Expansion의 개념, QMD에서 lex/vec/hyde로 동작하는 방식, qmd-query-expansion 모델의 학습 과정, 예제와 한계를 도식 중심으로 설명한다.

Query Expansion은 사용자의 짧은 질문을 검색 시스템이 더 잘 찾을 수 있는 여러 표현으로 바꾸는 단계다.
핵심은 질문을 바꿔서 다른 답을 만들려는 것이 아니다. 같은 의도를 유지하면서, 문서 안에 실제로 등장할 가능성이 높은 표현을 늘리는 것이다.
예를 들어 사용자가 이렇게 물었다고 하자.
auth config
사람은 이 말이 “인증 설정”이라는 걸 대충 알아듣는다. 하지만 검색 엔진은 문서에 어떤 단어가 들어 있는지에 민감하다. 문서에는 auth config가 아니라 authentication configuration, login settings, AUTH_SECRET, session setup처럼 쓰여 있을 수 있다.
Query Expansion은 이 간극을 메운다.
lex: authentication configuration
lex: auth settings setup
vec: how to configure authentication settings
vec: authentication configuration options
hyde: Authentication can be configured by setting the AUTH_SECRET environment variable.
이렇게 확장하면 BM25는 정확한 키워드를 더 잘 잡고, vector search는 의미가 비슷한 문서를 더 잘 잡고, HyDE는 답변처럼 생긴 문장과 가까운 문서를 찾을 수 있다.
왜 필요한가
검색 질문은 보통 짧다. 사용자는 문서에 실제로 어떤 단어가 쓰였는지 알지 못한다. 반대로 문서는 길고 표현이 제각각이다.
이때 원 질문 하나로만 검색하면 세 가지 문제가 생긴다.
첫째, 동의어 문제다. 사용자는 auth라고 쓰고 문서는 authentication이라고 쓸 수 있다.
둘째, 추상도 문제다. 사용자는 “가격 때문에 막힌 고객”이라고 묻고, 문서에는 “budget concern”, “procurement delay”, “too expensive”처럼 더 구체적인 표현이 들어 있을 수 있다.
셋째, 검색 backend의 성격 차이다. BM25는 짧고 정확한 키워드에 강하고, vector search는 자연어 문장에 강하다. 같은 질문을 그대로 둘 다에 던지는 것보다 backend에 맞는 표현으로 나눠주는 편이 낫다.
Query Expansion의 목표는 정답을 바로 생성하는 것이 아니라, 정답 문서가 1차 후보군에 들어올 확률을 높이는 것이다.
QMD에서의 동작 방식

QMD는 query expansion을 그냥 “비슷한 말 몇 개 만들기”로 처리하지 않는다. 소스 기준으로 보면 QMD의 expansion 결과는 타입이 붙은 query 목록이다.
대표 타입은 세 가지다.
lex는 lexical query다. 짧고 키워드 중심인 표현이다. QMD에서는 SQLite FTS5/BM25 검색으로 간다. 제품명, 함수명, 설정 키, 파일명, 에러 메시지처럼 글자가 중요한 검색에 맞다.
vec는 vector query다. 자연어 문장에 가까운 표현이다. QMD에서는 embedding을 만든 뒤 vector search로 보낸다. 원문과 단어가 달라도 의미가 비슷한 문서를 찾는 데 쓴다.
hyde는 hypothetical document query다. 검색어가 아니라 “이 질문에 답하는 문서라면 이렇게 생겼을 것” 같은 가상 문장이다. 이 문장을 embedding하면 실제 답변 문서와 벡터 공간에서 가까워질 수 있다.
QMD의 내부 흐름은 대략 이렇다.
- 원 질문으로 먼저 BM25 probe를 한다.
- 강한 검색 신호가 있으면 expansion을 생략할 수 있다.
- 필요하면 local LLM으로 lex/vec/hyde query를 생성한다.
- grammar로 출력 형식을 제한한다.
- lex는 FTS/BM25로 보낸다.
- vec와 hyde는 vector search로 보낸다.
- 결과를 RRF로 합친다.
- 후보 문서를 chunk로 나누고 rerank한다.
중요한 점은 expansion이 검색 전처리에서 끝나지 않는다는 것이다. QMD는 expansion 결과를 타입별로 다른 검색 경로에 보내고, 나중에 fusion과 rerank로 다시 합친다.
QMD가 사용하는 모델
QMD 소스의 기본 query expansion 모델은 다음 GGUF 모델이다.
hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf
이 모델은 범용 chat 모델을 그대로 쓰는 것이 아니라, QMD의 검색 파이프라인에 맞춰 query expansion 전용으로 튜닝된 모델이다. 기반 모델은 Qwen/Qwen3-1.7B이고, 출력은 lex, vec, hyde 형식을 따르도록 학습되어 있다.
QMD의 src/llm.ts를 보면 expansion prompt는 아주 직접적이다.
/no_think Expand this search query: {query}
intent가 있으면 다음처럼 검색 의도도 함께 넣는다.
/no_think Expand this search query: {query}
Query intent: {intent}
그리고 grammar로 출력 형식을 제한한다.
lex: ...
vec: ...
hyde: ...
즉 QMD는 모델에게 자유롭게 설명문을 쓰게 두지 않는다. 검색 시스템이 바로 라우팅할 수 있는 구조화된 라인만 받도록 설계되어 있다.
모델은 어떻게 학습되었나

Hugging Face 모델 카드 기준으로 이 모델은 Qwen3-1.7B를 바탕으로 두 단계 학습을 거쳤다.
첫 번째는 SFT, Supervised Fine-Tuning이다. 사람이 만든 정답 예시처럼 “이 질문이면 이런 lex, vec, hyde를 내야 한다”는 형식을 학습한다. 모델 카드에는 Qwen3-1.7B 기반, LoRA rank 16, 약 2,290개 train split 예시, 5 epoch 학습이 명시되어 있다.
두 번째는 GRPO, Group Relative Policy Optimization이다. SFT가 형식을 익히는 단계라면, GRPO는 더 좋은 expansion을 선호하도록 다듬는 단계다. 모델이 여러 expansion 후보를 만들고, reward function이 점수를 매긴다. 그 점수를 기준으로 더 나은 출력을 강화한다.
보상 기준은 rule-based다. LLM judge로 평가하는 방식이 아니라, 정해진 규칙으로 점수를 준다. 모델 카드에 나온 주요 기준은 다음과 같다.
- format: lex/vec/hyde 줄 형식을 지켰는가
- diversity: 표현이 다양하고 원 질문을 그대로 반복하지 않는가
- HyDE: 적절한 길이의 단일 문장인가
- quality: lex는 짧고 vec는 자연어에 가까운가
- entity: 중요한 고유명사와 핵심 용어를 보존했는가
- think bonus: think block 없이 바로 답했는가
마지막으로 base model, SFT adapter, GRPO adapter를 합쳐 GGUF로 변환한다. QMD는 이 GGUF 파일을 local model로 불러와 query expansion에 사용한다.
예제로 보는 동작
예를 들어 LLM Wiki에서 사용자가 이렇게 묻는다고 하자.
가격 때문에 막힌 고객 미팅 찾아줘
좋지 않은 검색은 이 문장을 그대로 한 번만 검색한다. 그러면 “가격”, “고객”, “미팅”이라는 단어가 그대로 들어간 문서는 찾지만, 다음 표현은 놓칠 수 있다.
budget concern
pricing objection
too expensive
procurement delayed
도입 비용 부담
예산 승인 지연
Query Expansion은 질문의 의도를 유지하면서 검색 표현을 늘린다.
lex: 가격 고객 미팅 예산 비용
lex: pricing objection customer meeting
vec: customer meetings blocked by budget concerns or pricing objections
vec: sales discussion where purchase was delayed because the product was too expensive
hyde: The customer said the product was too expensive and the deal was delayed until budget approval.
QMD는 이 결과를 그대로 한 검색기에 넣지 않는다.
lex 라인은 BM25로 간다. “가격”, “예산”, “pricing”, “customer” 같은 정확한 단어가 들어간 문서를 찾는다.
vec 라인은 vector search로 간다. “비용 부담”, “budget issue”, “expensive”처럼 표현이 달라도 의미가 가까운 문서를 찾는다.
hyde 라인은 답변 문서처럼 생긴 문장을 embedding한다. 실제 회의록 문장 중 “고객이 가격 때문에 구매를 보류했다”와 가까운 문서를 찾는 데 유리하다.
그 다음 QMD는 여러 결과를 RRF로 합치고, chunk 단위로 rerank해서 최종 결과를 만든다.
강한 BM25 신호가 있으면 왜 생략하나
QMD 소스에는 strong signal bypass가 있다. 처음 원 질문으로 BM25를 돌렸을 때 상위 결과 점수가 충분히 높고, 1등과 2등의 차이가 크면 query expansion을 생략할 수 있다.
이건 꽤 현실적인 설계다. 예를 들어 사용자가 정확한 에러 코드나 함수명을 검색한다면 expansion은 오히려 방해가 될 수 있다.
SQLITE_CONSTRAINT_UNIQUE
이런 질문은 의미 확장보다 정확한 문자열 검색이 더 중요하다. “unique constraint error”, “database duplicate key” 같은 확장은 관련 문서를 넓힐 수는 있지만, 사용자가 찾는 정확한 코드 문서를 아래로 밀어낼 수도 있다.
다만 intent가 제공된 경우에는 strong signal bypass를 끈다. 같은 단어라도 의도가 다르면 검색 방향이 달라질 수 있기 때문이다.
Structured Search와의 관계
QMD에는 structuredSearch도 있다. 이것은 QMD 내부의 작은 expansion 모델을 쓰지 않고, 외부 LLM이나 MCP caller가 직접 lex/vec/hyde query를 만들어 넘기는 방식이다.
소스 주석은 이 방식을 “capable LLMs에게 추천”한다고 설명한다. 이유는 간단하다. 더 큰 LLM은 도메인 맥락을 더 잘 이해하고, 특정 업무나 회사 문서에 맞는 검색 표현을 더 잘 만들 수 있기 때문이다.
정리하면 QMD에는 두 가지 경로가 있다.
- 일반 query: QMD가 내부 모델로 query expansion을 수행한다.
- structured search: 외부 LLM이 이미 확장한 typed query를 QMD가 실행한다.
LLM Wiki에서 agent가 충분히 똑똑하다면 structured search가 더 좋은 결과를 낼 수 있다. 반대로 단순 CLI 사용이나 작은 agent 환경에서는 QMD 내부 expansion 모델이 기본 품질을 보장해준다.
한계
Query Expansion은 recall을 높이는 기술이지 정답을 보장하는 기술은 아니다.
첫째, 잘못 확장하면 검색 노이즈가 늘어난다. “가격 문제”를 “성능 문제”나 “계약 조건 문제”로 넓혀버리면 관련 없는 문서가 후보에 들어온다.
둘째, 고유명사를 잃으면 위험하다. 고객명, 프로젝트명, 에러 코드, 함수명 같은 entity가 expansion 과정에서 사라지면 검색 방향이 틀어진다. 그래서 QMD 모델 학습의 reward에도 entity 보존 항목이 들어간다.
셋째, 도메인 지식이 약하면 확장 품질도 약하다. 범용 모델은 특정 회사 내부 약어, 제품 코드, 팀 이름을 모를 수 있다. 이런 경우 외부 LLM이 context를 보고 structured query를 만들어주는 방식이 더 낫다.
넷째, expansion은 비용이 든다. 로컬 GGUF 모델이라 API 비용은 없지만, 모델 로딩과 추론 시간이 든다. 그래서 QMD는 강한 BM25 신호가 있으면 expansion을 생략할 수 있게 한다.
다섯째, 1차 후보를 넓힐 뿐 최종 판단은 아니다. Query Expansion으로 후보가 넓어지고, RRF로 합쳐지고, rerank가 다시 줄 세워야 최종 품질이 나온다.
LLM Wiki에서의 의미
LLM Wiki에서 Query Expansion은 “사용자의 말”과 “문서의 실제 표현” 사이를 이어주는 번역 계층이다.
Obsidian이나 Markdown은 사람이 쓰는 지식 저장소다. 사람은 같은 의미를 매번 다르게 쓴다. 회의록에는 “가격”, “비용”, “예산”, “budget”, “pricing”, “too expensive”가 섞인다.
LLM이 이런 지식을 잘 쓰려면 검색 단계에서 표현 차이를 흡수해야 한다. Query Expansion은 그 첫 단계다.
다만 Query Expansion만으로 좋은 LLM Wiki가 되는 것은 아니다. 좋은 문서 구조, metadata, BM25, vector search, RRF, rerank, source citation이 함께 있어야 한다. Query Expansion은 그중 “정답 후보를 놓치지 않게 하는 입구”에 해당한다.
정리
Query Expansion은 짧은 질문을 여러 검색 표현으로 확장해 정답 문서가 후보군에 들어올 가능성을 높이는 기술이다.
QMD는 이 과정을 lex, vec, hyde라는 typed query로 구조화한다. lex는 BM25로, vec와 hyde는 vector search로 보내고, 결과는 RRF와 rerank로 합친다.
QMD의 기본 expansion 모델은 Qwen3-1.7B 기반의 qmd-query-expansion-1.7B GGUF 모델이다. SFT로 형식을 배우고, GRPO와 rule-based reward로 품질을 다듬은 뒤, GGUF로 변환되어 로컬에서 실행된다.
가장 중요한 관점은 이것이다. Query Expansion은 답을 만드는 기술이 아니라, 좋은 답을 만들 근거 문서를 놓치지 않게 하는 검색 전 단계다.