> 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.

# Kotlin SDK

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

## 安装

在应用级 `build.gradle` 文件中添加以下依赖项，即可将 ElevenLabs SDK 添加到 Android 项目：

**`build.gradle.kts`**

```kotlin build.gradle.kts
dependencies {
    // ElevenLabs Agents SDK (Android)
    implementation("io.elevenlabs:elevenlabs-android:<latest>")

    // Kotlin coroutines, AndroidX, etc., as needed by your app
}
```

> **Tip**
>
> 使用此 SDK 的 Android 示例应用可在
> [这里](https://github.com/elevenlabs/elevenlabs-android/tree/main/example-app)查看。

## 要求

* Android API 级别 21（Android 5.0）或更高版本
* 用于 API 调用的网络权限
* 用于语音输入的麦克风权限
* 用于 HTTPS 调用的网络安全配置

## 设置

### Manifest 配置

将所需权限添加到 `AndroidManifest.xml`：

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

### 运行时权限

对于 Android 6.0（API 级别 23）及更高版本，必须在运行时请求麦克风权限：

```kotlin
import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

private fun requestMicrophonePermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
        != PackageManager.PERMISSION_GRANTED) {

        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
            // Show explanation to the user
            showPermissionExplanationDialog()
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.RECORD_AUDIO),
                MICROPHONE_PERMISSION_REQUEST_CODE
            )
        }
    }
}
```

## 使用

在 `Application` 类或主 Activity 中初始化 ElevenLabs SDK：

使用以下任一方式启动对话会话：

* 公开智能体：传入 `agentId`
* 私有智能体：传入由后端提供的 `conversationToken`（切勿向客户端暴露 API 密钥）。

```kotlin
import io.elevenlabs.ConversationClient
import io.elevenlabs.ConversationConfig
import io.elevenlabs.ConversationSession
import io.elevenlabs.ClientTool
import io.elevenlabs.ClientToolResult

// Start a public agent session (token generated for you)
val config = ConversationConfig(
    agentId = "<your_public_agent_id>", // OR conversationToken = "<token>"
    userId = "your-user-id",
    // Optional callbacks
    onConnect = { conversationId ->
        // Called when the conversation is connected and returns the conversation ID. You can access conversationId via session.getId() too
    },
    onMessage = { source, messageJson ->
        // Raw JSON messages from data channel; useful for logging/telemetry
    },
    onModeChange = { mode ->
        // "speaking" | "listening" — drive UI indicators
    },
    onStatusChange = { status ->
        // "connected" | "connecting" | "disconnected"
    },
    onCanSendFeedbackChange = { canSend ->
        // Enable/disable thumbs up/down buttons for feedback reporting
    },
    onUnhandledClientToolCall = { call ->
        // Agent requested a client tool not registered on the device
    },
    onVadScore = { score ->
        // Voice Activity Detection score, range from 0 to 1 where higher values indicate higher confidence of speech
    },
    onAudioAlignment = { alignment ->
        // Character-level timing data for synchronized text display
        val chars = alignment["chars"] as? List<*>
        val startTimes = alignment["char_start_times_ms"] as? List<*>
        val durations = alignment["char_durations_ms"] as? List<*>
        Log.d("ExampleApp", "Audio alignment: $chars")
    },
    // List of client tools the agent can invoke
    clientTools = mapOf(
        "logMessage" to object : ClientTool {
            override suspend fun execute(parameters: Map<String, Any>): ClientToolResult {
                val message = parameters["message"] as? String

                Log.d("ExampleApp", "[INFO] Client Tool Log: $message")
                return ClientToolResult.success("Message logged successfully")
            }
        }
    ),
)

// In an Activity context
val session: ConversationSession = ConversationClient.startSession(config, this)
```

请注意，ElevenAgents 需要麦克风访问权限。建议在对话开始前，先在应用 UI 中说明并请求权限，尤其是在需要运行时权限的 Android 6.0 及更高版本上。

> **Note**
>
> 如果服务器上某个工具配置了 `expects_response=false`，请从 `execute` 返回 `null`，
> 以跳过向智能体发送工具结果。

## 公开与私有智能体

* **公开智能体**（无需身份验证）：在 `ConversationConfig` 中使用 `agentId` 初始化。SDK 会向 ElevenLabs 请求对话令牌，无需在设备上使用 API 密钥。
* **私有智能体**（需身份验证）：在 `ConversationConfig` 中使用 `conversationToken` 初始化。服务器使用 ElevenLabs API 密钥向 ElevenLabs 请求对话令牌。

> **Error**
>
> 切勿将 API 密钥嵌入客户端。它们很容易被提取并遭恶意使用。

## 客户端工具

注册客户端工具，让智能体能够调用设备上的本地功能。

```kotlin
val config = ConversationConfig(
    agentId = "<public_agent>",
    clientTools = mapOf(
        "logMessage" to object : io.elevenlabs.ClientTool {
            override suspend fun execute(parameters: Map<String, Any>): io.elevenlabs.ClientToolResult? {
                val message = parameters["message"] as? String ?: return io.elevenlabs.ClientToolResult.failure("Missing 'message'")

                android.util.Log.d("ClientTool", "Log: $message")
                return null // No response needed for fire-and-forget tools
            }
        }
    )
)
```

当智能体发出 `client_tool_call` 时，SDK 会执行匹配的工具，并以 `client_tool_result` 响应。如果工具未注册，将调用 `onUnhandledClientToolCall`，并向智能体返回失败结果（如果预期有响应）。

### 回调概览

* **onConnect**：建立 WebRTC 连接时调用。返回对话 ID。
* **onMessage**：收到新消息时调用。可能是用户语音的临时或最终转录文本、LLM 生成的回复或调试消息。提供来源（`"ai"` 或 `"user"`）及原始 JSON 消息。
* **onModeChange**：对话模式改变时调用。可用于指示智能体正在说话（`"speaking"`）还是正在聆听（`"listening"`）。
* **onStatusChange**：对话状态改变时调用（`"connected"`、`"connecting"` 或 `"disconnected"`）。
* **onCanSendFeedbackChange**：发送反馈的能力改变时调用。启用／禁用反馈按钮。
* **onUnhandledClientToolCall**：智能体请求设备上未注册的客户端工具时调用。
* **onVadScore**：语音活动检测分数改变时调用。范围为 0 到 1，值越高表示检测到语音的置信度越高。
* **onAudioAlignment**：收到音频对齐数据时调用，为智能体语音提供字符级时间信息。

> **Warning**
>
> 并非所有客户端事件都会默认对智能体启用。如果已启用回调但未收到事件，请确认 ElevenLabs 智能体已启用相应事件。
> 可在 ElevenLabs 控制台中智能体设置的“Advanced”标签页进行配置。

### 方法

#### startSession

`startSession` 方法会初始化 WebRTC 连接，并开始通过麦克风与 ElevenLabs Agents 智能体交流。

##### 公开智能体

对于公开智能体（即未启用身份验证的智能体），只需提供 `agentId`。可在 [ElevenLabs UI](https://elevenlabs.io/app/agents) 中获取智能体 ID。

```kotlin
val session = ConversationClient.startSession(
    config = ConversationConfig(
        agentId = "your-agent-id"
    ),
    context = this
)
```

##### 私有智能体

对于私有智能体，必须传入通过 ElevenLabs API 获取的 `conversationToken`。生成此令牌需要 ElevenLabs API 密钥。

> **Tip**
>
> `conversationToken` 的有效期为 10 分钟。

```typescript maxLines=0
// Server-side token generation (Node.js example)

app.get("/conversation-token", yourAuthMiddleware, async (req, res) => {
  const response = await fetch(
    `https://api.elevenlabs.io/v1/convai/conversation/token?agent_id=${process.env.AGENT_ID}`,
    {
      headers: {
        // Requesting a conversation token requires your ElevenLabs API key
        // Do NOT expose your API key to the client!
        "xi-api-key": process.env.ELEVENLABS_API_KEY,
      },
    }
  );

  if (!response.ok) {
    return res.status(500).send("Failed to get conversation token");
  }

  const body = await response.json();
  res.send(body.token);
});
```

然后，将令牌传给 `startSession` 方法。请注意，私有智能体只需提供 `conversationToken`。

```kotlin

// Get conversation token from your server
val conversationToken = fetchConversationTokenFromServer()

// For private agents, pass in the conversation token
val session = ConversationClient.startSession(
    config = ConversationConfig(
        conversationToken = conversationToken
    ),
    context = this
)
```

还可选择传入用户 ID，以便在对话中识别用户。这可以是自有的客户标识符。该 ID 会包含在发送到服务器的对话初始化数据中。

```kotlin
val session = ConversationClient.startSession(
    config = ConversationConfig(
        agentId = "your-agent-id",
        userId = "your-user-id"
    ),
    context = this
)
```

#### endSession

用于手动结束对话的方法。该方法会断开连接并结束对话。

```kotlin
session.endSession()
```

#### sendUserMessage

在活跃对话期间向智能体发送文本消息。这会触发智能体回复。

```kotlin
session.sendUserMessage("Hello, how can you help me?")
```

#### sendContextualUpdate

向智能体发送不会触发回复的上下文信息。

```kotlin
session.sendContextualUpdate(
    "User navigated to the profile page. Consider this for next response."
)
```

#### sendFeedback

针对对话质量提供反馈。这有助于提升智能体表现。在允许反馈时，使用 `onCanSendFeedbackChange` 启用点赞／点踩 UI。

```kotlin
// Positive feedback
session.sendFeedback(true)

// Negative feedback
session.sendFeedback(false)
```

#### sendUserActivity

通知智能体用户活动，防止被打断。适用于用户正在操作应用且智能体应暂停说话的场景，例如用户在聊天中输入文字时。

收到此信号后，智能体会暂停说话约 2 秒。

```kotlin
session.sendUserActivity()
```

#### getId

获取对话 ID。

```kotlin
val conversationId = session.getId()
Log.d("Conversation", "Conversation ID: $conversationId")
// e.g., "conv_123"
```

#### 静音／取消静音

```kotlin
session.toggleMute()
session.setMicMuted(true)   // mute
session.setMicMuted(false)  // unmute
```

观察 `session.isMuted`，以在 UI 标签中切换“静音”和“取消静音”。

### 属性

#### status

获取当前对话状态。

```kotlin
val status = session.status
Log.d("Conversation", "Current status: $status")
// Values: DISCONNECTED, CONNECTING, CONNECTED
```

## ProGuard / R8

如果使用压缩／混淆，请确保保留 Gson 模型和 LiveKit。示例规则如下（请按需调整）：

```proguard
-keep class io.elevenlabs.** { *; }
-keep class io.livekit.** { *; }
-keepattributes *Annotation*
```

## 故障排除

* 确保已在运行时授予麦克风权限
* 如果重连卡住，请确认应用调用了 `session.endSession()`，并且在重连前启动了新的会话实例
* 对于模拟器，请确认音频输入／输出路由正常；实体设备通常更可靠

## 实现示例

实现示例请参阅 [ElevenLabs Android SDK 仓库](https://github.com/elevenlabs/elevenlabs-android/tree/main/example-app)中的示例应用。该应用演示了：

* 一键连接／断开连接
* 说话／聆听状态指示器
* 带 UI 启用／禁用功能的反馈按钮
* 通过 `sendUserActivity()` 实现的输入状态指示器
* 从输入框发送上下文消息和用户消息
* 麦克风静音／取消静音按钮