> 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/unity-plugin-beta-overview/features/narrative-design/template-keys-dynamic-narrative-variables.md).

# 模板键：动态叙事变量

## 将运行时值注入叙事目标

模板键是运行时键值对，用于填充角色叙事目标中的占位符。您可在 [Convai 仪表板](https://convai.com) 中使用花括号语法进行定义——例如， `{PlayerName}` 或 `{CurrentTask}` —— SDK 会在运行时从 Unity 发送实际值，因此角色可以在对话中自然地引用它们。

这使得单个叙事图可以服务于多个会话，这些会话的参与者、场景或动态状态各不相同——而无需每次都编辑叙事设计。

## 在检查器中定义键

打开 `ConvaiNarrativeDesignManager` 在 Inspector 中并展开 **模板键** 折叠项。

单击 **+** 以添加一项。每项包含两个字段：

| 字段    | 说明                                        |
| ----- | ----------------------------------------- |
| **键** | 占位符名称，必须与叙事设计部分的目标中写法完全一致（区分大小写，不包含双大括号）。 |
| **值** | 初始值。你可以在运行时通过代码覆盖它。                       |

<figure><img src="/files/f8b0f20ba6c8cb13d7e98f1c578daecef9d2ce46" alt=""><figcaption></figcaption></figure>

在 Inspector 中定义的键会在 `Awake` 并在会话打开时自动发送到 Convai 后端。

{% hint style="info" %}
这些键不会在编辑时发送。它们会先在本地排队，并在角色的实时会话变为活动状态的瞬间刷新到后端。您无需手动调用任何操作即可确保传递。
{% endhint %}

在播放模式下，Inspector 会显示一个 **发送到服务器** 按钮，它会立即调用 `SendTemplateKeysUpdate()`。这对于无需编写代码即可测试会话中途的值变化非常有用。

## 在运行时更新键

请在 `ConvaiNarrativeDesignManager` 上使用以下任一方法从代码更新键：

**更新单个键：**

```csharp
narrativeManager.UpdateTemplateKey("PlayerName", "Alex");
```

**立即更新并发送（一次调用）：**

```csharp
narrativeManager.UpdateAndSendTemplateKey("ScenarioPhase", "Handwashing");
```

使用 `UpdateAndSendTemplateKey` 当键更改应在角色的下一条回复中立即生效时。

**一次更新多个键：**

```csharp
narrativeManager.UpdateTemplateKeys(new Dictionary<string, string>
{
    { "PlayerName", "Alex" },
    { "Department",  "Facilities" },
    { "CompletedSteps", "3" }
});
narrativeManager.SendTemplateKeysUpdate();
```

**读取当前键字典：**

```csharp
Dictionary<string, string> current = narrativeManager.GetTemplateKeys();
```

## 键的发送方式

```mermaid
flowchart TD
    A["UpdateAndSendTemplateKey(key, value)"] --> B[UpdateTemplateKey\n更新 _templateKeys 列表 + 控制器]
    B --> C[SendTemplateKeysUpdate\nSyncTemplateKeysToController]
    C --> D[Controller.SendTemplateKeysUpdate\n触发 OnTemplateKeysUpdateRequested]
    D --> E[ConvaiCharacter.NarrativeDesign.SetTemplateKeys]
    E --> F{IsInConversation?}
    F -- Yes --> G[ConnectionService.UpdateTemplateKeys\n立即发送]
    F -- No --> H[_hasPendingTemplateKeySync = true\n已排队等待 FlushPending]
    H --> I[会话打开 → FlushPending\n键会自动发送]
```

如果角色断开并重新连接，SDK 会在内部调用 `MarkPendingReplayAfterDisconnect` ，这样最新的键值会在下一次连接时重新发送。重新连接后，你无需手动重新发送键。

## 键命名规则

| 规则                   | 示例                                                     |
| -------------------- | ------------------------------------------------------ |
| 必须与仪表板占位符完全一致（区分大小写） | 仪表板： `{playerName}` → 键： `playerName` （不 `PlayerName`) |
| 前后不能有空白字符            | `"PlayerName"` ✓ — `" PlayerName"` ✗                   |
| 键字符串不能为空             | `""` 会被静默忽略                                            |
| 值可以为空字符串             | 键 `"OptionalField"` 的值 `""` 是有效的                       |

**良好的键名：**

| 键                      | 值示例          |
| ---------------------- | ------------ |
| `PlayerName`           | `"Maria"`    |
| `ScenarioLevel`        | `"Advanced"` |
| `CompletedCheckpoints` | `"4"`        |
| `SessionStartTime`     | `"09:15"`    |

**有问题的键名：**

| 键               | 问题                                                         |
| --------------- | ---------------------------------------------------------- |
| `player name`   | 名称中有空格 —— 将无法匹配 `{player name}` 占位符，如果仪表板使用 `{playerName}` |
| `"PlayerName "` | 尾随空格 —— 静默不匹配                                              |
| `""`            | 为空 —— 被忽略                                                  |

{% hint style="danger" %}
模板键值以纯字符串形式在网络上传输，并可能出现在角色的对话中。请不要在模板键值中包含密码、个人识别号码、API 密钥或任何其他敏感数据。
{% endhint %}

## 直接在角色上设置键

如果你在没有一个……的情况下 `ConvaiNarrativeDesignManager`，则可以通过角色 API 直接设置模板键：

```csharp
ConvaiCharacter character = GetComponent<ConvaiCharacter>();

// 单个键
character.NarrativeDesign.SetTemplateKey("PlayerName", "Alex");

// 多个键
character.NarrativeDesign.SetTemplateKeys(new Dictionary<string, string>
{
    { "PlayerName", "Alex" },
    { "Department", "Engineering" }
});
```

角色 API 和 Manager API 最终都会汇聚到同一个 `ConnectionService.UpdateTemplateKeys` 调用中。你可以在同一项目中使用任一路径，但避免在同一帧内对同一个键同时调用两种路径，因为这可能会发送冗余更新。

## 结论

模板键弥合了静态仪表板叙事目标与实时运行时数据之间的差距——玩家姓名、难度等级、会话参数——而无需对图本身做任何更改。会话打开前设置的键会被保留并自动刷新；会话中途设置的键会在角色下一次回复时生效。若需要超出检查器所提供范围的程序化控制——动态角色切换、异步数据获取，或在代码中订阅分段事件——请继续查看叙事设计脚本编写。


---

# 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:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/narrative-design/template-keys-dynamic-narrative-variables.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
