> 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/unity-plugin-beta-overview/features/dynamic-context/sync-behavior-and-timing.md).

# 同步行为与时序

## 动态上下文更新如何传输以及何时传输

理解 Dynamic Context 的更新何时被传输，以及以何种形式被传输，可以避免最常见的集成意外。SDK 并不会把每次更新都作为单个 Append 消息发送。根据 tracker 中已经存在的状态，它可能会发送两条消息，也可能会将更新排队，直到对话开始。本页将详细说明全部四种同步场景、对话前队列，以及规范上下文格式。

{% hint style="info" %}
本页面面向正在调试集成行为、优化更新频率，或将 Dynamic Context 与外部系统集成的开发者。初学者可以先跳过，直到遇到意外的字符行为再回来查看。
{% endhint %}

## 四种同步场景

同步行为取决于被更新的状态是否已经存在于 tracker 中。这些场景适用于活动对话期间。对话前行为将在下一节中说明。

### 场景 1 — 新状态已添加

当 `SetState("Name", "Value")` 被调用，且 `"Name"` 在 tracker 中尚不存在：

```mermaid
sequenceDiagram
    participant Game
    participant Tracker
    participant Transport
    participant Character

    Game->>Tracker: SetState("Hazard Level", "High")
    Note over Tracker: 未找到 "Hazard Level" → 新状态
    Tracker->>Transport: Append — "危险等级为高"
    Transport->>Character: context-update { mode: "append", text: "危险等级为高" }
```

会发送一条 Append 消息。角色接收到新事实，并将其纳入自身感知。

### 场景 2 — 已有状态发生变化

当 `SetState("Name", "NewValue")` 被调用，且 `"Name"` 已经存在，但其值 **不同** ：

```mermaid
sequenceDiagram
    participant Game
    participant Tracker
    participant Transport
    participant Character

    Game->>Tracker: SetState("Hazard Level", "Extreme")
    Note over Tracker: "Hazard Level" 已存在且值为 "High" → 值已更改
    Tracker->>Transport: Replace — 规范上下文全文（含新值）
    Transport->>Character: context-update { mode: "replace", text: "危险等级为极高\n..." }
    Tracker->>Transport: Append — "危险等级从高变为极高"
    Transport->>Character: context-update { mode: "append", text: "危险等级从高变为极高" }
```

**会发送两条消息：** 先发送一个携带完整规范上下文的 Replace（其中已更新的值就地替换），再发送一个携带人类可读差异说明的 Append。Replace 为角色提供权威、完整的视图；Append 则用清晰可读的方式说明具体发生了什么变化，从而让它在对话中能够自然地提及这一过渡。

{% hint style="warning" %}
这种双消息模式是有意设计且不可配置的。如果你在调试时监控网络流量，遇到已有状态被修改时，请预期会有两条 `上下文更新` 消息。
{% endhint %}

### 场景 3 — 状态被移除

当 `RemoveState("Name")` 被调用：

```mermaid
sequenceDiagram
    participant Game
    participant Tracker
    participant Transport
    participant Character

    Game->>Tracker: RemoveState("Hazard Level")
    Note over Tracker: 移除 "Hazard Level" → 重建规范上下文
    Tracker->>Transport: Replace — 不含 "Hazard Level" 的规范上下文
    Transport->>Character: context-update { mode: "replace", text: "当前站点为控制室 B\n..." }
```

会发送一条 Replace 消息，携带完整的规范上下文，但已移除的状态会被排除在外。不会有差异 Append——该状态的缺失从 Replace 载荷中即可一目了然。

### 场景 4 — 批量 SetStates，且包含新旧混合状态

当 `SetStates(dict)` 被调用，且字典中至少包含一个已经存在于 tracker 中的状态（无论其值为何）：

```mermaid
sequenceDiagram
    participant Game
    participant Tracker
    participant Transport
    participant Character

    Game->>Tracker: SetStates({"Phase": "Emergency", "Alert": "Red", "Time": "4 min"})
    Note over Tracker: "Phase" 已存在 → 混合批处理
    Tracker->>Transport: Replace — 全部状态更新后的规范上下文
    Transport->>Character: context-update { mode: "replace", text: "阶段为紧急\n警报为红色\n时间为 4 分钟\n..." }
    Tracker->>Transport: Append — 所有变更行合并
    Transport->>Character: context-update { mode: "append", text: "阶段从检查变为紧急\n警报为红色\n时间为 4 分钟" }
```

Replace + Append，其结构与场景 2 相同，但 Append 会将批处理中的所有变更汇总到一条消息里。如果字典中的所有状态都是新的（在 tracker 中都尚不存在），则只会发送一条 Append。

## 对话前队列

所有受跟踪的方法—— `SetState`, `SetStates`, `AddEvent`, `RemoveState`，以及 `Reset` ——在角色不处于活动对话时，会自动将其效果排队。对话开始时，队列会被刷新。

### 队列机制

每当在对话开始前对受跟踪的状态或事件进行更新时，SDK 都会记录一个待同步项。对话开始时，会发送一条 Replace 消息，其中包含当时完整的规范上下文——而不是一系列单独的更新。

当 `Reset()` 如果在对话开始前被调用，则会记录一个待重置项，并覆盖任何待同步项。对话开始时，角色接收到的是 Reset 消息，而不是 Replace。

**优先级规则：**

* 调用 `Reset()` 会取消任何待同步项。随后 `SetState()` 在 `Reset()` 会将队列恢复为待同步状态。
* 只会发送在对话开始时 tracker 的最终状态。多次 `SetState` 调用在对话开始前只会产生一条包含最终值的 Replace，而不会是一系列递增消息。

**`Apply()` 不会排队。** 如果 `Apply()` 如果在对话开始前被调用，更新会被静默丢弃。如果你需要让对话开始前的设置保留到会话启动，请使用受跟踪的方法。

## 规范上下文格式

规范上下文字符串是在某一时刻对所有受跟踪状态和事件的完整、权威的文本表示。它用于 Replace 消息以及对话前的刷新。

**格式：**

```
{State1Name} 是 {State1Value}
{State2Name} 是 {State2Value}
...
{Event1 text}
{Event2 text}
...
```

**规则：**

* 状态最先出现，按 **插入顺序** 排列——也就是每个状态名称首次引入的顺序。就地更新已有状态会保持其位置不变。
* 事件在所有状态之后出现，按 **时间顺序** 排列——也就是 `AddEvent` 被调用的顺序。
* 每个状态都按 `"{Name} 是 {Value}"`的形式格式化，每行一个。
* 每个事件都是单独一行，没有前缀。

**示例：**

在以下调用序列之后：

```csharp
context.SetState("Station", "Fire Suppression Bay");
context.SetState("Hazard Level", "Extreme");
context.AddEvent("学员绕过了手动锁定");
context.SetState("Hazard Level", "High");  // 已有状态 — 值发生变化
context.AddEvent("学员激活了正确的抑制器");
```

规范上下文是：

```
Station 是 Fire Suppression Bay
Hazard Level 是 High
学员绕过了手动锁定
学员激活了正确的抑制器
```

`"Station"` 即使 `"Hazard Level"` 在其之后被更新，其插入位置仍保持不变。事件则会按添加顺序保留。

## Apply() 与 Tracker 边界

`Apply(ConvaiDynamicContextUpdate update)` 是唯一一个完全绕过 tracker 的方法。它会将 update 中的内容直接发送到 transport，而不会重建规范上下文，也不会记录本地状态。

后果：

* `TryGetStateValue` 不会被 `Apply()` 调用所更新。如果你发送 `Apply(new ConvaiDynamicContextUpdate("Score is 95", ConvaiContextUpdateMode.Append))`，查询 `TryGetStateValue("Score", out _)` 返回 `false`.
* 随后由 `SetState` 调用触发的规范同步不会包含任何通过 `Apply()`.
* `Apply()` 发送的内容。如果角色不在对话中，它是一个静默的无操作——不会排队。

使用 `Apply()` 仅在你确实需要绕过 tracker 时才使用，例如从外部评分系统发送后端格式化的文本块，或强制进行一种与受跟踪状态相互独立的模式-`Reset` 更新。

## 传输层参考

Dynamic Context 更新通过 WebRTC 数据通道以 RTVI 消息的形式传输。

<table><thead><tr><th width="149.99993896484375">JSON 字段</th><th width="263">值</th><th>说明</th></tr></thead><tbody><tr><td><code>type</code></td><td><code>"context-update"</code></td><td>固定的消息类型标识符。</td></tr><tr><td><code>data.text</code></td><td>任意字符串，或省略</td><td>上下文文本载荷。当 mode 为 <code>重置</code>.</td></tr><tr><td><code>data.mode</code></td><td><code>"append"</code>, <code>"replace"</code>, <code>"reset"</code></td><td>对应 <code>ConvaiContextUpdateMode</code>.</td></tr><tr><td><code>data.run_llm</code></td><td><code>"auto"</code>, <code>"true"</code>, <code>"false"</code></td><td>对应 <code>ConvaiContextReactionMode</code>. <code>Auto</code> → <code>"auto"</code>, <code>立即响应</code> → <code>"true"</code>, <code>仅同步</code> → <code>"false"</code>.</td></tr></tbody></table>

此信息提供给正在检查网络流量或构建自定义传输集成的开发者。在正常使用 SDK 时无需执行任何操作——从类型化 C# 值到 JSON 的映射由 SDK 自动处理。

## 接下来做什么

* [故障排除与诊断](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/dynamic-context/troubleshooting-and-diagnostics.md) ——用于诊断当更新未产生预期角色响应时的异常行为。

## 结论

SDK 的同步逻辑——新状态使用 Append，已更改状态使用 Replace 然后 Append，移除状态使用 Replace——确保角色始终接收到对世界的权威视图。理解这些模式可以让集成行为变得可预测，调试也更直接。如果你遇到意外行为，请参阅 [故障排除与诊断](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/dynamic-context/troubleshooting-and-diagnostics.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:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/dynamic-context/sync-behavior-and-timing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
