给Agent装个“工具搜索引擎”:多文档RAG的规模化之道
你的Agent能完美回答一篇论文里的问题。但当你想让它“对比一下这3篇论文的方法论差异”时——它要么答不全,要么干脆报错。
这不是你的Agent变笨了。
是工具太多了。
本课要解决的核心问题:当文档从1篇变成11篇,工具从2个变成22个,Agent如何在不“撑爆”上下文窗口的前提下,精准找到它需要的那个工具?
答案是给Agent装一个“工具搜索引擎”。
1. 环境准备
-
导入 OpenAI API Key -
导入 nest_asyncio -
下载多篇 ICLR 论文
2. 第一阶段:3 文档 Agent
2.1 下载论文
以下 MetaGPT、LongLoRA、Self-RAG 3 篇论文为例:
2.2 为每篇论文构建工具
复用前几课的 get_doc_tools 辅助函数,为每篇论文生成 vector tool + summary tool:
from utils import get_doc_toolsfrom pathlib import Pathpaper_to_tools = {}for paper in papers: vector_tool, summary_tool = get_doc_tools(paper) paper_to_tools[paper] = [vector_tool, summary_tool]
工具结构:
MetaGPT → [vector_tool, summary_tool]LongLoRA → [vector_tool, summary_tool]Self-RAG → [vector_tool, summary_tool] ↓ 共 6 个工具(flat list)
2.3 构建 Agent
将 6 个工具扁平化后传给 Agent:
from llama_index.core.agent import FunctionCallingAgentWorker, AgentRunnerall_tools = [t for tools in paper_to_tools.values() for t in tools]print(len(all_tools)) # 6agent_worker = FunctionCallingAgentWorker.from_tools( all_tools, llm=llm, verbose=True)agent = AgentRunner(agent_worker)
2.4 测试跨文档查询
查询一:单文档具体内容
response = agent.query("Tell me about the eval dataset used in LongLoRA, ""and then tell me about the eval results.")
Agent 选择了 LongLoRA 的工具,返回:
One of the eval datasets used is the Feature19 test split. Eval results show performance of LongLoRA models…
查询二:跨文档摘要
response = agent.query("Give me a summary of both Self-RAG and LongLoRA.")
执行过程:
Step 1: 调用 selfrag 的 summary_tool → 返回 Self-RAG 摘要Step 2: 调用 longlora 的 summary_tool → 返回 LongLoRA 摘要Step 3: 合成最终响应
Agent 自动识别这是一个跨两篇论文的摘要请求,依次调用各自的摘要工具后合成答案。
3. 第二阶段:11 文档 Agent 与规模化挑战
3.1 规模化带来的问题
将文档扩展到 11 篇 ICLR 2024 论文(MetaGPT、LongLoRA、LoftQ、SWE-bench、Self-RAG 等),每篇 2 个工具 = 22 个工具。
直接将所有工具塞进 LLM prompt 会导致三大问题:
┌─────────────────────────────────────────────────────┐│ 将所有工具直接放入 Prompt 的问题 │├─────────────────────────────────────────────────────┤│ ││ 1. 上下文溢出 ││ → 工具数量超出 LLM 上下文窗口 ││ ││ 2. 成本与延迟飙升 ││ → prompt 中 token 数量激增 ││ ││ 3. LLM 决策质量下降 ││ → 选择过多,LLM 无法准确挑选正确工具 ││ │└─────────────────────────────────────────────────────┘
3.2 解决方案:Tool Retrieval(工具级 RAG)
核心思想:不对文本做 RAG,而是对工具做 RAG。
传统 RAG: 用户查询 → 检索相关文本块 → 喂给 LLMTool Retrieval: 用户查询 → 检索相关工具 → 喂给 Agent
工作流程:
用户查询 "Compare MetaGPT and SWE-bench" │ ▼ ┌─────────────┐ │ 工具索引 │ ← 对工具描述做向量索引 │ (Object │ │ Index) │ └──────┬──────┘ │ Top-K 检索 ▼ [metagpt_summary_tool, swebench_summary_tool, ...] │ ▼ ┌─────────────┐ │ Agent │ ← 只看到相关工具,而非全部 22 个 │ Reasoning │ └─────────────┘
3.3 Object Index — 索引 Python 对象
from llama_index.core import VectorStoreIndexfrom llama_index.core.objects import ObjectIndextool_index = ObjectIndex.from_objects( all_tools, # Python 工具对象列表 index=VectorStoreIndex, # 底层用向量索引)
Object Index 做了什么:
-
将 Python 工具对象序列化为字符串表示(工具名称 + 描述) -
对字符串做向量嵌入和索引 -
检索时返回原始 Python 工具对象(反序列化)
# 从 Object Index 创建检索器tool_retriever = tool_index.as_retriever(similarity_top_k=3)# 检索与 "MetaGPT" 相关的工具retrieved_tools = tool_retriever.retrieve("Tell me about the eval dataset in MetaGPT and SWE-bench")
检索结果示例:
Tool 1: summary_tool_metagpt ✅ 相关Tool 2: summary_tool_unrelated ❌ 不相关(嵌入质量依赖)Tool 3: summary_tool_swebench ✅ 相关
注意: 工具检索的质量取决于嵌入模型。不相关工具的出现意味着可以进一步优化嵌入或检索策略。说白了,就是把Python工具对象变成可索引的文本,用的时候再变回来——像给工具拍了张‘证件照’,检索时凭照片认领。
3.4 构建 Tool-Retrieval 增强的 Agent
agent_worker = FunctionCallingAgentWorker.from_tools( tool_retriever=tool_retriever, # ← 传入检索器而非全部工具 llm=llm, system_prompt="""You are an agent designed to answer queries over a set of given papers. Please always use the tools provided to answer a question, rather than relying on your own knowledge.""", verbose=True)agent = AgentRunner(agent_worker)
关键变化:
-
from_tools(all_tools)→from_tools(tool_retriever=tool_retriever) -
可选添加 system_prompt给 Agent 额外指导 -
Agent 每步推理时,自动通过 tool_retriever 获取相关工具 就是给Agent配一个‘工具图书馆管理员’:用户提问时,管理员先去工具库里检索,只把相关的3-5个工具递给Agent。
3.5 测试跨文档对比查询
查询一:MetaGPT vs SWE-bench
response = agent.query("Tell me about the eval dataset used in MetaGPT ""and compare it against SWE-bench.")
执行过程:
Step 1: Tool Retrieval → 检索到 metagpt 工具 + swebench 工具Step 2: 调用 metagpt summary_tool → 获取 MetaGPT 评估数据集信息Step 3: 调用 swebench summary_tool → 获取 SWE-bench 评估数据集信息Step 4: 合成对比回答
查询二:LongLoRA vs LoftQ 方法对比
response = agent.query("Compare and contrast the two LoRA papers, LongLoRA and LoftQ, ""and analyze the approach in each paper first.")
执行过程:
Step 1: Tool Retrieval → 检索到 LongLoRA 工具 + LoftQ 工具Step 2: 调用 longlora summary_tool → 获取 LongLoRA 方法概述Step 3: 调用 loftq summary_tool → 获取 LoftQ 方法概述Step 4: LLM 对比分析两种方法,合成最终回答
Agent 成功地将两个 LoRA 变体的方法进行了对比分析。
5. 核心要点总结
-
多文档 = 多工具:每篇文档生成 vector tool + summary tool,所有工具传给 Agent -
小规模直接传递:3 篇论文 = 6 个工具,可直接传入 Agent -
大规模需要 Tool Retrieval:11 篇以上论文的工具数量会导致 prompt 溢出、成本飙升、LLM 决策质量下降 -
Object Index 抽象:将 Python 工具对象序列化→向量索引→检索时反序列化回对象,实现对工具的 RAG -
可扩展架构:Tool Retriever 模式支持 100 甚至 1000 篇文档,Agent 每步只看到相关工具子集
总结
我们从最简单的 Router 出发,逐步构建了一个完整的多文档 Agentic RAG 系统。每一层能力都在前一层基础上叠加:
-
Router 赋予了动态选择能力 -
Tool Calling 赋予了参数推断能力 -
Reasoning Loop 赋予了多步推理和记忆能力 -
Multi-Document Agent 赋予了跨文档检索和可扩展能力
这些能力组合在一起,使你能够构建通用的、上下文增强的研究助手,能够回答跨多个文档的复杂问题。
夜雨聆风