这个Bug只在我切换LLM提供商之后才出现
标题:切换大模型提供商后冒出来的 Bug
请求成功了,返回的也是合法 JSON,SDK 没有抛出异常,但应用还是挂了。这个 Bug 出现在我把一个 LLM 应用切到另一个兼容 OpenAI 接口的提供商之后。集成过程看起来简单得离谱:改下 base URL,换掉 API key,请求体不变,直接上线。最简的提示词确实能跑通,但这不代表两个提供商的行为完全一致。问题不在 HTTP 协议层,而在于我的应用悄悄依赖了原提供商的一些行为特征。
请求兼容 ≠ 行为兼容
当某个 API 自称“兼容 OpenAI”时,通常意思是它能接受类似的请求结构:
{
"model": "some-model",
"messages": [
{ "role": "user", "content": "Summarize this support ticket." }
]
}
这种兼容确实有用,可以复用客户端、认证方式和大部分集成代码。但生产应用依赖的远不止“服务器能否接受请求”。它还可能依赖于:
- content 是字符串、数组、空值还是 null
- tool call 参数的序列化方式
- finish_reason 可能出现哪些值
- usage 字段是否总是存在
- 流式完成的信号通知方式
- 结构化输出约束是否被强制执行
- 错误、超时和不支持的参数如何报告
两个提供商可能接受相同的请求,返回的响应语法上都合法,但在运行层面却不同。——这就是有趣 Bug 的藏身之处。
我解析器里隐藏的那个假设
我原来的响应处理代码基本是这样写的:
const text = response.choices[0].message.content.trim();
一直跑得好好的,直到选中的模型返回了一个 tool call。在那次响应里,message.content 是 null,而实际输出藏在了 message.tool_calls 里。API 没挂,是我的解析器挂了。
第一轮修复很简单: