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

# 管理终端用户记录

从 Convai Editor 窗口或脚本中浏览并管理终端用户记录，涵盖编辑器工具、所有方法和分页。

Convai Unity SDK 会将每个连接到启用记忆角色的用户追踪为 **终端用户记录**。每条记录都会存储用户的稳定标识符、最后活动时间戳，以及你在连接期间发送的任何元数据。你可以在 Unity 编辑器中浏览和删除这些记录，或通过以下方式以编程方式管理它们 `client.EndUsers`.

{% hint style="warning" %}
**Beta API。** 方法签名是稳定的，但在未来的 SDK 更新中可能会更改。请在生产环境中固定你的 SDK 版本，并在升级前查看更新日志。
{% endhint %}

***

{% tabs %}
{% tab title="编辑器工具" %}
从 Unity 菜单栏访问该面板： **Convai → Long Term Memory**.

该面板显示与你的 API 密钥关联的所有终端用户记录。它以每批 200 条加载记录，并在数据量较大时通过基于游标的分页自动获取后续页面。

**可用操作：**

| 操作      | 方法                   |
| ------- | -------------------- |
| 刷新列表    | 点击 **刷新**            |
| 选择单条记录  | 点击某条记录行              |
| 全选或取消全选 | 点击 **全选** / **取消全选** |
| 删除所选记录  | 点击 **删除**            |

<figure><img src="https://2402152281-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtUJA212Zc1S9ACc8T4l%2Fuploads%2FcTJ9o3r47D0DwlJq3Nn7%2Fimage.png?alt=media&amp;token=8af97138-41e0-4395-914e-89fbf73fc5ac" alt="Unity menu bar showing Convai → Long Term Memory navigation path"><figcaption><p>从 Unity 菜单栏打开终端用户管理面板：Convai → Long Term Memory。</p></figcaption></figure>

从编辑器中删除一条终端用户记录会移除 **该记录以及该用户在所有角色中的所有记忆记录**。此操作无法撤销。删除前会显示确认对话框。

<figure><img src="https://2402152281-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtUJA212Zc1S9ACc8T4l%2Fuploads%2FDF6WIWV66SbC86DOigfT%2Fimage.png?alt=media&amp;token=97069e95-7620-487e-a56b-0a32bd24125c" alt="End-user records with name and session count"><figcaption><p>带有名称和会话次数的终端用户记录。</p></figcaption></figure>
{% endtab %}

{% tab title="脚本编写" %}
通过以下方式访问终端用户操作 `client.EndUsers` 在一个 `ConvaiRestClient` 实例上。

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

请参阅下面的每个方法以查看完整示例。
{% endtab %}
{% endtabs %}

***

### `EndUserDetails` 字段

每条终端用户记录都由以下内容表示 `EndUserDetails`:

| 属性               | 类型                           | 说明                                                                                                                    |
| ---------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `EndUserId`      | `string`                     | SDK 在连接时发送的稳定标识符                                                                                                      |
| `LastActiveTs`   | `string`                     | 上次会话的 ISO 8601 时间戳                                                                                                    |
| `LastLtmUsageTs` | `string`                     | 上次 LTM 交互的 ISO 8601 时间戳                                                                                               |
| `Metadata`       | `Dictionary<string, object>` | 由 IEndUserMetadataProvider 发送的键值数据 `IEndUserMetadataProvider`                                                         |
| `DisplayName`    | `string`                     | **计算属性** — 读取 `Metadata["name"]` 如果存在且非空；否则 `"User {EndUserId[..8]}"`，并可选择附加相对的最后活动时间（例如， `"User a1b2c3d4 (3d ago)"`) |
| `ShortId`        | `string`                     | **计算属性** — 的截断形式 `EndUserId` 用于紧凑显示（例如， `"a1b2c3d4...ef01"`)                                                          |

`DisplayName` 和 `ShortId` 是 C# 计算属性，不是存储字段。它们不会出现在 JSON API 响应中。

***

### 脚本 API

#### 列出终端用户

使用基于游标的分页检索所有终端用户记录。默认限制为每页 50 条记录。

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

public class EndUserLister : MonoBehaviour
{
    [ContextMenu("List All End Users")]
    private async void ListAllEndUsers()
    {
        using var client = new ConvaiRestClient(ConvaiSettings.Instance.ApiKey);

        string cursor = null;
        bool hasMore = true;
        var allUsers = new List<EndUserDetails>();

        while (hasMore)
        {
            var response = await client.EndUsers.ListAsync(limit: 50, cursor: cursor);

            if (response.EndUsers != null)
                allUsers.AddRange(response.EndUsers);

            hasMore = response.HasMore;
            cursor = response.NextCursor;

            if (!hasMore || string.IsNullOrEmpty(cursor))
                break;
        }

        Debug.Log($"终端用户总数：{allUsers.Count}");
        foreach (var user in allUsers)
            Debug.Log($"  {user.EndUserId} — 最后活动：{user.LastActiveTs}");
    }
}
```

**`EndUsersListResponse` 字段：**

| 属性           | 类型                     | 说明                         |
| ------------ | ---------------------- | -------------------------- |
| `EndUsers`   | `List<EndUserDetails>` | 此页上的记录                     |
| `TotalCount` | `int`                  | 终端用户记录总数                   |
| `NextCursor` | `string`               | 下一页的游标令牌； `null` 当不存在更多页面时 |
| `HasMore`    | `bool`                 | 是否存在更多页面                   |

你也可以使用以下方式按活动日期筛选 `activeAfter` 和 `activeBefore` （ISO 8601 字符串）：

```csharp
var response = await client.EndUsers.ListAsync(
    limit: 50,
    activeAfter: "2025-01-01T00:00:00Z",
    activeBefore: "2025-06-01T00:00:00Z");
```

***

#### 获取单个终端用户

通过其 `endUserId`.

```csharp
var user = await client.EndUsers.GetAsync("target-end-user-id");
Debug.Log($"最后活动：{user.LastActiveTs}");
Debug.Log($"显示名称：{user.DisplayName}");
```

***

#### 更新用户元数据

更新用户的一个或多个元数据键。补丁操作会保留你未包含的键——它不会替换整个元数据对象。

```csharp
var patch = new Dictionary<string, object>
{
    { "name", "Jordan Kim" },
    { "department", "Facilities Management" }
};

var updated = await client.EndUsers.UpdateMetadataAsync("target-end-user-id", patch);
Debug.Log($"已更新 {updated.EndUserId} 的元数据。");
```

***

#### 删除终端用户

{% hint style="danger" %}
`DeleteAsync` 会移除终端用户记录 **以及该用户在所有角色中的所有记忆记录**。与 `MemoryService.DeleteAllAsync`不同，后者仅作用于一个角色，而此操作会全局移除该用户。此操作无法撤销。
{% endhint %}

```csharp
var result = await client.EndUsers.DeleteAsync("target-end-user-id");

if (result.Deleted)
    Debug.Log($"终端用户 {result.EndUserId} 已删除。");
else
    Debug.LogWarning("删除返回 false — 用户可能不存在。");
```

***

### `DeleteAllAsync` 与 `DeleteAsync`

| 操作                                                     | 作用域         | 删除的内容            |
| ------------------------------------------------------ | ----------- | ---------------- |
| `client.Memory.DeleteAllAsync(characterId, endUserId)` | 一个用户 + 一个角色 | 该用户-角色配对的所有记忆记录  |
| `client.EndUsers.DeleteAsync(endUserId)`               | 一个用户 + 所有角色 | 用户记录以及跨所有角色的所有记忆 |

使用 `DeleteAllAsync` 当你想重置某个用户在特定角色中的记忆，同时保留其在其他角色中的记录时。使用 `EndUsers.DeleteAsync` 当你需要将用户从系统中完全移除时。

***

### 后续步骤

{% content-ref url="/pages/0b7e1084a14c3bcf13fec179ad83b458f07ecff4" %}
[记忆管理 API](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/long-term-memory/memory-management-api.md)
{% endcontent-ref %}

{% 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 %}


---

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