第 11 章 AI AgentAgentic Patterns工具发现

第 11 章 工具发现:让 Agent 在几百个工具里找到对的

工具一多,Agent 就"迷路"

上一章把接口设计好了。但还有一道坎:工具多了,Agent 怎么找到该用的那一个?

真实系统里工具不是几个,是几十上百个。MCP 生态里,一个配置了 7 个 server 的环境,光工具描述就能吃掉 67k token。全都塞进上下文,Agent 连"有哪些工具"都看不完,更别说挑对;不塞进去,Agent 又不知道有这些工具。这就像把整个图书馆的书单印在一张纸上让读者找书,纸比书还厚。

这一章讲四个模式,从"怎么浏览"到"怎么统一",回答"工具发现"的四个层次:

  1. Progressive Tool Discovery(渐进发现):把工具组织成文件系统,按需往下钻。
  2. Tool Search Lazy Loading(工具搜索懒加载):不预载全部工具,搜索到了才加载。
  3. Static Service Manifest(静态服务清单):在众所周知的 URL 上挂一份机器可读的清单。
  4. Unified Tool Gateway(统一工具网关):所有工具走一个网关,一次接入全部可用。

模式一:渐进发现(Progressive Tool Discovery)

问题

工具目录大到一定程度(几十到上千个),把所有工具定义一次性载入上下文就是灾难:

  • 绝大多数工具在当前任务里根本用不到,预载纯属浪费。
  • 上下文被工具定义挤占,真正干活的空间变小。
  • 工具越多,模型越难从一堆描述里挑出对的那个。

方案

把工具组织成一个文件系统式的层级,让 Agent 通过"逛目录"来按需发现能力。

三个层次的发现粒度,从省 token 到完整:

  1. 只看名字:浏览用的最小上下文。
  2. 名字 + 描述:够判断这个工具是干嘛的。
  3. 完整定义 + schema:确定要用时,才加载全部参数细节。

工具按集成/领域组织成目录,Agent 可以:

# Agent 的工作流
1. list_directory("./servers/")
   → 返回: ["google-drive/", "slack/", "github/", ...]

2. search_tools(pattern="google-drive/*", detail_level="name+description")
   → 返回: Google Drive 工具的简要描述

3. get_tool_definition("servers/google-drive/getDocument")
   → 返回: 带参数、类型、示例的完整 JSON schema

典型的目录结构:

servers/
├── google-drive/
│   ├── getDocument.ts
│   ├── listFiles.ts
│   └── shareFile.ts
├── slack/
│   ├── sendMessage.ts
│   └── getChannels.ts
└── github/
    ├── createIssue.ts
    └── listRepos.ts

证据

  • 来自 Anthropic 工程团队的实践(established,成熟),生产数据表明初始上下文消耗能降 70%-90%
  • 这个思路在 MCP、Cloudflare Code Mode、OpenAI、LangChain 等主流平台都被验证过。
  • 和 RAG 的思想同源:先检索再精读,而不是把整个语料库搬进窗口。

怎么用

  • 工具或集成超过 20 个的系统就该考虑。
  • 层级要清晰(按集成、按领域、按功能),每层要有有意义的名字和描述。
  • 支持通配符搜索(glob/regex);频繁一起请求的工具定义做缓存。
  • 工具定义用 OpenAPI / JSON Schema 兼容的格式,方便后面接别的工具。

取舍

  • 好处:初始上下文降 70%-90%;能扩展到上千个工具;Agent 通过探索自然了解工具生态;天然对接"代码优先"的接口(第 10 章);版本和废弃管理更优雅。
  • 代价:多了一次"发现"的开销(执行前多了工具调用);组织方式和命名要花心思;如果任务本来就要用大部分工具,收益变小;找对工具可能需要多次往返。

模式二:工具搜索懒加载(Tool Search Lazy Loading)

问题

渐进发现解决的是"怎么浏览"。还有个更常见的痛点:MCP server 一多,工具描述本身就吃爆上下文。

文档记录的案例:7+ 个 server 同时启用,光工具描述就消耗 67k+ token。带来的连锁反应:

  • 上下文膨胀:预载所有工具描述,把本该用于任务的 token 挤掉了。
  • 延迟:工具越多,每次请求的处理开销越大。
  • 发现困难:Agent 要在大量无关工具里扫描,才能找到相关的。
  • 内存压力:大型工具目录可能超出实际上下文限制。

方案

不预载,搜索到了才加载。 核心是一个 ToolSearchTool:让 Agent 通过搜索动态地把工具载入上下文,而不是初始化时全部预载。

function initialize_mcp_servers(servers):
    total_tool_tokens = calculate_tool_tokens(servers)

    if total_tool_tokens > CONTEXT_THRESHOLD:
        # 懒加载模式:只载入元数据,暴露搜索工具
        tool_registry = load_tool_metadata_only(servers)
        return ToolSearchTool(tool_registry)
    else:
        # 传统模式:直接预载全部
        return preload_all_tools(servers)

function tool_search(query, tool_registry):
    # Agentic search:不是基础 RAG,是智能搜索
    relevant_tools = agentically_search(tool_registry, query)
    return load_tool_definitions(relevant_tools)

四个关键动作:

  1. 阈值检测:监测工具描述是否会超过上下文阈值(Claude Code 用的是窗口的 10%)。
  2. 搜索接口:提供 ToolSearchTool,Agent 用它搜索工具元数据、选择性地加载工具。
  3. Server 指令:利用 MCP server 的 instructions 字段,引导 Agent"什么时候该去搜这个 server 的工具"。
  4. 智能搜索:用 agentic search(让模型理解意图再搜),而不是基础向量 RAG。

怎么用

如果你是 MCP server 作者:

  • 写好 server instructions:开了工具搜索之后,这个字段更关键,它告诉 Agent 什么时候该搜你的工具。
  • 元数据要丰富:描述、标签写清楚,方便被搜到。
  • 逻辑分组:把相关工具组织在一起,让发现更直观。

如果你是 MCP client 作者:

  • 实现 ToolSearchTool;用智能搜索而不是基础 RAG。
  • 阈值按场景定(Claude Code 用 10%);提供开关,允许用户关掉搜索回到预载。
  • 常用工具可以渐进预载(LRU 缓存最近用过的),长尾工具留在搜索模式。

取舍

  • 好处:基线上下文大幅下降(67k+ token 压到只剩元数据);能扩展到 100+ 工具;不需要工具时冷启动更快;可以同时启用更多 MCP server。
  • 代价:动态加载有额外延迟;要建搜索基础设施和维护元数据;可能错过"逛完整目录"时的偶然发现;server instructions 变得关键,写不好会坑 Agent。

模式三:静态服务清单(Static Service Manifest)

问题

前两个模式假设"工具已经在 Agent 的视野里"。但换个场景:Agent 第一次遇到一个陌生平台,怎么知道它提供什么服务?

现在的几种方式都不太行:

  • 把工具列表硬编码进系统提示词:不灵活,平台一更新就过时。
  • 运行时探索工具目录:上下文开销大,还不一定找得到。
  • 解析人工写的文档:格式不统一,解析成本高。

Agent 要么在不需要的完整目录上浪费上下文,要么根本没有办法了解这个平台,只能等人介入。

方案

在众所周知的 URL 上放一份静态、机器可读的清单,描述平台提供什么能力、有哪些服务、认证方式、使用限制。Agent 拉取一次、廉价解析,就能决定调哪些服务。不需要运行时工具调用的开销,也不需要人工整理。

两种互补的格式已经出现:

  1. llms.txt(来自 llmstxt.org 社区约定):放在站点根目录的纯文本/markdown 文件,给人给机器看的站点能力摘要。类比 robots.txt 的反向:robots.txt 说"哪些禁止访问",llms.txt 说"哪些可以用"。
  2. agent.json / ai-plugin.json:结构化 JSON 清单(放根目录或 /.well-known/),声明服务端点、认证方案、速率限制、能力元数据,schema 稳定,Agent 可以确定性解析。
// agent.json 示例
{
  "name": "Platform Name",
  "description": "这个平台提供什么",
  "auth": { "type": "api_key", "header": "X-API-Key" },
  "services": [
    {
      "name": "memory",
      "path": "/v1/memory",
      "description": "持久化的键值和向量存储",
      "methods": ["GET", "POST", "DELETE"]
    },
    {
      "name": "scheduler",
      "path": "/v1/scheduler",
      "description": "基于 cron 的任务调度",
      "methods": ["GET", "POST"]
    }
  ]
}
# llms.txt 示例

> Platform Name: 一个 API key,多个基础设施服务。

## 可用服务
- Memory: 持久化存储,支持向量搜索 (/v1/memory)
- Scheduler: 基于 cron 的任务调度 (/v1/scheduler)
- Event Bus: 发布/订阅消息 (/v1/events)

## 认证
所有端点都需要 X-API-Key 请求头。

## 速率限制
每个 key 每分钟 100 次请求。

Agent 的工作流:

1. fetch("{base_url}/llms.txt")     → 自然语言概览
2. fetch("{base_url}/agent.json")   → 结构化服务目录
3. 从清单里挑出要用的服务
4. 只调用这些端点

怎么用

  • 最适合:在单一基础 URL 后面暴露多个服务的 API 平台;需要先发现能力再规划的基础设施提供商;不同 API key 解锁不同服务子集的多租户平台。
  • 实施要点:llms.txt 用 text/plain 或 text/markdown 放站点根目录;agent.json 用 application/json 放根目录或 /.well-known/。清单保持小(llms.txt 小于 4K token,agent.json 小于 8KB)。带版本字段让 Agent 能检测清单变化。只列稳定、公开文档化的端点。服务变化时,把清单更新加进 CI/CD。
  • 和渐进发现互补:静态清单提供初始目录,渐进发现处理运行时细节加载。

取舍

  • 好处:能力发现零运行时开销(一次 HTTP 拉取);不绑定 MCP / function calling / 任何特定框架;Agent 可以"先规划再行动",减少无效工具调用;实现简单,就是个静态文件。
  • 代价:还没有统一的通用标准(多种格式竞争);不同步的话清单会和真实 API 状态漂移;只描述"有什么",不描述"每个端点怎么用"(完整 schema 还得靠 OpenAPI 或 MCP);如果没配好认证,可能把攻击面暴露给恶意 Agent。

模式四:统一工具网关(Unified Tool Gateway)

问题

Agent 能力越强,要接的外部工具越多:网页搜索、图像生成、竞品调研、安全扫描、视频制作……每个工具都有一套自己的 API key、认证流程、限流逻辑、计费方式。问题叠加起来:

  • 凭据泛滥:要管几十个提供商的 API key,认证方案还各不相同。
  • 集成税:每接一个新工具,都要写错误处理、重试、schema 转换、响应归一化的定制代码。
  • 计费碎片化:用量散落在各提供商的 dashboard 上,成本追踪和预算管控很难。
  • 发现开销:Agent 没有统一的方式知道"有哪些工具可用、我能访问什么"。

方案

在 Agent 和所有外部工具提供商之间放一个网关。网关统一处理发现、认证、路由、执行、计费,向 Agent 暴露一个统一接口,不管底层是哪个提供商。

四个层次:

  1. 发现层:统一注册表,Agent 通过一个端点/协议查询可用工具和能力(比如 MCP 的 tools/list)。
  2. 认证层:网关替 Agent 持有各提供商的凭据。Agent 只向网关认证一次,网关处理各提供商的认证。
  3. 路由层:进来的工具调用被路由到正确的提供商,必要时做 schema 转换。长时间运行的工具派发给异步 worker。
  4. 计量层:每次调用都记录、计量、计费,支持预算上限和用量可见性。
// Agent 侧:一个 key,一个协议
gateway = ToolGateway(api_key="tr_xxx")

// 发现
tools = gateway.discover(category="research")
// → [{name: "web_search", skills: ["search", "deep_research"]}, ...]

// 执行:网关处理提供商认证、重试、计费
result = gateway.call("web_search", "search", {query: "AI agent patterns"})

// 长任务异步执行
job = gateway.call("video_production", "generate_clip", {prompt: "..."})
// → {job_id: "abc", status: "processing"}
result = gateway.get_result(job.job_id)

怎么用

  • 最适合:需要 10+ 外部能力的 Agent 团队;向多个 Agent 或用户提供工具访问的平台;需要集中计费、限流、审计日志的场景。
  • 协议选择:MCP 是天然选择,Agent 本来就讲 MCP,网关直接当 MCP server 暴露所有工具。
  • 同步 vs 异步:短工具(搜索、查资料)内联执行;长工具(视频生成、大规模抓取)返回 job ID,在后台 worker 上跑。
  • 凭据管理:用户可以自带 API key(BYOK)降低成本,网关提供默认 key 图方便。
  • schema 归一化:各提供商的响应统一成一致的输出 schema,Agent 不用写针对某个提供商的解析逻辑。
  • 限流:在网关层做按用户、按工具的限流,防滥用、控成本。

取舍

  • 好处:Agent 只集成一次而不是每个提供商一次,集成面大幅缩小;所有工具的计费和用量追踪集中;新工具上线,所有 Agent 无需改代码就能用;限流、日志、访问控制集中在一个点;凭据隔离,Agent 永远看不到原始提供商 key。
  • 代价:单点故障,网关挂了所有工具都用不了;多一跳网络延迟;网关运营商通常会在提供商成本上再加价;用私有协议会有供应商锁定风险;对提供商特有功能或优化的控制变弱。

四个模式怎么选

场景 推荐模式
工具 20+ 个,预载吃爆上下文 渐进发现 / 懒加载
MCP server 多,光描述就 67k token 懒加载(阈值触发)
Agent 要发现陌生平台的能力 静态服务清单
团队要接 10+ 外部工具,凭据和计费要集中 统一工具网关

四个模式其实是一套工具的四个入口静态清单让 Agent 知道"这平台有啥",渐进发现让它"按需往下钻",懒加载解决"预载太贵",网关把"多个提供商"收敛成"一个入口"。小团队从渐进发现开始就够,规模上来再接网关。

实践清单

  • 工具 20+ 个:按集成/领域组织成文件系统层级,提供 list / search / get 三级发现
  • 工具描述超上下文 10%:启用懒加载,只载元数据,暴露 ToolSearchTool
  • 给平台挂 llms.txtagent.json,保持小于 4K token / 8KB,随 CI/CD 更新
  • 清单带版本字段,只列稳定端点
  • 10+ 外部工具:收敛到统一网关,一次接入全部可用
  • 网关层做凭据隔离、统一计费、按用户按工具限流
  • 长工具走异步 job,短工具内联执行
  • 检查:Agent 看到的是"整个目录",还是"它需要的几个工具"?

本章小结

  • 渐进发现:工具组织成文件系统,三级粒度按需加载,初始上下文降 70%-90%。
  • 懒加载:超阈值就只载元数据 + 搜索工具,67k token 压到只剩元数据。
  • 静态服务清单:llms.txt + agent.json 放众所周知的 URL,零运行时开销发现能力。
  • 统一工具网关:一个入口收敛所有提供商,集中认证、路由、计费、限流。
  • 发现做得好,Agent 才找得到工具;找到工具,接口设计(第 10 章)才派得上用场。

下一章讲执行环境:工具找到了,Agent 要在哪跑?智能 Bash、Shell 输出上下文化、CLI 优先的技能设计、虚拟机操作。

📑 Agent 模式实战:生产级 AI Agent 的工程模式

1 第 1 章 什么是 Agent 模式 2 第 2 章 规划-执行-观察:先想清楚,再动手 3 第 3 章 反思闭环:让 Agent 学会检查自己的作业 4 第 4 章 委派:让主 Agent 学会把活分出去 5 第 5 章 上下文预算治理:把 token 当成钱来管 6 第 6 章 上下文压缩与精选:装不下怎么办 7 第 7 章 上下文最小化:别让脏东西留在脑子里 8 第 8 章 记忆体系:让 Agent 记得住过去 9 第 9 章 学习沉淀:让 Agent 和团队一起变聪明 10 第 10 章 工具接口哲学:让 Agent 能用、好用、用得起 11 第 11 章 工具发现:让 Agent 在几百个工具里找到对的 12 第 12 章 执行环境:Agent 在哪动手、怎么动手 13 第 13 章 代码执行与沙箱:先写码,再跑码 14 第 14 章 结构化输出与契约:让 Agent 的输出能接住 15 第 15 章 验证循环:Agent 怎么检查自己的作业 16 第 16 章 评测基建:怎么系统地检验 Agent 17 第 17 章 可观测性:看见 Agent 在想什么、在干嘛 18 第 18 章 韧性工程:扛得住部分失效 19 第 19 章 威胁模型:先看风险长什么样,再谈防御 20 第 20 章 控制流隔离:把"谁做决定"和"谁执行"分开 21 第 21 章 权限与审批:谁有权干什么、谁点头 22 第 22 章 凭据与出口:Agent 手里的钥匙和门 23 第 23 章 多智能体信任:多个 Agent 之间怎么互信、怎么审计 24 第 24 章 反馈信号设计:给 Agent 的是信号,不是更大的提示词 25 第 25 章 评测驱动的改进:让 Agent 在真实使用和对抗测试里变强 26 第 26 章 强化学习:把反馈变成训练信号 27 第 27 章 复合式进化:让 Agent 系统越用越值钱 28 第 28 章 多智能体协调:让一群 Agent 一起干活不掉链子 29 第 29 章 模型路由:谁用哪个模型,怎么用得起 30 第 30 章 推理搜索结构:让 Agent 多想想,而不是一条道走到黑 31 第 31 章 控制谱系:从自动补全到完全自主的滑动条 32 第 32 章 团队与产品:把 Agent 变成团队资产,而不是个人玩具
← 返回本书大纲