> 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-identity.md).

# 终端用户身份

## SDK 如何跨会话识别用户

只有当 Convai 后端能够可靠地将用户与其历史记录关联起来时，长期记忆才有用。建立这种关联的标识符是 `end_user_id` —— 一个在每次会话连接时发送到服务器的字符串。本页说明 SDK 默认如何生成该标识符、它在各个平台上提供哪些保证，以及如何用你自己的、由应用管理的身份替换默认值。

## 为什么稳定身份很重要

如果该 `end_user_id` 在不同会话之间发生变化，后端会将该用户视为新用户，且不会保留任何记忆。相反，如果两个不同用户共享同一个标识符，他们就会共享同一个记忆分区，角色也会混淆他们。正确处理身份是可靠长期记忆最重要的前提。

SDK 会发送 `end_user_id` 作为会话连接请求的一部分。后端会将其解析为内部的 `speaker_id`，然后使用 `speaker_id:character_id` 作为该用户与该角色记忆的存储键。你的应用始终只与 `end_user_id` —— 内部键只是后端实现细节。

{% hint style="warning" %}
如果 `end_user_id` 为空或仅包含空白时，SDK 会将其规范化为 `null` 后再发送。一个 `null` 标识符会被服务器视为匿名会话——不会存储或检索任何记忆。请始终确保你的身份提供者返回非空字符串。
{% endhint %}

## 默认提供者：DeviceEndUserIdProvider

开箱即用时，SDK 会注册 `DeviceEndUserIdProvider` 作为身份来源。它使用以下逻辑生成一个稳定的、设备范围内的标识符：

### Player 构建

1. 阅读 `SystemInfo.deviceUniqueIdentifier`.
2. 如果该值有效（非空、非 null、不是 `SystemInfo.unsupportedIdentifier`、且不全为 0），则直接使用。
3. 否则，生成一个新的 `Guid` （格式为 `"N"`，不含连字符），将其存储在 `PlayerPrefs` 中，并使用键 `"convai.end_user_id"`保存，并返回它。此后每次运行都会返回同一个 GUID。

### Unity 编辑器

会完全跳过设备标识符路径。编辑器始终从 `PlayerPrefs` 使用相同的 `"convai.end_user_id"` 键读取，首次运行时生成新的 GUID，并在之后重复使用。这样同一项目中的每个 Play Mode 会话都拥有一致的身份，这对于在不登录的情况下测试记忆连续性很有用。

### 平台行为摘要

| 环境                   | 身份来源                                | 持久性                                 |
| -------------------- | ----------------------------------- | ----------------------------------- |
| Player 构建——设备 ID 可用  | `SystemInfo.deviceUniqueIdentifier` | 由操作系统管理；在某些平台上重装后仍会保留               |
| Player 构建——设备 ID 不可用 | 中的 GUID `PlayerPrefs`               | 应用更新后仍保留；重装后或 `PlayerPrefs` 清除后会被清空 |
| Unity 编辑器            | 中的 GUID `PlayerPrefs`               | 编辑器重启后仍保留；每个 Unity 项目一个 ID          |

{% hint style="warning" %}
**PlayerPrefs 不是安全存储。** 在设备 ID 不可用的平台上，回退用的 GUID 会存储在 `PlayerPrefs` 中，用户或项目中的其他代码都可以读取或清除它。对于身份要求严格的应用，请用由你的身份验证系统支持的提供者替换默认提供者。
{% endhint %}

## PlayerId 字段不是 end\_user\_id

`ConvaiPlayer` 提供一个 **Player ID** 字段在 Inspector 中。这是一个 **本地显示标识符** 仅用于转录 UI 的归属显示——即对话信息流中显示在玩家台词旁边的名称。它不会发送到服务器，也不会影响长期记忆。

{% hint style="warning" %}
设置 **Player ID** 在 `ConvaiPlayer` —— 无论是在 Inspector 中还是通过 `ConvaiPlayer.Configure(...)` —— 都 **上的初始动态信息文本** 更改 `end_user_id` 用于记忆的
{% endhint %}

## 。要控制服务器用于记忆范围划分的标识符，请按下述方式实现自定义身份提供者。

当你的应用管理用户账户时，请替换默认提供者——例如，用户登录后，你希望记忆绑定到他们的账户，而不是他们的物理设备。

### 两个身份接口

SDK 将身份分为两个接口。对于大多数自定义实现，你只需要其中一个：

| 接口                         | 方法                    | 用途                                                                                 |
| -------------------------- | --------------------- | ---------------------------------------------------------------------------------- |
| `IEndUserIdentityProvider` | `GetEndUserId()`      | **使用此接口。** 在连接时解析当前用户的标识符。通过 `ConvaiManager.SetEndUserIdentityProvider(provider)`. |
| `IEndUserIdProvider`       | `GenerateEndUserId()` | 底层生成接口，由 `DeviceEndUserIdProvider`实现。大多数自定义提供者不需要它。                                |

`DeviceEndUserIdProvider` 同时实现这两个接口。自定义提供者通常只实现 `IEndUserIdentityProvider`.

### 步骤 1：实现 IEndUserIdentityProvider

```csharp
using Convai.Domain.Identity;

public class AccountEndUserIdProvider : IEndUserIdentityProvider
{
    private readonly string _accountId;

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

    public string GetEndUserId() => _accountId;
}
```

对于同一个人用户，这个返回字符串在不同会话之间必须保持稳定。服务器分配的账户 ID、哈希后的邮箱，或你身份验证系统中的任何不透明标识符都可以很好地工作。避免使用在重新登录或更换设备后会变化的值。

### 步骤 2：在连接前注册

```csharp
using Convai.Runtime.Components;
using UnityEngine;

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

    // 在身份验证流程解析出 accountId 后调用此方法。
    public void OnUserSignedIn(string accountId)
    {
        var provider = new AccountEndUserIdProvider(accountId);
        _convaiManager.SetEndUserIdentityProvider(provider);
    }
}
```

`ConvaiManager.SetEndUserIdentityProvider` 接受任意 `IEndUserIdentityProvider`。提供者只会在会话连接时读取一次——如果在会话已激活后更改它，在下一次连接之前都不会生效。

{% hint style="warning" %}
请在 **之前** 首次会话连接前注册该提供者。如果你在 `SetEndUserIdentityProvider` 之后 `ConvaiManager` 已经打开会话，则新身份不会被使用，直到会话重启。
{% endhint %}

### 步骤 3（可选）：附加用户元数据

你可以在连接时与 `end_user_id` 一起发送任意元数据。服务器会将其存储在终端用户记录上，可在 End-User Management 编辑器工具中查看，也可通过 `EndUsersService`。 `"name"` 键会得到特殊处理——它会被用作编辑器中终端用户列表的显示名称。

```csharp
using System.Collections.Generic;
using Convai.Domain.Identity;

public class AccountMetadataProvider : IEndUserMetadataProvider
{
    private readonly string _displayName;
    private readonly string _role;

    public AccountMetadataProvider(string displayName, string role)
    {
        _displayName = displayName;
        _role = role;
    }

    public IReadOnlyDictionary<string, object> GetEndUserMetadata()
    {
        return new Dictionary<string, object>
        {
            { "name", _displayName },
            { "role", _role }
        };
    }
}
```

将其与身份提供者一并注册：

```csharp
_convaiManager.SetEndUserIdentityProvider(new AccountEndUserIdProvider(accountId));
_convaiManager.SetEndUserMetadataProvider(new AccountMetadataProvider(displayName, role));
```

## 身份解析摘要

```mermaid
flowchart LR
    A[你的应用] -->|实现| B[IEndUserIdentityProvider]
    B -->|GetEndUserId| C[end_user_id 字符串]
    C -->|连接时发送| D[Convai Server]
    D -->|解析为| E[内部 speaker_id]
    E -->|存储键| F["speaker_id : character_id"]
    F --> G[(记忆分区)]
```

## 结论

该 `end_user_id` —— 是长期记忆一切功能的基础——如果它不稳定，记忆就不会延续。默认的 `DeviceEndUserIdProvider` 会自动处理大多数基于设备的部署。对于有用户账户的应用，请实现 `IEndUserIdentityProvider` 并在首次会话连接前注册它。下一页， [在角色上启用记忆](/api-docs/zh/cha-jian-yu-ji-cheng/unity-plugin-beta-overview/features/long-term-memory/enabling-memory-on-characters.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-identity.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.
