集成自己的模型

将智能体连接到自己的 LLM,或托管自己的服务器。

Custom LLM 可通过外部端点将对话连接到自己的 LLM。 ElevenLabs 还支持原生集成的 LLM

自定义 LLM 可让你使用自己的 OpenAI API 密钥,或运行完全自定义的 LLM 服务器。

概述

默认情况下,我们会为 OpenAI 等热门模型使用内部凭据。要使用自定义 LLM 服务器,它必须符合以下任一与 OpenAI 兼容的请求/响应结构:

Responses API 是 OpenAI 较新的 API 格式,支持更多功能。两种 API 格式 均完全支持自定义 LLM 集成。

以下指南涵盖两种使用场景:

  1. 使用自己的 OpenAI 密钥:在平台中使用自己的 OpenAI API 密钥。
  2. 自定义 LLM 服务器:托管并连接自己实现的 LLM 服务器。

你将了解如何:

  • 在 ElevenLabs 中存储 OpenAI API 密钥
  • 托管一个复现 OpenAI Chat Completions 或 Responses 端点的服务器
  • 将 ElevenLabs 指向自定义端点
  • 按需向 LLM 传递额外参数

推理摘要

端点必须将推理内容与最终回答分别返回。ElevenLabs 不会根据最终回答生成推理内容。

要从支持的端点请求推理,请在智能体的 LLM 设置中开启 推理摘要,或通过 API 设置 enable_reasoning_summary。

返回推理内容

使用与端点匹配的格式:

在每个响应增量的 reasoning 或 reasoning_content 字段中流式传输推理内容。

对于兼容 Gemini 的端点,ElevenLabs 会通过 google.thinking_config.include_thoughts 请求思维内容,并读取标记为 extra_content.google.thought 的内容。

有关存储、传送和限制,请参阅推理摘要。

使用自己的 OpenAI 密钥

要集成自定义 OpenAI 密钥,请更新 ElevenLabs 控制台中的智能体设置,使其指向自定义 LLM 服务器,并创建一个包含 OPENAI_API_KEY 的密钥:

1

在 ElevenLabs 控制台的 Agent 设置中,从右侧的 “LLM” 下拉菜单选择 “Custom LLM”。

添加密钥

2

点击 “LLM” 下方的字段,向下滚动并选择 “Custom LLM”。

3

输入自定义 LLM 服务器的服务器 URL 和 Model ID。

输入 URL

4

点击 “API key” 下方的下拉菜单,然后选择 “Create new secret”。将密钥命名为 OPENAI_API_KEY,在 “value” 字段中添加密钥,然后点击 “Add secret”。

5

点击 “x” 按钮关闭 LLM 弹窗,然后点击 “Publish” 保存更改。

自定义 LLM 服务器

要使用自定义 LLM 服务器,请按 OpenAI 的风格设置兼容的服务器端点。你可以实现 Chat Completions API(/v1/chat/completions)或 Responses API(/v1/responses)。

两个端点都必须以 SSE(Server-Sent Events)格式返回响应,并使用 Content-Type: text/event-stream。

Chat Completions API 使用 /v1/chat/completions 端点。

每个块必须格式化为 data: {json}\n\n,流必须以 data: [DONE]\n\n 结束。

以下是服务器实现示例:

import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional
# Load environment variables from .env file
load_dotenv()
# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
raise ValueError("OPENAI_API_KEY not found in environment variables")
app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
class Message(BaseModel):
role: str
content: str
class ChatCompletionRequest(BaseModel):
messages: List[Message]
model: str
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = None
stream: Optional[bool] = False
user_id: Optional[str] = None
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async def event_stream():
try:
async for chunk in chat_completion_coroutine:
# Convert the ChatCompletionChunk to a dictionary before JSON serialization
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8013)

运行此代码或自己的服务器代码。

为服务器设置公共 URL

要让服务器可访问,请使用 ngrok 等隧道工具创建公共 URL:

ngrok http --url=<Your url>.ngrok.app 8013

配置 ElevenLabs CustomLLM

接下来,更新 ElevenLabs 控制台中的智能体设置,使其指向自定义 LLM 服务器。

将服务器 URL 指向 ngrok 端点,并将 “Limit token usage” 设为 5000。

现在可以使用自己的 LLM 服务器与智能体交互。

优化处理速度较慢的 LLM

如果自定义 LLM 的处理时间较长(例如由于智能体推理或预处理要求),可在流式响应中实现缓冲词来改善对话流畅度。这种方法可在 LLM 生成完整回答时保持自然的语音韵律。

缓冲词

当 LLM 需要更多时间处理完整回答时,先返回一个以 "... " 结尾的初始响应(省略号后跟一个空格)。这样,文本转语音系统可保持自然流畅,同时让对话保持动态。 这会产生自然的停顿,并能顺畅衔接 LLM 可花更长时间推理的后续内容。额外的空格至关重要,可避免后续内容附加到 ”…” 后面而导致音频失真。

实现方式

以下是修改自定义 LLM 服务器以实现缓冲词的方法:

@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
async def event_stream():
try:
# Send initial buffer chunk while processing
initial_chunk = {
"id": "chatcmpl-buffer",
"object": "chat.completion.chunk",
"created": 1234567890,
"model": request.model,
"choices": [{
"delta": {"content": "Let me think about that... "},
"index": 0,
"finish_reason": None
}]
}
yield f"data: {json.dumps(initial_chunk)}\n\n"
# Process the actual LLM response
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async for chunk in chat_completion_coroutine:
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")

系统工具集成

自定义 LLM 可触发系统工具来控制对话流程和状态。在智能体中配置后,这些工具会自动包含在聊天完成请求的 tools 参数中。

系统工具的工作方式

  1. LLM 决策:自定义 LLM 根据对话上下文决定何时调用这些工具
  2. 工具响应:LLM 以标准 OpenAI 格式返回函数调用
  3. 后端处理:ElevenLabs 处理工具调用并更新对话状态

有关系统工具的更多信息,请参阅我们的指南

可用系统工具

用途:在满足适当条件时自动结束对话。

触发条件:LLM 应在以下情况下调用此工具:

  • 主要任务已完成,且用户感到满意
  • 双方达成一致,对话自然结束
  • 用户明确表示想结束对话

参数:

  • reason(字符串,必填):结束通话的原因
  • message(字符串,选填):结束通话前发送给用户的告别消息

函数调用格式:

{
"type": "function",
"function": {
"name": "end_call",
"arguments": "{\"reason\": \"Task completed successfully\", \"message\": \"Thank you for using our service. Have a great day!\"}"
}
}

实现:在智能体设置中配置为系统工具。LLM 将收到有关何时调用此函数的详细说明。

了解更多:结束通话工具

用途:在对话过程中自动切换到检测到的用户语言。

触发条件:LLM 应在以下情况下调用此工具:

  • 用户使用的语言不同于当前对话语言
  • 用户明确请求切换语言
  • 对话需要多语言支持

参数:

  • reason(字符串,必填):切换语言的原因
  • language(字符串,必填):要切换到的语言代码(必须在支持的语言列表中)

函数调用格式:

{
"type": "function",
"function": {
"name": "language_detection",
"arguments": "{\"reason\": \"User requested Spanish\", \"language\": \"es\"}"
}
}

实现:在智能体设置中配置支持的语言,并添加语言检测系统工具。智能体会自动切换语音和回复,以匹配检测到的语言。

了解更多:语言检测工具

用途:根据用户需求,在专门的 AI 智能体之间转接对话。

触发条件:LLM 应在以下情况下调用此工具:

  • 用户请求需要专业知识或不同的智能体能力
  • 当前智能体无法妥善处理该查询
  • 对话流程表明需要不同类型的智能体

参数:

  • reason(字符串,选填):转接智能体的原因
  • agent_number(整数,必填):要转接到的智能体从零开始的编号(根据已配置的转接规则)

函数调用格式:

{
"type": "function",
"function": {
"name": "transfer_to_agent",
"arguments": "{\"reason\": \"User needs billing support\", \"agent_number\": 0}"
}
}

实现:定义将条件映射到特定智能体 ID 的转接规则。配置当前智能体可转接到哪些智能体。智能体在转接配置中以从零开始的编号引用。

了解更多:智能体转接工具

用途:当 AI 协助不足时,无缝将对话转交给人工客服。

触发条件:LLM 应在以下情况下调用此工具:

  • 复杂问题需要人工判断
  • 用户明确请求人工协助
  • AI 的能力无法满足特定请求
  • 触发升级处理流程

参数:

  • reason(字符串,选填):转接原因
  • transfer_number(字符串,必填):要转接到的电话号码(必须与已配置号码匹配)
  • client_message(字符串,必填):等待转接时向客户播放的消息
  • agent_message(字符串,必填):发送给接听通话的人工客服的消息

函数调用格式:

{
"type": "function",
"function": {
"name": "transfer_to_number",
"arguments": "{\"reason\": \"Complex billing issue\", \"transfer_number\": \"+15551234567\", \"client_message\": \"I'm transferring you to a billing specialist who can help with your account.\", \"agent_message\": \"Customer has a complex billing dispute about order #12345 from last month.\"}"
}
}

实现:配置转接电话号码和条件。为客户和接听通话的人工客服分别定义消息。适用于 Twilio 和 SIP 中继。

了解更多:转接人工客服工具

用途:让智能体暂停并等待用户输入,而不发出语音。

触发条件:LLM 应在以下情况下调用此工具:

  • 用户表示需要一点时间(“Give me a second”、“Let me think”)
  • 用户请求暂停对话流程
  • 智能体检测到用户需要时间处理信息

参数:

  • reason(字符串,选填):说明为何需要暂停的自由格式原因

函数调用格式:

{
"type": "function",
"function": {
"name": "skip_turn",
"arguments": "{\"reason\": \"User requested time to think\"}"
}
}

实现:无需额外配置。该工具仅向智能体发出信号,让其保持静默,直至用户再次说话。

了解更多:跳过回合工具

参数:

  • reason(字符串,必填):检测到语音信箱的原因(例如,“检测到自动问候语”“无人响应”)

函数调用格式:

{
"type": "function",
"function": {
"name": "voicemail_detection",
"arguments": "{\"reason\": \"Automated greeting detected with request to leave message\"}"
}
}

了解更多:语音信箱检测工具

包含系统工具的请求示例

配置系统工具后,自定义 LLM 将收到以标准 OpenAI 格式包含这些工具的请求:

{
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. You have access to system tools for managing conversations."
},
{
"role": "user",
"content": "I think we're done here, thanks for your help!"
}
],
"model": "your-custom-model",
"temperature": 0.7,
"max_tokens": 1000,
"stream": true,
"tools": [
{
"type": "function",
"function": {
"name": "end_call",
"description": "Call this function to end the current conversation when the main task has been completed...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "The reason for the tool call."
},
"message": {
"type": "string",
"description": "A farewell message to send to the user along right before ending the call."
}
},
"required": ["reason"]
}
}
},
{
"type": "function",
"function": {
"name": "language_detection",
"description": "Change the conversation language when the user expresses a language preference explicitly...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "The reason for the tool call."
},
"language": {
"type": "string",
"description": "The language to switch to. Must be one of language codes in tool description."
}
},
"required": ["reason", "language"]
}
}
},
{
"type": "function",
"function": {
"name": "skip_turn",
"description": "Skip a turn when the user explicitly indicates they need a moment to think...",
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Optional free-form reason explaining why the pause is needed."
}
},
"required": []
}
}
}
]
}

自定义 LLM 必须支持函数调用才能使用系统工具。请确保模型能够以 OpenAI 格式生成 正确的函数调用响应。

其他功能

你可以向自定义 LLM 实现传递额外参数。

1

定义额外参数

创建一个包含自定义参数的对象:

from elevenlabs.conversational_ai.conversation import Conversation, ConversationInitiationData
extra_body_for_convai = {
"UUID": "123e4567-e89b-12d3-a456-426614174000",
"parameter-1": "value-1",
"parameter-2": "value-2",
}
config = ConversationInitiationData(
extra_body=extra_body_for_convai,
)
2

更新 LLM 实现

修改自定义 LLM 代码以处理额外参数:

import json
import os
import fastapi
from fastapi.responses import StreamingResponse
from fastapi import Request
from openai import AsyncOpenAI
import uvicorn
import logging
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List, Optional
# Load environment variables from .env file
load_dotenv()
# Retrieve API key from environment
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
if not OPENAI_API_KEY:
raise ValueError("OPENAI_API_KEY not found in environment variables")
app = fastapi.FastAPI()
oai_client = AsyncOpenAI(api_key=OPENAI_API_KEY)
class Message(BaseModel):
role: str
content: str
class ChatCompletionRequest(BaseModel):
messages: List[Message]
model: str
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = None
stream: Optional[bool] = False
user_id: Optional[str] = None
elevenlabs_extra_body: Optional[dict] = None
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest) -> StreamingResponse:
oai_request = request.dict(exclude_none=True)
print(oai_request)
if "user_id" in oai_request:
oai_request["user"] = oai_request.pop("user_id")
if "elevenlabs_extra_body" in oai_request:
oai_request.pop("elevenlabs_extra_body")
chat_completion_coroutine = await oai_client.chat.completions.create(**oai_request)
async def event_stream():
try:
async for chunk in chat_completion_coroutine:
chunk_dict = chunk.model_dump()
yield f"data: {json.dumps(chunk_dict)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.error("An error occurred: %s", str(e))
yield f"data: {json.dumps({'error': 'Internal error occurred!'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8013)

请求示例

使用此自定义消息设置后,LLM 将以以下格式收到请求:

{
"messages": [
{
"role": "system",
"content": "\n <Redacted>"
},
{
"role": "assistant",
"content": "Hey I'm currently unavailable."
},
{
"role": "user",
"content": "Hey, who are you?"
}
],
"model": "gpt-4o",
"temperature": 0.5,
"max_tokens": 5000,
"stream": true,
"elevenlabs_extra_body": {
"UUID": "123e4567-e89b-12d3-a456-426614174000",
"parameter-1": "value-1",
"parameter-2": "value-2"
}
}