> 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)，打开该角色，并确认 **Memory** 选项卡显示 **长期记忆：已开启**。记忆默认是关闭的——这是 LTM 看起来不起作用的最常见原因。

**2. 验证终端用户 ID 在各个会话中保持稳定**

将下面的诊断脚本添加到你的场景中。运行播放模式，停止，然后再次运行。两次记录的 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 this session: {id}");
    }
}
```

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

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

检查控制台中的 Convai 启动消息。连接失败（无效的 API 密钥、网络错误）会阻止所有 LTM 操作，无论存储的数据如何。

***

### 决策流程

在记忆没有持久化时使用此流程：

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

***

### 常见问题

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

***

### 运行时诊断

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

使用此脚本确认服务器已存储的内容。通过在 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("List Memories")]
    private async void ListMemories()
    {
        var provider = new DeviceEndUserIdProvider();
        string endUserId = provider.GetEndUserId();

        Debug.Log($"[LTM] Querying memories for 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 {page} — {response.Memories.Count} records (total: {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] No memory records found for this user–character pair.");
    }
}
```

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

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

```csharp
[ContextMenu("Check Memory Enabled")]
private async void CheckMemoryEnabled()
{
    using var client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);

    bool isEnabled = await client.Characters.GetMemoryEnabledAsync(_characterId);
    Debug.Log($"[LTM] Memory enabled for character '{_characterId}': {isEnabled}");
}
```

***

### API 错误代码

所有 Memory Management API 错误都会抛出 `ConvaiRestException`.  `StatusCode` 属性对应下面的原因。

| HTTP 状态       | 原因                                                  | 修复方法                                                                                                                   |
| ------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `400 错误请求`    | 缺少必需参数（`character_id`, `end_user_id`，或者 `memories`) | 在调用之前，请验证所有必需参数都非空且非空字符串                                                                                               |
| `401 未授权`     | API 密钥无效或缺失                                         | 重新配置你的 API 密钥——参见 [配置 API 密钥](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/getting-started/configure-api-key.md) |
| `403 禁止访问`    | 该角色不属于拥有该 API 密钥的账户                                 | 确认该角色 ID 属于与你的 API 密钥关联的账户                                                                                             |
| `404 未找到`     | 无效的角色 ID、终端用户 ID 或记忆 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("Unreachable.");
    }
}
```

***

### 下一步

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