先把当时的情况摆出来。把 2.5 模型的完整响应存库后用于多轮续聊,第二轮回传时返回 400,原文 thought signature is invalid,只有开了思考的请求才会出现。

真正麻烦的地方在于,几个原因的现象几乎一样。带签名的思考块必须原样回传,改写或裁剪都会失效。还有一种是换 key 或换模型后签名不再匹配。

少数情况下是同一条历史被两个服务同时写,签名被覆盖。不容易想到的是,把思考块文本截断后重拼,签名对不上。

上来先检查历史里思考块的完整性

python -c "import json;d=json.load(open('hist.json'));print([bool(p.get('thoughtSignature')) for p in d['contents'][-1]['parts']])"

原样回传不裁剪,只截断普通文本

python -c "import json;d=json.load(open('hist.json'));json.dump(d,open('hist2.json','w'),ensure_ascii=False)"

等前面都确认过,再用完整历史续一轮验证

curl -sS "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key=$GEMINI_API_KEY" -H 'content-type: application/json' -d @hist2.json

别只看报错消失,验证要多做一步。续轮返回 200,历史中每块思考内容都保留原签名,不再出现 signature 校验失败。

提醒一点。签名让思考内容不可伪造,也让历史不可随意编辑,落库时按原始响应整块保存最省事。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189