mcpField
S
speech-engine

speech-engine

github.com/elevenlabs开发5166.8k 次安装今天更新
代码仓库

关于 speech-engine

speech-engine 是一项为自定义 Agent 和服务器流程添加实时语音对话的 ElevenLabs 技能。应用逻辑保留在开发者控制的服务器上,ElevenLabs 负责语音识别、轮次处理、语音生成和浏览器播放。

主要功能

  1. 01实时语音对话流程
  2. 02WebSocket 服务器集成
  3. 03语音转文本与文本转语音
  4. 04轮次处理与打断管理
  5. 05使用对话令牌启动浏览器会话

适用场景

  1. 01为现有聊天应用添加语音
  2. 02将语音接入自定义 Agent 运行环境
  3. 03将经过验证的服务器回复流式转为语音
  4. 04构建基于浏览器麦克风的对话

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/elevenlabs/skills --skill speech-engine

安装后在 Claude Code 中输入/speech-engine确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
installation.md
javascript-sdk-reference.md
python-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 连接都代表一次对话。

  1. 浏览器将用户音频发送给 ElevenLabs。
  2. ElevenLabs 将语音识别事件发送到你的服务器。
  3. 你的服务器推导可信的应用状态,同时避免让原始语音文本控制工具或特权操作。
  4. 你的服务器通过 SDK 流式传回文本。
  5. ElevenLabs 将响应转换为语音,并在浏览器中播放。

SDK 会管理 WebSocket 路由、请求验证、会话生命周期、 ping/pong、轮次交接和打断处理。sendResponse() / send_response() 接受字符串或异步可迭代的响应文本。

将语音识别文本视为不可信的用户输入。不要将原始语音文本直接映射到模型角色、响应或工具调用。在任何源自转录的值影响下游响应或工具逻辑之前,请使用确定性验证、意图允许列表或明确的用户确认。

实现流程

  1. 安装服务器依赖项并配置 ELEVENLABS_API_KEY。
  2. 通过公共 HTTPS URL 暴露你的 Speech Engine 服务器以供本地开发,例如使用 ngrok http 3001。
  3. 创建 Speech Engine 资源,并将 ws_url / wsUrl 指向公共 WebSocket URL,通常为 wss://.../ws。
  4. 保存返回的 Speech Engine ID,例如保存到 ELEVENLABS_SPEECH_ENGINE_ID。
  5. 在 Python 中使用 engine.serve(...) 或在 TypeScript 中使用 speechEngine.attach(...) 启动 Speech Engine 服务器。
  6. 从服务器端点颁发浏览器对话令牌。切勿将 ELEVENLABS_API_KEY 放入浏览器代码。
  7. 使用 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>;
}

参考资料

评论

支持 Markdown