> 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` 组件，已分配角色 ID 并连接到 Convai。第五个示例还假设至少有一个 `ConvaiCharacter` 在受跟踪值变化时已连接，因为世界对象更新会广播给每个已连接的角色。
{% endhint %}

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

**背景：** 一次消防抑制认证演练。一个训练员 NPC 引导操作员通过各个抑制站点。该角色必须始终知道操作员当前所在的站点，以便提供针对站点的指令和危险警告。

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

1. 添加 `ConvaiDynamicContextRelay` 到训练员 NPC 的 GameObject（**Convai → 动态上下文 → Convai Dynamic Context Relay**).
2. 在 **默认值** 部分中，设置 **响应模式** 到 `MustRespond` ——角色应确认每一次站点变更。
3. 启用 **立即刷新** ，这样每次站点变更都会立即发送，而不是等待常规批处理窗口。
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. 将 **站点名称** 设为区域标签，例如 `灭火区`，并为每个额外站点重复此操作。

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

#### 预期结果

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

> "你已经到达灭火区。当前危险等级极高，请在接触任何设备前确认你的 PPE 已佩戴完毕。"

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

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

**背景：** 一次企业入职模拟。一名 HR 代表 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` 请求了，因此 HR 角色会在批处理刷新后响应：

> "你已经拿到访客卡并完成了安全简报——你已获准进入楼层。下一步请前往 4B 工作站。"

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

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

**背景：** 一次博物馆导览。一名讲解员 NPC 会跟踪当前激活的展品，并按时间顺序记录访客交互，然后利用这些历史记录提供个性化推荐。

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

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

**子 GameObject 1 — "ExhibitRelay"**

* 添加 `ConvaiDynamicContextRelay`.
* **目标 → 自动解析角色：** 已禁用； **角色：** 讲解员的 `ConvaiCharacter` ——继电器位于子 GameObject 上，而不是角色自身的 GameObject 上。
* **默认值 → 响应模式：** `静默` ——展品名称会更新，但不会立即响应；游览叙事决定节奏。
* 向每个展品的触发体添加一个触发脚本（与 `StationZoneTrigger` 在第一个示例中相同的模式），调用 `_relay.SetState("ActiveExhibit", _exhibitName)` ，并让每个实例都指向该继电器。

**子 GameObject 2 — "EventRelay"**

* 添加 `ConvaiDynamicContextRelay`.
* **目标 → 自动解析角色：** 已禁用； **角色：** 讲解员的 `ConvaiCharacter`.
* **默认值 → 响应模式：** `自动` ——由 Convai 决定记录的交互是否值得立即响应。
* 将 UI 按钮直接连接到 `AddEvent`. `AddEvent` 需要一个字符串参数，因此可以从按钮的 **On Click ()** 通过一个静态字符串参数进行绑定——无需脚本。将一个按钮绑定到 `EventRelay → AddEvent` ，参数为 `访客询问了斗兽场重建`，再将第二个按钮绑定到 `AddEvent` 和 `访客拍摄了角斗士展品`.

#### 预期结果

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

```
ActiveExhibit 为 Ancient Rome Collection
访客询问了斗兽场重建
访客拍摄了角斗士展品
```

讲解员会同时提及当前展品和访客的具体交互：

> "既然你拍摄了角斗士展品，你可能会喜欢隔壁房间里关于罗马军事装备的额外展区。"

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

**背景：** 一次工业安全模拟。一名主管 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` 优先于 `静默`），因此角色只会产生一次响应，涵盖全部四项变更：

> "Bay 7 发生化学泄漏——所有人员立即撤离东翼。Bay 7 的通风已启动。在给出解除警报前不得返回。"

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

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

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

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

1. 添加 `ConvaiObjectMetadata` 到阀门的 GameObject（**Convai → 世界对象**，或在 **Add Component**).
2. 将 **对象名称** 到 `PressureValve` 和 **对象描述** 填写阀门位置的事实性描述。
3. 在 **跟踪属性**，添加一个 `ConvaiTrackedContextProperty` 条目：
   * **属性名称：** `Status`
   * **源组件：** 阀门的控制器脚本
   * **源成员名称：** 报告当前状态的公共属性或字段名称，例如 `Status`
   * **初始值：** `关闭` ——仅在反射读取失败时使用
   * **响应：** `自动` ——让 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 会在共享计时器上轮询每个带有 **源组件** 的跟踪属性。当 `ValveController.Status` 变化时， `ConvaiObjectMetadata` 会使用状态键 `PressureValve.Status`.

{% hint style="info" %}
向所有已连接的角色广播更新后的值，而不仅仅是一个角色。 **源组件**要在没有轮询 `的情况下推送值，请调用` 组件上的 `ConvaiObjectMetadata` ，从你自己的脚本中执行，而不是连接一个反射源。当你已经确切知道值何时变化，并希望避免每帧进行一次反射读取时，请使用此方法。
{% endhint %}

#### 预期结果

一旦阀门打开，监督员和安全讲师都会收到相同的状态更新：

```
PressureValve.Status 为 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 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/dynamic-context-usage-examples.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.
