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

# 最终用户身份

每个长期记忆会话都需要一个稳定的 `end_user_id` —— SDK 会在每次连接时发送给 Convai 的一个字符串。Convai 使用它来限定存储的记忆范围：从对话中提取的事实会根据 `end_user_id` 是位于 `character_id`。如果标识符在会话之间发生变化，服务器会将用户视为新用户，并且不会沿用任何记忆。

SDK 提供 `DeviceEndUserIdProvider` 作为零配置默认值。对于带有用户身份验证的应用程序，您可以将其替换为一个返回您账户 ID 的自定义提供程序。

***

### 默认身份： `DeviceEndUserIdProvider`

`DeviceEndUserIdProvider` 同时实现了 `IEndUserIdProvider` 是位于 `IEndUserIdentityProvider`。它会自动注册——无需配置。

**在 Unity 编辑器中：** 始终读取或创建存储在 `PlayerPrefs` 中的 GUID，键为 `"convai.end_user_id"`。同一台机器上的每个 Play Mode 会话都会共享此 GUID，因此在开发和测试期间记忆会按预期累积。

**在玩家构建中：** 首先尝试 `SystemInfo.deviceUniqueIdentifier`。如果该值不可用、为空、等于 `SystemInfo.unsupportedIdentifier`，或者全部由零组成，则会回退到一个 `PlayerPrefs` GUID——仅生成一次，并在该设备后续所有会话中重复使用。

GUID 格式为不含连字符的 32 位十六进制字符串（例如： `a1b2c3d4e5f6789012345678abcdef01`）。您会在控制台日志中以及检查 `EndUserDetails.EndUserId`.

`PlayerPrefs` 无法在重新安装后保留。清除 `PlayerPrefs` 或重新安装应用程序会生成一个新的 GUID。Convai 会将新的 GUID 视为新用户——所有先前存储的记忆在新 ID 下都无法访问（不过它们仍保留在服务器上的旧 ID 下）。对于需要在重新安装后保持数据连续性的应用程序，请通过自定义提供程序使用服务器分配的账户 ID。请参见 [实现自定义身份提供程序](#implement-a-custom-identity-provider) 下文。

***

### Player ID 不是终端用户 ID

**在 `ConvaiPlayer` 组件上设置 Player ID 并不会更改用于记忆范围划分的 `end_user_id` 所使用的标识符。** Player ID 仅控制本地玩家名称在转录和调试日志中的显示方式。记忆范围始终由 `IEndUserIdentityProvider`决定，而不是由 `ConvaiPlayer`.

***

### 实现自定义身份提供程序

如果您的应用程序对用户进行身份验证，请实现 `IEndUserIdentityProvider` 以返回一个稳定的、由服务器分配的账户标识符。这可确保记忆在不同设备和重新安装之间跟随同一用户。

#### 实现该接口

```csharp
using Convai.Domain.Identity;

public class AccountIdentityProvider : IEndUserIdentityProvider
{
    private readonly string _accountId;

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

    public string GetEndUserId()
    {
        return _accountId;
    }
}
```

请使用后端分配的账户 ID 作为标识符。不要使用电子邮件地址或显示名称——它们可能会变化，这会导致服务器将该用户视为新用户。

#### 可选地附加元数据

实现 `IEndUserMetadataProvider` 以便在发送身份信息时附带显示信息。Convai 会将此元数据存储在 `EndUserDetails.Metadata` 并使用 `"name"` 键中，以便在编辑器的长期记忆面板中填充显示名称。

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

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

    public AccountMetadataProvider(string displayName, string department)
    {
        _displayName = displayName;
        _department = department;
    }

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

#### 在首次连接前注册

调用 `SetEndUserIdentityProvider` 是位于 `SetEndUserMetadataProvider` on `ConvaiManager` **在首次会话连接之前**。如果 `ConvaiCharacter` 具有 **自动连接** 已启用，连接会在以下操作后立即开始 `ConvaiRoomManager.Start()` 完成——请在 `Awake()`，而不是 `Start()`中注册您的提供程序，以保证顺序正确。

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

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

    private void Awake()
    {
        // 在 ConvaiRoomManager.Start() 之前调用——适用于自动连接角色
        string accountId = AuthService.CurrentUser.AccountId;
        string displayName = AuthService.CurrentUser.DisplayName;
        string department = AuthService.CurrentUser.Department;

        _convaiManager.SetEndUserIdentityProvider(new AccountIdentityProvider(accountId));
        _convaiManager.SetEndUserMetadataProvider(new AccountMetadataProvider(displayName, department));
    }
}
```

{% hint style="danger" %}
如果您调用 `SetEndUserIdentityProvider` 在 `ConnectAsync` 如果当前会话已经调用过，则提供程序更改不会影响当前活动连接。新的提供程序将从下一次 `ConnectAsync` 调用开始生效。请始终在连接前注册。
{% endhint %}

***

### 空的或仅包含空白字符的终端用户 ID

{% hint style="danger" %}
如果 `GetEndUserId()` 返回空字符串、仅包含空白字符的字符串，或者 `null`null `null` 在发送前。Convai 会将 `null` 视为匿名会话—— **不会存储或检索任何记忆**。请始终确保您的身份来源返回非空且不含空白字符的值。
{% endhint %}

***

### 身份来源对比

| 场景        | 推荐来源                           | 重新安装后仍保留 | 更换设备后仍保留 |
| --------- | ------------------------------ | -------- | -------- |
| 开发 / 测试   | `DeviceEndUserIdProvider` （默认） | 没有       | 没有       |
| 消费者应用，无账户 | `DeviceEndUserIdProvider` （默认） | 没有       | 没有       |
| 带账户的消费者应用 | 自定义提供程序 → 服务器账户 ID             | 是        | 是        |
| 企业 / 培训平台 | 自定义提供程序 → 服务器账户 ID             | 是        | 是        |

***

### 下一步

{% content-ref url="/pages/49cef55d22f33c0ffdf4893701cd5a6686bd7590" %}
[管理终端用户记录](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/long-term-memory/end-user-management.md)
{% endcontent-ref %}

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


---

# 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/convai-unity-sdk/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.
