乐于分享
好东西不私藏

给Agent装个“工具搜索引擎”:多文档RAG的规模化之道

给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 做了什么:

  1. 将 Python 工具对象序列化为字符串表示(工具名称 + 描述)
  2. 对字符串做向量嵌入和索引
  3. 检索时返回原始 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. 核心要点总结

  1. 多文档 = 多工具:每篇文档生成 vector tool + summary tool,所有工具传给 Agent
  2. 小规模直接传递:3 篇论文 = 6 个工具,可直接传入 Agent
  3. 大规模需要 Tool Retrieval:11 篇以上论文的工具数量会导致 prompt 溢出、成本飙升、LLM 决策质量下降
  4. Object Index 抽象:将 Python 工具对象序列化→向量索引→检索时反序列化回对象,实现对工具的 RAG
  5. 可扩展架构:Tool Retriever 模式支持 100 甚至 1000 篇文档,Agent 每步只看到相关工具子集

总结

我们从最简单的 Router 出发,逐步构建了一个完整的多文档 Agentic RAG 系统。每一层能力都在前一层基础上叠加:

  • Router 赋予了动态选择能力
  • Tool Calling 赋予了参数推断能力
  • Reasoning Loop 赋予了多步推理和记忆能力
  • Multi-Document Agent 赋予了跨文档检索和可扩展能力

这些能力组合在一起,使你能够构建通用的、上下文增强的研究助手,能够回答跨多个文档的复杂问题。