工具定义三要素
名称、描述、参数 Schema:模型只能通过它们理解工具。
一句话理解:工具定义是写给模型看的说明书。名称决定它能不能被找到,描述决定它会不会被用对,参数 Schema 决定填进去的值合不合法。
研发交给你一份接口文档,你把它原样包装成工具,Agent 却总是选错工具、漏填参数、把日期格式写反。问题往往不在模型,而在工具定义:模型看不到你的代码和数据库,只能 读这三样东西。Anthropic 的工具定义指南也说得很直接:详细的描述是影响工具表现的最重要因素,没有之一。
对产品经理来说,工具定义是最值得亲自评审的研发产物之一。它篇幅短,却直接决定 Agent 的行为。
三要素各管什么
三要素各管一件事,下图先给出整体关系,表格再列出具体症状:
| 要素 | 模型用它来 | 写不好的症状 |
|---|---|---|
名称 name |
在一堆工具里快速识别“这是干什么的” | 选错工具、两个相似工具来回切换 |
描述 description |
判断何时该用、何时不该用、返回什么、有什么限制 | 该用不用、滥用、误解返回值 |
| 参数 Schema | 知道要填哪些字段、每个字段的类型、格式和取值范围 | 漏填、类型错误、编造取值 |
平台之间字段名略有差异:Claude 叫 input_schema,OpenAI 叫 parameters,MCP 叫 inputSchema,但内容都是 JSON Schema(基础见 JSON 与 JSON Schema)。Claude 要求名称匹配 ^[a-zA-Z0-9_-]{1,128}$,所以名称统一用英文加下划线,中文说明放进描述里。
一个反例和它的改写
电商订单场景里,研发给出的第一版定义:
{
"name": "query",
"description": "查询订单",
"input_schema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"type": { "type": "string" }
}
}
}
问题清单:query 什么都可能是;“查询订单”没说查哪类订单、返回什么;id 是订单号、支付流水号还是客户 ID?type 能填什么?哪些字段必填?
改写后:
{
"name": "get_order",
"description": "按订单号查询一个订单的当前状态。返回订单状态、下单与支付时间、订单金额、最近一次状态变更时间、是否存在售后异常及异常原因代码。适用于用户询问某个订单“到哪一步了”“为什么还没发货”“是否有售后”。不返回支付流水与退款明细,支付与退款问题请使用 get_payment。订单号不存在时返回 not_found 错误。",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单号,格式为 ORD 加 10 位数字,例如 ORD2026090142。用户给出的订单号带空格或小写时,先去掉空格并转为大写。",
"pattern": "^ORD[0-9]{10}$"
},
"include_items": {
"type": "boolean",
"description": "是否同时返回完整的商品明细。只在需要核对具体商品时设为 true,明细较长会占用更多上下文。默认 false。"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
描述怎么写:五个问题
Claude 的文档建议每个工具的描述至少写 3–4 句,复杂工具写得更多。一个实用模板是回答五个问题:
- 做什么:一句话说清功能,“按订单号查询一个订单的当前状态”。
- 什么时候用:列出触发场景,最好用用户的原话,“到哪一步了”“为什么还没发货”。
- 什么时候不用:指向替代工具,“支付与退款问题请使用
get_payment”。这一条最能减少选错工具。 - 返回什么:列出关键字段,让模型知道结果里有什么、没有什么,避免它以为查不到就是“没有”。
- 限制与边界:时间范围、分页、权限、错误情况,“只能查询近 90 天的订单”。
OpenAI 的函数调用指南给了一个很好用的检验方法,叫“实习生测试”:只把你给模型的定义交给一个新来的实习生,他能不能正确使用这个工具?如果他会问问题,就把答案写进描述里。
参数 Schema:让错误无法发生
描述靠模型理解,Schema 靠机器约束。能用 Schema 表达的规则,就不要只写在描述里:
| 手段 | 例子 | 作用 |
|---|---|---|
required |
["order_id"] |
明确必填字段,缺了会被拦下 |
enum |
"status": { "enum": ["pending", "paid", "failed"] } |
把自由文本变成有限选项,避免模型自创取值 |
pattern / format |
订单号正则、"format": "date" |
约束格式 |
minimum / maximum |
"days": { "minimum": 1, "maximum": 7 } |
约束范围,防止一次拉取海量数据 |
additionalProperties: false |
禁止多余字段 | 防止模型塞进不存在的参数 |
字段级 description |
单位、时区、示例值 | 消除歧义 |
OpenAI 的指南提醒:用枚举和对象结构让非法状态 无法表达。比如 toggle_light(on: bool, off: bool) 允许 on 和 off 同时为 true,改成 set_light(state: "on" | "off") 就不会出错。
两个平台都支持 strict: true,开启后模型输出的参数严格匹配 Schema。OpenAI 的 strict 模式有额外要求:每个对象都要设 additionalProperties: false,properties 里所有字段都要列进 required,可选字段用 null 作为类型之一来表示。对复杂、格式敏感的工具,Claude 还支持 input_examples 字段,提供几组符合 Schema 的参数示例,代价是多占一些输入 Token。
描述写得再好,参数仍然是模型生成的。Schema 与程序校验构成前后两道防线:
名称:短、准、带命名空间
- 动词 + 对象:
get_order、search_orders、create_ticket。一看就知道是读还是写。 - 同一系统加前缀:工具多起来之后,
orders_get_order与crm_get_order不会混淆。Claude 与 OpenAI 的文档都建议按服务命名空间组织工具。 - 避免近义词并存:
get_order、fetch_order、query_order同时出现,模型只能靠猜。 - 名称一旦上线就别随意改:Prompt、评测集、Trace 看板都依赖它。
常见误区
- “描述写短点省 Token。” 省下几十个 Token,换来的是选错工具和多轮重试,总成本更高。真正该省的是返回值,见 工具设计原则。
- “接口文档直接转成工具定义就行。” 接口文档写给工程师看,默认读者懂业务背景;工具定义写给模型看,必须把隐含知识写出来。
- “Schema 限制住了,就不用程序校验了。” Schema 只管格式。订单是否存在、是否属于当前用户,必须由程序检查。
产品经理检查清单
评审一份工具定义时逐条过:
- 只看名称,能猜出它是读操作还是写操作吗?
- 描述回答了“做什么、何时用、何时不用、返回什么、有什么限制”吗?
- 有没有两个工具职责重叠、描述又没有互相指路?
- 每个参数都有描述、示例和格式约束吗?能用枚举的是否用了枚举?
- 必填字段对吗?有没有本该由程序注入、却让模型填写的参数(比如用户 ID)?
- 开启
strict了吗?如果没有,原因是什么?
练习
找到你们产品里(或设想中)订单售后场景的 3 个接口:订单查询、支付与退款查询、发货记录查询。
- 先按“接口文档直译”的方式写出 3 个工具定义。
- 用五个问题模板重写描述,补齐字段级描述、枚举和
required。 - 设计 5 个用户问题,其中至少 2 个容易让模型选错工具(例如“这单的钱退了吗”)。对照改写后的描述,判断模型能否选对;选不对的,继续改描述。
延伸阅读
- Define tools(Claude):工具定义字段、描述最佳实践、
input_examples与tool_choice。 - Function calling(OpenAI):函数定义、命名空间与 strict 模式要求。
下一步
定义写清楚之后,更大的问题是:该给 Agent 哪些工具、每个工具做多大?下一课「工具设计原则」来回答。