Advanced RAG Agent
Advanced RAG Agent
"어떤 메타데이터 필드가 있는지"를 추측하지 않고, 실제로 Document Store를 들여다보며 필드를 파악하고 검색 범위를 좁히는 필터를 직접 만드는 에이전트예요. 메타데이터를 인식하는 RAG 에이전트입니다.
출처: 공식문서
파이프라인에서의 위치
| 필수 초기화 변수 | document_store: 조사하고 가져올 문서 저장소retriever: 관련성 점수 매기기 retriever 또는 검색 Pipeline |
| 필수 실행 변수 | messages: ChatMessage 목록 |
| 출력 변수 | last_message: [doc <short-id>]로 문서를 인용한 답변documents: 에이전트가 검색한 모든 문서(중복 제거됨) |
| API 레퍼런스 | Agent Pack |
| GitHub 링크 | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/agent_pack/src/haystack_integrations/agent_pack/advanced_rag |
| 패키지 이름 | agent-pack-haystack |
:::warning Agent Pack의 일부로, 현재는 실험적입니다. API와 에이전트 아키텍처는 일반적인 폐기 정책을 따르지 않고 어떤 릴리스에서든 변경될 수 있습니다. :::
이 에이전트를 언제 쓰나
고급 RAG 에이전트는 다음 상황에 씁니다.
- 잘 구조화된 메타데이터가 있는 크고 이질적인 코퍼스(많은 주제, 소스, 문서 유형이 섞인)가 있을 때. 에이전트는 그 메타데이터로 검색 범위를 좁혀 관련성 순위만으로 얻는 것보다 더 정확한 결과를 만듭니다.
- "이 파일의 모든 페이지", "소스 X의 모든 것"처럼 메타데이터로 문서의 정확하거나 완전한 부분집합을 검색해야 할 때. 가장 관련성 높은 매칭만 필요한 게 아니라면요.
다음 상황에서는 덜 유용합니다.
- 메타데이터가 없거나, 검색 범위를 좁히는 데 쓸모있는 메타데이터가 없을 때.
- 코퍼스가 작고 동질적이어서 단순 top-k 검색이 이미 올바른 문서를 반환할 때.
설치
pip install agent-pack-haystack arrow
arrow(기본 시스템 프롬프트에서 필요)는 오늘 날짜를 렌더링해서 "최근 5년" 같은 상대 날짜에 대한 필터를 만들 수 있게 해줍니다.
환경 변수에 OPENAI_API_KEY를 설정하세요.
사용 예시
다양한 메타데이터가 있는 코퍼스를 색인하고, 메타데이터를 조사해 필터를 만들고 그 필터로 검색해야만 잘 답할 수 있는 질문을 던져보세요.
from haystack import Document
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.dataclasses import ChatMessage
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack_integrations.agent_pack import create_advanced_rag_agent
document_store = InMemoryDocumentStore()
document_store.write_documents(
[
Document(
content="CRISPR gene editing corrected a hereditary blindness mutation in a clinical trial.",
meta={"category": "science", "year": 2021, "rating": 4.6},
),
Document(
content="A quantum computer demonstrated error-corrected logical qubits.",
meta={"category": "science", "year": 2023, "rating": 4.8},
),
Document(
content="Dolly the sheep became the first mammal cloned from an adult somatic cell.",
meta={"category": "science", "year": 1996, "rating": 4.2},
),
Document(
content="The Berlin Wall fell, a decisive moment in the end of the Cold War.",
meta={"category": "history", "year": 1989, "rating": 4.7},
),
Document(
content="Argentina won the FIFA World Cup final against France on penalties.",
meta={"category": "sports", "year": 2022, "rating": 4.9},
),
],
)
agent = create_advanced_rag_agent(
document_store=document_store,
retriever=InMemoryBM25Retriever(document_store=document_store, top_k=5),
)
result = agent.run(
messages=[ChatMessage.from_user("What science advances happened after 2015?")],
)
print(result["last_message"].text) # the answer, citing documents as [doc <short-id>]
for doc in result["documents"]: # every document the agent retrieved, deduplicated
print(f"[doc {doc.id[:8]}] {doc.meta} :: {doc.content[:60]}")
에이전트는 메타데이터 필드를 나열하고, category 값과 year 범위를 확인한 뒤, {"operator": "AND", "conditions": [{"field": "meta.category", "operator": "==", "value": "science"}, {"field": "meta.year", "operator": ">", "value": 2015}]} 같은 필터를 만들어 그 필터로 검색하고, CRISPR와 양자 관련 문서를 인용하며 답변합니다. 필터링은 선택 사항입니다. 메타데이터가 질문을 좁힐 수 없으면 에이전트는 필터 없이 검색합니다.
:::note
제공하는 검색은 점수 기반이어야 합니다: 키워드(BM25), 임베딩, 또는 하이브리드. 직접 점수 없이 메타데이터로 가져오는 것은 내장 fetch_documents_by_filter 도구가 이미 다룹니다.
:::
단일 retriever 대신 검색 파이프라인 사용하기
다중 컴포넌트 검색 흐름을 쓰려면 검색 Pipeline을 retriever로 전달하고 질의·필터·문서 출력에 대한 매핑을 제공하세요. 예를 들어 reciprocal rank fusion을 사용한 하이브리드 검색은 이렇게 합니다.
from haystack import Pipeline
from haystack.components.embedders import OpenAITextEmbedder
from haystack.components.joiners import DocumentJoiner
from haystack.components.retrievers.in_memory import (
InMemoryBM25Retriever,
InMemoryEmbeddingRetriever,
)
pipeline = Pipeline()
pipeline.add_component(
"bm25_retriever",
InMemoryBM25Retriever(document_store=document_store),
)
pipeline.add_component("text_embedder", OpenAITextEmbedder())
pipeline.add_component(
"embedding_retriever",
InMemoryEmbeddingRetriever(document_store=document_store),
)
pipeline.add_component("joiner", DocumentJoiner(join_mode="reciprocal_rank_fusion"))
pipeline.connect("text_embedder.embedding", "embedding_retriever.query_embedding")
pipeline.connect("bm25_retriever.documents", "joiner.documents")
pipeline.connect("embedding_retriever.documents", "joiner.documents")
agent = create_advanced_rag_agent(
document_store=document_store,
retriever=pipeline,
retrieval_pipeline_input_mapping={
"query": ["bm25_retriever.query", "text_embedder.text"],
"filters": ["bm25_retriever.filters", "embedding_retriever.filters"],
},
retrieval_pipeline_output_mapping={"joiner.documents": "documents"},
)
도구를 단독으로 사용하기
네 개의 document-store 기반 도구(아래 How it works 참고)는 개별적으로 내보내지며 DocumentStoreToolset로 묶여 있어서, 여러분의 Agent에 여러분의 프롬프트로 넣을 수 있습니다.
from haystack_integrations.agent_pack.advanced_rag import DocumentStoreToolset
agent = Agent(
chat_generator=...,
tools=[DocumentStoreToolset(document_store), my_retrieval_tool],
)
지원되는 Document Store
메타데이터 도구는 기본 DocumentStore 프로토콜의 일부가 아닌 Document Store 메서드에 의존합니다: get_metadata_fields_info, get_metadata_field_unique_values, get_metadata_field_min_max. InMemoryDocumentStore와 대부분의 Document Store 통합(OpenSearch, Elasticsearch, Weaviate, Chroma, pgvector, Qdrant, Pinecone, MongoDB Atlas, Astra 등)이 이를 구현합니다.
각 도구는 스토어가 필요한 메서드를 지원하지 않으면 생성 시점에 명확한 오류로 빠르게 실패합니다. 그래서 일부 메서드만 구현한 스토어도 일치하는 도구 부분집합을 사용할 수 있어요.
설정
모든 것은 create_advanced_rag_agent에 대한 키워드 인자로 설정합니다. 모든 파라미터는 키워드 전용입니다. document_store와 retriever만 필수이고 나머지는 선택 사항이에요.
검색
-
document_store는 메타데이터 조사 도구와fetch_documents_by_filter도구가 실행되는 스토어입니다. -
retriever는search_documents도구가 됩니다. 다음 중 하나일 수 있습니다.run메서드가query와filters를 받는 독립형 retriever 컴포넌트, 또는- 검색
Pipeline.
독립형 컴포넌트의 예로는
InMemoryBM25Retriever와TextEmbeddingRetriever로 감싼 임베딩 retriever가 있습니다. -
retrieval_pipeline_input_mapping은 도구 입력을 파이프라인 입력 소켓에 매핑하며 정확히query와filters키를 가져야 합니다. 예:{"query": ["embedder.text"], "filters": ["retriever.filters"]}.retriever가Pipeline일 때 필수입니다. -
retrieval_pipeline_output_mapping은 파이프라인 출력 소켓을 도구 출력에 매핑합니다. 예:{"retriever.documents": "documents"}.retriever가Pipeline일 때만 유효합니다.
모델과 프롬프트
llm은 에이전트 루프를 구동하는 LLM입니다. 기본값은 낮은 추론 노력의OpenAIResponsesChatGenerator("gpt-5.4")입니다.backup_answer_llm은 내장BackupAnswerHook이max_agent_steps로 실행이 끊겼을 때 최선의 답변을 쓰는 데 사용하는 LLM입니다. 기본값은 별도의 낮은 추론 노력OpenAIResponsesChatGenerator("gpt-5.4")입니다.system_prompt는 미리 만들어진 시스템 프롬프트를 재정의합니다.
제한
max_agent_steps는 에이전트 루프를 제한합니다. 기본값은20입니다.max_fetched_docs는fetch_documents_by_filter가 한 번 가져올 때 보여줄 문서 수를 설정합니다. 기본값은10입니다. 필터 가져오기는 retriever의top_k로 제한되지 않으므로, 이 값이 도구 결과를 제한합니다. 점수 기반search_documents도구는 검색 컴포넌트에 설정된top_k로 제한됩니다.
추가 커스터마이징
도구 추가, hooks, State 항목 같은 다른 것을 바꾸려면 반환된 에이전트에서 clone()을 사용하세요. 내장 도구와 훅을 유지하려면 기존 값을 풀어서 사용합니다.
agent = create_advanced_rag_agent(document_store=document_store, retriever=retriever)
customized = agent.clone(
tools=[*agent.tools, my_tool],
hooks={**agent.hooks, "before_llm": [my_hook]},
)
어떻게 동작하나
아키텍처는 다섯 개의 도구를 사용해 세 개의 논리적 단계로 작업하는 단일 Haystack Agent로 구성됩니다.
- 메타데이터 조사. 에이전트는 어떤 메타데이터 필드가 존재하는지 발견한 다음, 그 값이나 범위를 조사합니다.
- 문서 검색. 관련성 기반 검색을 실행하거나(선택적으로 메타데이터 필터로 좁혀서), 메타데이터가 문서를 고유하게 식별하면 직접 문서를 가져옵니다.
- 답변. 검색된 문서만 사용해 답변하고
[doc <short-id>]로 인용합니다.
검색된 모든 문서는 에이전트의 State에 documents 키 아래 축적되고 id로 중복 제거됩니다. 그 결과 agent.run(...)은 답변(last_message)과 실행 중 검색된 전체 문서 집합을, 표준 Agent 출력 messages, step_count, token_usage, tool_call_counts와 함께 모두 반환합니다. 답변은 각 문서를 id의 처음 8자로 인용합니다(예: [doc a1b2c3d4]). doc.id.startswith(...)를 사용해 인용을 반환된 목록과 대조하세요.
max_agent_steps로 답변이 쓰이기 전에 실행이 끊기면 BackupAnswerHook(after_run 훅)이 지금까지 모은 증거로 최선의 답변을 만들기 위해 LLM을 한 번 더 호출하므로, last_message는 항상 텍스트 답변을 담습니다.
에이전트의 도구:
| 도구 | 무엇인가 | 무엇을 하나 |
|---|---|---|
list_metadata_fields |
ListMetadataFieldsTool |
모든 메타데이터 필드와 그 유형을 나열합니다. 시스템 프롬프트가 에이전트에게 이걸 먼저 호출하라고 지시합니다. |
get_metadata_field_values |
GetMetadataFieldValuesTool |
필드의 고유한 값을 반환해 필터가 실제로 존재하는 값을 사용하게 합니다. 고카디널리티(값이 많은) 필드는 나열이 제한되며, 스토어가 제공하면 총 개수가 보고됩니다. |
get_metadata_field_range |
GetMetadataFieldRangeTool |
숫자 또는 정렬 가능한 필드(예: 연도, 평점, ISO 날짜)의 최소·최대를 반환합니다. |
fetch_documents_by_filter |
FetchDocumentsByFilterTool |
관련성 점수가 필요 없을 때(예: 알려진 제목이나 파일) 메타데이터 필터를 통해 직접 문서를 가져옵니다. |
search_documents |
retriever를 감싼 ComponentTool, 또는 검색 파이프라인을 감싼 PipelineTool |
질의에 대한 문서를 관련성으로 검색하며, 선택적으로 메타데이터 필터로 좁힙니다. 검색 컴포넌트의 top_k로 제한됩니다. 빈 결과는 에이전트가 필터를 완화하도록 유도합니다. |
fetch_documents_by_filter는 결과를 읽는 순서로 반환하며, 부모 파일별로 문서를 그룹화하고 분할 또는 페이지 순으로 정렬합니다. 호출당 최대 max_docs를 보여주고 총 일치 수를 보고하므로, 더 큰 일치 집합은 도구의 offset 입력으로 페이지네이션할 수 있어요. 필터로 문서 수를 셀 수 있는 스토어에서는 지나치게 넓은 필터가 어떤 문서도 가져오기 전에 거부되고, 그 거부는 에이전트가 필터를 좁혀 회복하는 오류로 LLM에 반환됩니다.
필터 문법
LLM이 유효한 Haystack 필터를 일관되게 만들도록 돕기 위해, 필터 문법은 시스템 프롬프트 전체에 두지 않고 search_documents와 fetch_documents_by_filter의 filters 파라미터 설명에 포함됩니다. 모델은 도구 사용 시점에 문맥적으로 받게 되죠.
- 단일 조건:
{"field": "meta.category", "operator": "==", "value": "science"} - 비교 연산자:
==, !=, >, >=, <, <=, in, not in - 논리 그룹:
{"operator": "AND"|"OR"|"NOT", "conditions": [...]}(중첩 가능) - 필드 이름은
meta.접두사를 붙여야 합니다.
시스템 프롬프트는 워크플로 규칙을 추가합니다: 먼저 필드를 조사하고, 필터링 전에 값을 검증하며, 검색이 빈 결과를 주면 필터를 완화하라는 것이죠.
더 알아보기
- Agent — Haystack 에이전트의 기본 개념
- Metadata Filtering — 필터 문법
- Hybrid Retrieval — 하이브리드 검색