注:文章使用了 Pi 和 GPT 6.1 terra 进行整理和润色。并尽可能人工修改了一些不说人话的表述😂。
在 Pi 使用 GPT 模型并且用 Responses API 形式调用工具时,你可能见过这样的情况:明明只需要一个搜索关键词,模型却把工具定义里的可选参数也一一写出来,填上空字符串、空数组或 false。如果对应字段的 schema 允许 null,模型也可能填入 null。
这不一定是模型“过度积极”地使用工具,也不一定是提示词要求它这么做。一个值得检查的原因是:工具 schema 可能在发送或处理过程中变成了严格模式要求的形式,使原本可选的字段也必须出现在调用中。
从一个现象说起
假设一个搜索工具的 query 是必填参数,domainFilter、includeContent、provider 和 modelId 是可选参数。你只是想做一次普通搜索,期待的调用可能是:
1 | { "query": "Pi tool calling" } |
但实际看到的可能更像这样:
1 | { |
上面的 JSON 只是说明现象的简化示例,不代表某次真实请求。关键区别是:在普通 schema 中,可选字段通常可以省略;在严格模式所要求的 schema 中,对象的所有属性都需要列为必填。要保留“没有提供值”的语义,一种做法是在 schema 中明确允许字段取 null。严格模式本身不会自动允许或要求使用 null。
以这个例子中的 provider 和 modelId 为例:它们主要用于选择部分 LLM 提供商及其模型,以调用这些提供商的搜索能力。只有打算使用相应的 LLM 提供商搜索能力时,才需要按工具要求填写;普通搜索通常应当省略它们。若模型为了“填满参数”而传入空值或不匹配的值,工具可能尝试使用错误的提供商或模型配置,进而导致搜索失败。
因此,这不只是调用记录看起来冗长:模型多填的参数可能改变工具用途,甚至让原本能成功的调用因为多写了参数失败。
严格模式为什么会让字段变多
OpenAI 的函数调用支持严格模式,用来约束模型生成的参数符合工具的 JSON Schema。严格模式要求对象设置 additionalProperties: false,并将 properties 中的所有字段列入 required。要用 null 表达原本可选字段的“没有值”,schema 必须明确允许 null,例如将类型声明为 ['string', 'null'];如果字段只允许 string 但是这次调用不需要,模型也不会返回 null,而是空字符串代替。
因此,如果模型实际面对的是经过严格化的 schema,它就可能需要为每个字段生成一个符合该字段类型的值:允许 null 时可以使用 null,不允许时则必须选择其他合法值。这解释了为什么一个原始定义中有不少可选参数的工具,在调用时却可能看起来“参数写全了”。
关键差异:Pi 有没有发送 strict
下面的分析以 @earendil-works/pi-coding-agent 1.4.0 的 openai-responses 实现为范围;其他 Pi 版本、API 类型和兼容服务可能不同。
Pi 模型配置中的 compat.supportsStrictMode 是兼容性声明:它告诉 Pi 模型是否支持工具定义中的 strict 字段。它只是告诉 Pi 这个模型支持工具定义中使用 strict 字段。它不是直接调整模型行为的开关,更不会在设为 true 后所有工具都会启用严格模式。
1 | { |
以原始 schema 含可选参数、且没有声明 constrainedSampling 的 web_search 工具为例,这个设置会产生一个有点反直觉的区别:
supportsStrictMode |
Pi 发出的工具定义 | OpenAI Responses API 的预期行为 |
|---|---|---|
| 未声明支持 | 省略 strict |
尝试将 schema 规范化为严格模式;不兼容时回退到非严格模式 |
| 声明支持 | 显式发送 strict: false |
使用非严格模式 |
也就是说,对这个特定工具和 API 路径,声明接口支持 strict,反而可能让 Pi 明确发送 strict: false,从而让模型只填写需要的可选参数。相反,不声明支持时,Pi 省略该字段,而 Responses API 会尝试严格化 schema。
这里一定要区分两件事:supportsStrictMode: false 表示 Pi 不会填入和发送 strict 字段;请求里的 strict: false 则是明确要求非严格模式。省略strict字段并不等于发送 false。(这个要看服务商的默认值逻辑)
这也不是所有 OpenAI API 的共同默认值:根据 OpenAI 文档,Chat Completions API 省略 strict 时默认使用非严格模式。第三方兼容接口或代理可能采用不同的规则,应以实际服务的行为为准。
为什么开启后,内置工具仍然会带上 null?
如果你开启了 supportsStrictMode,却仍然看到 read、bash 等工具的可选参数以 null 出现,这并不一定与上面的分析矛盾。
Pi 的内置 read、bash、edit 和 write 等工具声明了对严格 JSON Schema 的偏好:
1 | { |
对于支持 strict 的接口,Pi 会尝试转换这些工具的 schema:禁止额外属性、将所有属性列为必填,并显式修改原本可选属性的类型,使其接受 null。转换成功时,Pi 发送 strict: true;如果无法转换,prefer 允许回退到非严格方式。这里能填写 null 是因为转换后的 schema 允许它,而不是严格模式自带这一行为。
所以,同一个兼容性设置下,不同工具可以有不同结果:没有声明严格采样的 web_search 可能省略可选参数;内置工具的可选字段在被转换为必填且可为 null 后,则可以用 null 占位,但也可以填写其他符合 schema 的值。是否严格取决于工具声明、schema、API 实现和兼容性配置的组合。
如何判断自己的情况
在一次对比中,未开启兼容性标志时,多次观察到工具调用的字段均被填写;开启后则观察到只填写所需字段的调用。这与 Responses API 的默认严格化机制相符,但还不是完整证明:会话日志中的原始工具声明和模型调用结果,并不一定包含实际发送的请求体或服务端规范化后的 schema。
排查时可以依次确认:
- 确认 API 类型和服务商。 是 OpenAI Responses API、Chat Completions API,还是第三方兼容接口?它们对省略
strict的处理可能不同。 - 查看实际发出的工具定义。 检查
strict是被省略、设为false,还是设为true;同时检查 schema 的required和字段类型。模型层的兼容性设置可能覆盖 provider 层设置。 - 检查接口返回的工具定义。 根据 OpenAI 文档,Responses API 无法将 schema 规范化为严格模式而回退时,响应中的工具定义会显示
strict: false。结合请求和响应判断,比只看模型输出可靠。 - 检查具体工具的声明。 它是否要求或偏好严格采样?原本可选字段是否被转换为必填且可为
null? - 核对多余参数的实际效果。 空数组、空字符串、
false和null不应自动视为“没传”。提示词和模型输出的随机性也可能影响观察结果。
配置 supportsStrictMode 前,还需要确认实际接口接受 strict 字段;如果接口不支持,启用该兼容性声明可能导致请求失败。
总结
GPT 在 Pi 里把工具参数写全,可能是严格 schema 的结果,而不只是模型自己的填写习惯。尤其是在上述 Pi 版本的 openai-responses 路径中,对没有声明严格采样的工具而言,Pi 省略 strict 与显式发送 strict: false,可能导致 Responses API 采用不同的处理方式。