> 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": "Move To", "target": "cube" }] }
}
```

按如下方式解析实际事件类型：

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

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

***

### 机器人输出流

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

| 消息                | 负载         | 含义                              |
| ----------------- | ---------- | ------------------------------- |
| `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** 并且还会重复 `label` 在 `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`       | 字符串 | 此机器人响应的标识符，在整个轮次中保持稳定            |
| `neurosync_turn_id` | 整数  | NeuroSync 轮次标识，用于关联 blendshape 流 |
| `epoch`             | 整数  | NeuroSync 连接/会话纪元                |
| `sequence`          | 整数  | 每轮消息序号                           |

使用 `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":"happy","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}
```

`最终：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 视为重复项。即使共享 `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 }
```

| 字段                | 类型  | Presence       | 说明                                    |
| ----------------- | --- | -------------- | ------------------------------------- |
| `was_interrupted` | 布尔值 | 始终可用           | `是` 如果用户打断了机器人                        |
| `was_aborted`     | 布尔值 | 仅当 `是`         | 由于所需输出无法送达，本轮结束                       |
| `error_reason`    | 字符串 | 仅在中止时 **和** 设置 | 机器可读的中止原因；目前为 `audio_delivery_failed` |

`bot-turn-completed` 并不是客户端播放确认。它不表示用户已经听完音频，也不表示 avatar blendshapes 或客户端工具已经完成。驱动本地音频播放的客户端、 `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` 是 `是`时处理，然后返回一个终态的 `"已取消"` 结果给受影响的 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`。缺失的语义动作 `目标` 意味着该动作没有目标。v2 工具调用在 `arguments`中携带已验证的输入；其可选的 `目标` 字段不是授权决定。

对于 `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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
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=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
