Sonnet 5 升级后 API 为什么突然 400 还返回空结果?按 migration guide、thinking 和 stop_reason 修掉旧代码

维护咨询 大模型部署 问题解决 技能定制 大模型训练

站长交流微信: aixbwz

如果你最近把 Anthropic API、Claude 工作流、提取脚本或自建 agent 从 Sonnet 4.x 升到 Sonnet 5,然后马上撞上这些问题,这篇就是给你的:

  • 模型名一换成 claude-sonnet-5,请求就直接报 400
  • 同样的代码在 Sonnet 4.6 正常,到了 Sonnet 5 突然开始空结果、截断或 JSON 解析错
  • 长文档抽取、强制 tool use、结构化输出,升级后要么变浅、要么结果静默坏掉
  • 你明明没改 prompt,只是升级模型,成本和响应行为却变了

这篇教程适合两类人:

  • 直接调用 Anthropic API,把 Claude 接到你自己的产品、脚本或工作流里的人
  • 用 Claude Code、n8n、后端服务或中间层包了一层 Claude 调用,底层其实还是 Anthropic Messages API 的人

说明:本文里的模型行为、参数边界和 JSON 示例,均为基于 Anthropic 官方文档验证。本文没有把未在本文环境里实发的 Anthropic API 请求冒充成实机跑通结果。

Sonnet 5 API 迁移排查总览
Sonnet 5 API 迁移排查总览

先判断:你是不是中了 Sonnet 5 的迁移坑

现象 更像哪类问题 第一动作
一切正常,唯独切到 claude-sonnet-5400 旧参数不兼容 先删 temperature/top_p/top_k 和旧 thinking 配置
response.content[0].text 以前好好的,现在报错或拿到空值 响应里前面多了 thinking block 改成遍历 content blocks
请求返回成功,但正文被截断,甚至几乎没有正文 max_tokens 被 thinking 吃掉 检查 stop_reason,给 max_tokens 留余量或降 effort
tool use 第二轮继续请求时报 400 invalid_request_error thinking block 被你过滤、重排或重构了 continuation 时把 thinking blocks 原样带回

如果你现在的故障属于上面四种,别先去怪网络、SDK 或 SDK 版本。

Sonnet 5 的 breaking change 足够大,很多 Sonnet 4.x 时代“默认没事”的写法,升级后就是会炸。

适用场景

这篇主要处理下面几种真实场景:

  • 你有一套 Sonnet 4.6 的文档抽取、分类、判定、审核或工具调用链路
  • 你原来会给不同任务动态设置 temperature
  • 你还在用 thinking: {type: "enabled", budget_tokens: N}
  • 你把 response.content[0] 当成固定文本块,或者用“空字符串兜底”吞掉解析异常
  • 你会把上一次 assistant 消息拼回去继续 tool use,但中间做了 block 过滤

如果你只是用 Claude Code 聊天,不自己调 API,这篇不是首选。

你更应该先看:

先复现一遍:为什么 Sonnet 4.6 正常,Sonnet 5 就不行了

最常见的迁移事故是:你只换了模型 ID,其他参数一行没动。

比如你原来的请求长这样:

{
  "model": "claude-sonnet-4-6",
  "max_tokens": 4096,
  "temperature": 0.2,
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  },
  "messages": [
    {
      "role": "user",
      "content": "提取这份工单里的高风险变更,只返回 JSON。"
    }
  ]
}

你把模型改成 claude-sonnet-5 之后,官方文档明确说明会发生三类变化:

  1. adaptive thinking 默认开启
  2. manual extended thinking 不再支持,直接返回 400
  3. 非默认 temperaturetop_ptop_k 也会直接返回 400

也就是说,这不是“偶发兼容性问题”,而是官方明确写出来的 breaking change

原因判断:这次到底变了什么

Anthropic 官方文档里,和这次问题最相关的有 5 个点。

1. Sonnet 5 默认就会 thinking

Sonnet 4.6 没带 thinking 字段时,不会按 Sonnet 5 现在这套默认策略去思考。

Sonnet 5 则会默认跑 adaptive thinking。

这带来两个直接后果:

  • 响应内容里可能先出现 thinking block,再出现 text
  • max_tokens 不再是“只留给正文”的预算,而是 thinking + 正文共享

2. 旧 budget_tokens 写法直接废了

Sonnet 5 不支持下面这类手动 extended thinking:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 32000
  }
}

这类请求在 Sonnet 5 上会直接 400

正确方向是改成 adaptive thinking,并用 effort 控制深浅。

3. 非默认采样参数不再默默忽略,而是直接 400

如果你以前用:

  • temperature
  • top_p
  • top_k

给不同任务做“创意一点 / 保守一点”的调参,迁到 Sonnet 5 后先别继续传。

官方文档的结论很明确:非默认值返回 400

4. 旧代码把 content[0] 当成文本,是这轮最容易漏掉的坑

官方 primer 对 extended thinking 的说明很直接:开启 thinking 后,响应会包含 thinking content block,然后才是 text block。

所以你如果沿用下面这种写法:

text = response.content[0].text

在 Sonnet 5 上就很危险:

  • 有时拿到的不是文本块
  • 有时你在异常处理里把它吞了,最后变成“成功但空输出”
  • 有时正文被截断了,你还以为请求完整结束了

5. 新 tokenizer 会让你旧的预算心智失准

官方说明 Sonnet 5 的新 tokenizer 会让同样文本大约多出 30% token。

这会直接影响:

  • 你的 max_tokens
  • 你的成本估算
  • 你的上下文容量
  • 你的结构化输出任务稳定性

第一步:先把会直接 400 的旧参数删掉

下面这几项,迁到 Sonnet 5 后优先排查:

  • thinking: {type: "enabled", budget_tokens: N}
  • 非默认 temperature
  • 非默认 top_p
  • 非默认 top_k

更稳的基础请求,可以先改成这样:

{
  "model": "claude-sonnet-5",
  "max_tokens": 8192,
  "output_config": {
    "effort": "medium"
  },
  "messages": [
    {
      "role": "user",
      "content": "提取这份工单里的高风险变更,只返回最终结果。"
    }
  ]
}

这里有三个关键点:

  1. 不再传旧 budget_tokens
  2. 不再传非默认采样参数
  3. 先用 medium,不要一上来默认 high

上面的 output_config.effort 与 Sonnet 5 的 adaptive thinking,均为基于 Anthropic 官方文档验证

第二步:不需要思考块的任务,显式关掉 thinking

如果你的任务本来就是:

  • 简单抽取
  • 字段改写
  • JSON 转换
  • 规则明确的结构化输出

那你完全可以先把 thinking 关掉,换更低延迟、更稳定的输出。

Sonnet 5 升级后 API 为什么突然 400 还返回空结果?按 migration guide、thinking 和 stop_reason 修掉旧代码-China AI Hub
Sonnet 5 升级后 API 为什么突然 400 还返回空结果?按 migration guide、thinking 和 stop_reason 修掉旧代码
此内容为付费阅读,请付费后查看
19.9
立即购买
您当前未登录!建议登陆后购买,可保存购买订单
付费阅读
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容

七天热门