> 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/end-user-management.md).

# 终端用户管理

## 浏览和管理终端用户记录

当用户与启用记忆的角色交互时，Convai 后端会为每个唯一的终端用户创建一条终端用户记录 `end_user_id`。这些记录包含用户的显示名称、元数据，以及其最后活动和最后一次记忆交互的时间戳。本页介绍两种处理这些记录的方法：用于开发期间进行可视化管理的内置编辑器工具，以及 `EndUsersService` 用于运行时或工具使用的脚本 API。

{% hint style="info" %}
**Beta API 注意：** Convai 后端上的所有终端用户管理端点都使用 beta API 标志。就目前而言，方法签名和响应结构是稳定的，但在未来的 SDK 或后端更新中可能会发生变化。
{% endhint %}

## 编辑器工具：长期记忆选项卡

配置窗口包含一个 **长期记忆** 选项卡，允许你无需编写任何代码即可浏览和删除终端用户记录。在开发过程中，它可用于查看哪些用户已经积累了记忆，以及清理测试数据。

### 打开工具

前往 **Convai** 并选择 **长期记忆** 选项卡。

<figure><img src="/files/7562581514fe67609170ba9b8b83d8140546942b" alt=""><figcaption></figcaption></figure>

### 界面概览

<figure><img src="/files/0102113dc2b5fe44586f74bceb0ebec284b3ad99" alt=""><figcaption></figcaption></figure>

| 控件          | 函数                                                                  |
| ----------- | ------------------------------------------------------------------- |
| **刷新**      | 从服务器获取当前终端用户列表。列表不会自动加载——首次打开后或更改后请点击“刷新”。                          |
| **终端用户列表**  | 一个可滚动的列表，显示你账户中的所有终端用户记录。每一项显示显示名称（来自元数据的 `"name"` 键）以及用户 ID 的简写形式。 |
| **全选**      | 切换所有可见条目的选中状态。再次点击可取消全部选中。                                          |
| **删除**      | 永久删除所有选中的终端用户记录及其关联记忆。删除前需要进行确认。                                    |
| **状态 / 重试** | 加载失败时显示。点击 **重试** 以再次尝试获取。                                          |

### 理解终端用户列表

每个条目代表一个唯一的 `end_user_id` 已连接过至少一个角色的用户。显示名称来自用户元数据字典中的 `"name"` 键。如果未设置名称，该条目会显示用户 ID 的截断形式（`ShortId`).

该列表每次请求获取 200 项。基于游标的分页会自动处理更大的数据集——在显示列表之前，所有页面都会被获取并合并。

{% hint style="warning" %}
在编辑器工具中删除终端用户记录会移除该记录 **以及该用户在所有角色中的所有记忆记录** 。此操作无法撤销。
{% endhint %}

### EndUserDetails 字段

以下字段可在每条终端用户记录上使用，无论是在编辑器工具中还是通过脚本 API：

| 字段               | 类型                           | 说明                                                                                 |
| ---------------- | ---------------------------- | ---------------------------------------------------------------------------------- |
| `EndUserId`      | `string`                     | 完整的 `end_user_id` 由 SDK 发送的字符串。                                                    |
| `DisplayName`    | `string`                     | 根据 `metadata["name"]` （如果存在）计算得出；否则根据用户 ID 生成。                                     |
| `ShortId`        | `string`                     | 用于紧凑显示的 ID 截断形式。                                                                   |
| `LastActiveTs`   | `string`                     | 用户最近一次会话的 ISO 8601 时间戳。                                                            |
| `LastLtmUsageTs` | `string`                     | 该用户最近一次记忆读取或写入的 ISO 8601 时间戳。                                                      |
| `Metadata`       | `Dictionary<string, object>` | 通过以下方式提交的任意键值数据： `IEndUserMetadataProvider` 在连接时，或通过以下方式更新： `UpdateMetadataAsync`. |

## 脚本 API：EndUsersService

`ConvaiRestClient.EndUsers` 公开 `EndUsersService`，它提供与编辑器工具相同的功能，并额外支持元数据编辑。可将其用于运行时管理工具、自动清理任务或集成测试。

### 设置 ConvaiRestClient

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

public class EndUserManager : MonoBehaviour
{
    private ConvaiRestClient _client;

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

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

{% hint style="info" %}
`ConvaiSettings` 是用于存储你的 API 密钥的 ScriptableObject，配置在 **工具 → Convai → 配置**. `ConvaiRestClient` 实现 `IDisposable` ——用完后务必释放它。
{% endhint %}

{% hint style="info" %}
本页中的所有示例都使用 `async void` 以兼容 Unity。务必如图所示将异步调用包装在 `try/catch` 如图所示。
{% endhint %}

### 列出终端用户

通过基于游标的分页遍历所有终端用户记录。

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

public async void ListAllEndUsers(CancellationToken cancellationToken = default)
{
    try
    {
        string cursor = null;
        const int limit = 50;

        do
        {
            EndUsersListResponse response = await _client.EndUsers.ListAsync(
                limit: limit,
                cursor: cursor,
                cancellationToken: cancellationToken);

            foreach (EndUserDetails user in response.EndUsers)
            {
                Debug.Log($"{user.DisplayName} ({user.ShortId}) — 最后活动时间：{user.LastActiveTs}");
            }

            cursor = response.HasMore ? response.NextCursor : null;

        } while (cursor != null);
    }
    catch (Exception e)
    {
        Debug.LogError($"[LTM] 列出终端用户失败：{e.Message}");
    }
}
```

#### ListAsync 参数

| 参数                  | 类型                  | 默认值    | 说明                                            |
| ------------------- | ------------------- | ------ | --------------------------------------------- |
| `limit`             | `int`               | `50`   | 每页记录数。                                        |
| `cursor`            | `string?`           | `null` | 上一条响应中的分页游标 `NextCursor`时会被忽略。请 `null` 用于第一页。 |
| `activeAfter`       | `string?`           | `null` | ISO 8601 字符串。仅返回在此时间戳之后活跃的用户。                 |
| `activeBefore`      | `string?`           | `null` | ISO 8601 字符串。仅返回在此时间戳之前活跃的用户。                 |
| `cancellationToken` | `CancellationToken` | `默认`   | 可选的取消支持。                                      |

#### EndUsersListResponse 字段

| 字段           | 类型                     | 说明                        |
| ------------ | ---------------------- | ------------------------- |
| `EndUsers`   | `List<EndUserDetails>` | 当前页上的记录。                  |
| `TotalCount` | `int`                  | 跨所有页面的终端用户记录总数。           |
| `NextCursor` | `string`               | 下一页的游标。 `null` 当不再有更多页面时。 |
| `HasMore`    | `bool`                 | 是否存在更多页面。                 |

### 获取单个终端用户

```csharp
public async void GetUser(string endUserId, CancellationToken cancellationToken = default)
{
    try
    {
        EndUserDetails user = await _client.EndUsers.GetAsync(endUserId, cancellationToken);
        Debug.Log($"名称：{user.DisplayName} — 最后一次 LTM：{user.LastLtmUsageTs}");
    }
    catch (Exception e)
    {
        Debug.LogError($"[LTM] 获取终端用户失败：{e.Message}");
    }
}
```

### 更新终端用户元数据

元数据可随时更新——并不局限于连接时。请使用只包含你想更改的键的补丁字典；补丁中未包含的现有键会被保留。

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

public async void UpdateUserRole(
    string endUserId,
    string newRole,
    CancellationToken cancellationToken = default)
{
    try
    {
        var patch = new Dictionary<string, object>
        {
            { "role", newRole }
        };

        EndUserUpdateResponse updated = await _client.EndUsers.UpdateMetadataAsync(
            endUserId,
            patch,
            cancellationToken);

        Debug.Log($"已更新 {updated.DisplayName}：角色现在为 {newRole}");
    }
    catch (Exception e)
    {
        Debug.LogError($"[LTM] 更新元数据失败：{e.Message}");
    }
}
```

### 删除终端用户

```csharp
public async void DeleteUser(string endUserId, CancellationToken cancellationToken = default)
{
    try
    {
        EndUserDeleteResponse response = await _client.EndUsers.DeleteAsync(endUserId, cancellationToken);
        Debug.Log($"已删除：{response.Deleted} — 用户 ID：{response.EndUserId}");
    }
    catch (Exception e)
    {
        Debug.LogError($"[LTM] 删除终端用户失败：{e.Message}");
    }
}
```

{% hint style="danger" %}
`EndUsersService.DeleteAsync` 会永久移除终端用户记录 **以及该用户在所有角色中的所有关联记忆记录**。这比 `MemoryService.DeleteAllAsync`更广，它只会清除某一个角色的记忆，同时保留用户记录。请仅在彻底停用某个用户账户时使用 `DeleteAsync` 。此操作无法撤销。
{% endhint %}

## 完整 API 参考

所有方法都在 `ConvaiRestClient.EndUsers` (`EndUsersService`）。所有 `CancellationToken` 参数都默认为 `默认`.

| 方法                    | 签名                                                                                             | 返回                            |
| --------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------- |
| `ListAsync`           | `(int limit, string? cursor, string? activeAfter, string? activeBefore, CancellationToken ct)` | `Task<EndUsersListResponse>`  |
| `GetAsync`            | `(string endUserId, CancellationToken ct)`                                                     | `Task<EndUserDetails>`        |
| `UpdateMetadataAsync` | `(string endUserId, IReadOnlyDictionary<string, object> metadataPatch, CancellationToken ct)`  | `Task<EndUserUpdateResponse>` |
| `DeleteAsync`         | `(string endUserId, CancellationToken ct)`                                                     | `Task<EndUserDeleteResponse>` |

## 结论

终端用户记录会在用户与您的角色交互时自动创建。编辑器工具可在开发期间为您提供可视化概览； `EndUsersService` API 可通过代码提供相同的功能。有关将身份、记忆和终端用户 API 结合在一起的完整示例，请参见 [使用示例](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/long-term-memory/usage-examples.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/end-user-management.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.
