20 KiB
国产 AI Provider 的怪癖
采集于 2026-08-12。
可信度标注:本文件里 ✅ 表示来自厂商官方文档页面;⚠️ 表示来自搜索结果聚合或第三方资料,落地前需要用真实请求验证。国产厂商文档改版频繁(本次采集就遇到 moonshot.cn → kimi.com、volcengine.com → docs.volcengine.com 两处 301),且很多"历史怪癖"在新版本里已被修掉,不要照抄老博客。
国产 API 的怪癖和欧美三家不是一个类型。OpenAI/Anthropic/DeepSeek 的坑主要在语义层(thinking 怎么回传、tool_result 放哪);国产的坑主要在协议层和运维层:鉴权方式五花八门、HTTP 200 里藏业务错误、流式默认全量返回、内容安全是一等公民。
0. 跨厂商的共性怪癖(最重要)
这五条是中转站架构里必须提前留出的抽象层,逐个厂商去补会很痛苦。
0.1 HTTP 200 内嵌业务错误 ⚠️→✅
MiniMax 是典型:无论成功失败都返回 HTTP 200,真正的状态在 body 的 base_resp.status_code(0 = 成功)。
{"base_resp": {"status_code": 1002, "status_msg": "rate limit"}}
对中转站的直接后果:只看 HTTP 状态码的限流检测会完全失效——限流返回的是 200 而不是 429。重试逻辑、熔断器、计费统计全都要能读 body。
建议:网关的上游响应处理层做成"HTTP 状态码 + body 业务码"双通道判定,而不是假设 HTTP 语义可靠。
0.2 内容安全是一等公民
欧美三家只有 content_filter / refusal 一个笼统信号,国产普遍把内容安全做成结构化字段:
| 厂商 | 表现形式 |
|---|---|
| 智谱 GLM ✅ | finish_reason: "sensitive"(OpenAI 枚举里没有这个值) |
| MiniMax ✅ | input_sensitive / output_sensitive 布尔,外加 input_sensitive_type / output_sensitive_type(1–7 的违规分类),还有 mask_sensitive_info 参数 |
finish_reason: "sensitive" 会直接打破 OpenAI 客户端的枚举校验——严格的 SDK(尤其是 Go/Rust 这类强类型的)会反序列化失败。中转站必须把它映射成 content_filter,同时用扩展字段保留原值。
0.3 鉴权方式没有一个统一的
| 方式 | 厂商 |
|---|---|
Authorization: Bearer <key> |
智谱(现行)✅、Kimi ✅、豆包/火山方舟 ✅、腾讯混元兼容端点 ✅、DeepSeek |
| Bearer + 额外 header | 百度千帆 v2(Bearer bce-v3/ALTAK-... + appid header)⚠️ |
JWT 签名(key 形如 {id}.{secret},需自行签发带 exp 的 token) |
智谱(历史方式,现在仍支持)⚠️ |
access_token 放 query string(用 API Key + Secret Key 先换取) |
百度千帆 v1 ⚠️ |
| TC3-HMAC-SHA256 云厂商签名 | 腾讯混元原生接口 ✅ |
| HMAC-SHA256 签名拼进 WebSocket URL | 讯飞星火原生接口 ✅ |
AK:SK 拼接格式 |
讯飞星火 OpenAI 兼容端点 ⚠️ |
中转站的凭据存储不能假设"一个 key 一个字符串"——至少要支持 (key, secret, appid, region) 四元组,且签名逻辑要能按 provider 插拔。
0.4 流式默认可能是全量累积
见 §1.2 的阿里 incremental_output。这是最容易在生产上炸的一条:不设参数时每个 SSE chunk 返回的是从头累积的完整文本,直接按 OpenAI 语义拼接会得到指数级重复的内容。
0.5 非标参数塞 extra_body
国产厂商的 OpenAI 兼容模式普遍保留一批自有参数(思考开关、联网搜索、重复惩罚),官方推荐用 OpenAI SDK 的 extra_body 透传。中转站需要一个参数白名单 + 透传通道,而不是严格按 OpenAI schema 校验后丢弃未知字段。
1. 阿里 · 通义千问 / 百炼(DashScope)✅
来源:https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api 、https://help.aliyun.com/zh/model-studio/qwen-api-via-dashscope 、https://www.alibabacloud.com/help/zh/model-studio/qwen-api-via-openai-chat-completions
1.1 四套接口并存
百炼同时提供四种入口,这本身就是中转站的最佳参考:
- OpenAI 兼容 Chat Completions — 迁移成本最低
- OpenAI 兼容 Responses — 内置联网搜索、代码解释器、网页提取,自动管理对话历史
- Anthropic 兼容 Messages — 支持 thinking 和工具调用
- DashScope 原生 — "提供最完整的功能集和参数支持"
功能集不等价:原生接口是超集。做中转站时若只对接 OpenAI 兼容模式,会拿不到部分能力。
1.2 incremental_output:最大的坑 ⚠️
DashScope 原生流式下:
| 取值 | 行为 |
|---|---|
true |
每个 chunk 只含新增内容(OpenAI 语义) |
false(部分场景默认) |
每个 chunk 返回从头累积的完整文本 |
如果按 OpenAI 的"delta 累加"逻辑处理 incremental_output=false 的流,输出会变成 我我是我是通我是通义… 这样的雪球。
中转站务必显式传 incremental_output=true,不要依赖默认值。
1.3 原生接口是嵌套结构
// DashScope 原生
POST /api/v1/services/aigc/text-generation/generation
{
"model": "qwen-plus",
"input": { "messages": [...] }, // ← 嵌套
"parameters": { "temperature": 0.7, "result_format": "message" }
}
// 响应:output.choices[].message ← 比 OpenAI 多一层 output
result_format 取值 text / message——取 text 时响应结构完全不同(不是 choices 数组)。中转站应固定用 message。
1.4 其他
- 原生 HTTP 流式必须加 header
X-DashScope-SSE: enable⚠️,只设stream参数不生效。 - base_url 按地域 + workspace 分片 ✅:
https://{WorkspaceId}.{region}.maas.aliyuncs.com/compatible-mode/v1,region 含 cn-beijing / ap-southeast-1 / cn-hongkong / eu-central-1 / ap-northeast-1,另有https://dashscope-us.aliyuncs.com/compatible-mode/v1。中转站的上游配置不能是单一 URL 常量。 - OpenAI 兼容模式下需走
extra_body的非标参数 ✅:enable_thinking、thinking_budget、top_k、repetition_penalty(0.01–2.0)、enable_search/search_options、preserve_thinking/clear_thinking、enable_code_interpreter、tool_stream、vl_high_resolution_images。 - QwQ 等思考模型需要流式输出 ✅,非流式体验会因长推理阶段而劣化。
seed范围[0, 2³¹-1],与 OpenAI 的 int64 范围不同 ⚠️。
2. 智谱 GLM(BigModel)✅
端点:POST https://open.bigmodel.cn/api/paas/v4/chat/completions(注意路径是 /api/paas/v4/,不是 /v1/)
| 怪癖 | 详情 |
|---|---|
do_sample |
布尔,默认 true。为 false 时走贪心解码,temperature / top_p 全部失效。OpenAI 无此参数——中转站收到 temperature: 0 时应考虑翻译成 do_sample: false 而不是直接透传 |
temperature 范围 [0.0, 1.0] |
不是 OpenAI 的 0~2,超出需 clamp |
top_p 范围 [0.01, 1.0],默认 0.95 |
下界不是 0,top_p: 0 会被拒 |
finish_reason: "sensitive" |
内容被安全拦截。不在 OpenAI 枚举内,会打破严格客户端 |
thinking |
{"type": "enabled"|"disabled", "clear_thinking": bool},仅 GLM-4.5+ |
reasoning_content |
思维链输出字段(DeepSeek 风格的事实标准) |
| 工具上限 | 最多 128 个 function,另支持 web_search / 知识库检索 / MCP 工具 |
| 鉴权 | 现行推荐 Authorization: Bearer <api_key>;历史 JWT 方式仍支持 ⚠️——API Key 形如 {id}.{secret},需按点号拆分后签发含 api_key/exp/timestamp 的 JWT |
默认模型为 glm-5.2。流式同样以 data: [DONE] 结束。
3. 月之暗面 Kimi(Moonshot)✅
已拆分为独立文件:kimi.md(2026-08-13 全量重采集,并用真实 API key 实测校验)。本节仅保留跨厂商对比时用得上的摘要,细节以那边为准——尤其 §14 列出了 10 条被实测推翻的官方文档结论。
⚠️ 域名分层:文档站/控制台
platform.moonshot.cn→platform.kimi.com(301),但 API 数据面仍是api.moonshot.cn,没有迁到api.kimi.com。另有国际站platform.kimi.ai/api.moonshot.ai,账号与 key 与国内站完全隔离。
| 怪癖 | 详情 |
|---|---|
| 采样参数硬 400(实测) | K3 / K2.7-code 把 temperature(1.0) / top_p(0.95) / n(1) / 两个 penalty 写死,传其他值 400 而不是静默忽略(实测 invalid temperature: only 1 is allowed for this model)。而 moonshot-v1 的 temperature 默认是 0、上限是 1(OpenAI 是 2),且 n>1 要求 temperature>0(隐藏耦合,文档未提) |
| 同参数两入口行为相反(实测) | 同一个 temperature=0.7:OpenAI 入口 400,自家 Anthropic 入口 200 静默忽略。但 tool_choice 的限制两边一致——是选择性的不一致,不能"打不通就换入口" |
流式 usage 位置可控(实测) |
不传 stream_options.include_usage 时藏在 choices[0].usage(OpenAI SDK 的 chunk.usage 恒为 None,会漏计费);传 include_usage:true 则回到顶层 + choices:[],与 OpenAI 一致。中转站补这个参数即可,无需改写响应 |
| Partial Mode(前缀续写) | 在 messages 末尾加 {"role": "assistant", "content": "...", "partial": true},模型从该前缀继续。字段名是 partial,DeepSeek 叫 prefix —— 同一能力三家三个名字(另见 Anthropic 的 prefill,且 Claude 4.6+ 已禁用) |
n 最多 5 |
且仅 Moonshot V1 系列支持,新模型固定为 1 |
| 思考模型分裂成两套参数 | K3 用顶层 reasoning_effort(low/high/max,默认最贵的 max);K2.6/K2.5 用 thinking 对象;K2.7-code 用 thinking 但只接受 "enabled"(实测关闭 → 400)。⚠️ 文档称 K3 恒开不可关,实测 thinking:{"type":"disabled"} 有效(prompt_tokens 88→21,思考模式带约 67 token 隐藏 system prompt)。⚠️ reasoning_effort 传 "banana" 也返回 200——枚举校验形同虚设,非法值静默跑默认的 max 档 |
reasoning_content 明文回传 |
明文(非 Anthropic 那种加密 signature)。⚠️ 文档称 K3/K2.7-code 多轮缺失会报错,实测不报错(200 正常继续) |
tool_choice 与思考互斥 |
指定具体函数 + 思考开启 → 400 tool_choice 'specified' is incompatible with thinking enabled(两个入口一致)。⚠️ 但关闭思考后再指定函数 → 200 且模型不调用工具、直接幻觉出假数据——静默错误比 400 更危险,不要把关思考当变通方案 |
| tool_call id 可预测 | 格式是 {函数名}_{序号}(如 get_weather_0),不是 OpenAI 的随机串。多轮同名函数会重复 id,用它做去重键会误判 |
| 多模态不收 http URL | 只接受 base64 data URI 和 ms://<file_id>。与 OpenAI/Anthropic 的硬性差异 |
| 动态工具加载 | 工具可声明在 {"role":"system","tools":[...]}(无 content 字段)里,位置决定可见轮次。仅 K3 支持,其他模型报 tokenization failed |
finish_reason |
stop / length / tool_calls,与 OpenAI 一致,没有 sensitive——内容安全走 400 content_filter,比多数国产厂商干净 |
| 错误体缺字段 + 三种格式 | OpenAI 入口只有 error.type/error.message(无 code/param);Anthropic 入口多 request_id 和顶层 type;路径写错会掉到网关层,返回 {"code":5,"error":"url.not_found","message":"没找到对象","ua":"curl/8.7.1",...}——连 error 字段的类型都从 object 变成 string |
| 三种 429 语义相反 | engine_overloaded_error(退避重试)/ exceeded_current_quota_error(欠费,重试无用)/ rate_limit_reached_error(降并发)。只看状态码的通用重试会在欠费时打爆上游 |
| 错误信息泄漏账号标识 | 429 的 message 原文含 Your account org-xxx<ak-xxx> request reached organization max RPM: 3——org id 和 key 标识直接暴露。响应头另有 msh-org-id/msh-uid/msh-project-id。中转站原样透传 = 把自己的账号广播给所有下游用户 |
无 x-ratelimit-* 头 |
只在 429 时给 retry-after / x-retry-after / msh-cooldown-seconds(同一信息重复三份)。成功时无法得知剩余配额,配额管理只能本地计数 |
| 两个产品两套栈 | platform(api.moonshot.cn,key sk-)与 Kimi For Coding(api.kimi.com/coding,key sk-kimi-)key 互不通用,模型名(kimi-k3 vs k3)、Anthropic 入口路径、限流、错误语义、tool_call id 格式全不同。⚠️ coding 侧几乎不校验 model:传 gpt-4/空字符串都返回 200 并原样回显,只有带方括号的名字被拒(且返回 401 而非 404)。详见 kimi.md §17 |
无状态 API,多轮需自行拼接历史。
✅ context caching 已查证(原"需另行查证"结论作废):早期的独立 cache 对象 API 已被移除,现行是全自动前缀缓存——无需创建或引用 cache id。⚠️ 文档称"prompt > 256 token 才命中",实测 21 token 也完整命中。命中量同时报在 usage.cached_tokens(Kimi 私有)和 usage.prompt_tokens_details.cached_tokens(OpenAI 兼容,中转站直接用后者即可);未命中时字段不出现而非返回 0。K3 命中 ¥2/1M、未命中 ¥20/1M,10 倍价差意味着中转站任何对 prompt 前缀的改写都会带来 10 倍成本。
4. 字节 · 豆包 / 火山方舟(Ark)✅
来源:https://doubao.apifox.cn/265892759e0 、搜索结果
-
Base URL:
https://ark.cn-beijing.volces.com/api/v3,OpenAI SDK 直接可用。 -
model字段的历史怪癖 ⚠️:早期必须先在控制台创建"推理接入点"拿到 Endpoint ID(形如ep-2024...) 填进model,而不是模型名。现在也支持直接填模型名(如doubao-pro-32k-240615)。中转站的模型别名表必须允许"一个逻辑模型名 → 一个 ep-xxx 字符串"的映射,且不同用户的 ep id 不同(接入点是账号级资源)。这意味着模型路由表可能需要按租户隔离,而不是全局共享一张表。
-
usage 中含缓存相关明细字段。
-
⚠️ 抓取到的 OpenAPI 规范页未列出 thinking 参数和完整限制,深度思考模型(doubao-1.5-thinking 等)的参数需另查。
5. 百度 · 文心 / 千帆 ⚠️
来源:https://ai.baidu.com/ai-doc/WENXINWORKSHOP/0m2vwrjws(v2 通告,✅)+ 搜索结果(v1 约束,⚠️)
百度是"新旧两代差异最大"的一家。
v1(历史,仍在运行)的经典怪癖 ⚠️
- 每个模型一个独立 URL,模型名在路径里,没有
model参数。 - 鉴权用
access_token放 query string:先用 API Key + Secret Key 调grant_type=client_credentials换取。 messages必须是奇数条。- role 必须 user / assistant 严格交替:奇数位是
user(或function),偶数位是assistant,且第一条不能是function。 system是独立顶层字段,不能放进 messages(和 Anthropic 一样)。
这套约束意味着任何带连续同角色消息的 OpenAI 请求都无法直通 v1,中转站必须做消息合并(连续 user 合并成一条)。
v2(现行,已 OpenAI 兼容)✅
- 端点统一为
https://qianfan.baidubce.com/v2/chat/completions,用model参数选模型。 - 鉴权改为 IAM Bearer Token:
Authorization: Bearer bce-v3/ALTAK-...,并需要额外的appidheader ⚠️。 - 官方称"完全兼容 OpenAI SDK"。
- v1 在过渡期内继续可用,TPM 配额在两个版本间共享。
中转站建议直接对接 v2,但要注意 v2 是否仍保留 messages 交替约束——通告未明确说明,需实测验证。
6. 腾讯混元 ✅
两套接口差异极大:
原生接口
- 腾讯云 TC3-HMAC-SHA256 签名(云厂商通用签名 v3),需要多步 HMAC 派生密钥(SecretDate → SecretService → SecretSigning)。POST 支持最大 10MB。
- ⚠️ 字段命名是 PascalCase 大驼峰(
Messages/Role/Content),不是 OpenAI 的 snake_case。这是腾讯云全平台 API 的统一风格。
OpenAI 兼容接口(推荐)
- Base URL:
https://api.hunyuan.cloud.tencent.com/v1 - 直接用 API Key +
Authorization: Bearer,OpenAI SDK 无需改代码。
中转站结论:走兼容端点,不要碰 TC3 签名——那套签名实现成本高且容易在时钟偏移、URL 编码细节上出错。
7. MiniMax ✅
| 怪癖 | 详情 |
|---|---|
| 端点路径非标 | https://api.minimax.io/v1/text/chatcompletion_v2,不是 /v1/chat/completions |
HTTP 200 + base_resp |
业务错误也返回 200,状态在 base_resp.status_code(0 = 成功) |
| 业务状态码 | 1002 限流 / 1004 鉴权失败 / 1008 余额不足 / 1039 token 超限 / 2013 参数无效 |
| 内容安全字段 | input_sensitive、output_sensitive、input_sensitive_type / output_sensitive_type(1–7 分类)、mask_sensitive_info |
temperature 范围 |
0–1,非 OpenAI 的 0–2 |
top_p 默认 |
0.95 |
response_format |
JSON schema 仅 MiniMax-Text-01 支持 |
| 特有响应字段 | reasoning_content、audio_content、correct_rate(JSON schema 模式下的置信度) |
| usage | 单独统计 reasoning_tokens |
base_resp 这一条值得再强调:限流不返回 429,任何依赖 HTTP 状态码的重试/熔断中间件在 MiniMax 上都是失效的。
8. 讯飞星火 ✅
来源:https://www.xfyun.cn/doc/spark/ 系列页面
- 原生接口是 WebSocket,不是 HTTP ✅。鉴权方式是用 APPID + APIKey + APISecret 做 HMAC-SHA256,把签名拼进 WebSocket 连接 URL 里。这在中转站架构里是完全异类——需要独立的连接管理、心跳、重连逻辑,无法复用 HTTP 连接池。
- OpenAI 兼容 HTTP 端点:
https://spark-api-open.xf-yun.com/v1,用 APIPassword 认证;最新的 Spark-X2 还支持AK:SK拼接格式 ⚠️。
中转站结论:只对接 OpenAI 兼容 HTTP 端点。WebSocket 原生接口的接入成本远高于收益,除非需要它独有的能力(如实时语音)。
9. 给中转站的设计清单
从上面这些怪癖倒推,网关需要的抽象层:
- 双通道错误判定 — HTTP 状态码 + body 业务码(MiniMax
base_resp)。不能假设 HTTP 语义可靠。 - 可插拔签名器 — Bearer / JWT 自签 / query token / TC3-HMAC / WebSocket 签名 URL 至少五种。凭据模型要支持 (key, secret, appid, region) 而非单字符串。
- 流式规范化层 — 统一处理"全量累积 vs 增量"(阿里
incremental_output)、[DONE]有无、SSE header 要求(X-DashScope-SSE)。这一层是国产接入的核心价值。 - finish_reason 枚举映射 —
sensitive→content_filter,并用扩展字段保留原值,避免打破强类型客户端。 - 非标参数透传白名单 —
extra_body风格的旁路通道,而不是按 OpenAI schema 严格校验后丢弃。 - 租户级模型路由表 — 火山方舟的 Endpoint ID 是账号级资源,模型别名映射不能全局共享。
- 多地域 base_url — 阿里按 region + workspace 分片,上游地址不能是常量。
- 消息结构归一化 — 连续同角色消息合并(百度 v1 交替约束、Anthropic 单 system),作为通用前置处理。