摘要: 把 MCP Server 对接 OpenAI Agents API 的一次对照推演中,Agent 全程没调用工具,却输出了一份很自信的答案。根因不在 Server 挂了,而在工具描述在"按需加载"机制下没被选中。用一次对照讲清工具描述写法的变化。

它没碰工具,还给我交了份作业

上一篇把 MCP Server 怎么接进 OpenAI Agents API 盘了一遍,临走前我留了个话头:工具描述在"按需加载"的世界里,评价尺度变了。写完之后我琢磨,光说不够,得拿自己的 MCP Server 做一次对照。

先把话说清楚:我目前没有 OpenAI Agents API 的接入环境,下面这次对照,是把官方文档里的机制和自己本地 MCP 测试台的观察摆在一起推演的。 真实的部分——本地测试台里 agent 用不用我的工具——是我反复跑过的;推演的部分——换到 Agents API 上会发生什么——我会标出来,不混着说。机制描述基于 2026.09.10 发布的公开测试版文档,接口还在迭代,你看到时如有变动以官方为准。

这个测试台就是写 MCP Client 那篇时搭的:一个带工具调用能力的 agent 客户端,挂上我的 MCP Server,任务丢进去能直接看到它到底调不调工具。下面的观察都发生在这里。

对照任务我选了个很朴素的:查一个订单的发货状态。这任务有个好处:正经答案只存在于工具里,agent 不调工具就只能编。 编得好不好,一对比就现形。

现象:答案挺自信,日志里空空如也

任务丢下去,agent 回来的东西长这样(本地测试台里的真实复现):

订单已发货,预计 3 天后送达,承运方顺丰,当前物流节点为"运输中"。

看着像那么回事。我去翻 agent 的工具调用记录,一条调用都没有——它从头到尾没用过我的 MCP 工具。

如果是真实环境,这只是运气好级别的胡编:送达时间、承运方、物流节点全是我系统里查无实据的数据。它根本没查,它是在凭常识写一份体面的快递播报。

问题就来了:Server 在本地跑着明明好好的,地址能通,工具列表也能拉出来(这串列表是我自己用 MCP 客户端拉出来的,不是 agent 拉的)。为什么 agent 一个都没用?

把这个疑问摆到 Agents API 的 tool search 机制里看,链路是这么推出来的(推演部分):

   Agent 要查订单发货状态
        │
        ▼
   Agent 需要"订单信息"  ──►  tool search 按需加载候选工具
        │                             │
        │                             ▼
        │             query_orders("处理订单数据")
        │             ── 描述太泛,需要的时刻没被选中
        │                             │
        ▼                             ▼
   Agent 拿不到工具结果  ────────► 改用"常识"直接编答案
        │
        ▼
   一份自信且查无实据的物流播报

排查:不是连不上,是没被选上

排查按三步走,每一步都有明确结论:

轮次怀疑点动作结果
第一轮连接失败翻 agent 的工具调用记录任务跑完,调用记录为零——压根没调用过
第二轮配置有误核对 agent 里的 server_label 和 server_url配置正确、地址可达
第三轮加载机制对照官方文档 tool search 的原话按需加载——问题出在"该加载时没选我"

第二轮有个容易被忽略的细节:配置正确并不代表被使用。server_label 只是标识哪台 server,agent 用不用它,取决于工具有没有进入自己的视野。很多人排查到这里就停了,以为是玄学,其实方向在第三轮。

官方文档里 tool search 的说法是:按需加载相关的工具定义,省 token、保持缓存命中(原文档)。我读到的重点是"按需"——agent 不会把你的工具全搬进上下文,它需要的时候选一批进来。我的工具没被调用,是因为连"被选"这一关都没过。

根因:描述写得泛,检索的时候选不中

问题定位到我 Server 里那个查订单工具的描述。为什么要看这段:它是这次事故里唯一的嫌疑人——同一台 Server、同一个地址,唯一被质疑的就是这串描述(这是"改前"的样子):

{
  "name": "query_orders",
  "description": "处理订单数据",
  "inputSchema": { "orderId": "string" }
}

把它放回本地测试台触发同一种查询任务,观察到的调用情况是这样的(本地观测):

// 触发任务:"这个订单发到哪了"
// 可用工具列表里 query_orders 一直在
// 但任务跑完,没有产生一次对 query_orders 的调用
// 输出是 agent 直接给出的"常识性"回答

问题就藏在这串描述里。面对"这个订单到哪了"的问题,agent 要走检索,我的描述是"处理订单数据"——词太宽了,任何订单类任务都能沾边,但都不精准。在按需加载的过程里,它可能被更具体的同类工具顶掉,也可能被归进"通用数据处理"一类,需要它的时候没被选中。

这里要严谨一点:官方没公开检索内部是关键词还是语义匹配,我不确定机制细节。但方向是确定的——官方机制就是按需加载,而我的工具在"按需"这一环就落了榜。

解决:描述写到"这个工具什么时候会被需要"

重写之后长这样:

{
  "name": "query_order_shipping_status",
  "description": "按订单号查询订单发货状态,返回预计送达时间",
  "inputSchema": {
    "orderId": {
      "type": "string",
      "description": "订单号,必填"
    }
  }
}

为什么要看这段:同样的底层逻辑,我把名字和描述从"它是什么"改成了"什么场景会用上它"。描述里带上了"发货状态、预计送达时间、订单号"这些和查询任务强相关的词,按需加载时被选中的概率会高很多——就像给检索器递了个正好能挂住问题的钩子。

改完我在本地测试台验证了同批任务,结果差异是实实在在的(这部分是本地观测):

改前描述(query_orders / 处理订单数据)
  → 任务"查订单发到哪了":agent 跳过工具,直接给了个编造的时间
  → 日志:无工具调用

改后描述(query_order_shipping_status / 按订单号查发货状态)
  → 同一任务:agent 先调工具拿到数据,再基于数据作答
  → 日志:出现该工具的调用记录

对比放在一起看更清楚:

改前改后
namequery_ordersquery_order_shipping_status
description处理订单数据按订单号查询订单发货状态,返回预计送达时间
面对"查发货状态"任务很像"通用数据处理",大概率没被选中与问题强相关,可用性一眼可见
本地测试台效果经常跳过工具直接编基本先调工具再作答

想复现的话,实验步骤在这

这套对照实验不依赖 Agents API 的接入权限,用你手头的 MCP 工具链就能跑,步骤不长:

  1. 准备一个 MCP Server(没有的话拿官方示例改一个也行),注册一个"查订单"类工具
  2. 挂一个带工具调用能力的 agent 客户端(我用的就是写 MCP Client 那篇搭的测试台)
  3. 先用泛描述(query_orders / 处理订单数据)跑"这个订单发到哪了",看调用日志
  4. 改成具体描述(query_order_shipping_status / 按订单号查询发货状态),同一个任务再跑一遍
  5. 对比两次日志里的工具调用次数

判断标准很简单:改前大概率零调用、直接作答;改后出现工具调用、结果基于数据。如果两次没有差异,先检查 agent 客户端是不是把工具全量塞进了上下文——那样就绕过了"按需加载"这一步,对照就失效了。

边界:这套改动在哪些情况下不灵

别把这篇的经验用过头,几个边界先讲清楚:

  • 推演建立在"按需加载是主要路径"的假设上。官方文档(Agents API overview)提到可以挂自定义函数和内置工具,也保留了程序化调用的能力,不是所有场景都走"检索选中"这一步。
  • 描述写得具体是有代价的:越具体描述串越长,和"省 token"的目标就互相拉扯,得在"够具体"和"别啰嗦"之间找平衡,没有标准答案。
  • 我没在真实的 Agents API 上复现完整链路(没有接入环境),所以"改描述后必然命中"这个结论,在你们真实环境里仍然要自己验一遍。工具是否被选中,还取决于任务描述、上下文和候选池大小这些变量。

总结判断

这次对照给我的触动不是"描述要写具体"——这我早就知道了。新东西是:工具描述不只是在给 agent 的"理解"供料,现在多了一层,是在给"检索"供料。 写得泛,等于把选择权交给运气;写到任务场景上去,才是主动递给 agent 钩子。

给同样在把 MCP 接进 agent 的人一句:你磨参数 schema 的功夫已经够了,接下来值得把一半的精心放在 description 上——那是 agent 决定用不用你的第一道门。MCP 协议本身的细节可以看 modelcontextprotocol.io。

真要在自己环境里接,开工前对着这三条过一遍:

  • 任务是不是真的依赖工具才有答案(不依赖的话,agent 不调用工具不算 bug)
  • 工具描述是不是写到"什么场景会用到它"(泛描述在按需加载下风险最高)
  • 有没有在能复现的测试环境里验过一遍(我没有真实接入,结论需要你自己复验)
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐