多轮服务按 token 从头部截断历史,把一条 assistant 的 tool_calls 留下、对应的 tool 结果删掉,下一轮请求报 400,原文是 messages with role tool must be a response to a preceding message with tool_calls。
一开始怀疑是「截断后历史的首条正好是 tool 消息」,查了一圈,方向不对。
真正麻烦的地方在于,几个原因的现象几乎一样。截断按条数或 token 切,没有按消息对切。少数情况下是tool 消息必须紧跟对应的 assistant。
少数情况下是摘要时把 tool_call_id 丢掉了。
上来先按消息对整体裁剪历史
python trim.py --whole-pairs
保证 tool_call_id 与 assistant 对应
python check_pairs.py --strict
截断后校验首条角色
python trim.py --drop-leading-tool
末了用长历史请求复现验证
python chat.py --history long.json
中间停过一次,把已经改的东西回滚干净,再重新来一遍。
同样的操作我在测试机上先走了一遍,线上只做验证过的部分。
别只看报错消失,验证要多做一步。截断后的请求返回 200,历史里每条 tool 消息都能找到对应的 tool_calls,首条不是 tool。
消息不是独立单元,assistant 的 tool_calls 和 tool 结果是一对,按条截断一定会把它们拆散。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。
