> 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/unity-plugin-beta-overview/features/long-term-memory/usage-examples.md).

# 使用示例

四个常见长期记忆场景的完整可运行代码：自动持久化、账户范围身份、首次会话前的记忆种子和定向重置。

## 长时记忆实战

本页提供了最常见的长时记忆场景所需的完整可运行代码。每个示例都是自包含的，可以直接放入 MonoBehaviour 中。这些场景使用了安全培训模拟的上下文——由一名导师 NPC 引导受训者完成流程评估——但同样的模式也适用于任何应用领域。

## 示例 1：零配置自动持久化

长时记忆适用于大多数项目，开箱即用。无需编写脚本。

**场景：** 一个消防安全培训模块，其中导师角色会记住每位受训者的姓名以及两次会话之间的进度。

**操作步骤：**

{% stepper %}
{% step %}
**为角色启用记忆**

在 Convai 控制台中，进入你的角色 → **记忆 → 记忆设置** → 启用 **长时记忆**。请参阅 [为角色启用记忆](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/long-term-memory/enabling-memory-on-characters.md) 以查看完整步骤。
{% endstep %}

{% step %}
**像平常一样搭建场景**

添加 `ConvaiManager` 和 `ConvaiCharacter` 将其添加到你的场景中，并使用有效的角色 ID。长时记忆无需额外的组件或配置。
{% endstep %}

{% step %}
**运行模拟**

进入播放模式。 `DeviceEndUserIdProvider` 该系统会在每次会话连接时自动发送稳定的设备标识符。首次对话后，后端会存储其提取的所有事实。在每次后续会话中，这些记忆会在第一条回复之前注入。
{% endstep %}
{% endstepper %}

**无需实现任何内容。** 身份提供程序会自动注册，会话连接流程会发送 `end_user_id` 无需任何额外设置。

{% hint style="info" %}
在 Unity 编辑器中，所有播放模式会话共享一个 `end_user_id` —— 一个持久化在 `PlayerPrefs`中的 GUID。启动播放模式，进行对话，停止后再启动一次，以验证角色是否记得之前的交流。
{% endhint %}

***

## 示例 2：面向已认证用户的自定义身份提供程序

**场景：** 一个企业入职模拟场景，员工使用公司凭据登录。记忆必须跟随账户而不是设备，因此当员工更换机器时，体验也会随之迁移。

```csharp
using System;
using System.Collections.Generic;
using Convai.Domain.Identity;
using Convai.Runtime.Components;
using UnityEngine;

// --- 身份提供程序 ---
// 返回服务器分配的账户 ID，它在不同设备和重新登录之间保持稳定。

public class EmployeeIdentityProvider : IEndUserIdentityProvider
{
    private readonly string _accountId;

    public EmployeeIdentityProvider(string accountId)
    {
        _accountId = accountId;
    }

    public string GetEndUserId() => _accountId;
}

// --- 元数据提供程序 ---
// 将姓名和部门附加到终端用户记录。
// “name” 键会显示在编辑器的长时记忆选项卡中。

public class EmployeeMetadataProvider : IEndUserMetadataProvider
{
    private readonly string _name;
    private readonly string _department;

    public EmployeeMetadataProvider(string name, string department)
    {
        _name = name;
        _department = department;
    }

    public IReadOnlyDictionary<string, object> GetEndUserMetadata()
    {
        return new Dictionary<string, object>
        {
            { "name", _name },
            { "department", _department }
        };
    }
}

// --- 场景设置 ---

public class OnboardingSessionSetup : MonoBehaviour
{
    [SerializeField] private ConvaiManager _convaiManager;

    // 在你的身份验证层解析出已登录员工后调用此方法。
    // 必须在 ConvaiManager 打开会话之前调用。
    public void OnEmployeeSignedIn(string accountId, string fullName, string department)
    {
        _convaiManager.SetEndUserIdentityProvider(
            new EmployeeIdentityProvider(accountId));

        _convaiManager.SetEndUserMetadataProvider(
            new EmployeeMetadataProvider(fullName, department));
    }
}
```

**要点：**

* `SetEndUserIdentityProvider` 和 `SetEndUserMetadataProvider` 必须在 **之前** 第一次会话连接之前调用。若在会话已打开后再调用，则不会生效，直到会话重新启动。
* 请将认证系统分配的账户 ID 作为 `accountId`。避免使用电子邮件地址或显示名称——它们可能会变化。
* 若要读取 `end_user_id` 你的提供程序返回的内容（用于 `MemoryService`），请调用 `provider.GetEndUserId()` 并直接在同一个提供程序实例上调用。

***

## 示例 3：在首次会话前预置记忆

**场景：** 一个危险品评估场景，在第一段对话开始之前，导师角色必须知道受训者之前的认证历史。

与其等待 AI 在多次会话中自然提取这些信息，不如在受训者进入体验之前以编程方式注入这些信息。

{% hint style="danger" %}
此示例直接调用 `ConvaiRestClient` 并使用你的 API 密钥。 **切勿将此代码随面向玩家的构建一起发布。** 该 API 密钥会被嵌入到应用中并可能被提取。请在服务器端脚本、编辑器工具或仅限管理员的构建中运行记忆预置。
{% endhint %}

```csharp
using System;
using System.Collections.Generic;
using System.Threading;
using Convai.RestAPI;
using Convai.RestAPI.Internal;
using UnityEngine;

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

    private ConvaiRestClient _client;

    private void Awake()
    {
        _client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);
    }

    private void OnDestroy()
    {
        _client?.Dispose();
    }

    // 在身份验证解析出受训者的账户 ID 后调用，
    // 在受训者进入模拟场景之前调用。
    public async void SeedTraineeHistory(
        string endUserId,
        TraineeCertificationRecord record,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var facts = new List<string>
            {
                $"受训者完成了第 1 模块（消防安全），得分为 {record.Module1Score}%。",
                $"受训者在灭火器分类（第 1 模块，第 4 题）上回答错误。",
                $"受训者已完成 {record.CompletedModules}/{record.TotalModules} 个必需模块。"
            };

            AddMemoriesResponse response = await _client.Memory.AddAsync(
                _characterId,
                endUserId,
                facts,
                metadata: null,
                cancellationToken);

            Debug.Log($"已为受训者 {endUserId} 预置了 {response.Memories.Count} 条记忆记录。");
        }
        catch (Exception e)
        {
            Debug.LogError($"[LTM] 预置记忆失败：{e.Message}");
        }
    }
}

[System.Serializable]
public class TraineeCertificationRecord
{
    public int Module1Score;
    public int CompletedModules;
    public int TotalModules;
}
```

**要点：**

* 该 `endUserId` 此处传入的 **必须与** 你当前会话的身份提供程序针对同一受训者返回的内容完全一致。如果你使用的是自定义提供程序（见示例 2），请使用相同的账户 ID。
* 服务器会对重叠事实进行去重。如果角色在之前的会话中已经拥有关于第 1 模块的记忆，那么返回的 `事件` 将会是 `"UPDATE"` 而不是 `"ADD"`.
* 若要获取当前 `end_user_id` 来自默认提供程序的值： `new DeviceEndUserIdProvider().GetEndUserId()` （在主线程上调用）。

***

## 示例 4：为全新的模拟运行重置用户

**场景：** 一个重新认证工作流，要求受训者像第一次一样重复完整的安全模块。必须清除所有先前的记忆，这样导师就不会引用之前的尝试。

```csharp
using System;
using System.Threading;
using Convai.RestAPI;
using Convai.RestAPI.Internal;
using UnityEngine;

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

    private ConvaiRestClient _client;

    private void Awake()
    {
        _client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);
    }

    private void OnDestroy()
    {
        _client?.Dispose();
    }

    // 仅清除此角色下该受训者的所有记忆。
    // 终端用户记录（身份和元数据）会被保留。
    public async void ResetTraineeMemory(
        string endUserId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            MemoryDeleteAllResponse response = await _client.Memory.DeleteAllAsync(
                _characterId,
                endUserId,
                cancellationToken);

            Debug.Log($"记忆重置完成——用户：{response.EndUserId}，角色：{response.CharacterId}。");
        }
        catch (Exception e)
        {
            Debug.LogError($"[LTM] 重置记忆失败：{e.Message}");
        }
    }

    // 删除终端用户记录以及所有角色中的所有记忆。
    // 仅在彻底停用受训者账户时使用。
    public async void DeleteTraineeRecord(
        string endUserId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            EndUserDeleteResponse response = await _client.EndUsers.DeleteAsync(
                endUserId,
                cancellationToken);

            Debug.Log($"受训者记录已删除：{response.Deleted}");
        }
        catch (Exception e)
        {
            Debug.LogError($"[LTM] 删除受训者失败：{e.Message}");
        }
    }
}
```

**选择合适的重置方法：**

| 目标                 | 方法                                                     |
| ------------------ | ------------------------------------------------------ |
| 仅清除此角色的记忆；保留用户记录   | `MemoryService.DeleteAllAsync(characterId, endUserId)` |
| 删除用户记录以及跨所有角色的所有记忆 | `EndUsersService.DeleteAsync(endUserId)`               |

{% hint style="danger" %}
这两种删除方法都具有 **不可逆性**。该 API 不提供确认或撤销。请在应用界面中先实现确认步骤，再调用任一方法。
{% endhint %}

***

## 结论

这四种模式覆盖了长时记忆的完整使用场景：无需代码的自动持久化、面向已认证用户的账户级身份、首次接触前的程序化记忆预置，以及用于重复运行体验的定向重置。如果某些内容没有按预期工作，请参阅 [故障排除与诊断 ](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/long-term-memory/troubleshooting-and-diagnostics.md)以获取逐步排查指南。


---

# 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/unity-plugin-beta-overview/features/long-term-memory/usage-examples.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.
