做 AI Agent 或工具调用时,很容易遇到一种奇怪的情况:API 明明没问题,模型也足够聪明,Tool 却总是调用错。
比如,一个"创建会议"的接口有二十多个字段。时间、时区、参会人、会议室、提醒方式、重复规则、权限、描述、标签......开发者很自然地把这些字段全部暴露给模型,然后期待模型自己决定该填什么。
结果往往是:参数漏填、格式填错、Enum 猜错,甚至模型为了满足 Required 参数,凭空补出一个值。
问题可能不在模型,也不在 API,而在 Tool Schema。
一个好的 Tool Schema,真正要解决的不是"怎样完整描述 API",而是怎样让模型更容易做出正确选择。
Schema 的第一目标,是减少模型的决策空间

很多开发者设计 Tool 时,会下意识地从后端接口出发:
"API 有哪些字段,我就定义哪些字段。"
这对传统程序调用很自然,因为程序员知道接口文档,也知道业务规则。但模型调用工具时,面对的是另一种问题:它需要根据自然语言,判断应该调用哪个 Tool、填写哪些字段、哪些值合法。
因此,Schema 每多暴露一个参数,都意味着多增加一次判断。
假设有一个发送消息的 Tool:
send_message(
receiver,
content,
format,
encoding,
retry_count,
timeout,
priority,
source,
trace_id...
)
对于底层系统来说,这些字段都可能有意义。但用户说的只是:
"给张三发一句:会议推迟到三点。"
模型真正需要决定的,也许只有 receiver 和 content。
其余参数如果可以由系统默认、上下文推断或后端处理,就没有必要交给模型。
判断一个字段是否应该出现在 Schema 中,可以问一个很简单的问题:
这个参数真的需要模型做决定吗?
如果答案是否定的,通常就应该藏在 Tool 后面。
参数要结构化,但不要为了结构化而结构化

Tool 参数究竟应该使用自然语言,还是拆成结构化字段?
一个实用原则是:机器需要精确执行的部分尽量结构化,需要保留用户表达弹性的部分可以使用自然语言。
例如创建日历事件:
title
start_time
end_time
attendees
显然比一个:
request: "明天下午三点跟 Alice 开一个小时产品会议"
更适合真正执行操作。
因为结构化参数便于校验、确认和传给下游系统。
但如果你设计的是"总结这份报告",参数未必需要拆成:
tone
length
audience
focus
style
format
有时一个:
instructions
反而更加灵活。
关键区别在于:这个参数最终是给机器执行,还是给模型理解?
时间、金额、ID、状态、数量这类具有明确边界的数据,适合结构化;复杂意图、补充要求、写作指令,则不一定值得强拆。
好的 Schema 往往不是"全部 JSON 化",而是在精确性和表达能力之间找到边界。
Required 越多,不代表调用越可靠

Required 参数特别容易被误用。
开发者常觉得:"这个字段后端必须有,那 Tool 里就应该 Required。"
问题是,后端需要一个字段,不等于用户一定会提供这个字段。
例如订餐 Tool 要求:
restaurant
date
time
party_size
如果用户只说:
"帮我看看今晚附近有没有不错的日料。"
此时把 restaurant 设为 Required 就很奇怪,因为用户压根没有指定餐厅。
模型为了完成调用,只能猜。
Required 的设计标准应该是:
没有这个信息时,模型是否应该停止调用?
如果缺少它就无法合理执行,而且也不能从上下文得到,那么设为 Required 很合适。
如果字段可以使用默认值、后端补全、二次查询,或者它只在特定场景下需要,就不要轻易 Required。
真正危险的不是字段缺失,而是 Schema 逼着模型编一个答案。
Enum 的价值,是把开放题变成选择题

Enum 是 Tool Schema 里非常有价值、也经常被低估的设计。
假设订单状态参数写成:
status: string
模型可能生成:
"已支付""paid""payment_completed""complete"。
这些在人看来意思接近,对系统来说却可能完全不同。
如果改成:
status: ["pending", "paid", "cancelled"]
模型面对的就不再是开放题,而是一道选择题。
Enum 的意义不仅是方便校验,它还在告诉模型:
这个世界里,合法答案只有这些。
因此,状态、类型、排序方式、权限级别、输出格式等有限集合,非常适合 Enum。
但 Enum 也不能滥用。
如果一个字段理论上可能有数百种值,甚至持续变化,把所有值塞进 Schema,只会制造新的上下文负担。这种情况更适合先调用搜索 Tool 获取候选项,再让模型选择。
参数一多,问题不是"看起来复杂"这么简单

参数过多最直接的后果,是调用准确率下降。
模型需要同时判断:哪些参数相关、哪些可以省略、字段之间有什么依赖、应该使用什么格式。
参数之间还可能形成组合复杂度。
例如一个旅行搜索 Tool 同时暴露:
出发地、目的地、日期、返程日期、乘客数、舱位、航空公司、价格区间、中转次数、行李规则、退改政策、机场偏好、时间段......
任何一个字段单独看都合理,放在一起却会让一次简单搜索变成复杂表单。
更好的做法通常是拆分职责。
先用一个简单 Tool 搜索航班,再用另一个 Tool 查询具体航班规则;或者只把高频过滤条件放在主 Tool 中,把高级条件放到专门的 Tool。
这也是为什么 Tool 设计应该关注"模型完成任务的路径",而不能只关注后端数据结构。
不要把底层 API 直接翻译成 Tool

这是 Tool Schema 设计中最重要的一条原则。
Tool 是给模型使用的接口,不是后端 API 的镜像。
底层 API 也许需要:
user_id
tenant_id
request_id
locale
timezone
source
permission_scope
但其中很多信息完全可以由运行环境自动注入。
如果模型根本不应该决定 tenant_id,就不应该让它看到这个字段。
甚至底层有十个 API,也不代表一定要暴露十个 Tool。有时可以按照用户意图重新封装:
"查订单"
"取消订单"
"修改收货地址"
这种设计比直接暴露:
GET /orders
PATCH /orders/{id}
POST /order-actions
更符合模型理解任务的方式。
可以把 Tool 看成一层"面向 AI 的产品接口"。
后端 API 优化的是系统能力和工程复用;Tool Schema 优化的是模型判断、参数生成和任务成功率。两者关注的问题并不相同。
设计 Schema 时,先问这五个问题

真正实用的检查方式并不复杂。
每设计一个 Tool,可以依次检查:
这个 Tool 的职责能不能用一句话说清楚?
每个参数是否真的需要模型决定?
缺少某个 Required 参数时,模型应该询问用户,还是系统其实可以自己补?
有限选项能否改成 Enum,减少自由生成?
有没有底层实现字段泄漏到了模型这一层?
如果一个 Tool 让你必须写很长的说明,才能让模型知道什么时候调用、参数怎么组合、哪些字段不能同时出现,往往说明 Schema 本身已经过于复杂。
好的 Tool Schema 有点像好的产品界面。
用户不会因为一个页面拥有更多按钮,就觉得它功能更强;模型也不会因为一个 Tool 暴露更多参数,就调用得更聪明。
真正好的设计,是把复杂度留在系统内部,只把必要的决策交给模型。
所以设计 Tool 时,与其不断问"还有什么字段没有暴露",不如反过来问一次:
为了让模型完成这个任务,它最少需要知道什么?
这个问题,往往比任何 Schema 规范都更有用。
