指南/AI API 参数透传
AI API 参数为什么没有生效:从请求字段查到最终响应
请求 JSON 里写了 thinking、reasoning.effort 或输出上限,回答却没有明显变化。答案长短无法确认参数执行情况;排查要沿着最终请求、接口字段、模型支持范围和原始响应逐层核对。
JSON 里有这个字段,只能证明客户端发了它
调用方常把「序列化成功」「服务器返回 200」和「模型按预期执行」当成同一件事。它们属于不同阶段。
SDK 可以把任意扩展字段放进请求;接口可以接受但忽略不支持的字段;中转站还可以重命名、限幅或删除字段,再调用另一套上游接口。即使字段抵达模型服务,effort 这类控制也未必让每次答案出现肉眼可见的变化。
Anthropic 的官方 OpenAI SDK 兼容说明提供了一个明确例子:该兼容接口会接受多项 OpenAI 风格字段,但 reasoning_effort 等字段被忽略,多数不支持字段不会报错。因而 HTTP 成功与参数执行之间没有必然关系。
先把字段和接口对上
「多想一点」与「最多生成多少」要分开看。
| 原生接口 | 推理投入 | 输出边界 | 核对重点 |
|---|---|---|---|
| OpenAI Responses | reasoning.effort | max_output_tokens | effort 档位由模型决定,不能沿用另一型号的取值 |
| Claude Messages | output_config.effort、thinking | max_tokens | max_tokens 覆盖思考与正文,思考模式随模型变化 |
| Gemini GenerateContent | thinkingLevel 或 thinkingBudget | 按目标模型的生成配置核对 | 新旧模型支持的思考字段不同 |
OpenAI Responses API和模型指南分别给出字段定义与模型支持范围。Chat Completions、Responses 与旧 Completions 的字段并不完全相同;迁移时只改路径,旧参数可能落在错误的协议层。接口边界可继续参照“兼容 OpenAI API”到底兼容到哪一层。
Anthropic 的 Effort 文档说明,effort 影响文字、工具调用与思考的总投入,但不稳定控制可见回答长度。不同 Claude 型号支持的思考模式也不同,旧的手动预算不能直接搬到新型号,需按当前 Thinking 说明核对。
Google 的 Thinking 文档按模型区分 thinkingLevel 与 thinkingBudget。通过 OpenAI 兼容入口调用时,reasoning_effort 到 Gemini 配置的映射由兼容层定义,见 Gemini OpenAI compatibility。
字段名称相似,不代表预算口径、默认值和允许档位相同。排查前应固定「供应商原生接口、接口版本、模型 ID」这三个条件。
字段拼写也要原样保存。max_token、max_tokens 与 max_output_tokens 是不同的 JSON 键;接口或 SDK 没有明确记录别名映射时,不能假定它们会自动互换。中转层若接受未知字段又不报错,表面上的请求成功会把拼写问题留到响应阶段才暴露。
effort 是方向,输出上限是边界
推理投入通常是软控制。模型会结合任务难度、工具使用与内部策略决定消耗,低档位不保证每次都少用固定数量的 Token,高档位也不保证正文更长。两次答案字数接近,仍可能用了不同的推理量;答案更长,也可能只是措辞展开。
输出上限则规定不能越过的边界,但它不是目标长度。模型可以正常结束、调用工具、触发停止序列,也可以因思考消耗了可用输出空间而提前停止。判断是否碰到边界,应看响应的终止原因和官方定义的 usage 分类,不能用「少写了几段」代替。
这也是为什么参数比较必须与模型版本绑定。别名切换、模型快照更新或默认 effort 改变,都会让相同请求呈现新行为;这类长期变化见模型版本为什么会漂移。
Token 不是中文字符数
max_tokens、max_output_tokens 和 usage 里的计数单位都是 Token。Token 由对应模型的 tokenizer 划分,可以是一整个词、词的一部分、标点或其他文本片段。中文字符、英文字母、空格、JSON 符号与代码的切分方式不同,不存在跨模型通用的「一个汉字等于几个 Token」。
Google 的 Token 说明和 Anthropic 的 Token counting都要求按实际模型计数;Anthropic 还说明计数接口给出的是估算,最终 usage 可能因系统侧处理而有差异。三家 usage 的包含关系也不同,详见 OpenAI、Claude、Gemini 的 Token 为什么不能直接横向比较。
所以「设置了很大的 Token 上限,却只返回几百个汉字」本身没有诊断价值。上限不是最低产量,汉字数也不是 Token 用量。
反过来,如果同一笔原始响应报告的输出 Token 超过该接口已经接受的硬上限,证据会强很多。下结论前仍要排除字段名用错、客户端把重试结果合并、流式片段重复累计,以及界面把字符数当 Token 数。完成这些核对后,记录可以写成「请求上限与服务报告的单次输出用量不一致」,先不要延伸到模型身份或运营方动机。
中转层可能做了三类处理
兼容服务需要把客户端协议转换成上游协议,常见结果可归为三类:原样转发;按目标模型规则换算字段;对无法支持的字段删除、限幅或使用默认值。服务还可能把模型别名解析到不同上游,使原本有效的字段在新模型上失去意义。
这些处理未必带有欺骗意图。兼容层要面对多家接口差异,无法总是保持一一对应。问题在于调用方是否拿得到明确的支持范围、最终模型标识、终止原因和用量明细。若服务只返回一个统一答案对象,很多差异会被外形遮住。
客户端记录应区分三份对象:业务代码构造的参数、网络层最终发出的请求体、服务返回的原始响应。只有代码参数时,无法证明 SDK 插件或网关没有改写;只有返回对象时,也无法还原发出的字段。
重试与流式拼接会制造另一种错觉
重试中间件可能在超时或限流后重新发起请求。调用方看到的是后一次成功结果,前一次请求的参数、模型选择或中转路由未必相同。若日志只保留逻辑任务 ID,不保留每次尝试的请求 ID,就会把多次调用误认为一笔。
流式响应还会把思考块、工具参数增量、正文增量和结束事件分开发送。客户端若只拼接文字 delta,已经发生的思考也不会出现在最终文本里;连接中断后直接展示已收到的片段,则会把不完整输出误判为模型主动收尾。完整性边界见流式响应返回 200,为什么仍然可能失败。
一份能定位问题的最小记录
复现参数问题时,保存五组材料:业务代码构造的参数、网络层发出的最终 JSON、供应商与接口版本、每次尝试的请求 ID、未经 SDK 整理的原始响应。响应还要保留终止原因、完整 Usage 和流式终态事件。
随后用一组对照请求只改变一个字段。模型、prompt、工具、输出上限和其他参数保持不变;多次运行后比较终止原因与 Usage,不用单次答案字数做判据。官方直连与中转接口使用不同协议或模型时,结果不能直接归因给中转层。
若记录只能确认字段出现在最终请求里,结论写成「客户端已发送该字段,当前接口没有提供执行证明」。若服务方日志显示上游请求缺少或改写了字段,才能把变化定位到对应转换层。模型身份与运营方动机仍需其他证据。
LinkyMonitor 持续记录请求结构、响应终止原因与用量字段,让参数支持范围发生变化时能回到原始记录核对。