> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Unity SDK

> **Info**
>
> 有关 ElevenAgents 工作原理的说明，请参阅 [ElevenAgents 概览](/docs/zh/eleven-agents/overview)。

[ElevenAgents Unity SDK](https://github.com/elevenlabs/unity) 可将 Unity 场景连接到 ElevenLabs 智能体。它会采集玩家麦克风输入，通过 `AudioSource` 流式播放智能体语音，并通过统一的 `Conversation` API 暴露会话；该 API 在编辑器、桌面独立播放器和 WebGL 构建中行为一致。

## 要求

* Unity 6.3 LTS（`6000.3.0f1`）或更高版本。SDK 使用了 Unity 6.3 引入的可脚本化音频 API。
* 将 **Api Compatibility Level** 设为 **.NET Standard 2.1**，这是 Unity 2023.1 及更高版本的默认设置。
* ElevenLabs 账户和智能体。可在[控制台](https://elevenlabs.io/app/agents)创建，或按照[快速入门](/docs/zh/eleven-agents/quickstart)操作。

### 平台支持

| 目标                                        | 传输方式                            | 状态                     |
| ----------------------------------------- | ------------------------------- | ---------------------- |
| 编辑器（Play Mode）、桌面独立版（macOS、Windows、Linux） | 原生 WebSocket                    | 支持                     |
| WebGL                                     | 通过 JS 桥接使用 `@elevenlabs/client` | 支持（WebSocket 和 WebRTC） |
| iOS、Android、XR                            | 原生 WebSocket                    | 未测试                    |

SDK 会自动为当前构建目标选择传输方式。WebRTC 仅在 WebGL 上可用；在其他所有目标上，`ConnectionType.WebRTC` 会抛出 `NotSupportedException`。

> **Note**
>
> SDK 不会请求麦克风权限。在 iOS 和 Android 上，请在启动会话前自行请求权限。

## 安装

在 Unity 中，打开 **Window** > **Package Manager**，选择 **+** > **Install package from git URL**，然后粘贴仓库 URL：

```text
https://github.com/elevenlabs/unity.git
```

Package Manager 会安装 **ElevenAgents**（`io.elevenlabs.agents`）包及其依赖项：Input System 和 Newtonsoft Json。

> **Tip**
>
> 该包附带两个示例。从 **Package Manager** > **ElevenAgents** >
> **Samples** 导入。**QuickStart** 是一个用于渲染实时转写的单一 MonoBehaviour。
> **GettingStarted** 是一个支持空间音频和动态变量的即走即聊场景。有关分步指南，请参阅
> [入门 演练](https://github.com/elevenlabs/unity/blob/main/Docs~/GETTING_STARTED.md)。

## 快速入门

本节将语音智能体添加到空场景中。进入 Play Mode 时智能体会开始说话，退出时会话结束。

#### 创建公开智能体

在 [ElevenLabs 控制台](https://elevenlabs.io/app/agents)中创建智能体。在智能体的 **Security** 设置中关闭 **Authentication**，这样游戏仅凭智能体 ID 即可连接。复制智能体 ID。

对于需要身份验证的智能体，请参阅[公开和私有智能体](#public-and-private-agents)。

#### 安装 SDK

按照[安装](#installation)中的说明，通过 git URL 安装该包。

#### 添加对话脚本

创建 `Assets/Scripts/AgentConversation.cs`：

**`AgentConversation.cs`**

```csharp title="AgentConversation.cs"
using System;
using ElevenLabs.Agents;
using ElevenLabs.Protocol;
using UnityEngine;

public sealed class AgentConversation : MonoBehaviour
{
    [SerializeField]
    private string agentId = "agent_7101k5zvyjhmfg983brhmhkd98n6";

    private Conversation conversation;

    private async void Start()
    {
        try
        {
            conversation = await Conversation.StartSessionAsync(
                new ConversationOptions { AgentId = agentId }
            );
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to start session: {ex.Message}");
            return;
        }

        Debug.Log($"Connected: {conversation.ConversationId}");

        conversation.UserTranscriptReceived += OnUserTranscript;
        conversation.AgentResponded += OnAgentResponse;
        conversation.ErrorOccurred += message => Debug.LogError(message);
        conversation.Disconnected += details => Debug.Log($"Disconnected: {details.Reason}");
    }

    private void OnUserTranscript(UserTranscriptArgs args) =>
        Debug.Log($"User: {args.UserTranscript}");

    private void OnAgentResponse(AgentResponseArgs args) =>
        Debug.Log($"Agent: {args.AgentResponse}");

    private void OnDestroy()
    {
        _ = conversation?.EndSession();
    }
}
```

`Conversation.StartSessionAsync` 会在连接打开且音频设备准备就绪后返回。`EndSession` 是幂等操作，因此即使会话已结束，从 `OnDestroy` 调用它也是安全的。

#### 运行场景

创建一个空 GameObject，添加 `AgentConversation` 组件，并将智能体 ID 粘贴到 **Agent Id** 字段。进入 Play Mode，并在操作系统提示时允许麦克风访问。智能体会说出第一条消息，转写内容将显示在 Console 中。

## 公开和私有智能体

#### 公开智能体

直接传入智能体 ID。任何拥有该 ID 的人都可以与智能体开始对话。

```csharp
var conversation = await Conversation.StartSessionAsync(
    new ConversationOptions { AgentId = "agent_7101k5zvyjhmfg983brhmhkd98n6" }
);
```

#### 私有智能体

启用身份验证的智能体需要由你自己的服务器签发短期凭据。从后端请求[签名 URL](/docs/zh/eleven-agents/customization/authentication)，并将其作为 `SignedUrl` 传入：

```csharp
using UnityEngine.Networking;

private static async Awaitable<string> FetchSignedUrlAsync()
{
    using var request = UnityWebRequest.Get("https://api.example-game.com/agent/signed-url");
    await request.SendWebRequest();
    if (request.result != UnityWebRequest.Result.Success)
        throw new InvalidOperationException(request.error);
    return request.downloadHandler.text;
}

var conversation = await Conversation.StartSessionAsync(
    new ConversationOptions { SignedUrl = await FetchSignedUrlAsync() }
);
```

对于 WebGL 上的 WebRTC 会话，将对话令牌作为 `ConversationToken` 传入，并设置 `ConnectionType = ConnectionType.WebRTC`。

> **Warning**
>
> 切勿在游戏构建中包含 ElevenLabs API 密钥。应在你控制的服务器上生成签名 URL 和对话令牌。

`AgentId`、`SignedUrl` 或 `ConversationToken` 必须且只能设置一个。设置 `UserId` 可在自己的系统中将对话关联到玩家。

## 空间音频

默认情况下，SDK 会通过其创建的隐藏 `AudioSource` 播放智能体语音。要将语音放置在场景中，请将自己的 `AudioSource` 作为 `OutputAudioSource` 传入：

```csharp
[SerializeField]
private AudioSource voiceSource; // Spatial Blend = 1 for 3D audio

var conversation = await Conversation.StartSessionAsync(
    new ConversationOptions
    {
        AgentId = "agent_7101k5zvyjhmfg983brhmhkd98n6",
        OutputAudioSource = voiceSource,
    }
);
```

SDK 会保留该音源的空间混合、衰减、混音器路由和位置。会话结束时，它会恢复音源的剪辑、循环和音量设置。

如需通过智能体语音驱动视觉效果，例如嘴部动画，请在每一帧读取 `conversation.GetOutputVolume()`。它会在所有平台上返回介于 0 和 1 之间的值。

## 个性化

使用[动态变量](/docs/zh/eleven-agents/customization/personalization/dynamic-variables)填充智能体提示词和首条消息中的占位符，并使用[覆盖](/docs/zh/eleven-agents/customization/personalization/overrides)替换单次会话的部分智能体配置：

```csharp
using System.Collections.Generic;
using ElevenLabs.Protocol;

var options = new ConversationOptions
{
    AgentId = "agent_7101k5zvyjhmfg983brhmhkd98n6",
    DynamicVariables = new Dictionary<string, object>
    {
        ["player_name"] = "Ada",
        ["quest_stage"] = 3,
    },
    Overrides = new ConversationConfigOverride
    {
        Agent = new ConversationConfigOverrideAgent
        {
            FirstMessage = "Welcome back, {{player_name}}.",
        },
        Tts = new ConversationConfigOverrideTts { VoiceId = "JBFqnCBsd6RMkjVDRZzb" },
    },
};
```

动态变量值必须是字符串、数字或布尔值。服务器接受覆盖前，必须先在智能体的 **Security** 设置中启用相应覆盖。

## 客户端工具

[客户端工具](/docs/zh/eleven-agents/customization/tools/client-tools)可让智能体在游戏中触发操作。在智能体设置中定义工具，然后注册同名处理程序：

```csharp
using Newtonsoft.Json;

public sealed class OpenDoorParams
{
    [JsonProperty("door_id")]
    public string DoorId { get; set; }
}

conversation.RegisterTool<OpenDoorParams, string>("open_door", parameters =>
{
    DoorRegistry.Open(parameters.DoorId);
    return $"Door {parameters.DoorId} is open.";
});
```

SDK 使用 Newtonsoft Json 将工具参数反序列化为 `TParams`，因此当参数名称与属性名称不同时，请使用 `[JsonProperty]`。返回值会序列化为工具结果。对于异步处理程序，返回 `Awaitable<TResult>`。抛出带有 `errorType` 的 `ClientToolException` 可向智能体报告特定故障；其他任何异常都会报告为通用错误。

调用未注册处理程序的工具会触发 `ErrorOccurred`；如果你订阅了该事件，则会触发 `UnhandledClientToolCall`。

## 事件

订阅由 `StartSessionAsync` 返回的 `Conversation` 上的事件。SDK 会在 Unity 主线程上触发这些事件。

| 事件                        | 负载                     | 说明                                         |
| ------------------------- | ---------------------- | ------------------------------------------ |
| `Disconnected`            | `DisconnectionDetails` | 会话已结束。`Reason` 为 `User`、`Agent` 或 `Error`。 |
| `ErrorOccurred`           | `string`               | 会话启动后发生错误。                                 |
| `StatusChanged`           | `Status`               | 连接状态已更改。                                   |
| `ModeChanged`             | `Mode`                 | 智能体在 `Speaking` 和 `Listening` 之间切换。        |
| `UserTranscriptReceived`  | `UserTranscriptArgs`   | 玩家语音的最终转写。                                 |
| `AgentResponded`          | `AgentResponseArgs`    | 智能体回复的文本。                                  |
| `Interrupted`             | `InterruptionArgs`     | 玩家打断了智能体。                                  |
| `AgentToolRequested`      | `AgentToolRequestArgs` | 智能体开始调用工具。完成时触发 `AgentToolResponded`。      |
| `UnhandledClientToolCall` | `ClientToolCallArgs`   | 智能体调用了没有已注册处理程序的客户端工具。                     |
| `CanSendFeedbackChanged`  | `bool`                 | 最新智能体回复的反馈变为可用或不可用。                        |

`Connected` 事件会在 `StartSessionAsync` 返回前触发，因此调用完成后可直接读取 `ConversationId` 和 `Status`。[包更新日志](https://github.com/elevenlabs/unity/blob/main/CHANGELOG.md)列出了完整事件列表。

## 方法

| 方法                                                            | 说明                                         |
| ------------------------------------------------------------- | ------------------------------------------ |
| `EndSession()`                                                | 关闭连接并释放音频设备。                               |
| `SendUserMessage(string text)`                                | 像玩家说出该内容一样发送文本消息。                          |
| `SendContextualUpdate(string text)`                           | 在不中断对话的情况下与智能体共享游戏状态。                      |
| `SendUserActivity()`                                          | 表示玩家处于活跃状态，以便智能体暂不发言。                      |
| `SendFeedback(bool like)`                                     | 当 `CanSendFeedback` 为 `true` 时，为最新智能体回复评分。 |
| `SetMicMuted(bool isMuted)`                                   | 静音或取消静音麦克风。                                |
| `SetVolume(float volume)`                                     | 将智能体播放音量设为 0 到 1 之间。                       |
| `GetInputVolume()`, `GetOutputVolume()`                       | 读取当前麦克风或智能体音频电平，范围为 0 到 1。                 |
| `GetInputByteFrequencyData()`, `GetOutputByteFrequencyData()` | 用于向音频可视化工具填充频率数据字节缓冲区。                     |
| `UploadFileAsync()`, `SendMultimodalMessage()`                | 上传文件并将其附加到用户消息。                            |
| `ChangeInputDevice()`, `ChangeOutputDevice()`                 | 在会话期间切换麦克风或输出设备。                           |

例如，当玩家进入新区域时，将当前位置发送给智能体：

```csharp
conversation.SendContextualUpdate("The player entered the abandoned lighthouse.");
```

## WebGL 构建

WebGL 构建需要额外配置一项 Player Setting。在 **Player Settings** > **WebGL** > **Publishing Settings** 中，开启 **Use WebAssembly.Table**。如果此设置关闭，SDK 会使构建失败并提供操作说明。

Unity 在 WebGL 上没有可脚本化音频管线，因此 SDK 会通过浏览器的 Web Audio API 播放智能体语音，并镜像 `OutputAudioSource` 的音量、位置和空间设置。因此，`AudioSource.GetOutputData` 和 `OnAudioFilterRead` 在 WebGL 上会返回静音。请改用 `GetOutputVolume()` 和 `GetOutputByteFrequencyData()`。

> **Note**
>
> 当当前构建目标为 WebGL 时，编辑器中的 Play Mode 尚不受支持。请切换到桌面构建目标以在编辑器中测试。

完整的 WebGL 兼容性矩阵请参阅 [`COMPATIBILITY.md`](https://github.com/elevenlabs/unity/blob/main/COMPATIBILITY.md)。

## 故障排除

* **`StartSessionAsync` 抛出 `ArgumentException`。** `AgentId`、`SignedUrl` 或 `ConversationToken` 必须且只能设置一个。
* **使用有效智能体 ID 时会话无法连接。** 该智能体需要身份验证。请关闭身份验证，或使用签名 URL 连接。
* **覆盖未生效。** 在智能体的 **Security** 设置中启用对应覆盖。
* **玩家在 WebGL 上听不到声音。** 浏览器会暂停音频，直到玩家与页面交互。请在点击或按键后启动会话。
* **WebGL 上的麦克风不可用。** 浏览器仅向通过 HTTPS 或 `localhost` 提供的页面授予麦克风访问权限。

请在 [GitHub](https://github.com/elevenlabs/unity/issues) 报告错误和提出功能请求。