码上拾光

一个 400 引出的协议转换问题:只有 system 消息时 input 为空

· #实现#网关#协议

我维护着一个本地模型网关的分支——下游工具用 OpenAI 的 Chat 协议或 Responses 协议接进来,网关转换后发给不同上游。上一篇提到的多智能体循环第二轮的 400,根因在这个网关里。

现象

网关日志里,同一轮里前四个请求 200,第五个 400,上游报:One of "input" or "previous_response_id" ... must be provided

对比请求体:失败的那个只有一条 system 消息,没有 user 消息。

成因

网关把 Chat 协议转成 Responses 协议时,system 消息映射成 instructions 字段,userassistant 消息才进 input 数组。只有 system 时,input 就是空数组,上游拒收。

Chat 协议里只有 system 消息的请求是合法的——上游的 Chat 接口能正常回复。转换之后变成非法,问题在转换层。

谁会发这种请求

用编排框架、又配置了”不带对话历史”的智能体。它们把每轮的内容都写在系统提示词里,用户消息可有可无。第二轮如果前一个智能体只调了工具没产出文本,就没有用户消息可注入。

不是边缘情况,是这类编排的常态。

修法

思路:input 为空且有 instructions 时,把 instructions 的文本作为一条 input 项带上,并且不再重复写 instructions(否则提示词发两遍)。

问题是这条 input 项用什么角色。Responses 协议的 input 项支持 systemdeveloperuser 等角色,但上游不一定都接受。直接打了三发:

role: system     HTTP 400
role: developer  HTTP 200
role: user       HTTP 200

system 被这家上游拒收。developer 是 Responses 协议里 system 的正式对应角色,语义保真,选它。

改动十几行,加两个单元测试:纯 system 请求必须产出非空 input 且不带 instructions;带 user 消息的请求仍走原来的 instructions 路径。

顺手验证的两件事

教训

协议转换的正确性不能只看”字段对应关系”,要看两边合法请求的集合是否一致。A 协议合法的请求,转换后在 B 协议下必须也合法。这次的洞就是一个 A 合法、B 非法的例子,字段映射本身没错。

另外,修法里”用哪个角色”这种事,别查文档猜,直接打请求试。三次调用不到十秒,比翻文档快,而且是真相。


← 回到文章列表