> 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/cha-jian-yu-ji-cheng/convai-unity-sdk/core-concepts/runtime-architecture.md).

# 运行时架构

Convai Unity SDK 分层构建。每一层都有明确的职责，并向内通信——外层依赖内层，绝不相反。理解这种结构能让你知道 SDK 的哪些部分面向开发者、哪些可以替换，以及哪些是你无需触碰的内部实现细节。

***

### 系统层

下图展示了主要层级及其相互关系。 `ConvaiRuntime` 在第二层持有四个直接子系统—— `IRoomRuntime`, `IEventHub`, `IAgentRegistry`，以及模块列表。角色和玩家显示在 `IRoomRuntime` 和 `IAgentRegistry`之下。运行时级功能模块共享一个公共模块上下文；携带具身模块的角色还会获得自己专属的按角色组成根。

```mermaid
graph TD
    A[ConvaiRuntime<br/>IConvaiRuntime] --> B[IRoomRuntime<br/>连接 · 音频 · 所有权 · 诊断]
    A --> C[IEventHub<br/>解耦的发布/订阅]
    A --> D[IAgentRegistry<br/>角色 · 玩家]
    A --> E[IReadOnlyList&lt;IConvaiModule&gt;<br/>功能模块]
    B --> F[ConvaiCharacter<br/>按角色会话 + 状态]
    B --> G[ConvaiPlayer<br/>本地参与者身份]
    F --> H[模块上下文<br/>口型同步 · 视觉 · 叙事]
    G --> H
    F --> I[EmbodimentContext<br/>按角色组成根]
    I --> J[具身模块<br/>注视 · 身体动画 · 肢体语言 · 对话流程 · 情绪]
```

**`ConvaiRuntime` (顶层)** ——拥有所有子系统并协调完整生命周期。通过以下方式在应用生命周期内仅创建一次： `ConvaiRuntimeBuilder`。公开启动、暂停、恢复和停止操作，并将其传播到所有已注册模块。它持有四个直接子系统：

* **`IRoomRuntime`** ——管理实时会话：连接、断开、音频路由、角色所有权和诊断。角色和本地玩家在此层之下显示。
* **`IEventHub`** ——用于整个 SDK 中跨系统通信的解耦发布/订阅总线。
* **`IAgentRegistry`** ——所有处于活动状态的 `ConvaiCharacter` 和 `ConvaiPlayer` 实例的注册表。
* **`IReadOnlyList<IConvaiModule>`** ——已注册功能模块的集合。

**角色 / 玩家层** — `ConvaiCharacter` 和 `ConvaiPlayer` 注册到 `IAgentRegistry` 并通过 `IRoomRuntime`接收其按会话上下文。每个角色维护自己的会话状态。携带具身模块——注视、身体动画、肢体语言、对话流程或情绪——的角色还会获得自己的 `EmbodimentContext`；见下方具身层。

**模块上下文层** ——运行时级功能模块（`IConvaiModule`，例如 LipSync、Vision 和 Narrative）共享一个 `IModuleContext` ，该上下文提供对运行时服务的访问。模块可以声明彼此模块 ID 和服务之间的依赖，但通过 `IEventHub` 或注册在 `IModuleContext` 上的服务进行交互，而不是持有彼此具体类型的直接引用。

***

### 运行时接口清单

`IConvaiRuntime` 公开以下子系统作为属性。每个属性都是特定功能领域的入口点。

| 属性        | 类型                              | 它负责什么         |
| --------- | ------------------------------- | ------------- |
| `State`   | `运行时状态`                         | 运行时当前生命周期状态   |
| `房间`      | `IRoomRuntime`                  | 连接、音频、所有权和诊断  |
| `事件`      | `IEventHub`                     | 解耦的发布/订阅通信    |
| `智能体`     | `IAgentRegistry`                | 所有活动角色和玩家的注册表 |
| `模块`      | `IReadOnlyList<IConvaiModule>`  | 所有已注册的功能模块    |
| `传输`      | `ITransportProvider`            | 平台特定的实时传输     |
| `对话`      | `IConversationProvider`         | 与 Convai 的通信  |
| `配置`      | `ConvaiBootstrapConfigSnapshot` | 不可变的启动配置      |
| `运行时偏好设置` | `IRuntimePreferences`           | 可变的运行时偏好设置    |
| `功能变体`    | `IFeatureVariantProvider`       | 功能变体 / A-B 选择 |
| `持久化`     | `IPersistenceProvider`          | 运行时拥有的数据存储    |
| `遥测`      | `ITelemetryProvider`            | 可观测性和分析       |

开发者最常与 `房间`, `智能体`，以及 `事件` 交互。 `传输`, `对话`, `持久化`, `遥测`，以及 `功能变体` 可通过 `ConvaiRuntimeBuilder`.

***

### 你可以替换的内容

`ConvaiRuntimeBuilder` 是运行时在启动前用于组合的流畅 API。每个方法都会返回 `this`，因此可以链式调用。

```csharp
var runtime = new ConvaiRuntimeBuilder()
    .UsePersistence(myPersistenceProvider)
    .UseTelemetry(myTelemetryProvider)
    .WithEndUserIdentityProvider(myIdentityProvider)
    .AddModule<MyCustomModule>()
    .Build();
```

下表列出了哪些可替换、哪些仅限内部使用。

| 组件       | 可通过 Builder 替换                   | 默认值                        |
| -------- | -------------------------------- | -------------------------- |
| 传输提供程序   | `UseTransport()`                 | 平台默认值（WebSocket / LiveKit） |
| 会话提供程序   | `UseConversation()`              | Convai RTVI 会话提供程序         |
| 持久化提供程序  | `UsePersistence()`               | `PlayerPrefs`支持的键值存储       |
| 遥测提供程序   | `UseTelemetry()`                 | 无操作遥测                      |
| 功能变体提供程序 | `WithFeatureVariants()`          | 静态功能标志                     |
| 运行时偏好设置  | `WithRuntimePreferences()`       | 默认值来自 `ConvaiSettings`     |
| 事件总线     | `UseEventHub()`                  | 默认的内存内事件总线                 |
| 代理注册表    | `UseAgentRegistry()`             | 默认注册表                      |
| 最终用户身份   | `WithEndUserIdentityProvider()`  | 设备 ID 提供程序                 |
| 终端用户元数据  | `WithEndUserMetadataProvider()`  | 无                          |
| 模块       | `AddModule()` / `AddModule<T>()` | 仅限 SDK 功能模块                |
| 房间运行时    | `UseRoomRuntime()`               | 内部的、由 LiveKit 支持的房间        |

{% hint style="info" %}
`ConvaiRuntime` 由 `ConvaiManager` MonoBehaviour 自动创建。大多数项目从不直接调用 `ConvaiRuntimeBuilder` 。仅当你需要替换默认提供程序或添加自定义模块时才使用它。
{% endhint %}

***

### `IRoomRuntime` 子结构

房间层本身由四个协调器组成，都可通过 `IConvaiRuntime.Room`.

| 属性    | 类型                           | 职责                   |
| ----- | ---------------------------- | -------------------- |
| `连接`  | `IRoomConnectionCoordinator` | 连接、断开、会话状态           |
| `音频`  | `IRoomAudioCoordinator`      | 麦克风采集、远程音频播放         |
| `所有权` | `IRoomOwnershipCoordinator`  | 此客户端拥有哪些角色以及焦点在哪些角色上 |
| `诊断`  | `IRoomDiagnostics`           | 会话指标、健康监控            |

在脚本中，你最可能调用的是连接和音频协调器。当场景中有多个角色时，所有权会自动管理。诊断用于性能监控和调试。

***

### 模块层

模块是在运行时生命周期内运行的功能扩展。它们接收一个共享的 `IModuleContext` ，并且可以注册其他模块或呈现层使用的服务。

在 `Build()` 调用之前，通过构建器添加模块：

```csharp
new ConvaiRuntimeBuilder()
    .AddModule<LipSyncModule>()
    .AddModule(new MyCustomModule(someConfig))
    .Build();
```

模块会随着运行时一起启动、暂停、恢复和停止。 `IConvaiModule` 接口定义了这些生命周期钩子。使用 `RequiredModules`声明依赖，运行时会按依赖顺序启动模块，并按相反顺序停止——但模块仍通过 `IEventHub` 或 `IModuleContext.TryGetModuleService`访问另一个模块的数据，而绝不会直接引用另一个模块的具体类型。请参阅 [扩展 SDK](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/advanced-topics/extending-the-sdk.md) 以获取完整的模块编写参考。

***

### 具身层

某个 `ConvaiCharacter` 携带具身模块——注视、身体动画、肢体语言、对话流程或情绪——的角色都会获得自己的 `EmbodimentContext`。Convai 在某个具身模块首次在该角色上解析到它时，会自动添加此组件；你永远不会从“添加组件”菜单中手动添加它，而且它自身也没有菜单项。

`EmbodimentContext` 是按角色的组成根，而不是注册到 `ConvaiRuntimeBuilder`的功能模块。它公开了所有具身模块接入的共享基础设施：

| 属性              | 类型                    | 它负责什么                        |
| --------------- | --------------------- | ---------------------------- |
| `CharacterRoot` | `Transform`           | 角色的根变换                       |
| `EventHub`      | `IEventHub`           | 由 `IConvaiRuntime.Events`    |
| `日志记录器`         | `ILogger`             | 供具身模块使用的结构化诊断                |
| `RigBinding`    | `IStandardRigBinding` | 用于检测到的骨架的语义骨骼和 BlendShape 查找 |
| `Character`     | `ConvaiCharacter`     | 所属角色                         |

具身模块—— `ConvaiGazeController`, `ConvaiBodyAnimationController`, `ConvaiBodyLanguageController`, `ConvaiConversationFlowController`，以及 `ConvaiEmotionController` ——在 `OnEnable` 上解析上下文，并向其发布自己的契约，而不是彼此持有直接引用。 `EmbodimentContext` 触发 `RigBindingChanged`, `EmbodimentConfigurationChanged`，以及 `DependenciesPopulated` ，以便模块在骨架重建、预设切换或运行时依赖首次可用时作出响应。项目自身的组件可以通过 `RegisterTickable`/`UnregisterTickable`.

#### 确定性 Tick

具身模块不运行于 Unity 的按组件 `Update()` 顺序。每个模块都向上下文的 Tick 调度器注册，该调度器会在每一帧中按声明的 `认知 → 表达 → 完成` 顺序精确地对每个已注册模块 Tick 一次——同一阶段内的先后由注册顺序决定，而不是由层级位置或哪个模块先启用决定。调度器本身从 `Update`开始 Tick；消费其输出的动画器指挥者和面部合成器随后从 `LateUpdate`进行，前提是 Animator 已为该帧摆好骨架姿态。

#### 单写入 Animator 和面部 BlendShape

两个基础设施组件通过强制单写入访问，确保两个具身模块永远不会争夺同一输出：

* 该 **动画器指挥者** 是唯一调用 `Animator.SetFloat` 及相关方法来处理由具身驱动参数的组件。模块通过它提交命名参数写入，而不是直接触碰 `Animator` ；当第二个模块注册一个已经被占用的参数时，会被拒绝，而不是静默覆盖第一个。
* 该 **面部 BlendShape 合成器** 是唯一向 `SkinnedMeshRenderer` 为具身输出写入 BlendShape 的组件。模块在帧内提交各区域的层权重，而合成器只会在 `LateUpdate`.

这两个组件都会由 `EmbodimentContext` Convai 与其一同自动提供——将它们以及 `EmbodimentContext` 它本身视为 Convai 为角色添加的基础设施，而不是你直接配置的内容。

***

### `运行时状态` 生命周期

运行时会从创建到释放经历以下状态。

| State      | 含义                           |
| ---------- | ---------------------------- |
| `已创建`      | 已构建运行时，但尚未启动                 |
| `Starting` | `StartAsync()` 进行中           |
| `运行中`      | 完全运行                         |
| `暂停中`      | `PauseAsync()` 进行中           |
| `已暂停`      | 已暂停；可恢复                      |
| `恢复中`      | `ResumeAsync()` 进行中          |
| `停止中`      | `StopAsync()` 进行中            |
| `Stopped`  | 已关闭；无法重启                     |
| `已释放`      | `DisposeAsync()` 已调用；所有资源已释放 |

```mermaid
stateDiagram-v2
    [*] --> 已创建
    已创建 --> 启动中 : StartAsync()
    启动中 --> 运行中
    运行中 --> 暂停中 : PauseAsync()
    暂停中 --> 已暂停
    已暂停 --> 恢复中 : ResumeAsync()
    恢复中 --> 运行中
    运行中 --> 停止中 : StopAsync()
    停止中 --> [*] : DisposeAsync()
    已停止 --> [*] : DisposeAsync()
```

所有状态转换都是包装在 `IConvaiOperation<Unit>`失败。调用前请检查 `Status` 中的异步操作，并在继续之前在返回的操作上进行确认，以确保转换已完成。

{% hint style="info" %}
`ConvaiManager` 处理 `DisposeAsync()` 会在销毁时自动处理。如果你使用 `ConvaiRuntimeBuilder` 直接构建自定义宿主，请调用 `DisposeAsync()` 在……之后 `StopAsync()` 以释放所有资源。
{% endhint %}

***

### 下一步

现在你已经了解 Convai 运行时如何组成，以及每一层中哪些部分可以替换。接下来阅读“会话生命周期”，了解每个角色的会话如何创建、持久化和恢复，然后继续阅读“轮替模式”和“事件系统”。

{% content-ref url="/pages/1b034b7638b343540a3b205274bd08beaff9d15e" %}
[会话生命周期](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/core-concepts/session-lifecycle.md)
{% endcontent-ref %}

{% content-ref url="/pages/251e0bf7030a5f742a1182e15529c0604e3ee150" %}
[轮替模式](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/core-concepts/turn-taking-modes.md)
{% endcontent-ref %}


---

# 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/cha-jian-yu-ji-cheng/convai-unity-sdk/core-concepts/runtime-architecture.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.
