> 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 不提供确认或撤销。请在应用 UI 中先实现确认步骤，再调用任一方法。
{% 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 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/unity-plugin-beta-overview/features/long-term-memory/usage-examples.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.
