> 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/troubleshooting/debug-tools-reference.md).

# 调试工具参考

Convai Unity SDK 随附一套分层的诊断工具：可配置的日志系统，支持按子系统控制详细程度、用于动作调试的实时 Inspector 探针，以及用于会话诊断的实时 `ConvaiRoomManager`、输出到 Console 的会话指标，以及用于对话管线性能分析的客户端延迟测量。本页是它们的完整参考。

### 诊断

{% hint style="warning" %}
SDK `4.4.0` 移除了 `Convai → 日志设置` 菜单和窗口。日志配置现在位于诊断部分，可通过 `Convai → 设置` 以及通过 `编辑 → 项目设置 → Convai SDK`.
{% endhint %}

#### 配置位置

打开 `Convai → 设置` 或 `编辑 → 项目设置 → Convai SDK` 并滚动到 **诊断** 部分。两个入口点都会显示相同的 `DiagnosticsSectionView`，因此在一处所做的更改会同步显示在另一处。控制 Unity Console 中显示内容的设置包括：

* **预设** — 一键按钮，同时设置全局日志级别、包含堆栈跟踪和彩色 Console 输出；参见 [日志预设](#logging-presets)
* **全局日志级别** — 适用于所有日志类别的最低详细级别
* **包含堆栈跟踪** — Warning 和 Error 条目是否包含堆栈跟踪
* **彩色 Console 输出** — 日志条目是否在 Unity Console 中按颜色编码
* **类别覆盖** — 按子系统覆盖，优先于全局级别

诊断部分标题也有自己的 **重置** 按钮，其应用的配置与下面的 `默认` 预设相同。

#### 日志预设

全局日志级别字段上方有三个预设按钮。每个预设都会同时设置全局级别和两个输出标志，然后清除所有类别覆盖。

| 预设    | 全局日志级别 | 包含堆栈跟踪 | 彩色 Console 输出 | 类别覆盖 |
| ----- | ------ | ------ | ------------- | ---- |
| `详细`  | `跟踪`   | 开启     | 开启            | 清除   |
| `默认`  | `信息`   | 开启     | 开启            | 清除   |
| `仅错误` | `错误`   | 开启     | 开启            | 清除   |

`默认` 与 SDK 的默认日志配置一致。应用任何预设都会覆盖全局日志级别、包含堆栈跟踪和彩色 Console 输出，并移除现有的类别覆盖——在点击预设后请重新应用项目特定的覆盖。

#### 日志级别

SDK 使用五个日志级别。数值越高，详细程度越高。

| 级别     | 值 | Console 中显示的内容    |
| ------ | - | ----------------- |
| **错误** | 1 | 仅错误               |
| **警告** | 2 | 错误和警告             |
| **信息** | 3 | 错误、警告和信息消息 *（默认）* |
| **调试** | 4 | 以上全部，外加调试消息       |
| **跟踪** | 5 | 所有内容，包括细粒度的内部跟踪   |

默认值为 **信息**。切换到 **调试** 在排查问题时会产生更多输出——在发布到生产环境之前请将其禁用。

{% hint style="warning" %}
`调试`SDK 源码中的 -级别调用都使用 `[Conditional("UNITY_EDITOR")]`, `[Conditional("DEVELOPMENT_BUILD")]`，以及 `[Conditional("CONVAI_DEBUG_LOGGING")]`。这意味着 **调试日志调用会从非开发构建中被编译移除** 除非你添加 `CONVAI_DEBUG_LOGGING` 到你的脚本定义符号中。将 `GlobalLogLevel` 设置为 `调试` 在发布构建中不会产生 Debug 消息，因为调用位置在编译后的代码中不存在。Debug 消息在 Unity 编辑器和开发构建中仍然可用，无需额外定义。
{% endhint %}

要在生产构建中启用 Debug 消息，请添加 `CONVAI_DEBUG_LOGGING` 设置为 **编辑 → 项目设置 → Player → 脚本定义符号**.

#### 日志类别覆盖

类别覆盖可让你提高某个子系统的详细程度，而不会让其他内容的输出淹没 Console。例如，要诊断传输问题而不查看音频、UI 和角色日志：

1. 打开 **诊断** 并展开 **类别覆盖** 折叠区域——其中列出了每个日志类别，并带有一个下拉菜单，默认值为 `继承`
2. 将 `传输` 下拉菜单设置为 `调试`

其他所有类别都保持全局级别。折叠区域标题会显示当前生效的覆盖数量，例如 **类别覆盖（1）**。将某个类别的下拉菜单改回 `继承` 即可移除该覆盖。

#### 日志类别参考

| 类别           | 涵盖的子系统                      |
| ------------ | --------------------------- |
| `SDK`        | 一般 SDK 操作与初始化               |
| `角色`         | 角色和 NPC 生命周期                |
| `音频`         | 音频输出和麦克风输入                  |
| `UI`         | Transcript UI 和通知组件         |
| `REST`       | 对 Convai 的 REST API 调用      |
| `传输`         | LiveKit 和 WebRTC 传输层        |
| `事件`         | 事件转发系统（会话、角色、Transcript 事件） |
| `玩家`         | 玩家身份和输入                     |
| `编辑器`        | 仅编辑器工具和验证器                  |
| `视觉`         | 摄像头采集和视频发布                  |
| `引导`         | SDK 初始化和 ConvaiSettings 加载  |
| `Transcript` | Transcript 处理和路由            |
| `叙事`         | 叙事设计和故事触发系统                 |
| `唇形同步`       | 唇形同步处理和 BlendShape 播放       |

#### 自定义日志接收器

通过实现 `ILogSink` 并将其注册到 `ConvaiLogger`.

`ILogSink` 需要以下成员：

| 成员                              | 描述                       |
| ------------------------------- | ------------------------ |
| `string Name { get; }`          | 诊断中显示的接收器标识符             |
| `bool IsEnabled { get; }`       | 返回 `false` 以暂停接收器而无需取消注册 |
| `void SetEnabled(bool enabled)` | 在运行时切换接收器状态              |
| `void Write(LogEntry entry)`    | 对每个通过级别过滤器的日志条目调用        |
| `void Flush()`                  | 刷新所有缓冲的条目——在应用关闭前调用      |
| `void Dispose()`                | 在移除接收器时清理资源              |

{% code title="FileLogSink.cs" %}

```csharp
using System.IO;
using Convai.Domain.Logging;

public class FileLogSink : ILogSink
{
    private readonly string _path;
    private bool _enabled = true;

    public FileLogSink(string path) => _path = path;

    public string Name => "FileLogSink";
    public bool IsEnabled => _enabled;
    public void SetEnabled(bool enabled) => _enabled = enabled;

    public void Write(LogEntry entry)
    {
        string line = $"[{entry.Level}][{entry.Category}] {entry.Message}";
        File.AppendAllText(_path, line + "\n");
    }

    public void Flush() { }
    public void Dispose() { }
}
```

{% endcode %}

尽早注册接收器——在 `Awake()` 或 `[RuntimeInitializeOnLoadMethod]` 回调中——在任何 Convai 组件激活之前。 `ConvaiLogger` 会在首次使用时自动初始化；在初始化后注册的接收器只能接收后续消息。

```csharp
// 在任何 Convai 组件激活之前只注册一次
private void Awake()
{
    ConvaiLogger.RegisterSink(new FileLogSink(Application.persistentDataPath + "/sdk.log"));
}
```

当不再需要时移除接收器：

```csharp
ConvaiLogger.UnregisterSink(mySink);
```

`ConvaiLogger.SinkCount` 返回当前已注册接收器的数量。默认的 Unity Console 接收器（`UnityConsoleSink`）始终处于注册状态，且无法通过公共 API 移除。

### ConvaiActionDebugProbe

`ConvaiActionDebugProbe` 是 Actions 功能的主要诊断工具。它订阅每个 dispatcher 事件，并直接在 Inspector 中显示实时计数器和最近一次看到的动作数据——无需自定义日志。

**通过以下方式添加：** 添加组件 → **Convai/Debug/Convai Action Debug Probe**

该组件需要 `ConvaiCharacter` 位于同一个 GameObject 上，并会自动解析 `ConvaiActionDispatcher`。如果 `ConvaiActionDispatcher` 缺失，探针仍会通过 `ConvaiCharacter.OnActionsReceived`记录接收到的动作批次，但不会跟踪 dispatcher 生命周期事件（步骤开始、成功、失败）。

#### Inspector 字段

| 字段              | 描述                                              |
| --------------- | ----------------------------------------------- |
| **记录到 Console** | 启用后，每个动作事件都会带有完整细节打印到 Console。生产环境中请禁用，以避免日志刷屏。 |
| **已接收批次数**      | 自 Play 开始以来接收的动作批次总数                            |
| **已开始步骤数**      | dispatcher 已开始执行的步骤总数                           |
| **成功步骤数**       | 以...完成的步骤总数 `成功`                                |
| **失败步骤数**       | 返回以下结果的步骤总数 `失败`, `超时`，或缺少定义或目标                 |
| **未处理步骤数**      | 执行器返回以下结果的步骤总数 `未处理`                            |
| **已中止批次数**      | 因早停（Stop Batch 失败策略）而提前停止的批次总数                  |
| **最近接收的批次**     | 从 Convai 接收到的最近批次的 JSON                         |
| **最近开始的步骤**     | 最近开始的步骤详情                                       |
| **最近成功的步骤**     | 最近成功的步骤详情                                       |
| **最近未处理的步骤**    | 最近未处理的步骤详情                                      |

#### 上下文菜单操作

右键单击 `ConvaiActionDebugProbe` Inspector 中的组件标题栏以访问：

| 项目         | 作用                                         |
| ---------- | ------------------------------------------ |
| **注入测试批次** | 发送一个 `移动到` 动作，目标为第一个已注册对象——无需实时对话即可验证执行器连接 |
| **重置探针状态** | 重置所有计数器并清空最近一次看到的文本字段                      |

{% hint style="info" %}
**注入测试批次** 是验证执行器连接的最快方法。如果成功，说明你的动作定义、对象目标和执行器都已正确配置。如果 `未处理步骤数` 增加而不是 `成功步骤数`，则表示以下项的执行器 `移动到` 未在该 GameObject 上注册。
{% endhint %}

### ConvaiRoomManager 运行时状态

`ConvaiRoomManager` 以普通属性的形式暴露诊断状态——无需订阅事件。可从任何脚本、通过 `[ContextMenu]` 编辑器中的方法，或场景内的调试面板中读取。

#### 公共状态属性

| 属性                        | 类型             | 描述                                                       |
| ------------------------- | -------------- | -------------------------------------------------------- |
| `CurrentState`            | `SessionState` | 当前会话状态： `已断开连接`, `正在连接`, `已连接`, `正在断开连接`, `正在重新连接`, `错误` |
| `IsConnected`             | `bool`         | `true` 当房间处于活动连接状态时                                      |
| `ConnectAttemptCount`     | `int`          | 自场景加载以来的连接尝试总数                                           |
| `ReconnectCount`          | `int`          | 自场景加载以来的重新连接尝试总数                                         |
| `LastSessionErrorCode`    | `string`       | 最近一次错误事件的错误代码                                            |
| `LastSessionErrorMessage` | `string`       | 最近一次错误的可读消息                                              |

{% hint style="warning" %}
`SessionState.Error` 表示会话发生了不可恢复的故障。房间不会在此状态下自动重新连接。请调用 `DisconnectAsync()` 然后调用 `ConnectAsync()` 以重置会话。
{% endhint %}

#### IRoomDiagnostics 完整快照

如需更完整的快照，请调用 `GetDiagnostics()` 于 `ConvaiRoomManager.DiagnosticsCoordinator`。这会返回一个 `RoomDiagnosticsSnapshot` ，其中包含自诊断实例创建以来累积的连接统计信息。

```csharp
var room = FindFirstObjectByType<ConvaiRoomManager>();
if (room?.DiagnosticsCoordinator != null)
{
    RoomDiagnosticsSnapshot snap = room.DiagnosticsCoordinator.GetDiagnostics();
    Debug.Log($"状态:          {snap.CurrentState}");
    Debug.Log($"连接:          {snap.SuccessfulConnections} / {snap.TotalConnectionAttempts} 成功");
    Debug.Log($"失败:          {snap.FailedConnections}");
    Debug.Log($"错误总数:      {snap.TotalErrors}");
    Debug.Log($"最后连接时间: {snap.LastConnectedAt}");
    Debug.Log($"最后错误:     {snap.LastErrorCode} 于 {snap.LastErrorAt}");
    Debug.Log($"运行时间:      {snap.SessionUptime}");
    Debug.Log($"角色:          {snap.RegisteredCharacterCount}");
    Debug.Log($"玩家:          {snap.RegisteredPlayerCount}");
}
```

#### RoomDiagnosticsSnapshot 字段参考

| 字段                         | 类型          | 描述                               |
| -------------------------- | ----------- | -------------------------------- |
| `CurrentState`             | `string`    | 拍摄快照时的状态名称                       |
| `TotalConnectionAttempts`  | `int`       | 自启动或上次重置以来的所有连接尝试                |
| `SuccessfulConnections`    | `int`       | 到达已连接状态的尝试                       |
| `FailedConnections`        | `int`       | 最终失败的尝试                          |
| `TotalErrors`              | `int`       | 记录的错误总数                          |
| `LastConnectedAt`          | `DateTime?` | 上次成功连接的 UTC 时间戳； `null` 如果从未连接过  |
| `LastErrorAt`              | `DateTime?` | 上次记录错误的 UTC 时间戳； `null` 如果没有错误   |
| `LastErrorCode`            | `string`    | 上次错误的错误代码                        |
| `LastErrorMessage`         | `string`    | 来自上一个错误的人类可读消息                   |
| `SessionUptime`            | `TimeSpan?` | 自当前会话连接以来经过的时间； `null` 断开连接时     |
| `RegisteredCharacterCount` | `int`       | `ConvaiCharacter` 当前在代理注册表中注册的实例 |
| `RegisteredPlayerCount`    | `int`       | `ConvaiPlayer` 当前已注册的实例          |

{% hint style="info" %}
`DiagnosticsCoordinator` 是 `null` 直到房间的内部程序集被创建后才会可用，这发生在第一次连接尝试时。调用前请先进行空值检查 `GetDiagnostics()`.
{% endhint %}

### 会话指标控制台消息

`SessionMetrics` 将会话生命周期事件记录到控制台。某些消息会显示在 **信息** 级别（默认可见）；其他消息需要将全局日志级别设置为 **调试**.

| 消息                                      | 级别 | 出现时                                                               |
| --------------------------------------- | -- | ----------------------------------------------------------------- |
| `[SessionMetrics] 指标已重置`                | 调试 | 指标已通过程序重置                                                         |
| `[SessionMetrics] 会话已开始`                | 调试 | 初始连接尝试开始（房间从 Disconnected 过渡到 Connecting）                         |
| `[SessionMetrics] 已连接 - 开始持续时间计时器`      | 调试 | 初始连接到达 Connected 状态；开始会话在线时长计时器（重新连接将触发“Reconnection successful”） |
| `[SessionMetrics] 重新连接尝试 #N`            | 调试 | 每次重新连接尝试开始                                                        |
| `[SessionMetrics] 重新连接成功（尝试 #N，成功率：P%）` | 信息 | 一次重新连接尝试已成功                                                       |
| `[SessionMetrics] 重新连接失败（错误：X）`         | 警告 | 一次重新连接尝试已失败                                                       |
| `[SessionMetrics] 会话错误：X`               | 警告 | 记录了一次非重新连接的会话错误                                                   |
| `[SessionMetrics] 会话已结束（原因）：{snapshot}` | 信息 | 会话因任何原因终止；快照包含完整指标                                                |

{% hint style="info" %}
`[SessionMetrics]` 标记为 Debug 的消息仅会显示在 Unity 编辑器、开发构建，或带有 `CONVAI_DEBUG_LOGGING` scripting define。请参见 [日志级别](#log-levels) 上文。
{% endhint %}

### 客户端延迟指标

`ClientLatencyMetricsCollector` 衡量会话管线的端到端延迟，即从玩家停止说话的那一刻到角色音频开始播放的那一刻。它在 Unity 编辑器和开发构建中处于激活状态。

每次完成一个回合后，延迟条目会自动出现在控制台中：

```
[ClientLatency] 玩家：stop→finalTranscript=120ms | 角色：stop→firstTranscript=450ms stop→ttsStarted=520ms stop→firstLipSync=600ms stop→audioPlaying=650ms (audioHoldForLipSync=130ms)
```

#### 延迟分段参考

| 分段                     | 测量内容                             |
| ---------------------- | -------------------------------- |
| `stop→finalTranscript` | 从玩家停止说话到最终玩家转录到达客户端              |
| `stop→firstTranscript` | 从玩家停止说话到第一个角色转录词元到达              |
| `stop→ttsStarted`      | 从玩家停止说话到 Convai 开始文本转语音合成        |
| `stop→firstLipSync`    | 从玩家停止说话到第一帧唇形同步数据到达              |
| `stop→audioPlaying`    | 从玩家停止说话到角色音频实际开始播放 `AudioSource` |
| `audioHoldForLipSync`  | TTS 开始与音频播放之间的差值——播放开始前的音频缓冲填充时长 |

#### 解读数值

| 较高的分段值                                       | 可能原因                    |
| -------------------------------------------- | ----------------------- |
| `stop→firstTranscript` > 500 ms              | 到 Convai 的网络延迟；检查连接质量   |
| `stop→ttsStarted` 远高于 `stop→firstTranscript` | Convai 处理时间；复杂响应时这是预期内的 |
| `audioHoldForLipSync` > 200 ms               | 音频缓冲较大；可接受，但会降低感知响应速度   |
| `stop→audioPlaying` > 1000 ms                | 网络 + 处理 + 缓冲综合延迟；请逐段排查  |

{% hint style="info" %}
延迟测量仅会出现在编辑器和开发构建中—— `[ClientLatency]` 日志调用是条件编译的。它们在发布构建中不可用，除非 `CONVAI_DEBUG_LOGGING` 已定义。
{% endhint %}

### LipSync 漂移监视器

SDK `4.4.0` 已移除公开的 `IBlendshapeSink` 扩展接缝，SDK 中不再存在该名称的类型，并且不再支持自定义运行时 sink 注入。请改为通过受支持的映射或配置文件在 `ConvaiLipSyncComponent` 上驱动唇形同步。相关类型 `SkinnedMeshBlendshapeSink`, `LipSyncDriftMonitor`, `LipSyncDriftSample`，以及 `LipSyncDriftEvent` 并未被移除——它们已被内部化，因此仍然存在，但不再是公开 API 的一部分。唇形同步对齐的受支持诊断界面是 `Convai → LipSync 漂移监视器` 编辑器窗口。

从 `Convai → LipSync 漂移监视器`打开该窗口。监视为可选：启用 **Monitor** 切换开关，进入播放模式，并与角色对话以生成数据。当注册的角色超过一个时，请从下拉菜单中选择角色——样本和事件会按角色分别跟踪。

该窗口显示：

* 实时漂移误差，单位为毫秒，介于测得的音频播放头和视觉（blendshape）时钟之间；正值表示嘴部落后于音频
* 一张图表，绘制漂移误差和监视器的累计校正，时间窗口可配置（3-30 秒），并叠加生命周期事件标记
* 在可见窗口内计算的平均绝对误差、最大绝对误差和校正率
* 用于按视觉感知校准同步的实时音频/视觉偏移覆盖滑块
* 事件日志，列出生命周期事件（例如 gate open、anchor、cancel）及其时间戳

点击 **导出 CSV** 以将当前样本和事件保存到文件。保存对话框标题为 **导出漂移样本**，默认文件名为 `lipsync-drift-<characterId>.csv`。导出的文件包含一个样本表，列为 `time_s`, `error_ms`, `audio_target_s`, `visual_clock_s`, `cumulative_correction_ms`, `buffered_s`, `headroom_s`, `state`，以及 `audio_active`，随后是一个空行和一个事件表，列为 `event_time_s` 和 `label`.

### 快速参考

| 工具                         | 诊断内容                    | 访问方式                                                  |
| -------------------------- | ----------------------- | ----------------------------------------------------- |
| **诊断**                     | 所有 SDK 子系统——详细程度与过滤     | `Convai → 设置` 或 `编辑 → 项目设置 → Convai SDK` （诊断部分）       |
| **ConvaiActionDebugProbe** | 操作分发、执行器连接、批处理失败        | 添加组件 → Convai/Debug/Convai Action Debug Probe         |
| **ConvaiRoomManager 属性**   | 会话状态、错误代码、连接/重新连接次数     | `FindFirstObjectByType<ConvaiRoomManager>()` — 直接读取属性 |
| **IRoomDiagnostics 快照**    | 连接尝试次数、在线时长、最后错误、代理数量   | `room.DiagnosticsCoordinator.GetDiagnostics()`        |
| **Session Metrics 消息**     | 重新连接成功率、会话生命周期、错误时间线    | 控制台过滤器 `[SessionMetrics]`；需要 Info 或 Debug 级别          |
| **客户端延迟指标**                | 端到端会话管线延迟               | 控制台过滤器 `[ClientLatency]`；仅限编辑器和开发构建                   |
| **LipSync 漂移监视器**          | 音频与视觉唇形同步对齐、漂移误差、CSV 导出 | `Convai → LipSync 漂移监视器`                              |
| **自定义日志接收器**               | 将日志路由到文件、遥测或叠加层         | `ConvaiLogger.RegisterSink(new YourSink())`           |

### 后续步骤

有关特定平台问题——WebGL AudioContext 解锁、Android 麦克风处理，或平台构建设置——请参见平台指南部分。

{% content-ref url="/pages/c548f38700d19163b7037cf3152f210077f0967b" %}
[平台指南](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/platform-guides.md)
{% endcontent-ref %}

有关功能特定的诊断工具，请参见各功能部分内的故障排除页面。Actions、Emotion、Vision 和 Narrative Design 功能都提供了比此处更详细的决策树和控制台日志参考。


---

# 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/troubleshooting/debug-tools-reference.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.
