> 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/dynamic-context-usage-examples.md).

# 动态上下文使用示例

五个动态上下文示例展示了基于中继的触发、批量更新、事件日志、紧急切换以及同步的世界对象上下文。

以下示例从单个继电器驱动的触发器，逐步过渡到脚本化批量更新和同步的世界对象上下文。每个示例都列出了场景上下文、具体的 Inspector 或脚本设置，以及预期的运行时结果。

{% hint style="info" %}
所有示例都假设 `ConvaiManager` 已在场景中并配置了有效的 API 密钥，且每个 NPC 都有一个 `ConvaiCharacter` 组件，分配了 Character ID 并连接到 Convai。第五个示例还额外假设至少有一个 `ConvaiCharacter` 在跟踪值变化时已连接，因为世界对象更新会向每个已连接的角色广播。
{% endhint %}

### 安全演练：站点跟踪

**上下文：** 一次消防抑制认证演练。一个培训师 NPC 引导操作员通过各个抑制站点。角色必须始终知道操作员当前所在的站点，以便给出特定站点的指令和危险警告。

#### 设置（继电器 + 触发脚本）

1. 添加 `ConvaiDynamicContextRelay` 到培训师 NPC 的 GameObject（**Convai → Dynamic Context → Convai Dynamic Context Relay**).
2. 在 **默认值** 部分，设置 **Reaction Mode** 为 `MustRespond` ——角色应当对每次站点变更都作出确认。
3. 启用 **Flush Immediately** ，这样每次站点变更都会立即发送，而不是等待正常的批处理窗口。
4. 向每个站点的触发体添加一个小型触发脚本，让每个实例都指向同一个继电器：

```csharp
using Convai.Runtime.Presentation.DynamicContext;
using UnityEngine;

public class StationZoneTrigger : MonoBehaviour
{
    [SerializeField] private ConvaiDynamicContextRelay _relay;
    [SerializeField] private string _stationName;

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
            _relay.SetState("Station", _stationName);
    }
}
```

5. 设置 **站点名称** 设置为该区域的标签，例如 `Fire Suppression Bay`，并对每个其他站点重复此操作。

`ConvaiDynamicContextRelay.SetState` 同时接受状态名称和值，因此不能像单参数方法那样直接从 `UnityEvent` 在 Inspector 中绑定。上面的触发脚本会在运行时提供名称和每个区域的值；继电器会解析培训师的 `ConvaiCharacter` 并应用配置的反应模式。

#### 预期结果

当操作员进入 Fire Suppression Bay 触发区时， `StationZoneTrigger` 调用 `SetState("Station", "Fire Suppression Bay")` 在继电器上。继电器以 `MustRespond` 暂存状态，并立即刷新：

> "你已经到达 Fire Suppression Bay。鉴于当前极高的危险等级，在接触任何设备之前，请确认你的 PPE 已穿戴完毕。"

该 `Station` 状态会保留在本地跟踪器中。如果操作员在任何时候问“我在哪儿？”，角色都会用当前站点值作答。

### 入职引导：批量状态更新

**上下文：** 一个企业入职模拟。人力资源代表 NPC 会根据新员工收集了哪些物品以及通过了哪些检查点来调整引导内容。两个条件在同一时刻满足——一次性发送它们，让角色同时对两者作出反应。

#### 设置（脚本）

```csharp
using System.Collections.Generic;
using Convai.Runtime;
using Convai.Runtime.Components;
using UnityEngine;

public class OnboardingProgressTracker : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _hrCharacter;

    public void OnAccessCardAndBriefingComplete()
    {
        _hrCharacter.DynamicContext.SetStates(
            new Dictionary<string, string>
            {
                { "AccessCard", "Collected" },
                { "SecurityBriefing", "Completed" }
            },
            ConvaiRespondMode.MustRespond);
    }

    public void CheckIfReadyForFloorAccess()
    {
        bool hasCard = _hrCharacter.DynamicContext.TryGetStateValue("AccessCard", out string cardState)
                       && cardState == "Collected";
        bool hasBriefing = _hrCharacter.DynamicContext.TryGetStateValue("SecurityBriefing", out string briefingState)
                           && briefingState == "Completed";

        if (hasCard && hasBriefing)
            Debug.Log("Employee is ready for floor access.");
    }
}
```

#### 预期结果

`OnAccessCardAndBriefingComplete()` 一次性暂存两个状态。SDK 会将暂存的更新批量处理，最长可达 `ConvaiCharacter.DynamicContextBatchDelaySeconds` （默认 0.5 秒）后再向 Convai 发送一次规范重建。由于 `MustRespond` 被请求，人力资源角色会在批次刷新后作出回应：

> "你已领取访问卡并完成安全简报——你已获准进入楼层。下一步前往 Workstation 4B。"

`TryGetStateValue` 直接从本地跟踪器读取，不需要网络往返。如果状态已设置，它会返回当前值；否则返回 `false` 如果从未设置过或已被移除。

### 导览：展品跟踪与访客事件

**上下文：** 一次博物馆导览。讲解员 NPC 跟踪当前激活的是哪件展品，并按时间顺序记录访客互动，然后利用这些历史记录给出个性化推荐。

#### 设置（两个继电器实例）

`ConvaiDynamicContextRelay` 每个 GameObject 只能有一个实例，因此每个具有不同默认反应模式的继电器都放在讲解员 NPC 自己的一个子 GameObject 上。

**子 GameObject 1 — “ExhibitRelay”**

* 添加 `ConvaiDynamicContextRelay`.
* **Target → Auto Resolve Character:** 已禁用； **Character:** 讲解员的 `ConvaiCharacter` ——继电器位于子 GameObject 上，而不在角色自己的 GameObject 上。
* **Defaults → Reaction Mode:** `Silent` ——展品名称会更新，但不会立即回应；导览叙事负责推进节奏。
* 向每个展品的触发体添加一个触发脚本（与第一个示例中的 `StationZoneTrigger` 相同模式），调用 `_relay.SetState("ActiveExhibit", _exhibitName)` ，并让每个实例都指向这个继电器。

**子 GameObject 2 — “EventRelay”**

* 添加 `ConvaiDynamicContextRelay`.
* **Target → Auto Resolve Character:** 已禁用； **Character:** 讲解员的 `ConvaiCharacter`.
* **Defaults → Reaction Mode:** `自动` ——由 Convai 决定记录下来的互动是否值得立即回应。
* 将 UI 按钮直接连接到 `AddEvent`. `AddEvent` 只接受一个字符串参数，因此可以从按钮的 **On Click ()** 使用静态字符串参数进行绑定——无需脚本。将一个按钮绑定到 `EventRelay → AddEvent` ，参数为 `Visitor asked about the Colosseum reconstruction`，再将第二个按钮绑定到 `AddEvent` 使用 `Visitor photographed the gladiator exhibit`.

#### 预期结果

随着访客继续参观，讲解员的规范上下文会累积：

```
ActiveExhibit is Ancient Rome Collection
Visitor asked about the Colosseum reconstruction
Visitor photographed the gladiator exhibit
```

讲解员会同时引用当前展品和访客的具体互动：

> "既然你拍摄了角斗士展品，你可能会喜欢下一个房间里关于罗马军事装备的附加展示。"

### 应急响应：多状态切换与反应升级

**上下文：** 一次工业安全模拟。一个主管 NPC 必须对从例行检查同时切换到应急模式作出响应——三个状态变化和一个记录事件同时发生，角色必须在一次回应中全部予以确认。

#### 设置（脚本）

```csharp
using System.Collections.Generic;
using Convai.Runtime;
using Convai.Runtime.Components;
using UnityEngine;

public class EmergencyResponseController : MonoBehaviour
{
    [SerializeField] private ConvaiCharacter _supervisorCharacter;

    public void TriggerChemicalLeak()
    {
        _supervisorCharacter.DynamicContext.SetStates(
            new Dictionary<string, string>
            {
                { "OperationMode", "Emergency" },
                { "HazardType", "Chemical Leak - Bay 7" },
                { "EvacuationStatus", "In Progress" }
            },
            ConvaiRespondMode.MustRespond);

        _supervisorCharacter.DynamicContext.AddEvent(
            "Bay 7 化学泄漏警报已触发 - 已启用自动通风",
            ConvaiRespondMode.Silent);
    }
}
```

#### 预期结果

这两个调用都在同一个防抖窗口内暂存——首次暂存的变更后最长 `ConvaiCharacter.DynamicContextBatchDelaySeconds` （默认 0.5 秒）内。跟踪器将这三个状态和该事件合并为一次规范重建，并在整个批次中保留所请求的最强反应（`MustRespond` 高于 `Silent`），因此角色只会生成一条覆盖所有四个变更的回应：

> "Bay 7 发生化学泄漏——所有人员立即撤离东翼。Bay 7 通风已启动。未获解除警报前不得重新进入。"

调用 `SetStates` 来处理这三个同时发生的转换，而不是连续调用三次 `SetState` ，可将这些变更保留在角色用于描述过渡的 delta 行中。 `AddEvent` 调用会把警报作为单独的时间顺序行添加进去，同时不会降低此批次已请求的反应级别。

### 同步的世界对象上下文：共享设备状态

**上下文：** 一次工业安全模拟，工厂车间里有两个 NPC——一名主管和一名安全讲师。两个角色都必须知道压力阀是开启还是关闭，但不需要任何一个角色的脚本直接轮询该阀门。

#### 设置（带有被跟踪属性的世界对象）

1. 添加 `ConvaiObjectMetadata` 到阀门的 GameObject（**Convai → World Object**，或在 **添加组件**).
2. 设置 **中搜索“Convai World Object”** 为 `PressureValve` 和 **Object Description** 改为对阀门位置的事实性描述。
3. 在 **Tracked Properties**，添加一个 `ConvaiTrackedContextProperty` 条目：
   * **Property Name:** `状态`
   * **Source Component:** 阀门的控制器脚本
   * **Source Member Name:** 报告当前状态的公共属性或字段名，例如 `状态`
   * **Initial Value:** `Closed` ——仅在反射读取失败时使用
   * **Reaction:** `自动` ——让 Convai 决定是否值得提及该状态变化

```csharp
using UnityEngine;

public class ValveController : MonoBehaviour
{
    [SerializeField] private bool _isOpen;

    public string Status => _isOpen ? "Open" : "Closed";

    public void SetOpen(bool isOpen) => _isOpen = isOpen;
}
```

SDK 会轮询每个带有 **Source Component** 的被跟踪属性，使用共享计时器。当 `ValveController.Status` 变化时， `ConvaiObjectMetadata` 会使用状态键 `PressureValve.Status`.

{% hint style="info" %}
将更新后的值广播给每个已连接的角色——而不只是一个。若要在没有轮询 **Source Component**的情况下推送一个值，请调用 `SetTrackedPropertyValue("Status", "Open", ConvaiRespondMode.Auto)` 在 `ConvaiObjectMetadata` 组件上从你自己的脚本中调用，而不是接入反射源。当你已经确切知道值何时变化，并希望避免每帧进行反射读取时，请使用此方法。
{% endhint %}

#### 预期结果

一旦阀门打开，主管和安全讲师都会收到同样的状态更新：

```
PressureValve.Status is Open
```

任何一个 NPC 现在都可以在没有专门脚本向其提供数据的情况下引用该阀门——例如，主管："压力阀已开启——请保持原位，直到确认关闭。" 禁用或移除 `ConvaiObjectMetadata` 组件会移除 `PressureValve.Status` 状态，并使所有跟踪该状态的角色都失去它。

### 下一步

{% 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/5c3f9bcc544ac6f441fe54139ac1c670eeb5c958" %}
[动态上下文脚本 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/dynamic-context/dynamic-context-scripting-api.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 %}

{% content-ref url="/pages/3e23af730226f449644f6a9c60f206a69124eb0e" %}
[场景元数据](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/scene-metadata.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-unity-sdk/features/dynamic-context/dynamic-context-usage-examples.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.
