你给 Agent 提供了两个工具:updateUser 和 deactivateUser。一张工单写着“关闭此账号”。Agent 调用了 deactivateUser。而上周,一张几乎相同的工单却让它调用了 updateUser 并带上 status: "closed",API 接受了该请求,但在下游业务中其含义却略有不同。
系统并没有报错。模型只是在两个看似合理的选项之间进行选择,而这两个选项的描述并没有告诉它到底该适用哪一个。工具选择失误是一种人们往往归咎于模型、但实际上需要在 Schema(数据模型)中予以修复的失败模式,因为 Schema 是模型唯一的决策依据。
本指南将涵盖模型在选择工具时实际读取的内容、如何编写具有区分度的名称和描述、parameter 设计如何改变错误率,以及如何测试工具选择以防止措辞调整隐式破坏原有逻辑。一旦你的工具是通过规范(spec)生成的(如我们关于将 OpenAPI 规范转换为 Agent 工具的指南中所述),问题就变成了该在规范中写入什么内容。
如果你的工具来自接口定义,那么 Apifox 就是维护这些描述的地方,因此改善描述可以同步提升文档和工具的质量。
AI Coding 交流群
如果你也在用 AI 写代码,或者正在研究 Cursor、Claude Code 这些工具,欢迎加入以下交流群。群里平时会聊一些 AI 编程的实际用法、开发工作流,还有各种新工具和新玩法。
模型究竟看到了什么
在做出选择的那一刻,模型拥有的信息包括:上下文对话、系统提示词(system prompt)以及一份工具定义列表。每个定义都包含名称、描述和 parameter schema。它无法读取你的接口文档、代码注释,或是团队内部才知道的“updateUser 是旧版遗留接口”这类隐性知识。
这意味着所有的歧义消除都必须直接写进工具定义本身。OpenAI 函数调用指南和 Anthropic 工具使用文档都强调了同一点:描述是整个工具定义中最关键的文本,宜详尽而不宜过简。
工具选择错误主要有四种形态,每种都有针对性的修复方案:
- 当两个定义重叠时,模型会误选相似的工具。 修复方法:改进描述,明确说明“何时不应使用”该工具。
- 当没有任何描述匹配任务用语时,模型什么工具也不选,而是凭记忆回答。 修复方法:使用用户常用的词汇。
- 当参数含义模糊时,模型虽然选对了工具,但传参错误。 修复方法:通过类型、枚举值(enum)和单位予以约束。
- 当工具调用存在顺序要求却未明确声明时,模型链接工具的方式会出错。 修复方法:在描述中显式说明前置条件。
根据工具的具体功能进行命名
工具名称所传达的信息量远超其字面长度,因为模型首先读取的就是名称。
在整个工具集中保持统一的风格,使用 verbNoun(动词+名词)的命名规范:例如 createOrder、refundOrder、getOrderStatus。一致性与单个名称的选择同等重要,如果工具集中混杂着 order_create、getOrder 和 refund,会使每个名称的可读性都打折扣。
要明确具体的操作对象。search 不是一个好的工具名称。searchCustomersByEmail 则是一个好名称,因为它同时告诉了模型搜索的内容和搜索的方式。
避免使用内部专业术语。如果你的 API 将客户(customer)称为“entity”,将订阅(subscription)称为“instrument”,模型就无法将它们与写着“customer”和“plan”的工单联系起来。请根据任务语言而非数据模型(schema)的语言来命名工具。
切勿跨上下文重复使用名称。一旦两个在不同命名空间中都叫 list 的工具出现在同一个列表中,就会引发歧义。
编写具备区分度的描述
一份有用的描述需要回答四个问题:它做什么、它改变什么、何时使用它以及何时不使用它。
这是一个区分度较弱的示例组:
{ "name": "updateUser", "description": "Updates a user." }
{ "name": "deactivateUser", "description": "Deactivates a user." }
这是一个能有效区分的示例组:
{
"name": "updateUser",
"description": "Updates profile fields on an active user, such as name, email, or timezone. Use for corrections and profile edits requested by the user. Does NOT change account status. To disable an account, use deactivateUser instead. Do not use to close or cancel an account."
}
{
"name": "deactivateUser",
"description": "Disables a user account, revoking all sessions and blocking sign-in. Reversible with reactivateUser. Use when a customer asks to close, cancel, pause, or suspend their account. Does NOT delete data. For permanent deletion use deleteUser, which cannot be undone."
}
这里运用了四项技巧。
指出兄弟工具。 在模型正在对比两者的关键时刻,“改用 deactivateUser”(Use deactivateUser instead)可以直接消除歧义。
包含用户的常用词汇。 描述中出现了“close”(关闭)、“cancel”(取消)、“pause”(暂停)和“suspend”(挂起)等词,因为这些正是工单中经常出现的词汇。这是你能做的投资回报率最高且几乎零成本的修改。
说明它不做什么。 否定句式往往比肯定句式更具区分度,因为相近工具的正面描述通常看起来大同小异。
标注可逆性。 当你告诉模型存在风险时,它才会去推理风险。这可以配合我们关于 AI agent 防护栏(guardrails)文章中的执行模式一起使用,而那里才是真正发挥防护作用的地方。
描述长一点没有关系。如果一段百字左右的描述能避免一次对破坏性接口(endpoint)的错误调用,那就是非常划算的。
精心设计 parameter 以减少错误参数
一旦选定了正确的工具,接下来容易出错的就是参数(argument)了。
JSON Schema 为你提供了此处所需的大部分约束条件,值得快速浏览一下 JSON Schema 校验词汇表,了解你的工具调用 API 所支持的关键字。
只要集合是封闭的,就使用枚举(enum)。 将类型设为字符串的 status parameter 容易让模型自由发挥;而将其指定为 enum 则能约束模型仅传入 API 可接受的值。
"status": {
"type": "string",
"enum": ["pending", "paid", "refunded", "cancelled"],
"description": "Order status. 'cancelled' means never fulfilled; 'refunded' means fulfilled then reversed."
}将单位放入名称中。 amount 是模糊不清的,模型会不一致地猜测是美元还是美分。而 amount_cents 则绝不会产生歧义。同理,timeout_seconds、distance_meters 和 duration_ms 也是如此。
为日期格式提供示例。 比起单纯写着“start date”(开始日期),提供 "description": "Start date in ISO 8601 format, for example 2026-08-26" 能大幅提高模型生成正确格式日期的概率。
保持必填列表准确客观。 将所有内容都标记为可选会把错误推迟到运行时;而将 API 默认已有合理设定的内容标记为必填,则会导致模型虚构数值。这两种情况都很常见,并且都会引发校验错误,具体可参阅我们关于 Agent API 错误设计的文章。
优先选择扁平化而非嵌套。 模型在填充 {"customer": {"address": {"postal_code": "..."}}} 时容易犯结构性错误,而在处理 customer_postal_code 时则不会。建议在工具边界处进行扁平化处理,并在执行器中重新组装。
拆分重载的工具。 如果一个工具包含一个 mode parameter,且该 parameter 会改变其他所有字段的含义,那么它本质上是两个工具。将其拆分可以提高选择准确度并简化两个数据模型。
明确前提条件与调用顺序
当模型不知道操作顺序时,多步骤工作就会失败。请在依赖工具的描述中明确说明:
{
"name": "captureCharge",
"description": "Captures a previously authorized charge. Requires an authorization_id from authorizeCharge. Call authorizeCharge first if you do not already have one. Cannot capture more than the authorized amount."
}仅需两行说明,顺序问题就在模型读取信息时得到了解决。这类规则普遍适用:先创建再更新、先上传再处理、先授权再扣款。如果依赖步骤的描述没有提及前置步骤,模型很可能会跳过它。如果调用顺序跨越多个 Agent 而非多次调用,我们在子 Agent 之间传递上下文的文章中提到的交接规则同样适用。
像测试其他行为一样测试工具选择
描述也是代码,同样会出现退化。如果有人为了符合代码风格指南而缩短了某条描述,可能下周二 Agent 就会开始选错接口。
构建一个小型选择测试集。编写 20 到 50 个提示词(prompt),每个提示词对应你期望调用的工具。运行它们,记录模型选择的工具,并仅对名称进行断言。参数在不同运行中可能会有所变化,但工具的选择不应发生改变。这也是我们在非确定性 Agent 测试指南中所提方法的具体实践方式。
在测试集中优先加入最容易出错的用例:
- 工具集中最相似的两个工具,并附带应路由到各自工具的提示词。
- 使用客户日常词汇而非 API 词汇的提示词。
- 不应匹配任何工具的提示词,此时正确的行为是向用户询问而非强行发起调用。
- 具有破坏性的工具,选错工具会产生实际代价。
将每个 prompt 运行多次。如果一个工具在五次测试中胜出四次,这在生产环境中就像是在抛硬币赌运气,说明其描述还需要继续优化。
将运行指向 mock,这样选择测试就永远不会触及线上真实数据。我们关于在 mock 而不是生产环境中运行 Agent 的文章涵盖了相关配置,而 Apifox 可以基于生成工具的同一份接口定义来提供这些 mock 服务,从而保持数据模型和行为的一致性。

以相同方式出错的三类集合
CRUD 集合。 API 暴露了 getUser、listUsers、searchUsers 和 queryUsers,这些都是从多年来累积增加的接口中生成的。对模型而言,这只是同一个概念的四个不同名称。解决办法不是为这四个接口都编写更好的描述,而是仅向 Agent 暴露其中的一个,并将其余接口排除在工具列表之外。精选的工具集每一次都会胜过面面俱到的完整工具集。
管理(Admin)集合。 读取类工具与破坏性工具并排摆放,语气毫无二致:getInvoice、voidInvoice、deleteInvoice。文本中没有任何信息表明其中两个操作可能会带来灾难性后果。在描述中添加操作后果,将它们标记为需要审批,并在执行器中落实强制约束,而不是盲目信任提示词用词。我们关于防止 Agent 毁坏 API 的文章中详细介绍了这种分层防范的方法。
遗留(Legacy)集合。 两个接口执行相同的任务,其中一个已被废弃。接口定义中仍然列出了两者,因此生成器也导出了两者,导致 Agent 大约有一半的时间会误选旧接口。要么从生成的工具集中直接移除废弃的操作,要么在描述的开头加上“Deprecated. Use createOrderV2 instead.”(已废弃。请改用 createOrderV2。)当这句话放在最前面时模型会遵守,但如果埋在末尾,模型往往会忽视它。
描述是共享的配置
一旦你接受了“工具描述决定行为”这一观点,下一个问题就是:谁来维护它们?在大多数团队中,答案往往很随意:最初配置 Agent 的那个人,保存在他本地机器上的某个文件里。
相反,应该将工具集视为一种共享构件,像审查其他接口一样对其进行评审。围绕 Agent 构建的平台通常会直接对此建模。一个 Sharkly Agent 是包含指令、Runtime、Skills 和代码库的保存配置,将其在 Space 中共享可以让一个人的工作配置被整个团队复用。其价值不仅在于存储,更在于对描述的修改变成了一项可评审、会影响所有人的变更,而不是某个开发者在本地静默修改,导致自己的 Agent 行为与其他人产生偏差。

注意用户使用的词汇
最常见的差距在于词汇。你的 API 中写作 subscription,而你的客户可能会说 plan、membership 和 billing。你的 API 中写作 deactivate,他们可能会说 cancel、close 和 turn off。
收集真实的用户语言。从支持工单、搜索日志或运行失败的 Agent 记录中提取热门短语,然后将它们融入对应工具的描述中。这只需花费一小时,但对提升选择准确率的作用,通常比任何针对数据模型(schema)的调优都要显著。
也要时刻留意失败的情况。当 Agent 没有选择任何工具并基于自身知识进行回答时,这属于词汇未匹配(vocabulary miss),而非推理失败。任务描述的语言与工具文本完全没有重合,导致工具变成了“不可见”状态。
工具集检查清单
- 命名遵循统一的
verbNoun规范,并明确指定具体对象(object)。 - 每条描述都明确说明变更内容、适用场景以及禁用场景。
- 存在功能重叠的工具要显式提及彼此。
- 描述中应包含用户实际使用的词汇,而不仅仅是内部术语。
- 破坏性或不可逆的操作必须在描述中明确指出。
- 为每个封闭集合声明枚举(Enums)。
- 单位和格式应写在 parameter 名称或描述中,并附带示例。
- 必填项列表与 API 实际强制要求的规则保持一致。
- 存在依赖关系的工具必须注明其前置条件。
- 在 CI 中基于 mock 运行选择测试集。
模型是在对你编写的文本进行模式匹配。当它选择错误时,文本是你首先应该排查的地方,而且通常也是唯一需要修改的地方。如果你希望在一个项目里管理描述、mock 和测试(tests),可以下载 Apifox。
常见问题解答
工具描述应该写多长? 足够消除歧义即可,通常为两到五句话。描述确实会占用上下文空间,因此可以精简那些无歧义工具的描述,把空间留给功能相近、容易混淆的工具。
我应该在描述中放入示例吗? 对于格式和单位,是的,示例可以消除一整类错误。避免使用冗长的用法示例,因为它们会消耗上下文,且很少能改变选择结果。
拥有大量单一用途的细粒度工具,还是少量灵活的工具更好? 在一定范围内,细粒度工具更好。每个工具只做一件事,选择起来更加可靠。但当数量超过几十个时,列表本身就会成为问题,你需要进行过滤或检索,正如我们在关于从 OpenAPI 生成 Agent 工具的文章中所讨论的那样。
我能否改为在系统提示词中修复工具选择问题? 部分可以,对于一两个已知混淆点,这是一种合理的权宜之计。但它无法扩展,因为 prompt 是所有工具共享的,而描述是随特定需要它的工具一起传递的。
如果模型总是凭空捏造 parameter 值怎么办? 约束其类型,添加枚举(enum),并在描述中明确说明该值必须来自先前的调用而非自行构建。如果问题依然存在,请在包装层中进行校验,并返回包含允许值的错误信息。
这些规则也适用于 MCP 服务端吗? 是的。MCP 服务端(server)以相同的形式暴露名称、描述和数据模型(schemas),因此相同的用词规则同样适用。我们关于什么是 MCP 的解说文章涵盖了协议本身。
开发必备:API 全流程管理神器 Apifox
介绍完上文的内容,我想额外介绍一个对开发者同样重要的效率工具 —— Apifox。作为一个集 API 文档、调试、设计、测试、Mock、自动化测试于一体的工具,Apifox 是目前提升研发效率的首选。
如果你正在开发项目,不妨试试其极其友好的界面设计,它完全兼容 Postman 和 Swagger 数据格式,导入数据非常方便,,即使是新手也能很快上手,点击这里即可注册使用。

值得一提的是,除了个人和常规团队使用,针对有高安全合规要求、或需要在内网环境协作的企业,Apifox 还提供了深度定制的私有化部署方案。
获取专属报价与部署方案
详细的私有化部署系统架构与安全白皮书
针对您公司规模的专属报价单
免费的 1v1 专属产品演示 (Demo) 机会