> 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/features/dynamic-context/how-dynamic-context-works.md).

# 动态上下文如何工作

动态上下文为角色提供了对场景中正在发生事情的实时、结构化视图。角色不再只依赖在 Convai 仪表板上配置的静态系统提示，而是可以引用学员当前位置、已收集的设备，或最近触发的警报——因为这些信息会在发生时直接注入到会话中。本页解释其底层模型：SDK 跟踪的两个原语、它们如何组装成规范化上下文字符串、更新如何批量处理并刷新，以及 SDK 如何回传每次更新的结果。

### 状态与事件

动态上下文建立在两种原始类型之上。

**状态** 是持久的、带名称的键值对。每个状态都有名称和值。设置某个状态时，该名称之前的值会被替换。状态适合用于会随时间变化、但在任一时刻只有一个当前值的事实：操作员当前所在工位、某个区域的危险等级，或清单项是否已完成。

**事件** 是按时间顺序发生的一次性事件。与状态不同，事件会按顺序累积，而不是被替换。每次调用 `AddEvent` 都会向角色的上下文追加一行，但有一个例外：如果完全相同的事件文本已经在当前待处理批次中暂存，那么重复调用会被丢弃，不会再添加第二行。这个去重窗口会在批次刷新后关闭——同样的文本可以在后续批次中再次添加。事件适合表示会话中发生过、且角色应该能够按顺序引用的事情："学员绕过了手动锁定流程"、"7 号工位触发了化学警报"。

这两种原语会同时馈入角色的感知中。状态提供当前条件的稳定、可查询快照；事件提供已发生事项的时间顺序记录。

### 规范化上下文格式

在更新到达 Convai 之前，SDK 会将所有已跟踪的状态和事件组装成一个规范化上下文字符串：

```
{StateName} 是 {Value}
{AnotherState} 是 {Value}
事件文本第一行
事件文本第二行
```

状态会首先出现，顺序与它们被 **首次设置** 的顺序一致——更新某个状态的值不会改变它的位置。所有状态之后，事件会按照调用顺序排列。

状态在更新过程中保留插入顺序的原因，是为了给角色提供一个稳定、可预测的世界视图。如果 `Station` 是最先设置的内容，那么无论它的值改变了多少次，它都会始终最先出现在角色的上下文中。这使模型更容易始终如一地解释上下文。

**示例：**

```csharp
context.SetState("Station", "Bay 3");       // 位置 1
context.SetState("HazardLevel", "High");    // 位置 2
context.AddEvent("Operator bypassed interlock");
context.SetState("Station", "Bay 7");       // 更新值；位置仍保持为 1
```

所有四次调用后的规范化上下文：

```
Station 是 Bay 7
HazardLevel 是 High
Operator bypassed interlock
```

你只需提供名称、值和事件文本。SDK 会自动组装并传送规范化字符串。

### 通向同一跟踪器的两个入口

动态上下文有两个入口，它们都会写入同一个底层跟踪器，并产生相同的网络行为。

**检查器 — `ConvaiDynamicContextRelay`**

`ConvaiDynamicContextRelay` 是 Inspector 入口。它取代了已弃用的 `ConvaiDynamicContextCommand` 组件。可通过以下路径添加： **Convai → Dynamic Context → Convai Dynamic Context Relay**，既可以放在与 `ConvaiCharacter` 相同的 GameObject 上，也可以放在任何已显式分配 **角色** 引用的 GameObject 上。如果 **角色** 为空且 **Auto Resolve Character** 已启用（默认如此），该中继会在调用时查找其自身 GameObject 上的 `ConvaiCharacter` 。

与 `ConvaiDynamicContextCommand`不同，一个中继不会封装单个预配置操作，因此你不再需要为每个命令创建一个子 GameObject。该中继公开了可直接调用 `character.DynamicContext`: `SetState(name, value)`, `AddEvent(text)`, `SetCurrentAttentionObject(objectName)`, `ClearCurrentAttentionObject()`, `ResetContext()` / `ResetContext(removeStatic)`，以及 `Flush()`的公共方法。可像绑定任何其他公共 `UnityEvent` 一样，将它们绑定到触发器碰撞体、时间轴标记或 UI 按钮上。一个中继组件可以服务于同一角色上的多个不同 `MonoBehaviour` 方法 `UnityEvent` 回调。

有两个 Inspector 字段会作为该中继实例发出的每次调用的默认值： **反应模式** 会设置每次调用传入的 `ConvaiRespondMode` （默认 `Silent`），以及 **立即刷新**；启用后，会在操作完成后立即调用 `Flush()` ，使更新绕过批处理延迟。由于中继始终会显式传入其配置的 **反应模式** ，当调用经由中继路由时，方法自身脚本中的默认值不会生效——例如， `AddEvent`的脚本默认值 `Auto` 会被中继设置的值 **反应模式** 覆盖。

中继的 **事件** 部分公开了 `OnQueued` 和 `OnSkipped`. `OnSkipped` 当中继无法解析某个时触发 `ConvaiCharacter`. `OnQueued` 当中继解析出一个角色并分发调用时触发——它确认的是已分发，而不是值已被接受。即使在空的状态名称或 null 值的情况下，仍会记录一条 Console 警告。 `OnQueued` 。

**脚本 — `IConvaiDynamicContext`**

访问 `character.DynamicContext` 以获取 `IConvaiDynamicContext` 接口，并直接从 C# 调用方法。这可让你完全控制时序、批处理和响应模式。

```csharp
IConvaiDynamicContext context = _character.DynamicContext;
context.SetState("Station", "Bay 7");
context.AddEvent("Operator bypassed interlock");
```

当以下情况时使用此入口：

* 上下文更新依赖运行时逻辑或无法表示为静态 Inspector 字段的数据
* 多个状态必须以原子方式更改（使用 `SetStates`)
* 你需要回读状态值（`TryGetStateValue`)
* 更新来源是状态机或分析管道等外部系统

### 批处理与交付时序

已跟踪的调用—— `SetState`, `SetStates`, `AddEvent`, `RemoveState`, `SetCurrentAttentionObject`, `ClearCurrentAttentionObject`，以及 `重置` ——永远不会立即发送网络消息。每次调用都会立刻更新本地跟踪状态，然后暂存一个待交付批次。这样做是为了让在同一帧内触发的一连串相关更改合并为一次规范化更新，而不是每次调用都发送一条网络消息并可能触发一次 LLM 回合。

```mermaid
graph TD
    A["SetState / AddEvent / RemoveState 调用"] --> B["在本地暂存；批处理窗口打开或延长"]
    B --> C{"调用了 Flush，还是窗口超时？"}
    C -->|否| B
    C -->|是| D["向 Convai 发送一次上下文更新"]
    D --> E["DynamicContextUpdateResultReceived"]
```

在会话活动期间，首次暂存的更改会打开一个批处理窗口。随后每一次暂存更改都会重置一个倒计时： `ConvaiCharacter.DynamicContextBatchDelaySeconds` ——默认 0.5 秒。SDK 还会施加一个内部上限：从该窗口中第一次暂存更改开始计时，最长 3 秒，因此持续不断的更改流不会无限期推迟交付。调用 `Flush()` 会立即发送待处理批次，绕过倒计时剩余的时间。

如果调用发生在角色进入会话之前，它只会在本地暂存——不会启动倒计时。当会话的角色就绪信号到来时，SDK 会立即刷新已暂存的批次，因此在 `Awake` 或 `Start` 中发起的调用无需任何额外计时代码即可交付：

```csharp
void Start()
{
    // 安全——先在本地暂存，然后在角色就绪后立即刷新
    _character.DynamicContext.SetState("Facility", "Offshore Platform Alpha");
    _character.DynamicContext.SetState("Scenario", "Fire Drill");
    _character.DynamicContext.AddEvent("Session initialized");
}
```

当会话断开连接时，SDK 会将已跟踪上下文标记为需要完整的规范化重新同步，因此下一次重新连接时，即使离线期间本地没有任何变化，也会重建相同的上下文。

当多个已暂存的更改携带不同的响应模式时，整个批次会采用优先级最高的那个： `MustRespond` 的优先级高于 `Auto`，而后者又高于 `Silent`.

{% hint style="warning" %}
**已在 SDK 4.4.0 中重命名。** `ConvaiContextReactionMode` 已移除。动态上下文和动态视觉上下文现在共用一套响应模式词汇， `ConvaiRespondMode` （命名空间 `Convai.Runtime`): `SyncOnly` 映射为 `Silent`, `ReactImmediately` 映射为 `MustRespond`，以及 `Auto` 保持不变。
{% endhint %}

{% hint style="warning" %}
`Apply()` 是唯一的例外：它不会暂存也不会入队。角色不在会话中时调用它会丢弃该更新。对于必须一直保留到会话开始的上下文，请使用 `SetState`, `AddEvent`，或其他已跟踪的方法。
{% endhint %}

### 确认与令牌反馈

SDK 发送的每一次动态上下文更新——无论来自受跟踪的批次、一个显式的 `Flush()`，还是一个原始的 `Apply()` 调用——都会由 `DynamicContextUpdateResultReceived`确认，并通过 `ConvaiManager.ActiveManager.Events.OnDynamicContextUpdateResultReceived`传递。使用 `UpdateId`将您发送的更新与对应的确认消息匹配起来。受跟踪的批次始终会获得 SDK 生成的 ID；只有 `Apply()` 允许您提供自己的 `updateId` 用于关联。

```csharp
using Convai.Domain.DomainEvents.Runtime;
using Convai.Runtime.Components;

ConvaiManager.ActiveManager.Events.OnDynamicContextUpdateResultReceived += result =>
{
    Debug.Log($"{result.Status}: revision {result.ContextRevision}, {result.RemainingTokens} tokens remaining");
};
```

| 属性                                                                       | 类型              | 含义                                                  |
| ------------------------------------------------------------------------ | --------------- | --------------------------------------------------- |
| `状态`                                                                     | `string`        | `“成功”` 当更新已应用时；任何其他值都表示该更新被拒绝。                      |
| `消息`                                                                     | `string`        | 与状态相伴的、便于人类阅读的详细说明。                                 |
| `UpdateId`                                                               | `string`        | 与发送更新时分配的 ID 相匹配。                                   |
| `ContextRevision`                                                        | `int`           | 后端为该角色上下文维护的修订计数器。                                  |
| `TokenCount`, `StaticTokenCount`, `RuntimeTokenCount`, `RemainingTokens` | `int`           | 此次更新后，该角色上下文窗口的令牌统计。                                |
| `RequestedRunLlm`, `ActualRunLlm`                                        | `string`        | 请求的响应模式与后端实际遵循的响应模式。                                |
| `DowngradeReason`                                                        | `string`        | 说明为什么 `ActualRunLlm` 与 `RequestedRunLlm`不同（如果不同的话）。 |
| `Interrupted`                                                            | `布尔值`           | 此更新是否打断了正在进行中的角色回合。                                 |
| `LlmTriggered`                                                           | `布尔值`           | 此更新是否触发了新的角色回合。                                     |
| `PromptRebuild`, `PromptRebuildStatus`                                   | `布尔值`, `string` | 后端是否重建了角色提示词以包含此更新，以及其结果。                           |

同一个事件在更新包含动作配置补丁时，还会携带特定于动作的字段—— `ActionConfigUpdated`, `ActionsCount`, `CurrentAttentionObject`以及相关属性——请参见 [在运行时更新角色动作](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/character-actions/update-actions-at-runtime.md) 了解该确认流程。

### 下一步

{% content-ref url="/pages/aad74412c9dc416577acffb5b0f1b8be3c9ad61d" %}
[动态上下文快速入门](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/dynamic-context/dynamic-context-quick-start.md)
{% endcontent-ref %}

{% content-ref url="/pages/743bcc7de155496cbbf02a52a5577aee99f28aab" %}
[中继组件参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/dynamic-context/relay-component-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/bb1aef3496a2be08987c770aa7b8072e7d8c5cd6" %}
[同步行为和时序](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/dynamic-context/sync-behavior-and-timing.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/features/dynamic-context/how-dynamic-context-works.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.
