> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/turn-lifecycle-and-message-ordering.md).

# 轮次生命周期与消息顺序

了解 Live API 轮次传递，包括旧版文本、规范输出、动作结果续接、关联和顺序保证。

一个 Live API 会话会通过并行的媒体和数据载体交付一个逻辑轮次。请将消息顺序、逻辑轮次关联以及客户端工具完成视为彼此独立的问题。

| 载体              | 承载                       | 格式                                              |
| --------------- | ------------------------ | ----------------------------------------------- |
| **WebRTC 音频轨道** | 实际的语音音频                  | 标准 WebRTC 媒体轨道（或 `audio-data` 如果你启用数据通道路由，则为消息） |
| **机器人输出流**      | 机器人的响应文本和语音状态转换          | 数据通道，事件类型位于 **顶层**                              |
| **自定义服务器消息**    | 规范化模型输出、动作、情绪、转写、动画和生命周期 | 数据通道，事件类型嵌套在 `data.type`                        |

只有在你启用以下选项时，音频才会出现在数据通道流中： `audio_routing: "data_only"` 或 `"both"` 在 `audio_config`。请参阅 [通过数据通道传输音频数据](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/audio-data-via-data-channel.md).

***

### 两种封装形式

数据通道承载两种不同的封装结构。你的消息处理程序必须同时检查这两种情况。

**形式 A — 机器人输出流。** 事件类型位于顶层 `type`:

```json
{"label":"rtvi-ai","type":"bot-llm-text","data":{"text":"当然，马上就来。"}}
```

**形式 B — 自定义服务器消息。** 顶层 `type` 始终是 `"server-message"`，而真正的事件类型嵌套在其中：

```json
{
  "label": "rtvi-ai",
  "type": "server-message",
  "data": { "type": "action-response", "actions": [{ "name": "移至", "target": "cube" }] }
}
```

像这样解析实际事件类型：

```javascript
function eventType(message) {
  return message.type === "server-message" && message.data?.type
    ? message.data.type
    : message.type;
}
```

`server-response` 是第三种旧版结构。其字段位于顶层，而不是位于一个 `server-message` 封装之下。参见 [server-response](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/server-to-client-messages.md#server-response).

***

### 机器人输出流

这些消息使用 **形式 A**。它们是机器人响应文本出现的唯一位置。

| Message           | 载荷         | 含义                              |
| ----------------- | ---------- | ------------------------------- |
| `bot-llm-started` | `{}`       | 模型已开始生成本轮内容                     |
| `bot-llm-text`    | `{ text }` | 所选旧版或原始文本投影的增量片段。按到达顺序拼接即可重建该投影 |
| `bot-llm-stopped` | `{}`       | 生成完成                            |
| `bot-tts-started` | `{}`       | 本轮的语音合成已开始                      |

在省略能力或 `bot_llm_text_mode: "legacy"`, `bot-llm-text` 会携带经过过滤的对话文本。在 `bot_llm_text_mode: "raw"`时，它携带的是在结构化输出解析和对话过滤之前、提供方可见的文本。原始输出不被视为可信的可渲染或可执行内容。参见 [响应契约与解析](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/response-contract-and-parsing.md).

#### 语音状态消息

`bot-started-speaking` 和 `bot-stopped-speaking` 标记本轮音频的边界。这些消息使用 **形式 B** 并且还会重复 `标签` 在……内部 `data`:

```json
{
  "label": "rtvi-ai",
  "type": "server-message",
  "data": {
    "label": "rtvi-ai",
    "type": "bot-started-speaking",
    "response_id": "session-id:r4",
    "epoch": 1,
    "sequence": 3
  }
}
```

***

### 响应生命周期元数据

三条消息 — `bot-started-speaking`, `bot-stopped-speaking`，以及 `bot-turn-completed` — 可能携带可选的关联字段。每个字段仅在 **设置后**才会包含，因此不同轮次的键集合会有所不同。

| 字段                  | 类型      | 说明                                |
| ------------------- | ------- | --------------------------------- |
| `response_id`       | string  | 此机器人响应的标识符，在整个轮次中保持稳定             |
| `neurosync_turn_id` | integer | NeuroSync 轮次标识符，用于关联 blendshape 流 |
| `epoch`             | integer | NeuroSync 连接/会话周期                 |
| `sequence`          | integer | 每轮消息序号                            |

使用 `response_id` 用于将 blendshape 和取消消息与产生它们的轮次关联起来。不要假设这些字段一定存在。

协商使用 model output v2 的客户端会收到一个或多个 `model-output` 封装。使用 `output_id` 来对封装去重。使用可选的 `logical_turn_id` 将属于同一逻辑轮次的文本、语义动作和客户端工具调用封装分组。不同封装可以共享一个 `logical_turn_id`.

***

### 一个完整的旧版轮次

用户说 *“去拿那个立方体”*。一个具有代表性的 v1 消息序列如下：

```json
{"label":"rtvi-ai","type":"server-message","data":{"type":"vad-stt-started",
  "timestamp":"2026-08-10T10:30:45.123Z","pre_roll_ms":1500}}

{"label":"rtvi-ai","type":"server-message","data":{"type":"final-user-transcription",
  "text":"去拿那个立方体","speaker_name":"Alice"}}

{"label":"rtvi-ai","type":"bot-llm-started","data":{}}
{"label":"rtvi-ai","type":"bot-llm-text","data":{"text":"当然,"}}
{"label":"rtvi-ai","type":"bot-llm-text","data":{"text":"我这就去。"}}
{"label":"rtvi-ai","type":"bot-llm-stopped","data":{}}

{"label":"rtvi-ai","type":"server-message","data":{"type":"action-response",
  "actions":[{"name":"移至","target":"cube"},{"name":"拾起","target":"cube"}]}}
{"label":"rtvi-ai","type":"server-message","data":{"type":"bot-emotion",
  "emotion":"开心","scale":2}}

{"label":"rtvi-ai","type":"bot-tts-started","data":{}}
{"label":"rtvi-ai","type":"server-message","data":{"type":"bot-started-speaking",
  "label":"rtvi-ai","response_id":"session-id:r4"}}
{"label":"rtvi-ai","type":"server-message","data":{"type":"visemes","visemes":{ }}}
{"label":"rtvi-ai","type":"server-message","data":{"type":"bot-stopped-speaking",
  "label":"rtvi-ai","response_id":"session-id:r4"}}
{"label":"rtvi-ai","type":"server-message","data":{"type":"bot-turn-completed",
  "was_interrupted":false}}
```

本轮的语音音频会在 WebRTC 音频轨道上播放，并与上面的消息并行进行。

对于协商使用 model output v2 的客户端，Convai 还可以发出共享同一个逻辑轮次 ID 的独立规范化封装：

```json
{"type":"model-output","version":2,"output_id":"out_text","logical_turn_id":"turn_42",
  "format":"text","raw":"当然，马上就来。","items":[{"type":"message","role":"assistant",
  "channel":"final","content":"当然，马上就来。"}],"final":true}

{"type":"model-output","version":2,"output_id":"out_action","logical_turn_id":"turn_42",
  "format":"semantic-actions-json","raw":"{\"actions\":[{\"name\":\"移至\",\"target\":\"cube\"}]}",
  "items":[{"type":"semantic_action","id":"act_42","name":"移至","target":"cube"}],"final":true}
```

`final: true` 完成一个封装，但不会结束整个逻辑轮次。

***

### 顺序保证

正确处理这一点可以避免大量集成错误。

#### 哪些是有保证的

* **`bot-llm-text` 片段按顺序到达。** 按到达顺序将它们拼接即可重现所选文本投影。
* **轮次边界是有序的。** `bot-llm-started` 先于任何 `bot-llm-text`，其又先于 `bot-llm-stopped`. `bot-started-speaking` 先于 `bot-stopped-speaking`，其又先于 `bot-turn-completed`.
* **数组和条目顺序会被保留。** 这并不要求客户端顺序执行。
* **`output_id` 标识一个规范化封装。** 重复的 ID 视为重复项。即使不同的 ID 共享 `logical_turn_id`.
* **客户端工具调用会等待其关联的结果或超时。** Convai 会在该工具继续执行之前，将超时前已接受的结果提供给同一模型上下文。
* **`bot-turn-completed` 是终态** ，表示关联的服务器响应生命周期已结束，但这并不能证明客户端动作或播放已完成。

#### 什么是 *包含* 有保证的

{% hint style="danger" %}
**`action-response` 和 `bot-emotion` 与响应文本没有位置上的关系。** 它们是独立的消息，没有索引、时间戳或偏移量将它们绑定到任何 `bot-llm-text` 片段。
{% endhint %}

具体来说，这意味着：

* 你 **不能** 判断某个动作原本是要发生在“第二句之后”。
* 你 **不能** 判断哪些词受到某个 `bot-emotion` 的影响。情绪是 **轮次级别的**，而不是跨度级别的。
* 旧版 v1 无法表示如下交错序列： *先说，再移动，再说一次*。规范化 v2 可以将多个已完成的封装分组，但它不提供词级动作偏移。
* `action-response` 通常会在生成接近结束时到达，但它相对于 `bot-llm-stopped` 是 **并非约定保证**。不要把动作执行的条件设为必须先看到 `bot-llm-stopped`.

**建议的处理方式：** v1 客户端可以将 `action-response` 视为建议的动作计划。V2 客户端应使用 `model-output.items` 作为规范来源并忽略重复投影。在两种模式下，都应在你的应用中对操作进行授权和调度。返回一个 [`action-result`](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/client-to-server-messages.md#action-result) ，仅在客户端操作达到终态后。

***

### 轮次完成

`bot-turn-completed` 表示一种 **服务器端** 终态：服务器已完成本轮所需全部输出的交付，或者该轮次被中断或中止。

```json
{ "type": "bot-turn-completed", "was_interrupted": false }
```

| 字段                | 类型     | 存在情况           | 说明                                    |
| ----------------- | ------ | -------------- | ------------------------------------- |
| `was_interrupted` | 布尔值    | 始终             | `true` 如果用户打断了机器人                     |
| `was_aborted`     | 布尔值    | 仅当 `true`      | 本轮因无法交付所需输出而结束                        |
| `error_reason`    | string | 仅在中止时 **和** 设置 | 机器可读的中止原因；当前为 `audio_delivery_failed` |

`bot-turn-completed` 并不表示客户端播放已确认。它不意味着用户已听完音频，也不意味着 avatar blendshape 或客户端工具已完成。驱动本地音频播放、 `isSpeaking` 状态、口型同步或 avatar 动画的客户端，应在清除这些状态之前先清空自己的队列。

#### 中断

当用户插话时，当前轮次会以 `was_interrupted: true`结束。选择接收提前下发 NeuroSync 片段的客户端还会收到 [`neurosync-blendshapes-cancel`](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/server-to-client-messages.md#neurosync-blendshapes-cancel)，其中指定要保留多少已缓冲的视觉尾部。

已交付的动作和工具调用在中断时不会被撤回。如果你的体验需要取消正在进行中的客户端工作，请在应用中处理这一点，当 `was_interrupted` 是 `true`，然后返回一个终态 `"cancelled"` 结果给受影响的 v2 工具调用。

***

### 字段存在规则

字段是否存在 **并不统一** ，在不同消息类型之间各不相同。目前有三种不同约定；在编写假定某个键存在的客户端之前，请先检查此表。

| 约定                | 行为                            | 适用于                                                                                                                                                                                                                                                     |
| ----------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **始终存在**          | 即使值为 `null`                   | `moderation-response.reason`；每条消息上的所有必需字段                                                                                                                                                                                                               |
| **为空时省略**         | 该键 **缺失** 于 JSON 中，而不是 `null` | `server-response.message`, `server-response.extras`, `final-user-transcription.speaker_id` / `speaker_name` / `participant_id` / `message_id`, `user-idle-warning.message`, `llm-no-response.reason`, `bot-turn-completed.was_aborted` / `error_reason` |
| **嵌套值为 null 时省略** | 嵌套对象上的可选键会被移除                 | `action-response.actions[].target`                                                                                                                                                                                                                      |
| **仅在设置时包含**       | 关联元数据，随轮次而变化                  | `response_id`, `neurosync_turn_id`, `epoch`, `sequence`                                                                                                                                                                                                 |

请采取防御性编写。使用可选访问（`message.data?.target`）而不是只检查 `null`。缺失的 semantic-action `target` 意味着该动作没有目标。v2 工具调用会在 `arguments`；其可选的 `target` 字段并不是授权决定。

以下是 `final-user-transcription` 的最小真实载荷示例：

```json
{ "type": "final-user-transcription", "text": "你好" }
```

***

### 相关页面

* [响应契约与解析](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/response-contract-and-parsing.md) — 口语回复如何与动作及其他输出分离
* [服务器到客户端的消息](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/server-to-client-messages.md) — 每条服务器消息的完整字段参考
* [客户端到服务器的消息](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/client-to-server-messages.md) — 你发送的消息
* [消息术语表](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/message-glossary.md) — 所有消息类型的概要
* [连接 API](/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/connect-api.md) — 建立会话


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/api-can-kao/core-api-reference/live-apis-beta/turn-lifecycle-and-message-ordering.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
