先说一个我自己的真实感受:今年年初我们团队把内部 AI 能力从“只接一家模型”改成“多模型自由路由”,最大的痛点不是模型效果选择,而是每一家模型的 API 规范完全不一样。OpenAI 用/v1/chat/completions,Anthropic 用/v1/messages,Google Gemini 又是另一套/v1beta/models,请求头、消息结构、流式格式、错误码几乎没有一个地方是统一的。改完 OpenAI 的代码再去看 Claude 的文档,那种“重新学一门接口语言”的感觉,我相信很多同行都体会过。
所以 AI 聚合接口平台才在这两年成了刚需。它们的核心价值不是“多一个转发层”,而是把背后三套甚至更多的大模型 API 统一成一套规范,让业务代码只写一次。再加上统一鉴权、统一配额、统一计费日志,运维复杂度一下降了不少。但问题也跟着来了:市面上叫得上名字的聚合平台越来越多,各家都标榜自己“兼容 OpenAI / Anthropic / Gemini”,实际兼容到哪个深度?是只做到“能通”,还是连流式、工具调用、多模态、参数透传都做全?这个如果只看 README,根本看不出来。
我花了大概两周时间,把 OpenMove、LiteLLM(开源网关自建)、以及另一家商业聚合服务放在一起,做了一轮针对三大协议兼容性的实测。这篇文章只做一件事:用真实请求把它们的“协议兼容”掰开揉碎看,哪些是表面兼容,哪些是真能扛业务。
1. 聚合接口平台的本质与横评对象
1.1 为什么需要聚合层:直接对接大模型的三个痛点
先复盘一下,在没有聚合平台的时候,一个中型团队直接对接多家大模型有多麻烦。首先是接口规范,OpenAI 的ChatCompletion消息体里用role: system/user/assistant描述会话,Anthropic 的 Messages API 虽然也用类似字段,但系统提示词要放到顶层system参数里,Gemini 又完全不一样,人家用contents加parts的结构,角色映射直接叫user和model。哪怕你已经封装了一层客户端,每次新增模型厂商都得在 SDK 之上再补一个适配器。
其次是密钥管理和成本分摊。直接对接多家模型,意味着每个平台都有自己的 API Key,有的按项目维度开,有的按用户维度开,密钥散落在不同同事的本地环境变量里。每个月账单对账更是折磨,OpenAI 出了多少 token、Claude 出了多少 token、Gemini 又花了多少钱,全部要人工汇总。还有限流,不同模型的 RPM/TPM 配额完全独立,一个请求触发了限流,你很难快速判断是哪个上游导致的。
聚合层把这些问题集中到一个节点:统一 API 入口、统一密钥管理、统一用量账单,甚至在多个上游之间做自动故障转移。这也是我一开始决定引入这类平台的根本原因——不是追逐热点,是真的被多模型并行开发这件事逼出来的。
1.2 这次横评选了哪几个平台,为什么是它们
市面上做模型聚合的路线大致分成三类,这次我刻意各选了一个代表:
- OpenMove:商业闭源聚合服务,主打多模型接入和统一计费,官方声称兼容 OpenAI、Anthropic、Gemini 三种协议,有控制台可以做用量分析和模型路由。
- LiteLLM:开源模型网关,部署在自己服务器上,支持包一层代理把各种上游统一成 OpenAI 格式,适合喜欢自托管、数据不过第三方的团队。
- 某商业聚合 SaaS(下文简称为“竞品C”):同样是商业托管,但更侧重企业级功能,比如 SSO 登录、审计日志、私有化部署选项。
选这三个的原因是它们的部署形态差异足够大:一个是纯 SaaS 托管,一个是自托管开源网关,另一个是本地运行的开源协议转换层。如果这三家都声称兼容三大协议,那基本能代表主流聚合平台的整体水平。另一个考虑是,很多团队最终会在这三种路线之间纠结,所以横评结果可以直接对应到选型决策。
我给自己定了一个原则:不做“启动盘里所有模型都测一遍”的大而全测试,而是聚焦一个真实业务最常遇到的组合——文本对话、流式输出、工具调用、嵌入向量,这四个场景覆盖了 90% 的日常调用。
2. 三大协议兼容性实测方法论
2.1 协议兼容性的三个层级:能通、能用、能跑业务
做实测之前,我先把“兼容”这件事拆成了三个层次,避免“能通”和“能跑业务”被混为一谈。
第一层是基础连通性,也就是请求能发出去、能拿到 200 响应。比如你用 OpenAI SDK 把 base_url 改成聚合平台地址,能不能正常完成一次chat.completions.create。这个层次最容易做到,因为只要聚合平台在网关层做一个路径转发、把 Authorization Bearer Token 换成自己的 Key 就行,绝大多数平台在这一层都不会出问题。
第二层是参数映射与字段透传。这一层的差异才是实际业务中真正会踩到的坑:OpenAI 请求里的temperature怎么映射到 Anthropic 的temperature?max_tokens在 OpenAI 是生成的最大 token 数,在 Anthropic 的 Messages API 里同样有个max_tokens但却是必填参数——如果聚合层不帮你补默认值,OpenAI 客户端发过去的请求在 Anthropic 上游直接就 400 了。response_format、tools、tool_choice、stop这些参数更是重灾区,很多参数在不同协议里根本没有一一对应关系,聚合层要么做值转换,要么直接忽略。
第三层是流式协议和高级特性的完整映射。这个是最考验聚合平台功力的地方。OpenAI 的流式返回是data: {json}按行推送,Anthropic 的流式则是事件流模式,有message_start、content_block_delta、message_delta不同事件类型,Gemini 的streamGenerateContent又是另一种 JSON 分段结构。如果聚合层只是“透传上游流式响应”而不管格式转换,那客户端用 OpenAI SDK 接收到的流式数据结构就是错的,会出现“能拿到 200,但流式解析直接报错”或者“流不结束、卡在某个事件上”的诡异问题。工具调用也一样,OpenAI 的tool_calls结构、Anthropic 的tool_usecontent block、Gemini 的functionCall是三种完全不同的表达方式,不做深层次转换就没法正常触发函数调用。
2.2 测试环境与评判标准
为了尽量贴近真实业务,我做了一个最小可复现的测试项目,语言选了 Python,用了各家的官方 SDK 做客户端,同时把 SDK 的 base_url 指向聚合平台。这样做的好处是能直接检验“SDK 不换、只改 base_url 和 key”这个最理想的迁移方案是否成立。
测试环境大概是这样的:
- 客户端:Python 3.11,openai 1.x SDK,anthropic 0.x SDK,google-generativeai SDK
- 目标模型:OpenAI 协议对应 gpt-4o-mini,Anthropic 协议对应 claude-3-5-haiku,Gemini 原生协议对应 gemini-1.5-flash,都是低延迟、低成本的走量模型
- 测试场景:单轮对话、多轮对话、流式对话、工具调用、文本嵌入
- 评判维度:连通性、参数生效性、流式完整性、错误信息可读性、端到端延迟
我给自己定的通过标准比较严格:
- 流式场景必须能按协议解析出完整的增量内容,不能漏 chunk,不能出现事件顺序错乱
- 工具调用场景必须能正确返回
tool_calls或等价的tool_use结构,且参数能被客户端 SDK 正常解析 - 错误场景必须有清晰的错误码和错误说明,不能是“上游 500 被吞成 400”这类含糊响应
3. 三大协议兼容性实测过程与结果
3.1 OpenAI 协议兼容性:从 base_url 到高级参数
OpenAI 协议是聚合平台的“母语”,因为大部分聚合平台自己本身就是用 OpenAI 的接口格式做统一抽象的,所以理论上这层兼容性应该最稳。我实测下来也基本符合预期,OpenMove、LiteLLM、竞品C 三家的/v1/chat/completions基础调用全部通过,单轮对话和简单的多轮上下文都能正确返回。
但我测到流式的时候就发现了差异。用 OpenAI SDK 的stream=True参数做流式对话,三家的返回都能被 SDK 正常解析,但usage字段的处理方式不一样。OpenMove 在流式结束时带上了完整的usage信息,竞品C 需要额外传stream_options: {"include_usage": true}才能在流式末尾拿到 token 统计,LiteLLM 则默认不带,需要在请求里明确开启。这个差异直接影响成本统计的精确性,如果团队依赖流式响应里的 usage 做实时计费,就得注意聚合平台是否完整透传/生成了这个字段。
更值得说的是工具调用(function calling)这一层。我在测试脚本里定义了一个简单的天气查询工具,让模型在合适的时机触发调用。OpenMove 和 LiteLLM 都能正确返回tool_calls结构,包括id、type、function.name、function.arguments这些关键字段,OpenAI SDK 可以直接从响应里解析出参数。竞品C 在单轮工具调用上也通过了,但当我连续做“工具调用 -> 返回工具结果 -> 模型再次调用工具”这种多轮工具循环时,竞品C 偶尔会出现tool_choice失效的现象,模型没有按预期继续调用工具而是直接回复文本。排查下来大概率是它的协议转换层对多轮tool消息的角色映射做了简化处理,导致上下文里工具结果没有正确传递给上游模型。
还有两个细节值得注意。第一个是response_format参数,我在测试 JSON Output 模式时,OpenMove 可以正确把 OpenAI 的response_format: {"type": "json_object"}映射到 Anthropic 上游的 JSON 约束(如果路由到 Claude),LiteLLM 也能做类似映射,但竞品C 是直接透传给上游——如果上游恰好是 Gemini,这个参数对方并不认识,请求会被忽略但不会报错,结果就是模型可能返回非 JSON 文本。第二个是max_tokens,LiteLLM 在默认配置下不会帮你补这个参数,如果路由到 Anthropic 的 Claude,请求会因为缺少必填的max_tokens直接报 400,而 OpenMove 会在转发前自动补一个默认值,这个对“只改了 base_url 就切换模型”的团队来说差别很大。
3.2 Anthropic 协议兼容性:头部差异与必填参数的坑
Anthropic 的 Messages API 是这次横评里最见真章的部分,因为它的鉴权方式、请求结构和流式格式跟 OpenAI 差异太大,聚合层要做到“无感兼容”难度最高。
先说最基础的鉴权。Anthropic 原生要求两个请求头:x-api-key和anthropic-version,而 OpenAI 用的是Authorization: Bearer <key>。在测试中,OpenMove 和竞品C 都正确实现了这两种鉴权方式的映射——我用 Anthropic SDK 把base_url指向它们的地址,auth_token填自己平台的 Key,请求能正常通过。但 LiteLLM 如果配置时只保留了 OpenAI 格式的Authorization头映射,用 Anthropic 协议访问时就会在网关层被 401,需要在配置里额外做一份鉴权头转换规则。这个对自建用户来说是个典型的配置陷阱。
然后是消息结构。Anthropic 的 Messages API 有一个独立的system顶层参数,OpenAI 的消息列表里没有这个分层,系统提示词就是role: system的一条普通消息。实测三家都能把 OpenAI 的 system 消息正确转成 Anthropic 的顶层system字段,但“回退顺序”不一样:OpenMove 和 LiteLLM 是优先取第一条 system 消息并合并;竞品C 只取第一条 system,多条 system 消息会被直接丢弃,这会丢上下文。还有一个反向问题,如果直接用 Anthropic 协议往聚合平台发请求,平台怎么处理system字段;这块我测下来三家基本都能原样透传,问题不大。
流式输出是这一轮差异最大的点。Anthropic 原生流式是一系列事件:message_start、content_block_start、content_block_delta、content_block_stop、message_delta、message_stop。我用 Anthropic SDK 的stream: true请求,三家都能把上游响应转回 Anthropic 事件流,但从事件完整性上看,OpenMove 做得最完整,message_delta里的usage.output_tokens统计字段一直存在;LiteLLM 的事件流偶尔会缺少message_delta中的stop_reason字段,导致客户端在判断“模型为何停止生成”时拿不到原因;竞品C 则发现一个偶发问题,在长回答场景下content_block_delta的delta.text会被拆得比较碎,这不算 bug 但会让客户端渲染时更频繁地触发 UI 更新。
工具调用在 Anthropic 协议里是tool_use和tool_result这种 content block 结构。我实测三家都能把 OpenAI 格式的tool_calls转成 Anthropic 的tool_use,也可以反向转换,这个没有问题。但真正的坑在tool_choice的映射:Anthropic 的tool_choice支持auto、any、tool三种模式,其中any表示必须调用工具,OpenAI 没有直接对应的选项(只有auto、none、required)。测试中发现,当用户用 Anthropic 协议发送tool_choice: {"type": "any"}时,竞品C 会把any映射成 OpenAI 风格的required,这个基本等价,能用;但 LiteLLM 在某些版本里会把any直接忽略,回退到auto,导致模型可能不调用工具就回复。这种隐性问题在文档里根本不会写,只有实测才能发现。
3.3 Gemini 原生协议兼容性:路径、安全设置与流式格式
Gemini 是这次横评里最“特殊”的一个协议。Google 的生成式 AI 接口跟 OpenAI、Anthropic 都不是一个路数:路径是/v1beta/models/{model}:generateContent这种 RPC 风格,请求体用的是contents/parts结构,而不是messages。所以聚合平台对 Gemini 原生协议的兼容,往往不是“做映射”,而是“做翻译”。
先从最简单的模型列表和基础对话说起。用google-generativeaiSDK 的generate_content做单轮对话,三家都能正常返回,但方式不太一样。OpenMove 和竞品C 是直接实现了 Gemini 协议的路由,SDK 请求发过来后它翻译成内部统一格式再路由到不同上游,所以无论最终上游是不是 Gemini,SDK 收到的都是 Gemini 风格的candidates结构;LiteLLM 则是依赖一个收费很低的“协议转换器”,把 Gemini 请求转成 OpenAI 格式再走它的主链路,最终返回给 SDK 的也是 Gemini 结构。三种方案效果上都能通,但 OpenMove 的翻译做得更彻底,连prompt_feedback和安全评级这类 Gemini 特有的字段都会返回。
流式接口streamGenerateContent是这次测试里最有戏剧性的部分。Gemini 原生流式返回的candidates数组里,content.parts每次增量只包含一小段文本,但它的 JSON 结构是不变的,只是内容在变。这在聚合层做格式转换时很容易搞错:如果只做“按行解析 JSON 再组装成 OpenAI 的 chunk”,就必须每个 chunk 都保持完整的candidates结构,不能拆坏。实测 OpenMove 在 Gemini 流式转 OpenAI 格式时表现稳定,每个 chunk 的索引和完成原因都正确;竞品C 在长时间流式输出时出现过两次“chunk 中断”,需要客户端自己做超时重试;LiteLLM 的 Gemini 流式转换在短文本场景没问题,长文本生成到后半段时偶尔会出现字段finishReason提早出现、后面还跟着文本的异常情况,OpenAI SDK 解析时会忽略后面的文本,导致生成被“截断”。
安全设置(safetySettings)也是 Gemini 协议特有的东西。因为是翻译链路,很多平台在转成 OpenAI 格式时会把safetySettings直接丢到extra_body里透传,能不能生效完全取决于上游认不认。实测 OpenMove 支持把 Gemini 的safetySettings转发给真正的 Gemini 上游,也支持把 OpenAI 风格的moderation类参数映射成 Gemini 的安全档位;竞品C 对这一层的支持明显弱一些,safetySettings里如果指定了BLOCK_NONE这种挡位,它不会帮你做白名单透传,可能被上游拒绝;LiteLLM 则完全依赖 prompt 模板层面控制,没有专门透传安全参数。对内容安全有严格要求的业务,这块要单独验证。
3.4 三平台三大协议实测结果汇总
我把这一轮的关键结果整理成一张表,方便对照。
| 测试项 | OpenMove | LiteLLM | 竞品C |
|---|---|---|---|
| OpenAI 基础对话 | 通过 | 通过 | 通过 |
| OpenAI 流式对话 | 通过,含 usage 统计 | 通过,默认不含 usage | 通过,需额外参数 |
| OpenAI 工具调用(多轮) | 稳定 | 稳定 | 偶发 tool_choice 失效 |
| OpenAI JSON 模式路由到 Claude | 正确映射 | 正确映射 | 部分透传,不保证生效 |
| Anthropic 基础鉴权 | 通过(Automatic 头映射) | 需配置鉴权头转换 | 通过 |
| Anthropic 流式事件完整性 | 完整 | 偶缺 stop_reason | 通过但 text 碎片化 |
| Anthropic tool_choice: any | 正确映射 | 回退为 auto | 正确映射为 required |
| Gemini 基础对话 | 通过 | 通过 | 通过 |
| Gemini 流式长文本 | 稳定 | 偶发 finishReason 提前 | 偶发 chunk 中断 |
| Gemini 安全设置透传 | 支持 | 不支持 | 部分支持 |
| 错误信息可读性 | 上游错误原样转换,含原因 | 常见错误可读,部分透传原始 body | 错误码准确但详情偏少 |
整体看下来,OpenMove 在协议兼容深度上确实做得最全,尤其是高层级特性的映射比较扎实;LiteLLM 胜在开源可定制,但默认配置下的协议转换有很多隐藏条件,适合有技术精力去调优的团队;竞品C 在基础链路可用,但高级特性覆盖得不够完整,更适合只走 OpenAI 协议的业务。
4. 横评之外的实用维度:路由、计费与稳定性
4.1 模型路由策略:不只是“随机选一个上游”
协议兼容性是第一关,但聚合平台的日常价值更多体现在模型路由策略上。多数平台宣传的“智能路由”并不智能,只是根据你配的优先级列表按顺序尝试,上游出错就切换下一个。真正拉开差距的是故障转移的粒度:是整条请求级别切换,还是流式响应中途出错也能切换?这个差别很大。
我实测时给 OpenMove 配置了两个上游,一个主用 Anthropic、一个备用 Gemini,然后手动把 Anthropic 的 Key 改成无效值。发现它在请求发出前就会因鉴权失败快速切换;但如果是在流式生成到一半时上游突然断连,OpenMove 会直接返回给客户端一个错误,不会再切换到备用上游重新生成——这个我基本能理解,因为流式输出已经给客户端吐了一半内容,重试只会造成重复和错乱,不是所有场景都适合做“流式中途切换”。LiteLLM 则更依赖配置的retry_policy,如果设置合理,它是能做请求级重试的,但需要自己写清楚什么错误码触发切换。
对团队来说,路由策略要关注的是能不能“按成本优先”“按延迟优先”“按能力优先”这三类规则灵活调配。我建议在选型时把需求的优先级定清楚:如果你就是想让便宜模型先顶上,那平台是否支持按模型单价排序就很重要;如果你是做客服场景、对延迟敏感,那平台有没有就近节点和流式快速响应机制就更关键。而不是被“智能路由”这个营销词带偏。
4.2 用量统计与成本分摊:聚合平台真正的“记账本”
聚合平台除了转发请求,还有一个很重要的价值是把多个上游的 token 消耗统一成一份账单。但这个事情做得好不好,差别非常大。核心问题是:平台统计的 token 数跟上游模型官方账单里的 token 数能不能对上?
我在测试中对同一个请求,分别查看了 OpenMove 控制台的 token 统计、Anthropic 官方后台的 usage 记录,发现 OpenMove 的统计基本能做到一致,差异在 1%~3% 以内,这对于内部成本归因来说足够了。LiteLLM 作为开源网关,统计维度也很细,能在每次请求里记录model、messages、prompt_tokens、completion_tokens、cost等字段,但它更依赖你自己在配置里维护每个模型的单价表,如果模型单价没配全,成本统计就会是 0 或者错误。竞品C 的统计则偏“平台视角”,它统计的是聚合层实际消耗的 token,但不会把上游官方账单拉下来做核对,差异超过 5% 我遇到过。
如果团队有比较强的成本管控需求,我建议在接入聚合平台之后,先做两周的“双写核对”:既看平台统计,也看上游官方后台每个 Key 的用量,两边一对比就知道平台的统计口径是不是可靠的。另外还要看平台支不支持给每个内部项目单独签发子 Key、子 Key 能不能绑定独立的模型白名单和配额上限,这决定了下个月成本异常时你能不能精准定位到是哪个业务线在烧钱。
4.3 稳定性、限流与故障转移机制
聚合平台作为中间层,天然会引入额外的链路跳数,所以稳定性考察不能只看它的官网 SLA,还要看它在真实故障场景下的表现。我做了两个主动故障注入测试:一个是把上游 Key 改成错误的,另一个是在转发过程中把上游请求设置成可访问但响应超时。
第一种场景下,三家都能正确返回 401 错误,OpenMove 还会附带更语义化的提示,比如“上游模型配置错误,请检查 API Key”,竞品C 则直接返回一个标准的 401 加上平台自己的请求 ID,这个对排查问题也够用。
第二种场景更关键。上游响应超时状态下,OpenMove 在等待大约 20 秒后会返回一个 504 网关超时,错误体里带有具体是哪个上游超时的信息,这个对排障很有帮助;LiteLLM 的超时阈值可以在配置里自定义,但如果没配好,默认超时时间会很长,导致客户端一直干等。竞品C 的超时策略相对激进,大约 10 秒就断开,会触发客户端重试,但如果是没有实现幂等重试的业务,这反而会造成重复请求浪费上游额度。这里提醒一下,无论选哪个平台,客户端侧的请求超时时间一定要设置得比聚合平台的上游超时时间更长,否则会出现“客户端已经放弃,但聚合平台还在等上游返回”的悬空请求。
限流也是一个隐藏点。聚合平台自身通常有 RPM(每分钟请求数)限制,同时上游模型还有各自的 TPM 限制,如果聚合平台不做排队和削峰,突发流量会把两层的限流同时打爆。实测 OpenMove 支持在控制台为每个子 Key 设置独立的 RPM/TPM 配额,还能设置全局限流策略;LiteLLM 也可以通过配置文件做限流,但粒度比较粗,主要靠 redis 配合实现;竞品C 的限流策略则偏“上游透传”,它会告诉你上游 429 了,但不会主动帮你做请求排队。选型时建议评估一下:如果你经常有定时任务或者突发推广流量,聚合平台有没有内置的排队、重试和熔断机制,这些在故障时比“高可用架构”这些宣传词更实在。
5. 常见问题与排查技巧实录
5.1 聚合层最常见的失败模式
这一轮测试下来,我发现聚合平台接入失败的案例虽然五花八门,但总结起来就那么几类。我把它们整理成速查表,方便遇到问题时快速定位。
| 失败现象 | 可能原因 | 排查方向 |
|---|---|---|
| 401 错误 | 聚合平台的 Key 配置错误,或鉴权头映射缺失 | 检查请求头里 Authorization / x-api-key 是否正确 |
| 400 错误 | 参数映射失败,或缺必填参数 | 看上游是否是 Anthropic,确认 max_tokens 是否补齐 |
| 404 错误 | 协议路径不对,或平台不支持该协议 | 确认路径是 /chat/completions 还是 /messages 还是 /generateContent |
| 流式解析报错 | 流式格式转换不完整,或事件顺序错乱 | 抓原始流式响应,逐个事件对比官方协议格式 |
| 工具调用失效 | tool_choice 映射错误,或多轮 tool 消息被丢弃 | 检查平台对 tool 消息的角色映射策略 |
| 响应内容截断 | 流式 chunk 或 finishReason 提前 | 对比长回答的流式输出完整性 |
| 用量统计偏差大 | 平台统计口径与上游不一致 | 双写核对官方后台与平台统计 |
5.2 一个通用的排查思路:从“请求链路”抓到底
我自己的排查习惯是,遇到聚合平台的问题,不要先在业务代码里猜,而是把整个请求链路的每一层都记录下来。最基础的一步是抓原始请求和原始响应,无论用哪个平台,都可以先把 SDK 的调试日志打开,或者直接用 curl 按对应协议发一个最小请求,看返回结果。
比如用 Anthropic 协议访问 OpenMove,可以先手动构造一个最简单的请求:
curl https://api.openmove.com/v1/messages \ -H "x-api-key: $OPENMOVE_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-3-5-haiku", "max_tokens": 512, "messages": [{"role": "user", "content": "你好"}] }'如果这个请求直接返回 200,说明基础链路是通的,问题大概率出在 SDK 层的封装或参数映射上;如果返回错误,看错误体里的 code 和 message,很多平台会带上自己的 request_id,拿着这个 id 去控制台查日志,能快速定位是网关层的问题还是上游模型的问题。
第二步是确认参数映射的边界。比如temperature、top_p、max_tokens、stop这些常用参数,在切换协议后到底有没有生效,最好的验证办法是故意设置一个非常规值:把max_tokens设成 5,看输出是不是只生成了一小段;把temperature设成 0 和 2,故意让模型输出随机性大幅变化。如果两种值的结果没有明显差异,那基本可以判断参数在聚合层被忽略了。
第三步是抓流式。流式问题最烦人,因为它不像普通 HTTP 错误那样有明确的状态码,而是“结构不对但状态码 200”。我建议在测试脚本里把流式响应的原始字节流打印出来,逐段比对协议格式。用 OpenAI SDK 调试时,可以直接看response.text或者往流式回调里打个断点;如果用 curl,可以这样抓原始流式内容:
curl -N https://api.openmove.com/v1/chat/completions \ -H "Authorization: Bearer $OPENMOVE_API_KEY" \ -H "content-type: application/json" \ -d '{ "model": "gpt-4o-mini", "stream": true, "messages": [{"role": "user", "content": "讲一个短故事"}] }'看到data: [DONE]之前的每一行,确认 chunk 的字段结构是否符合 OpenAI 标准。这一步能帮你判断到底是聚合平台转换的锅,还是你自己解析逻辑的锅。
5.3 选型之外的一些血泪心得
最后说几个这次横评之后我自己的真实感受,算不上什么大道理,但都是踩过坑之后的记录。
第一,不要只看“兼容 OpenAI / Anthropic / Gemini”这几个大字,一定要问清楚兼容到第几层。很多平台的兼容文档写得天花乱坠,但实际只做到了“基础对话能通”,流式、工具调用、多模态这些高级特性完全没有按官方协议完整映射。我的建议是,把一个最接近生产场景的测试脚本准备好,选型时直接拿它跑一遍,比看任何文档都靠谱。
第二,LiteLLM 这类开源网关上限很高,但下限也很低。它默认配置下的很多行为其实不够“开箱即用”,尤其是 Anthropic 和 Gemini 的协议转换,需要你自己调优。如果团队里有懂网关原理的人,我挺推荐用它,毕竟数据不出内网,可控性强;如果团队没有专门的中间件负责人,更建议选商业平台,把精力省在业务侧。
第三,聚合平台不是越晚接越好,也不是越早接越好。我自己的判断标准是:当团队开始接第二家模型厂商时,就应该引入聚合层。因为第一家模型接入的时候,所有代码都是“原生对接”,没有任何抽象;接第二家时,你突然会发现“又要写一套适配”,狼狈不堪。这时候引入聚合层,重构一次,后面接第三家、第四家边际成本就趋近于零了。反而如果一开始就是多模型并行,那就更应该把聚合层当成基础设施来建设。
我个人现在团队的选择是:对外部不可控流量用 OpenMove 这类商业平台,靠它的完善协议映射和托管运维省心;对内部自研模型和私有化部署场景,用 LiteLLM 自建网关,把敏感数据留在内网。这个组合目前跑了整整两个季度,没有发生因为聚合层导致的线上故障。说到底,聚合平台解决的是“接入问题”,不是“模型效果问题”,模型选型还是得靠业务需求驱动,聚合层只是让这件事变得更丝滑。希望这篇横评能帮你少走点弯路,尤其在协议兼容性这个文档写得最少、坑却最多的地方。