Query Rewriting + HyDE
쿼리 재작성(Query Rewriting)과 HyDE(Hypothetical Document Embeddings)는 단일 쿼리를 변환하여 검색 품질을 높이는 기법입니다.
Query Rewriting (쿼리 재작성)
LLM을 사용하여 원본 질문을 검색에 최적화된 쿼리로 변환합니다.
왜 필요한가?
사용자 질문은 종종 구어체이거나, 약어를 포함하거나, 핵심 키워드가 부족합니다.
기본 구현
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = init_chat_model("gpt-4o-mini", temperature=0)
rewrite_prompt = ChatPromptTemplate.from_messages([
("system", """당신은 검색 쿼리 최적화 전문가입니다.
사용자의 질문을 벡터 데이터베이스 검색에 최적화된 쿼리로 변환하세요.
규칙:
- 모호한 표현을 구체적으로 변환
- 약어를 풀어서 작성
- 핵심 키워드를 포함
- 한 문장으로 작성"""),
("human", "원본 질문: {question}\n\n최적화된 검색 쿼리:"),
])
rewrite_chain = rewrite_prompt | llm | StrOutputParser()
optimized = rewrite_chain.invoke({"question": "RAG 좀 알려줘"})
# → "RAG(Retrieval-Augmented Generation)의 정의, 핵심 구성 요소, 동작 원리"
고급 프롬프트 설계
도메인에 따라 프롬프트를 커스터마이즈할 수 있습니다.
# 기술 문서용 쿼리 재작성
tech_rewrite_prompt = ChatPromptTemplate.from_messages([
("system", """당신은 기술 문서 검색 쿼리 최적화 전문가입니다.
규칙:
1. 약어를 풀어서 작성 (예: RAG → Retrieval-Augmented Generation)
2. 기술 용어를 정확히 사용
3. 동의어를 포함하여 검색 범위 확대
4. 구체적인 기술 키워드를 추가
예시:
- "LLM 환각" → "대규모 언어 모델(LLM)의 환각(hallucination) 문제 원인과 해결 방법"
- "벡터DB 비교" → "벡터 데이터베이스(Chroma, Qdrant, Milvus, Weaviate) 성능 비교와 특징"
"""),
("human", "원본 질문: {question}\n\n최적화된 검색 쿼리:"),
])
Retriever와 통합
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(collection_name="docs", embedding_function=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 쿼리 재작성 → 검색 파이프라인
def rewrite_and_retrieve(question: str):
optimized_query = rewrite_chain.invoke({"question": question})
results = retriever.invoke(optimized_query)
return results
results = rewrite_and_retrieve("RAG 좀 알려줘")
HyDE (Hypothetical Document Embeddings)
LLM이 질문에 대한 가상의 답변을 먼저 생성한 뒤, 그 답변의 임베딩으로 검색합니다.
핵심 아이디어
질문의 임베딩보다 답변의 임베딩이 실제 문서와 더 유사합니다.
from langchain.chat_models import init_chat_model
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = init_chat_model("gpt-4o-mini", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(collection_name="docs", embedding_function=embeddings)
# 1. 가상 답변 생성
hyde_prompt = ChatPromptTemplate.from_messages([
("system", """질문에 대한 답변을 작성하세요.
정확하지 않아도 됩니다. 관련 키워드와 개념을 최대한 포함하세요.
답변은 실제 기술 문서처럼 작성하세요."""),
("human", "{question}"),
])
hyde_chain = hyde_prompt | llm | StrOutputParser()
# 2. 가상 답변으로 검색
def hyde_retrieve(question: str, k: int = 4):
hypothetical_answer = hyde_chain.invoke({"question": question})
results = vectorstore.similarity_search(hypothetical_answer, k=k)
return results
results = hyde_retrieve("RAPTOR는 어떻게 동작하나요?")
HyDE가 효과적인 경우
| 시나리오 | 효과 | 이유 |
|---|
| 질문이 매우 짧을 때 | 높음 | 가상 답변이 풍부한 키워드를 생성 |
| 질문과 문서 스타일이 다를 때 | 매우 높음 | 스타일 갭을 해소 |
| 전문 용어가 포함된 질문 | 높음 | 관련 용어를 확장 |
| 사실 확인 질문 | 낮음 | 잘못된 가상 답변이 검색을 왜곡 |
HyDE는 LLM이 생성한 가상 답변으로 검색하므로, 답변에 환각(hallucination)이 포함될 수 있습니다. 이 경우 실제 관련 문서가 아닌 환각 내용과 유사한 문서가 검색될 수 있습니다. 사실 확인(fact-checking)이 중요한 경우에는 Query Rewriting이 더 안전합니다.
Query Rewriting vs HyDE 비교
| 항목 | Query Rewriting | HyDE |
|---|
| 변환 대상 | 질문 → 최적화된 질문 | 질문 → 가상 답변 |
| LLM 호출 | 1회 | 1회 |
| 환각 위험 | 낮음 | 있음 (가상 답변이 잘못될 수 있음) |
| 키워드 확장 | 보통 | 높음 |
| 적합한 경우 | 모호하거나 짧은 질문 | 질문-문서 스타일 차이 |
| 안전성 | 더 안전 | 환각 가능성 존재 |
안전한 기본 선택은 Query Rewriting입니다. 검색 품질이 부족하고, 특히 질문과 문서 간 스타일 차이가 큰 경우에 HyDE를 시도해보세요. 두 기법을 순차적으로 적용하는 것도 가능합니다.
참고 논문
| 논문 | 학회/연도 | 링크 |
|---|
| Precise Zero-Shot Dense Retrieval without Relevance Labels (Gao et al.) | ACL 2023 | arXiv 2212.10496 |
| Query Rewriting for Retrieval-Augmented Large Language Models (Ma et al.) | EMNLP 2023 | arXiv 2305.14283 |