一、为什么接入更多 MCP 工具后,Agent 反而不好用了?
企业第一次给 AI Agent 接业务系统时,通常只有三五个工具:
- 查询订单;
- 查询客户;
- 创建工单;
- 修改员工备注;
- 查询库存。
这时,把全部工具定义直接交给模型,往往就能得到不错的演示效果。
但真实企业后台不会永远只有三五个接口。一个商城可能包含商品、订单、售后、库存、会员、营销和财务;一个 SaaS 平台还会继续叠加租户、门店、员工、权限、消息和统计。接入多个业务系统后,可被 Agent 触达的接口很容易从个位数增长到数十甚至数百。
很多团队会下意识地认为:
> 工具越多,Agent 能做的事情越多,能力就越强。
这句话只对了一半。
Agent 的能力目录确实扩大了,但如果每轮都把全部工具定义塞进模型上下文,它同时也获得了一份越来越长、越来越相似、越来越难判断的“接口说明书”。最终用户可能看到三类问题:
1. 明明只是查一个员工,首轮响应却越来越慢;
2. 输入内容没有变长,模型调用费用却持续上升;
3. Agent 找到了“看起来差不多”的工具,却选错接口或填错参数。
问题不一定出在 MCP 协议本身,而往往出在工具装配方式:系统把“所有可能用到的工具”误当成了“这一轮必须全部交给模型的工具”。
## 二、Tool Schema 不是免费目录,它本身就是上下文
一个工具并不只有名字。为了让模型知道怎样调用,通常还需要提供:
- 工具名称或 `operationId`;
- 功能说明;
- 每个参数的名称、类型和描述;
- 必填字段;
- 枚举值;
- 嵌套对象结构;
- 可能的约束条件。
例如,“修改员工资料”可能不是一句简短描述,而是一段完整 JSON Schema:
```json
{
"name": "employee_update_profile",
"description": "修改当前门店员工的基础资料",
"parameters": {
"type": "object",
"properties": {
"employee_id": { "type": "string", "description": "员工唯一标识" },
"display_name": { "type": "string", "description": "显示名称" },
"remark": { "type": "string", "description": "内部备注" }
},
"required": ["employee_id"]
}
}
```
当工具只有几个时,这些内容并不显眼;当工具达到数百个时,Schema、描述和参数会占据大量模型输入。
不同 Agent 宿主和模型 API 对工具定义的装配方式并不完全相同,但在常见实现中,只要某个工具要参与本轮选择,它的定义就需要进入模型可见输入。于是即使用户只问一句“张三还在职吗”,系统也可能把商品、退款、优惠券、库存、门店装修等完全无关的工具一并发送。
这不仅带来 Token 消耗,还会挤压真正有价值的内容,例如用户最近对话、业务规则、知识库片段和页面上下文。
因此,工具上下文优化的第一个认识应该是:
> Tool Schema 不是模型外部的一张免费菜单,它也是需要占用注意力和上下文预算的数据。
## 三、工具过多的成本,不只是“多花一点 Token”
如果问题只是输入 Token 增加,最多是费用和延迟问题。但工具膨胀还会影响模型判断。
### 1. 相似工具更容易被混淆
企业接口经常具有相近名称,例如:
- `employee_search` 与 `customer_search`;
- `order_update_remark` 与 `order_update_internal_note`;
- `product_create` 与 `product_create_draft`;
- `refund_apply` 与 `refund_approve`。
当几十个相似工具同时出现时,模型需要在更多候选项中识别细微差别。描述不清、命名不稳定或参数相似,都会提高误选概率。
### 2. 参数选择也会变得不稳定
Agent 不仅要选对工具,还要理解每个字段。大量无关 Schema 会分散模型注意力,尤其当多个接口都使用 `id`、`status`、`type` 之类通用字段时,更容易把一个接口的语义带到另一个接口。
### 3. 多步编排会被放大
一次后台操作往往需要多步完成:先搜索,再查详情,最后写入。每一步都面对全量工具,模型就会重复承担选择成本。
### 4. 调试会变得困难
当 Agent 选错工具时,团队很难快速判断:
- 是工具没有声明清楚;
- 是路由暴露得太宽;
- 是当前用户本来就不该看到这个工具;
- 还是模型被大量相似定义干扰。
因此,“工具全部接进来了”不是完工标准。企业真正需要的是让 Agent 在正确的时间,看见正确身份下、正确场景中的少量能力。
## 四、先把三个容易混淆的“工具集合”分开
要解决工具膨胀,首先不要再用一个“工具列表”概括所有东西。
| 层次 | 它表示什么 | 应该由谁决定 |
| --- | --- | --- |
| 能力目录 | 业务系统已经声明、可供治理的全部能力 | 业务系统与 SDK / OpenAPI / MCP 适配层 |
| 授权能力面 | 当前路由、身份和策略允许触达的能力 | 中枢、路由、业务授权与治理规则 |
| 本轮活动工具集 | 当前用户意图最可能需要、实际交给模型选择的少量完整 Schema | Agent Runtime 的装配与检索逻辑 |
这三层的数量可能完全不同。
例如,一个商城系统声明了 300 个 Agent 能力;“门店员工助手”路由只允许员工和排班相关的 24 个;用户本轮要修改员工备注,真正需要交给模型的可能只有员工搜索、员工详情和员工资料修改等几个工具。
可以把它理解成企业软件中的权限菜单:
- 系统有多少菜单,不等于当前账号能看到多少;
- 当前账号能看到多少,也不等于这一步操作要把所有页面都打开。
## 五、第一层裁剪:先用路由把业务场景收窄
按需加载不是先做向量检索,而是先定义清楚场景边界。
如果一个“万能助手”路由同时挂载商品、订单、员工、财务、工单和营销工具,那么检索再聪明,也要在一个过宽的候选集中工作。
更合理的方式是按业务场景组织路由,例如:
- 售后助手只允许订单查询、售后申请和工单能力;
- 门店运营助手允许员工、排班、客户标签和商品查询;
- 商品助手只允许商品、类目、库存和上下架能力;
- 财务问答路由默认只开放只读统计,不直接开放付款与退款写入。
路由的工具源白名单还应继续细分到 scope,而不是只写“已连接某个业务系统”。
示意如下:
```text
业务系统声明的全部能力
-> 当前路由选择的工具源
-> 每个工具源的 scope allow 白名单
-> 当前身份和会话可用能力
-> 本轮按意图检索出的活动工具
```
路由裁剪解决的是“这个场景原则上可以做什么”。它既能减少无关工具,也能避免模型通过提示词把自己带到另一个业务区域。
## 六、第二层裁剪:不要先加载完整 Schema,先搜索轻量目录
完成授权面裁剪后,仍可能剩下几十个工具。下一步不是继续把它们全部发送给模型,而是建立“轻量目录 + 按需展开”的机制。
轻量目录可以只保留用于发现的关键信息:
- 稳定工具名;
- 简短用途说明;
- 业务 scope;
- 只读还是写入;
- 所属工具源;
- 可搜索的同义词或领域关键词。
用户提出任务后,运行时先在当前授权能力面内搜索相关工具,再把少量命中的完整 typed Tool Schema 交给模型。
```text
用户:找到张三,把员工备注改成“华东渠道”
|
v
在当前授权能力面内搜索:员工、查询、详情、修改备注
|
v
返回少量候选:
employee_search
employee_get
employee_update_profile
|
v
只把这些候选的完整参数 Schema 交给本地 Agent
|
v
Agent 逐步查询、确认对象、发起修改
```
这样,Agent 不需要在一开始知道企业后台的全部接口,只需要拥有一个稳定的能力发现入口,并能在任务进行过程中再次搜索。
这与人使用后台系统的方式也更接近:我们不会在登录后一次性阅读所有页面的字段说明,而是先进入相关模块,再打开具体页面。
## 七、为什么“目录搜索”不能直接等同于“向量检索”?
语义检索很有价值,但不能把整个能力发现系统押在单一向量服务上。
工具发现至少要考虑两种路径:
### 语义检索
适合处理用户自然语言和工具命名不一致的情况。例如用户说“把员工昵称改一下”,而工具叫 `employee_update_profile`。语义检索可以把“昵称”“资料”“员工信息修改”关联起来。
### 确定性文本回退
当向量模型不可用、索引未完成或某个工具源没有配置 embedding 时,系统仍应能根据工具名、scope、描述和关键词得到稳定排序,而不是让整个 Agent 无法工作。
企业场景尤其需要关注回退逻辑。能力发现失败应该表现为候选不够理想或明确报错,而不是在部分来源可检索、部分来源不可检索时悄悄形成不可解释的结果。
无论采用哪种搜索方式,都必须遵守同一个前提:
> 只在当前已经授权的能力集合内检索,不能通过搜索发现并越权调用未授权工具。
## 八、渐进式加载,不等于每轮只允许调用一次工具
“只给模型少量工具”很容易被误解成限制 Agent 的多步执行能力。
实际上,活动工具集应该是可扩展的工作集,而不是一次性抽签。
例如用户要求“找到库存低于 10 的商品并创建补货工单”,Agent 可能经历:
1. 搜索并加载库存查询工具;
2. 查询低库存商品;
3. 根据结果继续搜索工单创建能力;
4. 加载工单工具完整 Schema;
5. 发起受治理的写操作;
6. 查询工单状态并向用户汇报。
关键在于每一步只加入任务当前所需的工具,而不是在第一步就加载库存、商品、供应商、采购、员工、工单和通知系统的所有接口。
如果后续意图发生变化,Agent 可以再次调用能力搜索;如果某个工具已经使用过,也可以保留在短期活动集合中,避免重复发现。
## 九、工具描述质量,决定“按需加载”能不能真正工作
能力检索不是给糟糕工具声明加一层魔法。
如果 20 个工具都叫“更新数据”,描述都只写“用于更新”,无论关键词检索还是语义检索都很难稳定选择。
一份适合 Agent 发现的工具声明,至少应该做到:
### operationId 稳定且可区分
推荐使用领域和动作组合,例如:
```text
employee_search
employee_get
employee_update_profile
order_refund_apply
order_refund_get
```
不要用 `doAction1`、`updateData` 之类脱离领域的名字。
### 描述说明“何时使用”
不要只重复工具名。除了“做什么”,还应说明适用边界:
> 修改当前门店员工的显示名称与内部备注;不用于修改角色、账号状态和数据权限。
这句话能帮助 Agent 排除相似接口。
### 参数使用业务语义
`employee_id` 比泛化的 `id` 更清楚;`internal_remark` 比 `text` 更不容易误填。
### 查询与写入分开
“搜索员工”和“修改员工资料”应是不同工具。Agent 先通过只读工具确认对象,再调用写工具,既有利于编排,也便于治理和审计。
## 十、按需发现只解决“看见什么”,不解决“能不能执行”
这是企业落地时必须守住的边界。
工具被搜索出来,只能说明它位于当前候选能力面,并不等于模型已经获得最终执行权。
一次真实调用仍然需要经过:
- Agent Session 与业务身份重验;
- 路由和工具源 scope 校验;
- 写工具开放范围校验;
- ACC 风险与审批语义;
- 幂等、限流、签名和审计;
- 业务系统自己的用户、租户、对象和状态权限校验。
可以把发现与执行理解为两条职责不同的链路:
```text
能力发现:从有权使用的工具里,找出本轮最相关的少量工具
受治理执行:每次实际调用时,重新确认身份、权限、审批和业务状态
```
不能因为搜索结果来自中枢,就跳过业务系统的最终鉴权;也不能因为一个工具没有进入本轮活动集,就错误地认为业务侧没有声明该能力。
## 十一、一个已有 SaaS 应该怎样逐步改造?
不需要先重写整套后台,也不需要一次性给每个工具建立复杂向量索引。可以按四步推进。
### 第一步:整理业务能力目录
- 只声明真正适合 Agent 调用的接口;
- 统一 operationId、领域名和 scope;
- 分开查询、写入和高风险动作;
- 补全用途、边界和参数说明;
- 删除重复、废弃和仅供内部调试的接口。
### 第二步:按场景建立路由授权面
- 每条路由只挂载需要的工具源;
- 每个来源只允许必要 scope;
- 区分客服、门店运营、商品和财务等场景;
- 不把“以后可能会用”当成本轮必须开放。
### 第三步:建立渐进式能力发现
- 每轮任务准备上下文时,只给 Agent 少量初始活动工具;
- 提供当前授权范围内的能力搜索;
- 搜索只返回少量完整 Schema;
- 支持任务中途再次搜索;
- 语义检索不可用时保留确定性回退。
### 第四步:用真实任务验收
- 记录每轮暴露给模型的工具数量;
- 检查 Agent 是否选到正确工具;
- 检查是否先确认对象再执行写入;
- 对同义表达和模糊表达做回归测试;
- 验证未授权工具不会被搜索出来;
- 验证业务权限变化后调用会失败关闭。
## 十二、如何判断工具工作集是不是仍然太大?
不要只看 MCP Server 总共有多少工具,而应观察每个真实任务。
| 现象 | 可能原因 | 优先处理方式 |
| --- | --- | --- |
| 用户问员工问题,候选里大量出现商品工具 | 路由授权面过宽 | 先收窄工具源和 scope |
| 经常在两个相似修改工具间选错 | operationId 或描述边界不清 | 重写声明并补充反例边界 |
| 搜索不到用户口语表达对应的工具 | 描述与领域词不足 | 增加自然语言用途和同义词 |
| 每轮都携带大量完整 Schema | 没有渐进式发现 | 先目录检索,再加载少量 typed tools |
| 向量服务异常后所有工具都不可用 | 缺少回退机制 | 增加稳定 lexical 排序或明确失败 |
| 搜索到工具但执行被拒绝 | 发现与权限不是同一问题 | 排查身份、路由、审批和业务鉴权 |
如果一个简单任务仍然需要模型同时比较几十个完整工具定义,就应该继续检查路由是否承担了太多场景,或者工具是否拆得过碎、命名过于相似。
如果你已经能搜索到写工具,但调用时仍提示无权限或需要审批,下一篇《AI Agent 为什么能查询却不能修改后台?》会继续从 route 写权限、`write_tools`、ACC 审批和业务侧实时授权逐层排查。
## 十三、BailingHub v0.5.0 在这件事上做了什么?
BailingHub `v0.5.0` 的 Agent Client Runtime v1 把“授权能力总量”和“本轮活动工具上限”区分开来。
Agent Client 获取工作区 profile 时,可以看到:
- `authorized_total`:当前身份、路由和治理交集下的授权能力总量;
- `active_limit`:运行时交给本地 Agent 的活动工具上限。
这两个数字不同,正是在表达:业务能力可以很多,但不应该把全部完整 Schema 一次性交给模型。
当本轮需要更多能力时,本地 Agent 可以调用当前工作区的能力搜索接口。搜索只在授权集内进行,一次返回的完整 typed tools 数量被约束在 1 到 12 之间;路由的 `tools.sources[]` 与每个来源的 `allow` scope 则在更前面裁剪能力面。
因此,公开链路可以概括为:
```text
业务侧声明能力
-> 路由按工具源和 scope 裁剪
-> 当前身份得到授权能力目录
-> 本轮先使用少量活动工具
-> 需要时在授权集内搜索
-> 加载少量完整 Tool Schema
-> 本地 Agent 编排
-> 中枢受治理执行
```
这并不意味着所有上下文优化问题已经解决,也不代表任意规模的工具目录都不需要治理。它解决的是一个基础结构问题:让本地 Agent 不必在每一轮都先吞下当前业务系统的全部工具定义。
## 常见问题
### 1. MCP Server 工具越多,Token 一定越高吗?
取决于 Agent 宿主怎样装配工具。如果宿主每轮都把全部完整 Tool Schema 发送给模型,工具越多通常意味着更多输入;如果采用授权面裁剪和按需加载,服务端目录很大也不代表每轮都要全量注入。
### 2. 直接把工具数量限制为 10 个不就行了吗?
固定截断会漏掉真正需要的工具。关键不是随意取前 10 个,而是先按场景和权限缩小范围,再根据本轮意图搜索相关工具,并允许任务中途继续发现。
### 3. 使用更大的上下文窗口能解决工具太多吗?
只能延后问题。更大的窗口可以装下更多内容,但不会自动消除相似工具误选、无关 Schema 干扰、费用增长和调试困难。
### 4. 工具搜索会不会让 Agent 发现未授权接口?
不应该。正确做法是先形成当前身份和路由的授权能力面,再在这个集合内搜索。检索层不能扩大权限边界。
### 5. 所有工具都需要做向量索引吗?
不一定。规模较小或命名规范的目录可以先用确定性文本检索;规模扩大、用户表达与工具描述差异明显时,再增加语义检索。无论是否使用向量,都应设计可解释的回退路径。
### 6. 搜索返回了写工具,就可以直接执行吗?
不可以。发现、开放、审批和业务鉴权是不同环节。实际写入仍要经过中枢治理和业务系统实时校验。
### 7. 工具越细,检索效果越好吗?
不一定。工具过粗会让参数和副作用边界不清,过细则会产生大量相似候选。应围绕稳定业务动作拆分,并保证每个工具有明确、互斥的使用说明。
## 结语:Agent 不需要背下整个后台,只需要随时找到正确能力
企业后台接入 AI Agent 后,真正可扩展的方式不是把全部接口一次性交给模型。
更合理的结构应该是:
1. 业务系统维护完整、可信的能力声明;
2. 中枢按路由、工具源、scope 和身份形成授权能力面;
3. Agent Runtime 为每轮任务装配少量活动工具;
4. 本地 Agent 需要更多能力时,在授权集内继续搜索;
5. 只有命中的工具才加载完整 Schema;
6. 每次实际调用仍然经过审批、审计与业务系统最终鉴权。
这样,新增业务系统或新增接口时,企业不必让每一次对话都为全部能力付出上下文成本;Agent 也不必在数百个相似按钮之间猜测用户到底想做什么。
能力目录可以很大,模型当下看到的工作台应该足够小。
这正是 MCP 和 Agent 从演示走向商城、CRM、ERP 与 SaaS 生产场景时,必须补上的一层能力装配设计。
---
项目与延伸阅读:
- [BailingHub GitHub](https://github.com/bailinghub/bailinghub)
- [BailingHub 官网](https://www.bailinghub.com)
- [Agent Client Runtime v1 接口说明](https://github.com/bailinghub/bailinghub/blob/v0.5.0/docs/AGENT_CLIENT_RUNTIME_API.md)
- [Agent Client v1 接入指南](https://github.com/bailinghub/bailinghub/blob/v0.5.0/docs/AGENT_CLIENT_QUICKSTART.md)
- [BailingHub v0.5.0 Release](https://github.com/bailinghub/bailinghub/releases/tag/v0.5.0)
- [提交一条真实 API 接入评估](https://github.com/bailinghub/bailinghub/issues/new?template=integration_evaluation.yml)