事情本身不复杂,线索都在日志里。多轮会话从 Redis 里恢复历史后继续提问,接口返回 400,原文 messages: roles must alternate between user and assistant,最后一次写进历史的恰好是 assistant 的回复。

第一反应是「错误分支把空内容的 user 消息过滤掉了,角色链断开」,验证下来不成立。

把日志和配置对完,能解释这个现象的基本就这几处。历史里保留了上一条 assistant 回复,又直接追加新提问导致同角色相邻。另一种情况是裁剪历史时把中间的 user 消息删了,留下两个 assistant 挨着。

还有一种情况,预填充 assistant 前缀后忘记再补一条 user 消息。

上来先把历史导出来打印角色序列

python -c "import json;h=json.load(open('history.json'));print([m['role'] for m in h])"

再往下合并相邻同角色消息,空的直接丢掉

python fix_roles.py --in history.json --out history_fixed.json

随后按 2023-06-01 版本重发一次确认结构合法

curl -sS https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" -d @history_fixed.json

等前面都确认过,再在写入历史前加一条断言,越界就告警

assert msgs[-1]['role'] == 'user', 'last role must be user'

恢复的判定其实很直接。重发返回 200,role 序列严格 user/assistant 交替且以 user 结尾,会话连续追问十轮不再出现 400。

Claude 允许最后一条是 assistant 做预填充,但那是主动写法,从数据库恢复的历史要自己把这条尾巴补平。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。

作者 9823189