Q&A 패턴
Q&A 패턴
데이터 위에서 질의응답을 구현하는 방법은 목표에 따라 여러 갈래가 있어요. 시맨틱 검색 같은 기본 패턴부터 멀티 문서 질의·시계열 질의 같은 복잡한 패턴까지, 자주 쓰이는 Q&A 패턴을 하나씩 살펴볼게요.
출처: 공식문서
시맨틱 검색
LlamaIndex의 가장 기본적인 사용 예는 시맨틱 검색이에요. 시작을 위해 간단한 인메모리 벡터 스토어를 제공하지만, 벡터 스토어 통합 중 아무거나 선택할 수도 있어요.
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)
요약
요약 질의는 LLM이 답을 종합하기 위해 많고 많은 문서를(어쩌면 대부분을) 순회해야 해요. 예를 들어 이런 질의가 있을 수 있어요.
- "이 텍스트 모음의 요약은 뭔가요?"
- "X라는 사람의 회사 경험에 대해 요약해 주세요."
이런 유스 케이스에는 요약 인덱스(summary index)가 잘 맞아요. 요약 인덱스는 기본적으로 모든 데이터를 순회하죠.
경험상 response_mode="tree_summarize"로 설정하면 요약 결과가 더 좋아져요.
index = SummaryIndex.from_documents(documents)
query_engine = index.as_query_engine(response_mode="tree_summarize")
response = query_engine.query("<summarization_query>")
구조화 데이터에 대한 질의
LlamaIndex는 Pandas DataFrame이든 SQL 데이터베이스든 구조화 데이터에 대한 질의를 지원해요. Text-to-SQL 가이드와 SQL·Pandas 데모가 준비돼 있죠.
이종 데이터 라우팅
LlamaIndex는 RouterQueryEngine으로 이종 데이터 소스에 걸친 라우팅도 지원해요. 예를 들어 질의를 특정 문서나 하위 인덱스로 '라우팅'하고 싶은 경우에 쓸 수 있죠.
먼저 각 데이터 소스에 대해 하위 인덱스를 만든 다음, 각 인덱스에 대응하는 쿼리 엔진을 만들고, 각 쿼리 엔진에 설명을 달아 QueryEngineTool을 얻어요.
from llama_index.core import TreeIndex, VectorStoreIndex
from llama_index.core.tools import QueryEngineTool
...
# define sub-indices
index1 = VectorStoreIndex.from_documents(notion_docs)
index2 = VectorStoreIndex.from_documents(slack_docs)
# define query engines and tools
tool1 = QueryEngineTool.from_defaults(
query_engine=index1.as_query_engine(),
description="Use this query engine to do...",
)
tool2 = QueryEngineTool.from_defaults(
query_engine=index2.as_query_engine(),
description="Use this query engine for something else...",
)
그 위에 RouterQueryEngine을 정의해요. 기본적으로 LLMSingleSelector를 라우터로 쓰는데, 이 라우터는 주어진 설명을 바탕으로 LLM이 질의를 보낼 가장 좋은 하위 인덱스를 고르게 해요.
from llama_index.core.query_engine import RouterQueryEngine
query_engine = RouterQueryEngine.from_defaults(
query_engine_tools=[tool1, tool2]
)
response = query_engine.query(
"In Notion, give me a summary of the product roadmap."
)
비교(Compare/Contrast) 질의
ComposableGraph 안의 query transformation 모듈로 비교·대조 질의를 명시적으로 수행할 수 있어요.
from llama_index.core.query.query_transform.base import DecomposeQueryTransform
decompose_transform = DecomposeQueryTransform(
service_context.llm, verbose=True
)
이 모듈은 복잡한 질의를 기존 인덱스 구조에 맞는 더 단순한 질의로 쪼개는 걸 도와줘요. 비교·대조 질의 수행 여부를 LLM이 추론하도록 맡길 수도 있어요(아래 멀티 문서 질의 참고).
멀티 문서 질의
앞서 설명한 명시적 합성·라우팅 흐름 외에도, LlamaIndex는 SubQuestionQueryEngine 클래스로 더 일반적인 멀티 문서 질의를 지원해요. 주어진 질의에 대해 이 쿼리 엔진은 최종 답변을 합성하기 전에 하위 문서들에 대한 하위 질의를 담은 'query plan'을 만들어내죠.
먼저 문서·데이터 소스마다 인덱스를 정의하고 QueryEngineTool로 감싸요.
from llama_index.core.tools import QueryEngineTool
query_engine_tools = [
QueryEngineTool.from_defaults(
query_engine=sept_engine,
name="sept_22",
description="Provides information about Uber quarterly financials ending September 2022",
),
QueryEngineTool.from_defaults(
query_engine=june_engine,
name="june_22",
description="Provides information about Uber quarterly financials ending June 2022",
),
QueryEngineTool.from_defaults(
query_engine=march_engine,
name="march_22",
description="Provides information about Uber quarterly financials ending March 2022",
),
]
그리고 이 도구들 위에 SubQuestionQueryEngine을 정의해요.
from llama_index.core.query_engine import SubQuestionQueryEngine
query_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=query_engine_tools
)
이 쿼리 엔진은 최종 답변을 합성하기 전에 쿼리 엔진 도구의 임의 하위 집합에 대해 원하는 만큼 하위 질의를 실행할 수 있어요. 그래서 문서 간 비교·대조 질의나 특정 문서에 대한 질의에 특히 잘 맞죠.
멀티 스텝 질의
LlamaIndex는 반복적인 멀티 스텝 질의도 지원해요. 복잡한 질의가 주어지면 처음 하위 질문으로 쪼갠 다음, 반환된 답변을 바탕으로 하위 질문을 순차적으로 만들어가 최종 답변에 도달해요.
예를 들어 "작가가 시작한 액셀러레이터 프로그램의 첫 번째 배치에는 누가 있었나요?"라는 질문이 주어지면, 이 모듈은 먼저 질의를 "작가가 시작한 액셀러레이터 프로그램은 뭐였나요?"라는 더 단순한 초기 질문으로 분해하고, 인덱스에 질의한 뒤 후속 질문을 이어서 던져요.
시계열(Temporal) 질의
LlamaIndex는 시간 이해가 필요한 질의도 지원해요. 두 가지 방식으로 처리해요.
- 질문에 답하기 위한 추가 문맥을 검색하기 위해 노드 간의 시간 관계(prev/next)를 활용해야 하는지 판단하기
- 최신순으로 정렬하고 오래된 문맥을 걸러내기
더 알아보기
이 패턴들을 더 큰 애플리케이션에 엮는 방법은 'Putting It All Together' 이해 섹션에서 확인할 수 있어요. 각 패턴의 하위 모듈(리트리버, 노드 포스트프로세서, 응답 신시사이저)은 모듈 가이드에서 더 깊게 다룹니다.