详细解释
**Tool Calling Strict Mode(工具调用严格模式,简称 strict=true)**一句话:你之前用 Function Calling 调内部订单 API 时,100 次里总有 1-2 次模型把「日期字段」写成「2025年8月」而不是 ISO 8601 格式、或者漏掉必填的「用户 ID」、或者传了 Schema 里根本没定义的「附加备注」字段,导致你后端调 API 失败、还要写一堆 if-else 兜底;strict=true 模式就是在模型生成的每一步里,用 Grammar 状态机 + 原生结构化训练双重强制,保证输出的 tool_calls 结构 100% 符合你在 tools 参数里传的 JSON Schema,错一个字段都不行。它本质上是 Constrained Decoding(约束解码) 在 Function Calling 协议上的具体应用。
strict=true 的工作机制是双保险:(1)SFT 原生结构化训练层——模型在训练时就专门训过「严格遵守 Schema 输出工具调用参数」的样本,骨子里就知道哪些字段必填、字段是什么类型;(2)推理时 Grammar Logits 掩码层——即使模型脑子里想胡写,在 next token 选择那一步,Grammar 状态机根据你传的 Schema 会把所有「当前这一步不合法的 Token」(比如必填字段还没输出时想直接结束、或者要数字字段时模型想输出汉字)的概率全部掩码成 -inf,模型只能选合法 Token,最终的 tool_calls JSON 保证 100% parse 正确。
唯元智创 的 /chat/completions 兼容端点 strict=true 是默认开启的(不需要你额外传参数),同时还加了第三层保险:如果模型输出的参数在反序列化之后仍然不满足 Schema(极端边缘 case),会把具体错误以 role=system error_message 的格式自动回塞给模型再自纠正一次,2 轮内纠正率 99.9%,你不用自己写兜底逻辑。
strict=true 与普通模式的 5 个核心差异(2025 年选型表)
| 维度 | 普通 Function Calling(strict=false / 默认) | Tool Calling Strict(strict=true) | 什么时候选哪个? |
|---|---|---|---|
| 参数缺漏率(必填字段没给) | 1% 至 2%(常见) | < 0.2% | 只要你的后端对参数容忍度低,无脑选 strict=true |
| 类型错误率(数字字段写成字符串、日期格式错) | 3% 至 5%(非常常见) | < 0.3% | 日期/金额/数量这种强类型字段多的场景,必须 strict=true |
| 附加字段(模型瞎塞 Schema 里没定义的字段) | 5% 至 10% | 0%(additionalProperties 会被强制执行) | 你后端用严格类型语言(Go/TypeScript strict mode)必须 strict=true |
| Enum 命中率(枚举值瞎填) | 90%(10% 填错成非枚举值) | 100% | 舱位等级、订单状态、城市白名单这种枚举场景一定 strict=true |
| Token 消耗 / 延迟 | 无额外开销,最快 | 首字延迟 +10ms 至 +30ms(Grammar 状态机校验每步推进) | 这 20ms 换来 10 倍错误率下降,99% 场景血赚 |
用好 strict=true 的 5 条黄金工程实践
- Schema 字段 description 必须写人话,不能只写 type——strict=true 能保证「类型对」,但不能保证「值对」:比如你有个 date 字段写
description: 下单日期模型可能填成今天(用户没说具体日期时),但如果你写description: 下单日期,ISO 8601 格式 YYYY-MM-DD,只允许从用户原话中提取的日期,不要猜测;用户没说时传空字符串,模型传对值的概率能从 70% 涨到 95%。 - 能 Enum 就 Enum,绝不搞自由字符串——城市字段如果你的后端只支持 31 个省辖市,就把 31 个全部列在 enum 里,不要让模型自由发挥写「上海」「上海市」「SH」三种写法;strict=true 会强制它只能从你列的 31 个里选一个,根本不会写错。
- required 数组要真实准确,后端必填的全列出来——后端实际上 user_id 和 order_id 是必填,但你 Schema 的 required 里只写了 user_id 没写 order_id,10% 的请求里模型会漏掉 order_id(因为 Schema 没说它必填);strict=true 会严格按照你的 required 列表强制「必须有」,所以把真实必填的都写上是前提。
- additionalProperties: false 一定要显式加——不加的话,模型偶尔会塞一些「我觉得可能有用」的附加字段(比如 comment、note 这种),你的后端 JSON 反序列化时如果是 strict=false 模式可能没报错但这些字段悄悄进了数据库,造成脏数据;显式声明 additionalProperties=false,strict=true 模式下模型绝对不会传 Schema 以外的字段,100% 干净。
- 嵌套对象里的每一层都要把上面 1-4 条再做一遍——别只给顶层 args 写 description,嵌套对象(比如 args.billing_address 里的省/市/区/街道每层)也同样要写 description、enum、required、additionalProperties。很多工程师只把顶层写得很认真,嵌套层就潦草应付,结果最后 80% 的 strict 模式错误都出在嵌套层。