Multi-Query + Decomposition
하나의 질문에서 여러 쿼리를 생성하거나, 복잡한 질문을 단순한 하위 질문으로 분해하여 검색 범위를 넓히는 기법입니다.
Multi-Query (다중 쿼리)
원본 질문에서 여러 개의 다른 관점의 쿼리를 생성하여, 각각 검색한 결과를 합칩니다.
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)
multi_query_prompt = ChatPromptTemplate.from_messages([
("system", """사용자 질문에 대해 3개의 서로 다른 관점의 검색 쿼리를 생성하세요.
각 쿼리는 원본 질문의 다른 측면을 다뤄야 합니다.
한 줄에 하나씩, 번호 없이 작성하세요."""),
("human", "{question}"),
])
multi_query_chain = multi_query_prompt | llm | StrOutputParser()
def multi_query_retrieve(question: str, retriever):
queries = multi_query_chain.invoke({"question": question})
# 각 쿼리로 검색 후 결과 합치기 (중복 제거)
all_docs = []
seen = set()
for query in queries.strip().split("\n"):
query = query.strip()
if not query:
continue
results = retriever.invoke(query)
for doc in results:
doc_id = hash(doc.page_content[:200])
if doc_id not in seen:
seen.add(doc_id)
all_docs.append(doc)
return all_docs
# 사용 예시
results = multi_query_retrieve(
"RAG에서 검색 품질을 높이는 방법은?",
retriever,
)
효과적인 쿼리 생성 프롬프트
# 관점 다양성을 강조한 프롬프트
diverse_prompt = ChatPromptTemplate.from_messages([
("system", """사용자 질문에 대해 3개의 검색 쿼리를 생성하세요.
각 쿼리는 다음 관점 중 하나를 취해야 합니다:
1. 개념/정의 관점: 핵심 개념과 정의를 다루는 쿼리
2. 구현/실전 관점: 실제 구현 방법과 코드를 다루는 쿼리
3. 비교/평가 관점: 다른 기법과의 비교나 장단점을 다루는 쿼리
한 줄에 하나씩, 번호 없이 작성하세요."""),
("human", "{question}"),
])
Sub-query Decomposition (하위 쿼리 분해)
복잡한 질문을 여러 개의 단순한 하위 질문으로 분해하여 각각 검색합니다.
decompose_prompt = ChatPromptTemplate.from_messages([
("system", """복잡한 질문을 답변에 필요한 하위 질문들로 분해하세요.
각 하위 질문은 독립적으로 검색 가능해야 합니다.
한 줄에 하나씩, 번호 없이 작성하세요."""),
("human", "{question}"),
])
decompose_chain = decompose_prompt | llm | StrOutputParser()
def decompose_and_retrieve(question: str, retriever):
sub_queries = decompose_chain.invoke({"question": question})
all_docs = []
seen = set()
for query in sub_queries.strip().split("\n"):
query = query.strip()
if not query:
continue
results = retriever.invoke(query)
for doc in results:
doc_id = hash(doc.page_content[:200])
if doc_id not in seen:
seen.add(doc_id)
all_docs.append(doc)
return all_docs
results = decompose_and_retrieve(
"Self-RAG와 Corrective RAG의 차이점을 비교하고, Adaptive RAG가 이 둘을 어떻게 통합하는지 설명해줘",
retriever,
)
Step-back Prompting
구체적인 질문을 더 넓은 범위의 상위 질문으로 변환하여 배경 지식을 보강합니다.
stepback_prompt = ChatPromptTemplate.from_messages([
("system", """주어진 질문에 대해 한 단계 뒤로 물러서서, 더 넓은 범위의 상위 질문을 생성하세요.
상위 질문은 원래 질문에 답하는 데 필요한 배경 지식을 포함해야 합니다."""),
("human", "원본 질문: {question}\n\n상위 질문:"),
])
stepback_chain = stepback_prompt | llm | StrOutputParser()
def stepback_retrieve(question: str, retriever):
stepback_question = stepback_chain.invoke({"question": question})
# 원본 질문 + 상위 질문 모두로 검색
original_results = retriever.invoke(question)
stepback_results = retriever.invoke(stepback_question)
# 중복 제거 후 합산
all_docs = []
seen = set()
for doc in original_results + stepback_results:
doc_id = hash(doc.page_content[:200])
if doc_id not in seen:
seen.add(doc_id)
all_docs.append(doc)
return all_docs
results = stepback_retrieve(
"Self-RAG에서 Reflection Token의 ISREL은 어떤 역할을 하나요?",
retriever,
)
기법 조합 전략
이 기법들은 단독으로 사용할 수도 있지만, 조합하면 더 강력합니다.
| 조합 | 효과 | 적합한 경우 |
|---|
| Multi-Query + Reranking | 다양한 관점 검색 + 정밀 정렬 | 일반적인 품질 향상 |
| Decomposition + Multi-Query | 분해된 각 질문에 다중 쿼리 | 매우 복잡한 질문 |
| Step-back + Query Rewriting | 배경 지식 + 최적화된 쿼리 | 전문 도메인 질문 |
# 예시: Step-back + Multi-Query 조합
def combined_retrieve(question: str, retriever):
# 1. Step-back으로 상위 질문 생성
stepback_q = stepback_chain.invoke({"question": question})
# 2. 원본 + 상위 질문 모두에 Multi-Query 적용
original_results = multi_query_retrieve(question, retriever)
stepback_results = multi_query_retrieve(stepback_q, retriever)
# 3. 중복 제거 후 합산
all_docs = []
seen = set()
for doc in original_results + stepback_results:
doc_id = hash(doc.page_content[:200])
if doc_id not in seen:
seen.add(doc_id)
all_docs.append(doc)
return all_docs
시작 추천: Multi-Query로 시작하세요. 복잡한 비교/분석 질문이 많다면 Sub-query Decomposition을, 전문 도메인 질문이 많다면 Step-back Prompting을 추가하세요.
참고 논문
| 논문 | 학회/연도 | 링크 |
|---|
| Take a Step Back: Evoking Reasoning via Abstraction (Zheng et al.) | ICLR 2024 | arXiv 2310.06117 |
| Least-to-Most Prompting Enables Complex Reasoning (Zhou et al.) | ICLR 2023 | arXiv 2205.10625 |