> 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/scene-metadata/usage-examples.md).

# 场景元数据使用示例

用于医疗培训、工业演练、博物馆导览、运行时对象更新和跟踪属性状态的完整场景元数据设置。

下面的示例涵盖了用于训练模拟和交互体验的真实设置。每个示例都是自包含的：先描述 Inspector 配置，然后给出完成行为所需的任何脚本。先从与你当前复杂度级别相匹配的示例开始。

### 示例 1：医疗训练模拟——解剖实验室

**场景：** 一个外科训练模拟，医疗讲师 NPC 引导受训者穿过解剖实验室。角色必须识别并描述房间里的实体模型和设备——受训者会问诸如“这是什么器官？”或“主动脉在哪里？”之类的问题。

#### 设置

添加 `ConvaiObjectMetadata` 将以下内容添加到每个解剖模型和设备条目：

| 中搜索“Convai World Object” | Object Description                 |
| ------------------------ | ---------------------------------- |
| 心脏模型                     | 放在中央检查台上的真人大小心脏解剖模型。显示所有四个腔室和主要血管。 |
| 肝脏模型                     | 固定在展示架左侧的成人肝脏模型。肝静脉采用颜色编码。         |
| 手术刀                      | 放在器械托盘上的标准手术刀。手柄为蓝色。               |
| 听诊器                      | 挂在检查台旁挂钩上的听诊器。                     |

添加 `ConvaiSceneMetadataCollector` 连接到 `ConvaiManager` GameObject。启用 **启动时采集**.

无需脚本。讲师角色会在会话开始时接收所有描述，并且可以回答基于实际场景的解剖问题。

{% hint style="success" %}
受训者问：“有哪些模型可供学习？”讲师回答：“中央桌上有一个真人大小的心脏模型，显示所有四个腔室；你左手边的展示架上是一个带有颜色编码肝静脉的成人肝脏模型。”
{% endhint %}

### 示例 2：工业安全演练——基于阶段的元数据

**场景：** 一个包含多个演练阶段的安全培训模块。每个阶段都会引入不同的危险和设备。AI 讲师应该只了解与当前阶段相关的道具。

#### 设置

保持 **启动时采集** 已禁用 `ConvaiSceneMetadataCollector`。使用脚本在每个阶段加载后发送元数据。

```csharp
using Convai.Runtime.SceneMetadata;
using UnityEngine;

public class SafetyDrillController : MonoBehaviour
{
    [SerializeField] private ConvaiSceneMetadataCollector _metadataCollector;
    [SerializeField] private ConvaiObjectMetadata[] _phase1Props;
    [SerializeField] private ConvaiObjectMetadata[] _phase2Props;

    private ConvaiObjectMetadata[] _allProps;

    void Awake()
    {
        _allProps = GetComponentsInChildren<ConvaiObjectMetadata>(includeInactive: true);
    }

    public void LoadPhase(int phase)
    {
        // 排除所有道具
        foreach (var prop in _allProps)
            prop.IncludeInMetadata = false;

        // 仅启用当前阶段的道具
        ConvaiObjectMetadata[] activeProps = phase == 1 ? _phase1Props : _phase2Props;
        foreach (var prop in activeProps)
            prop.IncludeInMetadata = true;

        // 发送更新后的负载
        if (_metadataCollector.IsReadyToSendMetadata())
            _metadataCollector.CollectAndSendSceneMetadata();
    }
}
```

每个阶段只会将其相关道具发送给 Convai。讲师会根据当前演练上下文调整自己的知识，而不会知道其他阶段的道具。

### 示例 3：互动博物馆——展品导览

**场景：** 一个虚拟博物馆导览角色会回答参观者关于多个房间中展品的问题。导览员应该知道每件展品是什么、在哪里，以及它为何重要。

#### 设置

添加 `ConvaiObjectMetadata` 添加到每个展品的根 GameObject。撰写包含位置提示和关键信息的描述：

| 中搜索“Convai World Object” | Object Description                          |
| ------------------------ | ------------------------------------------- |
| 罗塞塔石碑复制品                 | 位于埃及展厅、2 号房间中央的一块大型石板。包含象形文字、世俗体和古希腊文的相同文本。 |
| 罗马军团士兵盔甲                 | 位于 3 号房间左侧墙边的假人身上的完整军团士兵战斗盔甲。年代可追溯到公元 1 世纪。 |
| 维京长船残片                   | 保存下来的 9 世纪维京长船船首部分，悬挂在北欧展厅的天花板上。            |

启用 **启动时采集**。当参观者问“2 号房间里有什么？”时，导览员会用准确、基于描述的信息作答。

从知识渊博的导览员会说的话的角度来撰写描述。包括房间位置、视觉识别特征和相关背景。AI 使用 `Object Description` 字段的原文作为其回答的依据。

### 示例 4：运行时上下文更新——结合场景元数据和动态上下文

**场景：** 一个仓库培训场景，其中物品可以被移动或移除。当某个危险被清除后，AI 应停止提及它。当新工具到达时，AI 应立即知道它。

#### 排除已清除的对象

```csharp
public void OnHazardCleared(ConvaiObjectMetadata hazardMetadata)
{
    // 从 AI 上下文中移除，但不销毁 GameObject
    hazardMetadata.IncludeInMetadata = false;

    if (_collector.IsReadyToSendMetadata())
        _collector.CollectAndSendSceneMetadata();
}
```

#### 在运行时添加新对象

```csharp
public void OnToolDelivered(GameObject toolObject, string toolName, string toolDescription)
{
    // 在运行时添加元数据组件
    var metadata = toolObject.AddComponent<ConvaiObjectMetadata>();
    metadata.ObjectName = toolName;
    metadata.ObjectDescription = toolDescription;
    // 组件会在 OnEnable 时自动注册

    if (_collector.IsReadyToSendMetadata())
        _collector.CollectAndSendSceneMetadata();
}
```

{% hint style="info" %}
场景元数据和动态上下文是互补的。使用场景元数据告诉 AI 场景中存在什么。使用动态上下文告诉 AI 运行时正在发生什么。配对 `CollectAndSendSceneMetadata()` 使用 `SetState` 调用 `IConvaiDynamicContext` 能让角色同时具备对象感知和事件感知。
{% endhint %}

### 示例 5：仓库装卸区——将门状态作为跟踪属性

**场景：** 仓库安全培训 NPC 必须始终知道装卸区门是开着还是关着，并且如果门的传感器报告卡滞，必须立即作出反应。每次门移动后重新发送一个 `Object Description` 都需要一个脚本来拦截每次状态变化并重新运行场景元数据收集。跟踪属性可以让角色保持最新，而无需那额外的一步。

#### 设置（声明式——基于反射轮询）

添加 `ConvaiObjectMetadata` 添加到装卸区门的 GameObject。将 **中搜索“Convai World Object”** 为 `LoadingBayDoor` 和 **Object Description** 设置为门的位置和用途的固定描述。在 **Tracked Properties**中，添加一个 `ConvaiTrackedContextProperty` 条目：

| 字段               | 值                            |
| ---------------- | ---------------------------- |
| 属性名称             | `DoorStatus`                 |
| Source Component | 门的控制器脚本                      |
| 源成员名称            | `状态` — 报告当前状态的公共属性           |
| 初始值              | `关闭` — 仅在反射读取失败时使用           |
| 响应               | `自动` — 让 Convai 决定这个变化是否值得提及 |

```csharp
using UnityEngine;

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

    public string Status => _isOpen ? "开启" : "关闭";

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

`ConvaiObjectMetadata` 轮询每个具有 **Source Component** 的已跟踪属性，并在共享的 0.25 秒计时器上进行轮询。当 `LoadingBayDoorController.Status` 发生变化时，更新后的值会以状态键 `LoadingBayDoor.DoorStatus` 广播给每个已连接的角色——无需手动重新发送。

#### 设置（命令式——从代码事件推送）

传感器卡滞是一个离散事件，而不是每帧都读取的值，因此直接推送它，而不是接入一个反射源。添加第二个 **Tracked Properties** 条目，带有 **属性名称** 设置为 `SensorFault`, **初始值** 设置为 `无`，以及 **Source Component** 留空。调用 `SetTrackedPropertyValue` 从传感器自身的事件处理程序中：

```csharp
using Convai.Runtime;
using Convai.Runtime.SceneMetadata;
using UnityEngine;

public class DoorSensorMonitor : MonoBehaviour
{
    [SerializeField] private ConvaiObjectMetadata _doorMetadata;

    public void OnSensorJamDetected()
    {
        _doorMetadata.SetTrackedPropertyValue("SensorFault", "卡住", ConvaiRespondMode.MustRespond);
    }

    public void OnSensorCleared()
    {
        _doorMetadata.SetTrackedPropertyValue("SensorFault", "无", ConvaiRespondMode.Silent);
    }
}
```

`SetTrackedPropertyValue` 构建状态键 `LoadingBayDoor.SensorFault` 并立即将新值推送给每个已连接的角色，完全绕过轮询计时器。

#### 预期结果

当受训者问“装卸区门开着吗？”时，讲师会根据当前 `LoadingBayDoor.DoorStatus` 值作答，而不是根据会话开始时写入的描述。如果 `OnSensorJamDetected()` 在门移动时触发， `MustRespond` 响应 `SensorFault` 会让讲师立即出声：

> “停下——装卸区门传感器报告卡滞。在维护人员清除故障之前不要继续。”

如果组件被禁用后又重新启用， `DoorStatus` 会重新读取 `LoadingBayDoorController.Status` 通过其 **Source Component** 再次，而 `SensorFault` — 其没有运行时源 — 会重置为其 **初始值** 的 `无`。禁用或销毁 `ConvaiObjectMetadata` 会将这两个状态键从每个正在跟踪它们的角色中移除。

### 下一步

{% content-ref url="/pages/f6f3f3d88a3636afea1b0c8774162c2014fba780" %}
[排查场景元数据问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/scene-metadata/troubleshooting-and-diagnostics.md)
{% endcontent-ref %}

{% content-ref url="/pages/eb13f9c057b5d7a65a7d8338048c0f7def05d5cf" %}
[动态上下文](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/dynamic-context.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/scene-metadata/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.
