重命名是容易的一半。容易让人踩坑的另一半是新字段的计数方式——max_completion_tokens 同时涵盖推理和可见输出,因此按答案本身设置的预算会返回空内容,finish_reason 为“length”,并且仍会计费。
错误
{
"error": {
"message": "Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.",
"type": "invalid_request_error",
"param": "max_tokens",
"code": "unsupported_parameter"
}
}原因和解决方法一览
| 原因 | 解决方法 |
|---|---|
| 推理模型系列替换了该字段 | 在这些模型上使用 max_completion_tokens,而不是 max_tokens。 |
| SDK 或封装程序固定使用旧字段 | 升级它,或显式设置字段,而不是通过辅助函数设置。 |
| 一条代码路径分发到多个提供商 | 在边缘层统一处理一次,而不是按模型分支。 |
| 修复后答案为空 | 预算包含推理令牌——将其设置得明显高于预期输出。 |
重命名字段
在调用位置,这是直接替换。请求的其他部分都不变。
# Before
resp = client.chat.completions.create(
model="gpt-5-6-sol", max_tokens=1024, messages=msgs)
# After
resp = client.chat.completions.create(
model="gpt-5-6-sol", max_completion_tokens=1024, messages=msgs)为你看不到的推理留出预算
max_completion_tokens 会共同限制推理令牌和可见输出。如果模型在 1,024 的上限下花费 900 个令牌进行思考,你只能得到 124 个答案令牌——或者得到 finish_reason 为“length”的空消息,并为全部令牌付费。请同时为两者设置预算,并在信任空响应之前检查 finish_reason。
统一处理一次,而不是按模型分支
在代码边缘设置一个统一辅助函数,可以让其余代码与提供商无关,也能避免下一个模型系列再次引发一轮修改。
def token_budget(model: str, n: int) -> dict:
"""One place that knows which spelling a model wants."""
if model.startswith("claude-"):
return {"max_tokens": n}
return {"max_completion_tokens": n}
resp = client.chat.completions.create(
model=model, messages=msgs, **token_budget(model, 4096))预期会出现相关字段的拒绝
删除 max_tokens 的同一批模型系列通常也会拒绝 temperature 和 top_p。修复这一项后,下一项问题往往会暴露出来;请移除不受支持的采样参数,而不是将其设置为默认值。
如果你通过 Kunavo 调用
Kunavo 的 /v1/chat/completions 接受 GPT-5.x 推理系列中的 max_tokens:转换器会读取你发送的两个拼写形式中的任意一个,并将其映射到上游字段;/v1/responses 也执行相反方向的映射。因此,在这些模型上无需重命名。明确说明一个未被掩盖的不对称之处:对于 claude-* 模型,聊天转换器目前只读取 max_tokens,因此请为 Claude 发送该拼写——上面的辅助函数正是如此处理的。
常见问题
max_completion_tokens 只是重命名吗?
在调用位置是,但含义不是——它同时限制推理令牌和输出,而 max_tokens 只限制可见输出。
为什么修复后响应为空?
预算被推理消耗了。检查 finish_reason:如果内容为空且为“length”,就提高上限。
必须按模型分支吗?
在 Kunavo 的 GPT 系列中不需要——两种拼写都接受。只有 claude-* 模型需要分支,或者在所有地方使用一个统一辅助函数。