多厂商差异
兼容度标识、各入口协议下的字段生效规则、厂商专有参数、输出上限、用量与计价维度。
更新于 2026-10-23·阅读约 9 分钟
平台上的模型来自不同厂商,各厂商的原生协议不完全相同。统一入口支持三种格式:
| 接口 | 格式 |
|---|---|
/v1/chat/completions、/v1/embeddings | OpenAI 格式 |
/v1/responses | OpenAI Responses 格式 |
/v1/messages | Anthropic Messages 格式 |
三种格式均使用 SciRouter 的 API Key,网关调用厂商时换用平台在该厂商的凭据。/v1 不替换模型(响应中的 model 即请求的模型),只在同一模型的多条线路之间自动转移。
兼容度标识
#模型详情页第一屏有一行兼容度标识(模型对比页中为「接入兼容度」一行),共三种:
| 标识 | 含义 |
|---|---|
| 「完全兼容 OpenAI 接口」 | 现有的 OpenAI 代码修改 base_url 与 model 即可使用 |
| 「兼容,专有参数需放 extra_body」 | 标准字段照常使用;该厂商特有的参数按厂商专有参数的方式传递 |
| 「需专用调用方式」 | 经统一入口可使用 OpenAI 或 Anthropic Messages 格式;只有绕过平台直连厂商时才需要该厂商的原生协议 |
标识下方如有一行说明,为平台针对该模型写明的具体差异,以说明为准。兼容度是平台标注的说明,不改变网关的行为;哪些字段生效,由模型背后线路的入口与本次调用所走的入口决定,见字段生效规则。
字段生效规则
#模型背后的每条线路对应一个上游账号,可接入多种协议;每种协议是线路的一个入口(openai、anthropic、gemini、custom,由平台配置)。每次调用先按调用方使用的格式选择入口:
| 调用格式 | 可原样处理该格式的入口 |
|---|---|
| OpenAI 格式 | openai、custom |
| Anthropic Messages 格式 | anthropic |
| Responses 格式 | 标记为「原生 Responses」的 openai、custom |
线路有可原样处理该格式的入口时走该入口;没有时走线路的主入口,由网关转换为该入口的协议。
无论走哪个入口,网关都不会原样复制请求体:三种格式先归一为同一份请求意图,经参数门按模型声明的能力与请求规则检查,再按入口的协议渲染为该厂商接受的写法。例外:Anthropic 格式发往 Anthropic 入口、Responses 格式发往原生 Responses 入口时原样转发,只修改少数几处,见 Messages 与 Responses。
每个参数的结局只有四种:原样带过去、翻译、平台模拟、明确告知(去掉并留痕,或返回 400)。逐个参数的规则,以及如何从响应头 X-SciRouter-Param-Actions 查看平台的修改,见参数如何被处理。本节只说明协议之间的主要差异。
OpenAI 兼容入口
#走 openai 或 custom 入口时:
- 标准参数按意图渲染(输出上限按该厂商接受的字段名写入,思考强度按该厂商的写法写入等);登记表之外的专有参数原样带过去。
user与metadata不发给上游,只记录在请求日志中;按需补上stream_options.include_usage。- 上游明确不支持某个参数时:提示类参数由平台去掉后重发(响应头中记为
healed=…);契约类参数换线路,都不行时将上游的 400 返回调用方(code为unsupported_parameter,param为参数名)。 - 无法识别是哪个参数的上游 400 仍为
validation_failed,error.details.source为upstream,厂商的原始错误在error.details.provider_error中。
Anthropic 入口
#anthropic 入口使用 Anthropic Messages 协议,有该入口的线路可原样处理 Anthropic 格式。
- 调用方使用
/v1/messages时请求原样转发:只替换model、去掉平台自用的 metadata、写入生效的max_tokens,历史只追加、不改写。响应保持 Anthropic 格式,厂商自定义参数、思考块与签名、文档块、anthropic-beta均会到达上游。 - 调用方使用 OpenAI 格式、且线路没有 OpenAI 兼容入口时,才按登记表将请求翻译过去。
格式转换
#走 gemini 入口,或以 OpenAI 格式调用只有 Anthropic 入口的线路时,网关将请求翻译为入口的格式。常见字段:
| 字段 | anthropic | gemini |
|---|---|---|
messages(文本、image_url 图片、PDF) | ✓(http:// 图片由平台取回后内联) | ✓(远程图片由平台取回后内联) |
max_tokens / max_completion_tokens | ✓(不写时按 4096) | ✓ |
temperature、top_p、stop、stream | ✓(开启思考时去掉 temperature;去掉纯空白的 stop) | ✓(stop 超过 5 个时由平台截断) |
tools、tool_choice、tool 角色的消息、parallel_tool_calls | ✓ | ✓(parallel_tool_calls: false 由平台只保留第一个) |
response_format(json_object / json_schema) | 原生结构化输出,否则由平台模拟 | ✓ |
reasoning_effort | 翻译为 thinking,按模型的思考控制 | 翻译为思考配置,按模型的思考控制 |
seed、presence_penalty / frequency_penalty | 去掉并留痕 | ✓ |
内容段上的 cache_control | ✓ | 去掉 |
n > 1、logprobs | 400 unsupported_param | ✓ |
logit_bias、音频输出 | 400 unsupported_param | 400 unsupported_param |
调用方使用 Anthropic 格式、线路没有 Anthropic 入口时,网关先转为同一份意图再渲染,规则相同,并尽量不丢失内容:缓存断点、思考签名、anthropic-beta 按线路配置以扩展字段带过去;OpenAI 格式中没有对应概念的块改写为等价的文字或普通工具。分级规则与只能返回 400 的内容见 Messages「请求的转发方式」。
能力标签
#模型详情页的「能力」标签(工具调用、JSON 模式、看图等)即 GET /models 中的 features,参数门按其判断。例如给未标注「工具调用」的模型带 tools 时,请求不发往上游,直接返回 400 unsupported_param(details.cause 为 feature_missing)。
不确定时先在 Playground 中试用,或读取 GET /models 中的 supported_params。
厂商专有参数
#模型详情页的参数表将参数分为通用与「该厂商专有」两组,名称、类型、范围、默认值来自平台维护的能力描述符;/v1 不按其校验请求。
专有参数写在请求体顶层,与 temperature 并列。OpenAI Python SDK 的 extra_body 将字典中的字段合并到请求体顶层:
pythonimport os
from openai import OpenAI
client = OpenAI(api_key=os.environ["SCIROUTER_API_KEY"], base_url="https://api.scirouter.cn/v1")
response = client.chat.completions.create(
model="YOUR_MODEL_ID",
messages=[{"role": "user", "content": "…"}],
temperature=0.7,
extra_body={"top_k": 20}, # 发出去的请求体里是顶层的 "top_k": 20
)使用 curl 或其他语言时直接写在顶层:
bashcurl https://api.scirouter.cn/v1/chat/completions \
-H "Authorization: Bearer $SCIROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "YOUR_MODEL_ID", "messages": [{"role": "user", "content": "…"}], "top_k": 20}'请求体中不写名为 extra_body 的字段:/v1 不展开该字段,会将其原样转给上游(通常被忽略或拒绝)。
- 参数表中写作
extra_body.xxx的,xxx即顶层字段名;写作嵌套路径的(如chat_template_kwargs.enable_thinking),按该嵌套结构写入请求体。 - 专有参数只在走 OpenAI 兼容入口(
openai、custom)时生效,见字段生效规则;走 Anthropic 或 Gemini 入口时去掉并留痕(响应头X-SciRouter-Param-Actions中记为other)。
输出上限
#各模型的单次最多输出不同,见模型详情页「单次最多输出」。max_tokens 超过模型的上限或套餐的单次上限时:
| 接口 | 处理 |
|---|---|
对话补全(/chat/completions) | 直接返回 400,error.details.limit 为允许的最大值;不截断到上限 |
Anthropic 格式(/messages) | 按上限发出,响应头 X-SciRouter-Max-Tokens-Clamped 写明请求值、实际值与生效的是哪一道上限,见 Messages |
用量与计费
#响应中的 usage 采用 OpenAI 的格式:prompt_tokens、completion_tokens、total_tokens,另有以下细分字段:
| 字段 | 说明 |
|---|---|
usage.prompt_tokens_details.cached_tokens | 命中上游提示词缓存的输入,已含在 prompt_tokens 中 |
usage.prompt_tokens_details.cache_write_tokens | 本次写入上游缓存的输入。非 OpenAI 标准字段,有写入时才返回 |
usage.completion_tokens_details.reasoning_tokens | 思考过程的 token,已含在 completion_tokens 中 |
- 走 OpenAI 兼容入口时
usage由上游原样返回,细分字段是否存在、名称如何取决于上游(个别兼容服务只在顶层返回prompt_cache_hit_tokens);经其他入口转换时由网关按上表生成。 - 对账不使用
usage自行计算费用。单次调用扣除的配额见响应体的scirouter.cost_quota(非流式另有响应头X-SciRouter-Cost-Quota),或控制台请求日志与导出中的cost_quota。 - 上游未返回用量时平台按估算计费(日志中标注「估算」);命中响应缓存时
usage为原结果的数值,但不扣费。这两种情况只能以cost_quota为准。
计价维度
#账单按各维度的用量乘以倍率计算,各维度互不重叠:
| 维度 | 含义 | 模型未单独设置倍率时 |
|---|---|---|
input_tokens | 普通输入(不含缓存命中与写入) | — |
cached_input_tokens | 命中上游缓存的输入 | 按 input_tokens 的倍率 |
cache_write_input_tokens | 写入上游缓存的输入(5 分钟缓存) | 按 input_tokens 的倍率 |
cache_write_1h_input_tokens | 写入上游 1 小时缓存的输入 | 按 cache_write_input_tokens 的倍率;该倍率也未设置时按 input_tokens |
output_tokens | 普通输出(不含思考) | — |
reasoning_tokens | 思考过程 | 按 output_tokens 的倍率 |
- 维度可能有阶梯:用量达到某个值之后,整笔按另一档倍率计,不累进。
- 维度可能有最小计费量:不足时按最小量计。
- 部分模型有长上下文分档:输入合计超过某个值时整笔换用另一套倍率,见模型列表的
context_tiers。
各模型的维度与倍率以模型中心详情页的价格表为准。代码中按数组遍历维度,不只读取两个键。
流式
#所有对话模型均支持流式(stream: true)。对话补全的分片为 OpenAI 的 chat.completion.chunk,经转换的调用也由网关转为相同的形状;/messages 的流为 Anthropic 的事件,见 Messages。对话补全与直连 OpenAI 相比有以下差异:
| 项目 | 说明 |
|---|---|
| 心跳 | 长时间无输出时(如排队),约每 10 秒发送一行 : ping。这是 SSE 注释,官方 SDK 会忽略;自行解析 SSE 时跳过以 : 开头的行 |
| 收尾扩展分片 | data: [DONE] 之前多一个 choices 为空、带顶层 scirouter 字段的分片(本次消耗、尝试次数、耗时,见可观测性)。上游未输出任何分片即结束时不发送 |
| 中途出错 | 流开始后出错时,最后一个分片为 {"error": {…}}(与非流式错误体格式相同,带 request_id),之后没有 [DONE] |
| 用量分片 | 只在请求带 stream_options: {"include_usage": true} 时转发;未带时平台仍按上游用量结算 |
| 工具进度分片 | 使用平台托管工具时,另有 choices 为空、带 scirouter.tool 的工具进度分片 |
解析时忽略无法识别的分片,不作为错误处理。
思考过程与结束原因
#| 项目 | 对话补全 | /messages |
|---|---|---|
| 思考过程 | 带思考的模型在 reasoning_content 与 reasoning 两个字段中返回相同的思考内容(非流式在 message,流式在 delta),读取任一个即可。对思考过程签名的模型另有 thinking_blocks(带签名的思考块,多轮工具调用时原样带回,见对话补全「思考过程」) | Anthropic 的 thinking 块 |
| 结束原因 | 统一为 OpenAI 的取值 stop / length / tool_calls / content_filter | Anthropic 的 stop_reason |
错误
#各厂商的错误均归一为平台错误码,见错误码。厂商原始的错误码与信息在 error.details.provider_error 中,只用于排障,不用于程序分支(随上游变化)。
上游拒绝请求时返回 400:
- 平台识别出语义的归为固定的 code:上下文超长
context_window_exceeded、参数不支持unsupported_param(提示类参数平台会先自行去掉后重发)、内容审核content_filtered。这三个的error.code为原厂兼容值,平台码在error.scirouter_code。 - 无法识别语义的仍为
validation_failed,error.details.source为upstream。