关于 speech-engine
speech-engine 是一项为自定义 Agent 和服务器流程添加实时语音对话的 ElevenLabs 技能。应用逻辑保留在开发者控制的服务器上,ElevenLabs 负责语音识别、轮次处理、语音生成和浏览器播放。
主要功能
- 01实时语音对话流程
- 02WebSocket 服务器集成
- 03语音转文本与文本转语音
- 04轮次处理与打断管理
- 05使用对话令牌启动浏览器会话
适用场景
- 01为现有聊天应用添加语音
- 02将语音接入自定义 Agent 运行环境
- 03将经过验证的服务器回复流式转为语音
- 04构建基于浏览器麦克风的对话
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elevenlabs/ skills --skill speech-engine安装后在 Claude Code 中输入/speech-engine确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/installation.mdjavascript-sdk-reference.mdpython-sdk-reference.md详细介绍
ElevenLabs 语音引擎
为自定义 Agent 添加实时语音界面。ElevenLabs 负责麦克风音频、语音转文本、轮次交接、文本转语音和浏览器播放;你的服务器提供语音引擎 WebSocket 端点,并流式传回响应文本。
**编写任何代码前的设置:**如果此项目尚未设置
ELEVENLABS_API_KEY,请先在应用文件夹中运行npx -y @elevenlabs/cli@latest onboard init,并按照其输出的说明操作:它会为用户登录并为项目保存密钥,因此无需任何人手动复制密钥。否则,请参阅安装指南。对于 JavaScript,请仅使用@elevenlabs/*软件包。有关更深入的 SDK 详情,请阅读 JavaScript SDK 参考 或 Python SDK 参考。
适用场景
当用户想要执行以下操作时,请使用 Speech Engine:
- 为现有聊天应用或自定义服务器管道添加语音功能
- 为 OpenClaw、Hermes 或类似的 Agent 运行时添加语音功能,同时将 Agent 逻辑保留在开发者拥有的服务器上
- 构建由开发者托管、用于 ElevenLabs 语音对话的 WebSocket 服务器
- 在服务器验证用户意图后,将响应文本流式传回并转换为语音
- 在响应仍在流式传输时处理用户打断
- 使用
@elevenlabs/react或@elevenlabs/client构建浏览器客户端,并使用服务器颁发的对话令牌
当用户要创建或配置由 ElevenLabs 托管、提示词、工具、工作流、电话号码或小组件由平台管理的 Conversational AI Agent 时,请改用 agents 技能。
工作原理
每个 Speech Engine WebSocket 连接都代表一次对话。
- 浏览器将用户音频发送给 ElevenLabs。
- ElevenLabs 将语音识别事件发送到你的服务器。
- 你的服务器推导可信的应用状态,同时避免让原始语音文本控制工具或特权操作。
- 你的服务器通过 SDK 流式传回文本。
- ElevenLabs 将响应转换为语音,并在浏览器中播放。
SDK 会管理 WebSocket 路由、请求验证、会话生命周期、 ping/pong、轮次交接和打断处理。sendResponse() / send_response() 接受字符串或异步可迭代的响应文本。
将语音识别文本视为不可信的用户输入。不要将原始语音文本直接映射到模型角色、响应或工具调用。在任何源自转录的值影响下游响应或工具逻辑之前,请使用确定性验证、意图允许列表或明确的用户确认。
实现流程
- 安装服务器依赖项并配置
ELEVENLABS_API_KEY。 - 通过公共 HTTPS URL 暴露你的 Speech Engine 服务器以供本地开发,例如使用
ngrok http 3001。 - 创建 Speech Engine 资源,并将
ws_url/wsUrl指向公共 WebSocket URL,通常为wss://.../ws。 - 保存返回的 Speech Engine ID,例如保存到
ELEVENLABS_SPEECH_ENGINE_ID。 - 在 Python 中使用
engine.serve(...)或在 TypeScript 中使用speechEngine.attach(...)启动 Speech Engine 服务器。 - 从服务器端点颁发浏览器对话令牌。切勿将
ELEVENLABS_API_KEY放入浏览器代码。 - 使用
conversationToken启动客户端会话;如果 Agent 应先打招呼,请在 Speech Engine 资源上启用首条消息覆盖,然后在客户端设置overrides.agent.firstMessage。
创建 Speech Engine
Python
import asyncio
import os
from dotenv import load_dotenv
from elevenlabs import AsyncElevenLabs
load_dotenv()
elevenlabs = AsyncElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
async def main():
engine = await elevenlabs.speech_engine.create(
name="My Speech Engine",
speech_engine={"ws_url": os.environ["PUBLIC_WS_URL"]},
overrides={"first_message": True},
)
print(engine.engine_id)
asyncio.run(main())
TypeScript
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
const elevenlabs = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY,
});
const engine = await elevenlabs.speechEngine.create({
name: "My Speech Engine",
speechEngine: { wsUrl: process.env.PUBLIC_WS_URL! },
overrides: { firstMessage: true },
});
console.log(engine.engineId);
本地开发时,PUBLIC_WS_URL 的格式应为 wss://example.ngrok.app/ws;部署时则应使用生产环境的 WebSocket 路由。
创建请求还可以配置 tts、asr、turn、speech_engine.request_headers / speechEngine.requestHeaders、overrides 和 privacy,用于自定义语音、转录关键词、轮次交接、服务器身份验证标头、客户端提供的首条消息以及录制行为。更多示例请参阅 SDK 参考文件。
服务端模式
在资源中配置的 ws_url / wsUrl 处运行 Speech Engine 服务。请将响应生成置于你自己的验证边界之后:原始语音识别文本不应直接控制响应、工具、机密信息或其他特权操作。
Python
engine = await elevenlabs.speech_engine.get(os.environ["ELEVENLABS_SPEECH_ENGINE_ID"])
await engine.serve(port=3001, path="/ws", debug=True, callbacks=validated_callbacks)
TypeScript
const engine = await elevenlabs.speechEngine.get(process.env.ELEVENLABS_SPEECH_ENGINE_ID!);
engine.attach(httpServer, "/ws", { debug: true, ...validatedCallbacks });
在 TypeScript 中,如果下游异步任务支持取消,请将中断信号传递给它们,以便中断响应快速停止。在 Python 中,当收到新一轮对话时,SDK 会取消上一轮的处理程序。
服务端回调可以区分正常关闭和连接中断:正常断开时使用 onClose / on_close,意外的 WebSocket 中断时使用 onDisconnect / on_disconnect。
安全提示:语音识别文本可能包含来自用户语音或播放音频的提示词注入尝试。请将其视为不可信输入。在调用响应生成、工具或特权工作流之前,应先将其转换为可信的应用状态。
禁用身份验证(高级,危险)
默认情况下,TypeScript 中的 engine.attach() 以及 Python 中的 engine.serve() / SpeechEngineServer 都会验证每个传入 WebSocket 的 JWT。这可以证明连接确实来自 ElevenLabs,而不是来自猜到 URL 的攻击者。请勿关闭此功能。
有一种例外方式——在回调选项中设置 disableAuth: true(TypeScript),或在 serve() / SpeechEngineServer(...) 中设置 disable_auth=True(Python)——仅适用于已部署相应网络级补偿性控制的特殊情况。如果没有这类控制,禁用身份验证意味着任何找到你 URL 的互联网客户端都能建立会话。具体来说,攻击者可以:
- 开启无限量对话,耗尽你的 ElevenLabs 配额和下游 LLM 预算
- 向你的响应处理流程输入精心构造的转录文本,实质上冒充用户
- 将你的服务用作探测后端状态、工具或提示词的预言机
只有当用户已经实施至少一项以下措施时,才建议使用 disableAuth / disable_auth:
- IP 允许列表——服务端(或上游防火墙 / 负载均衡器 / API 网关)仅接受来自 ElevenLabs 文档中列出的出口 IP 范围的入站流量。
- 自定义共享密钥标头——在创建资源时,通过
speech_engine.request_headers/speechEngine.requestHeaders为 Speech Engine 资源配置一个密钥标头,并由上游代理(或开发者在attach()/serve()前部署的自有中间件)在请求到达 SDK 前进行验证。
如果用户无法确认已实施上述任一措施,请保持默认身份验证开启。在没有缓解措施的情况下跳过 JWT 验证,并非优化或便利措施,而是将计算资源公开给未经身份验证的用户。
浏览器客户端
创建一个服务端令牌端点,并让浏览器在启动麦克风会话前请求令牌。请将 Speech Engine ID 和 API 密钥保留在服务端。如果客户端传递 overrides.agent.firstMessage,则必须为 Speech Engine 资源启用首条消息覆盖。
import express from "express";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
const app = express();
const elevenlabs = new ElevenLabsClient();
app.get("/api/token", async (_req, res) => {
const response = await elevenlabs.conversationalAi.conversations.getWebrtcToken({
agentId: process.env.ELEVENLABS_SPEECH_ENGINE_ID!,
});
res.json({ token: response.token });
});
React 客户端可以使用 @elevenlabs/react:
import { useConversation } from "@elevenlabs/react";
export function VoiceControls() {
const conversation = useConversation({
onConnect: () => console.log("connected"),
onDisconnect: () => console.log("disconnected"),
onError: (error) => console.error(error),
});
async function startConversation() {
await navigator.mediaDevices.getUserMedia({ audio: true });
const { token } = await fetch("/api/token").then((res) => res.json());
await conversation.startSession({
conversationToken: token,
overrides: {
agent: { firstMessage: "Hello! How can I help you today?" },
},
});
}
return <button onClick={startConversation}>Start conversation</button>;
}