Haystack Agent Pack 实战指南:构建 Advanced RAG 与 Deep Research Agent
【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack
本篇技术文章围绕 Haystack Agent Pack 集成包展开:它是构建在 HaystackAgent组件之上的一套「开箱即用」Agent 工厂,提供 Advanced RAG Agent(基于文档库的检索增强问答)与 Deep Research Agent(多子研究者并行深度调研)两种预制 Agent。读完本文,你将掌握两个工厂函数的完整参数语义、Agent Pack 对文档库元数据内省接口的依赖关系、FetchDocumentsByFilterTool等工具的内部行为,以及BackupAnswerHook这类钩子如何与 Haystack Agent 的after_run钩子点配合,保证 Agent 运行在步数耗尽时也能产出兜底答案。
Agent Pack 的定位与源码边界
Agent Pack 是独立发布的集成包(haystack_integrations.agent_pack命名空间),不在本仓库haystack/核心源码树内,本仓库中它的存在体现为 API 参考文档 agent_pack.md。它的全部能力都建立在本仓库提供的基础设施之上:
Agent组件:实现于 agent.py,提供带工具调用循环、状态管理(State)与钩子点的 Agent 运行时;Tool/Toolset:Agent Pack 的检索与元数据工具均以Tool为基类,并以DocumentStoreToolset形式成组交付;Hook协议:实现于 protocol.py,定义before_run、before_llm、before_tool、after_tool、on_exit、after_run六个钩子点(见 HookPoint 定义),Agent Pack 的BackupAnswerHook正是注册在after_run钩子点上。
理解这层基础,才能理解 Agent Pack 各参数的设计意图。
Agent 运行循环:Agent Pack 各参数的作用点
从源码看,Agent.run的核心是一个受max_agent_steps约束的循环(run 方法实现):
before_run 钩子 └─ while counter < max_agent_steps: 调用 LLM → 执行模型请求的工具调用 → counter += 1 (任一步满足退出条件则 break) 若循环耗尽步数未 break → exit_reason 置为 "max_agent_steps" after_run 钩子 返回状态中的公开输出(messages、last_message、step_count、token_usage、tool_call_counts、exit_reason 等)两个关键点与 Agent Pack 直接相关:
max_agent_steps是循环的硬上限。当 Agent 在步数耗尽时停在工具调用或工具结果上(没有助手文本答案)时,源码会把exit_reason设为"max_agent_steps"(见 循环耗尽分支),随后仍会执行AFTER_RUN钩子——这就是BackupAnswerHook能介入的唯一时机;clone支持以覆盖参数方式派生新 Agent(clone 实现):它通过内省__init__签名取出当前实例的所有参数,再用传入的 overrides 替换,因此agent.clone(tools=[*agent.tools, my_tool])这类写法可以安全地给 Agent Pack 返回的预制 Agent 追加工具或钩子而不改动原对象。Agent Pack 文档中「进一步定制」的推荐做法即基于此。
Advanced RAG Agent:create_advanced_rag_agent
函数签名
create_advanced_rag_agent( *, document_store: DocumentStore, retriever: TextRetriever | Pipeline | None = None, retrieval_pipeline_input_mapping: dict[str, list[str]] | None = None, retrieval_pipeline_output_mapping: dict[str, str] | None = None, llm: ChatGenerator | None = None, backup_answer_llm: ChatGenerator | None = None, system_prompt: str | None = None, max_agent_steps: int = 20, max_fetched_docs: int = 10, ) -> Agent其设计目标与普通 RAG 管线不同:Agent 回答问题时不靠开发者预先假设文档库中有哪些元数据字段,而是可以先内省文档库(字段、取值、数值范围),在元数据确实有助于缩小检索范围时动态构造 Haystack filter;当元数据帮不上忙时,仍可直接做无过滤的相关性检索。答案中对引用文档以 id 前 8 个字符标注,例如[doc a1b2c3d4]。
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
document_store | DocumentStore | 元数据内省工具与fetch_documents_by_filter工具操作的文档库。必须实现元数据内省方法(get_metadata_fields_info、get_metadata_field_unique_values、get_metadata_field_min_max),否则无法工作。 |
retriever | TextRetriever \| Pipeline \| None | 实际必填。成为search_documents工具。可传满足TextRetriever协议的独立检索组件(即run方法接受query与filters,如InMemoryBM25Retriever,或用TextEmbeddingRetriever包装的向量检索器),也可以传自定义检索Pipeline(如 embedder -> retriever 或混合检索)。应基于相关性打分检索(关键词或向量)——无打分的直接取回已由内置fetch_documents_by_filter工具覆盖。 |
retrieval_pipeline_input_mapping | dict[str, list[str]] \| None | retriever为Pipeline时必填:把工具输入映射到管线输入 socket,键必须恰好为"query"和"filters",例如{"query": ["embedder.text"], "filters": ["retriever.filters"]}。 |
retrieval_pipeline_output_mapping | dict[str, str] \| None | retriever为Pipeline时可选:把管线输出 socket 映射到工具输出,例如{"retriever.documents": "documents"}。 |
llm | ChatGenerator \| None | 驱动 Agent 循环的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4"),低推理强度。 |
backup_answer_llm | ChatGenerator \| None | 内置BackupAnswerHook在运行被max_agent_steps截断时撰写兜底答案所用的 LLM。默认是另一个独立的OpenAIResponsesChatGenerator("gpt-5.4"),低推理强度。 |
system_prompt | str \| None | 覆盖预制的系统提示词。 |
max_agent_steps | int | Agent 循环最大步数(默认 20)。若循环在该上限前被截断且尚未写出答案,after_run钩子(BackupAnswerHook)会额外发起一次 LLM 调用,基于已收集的证据尽力给出答案,从而保证last_message始终携带文本答案。 |
max_fetched_docs | int | fetch_documents_by_filter单次取回展示给 Agent 的文档数上限(默认 10)。过滤取回不受检索器top_k约束,因此用它来封顶工具结果;而打分检索的search_documents则由检索组件自身配置的top_k限制。 |
返回值与调用方式
工厂函数返回一个Agent。调用方式:
agent = create_advanced_rag_agent(document_store=doc_store, retriever=retriever) result = agent.run(messages=[ChatMessage.from_user(question)]) answer = result["last_message"] # ChatMessage,答案文本(含 [doc xxxxxxxx] 引用) docs = result["documents"] # 本次运行中取回的全部文档:按 id 去重、按首次取回顺序排列 # 同时还有标准 Agent 输出:messages、step_count、token_usage、tool_call_counts这些标准输出与源码中Agent的运行元数据状态键一一对应(step_count、token_usage、tool_call_counts等,见 运行元数据定义),exit_reason可用于判断本次运行是正常以文本退出("text")还是被步数耗尽截断("max_agent_steps")。
需要追加工具或钩子时,使用Agent.clone:
agent = agent.clone(tools=[*agent.tools, my_tool])文档库元数据内省接口
Advanced RAG Agent 对document_store的要求是本仓库核心能力的一个体现:文档库需要实现元数据内省方法。以内置的InMemoryDocumentStore为例(document_store.py):
| 方法 | 作用 | 实现要点 |
|---|---|---|
get_metadata_fields_info | 返回库中所有元数据字段及其类型 | 类型从存储值推断:bool→boolean、int→int、float→float、其余 →keyword |
get_metadata_field_min_max | 返回某元数据字段的最小/最大值 | 支持带或不带meta.前缀的字段名;无可比较值时返回{"min": None, "max": None} |
get_metadata_field_unique_values | 返回某字段的去重取值 | 支持search_term子串匹配、from_/size分页,并支持filters限定参与统计的文档 |
count_documents_by_filter | 统计命中过滤条件的文档数 | FetchDocumentsByFilterTool的拒绝机制依赖此方法 |
这套接口的实际意义:Agent 可以先调用list_metadata_fields知道「库里有哪些字段、什么类型」,再调用get_metadata_field_values或get_metadata_field_range知道「字段里有哪些值、数值范围多大」,最后据此写出合法的 Haystack filter 传给search_documents或fetch_documents_by_filter,避免了传统 RAG 中「开发者必须提前硬编码元数据假设」的脆弱性。其他集成文档库(pgvector、qdrant、weaviate 等)在各自参考文档中也声明了对应的内省方法支持。
Advanced RAG 的四个工具与 DocumentStoreToolset
三个元数据内省工具
三者均为Tool基类的子类,构造时都接收一个document_store,并在 store 未实现相应内省方法时抛出ValueError,且都提供to_dict/from_dict序列化方法:
ListMetadataFieldsTool:列出文档库所有元数据字段及类型,依赖get_metadata_fields_info;GetMetadataFieldValuesTool:返回某元数据字段的去重取值,依赖get_metadata_field_unique_values;GetMetadataFieldRangeTool:返回某元数据字段的最小/最大值,依赖get_metadata_field_min_max。
FetchDocumentsByFilterTool
这是与打分检索互补的「直接取回」工具,按元数据 filter 从文档库取文档,不做相关性排序——适合「已知确切标题或源文件」这类按身份取回的场景,无需走相关性搜索。
__init__( document_store: DocumentStore, max_docs: int = 10, max_fetch_factor: int = 10, )document_store:取文档的文档库;max_docs:每次取回展示给 Agent 的文档数上限。过滤取回没有检索器top_k约束,因此用此值封顶工具结果。LLM 可通过工具的可选max_docs输入请求更少的文档,但不能超过该上限;max_fetch_factor:允许命中的文档数达到max_docs的多少倍后直接拒绝取回(前提是 store 支持count_documents_by_filter)。拒绝会以错误形式呈现给 LLM,提示其收窄 filter 后重试,避免一次取回过多无关文档。
行为上的两个细节值得注意:
- 阅读顺序重排:取回的文档先按父文件分组(依次尝试
file_name/file_path/source_id元数据),再按文件内位置排序(依次尝试split_id/split_idx_start/page_number),使用文档实际携带的字段; - 分页:命中集大于
max_docs时分页返回,每次返回一页加总命中数,工具提供offset输入继续下一页。
DocumentStoreToolset
DocumentStoreToolset(继承Toolset)把上述三个内省工具与FetchDocumentsByFilterTool打包为一个对象,可直接交给Agent(或与检索工具组合):
DocumentStoreToolset(document_store: DocumentStore, max_fetched_docs: int = 10)构造时要求 store 实现全部三个内省方法;max_fetched_docs即FetchDocumentsByFilterTool.max_docs的取值。同样提供to_dict/from_dict序列化。
BackupAnswerHook:步数耗尽时的兜底答案
BackupAnswerHook是 Advanced RAG Agent 内置在after_run钩子点上的钩子,解决的问题很具体:Agent 耗尽max_agent_steps时,运行可能停在一个工具调用或工具结果上而非助手文本答案上,此时只有after_run钩子会执行。该钩子检测到这一情形后,对当前对话发起一次 LLM 调用,基于已收集的证据撰写一个尽力而为的最终答案并追加到对话中。
其 API 面如下:
__init__(chat_generator: ChatGenerator) # 撰写兜底答案的 LLM warm_up() # Agent warm_up 时调用,准备生成器 close() # Agent close 时调用,释放生成器资源 to_dict() / from_dict(data) # 序列化/反序列化 run(state: State) -> None # 运行结束无答案时追加尽力而为的最终答案对照本仓库源码可以确认其生命周期机制:Agent.warm_up会依次预热工具、钩子和聊天生成器(warm_up 实现),Agent.close会关闭钩子与生成器(close 实现);Hook 协议明确允许钩子实现可选的warm_up/close生命周期方法,由 Agent 统一调用。这也解释了为什么create_advanced_rag_agent中backup_answer_llm独立于主llm:兜底答案的生成器在 Agent 生命周期中独立预热与释放。
Deep Research Agent:create_deep_research_agent
函数签名
create_deep_research_agent( *, llm: ChatGenerator | None = None, system_prompt: str | None = None, max_agent_steps: int = 8, brief_llm: ChatGenerator | None = None, researcher_llm: ChatGenerator | None = None, search_tool: Tool | None = None, page_summary_llm: ChatGenerator | None = None, report_llm: ChatGenerator | None = None, max_researcher_steps: int = 20, max_concurrent_researchers: int = 5, max_subtopics: int = 5, max_page_chars: int = 50000, ) -> Agent这是一个「规划者 + 多个并行子研究者」的调研 Agent:顶层 LLM 负责规划调查并委派子问题,每个子研究者各自运行自己的「搜索/阅读/思考」循环。同样支持用Agent.clone定制(agent.clone(tools=[*agent.tools, my_tool]))。
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
llm | ChatGenerator \| None | 规划调查并委派子问题的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4")。 |
system_prompt | str \| None | 覆盖预制系统提示词。其中的{{ max_subtopics }}会被替换为max_subtopics的实际值。 |
max_agent_steps | int | Agent 循环最大步数(默认 8),对应「反思 -> 委派」的轮数。 |
brief_llm | ChatGenerator \| None | 把用户查询改写成聚焦研究简报的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4")。 |
researcher_llm | ChatGenerator \| None | 驱动每个子研究者「搜索/阅读/思考」循环的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4-mini")。 |
search_tool | Tool \| None | 每个子研究者使用的网络搜索工具。默认TavilyWebSearchTool(top_k=10),需要安装tavily-haystack。预制研究者提示词中把该工具称为web_search。 |
page_summary_llm | ChatGenerator \| None | read_url工具内部用于针对问题摘要所抓网页的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4-mini")。 |
report_llm | ChatGenerator \| None | 把研究简报加收集到的笔记整合成最终报告的 LLM。默认OpenAIResponsesChatGenerator("gpt-5.4")。 |
max_researcher_steps | int | 每个子研究者 Agent 循环的最大步数(默认 20)。 |
max_concurrent_researchers | int | 同时运行的子研究者数量上限(默认 5)。 |
max_subtopics | int | Agent 可委派的子问题数量上限,即调研广度(默认 5)。 |
max_page_chars | int | 摘要前送入摘要器的原始网页字符数上限(默认 50000)。 |
返回值
result = agent.run(messages=[ChatMessage.from_user(question)]) # result["report"] # str,最终 markdown 报告(主要输出) # result["brief"] # str,研究简报(中间产物) # result["notes"] # list[str],子研究者收集的研究笔记(中间产物) # 另含标准 Agent 输出:messages、last_message、step_count、token_usage、tool_call_counts使用建议与适用前提
结合参考文档与核心源码,落地时有几点值得注意:
- 文档库选型:Advanced RAG Agent 要求文档库实现三个元数据内省方法。
InMemoryDocumentStore已完整实现(见上节),生产环境应选择同样声明支持这些方法的集成文档库; - 检索路径分工:把「按相关性找」交给
search_documents(受top_k控制),把「按条件精确取」交给fetch_documents_by_filter(受max_fetched_docs与max_fetch_factor保护),避免把无打分的过滤取回当作检索手段; - 步数预算:Advanced RAG 默认 20 步、Deep Research 顶层默认 8 步 + 每子研究者 20 步,均可通过参数调整。步数耗尽时 Advanced RAG 有
BackupAnswerHook兜底,但兜底答案质量受限于已收集证据,仍应把预算设得合理; - 依赖前提:本文所有默认模型名(如
gpt-5.4)与TavilyWebSearchTool依赖均以 version-2.21 参考文档描述为准,实际部署前需确认所用集成包版本中这些默认值是否变化;deep_research_agent的默认搜索工具需要额外安装tavily-haystack; - 定制入口:两个工厂返回的都是标准
Agent,任何超出工厂参数的需求(加工具、加钩子、换提示词)都应通过Agent.clone或直接把 Agent 嵌入Pipeline解决,这与 Agent 组件文档 所描述的组件化扩展方式一致。
小结
Agent Pack 在 Haystack 的Agent运行时(工具循环、State、钩子点、clone)之上提供了两种可直接投产的 Agent 形态:Advanced RAG Agent 通过元数据内省工具让检索条件「可发现」而非「硬编码」,并用BackupAnswerHook保证步数耗尽时仍有答案;Deep Research Agent 则以「简报 -> 并行子研究者 -> 报告」的多 LLM 分工结构实现可配置的调研广度与并发。两者均以标准Agent对象返回,天然融入 Haystack 的管线与序列化体系。
【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考