跳至正文
来两杯美式
返回

MCP 爆炸之后,Agent 怎么从一堆工具里选对那一个

By 来两杯美式
发布于

MCP 刚接入的时候,最让人兴奋的是“Agent 终于能调用外部系统了”。查文档、发消息、搜知识库、读用户记忆、跑审批、查工单、改配置,好像只要把能力包成工具,模型就能顺着自然语言一路把事情做完。

但工具一多,问题很快反过来。

真正难的不是“怎么让 Agent 有工具”,而是:当它面前有几十个、几百个工具时,怎么让它在当前问题里只看见该看的那几个。

这就是 MCP 爆炸之后,Agent 平台必须面对的工具筛选问题。

一、工具太多时,直接全量下发会发生什么

最朴素的做法,是把所有 MCP Server 暴露出来的工具都塞进模型上下文,让模型自己判断。

这个方案在 5 个工具以内通常没什么问题。一旦工具数量上来,就会开始出现四类症状。

第一,上下文被工具说明挤满。每个工具都有名称、描述、参数 schema、使用规则。几十个工具一展开,真正的用户问题、历史对话、业务上下文反而被压缩到很小。

第二,模型会被相似工具干扰。比如“查项目”“查文档”“查知识库”“查 API”“搜索资源”同时存在,描述又都写得很泛,模型很容易选错。

第三,工具误触发成本变高。读工具误触发还只是答错;写工具、消息工具、审批工具误触发就可能变成生产事故。工具越多,误调用的攻击面也越大。

第四,治理失焦。如果所有工具都平铺给模型,优先级、权限、连接状态、灰度范围、参数约束都只能靠提示词提醒。提示词可以表达意图,但不应该承担系统边界。

所以工具调用系统要先承认一个事实:模型擅长语义判断,但不适合作为第一层路由器。

模型应该在一个足够小、足够相关、边界清晰的候选池里做选择,而不是在全平台工具集里大海捞针。

二、工具选择不是一次判断,而是一条漏斗

一个比较稳的工具筛选链路,可以拆成三层筛选,外加一层执行兜底。

用户问题

Layer 1:静态可用性过滤

Layer 2:动态相关性筛选

Layer 3:模型运行时决策

Layer 4:工具执行兜底(超时、重试、权限、审计)

结果回填

前三层是一条逐层收窄的漏斗,解决的是不同问题。

Layer 1 解决“这个工具现在能不能给这个用户看”。

Layer 2 解决“这个工具和当前问题有没有关系”。

Layer 3 解决“在这些相关工具里,当前这一步到底要不要调用、调用哪个、参数怎么填”。

Layer 4 不参与选工具,它是横切的兜底层:模型选完之后,超时、重试、权限复核、审计都归它管(详见第九节)。

不要把三件事揉成一句 Prompt:“请你根据用户问题选择合适工具”。这句话听起来聪明,工程上却太薄。真正上线后,任何一层缺失都会变成不可解释的误调用。

三、第一层:静态过滤,先把不能用的工具挡掉

第一层不需要模型参与,它应该是确定性的系统逻辑。

至少过滤这些维度:

这层的关键是:不要让模型看到它不应该使用的工具。

如果一个普通用户没有权限调用“删除用户”“发全员消息”“重建索引”这类工具,最好的处理不是在 Prompt 里提醒“不要乱调用”,而是从候选工具列表里直接移除。模型看不见,就不会误选;客户端伪造调用,也会在服务端权限边界被拒绝。

这一层常见的数据结构大概是这样的:

type ToolRegistryItem = {
  id: string;
  type: "mcp" | "skill";
  name: string;
  description: string;
  inputSchema: Record<string, unknown>;
  priority: number;
  keywords: string[];
  connectionId?: string;
  connectionName?: string;
  visibility?: "all" | "admin";
  isEnabled: boolean;
};

这不是给模型看的完整上下文,而是平台自己的工具目录。它的职责是维护工具的元数据、权限和生命周期。

这里有个很重要的设计分工:工具目录是单一可信源,Prompt 只是目录的一种投影。

如果后台关闭一个工具,模型下一轮就不该再看到它;如果权限收回,工具不该再进入候选池;如果 MCP 连接不可用,候选池也不该假装一切正常。工具治理要落在目录和执行层,不能只落在自然语言提示里。

四、第二层:动态筛选,给模型一个小而准的候选池

静态过滤后,仍然可能剩下很多工具。第二层要做的是根据当前用户问题,从工具目录里选出最相关的一小批。

这个阶段不一定要一上来就做复杂向量检索。很多企业内部工具选择,用一个可解释的打分模型就足够好:

维度作用示例
关键词匹配捕捉业务黑话和高频表达“企微”“审批”“知识库”“工单”
描述匹配让自然语言问题命中工具说明用户说“查接口文档”,描述里有“查询 API 文档”
工具名匹配支持用户直接点名工具search_docssend_message
连接名匹配支持按系统或品牌路由“飞书”“企微”“权限中心”
优先级同类工具排序和兜底新工具替代旧工具,高质量工具排前面

打分之后按“匹配分优先,优先级其次”排序,截取 Top-K。K 不宜太大,常见可以从 8 到 12 起步。太小会漏工具,太大又回到全量下发的问题。

一个简化版打分可以这么写:

function scoreTool(tool: ToolRegistryItem, message: string) {
  let score = 0;

  score += keywordScore(tool.keywords, message) * 50;
  score += descriptionScore(tool.description, message) * 30;
  score += nameScore(tool.name, message) * 10;
  score += connectionScore(tool.connectionName, message) * 10;

  return Math.min(score, 100);
}

这里的重点不是公式本身,而是几个工程原则。

关键词要来自业务语言。

工具作者经常喜欢写系统语言,比如“调用企业消息平台发送通知”。但用户说的是“帮我发个企微”“通知研发群”“催一下审批”。关键词字段就是拿来补齐这些业务叫法的。

描述要写给模型,不只是写给人。

“文档管理工具”这种描述太泛。更好的写法是:“根据关键词检索技术开放平台文档,返回文档标题、摘要、链接和匹配片段,适合回答 API、接入流程、平台规则类问题。”

模型能从这句话里判断适用范围,也能知道结果大概长什么样。

优先级不应该掩盖相关性。

高优先级工具不是永远优先,而是在相关性接近、或者需要补齐候选池时优先。否则一个全局高优先级工具会挤掉更相关的低优先级工具,最后变成“权重越高越误触发”。

候选池可以有 fallback,但要克制。

如果只有 2 个工具命中了,可以用高优先级工具补到 10 个,给模型一点探索空间。但如果没有任何匹配,直接返回全平台最高优先级工具也要谨慎。更好的做法是配合工具类型、场景入口、用户角色和历史上下文继续收缩,而不是把“没匹配上”解释成“随便给几个”。

五、第三层:模型只在候选池里做运行时决策

候选池进入模型后,才轮到 LLM 的语义能力发挥作用。

这一层要回答的问题更细:

这时 system prompt 里的工具调用规则就很重要。比如:

1. 优先使用高优先级工具。
2. 调用前检查必填参数。
3. 不要传空参数。
4. 参数只能来自用户输入、历史上下文或工具结果。
5. 缺少必填参数时先追问。
6. 工具失败时不能编造答案。

注意,这些规则不是第一层防线,而是第三层的行为约束。它们可以提高模型调用质量,但不能替代权限校验、工具过滤和执行兜底。

这也是很多 Agent 系统前期容易犯的错:把所有控制逻辑写进 Prompt,然后期待模型永远遵守。Prompt 是表达意图的好地方,不是系统安全边界。

六、能力介绍类问题要走特殊分支

有一类问题很特殊:

这类问题不是要调用某个具体工具,而是要介绍工具目录本身。

如果仍然走普通 Top-K,可能会出现一个尴尬结果:用户问“你有哪些工具”,筛选器只返回了和“工具”两个字相关的少数工具,模型反而看不到完整能力列表。

所以能力介绍类问题应该单独识别,返回更完整的工具清单,让模型基于目录介绍能力,而不是调用工具。

识别本身不需要很复杂。实用的做法是在动态筛选之前加一条意图规则:命中“你能做什么”“有哪些工具 / 能力 / MCP”“XX 是什么 / 怎么用”这类固定句式,就直接走能力介绍分支,返回按场景分组的完整目录。句式覆盖不住的长尾,再用一个轻量意图分类兜底。

有一点不能省:这条分支同样要过第一层静态过滤。介绍给用户的能力清单,永远只是他有权限看到的那部分,而不是全平台目录。

这类分支看起来像小优化,实际很重要。因为它决定了用户第一次认识 Agent 的方式。如果第一次问“你能干什么”,Agent 就答得支离破碎,后面再强也很难建立信任。

七、工具元数据比模型参数更重要

MCP 工具筛选做不好,很多时候不是模型不行,而是工具元数据写得太差。

新增一个工具时,至少要认真写四样东西。

1. name:稳定、短、可辨认。

工具名不一定要给最终用户看,但模型会看。querysearchrun 这种名字太空。search_tech_docssend_wecom_messageget_api_key_usage 会好很多。

2. description:说明适用场景和返回结果。

不要只写“查询数据”。要写清楚查询什么数据、适合回答什么问题、返回哪些关键字段。

坏例子:

查询用户信息。

好例子:

根据工号或姓名查询员工基础信息,返回姓名、部门、岗位、邮箱和在职状态。适合回答人员归属、联系方式和组织关系类问题。

3. keywords:补业务黑话。

description 适合完整说明,keywords 适合放用户口语、缩写、系统别名、历史叫法。比如同一个“企业微信消息”工具,关键词里可以放“企微”“群通知”“消息推送”“催办”。

4. inputSchema:把必填参数说清楚。

模型能不能正确补参数,很大程度取决于 schema。字段名要稳定,描述要具体,required 要真实。一个错误的 required,会让模型要么频繁追问,要么传空值硬调。

工具元数据不是配置杂项,而是 Agent 的路由索引。它写得越清楚,模型越像在用一张地图;它写得越含糊,模型越像在猜谜。

八、系统型 MCP 不一定要暴露成普通工具

不是所有 MCP 调用都应该交给模型临场决定。

比如用户长期记忆、身份注入、审计日志、会话摘要、健康检查,这些更像 Agent 运行时基础设施。它们有固定触发点:请求前加载、请求后沉淀、定时补偿、失败重试。

以长期记忆为例,更稳的做法通常是:

对话开始前:加载用户画像和长期记忆,注入上下文
对话过程中:模型按需调用 search_user_memory 做语义检索
对话结束后:异步提取持久事实,写入本地记忆或同步到 MCP
定时任务中:扫描遗漏记录,做补偿投递

这里至少有两个不同性质的工具。

list_user_memory 更像上下文加载的一部分,可以由系统固定调用。

search_user_memory 更像模型按需使用的检索工具,可以进入候选池。

ingest_user_memory 则更像对话结束后的异步副作用,不一定应该让模型每轮都看见。

这条边界如果不画清楚,模型会被迫决定太多“平台内部事务”。结果就是该稳定发生的事变成偶发,该偶发调用的事又变成每轮都触发。

一个实用判断是:

如果某个 MCP 调用是为了让 Agent 运行环境更完整,它应该在系统编排层;如果它是为了回答用户当前问题,它才应该进入工具候选池。

九、执行层要兜住模型的不确定性

模型选中了工具,不代表系统就可以裸调。

执行层至少要有这些保护:

这层的职责很像数据库事务边界:上游可以灵活,下游必须确定。

尤其是写类工具,更要把权限、幂等、审计、二次确认这些能力放在执行层,而不是相信模型“应该不会乱调”。

十、新增一个 MCP 工具时,先问这 12 个问题

工具爆炸通常不是一天发生的,而是每次“再加一个工具”慢慢堆出来的。所以最有效的治理点,就是新增工具时的准入清单。

我会建议每个新 MCP 工具上线前问 12 个问题:

  1. 这个工具解决的是用户问题,还是平台内部编排问题?
  2. 它应该暴露给模型,还是由系统固定触发?
  3. 哪些用户能看见它,哪些用户能调用它?
  4. 它是 read、write,还是 dangerous 动作?
  5. 工具名能不能和同类工具明显区分?
  6. description 是否写清楚适用场景和返回结果?
  7. keywords 是否覆盖业务叫法、缩写和系统别名?
  8. inputSchema 的 required 是否真实,字段描述是否足够具体?
  9. 有同类工具时,它的 priority 应该排在谁前面?
  10. 调用失败时,用户应该看到什么错误信息?
  11. 输出结果会不会过大,是否需要摘要化或分页?
  12. 它的调用日志是否足够支撑审计和问题排查?

这 12 个问题看起来像流程,其实是在保护模型。你把工具边界定义得越清楚,模型需要猜的东西就越少。

十一、一个可落地的整体架构

把上面的逻辑合起来,一个工具选择系统大概会长这样:

Tool Registry
  - MCP 工具元数据
  - Skill 元数据
  - 权限与可见性
  - 优先级与关键词
  - 连接健康状态

Tool Selector
  - 静态过滤
  - 动态打分
  - Top-K 候选池
  - 能力介绍特殊分支

Prompt Builder
  - 注入候选工具描述
  - 标注必填参数
  - 标注优先级
  - 注入工具使用规则

LLM Runtime
  - 判断是否调用工具
  - 选择工具
  - 填写参数
  - 多步调用

Tool Executor
  - 权限复核
  - 超时和重试
  - MCP 调用
  - 结果解析
  - 日志审计

这套架构里,模型不是被削弱了,反而是被放到了更适合的位置。

它不需要维护全局工具目录,不需要记住每个用户的权限,不需要判断连接是否健康,也不需要承担审计责任。它只需要在当前问题、当前上下文、当前候选工具之间做语义决策。

这才是 LLM 最擅长的部分。

小结

MCP 让 Agent 获得了连接外部世界的标准方式。但当工具数量开始增长,平台真正要建设的就不只是 MCP Server,而是一套工具选择和治理系统。

我更喜欢把这件事总结成一句话:

MCP 解决“工具怎么接进来”,工具筛选解决“工具什么时候该出现”。

前者让 Agent 有手,后者让 Agent 不乱伸手。

一套可用的工具筛选逻辑,至少要包含四个层次:静态过滤管可用性,动态匹配管相关性,模型决策管语义选择,执行层兜住超时、重试、权限和审计。

当这几层边界画清楚之后,工具数量增长就不再必然带来混乱。相反,每个新 MCP 工具都能进入一个可解释、可观测、可治理的系统里,成为 Agent 能力的一部分,而不是上下文里的噪音。

AI 工程化相关阅读


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.MCP Server 选型决策树:从语言到审计,六个维度一次说清
  2. 02.当 MCP Server 只是"翻译"现成能力:FastMCP + Mem0 + Redis 的选型逻辑
  3. 03.当 MCP Server 要"嵌进"对外平台:Node.js + 官方 SDK + API Key 的选型逻辑
  4. 04.SSE 已被 MCP 判为 legacy:存量服务怎么迁、新服务怎么选
  5. 05.MCP 权限怎么分级?只读、可操作和高风险动作(附完整源码)
  6. 06.MCP 工具权限码怎么设计?read、write、dangerous 三层模型(附完整源码)
  7. 07.自建 SearXNG + MCP:给 AI 工具接入可控的网页搜索
  8. 08.Agent Skill 是快照,快照会腐烂:我把业务流程全部搬到了服务端
  9. 09.能力目录与协议下发:把接口设计和控制流一起交给 Agent
  10. 10.四套版本号:把 Agent Skill 的版本管理挪回服务端之后
  11. 11.让用户自定义 Agent 能力,而不打开注入的后门
  12. 12.MCP 2026-07-28 改了什么:从有状态会话到无状态核心
  13. 13.Anthropic 发布 Agent Skills:AI Agent 的能力该如何被打包
  14. 14.Agent Skills 设计模式:用文件夹给 Agent 装上专业能力
  15. 15.Agent Skills 最佳实践:从评测、结构拆分到安全审查
  16. 16.MCP Server 升级 2026-07-28:不是换依赖,而是重画协议边界
  17. 17.MCP Client 升级 2026-07-28:服务端能留兼容窗口,我们偏不兼容旧协议
  18. 18.MCP 爆炸之后,Agent 怎么从一堆工具里选对那一个

下一篇
MCP Client 升级 2026-07-28:服务端能留兼容窗口,我们偏不兼容旧协议