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' 이해 섹션에서 확인할 수 있어요. 각 패턴의 하위 모듈(리트리버, 노드 포스트프로세서, 응답 신시사이저)은 모듈 가이드에서 더 깊게 다룹니다.