工具目录中的依赖炸弹:为什么增加一个工具会破坏五个智能体
我认识的一个团队在某个周二向他们的支持智能体目录发布了一个新的 lookup_customer_v2 工具。这个工具的作用范围很窄,经过了充分的隔离测试,并通过了评审。到了周四,一个毫不相关的流程——退款处理——在之前一直运行良好的案例中出现了约 4% 的失败。退款工具没有变。退款提示词没有变。模型也没有变。改变的是规划器(planner)现在在处理退款资格查询时选择了 lookup_customer_v2,而以前这些查询都会清晰地路由到 get_account_status。原因是新工具的描述中恰好包含 "eligibility"(资格)这个词,并在模型内部使用的某种相似性启发式算法下获得了更高的排名。
这就是依赖炸弹。团队通常将工具注册表视为增量式的——“我们只是增加了一个东西,能出什么问题”——但规划器并不将你的注册表视为独立能力的列表。它看到的是各种选择上的概率分布,而每一个条目都会重新分配权重。增加一个工具可能会悄悄地削弱其他地方的行为,而你的评估套 件(eval suite)可能会漏掉这一点,因为没有人写过回归测试来规定“在这种情况下,智能体应该仍然选择 旧 工具”。
目录是一个全局命名空间,而规划器是一个 Softmax
工程师像思考函数库一样思考工具注册表:每个条目都是一个离散的能力,增加一个条目就能在不影响其余部分的情况下扩展你可以做的事情。这种心理模型对于 LLM 驱动的规划器(planner)来说是错误的。
当模型决定调用哪个工具时,它实际上是在隐含地将每个工具的名称和描述与用户的意图以及当前的追踪(trace)进行比较。这个决策是竞争性的,而非独立的。增加一个描述与现有工具哪怕只是稍微重叠的新工具——同义词、共用的动词、相同的领域名词——你就会改变所有以前能自信落到旧工具上的提示词的相对分数。微软研究院(Microsoft Research)对 MCP 工具空间的研究发现,在公共生态系统中存在 775 个名称冲突的工具,仅 "search" 这个动词就出现在 32 个不同的 MCP 服务器中。当两个服务器声称拥有相同的名称时,这些冲突不仅仅会导致硬性错误;在发生任何字面上的名称冲突之前,它们就会导致语义相邻项(semantic neighbors)之间的无声重新路由。
即使没有重叠,同样的动态在大规模情况下也会显现。OpenAI 的建议是,为了获得最高准确度,在每一轮开始时保持少于 20 个可用函数。Anthropic 建议一旦超过 30 个工具就切换到动态工具搜索(Tool Search)——该建议背后的数据非常惊人:在 Claude Opus 4.5 上进行的一项受控实验发现,仅通过将工具定义推迟到搜索步骤之后,而不是全部塞进上下文中,工具选择的准确率就从 79.5% 提高到了 88.1%。相同工作负载下的 Token 成本从约 77k 下降到 8.7k。MCPVerse 基准测试总结了这一发现:大多数模型会随着目录的增大而退化,退化速度取决于模型,而不仅仅是工具。
重点不在于你的目录太大。重点在于目录是一个 耦合 的系统。每一个条目都与所有其他条目处于紧张关系中,如果不针对所有其他条目做出概率性的声明,就无法增加一个工具。
评估套件通过了,因为它从未问过正确的问题
大多数团队的评估案例(eval cases)看起来像“给定这个用户查询,智能体应该产生这个答案”或“智能体应该带这些参数调用 refund_order”。这些案例捕捉到了明显的失败模式:幻觉参数、错误的端点、格式错误的 JSON。它们无法捕捉到这样一种回归:规划器以前选择工具 A,现在选择工具 B,而工具 B 恰好返回了一个看似合理但错误的答案,且评估的输出检查仍然接受了这个答案。
这就是结构性的盲点。轨迹级评估(Trajectory-level evals)评分的是智能体所采取的 路径,而不仅仅是最终答案。如果没有轨迹检查,由于重构导致智能体采取效率较低或正确性较低的路线是不可见的——直到一个 季度后,由于一个无关的事件,有人回放追踪记录时才发现,工作流已经无声无息地退化了数周。
解决方法是选择稳定性测试(selection-stability tests)。对于套件中的每个标准用户提示词,固定预期的工具——不是答案,是 工具——并以规划器的首次调用是否仍然解析为该工具作为合并的门槛。这些测试编写成本低,运行成本也低;它们不需要带标签的标准答案,只需要一个预期的工具名称。当有人增加了一个描述遮蔽了现有工具的新工具,且选择稳定性测试在合并前而非生产环境中失败时,这项投资就得到了回报。
一个合理的准则:
- 每一个具有“标准路径”(happy path)工具的工作流,至少要有一个稳定性测试,为一个代表性的提示词固定该工具。
- 每一个增加或重命名的工具都会触发一次消融实验(ablation):运行完整的稳定性套件(保持目录现状),然后运行移除新工具/更名工具后的套件,并对比规划器的选择。任何所选工具发生变化的提示词都是潜在的回归——不一定是 Bug,但需要人工介入查看。
- 描述的编辑应该被版本化,并被视为改变行为的发布,而不是文案润色。在大多数团队中,“精简工具描述的措辞”的诱惑往往会在不知不觉中发布;但这些措辞是提示词的一部分,改变它就是一次发布。
为什么“先添加,后消融”行不通
有一种诱人的工作流程:添加工具,部署,观察仪表盘。如果出现回归 ,就回滚。这听起来像是经典的阶段性部署(canary deploy)逻辑,但对于工具目录来说,它会在两个具体方面失效。
首先,回归通常不会显示在你观察的指标中。如果新工具以高频率成功服务于其预期的工作流,你的“工具 X 的成功率”就会上升。但事实是,它现在也从 get_account_status 中分流了流量,并且在回答退款资格问题时比旧工具表现稍差,这在单个工具的聚合数据中是不可见的。你需要观察按推断意图细分的端到端任务完成率——而大多数团队并不具备这种精细度。
其次,这类回归的检测时间(time-to-detect)很长,因为症状是统计性的。退款资格上 4% 的回归正好处于噪声水平,在有人注意到趋势之前,它往往会被当作波动而被忽视好几周。到那时,你已经在此基础上又发布了两个工具,二分定位(bisect)的范围会变得极其糟糕。
这与最近研究中量化的“工具使用税”(tool-use tax)如出一辙:工具调用的协议开销和选择成本可能会超过在实际分布上的能力提升,尤其是在语义噪声下。即使每个工具单独工作正常,添加工具也不是免费的。
真正的纪律是什么样的
将工具目录视为发布界面(release surface)而非配置文件,前期成本更高,但后期能节省巨大的开销。具体而言:
- CI 中的选择稳定性测试。 针对典型提示词(canonical prompts)固定预期的工具。每个涉及注册表的 PR 都要运行这些测试。失败需要显式确认(“是的,这个提示词现在应该路由到新 工具”),而不是默默通过。
- 合并前消融(Pre-merge ablation)。 在添加或重命名工具时,自动运行带修改和不带修改的稳定性套件,并将差异(diff)呈现给评审者。大多数 PR 将显示零差异并快速通过。显示差异的 PR 正是那些值得仔细审查的情况。
- 描述变动即发布。 对工具描述字符串的更改应与对工具行为的代码更改遵循相同的流程。不允许从与智能体(agent)无关的任务中合并“顺手清理措辞”的修改。
- 为你的工具设置命名空间。 如果你正在聚合多个 MCP 服务器或内部工具源,请保持前缀一致——例如
crm.lookup_customer、billing.refund_order。这不能消除语义干扰,但它消灭了字面冲突类的错误,并使规划器(planner)的决策在追踪记录中更清晰。这也意味着未来当你为某个工具更换底层服务器时,不会意外地重命名了另一个团队的工具。 - 限制初始工具数量,依靠检索处理长尾。 如果你的目录超过了 20 到 30 个的范围,请接受你现在已进入动态发现(工具搜索、语义预过滤、层级分解)优于扁平注册表的阶段。Token 成本和准确性数据是不言而喻的。
- 发布附带原因的工具变更日志。 不仅是“添加了 X”,而是“添加 X 是因为 Y;我们预期它会被 Z 类型的提示词选中;这是固定它的稳定性测试”。这是你未来在六个月后进行回归二分定位时所需的产物。
领悟
工具目录不是一套独立的螺丝刀工具箱;它是一个规划器完整读取的单一语言。每个条目都约束着其他每个条目的含义,就像词典中的一个新词会微妙地改变其邻近词汇的界限一样。对待添加和重命名要像更改公共 API 中的函数签名一样谨慎——因为从功能上讲,它们就是如此。
能够以较低成本学会这一点的团队,是在工具超过 20 个之前就编写了选择稳定性测试框架的团队。而付出昂贵代价才学会这一点的团队,是在六个月后通过客户投诉才发现问题,在三个发布周期中进行二分定位,最后发现回归始于某人“仅仅添加了一个工具”的那个周二。
- https://www.microsoft.com/en-us/research/blog/tool-space-interference-in-the-mcp-era-designing-for-agent-compatibility-at-scale/
- https://arxiv.org/abs/2511.14650
- https://arxiv.org/html/2508.16260v1
- https://arxiv.org/html/2605.00136
- https://www.jenova.ai/en/resources/mcp-tool-scalability-problem
- https://dev.to/nebulagg/mcp-tool-overload-why-more-tools-make-your-agent-worse-5a49
- https://forum.cursor.com/t/mcp-tools-name-collision-causing-cross-service-tool-call-failures/70946
- https://github.com/openai/openai-agents-python/issues/464
- https://www.letsdodevops.com/p/fixing-mcp-tool-name-collisions-when
- https://www.agentpatterns.tech/en/testing-ai-agents/regression-testing
- https://platform.openai.com/docs/guides/function-calling
