> 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/features/long-term-memory/troubleshooting-and-diagnostics.md).

# 排查长期记忆问题

诊断长期记忆为什么没有持久化，验证终端用户身份在会话间是否稳定，并解决 Memory Management API 的 HTTP 错误。

大多数 LTM 问题可归为三类：记忆无法在会话之间持久保存、错误的用户接收到记忆，以及 API 调用因 HTTP 错误而失败。在查阅参考表之前，请先按照下面的诊断流程进行排查。

***

### 首要排查

在检查其他任何内容之前，请按顺序执行以下三步：

**1. 确认角色已启用记忆**

在 [convai.com](https://convai.com)，打开角色，并确认 **记忆** 选项卡显示 **长期记忆：开启**。记忆默认是禁用的——这是 LTM 看起来不起作用的最常见原因。

**2. 验证 end-user ID 在各个会话之间保持稳定**

将下面的诊断脚本添加到你的场景中。运行 Play Mode，停止，然后再次运行。两次记录的 ID 必须完全相同。

```csharp
using Convai.Runtime.Identity;
using UnityEngine;

public class EndUserIdDebug : MonoBehaviour
{
    private void Start()
    {
        var provider = new DeviceEndUserIdProvider();
        string id = provider.GetEndUserId();
        Debug.Log($"[LTM] 本次会话的 end_user_id：{id}");
    }
}
```

如果会话之间的 ID 发生变化，记忆就无法累积——服务器会把每个新 ID 当作新用户。

**3. 确认会话已成功连接**

检查 Console 中的 Convai 启动消息。连接失败（无效的 API key、网络错误）会阻止所有 LTM 操作，不受已存储数据影响。

***

### 决策流程

当记忆无法持久保存时，请使用此流程：

```mermaid
graph TD
    A[角色控制台中的 LTM 已启用？] -->|否| B[在 convai.com 启用并重试]
    A -->|是| C[跨会话记录到的 end_user_id 相同？]
    C -->|否| D[身份来源在变化——参见常见问题]
    C -->|是| E[会话连接成功？]
    E -->|否| F[先解决连接问题——API key 或网络]
    E -->|是| G[对话是否包含值得记住的事实？]
    G -->|否| H[进行更有实质内容的对话并重试]
    G -->|是| I[向 Convai 支持提供角色 ID 和会话时间戳]
```

***

### 常见问题

| 症状                      | 可能原因                                     | 修复方法                                                                                                            |
| ----------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| 角色从不引用之前的会话             | 角色未启用 LTM                                | 在角色的 Memory 选项卡中启用，位置： [convai.com](https://convai.com)                                                         |
| 记忆在编辑器中可用，但在构建中不可用      | `DeviceEndUserIdProvider` 在编辑器与构建中返回不同的值 | 预期行为——编辑器使用 `PlayerPrefs` GUID；构建使用设备 ID。确保构建中的设备 ID 保持稳定。为了跨上下文一致性，请实现自定义 `IEndUserIdentityProvider`.          |
| 重新安装后记忆丢失               | `PlayerPrefs` 在重新安装时会被清除                 | 请使用通过自定义 provider 分配的服务器账号 ID。基于设备和基于 GUID 的 ID 都无法在重新安装后保留。                                                    |
| 同一设备上的不同用户共享记忆          | 多个用户共用一台设备                               | 每个用户都必须获得唯一的 `end_user_id`。如果 `DeviceEndUserIdProvider` 如果正在使用，则按设备范围的 GUID 会被共享。实现一个返回每个用户账号 ID 的自定义 provider。 |
| 自定义 provider 未生效        | Provider 在之后注册 `ConnectAsync`            | 在……中注册 `Awake()`，在……之前 `ConvaiRoomManager.Start()` 完成。如果 `ConvaiCharacter` 已勾选 **自动连接** 已启用， `Start()` 可能就太晚了。  |
| 记忆事实看起来有误或已过时           | 来自测试的过时记录                                | 使用 `client.Memory.DeleteAllAsync` 在开始新的评估前清除测试数据。                                                               |
| `GetEndUserId()` 返回空字符串 | 自定义 provider 返回 null 或空白                 | SDK 会在发送前将 null/空白规范化为 `null` ，这会导致匿名会话。请确保你的 provider 始终返回非空值。                                                 |

***

### 运行时诊断

#### 列出某个用户的所有记忆

使用此脚本确认服务器已存储的内容。可在 Inspector 中右键 → **列出记忆**.

```csharp
using Convai.RestAPI;
using Convai.RestAPI.Internal;
using Convai.Runtime;
using Convai.Runtime.Identity;
using UnityEngine;

public class MemoryDiagnostic : MonoBehaviour
{
    [SerializeField] private string _characterId;

    [ContextMenu("列出记忆")]
    private async void ListMemories()
    {
        var provider = new DeviceEndUserIdProvider();
        string endUserId = provider.GetEndUserId();

        Debug.Log($"[LTM] 正在查询 end_user_id 为 {endUserId} 的记忆");

        using var client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);

        int page = 1;
        bool hasMore = true;
        int totalRecords = 0;

        while (hasMore)
        {
            var response = await client.Memory.ListAsync(_characterId, endUserId, page);

            Debug.Log($"[LTM] 第 {page} 页 — {response.Memories.Count} 条记录（总计：{response.TotalCount}）");

            foreach (MemoryRecord record in response.Memories)
                Debug.Log($"  [{record.Id}] {record.Memory}");

            totalRecords += response.Memories.Count;
            hasMore = response.HasMore;
            page++;
        }

        if (totalRecords == 0)
            Debug.Log("[LTM] 未找到该用户–角色组合的记忆记录。");
    }
}
```

#### 检查记忆启用状态

以编程方式确认角色当前是否已启用 LTM。

```csharp
[ContextMenu("检查记忆是否启用")]
private async void CheckMemoryEnabled()
{
    using var client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);

    bool isEnabled = await client.Characters.GetMemoryEnabledAsync(_characterId);
    Debug.Log($"[LTM] 角色 '{_characterId}' 的记忆已启用：{isEnabled}");
}
```

***

### API 错误代码

所有记忆管理 API 错误都会抛出 `ConvaiRestException`。 `StatusCode` 属性对应下列原因。

| HTTP 状态       | 原因                                                 | 修复方法                                                                                                                    |
| ------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `400 错误请求`    | 缺少必需参数（`character_id`, `end_user_id`，或 `memories`) | 在调用前，请确保所有必需参数都非空且非 null。                                                                                               |
| `401 未授权`     | API key 无效或缺失                                      | 重新配置你的 API key——参见 [配置 API 密钥](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/getting-started/configure-api-key.md) |
| `403 禁止访问`    | 该角色不属于拥有该 API key 的账户                              | 确认角色 ID 属于与你的 API key 关联的账户                                                                                             |
| `404 未找到`     | 无效的角色 ID、end-user ID 或 memory ID                   | 对照 Convai 控制台再次检查 ID。如果按以下方式查询： `memoryId`，请从先前的 `ListAsync` 调用。                                                        |
| `429 请求过多`    | 超过速率限制                                             | 实现指数退避。例如：在以下时间后重试 `2^attempt` 秒，并使用 `CancellationToken` 以便在达到最大重试次数后中止。                                                |
| `500 服务器内部错误` | 临时性服务器错误                                           | 在短暂延迟后重试请求。如果错误仍然存在，请查看 Convai 状态页面，并联系支持团队，提供你的角色 ID 和时间戳。                                                             |

#### 指数退避模式

```csharp
using System;
using System.Threading;
using System.Threading.Tasks;
using Convai.RestAPI;

public static class MemoryRetryHelper
{
    public static async Task<T> WithRetry<T>(
        Func<Task<T>> operation,
        int maxAttempts = 3,
        CancellationToken cancellationToken = default)
    {
        for (int attempt = 0; attempt < maxAttempts; attempt++)
        {
            try
            {
                return await operation();
            }
            catch (ConvaiRestException ex) when (ex.StatusCodeInt == 429 || ex.StatusCodeInt == 500)
            {
                if (attempt == maxAttempts - 1) throw;

                int delayMs = (int)Math.Pow(2, attempt) * 1000;
                await Task.Delay(delayMs, cancellationToken);
            }
        }

        throw new InvalidOperationException("无法到达此处。");
    }
}
```

***

### 下一步

{% content-ref url="/pages/9c55a31cd790424665e521972bff5affc2dbe7f9" %}
[长期记忆脚本参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/long-term-memory/long-term-memory-scripting-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/b54beab8714c42a31356b3abe240c02692f3d79b" %}
[为角色配置记忆](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/long-term-memory/configure-memory-for-a-character.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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/long-term-memory/troubleshooting-and-diagnostics.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
