> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aihubmix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 实时对话

> 通过 WebSocket 建立持久连接，与语音对话模型进行低延迟的双向语音／文本交互

## 介绍

实时对话通过 WebSocket（一种在客户端与服务端之间保持长连接、可双向推送数据的协议）建立持久连接，把你的音频或文本输入实时送入对话模型，模型再以增量方式推送文本与语音回复，适合语音助手、实时问答、口语陪练等需要来回交互的场景。

它与[实时转录](https://docs.aihubmix.com/cn/api/realtime-transcription.md)同样走 WebSocket，但用途不同：

| 维度 | 实时转录 | 实时对话（本页） |
| - | - | - |
| 目标 | 把语音转成文字 | 与模型进行多轮对话，模型会生成回复 |
| 方向 | 单向：音频进、文本出 | 双向：音频／文本进，文本＋语音出 |
| 连接参数 | `?intent=transcription&model=...` | 仅 `?model=...`（不带 `intent`） |
| 语音活动检测（VAD） | 不支持，必须 `null` | 支持，可开启自动断句 |
| 典型场景 | 会议字幕、直播听写 | 语音助手、实时口语交互 |

**可用模型：**

* **gpt-realtime-2.1**：语音对话模型，支持音频、文本与图片输入，实时输出文本与语音回复。

> [!WARNING]
>
> **本接口面向服务端集成，浏览器无法直连。** 出于安全考虑，网关会校验并拒绝带 `Origin` 头的连接、拒绝 `openai-insecure-api-key` 子协议，密钥只接受标准 `Authorization` 头。浏览器发起的 WebSocket 会自动附带 `Origin` 头，因此会被拒绝。若需在前端做实时对话，请在你自己的服务端建立到网关的连接、再把音频与结果在前端与服务端之间转发。

## 快速开始

### 连接端点

```text theme={null}
wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1
```

* `model=gpt-realtime-2.1`：**必填**，模型在连接时由 URL 参数定死，会话中不可再更改（见下方约束）。
* **注意与转录的区别**：对话端点**不带** `intent=transcription`。

### 鉴权

握手时通过标准 HTTP 头传入密钥：

```text theme={null}
Authorization: Bearer $AIHUBMIX_API_KEY
```

### 音频格式要求

当前音频输入与输出均只支持一种格式，发送前请将音频转换为：

* **编码**：PCM16（16 位有符号整数，小端序）
* **采样率**：24000 Hz
* **声道**：单声道（mono）

即 `audio/pcm@24000`。输入或输出声明为其它格式（如 G.711/µ-law）会被拒绝并关闭会话。

> [!NOTE]
>
> 与转录不同，对话会话**支持**语音活动检测（turn\_detection / VAD）。开启后模型会自动判断一段话何时结束并触发回复；关闭（设为 `null`）则由你手动控制何时提交音频、何时请求回复。二者按需选择即可。

## 会话配置（session.update）

连接建立后，客户端可发送一帧 `session.update` 配置对话参数（如语音音色、系统指令、是否开启 VAD）。对话会话的模型已由连接 URL 锚定，因此**不发送 `session.update` 也能直接对话**；需要自定义音色或指令时再发送。

```json theme={null}
{
  "type": "session.update",
  "session": {
    "type": "realtime",
    "instructions": "You are a helpful voice assistant. Keep answers concise.",
    "audio": {
      "input": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "turn_detection": { "type": "server_vad" }
      },
      "output": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "voice": "alloy"
      }
    }
  }
}
```

### 配置参数

**session.type** `string` *required*

会话类型，对话场景为 `realtime`。

**session.instructions** `string`

系统指令，用于设定模型的角色、语气与回答约束。

**session.output\_modalities** `string[]`

输出模态，取值为 `["audio"]` 或 `["text"]`：传 `["audio"]`（默认）时模型输出语音，回复文本随 `response.output_audio_transcript.delta` 事件返回；传 `["text"]` 时只输出文本，文本随 `response.output_text.delta` 事件返回。其它组合（如 `["audio", "text"]`）会被拒绝并返回 `error` 事件。

**session.audio.input.format** `object` *required*

输入音频格式，固定为 `{ "type": "audio/pcm", "rate": 24000 }`。

**session.audio.input.turn\_detection** `object | null`

语音活动检测。传入 `{ "type": "server_vad" }` 开启自动断句；传入 `null` 关闭，由客户端手动提交与请求回复。

**session.audio.output.format** `object` *required*

输出音频格式，固定为 `{ "type": "audio/pcm", "rate": 24000 }`。

**session.audio.output.voice** `string`

回复语音的音色。**首次回复开始后不可再更改**：会话进入生成状态后，再次发送的 `voice` 会被忽略（其余配置正常生效）。因此如需指定音色，请在首次请求回复前设置。

> [!NOTE]
>
> `gpt-realtime-2.1` 支持 PNG/JPEG/WebP 图片输入；不支持的图片格式会返回错误。以下能力**本期暂不支持**，配置后会话将被关闭（关闭码 `1008`）：在对话会话内开启内嵌转写（`audio.input.transcription`，原因 `input_transcription_not_supported`）、通过会话项注入音频（原因 `item_audio_not_supported`）、文本及受支持图片以外的其它内容项类型（原因 `unsupported_content_part`）。音频输入请统一走 `input_audio_buffer.append` 通道。

## 发送输入

### 发送音频

将 PCM16 音频切成小片（如每 100ms 一片），base64 编码后通过 `input_audio_buffer.append` 事件持续发送：

```json theme={null}
{
  "type": "input_audio_buffer.append",
  "audio": "<base64 编码的 PCM16 音频片段>"
}
```

开启 VAD 时，模型会自动判断说话结束并触发回复；关闭 VAD 时，发完一段音频后需手动提交并请求回复：

```json theme={null}
{ "type": "input_audio_buffer.commit" }
{ "type": "response.create" }
```

### 发送文本

也可以直接注入一条文本消息，再请求回复：

```json theme={null}
{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [{ "type": "input_text", "text": "Introduce yourself in one sentence." }]
  }
}
{ "type": "response.create" }
```

## 接收回复

服务端会持续推送事件，关键事件类型：

**session.created / session.updated** `event`

会话创建、配置更新的确认。收到 `session.created` 后即可开始发送音频、文本与受支持图片。

**conversation.item.added / conversation.item.done** `event`

会话项写入完成：用户输入与模型回复各生成一条会话项。

**input\_audio\_buffer.speech\_started / input\_audio\_buffer.speech\_stopped** `event`

开启 VAD 时，服务端检测到用户开始、结束说话。`speech_started` 通常意味着用户正在打断模型，处理方式见[打断与截断](#interruption)。

**response.created** `event`

一轮回复开始生成。

**response.output\_item.added / response.output\_item.done** `event`

本轮回复的输出项开始、结束。`added` 事件里的 `item.id` 是后续截断音频时要引用的会话项 ID。

**response.output\_audio.delta / response.output\_audio.done** `event`

回复语音的**增量**片段（base64 编码的 PCM16 音频）与结束标记，可边收边播。

**response.output\_audio\_transcript.delta / response.output\_audio\_transcript.done** `event`

与回复语音逐句对应的文字稿**增量**与结束标记，字段 `delta` 是本次新增的文字。**开启语音输出时，回复文本从这个事件取**，可用于边播语音边上屏字幕。

**response.output\_text.delta / response.output\_text.done** `event`

纯文本回复的**增量**与结束标记，仅在把输出模态设为纯文本（`output_modalities: ["text"]`）时出现。

**response.done** `event`

一轮回复完成。该事件携带本轮的 token 用量（`usage`），是计费的依据。

**conversation.item.truncated** `event`

截断请求已生效的确认，见[打断与截断](#interruption)。

**error** `event`

错误事件，包含错误码与说明。请求本身有问题（如 `output_modalities` 取值非法）时只回一帧 `error`，会话继续可用；涉及策略（如改模型、余额耗尽）时关闭会话。

> [!NOTE]
>
> **取回复文本要认准事件。** 默认（输出含语音）时模型只推 `response.output_audio_transcript.delta`，**不会**推 `response.output_text.delta`；把输出模态设为纯文本后，文本才走 `response.output_text.delta`。两种模式都建议一并监听，避免漏字（见下方[运行效果](#run-output)）。

## 打断与截断

用户在模型说话途中开口时，已经生成但还没播放的内容会与用户的下一句错位。WebSocket 连接由客户端负责播放，打断后的收尾需要客户端完成。

开启 VAD 时，服务端检测到用户开口后会推送 `input_audio_buffer.speech_started`。客户端收到该事件后：

1. **立即停止本地播放**，并记录这一轮回复已播放到的位置（毫秒）。
2. 发送 `conversation.item.truncate`，把未播放的音频从会话中移除，避免下一轮对话里模型认为这些内容已经说过。

```json theme={null}
{
  "type": "conversation.item.truncate",
  "item_id": "item_ABC123",
  "content_index": 0,
  "audio_end_ms": 1500
}
```

* `item_id`：本轮回复的会话项 ID，取自 `response.output_item.added` 事件的 `item.id`。
* `content_index`：音频内容项的下标，固定为 `0`。
* `audio_end_ms`：保留的音频长度，单位毫秒，按客户端实际播放到的位置填写。

服务端处理完成后返回 `conversation.item.truncated`。截断只影响这一轮回复的音频与对应文字稿，会话本身不受影响，之后可以继续下一轮对话。用 OpenAI SDK 时对应 `conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...)`。

关闭 VAD（如按键说话）时，按下按键即表示打断：客户端在按下时发送 `response.cancel` 取消进行中的回复，再按上面的步骤截断；松开按键后依次发送 `input_audio_buffer.append`、`input_audio_buffer.commit` 与 `response.create`。

## 完整示例

下面给出三种写法，任选其一：

* **OpenAI 官方 SDK（推荐）**：无需手写 WebSocket，把 `websocket_base_url`（SDK 的 WebSocket 基址参数）指到网关即可复用官方库。
* **OpenAI Agents SDK**：官方 agent 框架的实时语音形态，把 `model_config` 里的 `url` 换成网关地址即可。
* **原生 websockets**：不装 SDK，直接按协议收发帧，依赖最少、便于排查。

> [!NOTE]
>
> **为什么要在连接时传模型名？** AiHubMix 网关在 WebSocket 握手那一刻就要用 `model` 选择模型推理商、鉴权、预留额度，而 `session.update` 是握手完成之后才到、来不及。所以用 SDK 时要给 `connect()` 显式传 `model`（SDK 会把它拼进 URL query）；缺了它网关在**握手期就会拒绝**，连接根本建不起来。与转录不同，对话端点**不需要** `intent=transcription`。

```python Python (OpenAI SDK) theme={null}
# 依赖: pip install "openai[realtime]"
import asyncio
import base64
from openai import AsyncOpenAI

# 复用官方 SDK 只需两处适配:
#   1) websocket_base_url 指向 AiHubMix 网关(而非 OpenAI 默认地址)
#   2) connect() 显式传 model,网关握手必需(对话端点不需要 intent)
client = AsyncOpenAI(
    api_key="sk-***",  # 替换为你的 AiHubMix API 密钥
    websocket_base_url="wss://aihubmix.com/v1",
)

async def main():
    async with client.realtime.connect(model="gpt-realtime-2.1") as conn:
        # 1) 配置会话: 系统指令 + 音色 + 开启 VAD(自动断句)
        await conn.session.update(session={
            "type": "realtime",
            "instructions": "You are a helpful voice assistant. Keep answers concise.",
            "audio": {
                "input": {
                    "format": {"type": "audio/pcm", "rate": 24000},
                    "turn_detection": {"type": "server_vad"},
                },
                "output": {
                    "format": {"type": "audio/pcm", "rate": 24000},
                    "voice": "alloy",
                },
            },
        })

        # 2) 发送一条文本消息并请求回复(音频输入见原生示例的 append/commit)
        await conn.conversation.item.create(item={
            "type": "message",
            "role": "user",
            "content": [{"type": "input_text", "text": "What is the capital of France?"}],
        })
        await conn.response.create()

        # 3) 接收本轮回复: 文本走转写增量, 语音走音频增量
        async for event in conn:
            if event.type == "response.output_audio_transcript.delta":
                print(event.delta, end="", flush=True)  # 回复文本(输出含语音时)
            elif event.type == "response.output_audio_transcript.done":
                print()  # 本轮文字稿结束
            elif event.type == "response.output_text.delta":
                print(event.delta, end="", flush=True)  # 纯文本输出时
            elif event.type == "response.output_text.done":
                print()
            elif event.type == "response.output_audio.delta":
                pass  # base64 PCM16 音频片段, 可解码后播放
            elif event.type == "input_audio_buffer.speech_started":
                pass  # 用户打断: 停播后调用 conn.conversation.item.truncate(...) 截断
            elif event.type == "response.done":
                print("\n[本轮完成]", event.response.usage)
                break
            elif event.type == "error":
                print("\n[错误]", event.to_dict())
                break

asyncio.run(main())
```

```python Python (websockets) theme={null}
import asyncio
import base64
import json
import websockets

API_KEY = "sk-***"  # 替换为你的 AiHubMix API 密钥
URL = "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1"

async def main():
    # websockets >= 13 用 additional_headers；旧版本用 extra_headers
    async with websockets.connect(
        URL, additional_headers={"Authorization": f"Bearer {API_KEY}"}
    ) as ws:
        # 1) 配置会话(关闭 VAD, 手动控制回复时机)
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "type": "realtime",
                "instructions": "You are a helpful voice assistant.",
                "audio": {
                    "input": {
                        "format": {"type": "audio/pcm", "rate": 24000},
                        "turn_detection": None,
                    },
                    "output": {
                        "format": {"type": "audio/pcm", "rate": 24000},
                        "voice": "alloy",
                    },
                },
            },
        }))

        # 2) 读取本地 PCM16 / 24kHz / 单声道 裸音频,分块发送,再 commit + 请求回复
        async def send_audio():
            with open("audio_pcm16_24k.raw", "rb") as f:
                pcm = f.read()
            chunk = 24000 * 2 // 10  # 100ms = 采样率 × 2字节 ÷ 10
            for i in range(0, len(pcm), chunk):
                await ws.send(json.dumps({
                    "type": "input_audio_buffer.append",
                    "audio": base64.b64encode(pcm[i:i + chunk]).decode(),
                }))
                await asyncio.sleep(0.1)  # 模拟实时节奏
            # 关闭 VAD 时手动提交并请求回复
            await ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
            await ws.send(json.dumps({"type": "response.create"}))

        asyncio.create_task(send_audio())

        # 3) 接收回复
        async for msg in ws:
            evt = json.loads(msg)
            etype = evt.get("type", "")
            if etype == "response.output_audio_transcript.delta":
                print(evt.get("delta", ""), end="", flush=True)  # 回复文本(输出含语音时)
            elif etype == "response.output_audio_transcript.done":
                print()  # 本轮文字稿结束
            elif etype == "response.output_text.delta":
                print(evt.get("delta", ""), end="", flush=True)  # 纯文本输出时
            elif etype == "input_audio_buffer.speech_started":
                pass  # 用户打断: 停播后发送 conversation.item.truncate 截断
            elif etype == "response.output_audio.delta":
                pass  # base64 PCM16 音频片段, 可解码后播放
            elif etype == "response.done":
                print("\n[本轮完成]", evt.get("response", {}).get("usage"))
                break
            elif etype == "error":
                print("\n[错误]", evt.get("error"))
                break

asyncio.run(main())
```

```python Python (Agents SDK) theme={null}
# 依赖: pip install openai-agents
import asyncio
from agents.realtime import (
    OpenAIRealtimeWebSocketModel,
    RealtimeAgent,
    RealtimeSession,
)

agent = RealtimeAgent(
    name="Assistant",
    instructions="You are a helpful voice assistant. Keep answers concise.",
)

async def main():
    session = RealtimeSession(
        OpenAIRealtimeWebSocketModel(),
        agent,
        None,
        model_config={
            "api_key": "sk-***",  # 替换为你的 AiHubMix API 密钥
            "url": "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1",
        },
        run_config={
            "model_settings": {
                "modalities": ["audio"],
                "output_audio_format": "pcm16",
                # 必关: 默认开启的输入音频转写本接口暂不支持, 不关闭会话会被 1008 关闭
                "input_audio_transcription": None,
            },
        },
    )
    async with session:
        await session.send_message("What is the capital of France?")
        async for event in session:
            if getattr(event, "type", "") == "raw_model_event":
                data = getattr(event, "data", None)
                if getattr(data, "type", "") == "transcript_delta":
                    print(getattr(data, "delta", ""), end="", flush=True)
                elif getattr(data, "type", "") == "turn_ended":
                    print("\n[本轮完成]")
                    return

asyncio.run(main())
```

```bash 连接测试 (wscat) theme={null}
# 用 wscat 快速验证连通性与鉴权（需先 npm i -g wscat）
wscat -c "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"

# 连接成功后，粘贴一帧 session.update 配置，再发文本消息 + response.create 即可开始
```

> [!TIP]
>
> 把任意音频转成本接口要求的裸 PCM 格式，可用 ffmpeg：
>
> ```bash theme={null}
> ffmpeg -i input.mp3 -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio_pcm16_24k.raw
> ```

### 复用官方示例

OpenAI 官方发布的实时对话示例多数只依赖 SDK 的基址参数，把地址换成 AiHubMix 端点即可复用：

| 官方示例 | 只改地址能否复用 | 需要改动的地方 |
| - | - | - |
| 官方 Python SDK 实时对话示例 | 可以 | `websocket_base_url` 设为 `wss://aihubmix.com/v1`，`connect()` 传入 `model` |
| 官方 Node / JS SDK 实时对话示例 | 可以 | `baseURL` 设为 `https://aihubmix.com/v1`（SDK 会自动换成 `wss` 并拼出 `/realtime?model=...`） |
| 官方 Agents SDK 实时语音示例 | 可以 | `model_config.url` 设为 `wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1`，并关闭默认开启的输入音频转写 |
| 官方浏览器端 Demo（如实时控制台、多 Agent 语音示例） | 不可以 | 这类 Demo 由浏览器直连，会被网关的 `Origin` 校验拒绝；且依赖服务端签发临时密钥，本接口只开放服务端 WebSocket |

## 运行效果（线上实测）

以下为 OpenAI SDK 示例在 `aihubmix.com` 线上环境（模型 `gpt-realtime-2.1`）的真实运行结果。会话开启 `server_vad`，prompt 与语音均为英文。

**文本输入**

```text theme={null}
# 发送文本 "What is the capital of France?"
[轮次1·文本] 完成 2.33s | 语音输出 2.70s PCM16
  回复：The capital of France is Paris.
  usage：input_tokens 29（text 29）/ output_tokens 81（audio 54 + text 27，含 reasoning 8）
```

**音频输入**

```text theme={null}
# 分块发送 6.3s 英文语音后，模型自动断句并回复
[轮次2·音频] 完成 9.59s | 语音输出 4.35s PCM16
  回复：Everything sounds good on my side, and I'm ready to keep this conversation going.
  usage：input_tokens 117（audio 67 + text 50）/ output_tokens 129（audio 87 + text 42，含 reasoning 11）
VAD 事件：input_audio_buffer.speech_started / input_audio_buffer.speech_stopped
```

**两种配置的实测差异**

| 配置 | 实测结果 |
| - | - |
| `turn_detection: {"type": "server_vad"}` | 送完音频后收到 `input_audio_buffer.speech_started` 与 `speech_stopped`，模型自动断句并开始回复，无需手动 `commit` 与 `response.create` |
| `output_modalities: ["text"]` | 回复文本改从 `response.output_text.delta` 返回，本轮无语音输出事件，usage 中 `audio_tokens` 为 0 |

> [!NOTE]
>
> 实测确认：默认（输出含语音）时回复文本只从 `response.output_audio_transcript.delta` 逐字返回，`response.output_text.delta` 不出现；`response.done` 携带本轮 `usage`；握手耗时约 2 至 4 秒，为建会话时的额度预留开销。

## 计费说明

* **按 token 计费**：对话会话在每轮回复完成时随 `response.done` 事件返回本轮 token 用量（`usage`），据此计费。用量按**音频输入 / 音频输出 / 文本输入 / 文本输出**等组件分别计量，各组件单价以[模型详情页](https://aihubmix.com/model/gpt-realtime-2.1)实时挂牌价为准。
* **边用边结算**：这是一条长连接，费用在会话过程中随每轮回复实时扣除，无需等会话结束再一次性结算。建立会话时会先做一次约 1 分钟用量的**额度预留**（仅作准入校验，不是真实扣费），会话结束后释放剩余预留。**因此账户可用余额至少要够约 1 分钟的用量，会话才能建立。**
* 在[用量与账单](https://aihubmix.com)的消费明细里可以逐条查看每次实时对话的计费记录。

## 限制与约束

1. **单会话时长**：一条 WebSocket 连接最长 **62 分钟**，到时服务端主动关闭（关闭码 `1000`，原因 `session_duration_limit`）；需要更长请分段重连。
2. **空闲断连**：客户端与模型双方都没有活动持续 **5 分钟**时，服务端关闭会话（关闭码 `1008`，原因 `idle_timeout`）。任意一方有活动都会重置计时，模型持续输出的长回复不会因此中断。
3. **余额不足**：**建立会话时**可用余额不足以覆盖约 1 分钟的预留额度，握手直接被拒（HTTP 403），会话不会建立；**会话进行中**若余额耗尽，已建立的连接会被立即关闭。
4. **仅服务端**：不支持浏览器直连（校验 `Origin` 头），请在服务端集成。
5. **模型锁定**：模型在连接 URL 定死，会话中通过 `session.update` 改模型会被拒绝并关闭会话。
6. **格式锁定**：音频输入与输出均仅支持 `audio/pcm@24000` 单声道，其它格式会被拒绝。
7. **音色锁定**：`voice` 在首次回复开始后不可更改，请在首次请求回复前设定。
8. **暂不支持**：对话会话内的内嵌转写、通过会话项注入音频，以及文本和受支持图片以外的其它内容项类型。
9. **单轮回复**：同一会话同一时刻只允许一轮进行中的回复，上一轮未完成时再次发送 `response.create` 会被拒绝并关闭会话（关闭码 `1008`，原因 `response_already_active`）。

## 常见错误

| 场景 | 关闭码 / 状态 | 说明 |
| - | - | - |
| 服务未开启 | HTTP 403 `realtime_disabled` | 实时对话未对该环境开放 |
| 携带 `Origin` 头 / 浏览器直连 | 握手被拒 | 改用服务端连接 |
| 建连时余额不足 | HTTP 403 `insufficient_user_quota` | 余额不足以预留约 1 分钟用量，充值后重试 |
| 会话中改模型 | `1008` `model_override_forbidden` | 模型只能在连接 URL 指定 |
| 音频格式非 PCM | `1008` `audio_format_unsupported` | 转成 `audio/pcm@24000` |
| 首次回复后改音色 | `voice` 字段被忽略 | 在首次请求回复前设定 `voice` |
| 开启内嵌转写 | `1008` `input_transcription_not_supported` | 对话会话本期暂不支持 |
| 会话项注入音频 | `1008` `item_audio_not_supported` | 音频请走 `input_audio_buffer.append` |
| 不支持的图片格式 | 返回错误 | 使用 PNG/JPEG/WebP 图片 |
| 会话项使用文本及受支持图片以外的内容类型 | `1008` `unsupported_content_part` | 使用文本或受支持的图片 |
| `output_modalities` 取值非法 | `error` 事件 `invalid_value` | 仅支持 `["audio"]` 与 `["text"]`，会话不关闭 |
| 会话达到最长时长 | `1000` `session_duration_limit` | 已达 62 分钟上限，重连后继续 |
| 双方空闲 5 分钟 | `1008` `idle_timeout` | 任意一方有活动即重置计时 |
| 上一轮回复未结束时再次请求 | `1008` `response_already_active` | 等 `response.done` 后再发 `response.create` |
| 重复的 `event_id` | `1008` `duplicate_event_id` | 客户端事件的 `event_id` 需在会话内唯一 |
| `response.conversation` 设为非默认值 | `1008` `conversation_mode_not_supported` | 仅支持默认会话模式 |
| 客户端发送服务端专属事件 | `1008` `client_forged_lifecycle_event` | `response.*` 命名空间下客户端只能发 `response.create` 与 `response.cancel` |
| 会话中余额耗尽 | 连接被关闭 | 充值后重连 |

***

更新时间：2026-09-21


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.