# Chat Completions 格式

来源: https://platform.lyrouter.com/ai_gateway/docs/chat-completions
更新日期: 2026-10-19

根据对话历史生成模型回答，支持流式和非流式响应、图片输入和工具调用。兼容 OpenAI Chat Completions API：OpenAI 官方 SDK 和大多数兼容 OpenAI 的应用把 Base URL 改成 `https://api.lyrouter.com/v1` 即可使用。同一个模型可能由 OpenAI 兼容或 Anthropic 协议的通道服务，网关会自动转换请求和响应，本页同时说明转换时哪些参数会变化。

`POST /v1/chat/completions`

## 鉴权

在请求头中携带 API Key：

```text
Authorization: Bearer <API Key>
```

`Bearer` 区分大小写。也可以改用 `x-api-key: <API Key>`，两者同时存在时只读 `Authorization`。API Key 在工作空间的 **API 密钥** 页面创建，只用于 LYRouter 鉴权，不会转发给上游服务商。完整说明见[接入地址与鉴权](https://platform.lyrouter.com/ai_gateway/docs/call-gateway)。

## 请求头

| 请求头 | 必填 | 说明 |
| --- | --- | --- |
| `Content-Type` | 否 | 建议携带 `application/json`。不带时网关按 JSON 解析请求体；写成其他值（如 `application/x-www-form-urlencoded`）时返回 400 |

## 请求体

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `model` | string | 否 | 模型 ID，例如模型广场中复制的系统模型标识；也可以写虚拟模型 `@vmodel/<名称>` 或 `lyrouter/auto`。不写、写成 `""` 或 `null` 时使用工作空间的默认模型，没有默认模型时返回 400。见[模型命名与查询](https://platform.lyrouter.com/ai_gateway/docs/model-names) |
| `messages` | array | 是 | 对话消息，结构见下文 |
| `stream` | boolean | 否 | `true` 时以 SSE 流式返回，默认 `false` |
| `stream_options` | object | 否 | 流式时网关总是设为 `{"include_usage": true}`，最后一个 chunk 带 `usage`，你传的值会被覆盖 |
| `max_tokens` | integer | 否 | 本次最多生成的 Token 数。Anthropic 协议通道要求必填，请求中没有 `max_tokens` 和 `max_completion_tokens` 时网关补 4096 |
| `max_completion_tokens` | integer | 否 | OpenAI 新版的输出上限字段，透传给 OpenAI 兼容通道；发往 Anthropic 协议通道时在没有 `max_tokens` 的情况下作为 `max_tokens` 使用 |
| `temperature` | number | 否 | 采样温度，取值范围以模型为准（Anthropic 协议通道为 0～1），网关不做换算 |
| `top_p` | number | 否 | 核采样 |
| `stop` | string \| array | 否 | 停止序列；发往 Anthropic 协议通道时转换为 `stop_sequences` |
| `tools` | array | 否 | 可调用的函数列表，见[工具调用](https://platform.lyrouter.com/ai_gateway/docs/tool-call) |
| `tool_choice` | string \| object | 否 | `"auto"`、`"none"`、`"required"` 或 `{"type":"function","function":{"name":"..."}}`，各协议的对应关系见[工具调用](https://platform.lyrouter.com/ai_gateway/docs/tool-call) |
| `parallel_tool_calls` | boolean | 否 | 是否允许一次返回多个工具调用；发往 Anthropic 协议通道时会被去掉 |
| `response_format` | object | 否 | `{"type":"json_object"}` 或 `{"type":"json_schema",...}`；只对 OpenAI 兼容通道生效，发往 Anthropic 协议通道时会被去掉 |
| `reasoning_effort` | string | 否 | 推理强度，网关按服务商处理，见下文「推理参数」 |
| `enable_thinking` / `thinking_budget` | boolean / integer | 否 | 思考开关与思考预算，网关按服务商处理，见下文「推理参数」 |
| `seed` | integer | 否 | 随机种子；透传给 OpenAI 兼容通道，是否生效取决于模型；发往 Anthropic 协议通道时会被去掉 |
| `user` | string | 否 | 终端用户标识，透传给上游，是否生效取决于模型 |
| `frequency_penalty` / `presence_penalty` / `logprobs` / `n` | — | 否 | 透传给 OpenAI 兼容通道，是否生效取决于模型；发往 Anthropic 协议通道时会被去掉 |

表中没有列出的其他参数，网关原样转发给 OpenAI 兼容通道，是否生效取决于模型。

### messages 的结构

每条消息包含 `role` 和 `content`：

| `role` | 用途 | 说明 |
| --- | --- | --- |
| `system` | 系统提示词 | 放在最前面。发往 Anthropic 协议通道时，所有 `system` 和 `developer` 消息合并为 Anthropic 的 `system`，`content` 请写成字符串 |
| `developer` | 新版 OpenAI 的系统提示词 | 透传给 OpenAI 兼容通道，是否支持取决于模型；Anthropic 协议通道按 `system` 处理 |
| `user` | 用户输入 | `content` 可以是字符串或内容块数组 |
| `assistant` | 模型之前的回答 | 多轮对话时原样放回；包含工具调用时带 `tool_calls` |
| `tool` | 工具执行结果 | 必须带 `tool_call_id`，对应上一条 assistant 消息中的调用 ID |

`content` 有两种写法。纯文本直接写字符串；需要图片时写成内容块数组：

```json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "这张图里有什么？" },
    { "type": "image_url", "image_url": { "url": "https://example.com/cat.jpg" } }
  ]
}
```

- `image_url.url` 可以是公网可访问的 `https://` 地址，也可以是 `data:image/png;base64,<数据>` 形式的 Base64 数据。
- 模型必须支持图像输入，只接受文本的模型返回 400 `This model accepts text input only`。在模型广场用「输入模态」筛选可用模型，详见[多模态能力](https://platform.lyrouter.com/ai_gateway/docs/multimodal)。
- Anthropic 协议通道只转换文本块和图片块；`input_audio`、`file` 等其他内容块只对 OpenAI 兼容通道有效。

### 推理参数

三个推理参数分属不同服务商的写法，网关按实际服务这次请求的服务商保留或去掉，不会把其中一个换算成另一个（发往 Anthropic 协议通道时转换成 `thinking`，见表中说明）：

| 实际服务的上游 | `reasoning_effort` | `enable_thinking` | `thinking_budget` |
| --- | --- | --- | --- |
| OpenAI 官方接口 | 转发 | 去掉 | 去掉 |
| 阿里云百炼（DashScope 兼容接口） | 去掉 | 转发 | 转发 |
| DeepSeek 官方接口 | 去掉 | 去掉 | 去掉（是否思考由所选模型决定） |
| Anthropic 协议通道 | 去掉 | `true` 且没有预算时转为 `thinking: {"type": "adaptive"}`，`false` 时不开启思考 | 大于 0 时转为 `thinking: {"type": "enabled", "budget_tokens": N}` |
| 其他 OpenAI 兼容服务商 | 原样转发 | 原样转发 | 原样转发 |

- 实际服务的服务商在[请求日志](https://platform.lyrouter.com/ai_gateway/docs/logs)详情的「服务商」中查看。同一模型有多个来源时，请对每个来源验证推理参数是否生效。
- Anthropic 协议通道开启思考时，上游要求 `max_tokens` 大于 `budget_tokens`，网关不调整这些参数，不满足时由上游返回 400。
- 强制工具调用（`tool_choice` 为 `"required"` 或指定函数）且没有写 `enable_thinking` / `thinking_budget` 时，阿里云百炼通道会被自动加上 `enable_thinking: false`，因为这类上游在思考模式下不接受强制工具调用。
- OpenAI Python SDK 没有定义 `enable_thinking`、`thinking_budget`，请放在 `extra_body` 中传递，例如 `extra_body={"enable_thinking": False}`；Node.js SDK 直接写在请求参数里即可。

## 请求示例

**请求示例**

cURL:
```bash
curl https://api.lyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "messages": [
      {"role": "system", "content": "你是一个简洁的助手。"},
      {"role": "user", "content": "用一句话介绍 LYRouter。"}
    ],
    "temperature": 0.7,
    "max_tokens": 512
  }'
```

Python:
```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LYROUTER_API_KEY"],
    base_url="https://api.lyrouter.com/v1",
)
completion = client.chat.completions.create(
    model="MODEL_ID",
    messages=[
        {"role": "system", "content": "你是一个简洁的助手。"},
        {"role": "user", "content": "用一句话介绍 LYRouter。"},
    ],
    temperature=0.7,
    max_tokens=512,
)
print(completion.choices[0].message.content)
print(completion.usage)
```

Node.js:
```js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.LYROUTER_API_KEY,
  baseURL: "https://api.lyrouter.com/v1",
});
const completion = await client.chat.completions.create({
  model: "MODEL_ID",
  messages: [
    { role: "system", content: "你是一个简洁的助手。" },
    { role: "user", content: "用一句话介绍 LYRouter。" },
  ],
  temperature: 0.7,
  max_tokens: 512,
});
console.log(completion.choices[0].message.content);
console.log(completion.usage);
```

## 响应

非流式请求返回一个 `chat.completion` 对象；出错时返回 `error` 对象。下面的 200 是 OpenAI 兼容通道的典型响应，400 是没有写 `model` 且工作空间没有默认模型，429 是 API Key 当天的 Token 配额已用完（见[429：限流与配额](https://platform.lyrouter.com/ai_gateway/docs/rate-errors)）。

**响应示例**

200:
```json
{
  "id": "chatcmpl-9f1c2a7e4b",
  "object": "chat.completion",
  "created": 1791950400,
  "model": "MODEL_ID",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "LYRouter 是可信模型服务与 Token 治理平台，用一个 API Key 统一调用多家模型服务商。"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1250,
    "completion_tokens": 32,
    "total_tokens": 1282,
    "prompt_tokens_details": { "cached_tokens": 1024 },
    "completion_tokens_details": { "reasoning_tokens": 0 }
  }
}
```

400:
```json
{
  "error": {
    "message": "you must provide a model parameter",
    "type": "invalid_request_error",
    "code": "missing_model"
  }
}
```

429:
```json
{
  "error": {
    "message": "rate limit exceeded on apikey total_tokens/day",
    "type": "rate_limit_error",
    "subject": "apikey",
    "metric": "total_tokens",
    "window": "day",
    "limit": 1000000,
    "used": 1000523,
    "reset_at": "2026-10-14T16:01:00Z"
  }
}
```

| 字段 | 说明 |
| --- | --- |
| `id` | 上游生成的响应 ID，不是 LYRouter 的 Request ID。网关不在响应中返回 Request ID，排查时在日志详情顶部复制 |
| `model` | 上游返回的模型名，可能与请求中的模型 ID 写法不同；实际使用的模型以日志的「模型（实际）」为准 |
| `choices[0].message.content` | 回答正文；只返回工具调用时可能为 `null` 或空字符串 |
| `choices[0].message.tool_calls` | 模型请求调用的工具，见[工具调用](https://platform.lyrouter.com/ai_gateway/docs/tool-call) |
| `choices[0].message.reasoning_content` | 部分模型返回的思考过程；由 Anthropic 协议通道服务且开启思考时同样出现在这里 |
| `choices[0].message.reasoning_blocks` | LYRouter 扩展字段，只在 Anthropic 协议通道开启思考时出现，保存带签名的原始思考块。多轮工具调用时把 assistant 消息原样放回，网关才能把思考块交还上游 |
| `choices[0].finish_reason` | `stop`（正常结束）、`length`（达到输出上限）、`tool_calls`（等待工具结果）、`content_filter`（被上游过滤）等。Anthropic 协议通道的 `end_turn`、`stop_sequence` 映射为 `stop`，`max_tokens` 映射为 `length`，`tool_use` 映射为 `tool_calls` |
| `usage` | 本次的 Token 用量，由上游上报，见下表 |

### usage 与日志中的四项 Token

`usage` 的字段取决于实际服务的上游。它与[请求日志](https://platform.lyrouter.com/ai_gateway/docs/logs)「Token 消耗」四项的对应关系如下：

| 日志中的名称 | OpenAI 兼容通道 | Anthropic 协议通道 |
| --- | --- | --- |
| 「输入（缓存命中）」 | `prompt_tokens_details.cached_tokens`（DeepSeek 等上游为 `prompt_cache_hit_tokens`） | `cache_read_input_tokens` |
| 「输入（缓存未命中）」 | `prompt_tokens` 减去缓存命中 | `prompt_tokens` |
| 「输出」 | `completion_tokens`，包含思考 Token（`completion_tokens_details.reasoning_tokens`） | `completion_tokens` |
| 「显式缓存创建」 | 不出现 | `cache_creation_input_tokens` |

由 Anthropic 协议通道服务时，响应中的 `usage` 是这样的，`prompt_tokens` **不包含**缓存命中和缓存写入：

```json
"usage": {
  "prompt_tokens": 226,
  "completion_tokens": 32,
  "total_tokens": 258,
  "cache_read_input_tokens": 1024,
  "cache_creation_input_tokens": 0
}
```

自己按 `usage` 估算费用时，先确认是哪种上游，再按四项分别计价，方法见[调用费用与缓存计费](https://platform.lyrouter.com/ai_gateway/docs/estimate)。核对时以日志为准。

## 流式响应

设置 `"stream": true` 后，响应的 `Content-Type` 为 `text/event-stream`，每个事件是一行 `data: <JSON>` 加一个空行：

```text
data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"LYRouter 是"},"finish_reason":null}]}

data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"可信模型服务与 Token 治理平台。"},"finish_reason":null}]}

data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-9f1c2a7e4b","object":"chat.completion.chunk","created":1791950400,"model":"MODEL_ID","choices":[],"usage":{"prompt_tokens":1250,"completion_tokens":32,"total_tokens":1282,"prompt_tokens_details":{"cached_tokens":1024}}}

data: [DONE]
```

- 正文在 `choices[0].delta.content` 中逐段到达，思考过程在 `delta.reasoning_content`（个别上游为 `delta.reasoning`）。
- 网关总会开启 `include_usage`，所以结束前有一个 `choices` 为空数组、带 `usage` 的 chunk。读取 `choices[0]` 之前先判断数组非空。
- `data: [DONE]` 是正常结束的标记。没有收到它就断开，或收到了 `"code": "stream_interrupted"` 的错误事件，说明输出不完整，见[超时与流式中断](https://platform.lyrouter.com/ai_gateway/docs/stream-errors)。
- 由 Anthropic 协议通道服务时，chunk 由网关转换生成，带 `usage` 的 chunk 只有 `prompt_tokens`、`completion_tokens`、`total_tokens` 三个字段，缓存数量请在日志中查看。

**流式示例**

cURL:
```bash
curl -N https://api.lyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","stream":true,"messages":[{"role":"user","content":"写两行关于秋天的诗"}]}'
```

Python:
```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LYROUTER_API_KEY"],
    base_url="https://api.lyrouter.com/v1",
)
stream = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "写两行关于秋天的诗"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\n", chunk.usage)
```

Node.js:
```js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.LYROUTER_API_KEY,
  baseURL: "https://api.lyrouter.com/v1",
});
const stream = await client.chat.completions.create({
  model: "MODEL_ID",
  messages: [{ role: "user", content: "写两行关于秋天的诗" }],
  stream: true,
});
for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content;
  if (text) process.stdout.write(text);
  if (chunk.usage) console.log("\n", chunk.usage);
}
```

三种对话接口的事件格式对比和客户端实现要点见[流式响应](https://platform.lyrouter.com/ai_gateway/docs/streaming)。

## 网关行为

与直接调用上游相比，经 LYRouter 调用有以下差别：

- **选择通道与回退**：网关按工作空间的路由策略为模型选择通道，失败时换下一个通道，最后可能改用工作空间的默认模型，见[失败重试与备用模型](https://platform.lyrouter.com/ai_gateway/docs/fallback)和[通道选择与路由策略](https://platform.lyrouter.com/ai_gateway/docs/routing)。流式输出开始后不再换通道。
- **协议转换**：通道是 Anthropic 协议时，网关把请求转换成 Anthropic Messages 格式，响应再转换回 Chat Completions 格式。`response_format`、`seed`、`n`、`logprobs`、`top_logprobs`、`frequency_penalty`、`presence_penalty`、`logit_bias`、`parallel_tool_calls`、`reasoning_effort` 会被去掉，`metadata` 只保留 `user_id`；这些去掉不会写进响应头。对比见[模型能力与兼容性](https://platform.lyrouter.com/ai_gateway/docs/capabilities)。
- **推理参数**：按上文「推理参数」的表格保留、去掉或转换。
- **提示词缓存标记**：`cache_control` 只在 Anthropic 协议通道上生效，保留文本内容块和工具定义上的标记，`"ttl": "1h"` 时网关把 `anthropic-beta` 请求头设为 1 小时缓存所需的值（覆盖客户端自带的值）；发往 OpenAI 兼容通道时全部删除，由上游的隐式缓存工作。
- **历史消息中的扩展字段**：发往 OpenAI 兼容通道前，网关删除消息上的 `reasoning_content`、`reasoning_blocks` 和 `cache_control`。
- **固定 JSON 键序**：网关把发往上游的请求体中所有 JSON 对象的键按固定顺序重新排列，包括工具调用参数字符串里的 JSON，使相同内容每次产生相同的字节，提高上游前缀缓存的命中率，见[降低成本与优化缓存](https://platform.lyrouter.com/ai_gateway/docs/cost-practice)。
- **数字精度**：网关会解析并重新编码请求体，数字最多保留 14 位有效数字。超过的长整数（如 19 位 ID）会变成科学计数法，请以字符串传递。
- **用量**：流式请求总会返回 `usage`；`usage` 的字段随上游变化，见上文对照表。

## 常见错误

错误响应为 `{"error":{"message":"...","type":"...","code":"..."}}`，`code` 只有部分错误才有。

| 状态码 | `error.message` | `error.code` | 原因与处理 |
| --- | --- | --- | --- |
| 400 | `you must provide a model parameter` | `missing_model` | 没有写 `model`，工作空间也没有默认模型。写上模型 ID。请求体不是合法的 JSON 对象时，部分版本也返回这个错误，见下一行 |
| 400 | `request body must be a JSON object` | `invalid_json` | 请求体不是合法的 JSON 对象，例如 JSON 被截断或顶层是数组。部分版本返回的是 400 `you must provide a model parameter`（`missing_model`）。工作空间设置了默认模型时，返回的是 `{"message":"could not get parse JSON request body: <解析错误>"}`，外层没有 `error` |
| 400 | `This model accepts text input only` | `unsupported_model_input` | 给只接受文本的模型发了图片等非文本内容，换用支持图像输入的模型 |
| 400 | `This model does not support the chat_completions API` | `unsupported_model_api` | 模型不支持对话接口，例如图像生成模型 |
| 403 | `Model is not available in the model plaza` | `model_not_available` | 工作空间没有开通这个模型，请管理员在模型广场开通，或换用已开通的模型 |
| 503 | `all routed candidates exhausted` | `no_route_candidate` | 找不到可服务这次请求的通道，常见于 `@vmodel/<名称>` 写错或模型没有可用来源，见[模型不可用](https://platform.lyrouter.com/ai_gateway/docs/unavailable) |

- 401 / 403 鉴权问题见[401 / 403：鉴权与权限](https://platform.lyrouter.com/ai_gateway/docs/auth-errors)，429 见[429：限流与配额](https://platform.lyrouter.com/ai_gateway/docs/rate-errors)。
- 500 / 504 是连接上游失败或等待上游超时，响应体是 HTML 页面而不是 JSON，见[超时与流式中断](https://platform.lyrouter.com/ai_gateway/docs/stream-errors)。
- 网关自己生成的部分 400 是一行纯文本而不是 JSON，例如 `Content-Type` 不是 `application/json` 时的 `unsupported content-type: …`。这类响应的响应头目前仍是 `Content-Type: application/json`，解析错误体前请先判断正文是否为 JSON。
- 上游服务商返回的错误会原样转发，状态码和报错原文由上游决定。完整列表见[错误码与请求标识](https://platform.lyrouter.com/ai_gateway/docs/errors)。
