把历史对话从数据库读出来重放,用户消息的 content 存成了空字符串,调 claude-3-5-sonnet 返回 400,原文 messages.0.content.0.text: Field required,同一条记录在 gpt-4o 上只是被忽略,服务照常返回。
真正麻烦的地方在于,几个原因的现象几乎一样。空 content 在 Claude 侧不合法 必须省略该块;content 数组里混进了只有 type 没有 text 的块;历史裁剪之后留下空数组;工具结果块用了 text 类型 应该用 tool_result。
上来先找出所有内容为空的消息下标
python -c "import json;d=json.load(open('msgs.json'));print([i for i,m in enumerate(d) if not m.get('content')])"
过滤空块并保证数组非空
python clean_msgs.py --in msgs.json --out msgs_ok.json --drop-empty
工具结果改成 tool_result 结构再拼
python -c "print({'type':'tool_result','tool_use_id':'t1','content':'ok'})"
末了用清洗后的消息做一次最小调用
curl https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_KEY" -H 'anthropic-version: 2023-06-01' -H 'Content-Type: application/json' -d @req_claude.json
白天有业务在跑,能验证的部分都压到了晚上做。
当时还开着另一个排查窗口,两边日志对照着看才定位到。
恢复的判定其实很直接。调用返回 200,stop_reason 是 end_turn,历史重放十轮都不再出现字段缺失。
各家对空值的容错不一样,跨模型复用历史要在入库前统一清洗,别等报错再逐条补。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
