> 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` 添加到每个解剖模型和设备物品上：

| 对象名称 | 对象描述                               |
| ---- | ---------------------------------- |
| 心脏模型 | 放在中央检查台上的真人大小解剖心脏模型。显示全部四个心腔和主要血管。 |
| 肝脏模型 | 安装在展示架左侧的成人肝脏模型。肝静脉采用颜色编码。         |
| 手术刀  | 放在器械托盘上的标准手术刀。手柄为蓝色。               |
| 听诊器  | 挂在检查台旁边挂钩上的听诊器。                    |

将 `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。撰写包含位置提示和关键信息的描述：

| 对象名称     | 对象描述                                           |
| -------- | ---------------------------------------------- |
| 罗塞塔石碑复制品 | 埃及展厅中的大型石板，位于 2 号房间中央。包含以象形文字、世俗体和古希腊文书写的相同文本。 |
| 罗马军团士兵铠甲 | 3 号房间左侧墙边的人体模特上穿着的完整军团战斗盔甲。年代可追溯到公元 1 世纪。      |
| 维京长船残片   | 保存下来的 9 世纪维京长船船首部分，悬挂在北欧展厅的天花板上。               |

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

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

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

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

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

```csharp
public void OnHazardCleared(ConvaiObjectMetadata hazardMetadata)
{
    // 在不销毁 GameObject 的情况下将其从 AI 上下文中移除
    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 必须始终知道装货区门是开着还是关着，并且如果门的传感器报告卡住，必须立即作出反应。每次重新发送一个 `对象描述` 在每次门移动后都需要一个脚本来拦截每次状态变化并重新运行场景元数据收集。受跟踪属性可以让角色保持最新，而无需这一步额外操作。

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

将 `ConvaiObjectMetadata` 添加到装货区门的 GameObject 上。将 **对象名称** 设置为 `LoadingBayDoor` ，并将 **对象描述** 设置为对门的位置和用途的固定描述。在 **受跟踪属性**中，添加一个 `ConvaiTrackedContextProperty` 条目：

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

```csharp
using UnityEngine;

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

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

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

`ConvaiObjectMetadata` 轮询每个带有 **源组件** 的受跟踪属性，使用共享的 0.25 秒计时器。当 `LoadingBayDoorController.Status` 变化时，更新后的值会以状态键 `LoadingBayDoor.DoorStatus` 广播给每个连接的角色——无需手动重新发送。

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

传感器卡住是一个离散事件，而不是每帧读取的值，因此直接推送它，而不是接入反射源。添加第二个 **受跟踪属性** 条目， **属性名称** 设置为 `SensorFault`, **初始值** 设置为 `None`，以及 **源组件** 留空。调用 `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", "Jammed", ConvaiRespondMode.MustRespond);
    }

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

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

#### 预期结果

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

> “停止——装货区门传感器报告卡住。请勿继续，直到维护人员清除故障。”

如果组件被禁用后又重新启用， `DoorStatus` 会重新读取 `LoadingBayDoorController.Status` 通过其 **源组件** 再次，而 `SensorFault` ——它没有运行时源——会重置为其 **初始值** 的 `None`。禁用或销毁 `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 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/scene-metadata/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.
