news 2026/9/10 15:34:58

OpenMontage 实战:基于 ElevenLabs Scribe 的浏览器端实时语音转写(Client-Side Real-Time Streaming)完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenMontage 实战:基于 ElevenLabs Scribe 的浏览器端实时语音转写(Client-Side Real-Time Streaming)完整指南

OpenMontage 实战:基于 ElevenLabs Scribe 的浏览器端实时语音转写(Client-Side Real-Time Streaming)完整指南

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

导读

本文聚焦 OpenMontage 仓库中 speech-to-text 技能 的客户端实时流式转写能力:如何从浏览器直接采集麦克风音频,以约 150ms 级别的低延迟流式送往 ElevenLabs Scribe v2 Realtime 模型进行实时转写,并处理"部分转写(partial)"与"提交转写(committed)"两类结果。读完本文你将掌握单次令牌(single-use token)签发、ReactuseScribeHook 与原生 JavaScriptScribe.connect两条接入路径、scribe.status状态机、VAD 自动提交策略以及手动 PCM 分块推送的完整实战方案,并了解该能力在 OpenMontage 视频生产流水线中的落点。

本文以仓库内 realtime-client-side.md 为主体骨架,同时结合 SKILL.md、realtime-events.md、realtime-commit-strategies.md 等姊妹文档与仓库源码进行纵深展开,确保配置、代码、参数均可直接复制运行。

一、能力定位:在 OpenMontage 中用于什么

OpenMontage 是一个开源的 Agent 化视频生产系统,其.agents/skills/skills/目录沉淀了大量可供 AI 编码助手调用的技能文件。speech-to-text技能的整体定位是"使用 ElevenLabs Scribe v2 将音频/视频转换为文本",用于生成字幕、转写会议、处理口播内容等场景。该技能包含两类模型(详见 SKILL.md):

模型 ID特点适用场景
scribe_v2高精度、支持 90+ 语言批量转写、字幕生成、长音频
scribe_v2_realtime低延迟(约 150ms)实时转写、语音 Agent

本文讨论的scribe_v2_realtime即属于第二条路线。在仓库中,该技能与具体的转写工具形成配套:tools/analysis/transcriber.py中的Transcriber工具声明了agent_skills = ["speech-to-text"],这意味着 Agent 在触发转写任务时可以查阅本文档对应的技能知识。该工具默认基于 faster-whisper / WhisperX 提供本地离线转写(支持词级时间戳、说话人分离、语言检测,见 transcriber.py),而 ElevenLabs Scribe 则提供了云端低延迟实时的另一条路径——两者互补:离线批处理交给 Whisper,浏览器实时对话场景则交给 Scribe Realtime。

仓库中同为 ElevenLabs 生态的 elevenlabs 技能 还展示了语音生成侧(TTS、声音克隆、音效、音乐)与 Remotion 的集成工作流(语音旁白脚本 → MP3 → Remotion<Audio>组件逐场景同步)。本文的实时转写能力与该工作流配合,即可构建"实时聆听 → 生成逐词时间戳 → 驱动字幕/歌词/口型同步"的完整闭环。

二、安装:必须使用 @elevenlabs/* 命名空间

根据 installation.md 与本文档,客户端实时转写需要安装对应的官方包:

# React npm install @elevenlabs/react @elevenlabs/elevenlabs-js # JavaScript npm install @elevenlabs/client @elevenlabs/elevenlabs-js

Warning:客户端包必须使用@elevenlabs/*命名空间。旧版elevenlabs(v1.x)npm 包已废弃,不应再使用;如果项目里残留旧包,先执行npm uninstall elevenlabs再安装新包。

各包的分工如下(对应 installation.md 的迁移说明):

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; // 服务端/Node:客户端实例与令牌签发 import { Scribe } from "@elevenlabs/client"; // 浏览器端:底层流式连接 import { useScribe } from "@elevenlabs/react"; // React:Hook 封装

背景补充:OpenMontage 的语音生成侧同样遵循"直连与托管路由并存"的原则。elevenlabs 技能 指出,TTS 场景优先路由到fal_elevenlabs_tts(通过 fal.ai 集中管理凭证),仅当注册表中直接 Provider 可用时才使用elevenlabs_tts。实时转写侧的密钥管理(ELEVENLABS_API_KEY)与之一致:API Key 只应存在于服务端环境变量中,绝不下发浏览器。

三、Token 生成:用单次令牌保护 API Key

浏览器端流式转写必须使用"单次使用令牌(single-use token)"来保护你的 API Key——绝不能让浏览器直接持有或拼接 API Key。令牌在你的后端服务中生成,并暴露为一个受鉴权保护的安全接口:

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; const elevenlabs = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY, }); app.get("/scribe-token", yourAuthMiddleware, async (req, res) => { const token = await elevenlabs.tokens.singleUse.create("realtime_scribe"); res.json(token); });

Note:单次使用令牌的有效期只有 15 分钟,过期后必须重新签发。这要求前端在每次建立连接前实时获取令牌,而不是缓存复用。

四、React 实现:useScribe Hook 全流程

React 应用通过@elevenlabs/react提供的useScribeHook 接入实时转写。核心思路:Hook 负责管理连接生命周期与事件回调,你只需要在点击"开始"时获取令牌并connect()

import { useScribe, CommitStrategy } from "@elevenlabs/react"; function TranscriptionComponent() { const [transcript, setTranscript] = useState(""); const scribe = useScribe({ modelId: "scribe_v2_realtime", commitStrategy: CommitStrategy.VAD, // Auto-commit on silence for mic input onPartialTranscript: (data) => { // Show live feedback as user speaks console.log("Partial:", data.text); }, onCommittedTranscript: (data) => { // Final transcript for this segment setTranscript((prev) => prev + data.text); }, }); const startRecording = async () => { const tokenResponse = await fetch("/scribe-token"); const { token } = await tokenResponse.json(); await scribe.connect({ token, microphone: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }, }); }; const stopRecording = () => { scribe.disconnect(); }; return ( <div> <div>Status: {scribe.status}</div> <button onClick={startRecording}>Start</button> <button onClick={stopRecording}>Stop</button> <p>{transcript}</p> </div> ); }

4.1 Commit 策略:麦克风输入必须用 VAD

Important:useScribe默认提交策略是CommitStrategy.MANUAL,即必须显式调用scribe.commit()才会产出最终转写。对麦克风实时输入,务必设置CommitStrategy.VAD,让服务端在检测到静音时自动提交。如果不设置,committed转写永远不会触发,连接甚至可能因长期无提交而断开。

两种策略的取舍在 realtime-commit-strategies.md 中有完整说明:

策略行为适用场景
Manual由你调用commit()完成段提交文件处理、由你控制音频分段
VAD检测到静音自动提交实时麦克风输入、对话式应用

VAD 还有一组可调参数(React 侧以 Hook 选项传入):

const scribe = useScribe({ modelId: "scribe_v2_realtime", commitStrategy: CommitStrategy.VAD, // Optional VAD tuning: vadSilenceThresholdSecs: 1.5, // Silence duration before commit(默认 1.5s) vadThreshold: 0.4, // Speech detection sensitivity 0-1(默认 0.4,越小越灵敏) minSpeechDurationMs: 100, // Minimum speech length required(默认 100ms) minSilenceDurationMs: 100, // Minimum silence length required(默认 100ms) });

4.2 两类转写结果:partial 与 committed

理解实时转写,首先要分清两类输出(详见 SKILL.md 与 realtime-commit-strategies.md):

类型说明用途
Partial(部分转写)随音频处理高频更新的"当前最佳猜测"边说边显示的字幕反馈;不要落库,随时可能被修正
Committed(提交转写)提交后稳定不变的最终结果应用的"事实来源(source of truth)",可安全拼接与保存
Committed + Timestamps带词级时间戳的最终结果字幕、卡拉 OK、口型同步

4.3 scribe.status 状态机

StatusMeaning
"disconnected"无活动连接
"connecting"正在建立连接
"connected"已连接,可接收音频
"transcribing"正在处理语音(检测到音频或 VAD 提交时由"connected"转入)
"error"发生错误

Important:判断会话是否处于活动状态时,必须同时检查"connected""transcribing"。因为在语音处理期间状态会切到"transcribing",只检查"connected"会导致按钮、波形、指示灯等 UI 元素在 VAD 提交时被错误重置。

// Correct - handles both active states const isListening = scribe.status === "connected" || scribe.status === "transcribing"; // Wrong - will flicker/reset when VAD commits const isListening = scribe.status === "connected";

4.4 VAD 的底层原理

VAD(Voice Activity Detection)监听静音并在说话人停顿时自动提交,从而产出贴合人类自然说话节奏(句间、思想间停顿)的转写分段。其推荐使用场景是实时麦克风输入与对话式应用;而 Manual 提交则适合文件处理、已知分段边界、需要最大时间控制的场景(对应 realtime-commit-strategies.md)。若采用 Manual 策略,官方还给出最佳实践:每 20–30 秒提交一次、在静音或逻辑断点(句末、说话人切换)提交、若 90 秒无手动提交则自动提交。

五、JavaScript 实现:Scribe.connect 事件驱动

不依赖 React 的场景(原生 JS、Vue、小程序等)使用@elevenlabs/clientScribe.connect。与 React Hook 的回调注册方式不同,底层客户端采用事件监听模型,事件名与 WebSocket 消息类型一一对应:

import { Scribe, RealtimeEvents } from "@elevenlabs/client"; async function startTranscription() { const tokenResponse = await fetch("/scribe-token"); const { token } = await tokenResponse.json(); const connection = Scribe.connect({ token, modelId: "scribe_v2_realtime", includeTimestamps: true, microphone: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }, }); connection.on(RealtimeEvents.OPEN, () => { console.log("Connected"); }); connection.on(RealtimeEvents.PARTIAL_TRANSCRIPT, (data) => { console.log("Partial:", data.text); }); connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => { console.log("Committed:", data.text); }); connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS, (data) => { for (const word of data.words) { console.log(`${word.text}: ${word.start}s - ${word.end}s`); } }); connection.on(RealtimeEvents.ERROR, (error) => { console.error("Error:", error); }); connection.on(RealtimeEvents.CLOSE, () => { console.log("Disconnected"); }); return connection; }

5.1 事件协议:底层消息格式

理解事件驱动模型,推荐通读 realtime-events.md。该文档完整定义了实时转写的 wire protocol:

客户端 → 服务端(Sent Events):

  • input_audio_chunk:发送音频数据。关键字段包括message_type(恒为"input_audio_chunk")、audio_base_64(Base64 编码的 PCM 音频)、commit(此块后是否提交)、sample_rate(采样率,8000–48000)、previous_text(仅首块可携带,最长 50 字符,用于断线重连后的上下文续接):
{ "message_type": "input_audio_chunk", "audio_base_64": "<base64-encoded-pcm-audio>", "commit": false, "sample_rate": 16000 }
  • commit:终结当前转写段:
{ "message_type": "commit" }

服务端 → 客户端(Received Events,均以message_type作为判别字段):

事件含义
session_started连接建立成功,返回session_id与回显的会话配置(采样率、音频格式、模型 ID、提交策略、是否含时间戳)
partial_transcript部分转写,随音频处理频繁更新
committed_transcript提交后的最终转写
committed_transcript_with_timestamps含词级时间戳的最终转写(include_timestamps=true时在 committed 之后发送),每个词包含textstartendtypeword/spacing/audio_event)与可选speaker_id

错误事件与错误码:服务端通过error事件上报失败原因,常见错误码包括auth_error(Key 或令牌无效)、quota_exceeded(用量超限)、input_error(不支持的音频格式或非法输入)、rate_limited(请求过频)、commit_throttled(提交过于频繁)、session_time_limit_exceeded(会话超时)、chunk_size_exceeded(音频块过大)、insufficient_audio_activity(未检测到足够语音)、transcriber_error(内部处理错误)等。

六、手动音频分块:处理文件与自定义音频源

对于文件上传或自定义音频源(例如不是麦克风,而是从本地文件解码出的 PCM 流),需要将音频编码为 PCM-16 并按块推送给服务端:

const chunkSize = 4096; for (let offset = 0; offset < pcmData.length; offset += chunkSize) { const chunk = pcmData.slice(offset, offset + chunkSize); const bytes = new Uint8Array(chunk.buffer); const base64 = btoa(String.fromCharCode(...bytes)); scribe.sendAudio(base64); // Simulate real-time streaming await new Promise((resolve) => setTimeout(resolve, 50)); } // Finalize transcription scribe.commit();

6.1 音频格式要求

服务端对音频有明确要求(详见 realtime-server-side.md 的 Audio Requirements):

参数推荐值
格式PCM 16-bit
采样率16000 Hz(推荐),支持 8kHz–48kHz
声道单声道(Mono)
分块大小32,000 字节 ≈ 16kHz 下的 1 秒音频(服务端侧示例);客户端手动分块示例使用 4096 字节

提示:处理多声道或非 16kHz 音频时,应先做预处理。服务端示例给出了 Python 侧用 pydub 的转换思路:多声道set_channels(1)、重采样set_frame_rate(16000)、位深set_sample_width(2),再按块 Base64 编码发送。JavaScript 侧可用fs.readFileSync读取.pcm文件按chunkSize切分后toString("base64")发送,最后connection.commit()收尾。

6.2 提供上下文(previous_text)

如果需要在断线重连后续接对话,或在转写开头为模型提供语境,可在首个音频块中携带previous_text(不超过 50 字符)。这有助于:续接重连后的对话、提升上下文准确性、处理句子碎片(对应 realtime-commit-strategies.md)。

七、麦克风选项:浏览器采集参数

connect时传入的microphone配置直接映射到浏览器getUserMedia约束,用于改善采集质量:

OptionDescription
echoCancellation消除扬声器回声
noiseSuppression过滤背景噪声
autoGainControl归一化音量水平

在"说话人 A 对着扬声器讲话、扬声器同时播放对方声音"这类视频会议场景中,echoCancellation尤其重要——否则转写会把回放的对方语音也当作输入。

八、安全底线

浏览器端实时转写存在天然的密钥泄露风险,必须遵守以下三条红线(对应原文档 Security 章节):

  • 绝不在客户端暴露你的 API Key:API Key 只存在于服务端环境变量(如ELEVENLABS_API_KEY),任何前端代码、构建产物、网络请求中都不应出现。
  • 始终在后端生成单次使用令牌:通过elevenlabs.tokens.singleUse.create("realtime_scribe")签发,前端只持有一次性、15 分钟有效的令牌。
  • 用鉴权中间件保护令牌接口/scribe-token这类端点必须挂在yourAuthMiddleware之后,防止匿名用户刷取令牌造成额度消耗。

九、在 OpenMontage 中的落地与延伸

9.1 与仓库转写工具链的配合

OpenMontage 的转写能力呈现"本地离线 + 云端实时"双轨结构:

  • 本地离线:Transcriber 工具 基于 faster-whisper 提供确定性的批量转写(词级时间戳、VAD 过滤、GPU 探测与 CPU 回退、WhisperX 说话人分离),输出{文件名}_transcript.json,适合音视频文件的后期字幕生成。
  • 云端实时:本文的 Scribe v2 Realtime 客户端流式方案,适合交互式、低延迟场景(直播字幕、会议实时记录、语音助手)。

两种路径产出相同的"文本 + 词级时间戳"数据结构,可统一喂给下游的 字幕同步技能 或 subtitle-sync 技能 完成 SRT/ASS 字幕生成,再交给 Remotion 合成器 渲染为带字幕的视频成品。

9.2 延伸:WebSocket 直连

在不便使用 SDK 的场景(例如非 JS 语言、嵌入式环境),可以绕过 SDK 直连 WebSocket 端点(详见 realtime-server-side.md):

wss://api.elevenlabs.io/v1/speech-to-text/realtime?model_id=scribe_v2_realtime

直连时的消息格式与上文事件协议完全一致:上行input_audio_chunk/commit,下行以message_type区分的各类转写事件。这意味着本文描述的令牌签发、音频格式、事件语义在直连场景下同样适用——你完全可以用同一套后端令牌体系驱动任意语言的实时转写客户端。

十、快速自检清单

落地前对照检查以下要点,可避免绝大多数"连接建了但没结果"的坑:

  1. ✅ 包名是否为@elevenlabs/react/@elevenlabs/client/@elevenlabs/elevenlabs-js(拒绝旧版elevenlabs)。
  2. ✅ API Key 是否仅存在于服务端,令牌接口是否挂了鉴权中间件。
  3. ✅ 麦克风输入是否设置了CommitStrategy.VAD(默认 Manual 会导致 committed 永不触发)。
  4. ✅ UI 活动态判断是否同时覆盖"connected""transcribing"
  5. ✅ 手动推流时音频是否为 PCM-16 / 单声道 / 16kHz,块大小是否在服务端接受范围内。
  6. ✅ 是否处理了error事件并映射错误码(auth_errorrate_limitedchunk_size_exceeded等)。

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 15:32:30

TVBoxOSC 上手手册:三步跑起电视端统一媒体中心

TVBoxOSC 上手手册&#xff1a;三步跑起电视端统一媒体中心 【免费下载链接】TVBoxOSC TVBoxOSC - 一个基于第三方项目的代码库&#xff0c;用于电视盒子的控制和管理。 项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC 你窝在沙发里拿着遥控器&#xff0c;…

作者头像 李华
网站建设 2026/9/10 15:31:15

电梯内电动车识别:小目标+遮挡+低光照实战方案

简介&#xff1a;本资源是面向智能安防与计算机视觉开发者、AI算法工程师及高校科研人员的电梯内电动车识别专用数据集&#xff0c;旨在解决住宅与公共建筑中电动车违规进梯引发的安全监管难题。数据集共7111张真实场景电梯内部图像&#xff0c;全部标注为COCO格式&#xff0c;…

作者头像 李华
网站建设 2026/9/10 15:30:14

网络与信息安全专业Python毕设选题指南与创新方向

1. 网络与信息安全专业Python毕设选题现状分析2026届网络与信息安全专业的同学们正面临着一个关键抉择——如何选择一个既符合专业要求又能体现个人技术特色的Python毕业设计题目。作为带过5届毕业设计的导师&#xff0c;我发现学生们普遍存在三个典型困境&#xff1a;首先是选…

作者头像 李华
网站建设 2026/9/10 15:26:17

老 Mac 升级 macOS 完整指南:用 OpenCore Legacy Patcher 安装新系统

老 Mac 升级 macOS 完整指南&#xff1a;用 OpenCore Legacy Patcher 安装新系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher 是…

作者头像 李华