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

# 同步行为与时序

了解动态上下文如何在去抖后刷新、Wait Until Conversation Is Idle 如何保持更新，以及离线排队如何工作。

动态上下文更新不会在每次 Blueprint 调用时都发送给 Convai。默认情况下，插件会短暂等待，以便将快速变化合并为一次发送，并且每个变更函数还带有一个单独的 `传递` 选项，可将更新保留到对话中自然停顿时再发送。本页说明何时发生刷新、每次刷新包含什么、保留的更新实际何时传递，以及何时绕过任一种等待。

### 更新何时发送

| 情况                                                 | 发生的情况                                                                                                                        |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| 连接时的默认防抖调用， `传递` = `正常发送`                          | 更新在本地暂存；一次 `替换` `context-update` 将在以下时间后发送： `ContextDebounceWindow` （默认 `0.5` 秒）窗口内没有新的更新                                    |
| 快速连续更新                                             | 突发期间的所有更改合并为一次刷新；每次调用都会重置计时器，但从首次更新起不得超过 `ContextMaxDebounceWindow` （默认 `3.0` 秒）                                             |
| `传递` = `等待对话空闲`，聚合 `ShouldRespond` 是 `自动`/`Always` | 该更新会被保留——不会暂存到防抖批次中——直到对话持续保持安静达 `传递前静默时间（秒）` （默认 `2.0` 秒），然后刷新。请参阅 [保留的更新实际何时传递](#when-a-held-update-is-actually-delivered) |
| `传递` = `等待对话空闲`，聚合 `ShouldRespond` 是 `Never`       | 保留不适用——静默更新没有可中断的内容，因此会通过常规防抖路径正常发送                                                                                          |
| `bFlushImmediately = true` 连接后， `传递` = `正常发送`      | `FlushDynamicContext()` 在当前帧运行，绕过防抖                                                                                          |
| `bFlushImmediately = true` 连接后， `传递` = `等待对话空闲`    | 仍会等待安静时刻，但会在首次安静时刻发送，而不是等待完整的 `传递前静默时间（秒）` 窗口                                                                                |
| 会话连接前的更新                                           | 更改累积在 `PendingContextBatch`中；连接后且防抖截止时间到达时进行首次刷新                                                                             |
| `重置动态上下文`                                          | 先清空暂存内容，然后发送一个 `Reset` `context-update`，然后清除本地跟踪器                                                                            |

### 保留的更新实际何时传递

`设置上下文状态`, `设置上下文状态（多个）`，以及 `添加上下文事件` 每个都公开一个 `传递` 参数（`EConvaiContextDelivery`）。防抖和传递是两种独立的等待，并按顺序应用：

1. **防抖** (`ContextDebounceWindow`, `ContextMaxDebounceWindow`）决定一批暂存更新何时准备好离开客户端。
2. **传递** 决定一个已就绪更新是立即发送，还是先等待对话变得安静。

带有 `传递` 设为 `等待对话空闲` 的更新会被保留，而不是暂存到常规防抖批次中。只有在对话持续保持安静达以下时间后，它才会被释放并作为一次 `替换` `context-update`进行刷新： `传递前静默时间（秒）` （ `ConversationIdleSettleSeconds` 属性，默认 `2.0` 秒）。任何对话活动——角色说话、用户说话——都会重启该静默窗口。总保留时间刻意不设上限，因此等待中的更新绝不会中断正在进行的交流；它可以等待对话持续的任意时长。一个被保留的 `添加上下文事件` 最终发送时会落在事件历史的末尾，因此它仍会被视为最近发生的事。

仅当批次的聚合 `ShouldRespond` 是 `自动` 或 `Always`时才适用保留。静默（`Never`）更新没有可中断的内容，因此 `传递` 对它无效，它始终通过常规防抖路径发送。

`bFlushImmediately = true` 改变的是保留等待的对象，而不是是否保留：使用 `传递` 设为 `等待对话空闲`时，立即调用仍会等待安静时刻，但一旦检测到首次安静时刻就会发送，而不是等待完整的 `传递前静默时间（秒）` 窗口。

### 传输消息结构

`UConvaiSubsystem::UpdateContext` 发送一条 `context-update` 消息，其 JSON 数据负载为：

| JSON 字段                    | 值（线路格式）                       | 何时存在                                                      |
| -------------------------- | ----------------------------- | --------------------------------------------------------- |
| `mode`                     | `追加`, `替换`, `重置`              | 每条消息中都存在                                                  |
| `run_llm`                  | `"auto"`, `"true"`, `"false"` | 每条消息中都存在——映射自 `EC_RunLLMOption` (`自动`, `Always`, `Never`) |
| `text`                     | 上下文字符串                        | 在以下情况下省略： `Reset` 文本为空时                                   |
| `current_attention_object` | 对象名称字符串                       | 当注意力被合并到同一条消息中时                                           |

Blueprint 枚举显示名称（`附加`, `替换`, `Reset`, `自动`, `Always`, `Never`）与上述线路字符串不同。

### 刷新负载结构

每次包含暂存状态或事件的刷新都会构建一个文本负载：

```
{StateName} 为 {Value}
{AnotherState} 为 {Value}
事件文本第一行
{Key} 已从 {OldValue} 更改为 {NewValue}
{NewKey} 为 {NewValue}
```

第一个区块是 **规范上下文** ——按插入顺序排列的状态（`"{Key} 为 {Value}"` 每行）后接按时间顺序排列的事件。它由以下函数构建： `FConvaiDynamicContextTracker::BuildCanonicalContext()`.

当批次的聚合 `ShouldRespond` 不是 `Never` 且存在状态增量行时，插件会在 **下一行** 规范上下文之后追加它们（一个 `\n` 分隔符，而非空行）。增量行会在提示词末尾呈现状态变化。

对于新值包含超过三个以空白字符分隔的单词的状态更新，增量行使用 `"{Key} 已从 {OldValue} 更改"` ，而不重复较长的新值。

### 活动会话期间的场景

#### 新状态——聚合 `ShouldRespond` 是 `Never`

**触发调用：** `设置上下文状态` 或 `设置上下文状态（多个）` 使用此前未设置过的键，并且批次中的每个暂存项都保持聚合 `ShouldRespond` 在 `Never`.

新键会立即进入规范上下文。不会添加增量行。

```
设施为近海平台 Alpha
```

#### 新状态——聚合 `ShouldRespond` 是 `自动` 或 `Always`

**触发调用：** `设置上下文状态` 或 `设置上下文状态（多个）` 使用一个新键，并且至少一个暂存项提高了聚合 `ShouldRespond` 到 `自动` 或 `Always`.

在此次刷新中，新键会被 **排除** 在规范上下文之外。诸如 `生命值为 50` 的增量行会追加在规范区块之后。

```
区域为市场区
生命值为 50
```

在 **下一次刷新**时，该键会正常进入规范上下文。

#### 对现有状态的更新——聚合 `ShouldRespond` 是 `Never`

**触发调用：** `设置上下文状态` 使用一个现有键，且批次保持聚合 `ShouldRespond` 在 `Never`.

更新后的值会进入规范上下文。不会添加增量行。

```
区域为码头
```

#### 对现有状态的更新——聚合 `ShouldRespond` 是 `自动` 或 `Always`

**触发调用：** `设置上下文状态` 使用一个现有键，且至少一个暂存项提高了聚合 `ShouldRespond` 到 `自动` 或 `Always`.

更新后的值会进入规范上下文。会追加描述该转换的增量行。

```
区域为码头
区域已从市场区更改为码头
```

#### 一次多个状态

**触发调用：** `设置上下文状态（多个）` 使用键值对映射，可能混合新键和现有键。

所有值都会写入跟踪器。当聚合不为 `Never`时，首次出现延迟会按每个新键分别应用。所有更改共享一次刷新——一次 `替换` `context-update`.

```
区域为码头
场景为活动状态
区域已从市场区更改为码头
难度为困难
```

#### 添加上下文事件

**触发调用：** `添加上下文事件`.

事件文本会提交到跟踪器的事件列表中，并出现在规范上下文中。常规上下文事件不会重复到单独的增量行中。

在同一防抖窗口内多次暂存的相同事件字符串会被去重——该次刷新只保留首次出现。

```
区域为码头
玩家已到达撤离点
```

#### 移除上下文状态

**触发调用：** `移除上下文状态`.

该键会从跟踪器中移除。 `bForceReplace` 已设置，因此刷新会发送不含已移除键的规范上下文。移除操作不会添加增量行。

#### 动态上下文重置

**触发调用：** `重置动态上下文`.

插件会先清空任何暂存的上下文批次，然后发送一条 `Reset` `context-update` (`mode: reset`, `run_llm: "false"`），且没有 `text` 字段，用于空的 `Reset` 负载。该 `Reset` 消息发送后会清除本地跟踪器。

当 `重置动态上下文` 被调用时，已经存在的排队触发器会立即清除。在待处理的离线 `Reset` 之后排队的触发器可能仍会在 `Reset` 期间清空 `FlushDynamicContext()`.

连接时， `重置动态上下文` 会立即调用刷新路径。离线时，重置标志会等待连接后的第一次刷新。

### 会话前排队

默认防抖变更调用—— `设置上下文状态`, `设置上下文状态（多个）`, `添加上下文事件`, `移除上下文状态`，以及 `重置动态上下文` ——会累积在 `PendingContextBatch` 中，同时 `IsChatbotConnected()` 是 `false`.

`TickDynamicContext()` 会持续累积，直到会话连接。连接后且防抖截止时间到达时， `FlushDynamicContext()` 会传递待处理批次。

离线更新会折叠为上述刷新行为——不会作为一系列单独消息重放。连接后，在防抖截止时间已过的第一个 tick 中，聚合为 `ShouldRespond` 仍为 `Never` 的批次会从跟踪器发送当前规范快照；聚合为 `自动` 或 `Always` 的批次遵循上述规范上下文加增量行为。任何待处理的 `Reset` 最后清除上下文。

```
// BeginPlay —— 三者都会在会话连接前安全排队
设置上下文状态  Name="Facility"  Value="Offshore Platform Alpha"
设置上下文状态  Name="Scenario"  Value="Fire Drill"
添加上下文事件  Text="Session initialized"  ShouldRespond=Never

// 连接后且防抖截止时间到达 —— 一次 Replace 上下文更新，随后若已排队则执行 Reset：
// 设施为近海平台 Alpha
// 场景为消防演习
// 会话已初始化
```

如果 `重置动态上下文` 且后续更新均在离线状态下待处理时，刷新会先发送暂存内容，然后 `Reset` 最后。

{% hint style="warning" %}
请勿使用 `bFlushImmediately = true` 在连接前处理必须传递的上下文。立即刷新会绕过断开连接时的 tick 防护，并可能在有效会话能够发送前清除暂存工作。
{% endhint %}

### `bFlushImmediately`

每个变更函数—— `设置上下文状态`, `设置上下文状态（多个）`, `添加上下文事件`, `移除上下文状态` ——都公开一个 **`高级`** 参数 `bFlushImmediately`。当 `true`, `FlushDynamicContext()` 在当前帧运行。

使用 `bFlushImmediately` 适用于默认防抖延迟会导致明显滞后的时间关键更新。每次立即调用都会运行 `FlushDynamicContext()` 在当前帧，并在聊天机器人已连接且存在可传递的暂存工作时发送一次 `context-update` 。避免在持续变化的值上每个 tick 都调用它。

`重置动态上下文` 在连接时会立即调用刷新路径。它没有 `bFlushImmediately` 参数。

`bFlushImmediately` 不会覆盖 `传递`。对于带有 `传递` 设为 `等待对话空闲`, `bFlushImmediately` 的更新，仍会等待对话变得安静——请参阅 [保留的更新实际何时传递](#when-a-held-update-is-actually-delivered) ——它只会在检测到安静后跳过完整的 `传递前静默时间（秒）` 窗口。

{% hint style="warning" %}
高频使用 `bFlushImmediately` ——例如同步每帧更新的值——在连接时每次调用都可能生成一条 `context-update` 。对于持续变化的值，请使用默认防抖行为。
{% endhint %}

### 下一步

{% content-ref url="/pages/b420c88b0fe545f2bdf932b279270d9c5974103c" %}
[排查动态上下文问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unreal-engine-plugin/features/dynamic-context/troubleshoot-dynamic-context.md)
{% endcontent-ref %}

{% content-ref url="/pages/a80705d8480f2f63dc8b84ea2e437c9dac45b7c5" %}
[动态上下文 Blueprint 参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unreal-engine-plugin/features/dynamic-context/dynamic-context-blueprint-reference.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 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/cha-jian-yu-ji-cheng/convai-unreal-engine-plugin/features/dynamic-context/sync-behavior-and-timing.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.
