> 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 的会话指标，以及用于对话流水线分析的客户端延迟测量。此页是它们的完整参考。

### 日志配置

#### 配置位置

打开 **编辑 → 项目设置 → Convai SDK → 日志**。控制哪些内容显示在 Unity Console 中的设置如下：

* **全局日志级别** — 适用于所有日志类别的最低详细程度
* **类别覆盖** — 按子系统覆盖并优先于全局级别
* **包含堆栈跟踪** — 是否让 Warning 和 Error 条目包含堆栈跟踪
* **彩色输出** — 日志条目是否在 Unity Console 中以颜色区分

#### 日志级别

该 SDK 使用五个日志级别。数值越高，输出越详细。

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

默认是 **信息**。在调查问题期间切换到 **调试** 会产生显著更多的输出——在发布到生产环境前请将其禁用。

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

要在生产构建中启用 Debug 消息，请添加 `CONVAI_DEBUG_LOGGING` 到 **编辑 → 项目设置 → Player → Scripting Define Symbols**.

#### 日志类别覆盖

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

1. 前往 **编辑 → 项目设置 → Convai SDK → 日志 → 类别覆盖**
2. 单击 **+** 以添加一个覆盖
3. 设置 **类别** 到 `Transport` 是位于 **级别** 到 `调试`

其他所有类别都保持全局级别。

#### 日志类别参考

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

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

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

`ILogSink` ，即可将 SDK 日志条目转发到自定义目标——文件、遥测服务或游戏内调试覆盖层——需要以下成员：

| 成员                              | 说明                         |
| ------------------------------- | -------------------------- |
| `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 功能的主要诊断工具。它订阅每个分发器事件，并直接在 Inspector 中显示实时计数器和最近一次看到的动作数据——无需自定义日志。

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

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

#### Inspector 字段

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

#### 上下文菜单操作

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

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

{% hint style="info" %}
**注入测试批次** 是验证执行器连线的最快方式。如果成功，你的动作定义、对象目标和执行器都已正确配置。如果 `未处理步骤数` 增加而不是 `成功步骤数`，则 `Move To` 的执行器未在此 GameObject 上注册。
{% endhint %}

### ConvaiRoomManager 运行时状态

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

#### 公共状态属性

| 属性                        | 类型             | 说明                                                                                       |
| ------------------------- | -------------- | ---------------------------------------------------------------------------------------- |
| `CurrentState`            | `SessionState` | 当前会话状态： `Disconnected`, `Connecting`, `Connected`, `Disconnecting`, `Reconnecting`, `错误` |
| `IsConnected`             | `bool`         | `true` 当房间处于活动连接状态时                                                                      |
| `ConnectAttemptCount`     | `int`          | 自场景加载以来的总连接尝试次数                                                                          |
| `ReconnectCount`          | `int`          | 自场景加载以来的总重连尝试次数                                                                          |
| `LastSessionErrorCode`    | `string`       | 最近一次错误事件中的错误代码                                                                           |
| `LastSessionErrorMessage` | `string`       | 最近一次错误的可读消息                                                                              |

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

#### IRoomDiagnostics 完整快照

如需更丰富的快照，请调用 `GetDiagnostics()` on `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`       | 达到 Connected 状态的尝试               |
| `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` 将会话生命周期事件记录到 Console。某些消息会以 **信息** 级别显示（默认可见）；其他消息则要求将全局日志级别设置为 **调试**.

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

{% hint style="info" %}
`[SessionMetrics]` 标记为 Debug 的消息仅会出现在 Unity 编辑器、开发构建，或包含 `CONVAI_DEBUG_LOGGING` 脚本定义时。见 [日志级别](#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 毫秒              | 到 Convai 的网络延迟；检查连接质量   |
| `stop→ttsStarted` 远高于 `stop→firstTranscript` | Convai 处理时间；复杂回复时属于预期情况 |
| `audioHoldForLipSync` > 200 毫秒               | 音频缓冲较大；可以接受，但会降低感知响应速度  |
| `stop→audioPlaying` > 1000 毫秒                | 网络 + 处理 + 缓冲的综合；逐段排查    |

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

### 快速参考

| 工具                         | 它诊断什么                 | 如何访问                                                  |
| -------------------------- | --------------------- | ----------------------------------------------------- |
| **日志配置**                   | 所有 SDK 子系统——详细程度和过滤   | 编辑 → 项目设置 → Convai SDK → 日志                           |
| **ConvaiActionDebugProbe** | 动作分发、执行器接线、批处理失败      | 添加组件 → Convai/Debug/Convai Action Debug Probe         |
| **ConvaiRoomManager 属性**   | 会话状态、错误代码、连接/重连次数     | `FindFirstObjectByType<ConvaiRoomManager>()` — 直接读取属性 |
| **IRoomDiagnostics 快照**    | 连接尝试次数、运行时长、最后错误、代理数量 | `room.DiagnosticsCoordinator.GetDiagnostics()`        |
| **Session Metrics 消息**     | 重连成功率、会话生命周期、错误时间线    | 控制台过滤器 `[SessionMetrics]`；需要 Info 或 Debug 级别          |
| **客户端延迟指标**                | 端到端对话流程延迟             | 控制台过滤器 `[ClientLatency]`；仅限编辑器和开发版                    |
| **自定义日志接收器**               | 将日志路由到文件、遥测或覆盖层       | `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:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/troubleshooting/debug-tools-reference.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.
