# 多厂商差异

> 兼容度标识、各入口协议下的字段生效规则、厂商专有参数、输出上限、用量与计价维度。

来源：https://www.scirouter.cn/docs/vendors · 更新于 2026-10-23

平台上的模型来自不同厂商，各厂商的原生协议不完全相同。统一入口支持三种格式：

| 接口 | 格式 |
|---|---|
| `/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](https://www.scirouter.cn/docs/messages) 与 [Responses](https://www.scirouter.cn/docs/responses)。

每个参数的结局只有四种：原样带过去、翻译、平台模拟、明确告知（去掉并留痕，或返回 400）。逐个参数的规则，以及如何从响应头 `X-SciRouter-Param-Actions` 查看平台的修改，见[参数如何被处理](https://www.scirouter.cn/docs/params)。本节只说明协议之间的主要差异。

### 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](https://www.scirouter.cn/docs/messages)「请求的转发方式」。

### 能力标签

模型详情页的「能力」标签（工具调用、JSON 模式、看图等）即 `GET /models` 中的 `features`，参数门按其判断。例如给未标注「工具调用」的模型带 `tools` 时，请求不发往上游，直接返回 400 `unsupported_param`（`details.cause` 为 `feature_missing`）。

不确定时先在 [Playground](https://www.scirouter.cn/playground) 中试用，或读取 `GET /models` 中的 `supported_params`。

## 厂商专有参数

模型详情页的参数表将参数分为通用与「该厂商专有」两组，名称、类型、范围、默认值来自平台维护的能力描述符；`/v1` 不按其校验请求。

专有参数写在请求体顶层，与 `temperature` 并列。OpenAI Python SDK 的 `extra_body` 将字典中的字段合并到请求体顶层：

```python
import 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 或其他语言时直接写在顶层：

```bash
curl 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](https://www.scirouter.cn/docs/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` 的倍率 |

- 维度可能有阶梯：用量达到某个值之后，整笔按另一档倍率计，不累进。
- 维度可能有最小计费量：不足时按最小量计。
- 部分模型有长上下文分档：输入合计超过某个值时整笔换用另一套倍率，见[模型列表](https://www.scirouter.cn/docs/models)的 `context_tiers`。

各模型的维度与倍率以[模型中心](https://www.scirouter.cn/models)详情页的价格表为准。代码中按数组遍历维度，不只读取两个键。

## 流式

所有对话模型均支持流式（`stream: true`）。对话补全的分片为 OpenAI 的 `chat.completion.chunk`，经转换的调用也由网关转为相同的形状；`/messages` 的流为 Anthropic 的事件，见 [Messages](https://www.scirouter.cn/docs/messages)。对话补全与直连 OpenAI 相比有以下差异：

| 项目 | 说明 |
|---|---|
| 心跳 | 长时间无输出时（如排队），约每 10 秒发送一行 `: ping`。这是 SSE 注释，官方 SDK 会忽略；自行解析 SSE 时跳过以 `:` 开头的行 |
| 收尾扩展分片 | `data: [DONE]` 之前多一个 `choices` 为空、带顶层 `scirouter` 字段的分片（本次消耗、尝试次数、耗时，见[可观测性](https://www.scirouter.cn/docs/observability)）。上游未输出任何分片即结束时不发送 |
| 中途出错 | 流开始后出错时，最后一个分片为 `{"error": {…}}`（与非流式错误体格式相同，带 `request_id`），之后没有 `[DONE]` |
| 用量分片 | 只在请求带 `stream_options: {"include_usage": true}` 时转发；未带时平台仍按上游用量结算 |
| 工具进度分片 | 使用平台托管工具时，另有 `choices` 为空、带 `scirouter.tool` 的工具进度分片 |

解析时忽略无法识别的分片，不作为错误处理。

## 思考过程与结束原因

| 项目 | 对话补全 | `/messages` |
|---|---|---|
| 思考过程 | 带思考的模型在 `reasoning_content` 与 `reasoning` 两个字段中返回相同的思考内容（非流式在 `message`，流式在 `delta`），读取任一个即可。对思考过程签名的模型另有 `thinking_blocks`（带签名的思考块，多轮工具调用时原样带回，见[对话补全](https://www.scirouter.cn/docs/chat-completions)「思考过程」） | Anthropic 的 `thinking` 块 |
| 结束原因 | 统一为 OpenAI 的取值 `stop` / `length` / `tool_calls` / `content_filter` | Anthropic 的 `stop_reason` |

## 错误

各厂商的错误均归一为平台错误码，见[错误码](https://www.scirouter.cn/docs/errors)。厂商原始的错误码与信息在 `error.details.provider_error` 中，只用于排障，不用于程序分支（随上游变化）。

上游拒绝请求时返回 400：

- 平台识别出语义的归为固定的 code：上下文超长 `context_window_exceeded`、参数不支持 `unsupported_param`（提示类参数平台会先自行去掉后重发）、内容审核 `content_filtered`。这三个的 `error.code` 为原厂兼容值，平台码在 `error.scirouter_code`。
- 无法识别语义的仍为 `validation_failed`，`error.details.source` 为 `upstream`。
