> 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/ui-and-presentation/transcript-ui/chat-and-subtitle-modes.md).

# 聊天和字幕模式

`ConvaiManager.ActiveManager.Transcripts` (`ConvaiTranscripts`) 提供对话的两个独立视图：实时语音对齐字幕和持久化的轮次历史。本页介绍如何添加 `SubtitleTranscriptUI` 用于字幕， `ChatTranscriptUI` 用于历史记录，或两者都添加——二者读取同一个门面并独立更新，因此添加一个不会移除或替换另一个。

### 选择你需要的投影

| 投影  | 门面成员                                                                  | 由...使用                        | 最适合                           |
| --- | --------------------------------------------------------------------- | ----------------------------- | ----------------------------- |
| 字幕  | `CurrentCaptions`, `字幕变更`, `SubscribeCaptions(...)`                   | `SubtitleTranscriptUI` （参考脚本） | 低延迟、与语音对齐的文本。短暂存在——不会作为聊天历史存储 |
| 时间线 | `CurrentTimeline`, `已更改`, `Subscribe(...)`, `SubscribeCommitted(...)` | `ChatTranscriptUI` （已随组件提供）   | 可持久保存、可滚动的对话历史，便于审阅、回放或导出     |

没有单一的模式开关可以启用一种投影而以牺牲另一种为代价。自定义组件可以订阅任一投影，或两者都订阅，来自 `ConvaiManager.ActiveManager.Transcripts` 直接：

```csharp
using System;
using Convai.Domain.Models;
using Convai.Runtime.Components;
using UnityEngine;

public class TranscriptProjectionExample : MonoBehaviour
{
    private IDisposable _captionSubscription;
    private IDisposable _historySubscription;

    private void OnEnable()
    {
        if (!ConvaiManager.ActiveManager.TryGetTranscripts(out ConvaiTranscripts transcripts)) return;

        // 用于屏幕字幕的低延迟字幕
        _captionSubscription = transcripts.SubscribeCaptions(OnCaption);

        // 用于可滚动聊天日志的持久化轮次历史
        _historySubscription = transcripts.Subscribe(
            OnHistoryChange,
            new TranscriptSubscriptionOptions { ReplayExisting = true });
    }

    private void OnDisable()
    {
        _captionSubscription?.Dispose();
        _historySubscription?.Dispose();
    }

    private void OnCaption(TranscriptCaption caption) { /* 更新字幕文本 */ }
    private void OnHistoryChange(TranscriptChange change) { /* 更新聊天气泡 */ }
}
```

两个随组件提供的显示组件也遵循 `ConvaiTranscripts.IsPresentationEnabled`。当设置面板的 `转录` 切换关闭，或者 `ConvaiRuntimeSettingsPatch` 将 `TranscriptEnabled = false`, `ChatTranscriptUI` 并且 `SubtitleTranscriptUI` 二者都会停止渲染——但 `CurrentTimeline` 仍会继续记录。关闭显示不会丢弃历史。

### 添加聊天历史显示

`ChatTranscriptUI` 将每个轮次渲染为可滚动列表中的消息气泡，角色和玩家轮次分列显示。轮次流式传输时气泡会实时更新，轮次提交后则固定下来。

{% stepper %}
{% step %}

#### 将预制体添加到场景中

拖拽 `TranscriptUI_Chat.prefab` 到场景中。可在 `Prefabs/TranscriptUI/TranscriptUI_Chat.prefab` 在 <code class="expression">space.vars.sdk\_package\_id</code> 包中找到。该预制体包含自己的 `Canvas` ——不要将其嵌套到现有的 `Canvas`.

`ChatTranscriptUI` 会查找 `ConvaiManager.ActiveManager` 并自动调用 `Subscribe(...)` 于 `ConvaiManager.ActiveManager.Transcripts` 一旦找到。无需手动注册。
{% endstep %}

{% step %}

#### 确保存在 EventSystem

聊天输入字段需要一个 `EventSystem` 位于场景中。如果你的场景没有它，可通过以下方式添加： **GameObject → UI → Event System**.
{% endstep %}

{% step %}

#### 运行你的场景

连接到一个角色并开始说话。角色语音显示在一个气泡列中，玩家语音显示在另一列中，面板会自动滚动到最新消息。
{% endstep %}
{% endstepper %}

#### `ChatTranscriptUI` 检查器字段

| 字段                       | 描述                                      |
| ------------------------ | --------------------------------------- |
| `scrollRect`             | `ScrollRect` 其中包含消息列表。自动滚动所必需           |
| `chatContainer`          | `RectTransform` 消息气泡 GameObject 所挂载的父对象 |
| `characterMessagePrefab` | 为每个角色轮次实例化的预制体                          |
| `playerMessagePrefab`    | 为每个玩家轮次实例化的预制体                          |
| `chatInputField`         | 可选 `TMP_InputField` 用于键入文本输入            |
| `fadeDuration`           | 面板淡入/淡出动画的秒数（默认 `0.5`)                  |
| `canvasFader`            | `CanvasFader` 驱动淡入淡出动画                  |
| `canvasGroup`            | `CanvasGroup` 在淡入淡出期间控制交互性              |

#### `ChatMessageBubble` 检查器字段

每个消息气泡预制体都必须包含一个 `ChatMessageBubble` 组件位于其根节点：

| 字段          | 描述                          |
| ----------- | --------------------------- |
| `senderUI`  | `TextMeshProUGUI` 用于显示说话者名称 |
| `messageUI` | `TextMeshProUGUI` 用于显示转录文本  |

`ChatTranscriptUI` 使用该角色配置的 `NameTagColor` （来自 `ConvaiCharacter` 或其角色配置资源）。若要在脚本中覆盖此设置，请调用 `bubble.SetSenderColor(Color)`.

调用 `ChatTranscriptUI.ClearAll()` 以销毁所有已渲染的消息气泡并重置面板。这只会清除视觉显示——底层轮次历史位于 `ConvaiManager.ActiveManager.Transcripts.CurrentTimeline` 不受影响。参见 [Transcript UI — Clear the transcript display](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/ui-and-presentation/transcript-ui.md#clear-the-transcript-display) 以了解注意事项。

### 添加字幕

`SubtitleTranscriptUI` 在固定位置显示一段文本，由 `SubscribeCaptions`驱动。当字幕定稿后，文本会在可配置的延迟后自动隐藏。

{% hint style="warning" %}
`SubtitleTranscriptUI` 仅以脚本形式提供——没有配套预制体。它是位于 `SamplesShared`中的参考实现；在修改之前请先复制到你自己的程序集，因为 `SamplesShared` 脚本会在 SDK 更新时被覆盖。
{% endhint %}

{% stepper %}
{% step %}

#### 将脚本添加到 UI GameObject

添加 `SubtitleTranscriptUI` （来自 `Scripts/UI/Transcript/Subtitle/SubtitleTranscriptUI.cs` 在 `SamplesShared`）到一个 `GameObject` 位于一个 `Canvas`。创建或复用两个 `TMP_Text` 用于字幕正文和说话者名称的元素，以及一个容器 `GameObject` 来切换可见性。
{% endstep %}

{% step %}

#### 连接检查器字段

分配 `subtitleText`, `speakerLabel`，以及 `subtitleContainer`。添加一个 `CanvasFader` 并且 `CanvasGroup` 如果你想要淡入淡出过渡。
{% endstep %}

{% step %}

#### 运行你的场景

开始说话，或者让角色说话。字幕会淡入，在轮次流式传输时逐词更新，并在以下时间后隐藏 `autoHideDelay` 秒后于轮次定稿后。
{% endstep %}
{% endstepper %}

#### `SubtitleTranscriptUI` 检查器字段

| 字段                  | 默认    | 描述                           |
| ------------------- | ----- | ---------------------------- |
| `subtitleText`      | —     | `TMP_Text` 渲染字幕正文            |
| `speakerLabel`      | —     | `TMP_Text` 渲染说话者名称           |
| `subtitleContainer` | —     | `GameObject` 包裹两个文本元素，可切换开/关 |
| `fadeDuration`      | `0.3` | 淡入/淡出动画所需秒数                  |
| `canvasFader`       | —     | `CanvasFader` 驱动淡入淡出动画       |
| `canvasGroup`       | —     | `CanvasGroup` 控制交互性          |
| `autoHideDelay`     | `3.0` | 字幕定稿后隐藏前等待的秒数                |

**筛选：**

| 字段                    | 默认      | 描述                                                                             |
| --------------------- | ------- | ------------------------------------------------------------------------------ |
| `finalOnly`           | `false` | 当 `true`时，只显示已定稿字幕；流式文本会被跳过                                                    |
| `filterBySpeakerType` | `false` | 当 `true`，将字幕限制为 `speakerType`                                                  |
| `speakerType`         | `角色`    | `TranscriptSpeakerType` 当启用时按以下项筛选 `filterBySpeakerType` 开启（`玩家`, `角色`，或 `系统`) |
| `speakerIdFilter`     | —       | 将字幕限制为特定说话者 ID                                                                 |
| `participantIdFilter` | —       | 将字幕限制为特定房间参与者 ID（多用户房间）                                                        |

**说话者标签颜色：** 角色语音——青色；玩家语音——绿色。

### 为聊天消息添加反馈按钮

聊天气泡可以包含点赞/点踩反馈按钮，让用户对单个 AI 回复进行评分。

{% stepper %}
{% step %}

#### 将 FeedbackButtons 添加到你的气泡预制体中

添加 `FeedbackButtons.prefab` 作为角色消息气泡预制体的子对象。可在 `Prefabs/TranscriptUI/FeedbackButtons.prefab` 在 <code class="expression">space.vars.sdk\_package\_id</code> 包中找到。该预制体包含反馈按钮视觉效果和一个 `FeedbackHandler` 组件。
{% endstep %}

{% step %}

#### 在用户可以为消息评分之前设置交互 ID

调用 `ChatMessageBubble.SetInteractionID(string)` 用于你想要设为可评分的轮次。交互 ID 可从 `ConvaiManager.ActiveManager.Events.OnInteractionCreated` (`InteractionCreated.InteractionId`，键为 `CharacterId`).
{% endstep %}

{% step %}

#### 运行你的场景

使用拇指按钮对角色回复进行评分。所选按钮会高亮；相反按钮会停用。 `FeedbackHandler.ResetState()` 调用时会将两个按钮都重置为中性状态。
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
没有随组件提供的组件会自动调用 `ChatMessageBubble.SetInteractionID(string)` 。在你的代码为某轮次调用它之前， `ChatMessageBubble.SendFeedback(bool)` 返回 `false` ，该消息的按钮也永远不会高亮。
{% endhint %}

#### `ChatMessageBubble` 反馈 API

| 方法                                               | 描述                                                            |
| ------------------------------------------------ | ------------------------------------------------------------- |
| `SetInteractionID(string interactionID)`         | 在可为该气泡发送反馈之前必需                                                |
| `SetAgentRegistry(IAgentRegistry agentRegistry)` | 用于角色查找所必需。由 `ChatTranscriptUI`                                |
| `bool SendFeedback(bool isPositiveFeedback)`     | 返回 `true` 如果 `interactionID` 已设置，且在代理注册表中找到了该角色。返回 `false` 否则 |

`FeedbackHandler.ResetState()` 会停用正向和负向按钮的填充视觉效果，使按钮恢复到中性状态。反馈按钮仅适用于角色消息气泡——玩家气泡不会收到交互 ID。

### 使用示例

#### 安全培训——用于会后复盘的聊天历史

一个职场安全培训模拟使用聊天历史，以便受训者在完成场景后可以回顾完整的 AI 教官对话：

* 拖拽 `TranscriptUI_Chat.prefab` 到场景中 `Canvas`
* 设置 `fadeDuration` 为 `0.3` 以便在进行中的场景中获得响应式过渡
* 调用 `ChatTranscriptUI.ClearAll()` 当新场景开始时，以免旧消息被带过去

运行时，受训者会看到 AI 教官所说内容的持久记录，并在总结复盘时浏览，然后再进入下一个模块。

#### 医疗模拟——用于整洁叠加的字幕

一个程序化医疗模拟使用字幕，让 AI 患者的语音以整洁的叠加层显示在患者模型上方，同时不会遮挡屏幕上的临床读数：

* 添加 `SubtitleTranscriptUI` 与 `subtitleContainer` 锚定在底部居中
* 设置 `autoHideDelay` 为 `2.0` ——患者回复之间快速清除可保持屏幕整洁
* 保持 `filterBySpeakerType` 关闭，这样患者和受训者的语音都会以字幕形式显示

运行时，每次患者回复都会短暂以字幕形式出现并清除，不会累积历史，从而在整个流程中保持模拟界面整洁。

#### 博物馆信息亭——字幕与历史同时运行

一家自然历史博物馆为游客运行一个展览屏幕，并为讲解员提供一个单独的复盘站点，两者都由同一段对话驱动：

* 添加 `SubtitleTranscriptUI` 到展览屏幕的 `Canvas`，并将 `filterBySpeakerType` 开启，并将 `speakerType` 设置为 `角色`，因此只有展览角色的语音会变成字幕
* 添加 `TranscriptUI_Chat.prefab` 到讲解员站点的 `Canvas`
* 二者都读取同一个 `ConvaiManager.ActiveManager.Transcripts` 并独立更新——无需按模式单独配置

运行时，游客在展览处看到整洁滚动的字幕，而讲解员站点会累积完整对话以便复盘，全部来自同一次会话。

### 故障排除

| 症状                                               | 可能原因                                                  | 修复                                                                                                               |
| ------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `[ChatTranscriptUI] 未分配 chatContainer - 消息将不会显示` | `chatContainer` 未在检查器中连接                              | 分配 `RectTransform` 消息气泡应作为其父级的                                                                                   |
| `[ChatTranscriptUI] 未分配 scrollRect - 自动滚动将不起作用`  | `scrollRect` 未在检查器中连接                                 | 分配 `ScrollRect` 组件；自动滚动缺失，但气泡仍会出现                                                                                |
| `[ChatTranscriptUI] 未找到活动的 ConvaiManager。`       | `ConvaiManager` 缺失或尚未初始化                              | 添加 `ConvaiManager` 到场景中，并确认它在该组件查找之前已初始化                                                                         |
| `[ChatTranscriptUI] 无法发送消息 - 依赖项未注入`             | 在其中输入的文本 `chatInputField` 在依赖项注入之前                    | 确保 `ConvaiManager` 以及一个 `ConvaiPlayer` 存在并已在场景中初始化                                                               |
| `[SubtitleTranscriptUI] 未找到活动的 ConvaiManager。`   | 与上面相同的原因，适用于字幕脚本                                      | 添加 `ConvaiManager` 到场景中                                                                                          |
| 字幕始终不出现                                          | 没有组件在调用 `SubscribeCaptions` 在场景中，或者字幕的说话者与已配置的筛选条件不匹配 | 添加 `SubtitleTranscriptUI`，确认其 `TMP_Text` 字段已分配，并检查 `filterBySpeakerType`/`speakerIdFilter`/`participantIdFilter` |
| 聊天和字幕显示都不更新                                      | `ConvaiTranscripts.IsPresentationEnabled` 是 `false`   | 检查设置面板的 `转录` 切换，或应用 `TranscriptEnabled = true` 通过一个 `ConvaiRuntimeSettingsPatch`                                 |
| 反馈按钮始终不高亮                                        | `SendFeedback` 返回 `false`                             | 确认你的代码调用了 `ChatMessageBubble.SetInteractionID(string)` 用于该轮次——没有任何随组件提供的组件会自动执行此操作                               |

### 下一步

你已经添加了聊天历史显示、字幕，或两者都添加了，并为聊天消息连接了反馈按钮。若要自定义气泡的视觉外观或构建完全自定义的转录显示，请参见 Customizing UI Components。若要让用户在运行时显示或隐藏转录显示，请参见 Settings Panel。

{% content-ref url="/pages/5f60eaa8d444f5ee8a995392852da827736d3b54" %}
[自定义 UI 组件](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/ui-and-presentation/customizing-ui-components.md)
{% endcontent-ref %}

{% content-ref url="/pages/dddf624ca0ae7ec32afa0c9e601f2cb7c7ab2b6a" %}
[设置面板](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/ui-and-presentation/settings-panel.md)
{% endcontent-ref %}

{% content-ref url="/pages/b846767a1ed5a04b3cfb93646c3c8472baf91067" %}
[转录历史与查询](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/ui-and-presentation/transcript-ui/transcript-history-and-queries.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/ui-and-presentation/transcript-ui/chat-and-subtitle-modes.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.
