> 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/advanced-topics/performance-and-optimization.md).

# 日志、指标与重试策略

Convai Unity SDK 附带默认配置，可适用于训练模拟、交互式体验和游戏。当你需要诊断延迟、减少生产环境中的日志噪声、了解 AI 管线的时序，或调整重连行为时——本页涵盖可用的控制项。

**跳转到你需要的主题：**

* [日志级别配置](#log-level-configuration) — 减少生产构建中的控制台噪声
* [自定义日志接收器](#custom-log-sinks) — 将日志路由到文件或远程服务
* [RTVI 服务器端调试指标](#rtvi-server-side-debug-metrics) — 按轮次测量 AI 管线延迟
* [重试策略](#retry-policy) — 为不稳定网络调整重连行为

***

### 日志级别配置

#### 日志级别

| 级别   | 枚举值 | 适用场景             |
| ---- | --- | ---------------- |
| `关闭` | `0` | 禁用所有 SDK 日志。     |
| `错误` | `1` | 生产构建——仅错误。       |
| `警告` | `2` | 预发布——警告和错误。      |
| `信息` | `3` | **默认。** 正常运行消息。  |
| `调试` | `4` | 主动开发——详细的生命周期事件。 |
| `跟踪` | `5` | 深度调试——高频内部状态。    |

这些值对应于 `Convai.Domain.Logging.LogLevel` 枚举。

{% hint style="warning" %}
`跟踪` 是位于 `调试` 级别会产生大量输出。SDK 调试日志在 Unity 编辑器和开发构建中处于激活状态。在发布构建中，除非 `CONVAI_DEBUG_LOGGING` 被添加到 Scripting Define Symbols 中，否则会被编译器剥离。请不要在 `跟踪` 启用的情况下发布。
{% endhint %}

#### 在 Inspector 中配置

打开 **项目设置 → Convai** （或选择 `ConvaiSettings` 资源资产 `Assets/Resources/ConvaiSettings.asset`）。在 **Logging** 部分下：

| 字段       | 说明                             |
| -------- | ------------------------------ |
| `全局日志级别` | 所有 SDK 子系统的最低级别。               |
| `包含堆栈跟踪` | 为 Warning 和 Error 消息附加堆栈跟踪。    |
| `彩色输出`   | 在 Unity 控制台中对日志消息进行颜色编码。       |
| `类别覆盖`   | 按子系统覆盖级别——添加条目，为特定类别设置更细粒度的级别。 |

#### 日志类别

| 类别          | 子系统                    |
| ----------- | ---------------------- |
| `SDK`       | 通用操作                   |
| `角色`        | 按角色会话生命周期              |
| `音频`        | 麦克风采集、播放、音频设备          |
| `Transport` | WebRTC / WebSocket 连接层 |
| `事件`        | 内部事件总线                 |
| `转录`        | 转录处理和路由                |
| `叙事`        | 叙事设计系统                 |
| `LipSync`   | 唇形同步处理和 blendshape 播放  |
| `视觉`        | 摄像头捕获和视频发布             |
| `引导`        | SDK 启动和初始化             |
| `UI`        | UI 组件生命周期              |
| `REST`      | REST API 通信            |
| `玩家`        | 玩家身份和输入                |
| `编辑器`       | 仅编辑器操作                 |

这些值对应于 `Convai.Domain.Logging.LogCategory` 枚举。

#### 在运行时配置

```csharp
using Convai.Runtime;
using Convai.Domain.Logging;

// 设置全局级别——会立即应用于后续所有日志调用。
ConvaiSettings.Instance.SetGlobalLogLevel(LogLevel.Debug);

// 覆盖特定子系统，而不改变全局级别。
ConvaiSettings.Instance.SetCategoryOverrides(new[]
{
    new LogLevelOverride(LogCategory.Audio,     LogLevel.Debug),
    new LogLevelOverride(LogCategory.Transport, LogLevel.Warning)
});
```

`LogLevelOverride` 是一个包含两个字段的结构体： `LogCategory Category` 是位于 `LogLevel Level`.

#### `CONVAI_DEBUG_LOGGING` 脚本定义

`ConvaiLogger.Debug(...)` 调用受三个 `[Conditional]` 属性保护： `UNITY_EDITOR`, `DEVELOPMENT_BUILD`，以及 `CONVAI_DEBUG_LOGGING`。如果这些符号都未定义，编译器会将其从任何构建中剥离——在生产发布构建中零开销。

要在发布构建中启用调试日志：添加 `CONVAI_DEBUG_LOGGING` 到 **项目设置 → Player → Scripting Define Symbols**。开发构建会自动启用它们，无需此定义。

***

### 自定义日志接收器

通过实现以下接口，将 SDK 日志输出路由到文件、远程日志服务或任何目标： `ILogSink` 并将其注册到 `ConvaiLogger`.

```csharp
// FileSink.cs
using System;
using System.IO;
using Convai.Domain.Logging;

public class FileSink : ILogSink
{
    private readonly StreamWriter _writer;

    public string Name      => "FileSink";
    public bool   IsEnabled { get; private set; } = true;

    public FileSink(string path)
        => _writer = new StreamWriter(path, append: true);

    public void Write(LogEntry entry)
        => _writer.WriteLine($"[{entry.Timestamp:HH:mm:ss.fff}] [{entry.Level}] [{entry.Category}] {entry.Message}");

    public void Flush()             => _writer.Flush();
    public void SetEnabled(bool en) => IsEnabled = en;
    public void Dispose()           => _writer?.Dispose();
}
```

在以下之后注册 `ConvaiLogger` 初始化：

```csharp
using Convai.Runtime.Logging;

// 当 ConvaiLogger 就绪时会触发 OnInitializationCompleted——务必在这里注册 sink，
// 切勿更早，以避免错过初始化窗口。
ConvaiLogger.OnInitializationCompleted += _ =>
{
    string logPath = Path.Combine(Application.persistentDataPath, "convai_session.log");
    ConvaiLogger.RegisterSink(new FileSink(logPath));
};
```

**Sink 生命周期：**

* `ILogSink` 扩展 `IDisposable` — 实现类必须提供一个 `Dispose()` 方法。
* 调用 `ConvaiLogger.UnregisterSink(sink)` 在释放 sink 之前调用。
* 调用 `ConvaiLogger.FlushAllSinks()` 应在应用退出时调用，以确保缓冲的条目被写入。

***

### RTVI 服务器端调试指标

RTVI（Real-Time Voice Interaction，实时语音交互）是 Convai 的会话协议。当 `ConvaiRoomManager.Debug` 为 `true`启用时，服务器会在每次 AI 回合后将管线指标流式传输到客户端，显示每个阶段（语音转文本、LLM、文本转语音等）花费的时间。当你需要定位管线中的瓶颈阶段时使用此项——例如，AI 响应慢是因为转录延迟还是 LLM 生成时间？

#### 启用调试指标

`ConvaiRoomManager.Debug` 是一个只读运行时属性。通过勾选 **调试** 复选框 `ConvaiRoomManager` 组件上的

{% hint style="warning" %}
在进入播放模式之前于 Inspector 中。调试指标会给每回合增加少量开销。仅在性能分析会话中勾选此选项——不要在启用状态下发布。
{% endhint %}

#### 读取指标

```csharp
// MetricsLogger.cs
using Convai.Runtime.Adapters.Networking;
using Convai.Infrastructure.Protocol.Messages;
using UnityEngine;

public class MetricsLogger : MonoBehaviour
{
    [SerializeField] private ConvaiRoomManager _roomManager;

    private void OnEnable()
        => _roomManager.OnRtvMetricsReceived += OnMetricsReceived;

    private void OnDisable()
        => _roomManager.OnRtvMetricsReceived -= OnMetricsReceived;

    private void OnMetricsReceived(RTVIMetricsPayload payload)
    {
        if (payload.Ttfb       != null) Debug.Log($"[指标] TTFB: {payload.Ttfb}");
        if (payload.Processing != null) Debug.Log($"[指标] Processing: {payload.Processing}");
        if (payload.Custom     != null) Debug.Log($"[指标] Custom: {payload.Custom}");
    }
}
```

#### 指标字段

| 字段           | 类型        | 说明                                                                         |
| ------------ | --------- | -------------------------------------------------------------------------- |
| `Ttfb`       | `JToken?` | 每个处理器的首字节时间（秒）。包含 `{processor, value}` 条目的数组。                              |
| `Processing` | `JToken?` | 本次回合每个处理器的总处理时长（秒）。                                                        |
| `自定义`        | `JToken?` | 特定提供方的指标——格式因处理器而异（例如， `output_fps`, `blendshapes_received` 来自 NeuroSync）。 |

`Ttfb` 是位于 `Processing` 是原始 `JToken` 值（来自 `Newtonsoft.Json.Linq`，通过以下方式包含在 Unity 中： `com.unity.nuget.newtonsoft-json` 包）。将它们反序列化为 `payload.Ttfb.ToObject<List<MetricEntry>>()` 其中 `MetricEntry` 是一个与该 `{processor, value}` 形状匹配的结构体。并非每一轮都会包含所有指标类型——访问前务必进行空值检查。

***

### 重试策略

SDK 使用 `ExponentialBackoffPolicy` 来处理瞬时连接失败。默认行为：

| 尝试    | 重试前的延迟 |
| ----- | ------ |
| 1（初始） | 0 秒    |
| 2     | 1 秒    |
| 3     | 2 秒    |
| 4     | 4 秒    |

在总共 4 次尝试后（1 次初始 + 3 次重试），操作失败。该策略只会重试瞬时错误：超时、网络中断，以及 `OperationCanceledException`。非瞬时错误（身份验证失败、配置无效）会立即失败。

#### 何时自定义重试策略

默认策略（4 次尝试、指数退避，最长 4 秒）可覆盖大多数普通网络条件。在以下情况下考虑自定义策略：

* **部署在工业或嵌入式硬件上** 且已知会间歇性连接——增加 `MaxAttempts` 和延迟步长。
* **自助终端或常开安装** ——更激进的重试（更多尝试、更长的最大延迟）可避免在短暂网络中断后需要用户干预。
* **低延迟训练环境** 且网络可靠——减少 `MaxAttempts` 以快速失败，并将错误暴露到你自己的重连 UI，而不是静默重试。

如果你只需要更多尝试而不改变延迟曲线，请增加 `maxRetryAttempts` 在 `ConvaiBootstrapConfigSnapshot` 中——无需自定义策略。

#### 自定义重试策略

实现 `IRetryPolicy` 并使用 `RetryExecutor` 来包装任何异步操作：

```csharp
// AggressiveRetryPolicy.cs
using System;
using Convai.Runtime.Core.Policies;

public class AggressiveRetryPolicy : IRetryPolicy
{
    public int MaxAttempts => 6;

    public TimeSpan GetDelay(int attempt) => attempt switch
    {
        0 => TimeSpan.Zero,
        1 => TimeSpan.FromSeconds(2),
        2 => TimeSpan.FromSeconds(5),
        3 => TimeSpan.FromSeconds(10),
        4 => TimeSpan.FromSeconds(20),
        _ => TimeSpan.FromSeconds(30)
    };

    public bool ShouldRetry(Exception exception, int attempt)
    {
        if (attempt >= MaxAttempts) return false;
        return exception is TimeoutException
            || exception is System.Net.WebException
            || exception is OperationCanceledException;
    }
}
```

```csharp
using Convai.Runtime.Core.Policies;

var executor = new RetryExecutor(new AggressiveRetryPolicy());

await executor.ExecuteAsync(async (attempt, ct) =>
{
    Debug.Log($"连接尝试 {attempt + 1}...");
    await someAsyncOperation(ct);
    return true;
}, cancellationToken);
```

***

### 生产检查清单

* [ ] `全局日志级别` 设置为 `警告` 或 `错误` 在生产 `ConvaiSettings` 资源资产中。
* [ ] `CONVAI_DEBUG_LOGGING` **不是** 存在于发布构建的 Scripting Define Symbols 中。
* [ ] `ConvaiRoomManager.Debug` 复选框 **未勾选** 在生产场景中。
* [ ] `maxRetryAttempts` 在 `ConvaiBootstrapConfigSnapshot` 适合你的目标网络（默认 `3` 适用于大多数普通消费者连接）。
* [ ] `connectionTimeoutSeconds` 考虑到了你部署中最慢的目标设备/网络（默认 `30` 秒）。
* [ ] 自定义 `ILogSink` 实例会在应用退出时注销并释放。
* [ ] `ConvaiLogger.FlushAllSinks()` 在以下情况下调用 `Application.quitting` 处理程序，如果任何 sink 会缓冲输出。

***

### 故障排查

| 症状                          | 可能原因                                                           | 修复                                                                              |
| --------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| 发布构建中控制台被 SDK 消息刷屏          | `CONVAI_DEBUG_LOGGING` 定义已存在，或者 `GlobalLogLevel` 为 `调试` 或 `跟踪` | 移除脚本定义，并将全局级别设置为 `警告` 或 `错误`.                                                   |
| `OnRtvMetricsReceived` 从不触发 | `ConvaiRoomManager.Debug` 未勾选，或者角色尚未完成完整回合                     | 启用 Debug 复选框，并验证完整 AI 回合已完成（用户发言、角色响应）。                                         |
| `Ttfb` / `Processing` 字段为空  | 服务器没有为此回合包含这些指标类型                                              | 访问前先处理空值。并非每一轮都会包含所有指标类型。                                                       |
| 连接在 4 次尝试后失败且不再重试           | 默认值 `ExponentialBackoffPolicy` 已达到 `MaxAttempts = 4`           | 增加 `maxRetryAttempts` 在 `ConvaiBootstrapConfigSnapshot`，或者实现自定义 `IRetryPolicy`. |
| 自定义日志 sink 缺少早期启动消息         | sink 在以下之后注册 `ConvaiLogger.Initialize()` 已经触发                  | 通过 `OnInitializationCompleted` 回调注册——在初始化完成前切勿注册 sink。                          |

***

### 下一步

{% content-ref url="/pages/aed151614a7019028126be07590213a17602a2f2" %}
[故障排查](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/troubleshooting.md)
{% endcontent-ref %}

{% content-ref url="/pages/007b237f8ee6f8a26b307152ee716a33905cd6da" %}
[运行时模块系统](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/advanced-topics/extending-the-sdk.md)
{% endcontent-ref %}

{% content-ref url="/pages/c94f3576730a705ef8d7adec01d712a12612b542" %}
[实现自定义模块](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/advanced-topics/implement-a-custom-module.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:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/advanced-topics/performance-and-optimization.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.
