现场是这样的。google-genai 1.2 调 gemini-2.0-flash,把返回的 function_call.args 直接 json.dumps 后塞进 function_response.response,报 400,原文 Invalid value at contents[2].parts[0].function_response.response (type.googleapis.com/google.protobuf.Struct)。

中间走过弯路,把「response 必须是对象,传字符串会被拒」当成主因,改完现象没变。

原因不止一个,得分开看。args 是 protobuf Struct,需要先转成普通 dict;Struct 不接受 None,空值会直接报类型错误;嵌套结构里混了 numpy 的标量类型。

动手时先看 args 的真实类型再决定怎么转

python -c "from google.genai import types;print(type(types.FunctionCall))"

再往下统一走 to_dict 再回填

python -c "import json;print(json.dumps({'ok':True,'rows':3}))"

递归清洗 None 与 numpy 标量

python norm_none.py --in tool_result.json --out clean.json

等前面都确认过,再用最小 function_response 请求验证结构

python gemini_tool_probe.py --mode response --file clean.json

同样的操作我在测试机上先走了一遍,线上只做验证过的部分。

这台环境是别人交接的,不少默认值被动过,只能一项项核。

同事提醒我看一下最近一次发布,时间点确实对得上。

命令输出我都留了记录,免得改完记不清原来的值。

恢复的判定其实很直接。请求返回 200,function_response 被模型正确识别,下一轮能基于工具结果给出答案。

跨语言的 protobuf 结构别用 json.dumps 硬转,先 to_dict 再清空值,能省掉一大半 400。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189