返回博客
2026年10月19日Sergei Solod22 分钟阅读

如何使用 Whisper 和 WebAssembly 为网站添加保护隐私的语音转文字功能

使用 Whisper、Transformers.js、ONNX Runtime Web、AudioWorklet 和 WebAssembly,在浏览器端实现完整的语音输入。了解如何优化移动端性能,并排查导致语音识别无法运行的运行时、Worker 和分词器错误。

语音转文字WhisperWebAssemblyAudioWorkletJavaScript

在网站上添加一个麦克风按钮,乍看很简单:采集用户的语音、识别说话内容,再将文字填入输入框。但要让这套功能在桌面和移动浏览器中稳定运行,实际困难得多,尤其是在所有语音识别都必须在本地完成、麦克风音频不能上传到服务器的情况下。

难点不只是选用哪种语音识别模型。要真正实现可用的功能,还必须妥善处理麦克风权限、音频采集、重采样、浏览器 Worker、WebAssembly、ONNX 模型初始化、分词与解码、缓存,以及与应用文本输入框的集成。

本文介绍如何使用 OpenAI Whisper、Transformers.js、ONNX Runtime Web、AudioWorklet 和独立 Web Worker 构建一套实用且保护隐私的语音转文字功能,并分析几个真实的集成故障:即使模型文件已经下载成功,麦克风按钮仍可能一直显示加载状态。

目标很明确:访客点击麦克风按钮,自然说话,识别出的文字就会出现在现有输入框中。提交前,访客可以检查或修改结果。麦克风录音本身无需离开浏览器。

什么是真正完全在客户端运行的语音识别

为网站加入语音输入,基本上有两种不同的实现方式。

第一种是使用语音识别服务。浏览器采集音频,将其发送给远程服务,再接收识别出的文字。这种方式可以简化开发,但也会带来网络依赖、隐私问题、服务提供商的可用性限制,以及可能按用量计费的成本。

第二种是在浏览器内直接执行推理。网站下载语音识别模型及其运行环境;浏览器采集麦克风音频,将其转换为数值采样数据,在本地运行模型,并且无需语音识别服务器就能生成转写文本。

本文只讨论第二种方式。

不要把一般的浏览器语音识别误认为一定在本地运行。Web Speech API 可能使用由服务器支持的识别引擎。部分较新的实现提供 processLocally 选项,但不同浏览器和操作系统的支持情况并不一致。如果必须严格保证本地处理,就不能将通用的 SpeechRecognition 用作自动回退方案。

应用应当自行控制整个推理流程。

麦克风
    |
    v
AudioWorklet
    |
    v
Float32 PCM 音频
    |
    v
独立 Web Worker
    |
    v
16 kHz 重采样
    |
    v
Whisper + ONNX Runtime Web
    |
    v
分词与文本解码
    |
    v
React 输入状态

网站仍然需要提供 JavaScript、WebAssembly、配置文件和模型权重等资源,这些只是常规静态文件下载。关键区别在于:麦克风音频在本地处理,不会为了识别而上传。如果用户之后通过普通表单提交识别出的文字,那是应用中的另一项独立操作。

如何选择实用的模型:Whisper Tiny 还是 Base

Whisper 是一系列多语言语音识别模型。它可以用同一个模型识别多种语言,因此特别适合多语言网站。

对于简短的语音消息,Whisper Tiny 和 Whisper Base 能在识别质量与浏览器资源消耗之间提供较实用的平衡。

模型所选 Q8 ONNX 权重的大致大小建议用途
Whisper Tiny41 MB移动设备和低资源环境的保守选择
Whisper Base77 MB适用于已测试且性能足够的设备,识别质量更高

这些大小仅指所检查模型导出版本中选用的编码器和解码器 ONNX 文件,不包含全部辅助文件,也不能等同于浏览器运行时的峰值内存占用。

Whisper Tiny 的优势是下载更快、通常占用更少计算资源,在移动设备环境不明确时也更稳妥。Whisper Base 通常能提供更高的识别质量,尤其是在某些语言中,小模型更容易出错。

不能只根据设备的物理 RAM 容量选择模型。浏览器实际可用的内存可能远低于设备标称值。页面已有的内存占用、WebAssembly 内存分配、临时张量,以及操作系统的内存压力都需要考虑。

比较稳妥的策略是:对性能未知的移动设备先使用 Tiny,只在确认设备能力足够时选用 Base;如果 Base 初始化失败,再回退到 Tiny。

加载 Tiny 前,必须先彻底释放失败的 Base 实例。如果两个模型同时驻留内存,回退机制就失去了意义。

为什么 WebAssembly 适合作为生产环境的默认方案

ONNX Runtime Web 支持在浏览器内执行神经网络。对于这里的工作负载,WebAssembly 能在主流桌面和移动浏览器中提供实用的 CPU 推理方案。

WebGPU 可以在合适的硬件上加速受支持的模型,但能使用 WebGPU 并不等于语音识别流程一定可用。即使浏览器暴露了 navigator.gpu,也不能保证 ONNX Runtime 支持该平台、特定 Whisper 计算图能正确初始化,或反复推理时不会出现资源问题。

因此,保守的做法是在生产环境使用 WASM 作为后端,等完成兼容性、准确率、内存和重复会话测试后,再单独评估 WebGPU。

初始版本使用单线程 WASM,还能避免整个网站依赖跨源隔离和 SharedArrayBuffer。

env.backends.onnx.wasm.numThreads = 1;
env.backends.onnx.wasm.proxy = false;

将推理放在独立 Worker 中,可以避免高开销计算占用主 JavaScript 线程。模型处理音频时,输入框、虚拟键盘、页面滚动及应用界面仍能保持响应。

不要让语音识别影响页面首次加载

不使用麦克风功能的访客,不应该为此承担明显的网络流量和内存开销。

不要在应用初始包中提前导入 Transformers.js。页面打开时不要初始化 ONNX Runtime,也不要在浏览器空闲时自动下载模型权重。

只有用户明确点击麦克风按钮后,才加载整套识别组件。

首次使用时,可以按以下顺序执行:

  1. 接收用户对麦克风按钮的点击。
  2. 通过 getUserMedia() 请求麦克风权限。
  3. 如果权限遭拒,直接显示错误,不下载模型。
  4. 如果权限获准,准备模型期间先释放临时麦克风流。
  5. 动态初始化独立 Worker,并加载选定的本地模型。
  6. 等待 Worker 确认识别流程已准备就绪。
  7. 如果用户尚未取消操作,则自动开始录音。
  8. 显示清晰易懂的正在聆听状态。

权限请求必须与用户操作关联。应用还要处理用户取消以及各浏览器特有的采集限制,不能假设录音总能在后台无提示地重新开始。

准备模型时应显示明确状态,例如正在准备语音输入。如果加载指示器始终没有清晰的状态变化,即使模型成功下载,用户也可能以为麦克风坏了。

初始化后,可以暂时保留 Worker,以便用户接着进行下一次语音输入。将闲置超时设为约一分钟,是一个合理的起点。超时后终止 Worker,释放内存中的模型。浏览器缓存的模型文件仍可供以后使用。

使用 AudioWorklet 采集原始音频

Whisper 需要数值形式的音频样本。MediaRecorder 很适合录制音频文件,但在本地语音识别场景中使用它,可能引入不必要的编码和解码过程。

更简单的处理路径如下:

getUserMedia()
    |
    v
AudioContext
    |
    v
AudioWorklet
    |
    v
Float32 PCM 缓冲区
    |
    v
独立 Worker

AudioWorklet 在应用的主 JavaScript 线程之外运行音频处理代码。它可以采集 PCM 样本、将多声道音频混合为单声道、计算基本音量指标,并把积累的缓冲区传给 Worker。

麦克风初始化的简化示例如下:

const stream = await navigator.mediaDevices.getUserMedia({
  audio: {
    channelCount: { ideal: 1 },
    echoCancellation: true,
    noiseSuppression: true,
    autoGainControl: true,
  },
});

const context = new AudioContext();

await context.audioWorklet.addModule(
  '/assets/speech/pcm-worklet.js'
);

const source = context.createMediaStreamSource(stream);
const processor = new AudioWorkletNode(
  context,
  'pcm-capture'
);

source.connect(processor);

// Connect the worklet through an inaudible output path
// if required to keep the audio processing graph active.

麦克风约束只是偏好设置,并非保证。不同浏览器可能使用不同的回声消除、噪声抑制和自动增益控制算法。

一个重要细节是:麦克风采样率不一定是 16 kHz。很多音频上下文使用 44.1 kHz 或 48 kHz,而 Whisper 需要 16 kHz 的输入,因此必须对采集到的样本正确重采样。

简单地每隔两个或三个样本丢弃一个,并不是普遍正确的解决办法。采用合适的低通重采样器(例如窗函数 sinc 实现),可以在降低采样率时减少混叠,并保留有用的语音频率成分。

重采样应放在独立 Worker 中执行,而不是在实时 AudioWorklet 回调中进行。

如何高效地在线程之间传输音频

AudioWorklet 可能产生大量小块音频数据。如果把每个音频渲染处理周期都作为单独消息转发,就可能产生不必要的开销。

实用的做法是先将 PCM 样本积累成较短的数据批次,再以 ArrayBuffer 的形式传输。

worker.postMessage(
  {
    type: 'audio-chunk',
    buffer,
  },
  [buffer]
);

传输列表很重要:它会转移 ArrayBuffer 的所有权,而不是额外完整复制一次缓冲区。

Worker 可以将这些数据块追加到音频累积缓冲区,并在录音结束后合并。

还应记录实际采集采样率、收集到的样本数量和录音时长。这些元数据有助于诊断静音录音问题,而无需记录敏感音频数据。

无需增加另一套神经网络,也能检测停顿

短文本语音输入不一定需要神经网络语音活动检测器。

自适应 RMS 检测器可以用较低的开销近似判断是否有人说话。RMS 衡量信号幅度,因此应用可以估计先前正在说话的用户何时安静下来。

界面始终应该允许用户再次点击麦克风按钮手动停止录音。自动检测静音只是便利功能,并非成功识别的必要条件。

以下参数可作为短时语音输入的合理初始值:

参数初始值
分析间隔20 ms
噪声校准300 ms
语音确认140 ms
自动停止前的静音时长1.4 秒
无语音超时8 秒
最长录音时长30 秒

这些只是配置的起始参考值,并非普遍适用的最优阈值。轻声说话、背景音乐以及麦克风降噪处理都可能影响 RMS 测量。

与其自动截断句尾,不如优先让用户手动结束录音。

对于普通的短文本输入,一次性转写通常比反复调用 Whisper 生成中间结果更简单。先录下整段话,停止录音后只运行一次模型,再插入最终识别文本。

完整的 Whisper 流程不仅需要 ONNX 模型

这是实现过程中最重要的细节之一。

Whisper 推理并不是简单调用 ONNX 编码器后再调用 ONNX 解码器。完整的自动语音识别流程还需要提取音频特征、进行分词、配置文本生成过程,并将预测的 token 解码为可读文字。

完整处理流程如下:

麦克风原始 PCM 音频
        |
        v
重采样至 16 kHz
        |
        v
Whisper 特征提取器
        |
        v
ONNX 编码器
        |
        v
ONNX 解码器与 token 生成
        |
        v
Whisper 分词器
        |
        v
识别文本

ONNX 会话成功初始化,只能证明模型计算图可以加载,并不能证明整个识别流程能够运行。

在一项经过测试的 Transformers.js 集成中,模型会话可以初始化,但转写仍然失败,因为本地构建的流程没有正确配置所需的处理器和分词器。

兼容的实现方案使用本地模型元数据,显式初始化这些组件。

下面是这种特定版本实现方式的简化示意,并非完整、可独立运行的 Worker:

import {
  WhisperTokenizer,
  WhisperFeatureExtractor,
  WhisperProcessor,
} from '@huggingface/transformers';

// These values come from local model configuration files.
const tokenizer = new WhisperTokenizer(
  tokenizerJson,
  tokenizerConfig
);

const featureExtractor = new WhisperFeatureExtractor(
  preprocessorConfig
);

const processor = new WhisperProcessor(
  {},
  {
    tokenizer,
    feature_extractor: featureExtractor,
  },
  ''
);

// In the affected version-specific integration:
recognizer.tokenizer = tokenizer;
recognizer.processor = processor;

具体构造函数签名和集成方式必须以所安装的 Transformers.js 版本为准。有些版本或模型导出文件可以自动创建这些组件,因此不能把手动创建视为普遍要求。

完整流程准备好后,可以明确指定语言和任务,调用短语音转写:

const result = await recognizer(audio16k, {
  language: 'en',
  task: 'transcribe',
  return_timestamps: false,
});

如果应用已经知道用户使用的语言,显式传入该语言就能避免不必要的语言检测,也可能让短录音的结果更可预测。

锁定兼容的 Transformers.js 与 ONNX Runtime 版本

第二类重要故障与 Transformers.js 和 ONNX Runtime Web 之间的配合有关。

Transformers.js 提供较高层的识别流程,ONNX Runtime Web 则负责执行神经网络计算图。这两个软件包必须相互兼容,也必须兼容实际使用的 ONNX 模型导出文件。

在所调查的实现中,某个 ONNX Runtime Web 开发版本可以初始化 Whisper 编码器,却在创建量化解码器会话时失败。错误涉及 DequantizeLinear 操作缺少量化缩放系数。

这既不是麦克风权限问题,也不是下载或音频格式问题。浏览器能够收到模型文件,但运行时环境无法初始化其中一个必需的模型计算图。

通过固定一个稳定的 ONNX Runtime Web 版本,并直接测试编码器和解码器,解决了这次特定的不兼容问题。

兼容性检查尤其有价值,因为它区分了两个不同的结论:

  • 模型文件已经成功下载。
  • 运行时环境已经成功使用该模型文件创建可用的推理会话。

两者不能画等号。

对于经过测试的模型导出版本,Transformers.js 4.2.0 搭配 ONNX Runtime Web 1.24.3 可以正常工作。更新的开发版运行时在量化解码器计算图上失败。这只是针对特定版本的观察结果,并不能说明旧版运行时在所有情况下都更好。

覆盖间接依赖的运行时版本时,应在包锁定文件中固定精确版本,并记录覆盖原因。升级运行时之后,必须执行完整的浏览器推理回归测试,不能只检查软件包是否安装成功。

调试 Whisper 前,先检查 Worker 打包格式

JavaScript Web Worker 可以通过不同的模块格式创建。

一次集成故障的原因是:应用将 Worker 作为 ES 模块启动,但生成的 Worker 包却使用经典 Worker 的 importScripts() 机制。

这两种执行模型不能互换。模块 Worker 无法直接执行仅适用于经典 Worker 的代码。

实际经验是:Worker 构造函数必须匹配真正生成的打包结果,而不是照搬通用教程里的构造参数。

例如,下面两种写法并不必然等价:

// Module worker: requires a compatible module bundle.
new Worker(workerUrl, { type: 'module' });

// Classic worker: requires a compatible classic bundle.
new Worker(workerUrl);

正确的选择取决于打包工具及其生成的 JavaScript。

排查 Worker 初始化错误时,应检查浏览器控制台、生成的 Worker 文件和网络面板,确定故障是否发生在 ONNX Runtime 开始初始化之前。

有些被打包的浏览器库还会根据自身模块的位置计算 URL。将它们打包进 Worker 时,需要测试最终运行时资源的路径,不能想当然地认为原始软件包相对路径仍然正确。

为 WASM 和 JavaScript 模块配置正确的 MIME 类型

即使 JavaScript 和 ONNX 模型文件完全兼容,Web 服务器返回错误的响应头也可能导致运行失败。

一次具体故障是:扩展名为 .mjs 的 JavaScript 模块被服务器以 application/octet-stream 类型返回。浏览器发现响应类型不符合 JavaScript 模块执行要求,因此拒绝加载该模块。

通常应使用以下响应类型:

文件扩展名Content-Type
.mjsapplication/javascript
.wasmapplication/wasm

应检查生产环境实际返回的响应,而不只是检查服务器配置文件。

curl -I https://example.com/runtime/speech/v2/runtime.mjs
curl -I https://example.com/runtime/speech/v2/runtime.wasm

上面的 URL 仅作示例。实际使用时,应替换为所选运行时构建版本中带版本标识的资源文件名。

HTTP 状态码正确也不代表一定能运行。浏览器仍可能因为 MIME 类型、内容安全策略(Content Security Policy)、模块执行模式或资源 URL 错误而拒绝文件。

请注意:同一请求在浏览器标签页中直接打开可能成功,但作为可执行模块或 WebAssembly 依赖加载时却可能失败。

使用不可变资源 URL,同时避免用户被损坏的运行时缓存困住

Whisper 模型文件比较大,因此浏览器缓存非常重要。

采用带版本号且内容不可变的资源路径,可以让浏览器复用未变化的模型和运行时文件,不必在每次语音输入时重新下载。

典型的缓存策略如下:

Cache-Control: public, max-age=31536000, immutable

但这也意味着部署时必须遵守一项要求:URL 一旦声明为不可变,就不应在不更改 URL 的情况下悄悄替换文件内容。

如果必须更换运行时二进制文件,就应发布到带有新版本标识的 URL。

/models/speech/v1/
/runtime/speech/v1/
/runtime/speech/v2/

更改运行时路径不是形式问题。如果更新了不可变 URL 对应的文件内容,却继续使用同一个 URL,浏览器可能仍会使用此前缓存的不兼容 WebAssembly 二进制文件。

JavaScript 胶水代码与 WebAssembly 文件最好来自同一个运行时发行版本。混用不同版本的运行时文件,可能引发难以排查的初始化故障。

在构建阶段创建清单,记录预期文件、大小和加密校验和,可以在部署前发现资源缺失或内容不匹配。

应配置识别库只使用站点自身托管的资源,并禁用远程模型加载。这样本地模型缺失时就会明确报错,而不会意外从外部模型托管站点下载。

为什么麦克风一直转圈,却什么也识别不出来

开发中一种常见现象是:点击麦克风后出现权限请求和加载动画,最终却进入错误状态,输入框中没有任何文字。

浏览器甚至可能显示麦克风正在使用中。

这不能证明语音识别器正常工作,只能证明某个时刻麦克风采集获得了许可或处于活动状态。

下面几种互不相关的故障,可能呈现几乎相同的症状:

  1. JavaScript 模块 MIME 类型错误。浏览器下载了运行时模块,却拒绝执行。
  2. Worker 执行格式不匹配。编译后的 Worker 使用经典 Worker API,却被作为模块 Worker 启动,或反过来。
  3. ONNX 解码器不兼容。运行时成功创建编码器会话,却在量化解码器计算图上失败。
  4. 识别流程不完整。ONNX 会话虽然存在,但分词器或音频处理器没有正确初始化。

必须分别诊断这些问题。修复其中一个之后,另一个问题才可能显现出来。

调试时最重要的原则是:HTTP 200 不代表模型成功初始化,而模型成功初始化也不代表转写成功。

即使转写成功,也无法证明结果已经写入应用实际使用的文本框。

整个流程的每个边界都需要有意义的诊断信号。

在不收集私人语音内容的情况下增加可观测性

有用的诊断日志不需要记录用户说了什么。

应用可以追踪如下技术状态:

permission-granted
worker-created
runtime-loading
runtime-ready
encoder-ready
decoder-ready
recording-started
pcm-received
recording-stopped
inference-started
inference-completed
result-inserted

发生故障时,应记录所处阶段、稳定的技术错误代码以及经过脱敏处理的错误元数据。

模型初始化失败应与麦克风权限被拒或空白录音区分开来。开发期间,Worker 故障应该可见,而不是被通用错误处理器吞掉。

不要记录麦克风样本、识别出的句子、token 序列或其他语音内容。性能遥测可以只记录时长区间、选用的模型档位、初始化耗时、推理耗时和错误类别。

这样既能更容易地排查麦克风故障,又不会削弱隐私保护要求。

通过 React 状态插入转写文本

只有最终识别文本进入现有表单输入框,语音识别功能才真正有用。

在 React 应用中,输入框通常由组件状态控制。直接赋值给 input.value 可能与框架的渲染机制冲突。

因此,应让识别文本沿用普通键盘输入使用的状态更新路径。

集成时还必须考虑异步行为:语音识别完成前,用户可能已经继续输入了字符,或者移动了光标。

如果使用录音开始时保存的旧输入值,就可能不小心覆盖用户之后的编辑。

一种简化的集成方式如下:

const insertion = insertTranscript({
  currentValue: latestValue,
  transcript: recognizedText,
  selection: currentSelection,
  maxLength: inputLimit,
});

setMessage(insertion.value);
restoreCaret(insertion.caret);

插入辅助函数应保留当前文本、在需要时替换选中的范围、合理处理空格,并遵守输入框的最大长度限制。

还需要注意 Unicode:JavaScript 字符串长度按 UTF-16 代码单元计算。如果在任意位置截断字符串,可能把一个代理项对拆开。

最重要的是,完成语音识别后绝不能自动提交表单。发送前必须允许用户检查并修改文本。

处理故障时,不要影响正常的文字输入

语音子系统应该是可选功能。

无论麦克风权限遭拒、浏览器无法为模型分配内存、Worker 崩溃,还是录音没有捕获到语音,普通文本输入框都必须继续可用。

应通过明确的状态来表示麦克风的生命周期,例如:

idle
preparing
listening
recognizing
error

应提供无障碍可访问的标签和可见的状态提示。如果加载后麦克风只是变成红色或粉色,用户几乎无法知道发生了什么。

用户取消操作时,应清理音轨、音频上下文、待处理缓冲区和当前录音会话。界面还应防止较早取消的录音在稍后返回过期结果,并覆盖较新的输入状态。

如果大模型因为内存或初始化限制而失败,在尝试小模型前必须彻底释放它。还应限制重试次数,避免浏览器反复下载或初始化根本无法运行的模型。

本地识别失败时,不要悄悄切换到云端语音服务。如果严格要求只在本地处理,那么本地识别器失败后也必须在本地报错结束。

测试完整的浏览器处理流程

TypeScript 类型检查或生产构建成功,不足以证明语音输入功能正常。

同样,浏览器能下载 ONNX 文件,也不能证明编码器和解码器两个模型会话都成功初始化。

最有价值的回归测试,应使用与生产环境相同的 Worker、运行时文件、模型、处理器和分词器。

将一段内容已知的简短 PCM 录音送入真实的浏览器推理流程,确认返回非空转写文本。

随后,在真实浏览器中测试完整的麦克风交互。

  • 页面首次加载:确认用户使用麦克风前,不会下载语音模型或运行时资源。
  • 冷启动初始化:检查麦克风权限、模型请求、JavaScript 和 WASM 的 MIME 类型,以及 Worker 真正就绪的响应。
  • ONNX 初始化:确认编码器与解码器会话都能创建。
  • 音频采集:确认 AudioWorklet 能产生非空 PCM 缓冲区。
  • 重采样:确认 Worker 收到真实采样率,并生成有效的 16 kHz 音频。
  • 手动停止:确认第二次点击麦克风能可靠地停止录音。
  • 自动停止:确认自然停顿会结束录音,同时不会截断轻声说出的内容。
  • 转写:确认完整的处理器、模型与分词器能够返回可辨认的文字。
  • 输入框集成:确认识别文本出现在真正受状态控制的输入框中,且不覆盖用户编辑。
  • 故障恢复:测试权限被拒、文件缺失、Worker 错误、模型初始化失败、取消操作和回退到小模型。
  • 隐私:检查网络请求,确认麦克风音频没有上传到服务器或分析服务。
  • 移动端兼容性:在真实手机上重复测试,包括冷启动、热启动及连续多次语音输入。

预录制 PCM 测试非常适合验证推理组件,但不能证明麦克风采集正常。最终验收测试必须包含真实麦克风和实际用户界面。

可靠的浏览器语音输入架构

对于需要简短、多语言且注重隐私的语音输入功能的网站,最实用的架构是刻意保持简单:

React 麦克风按钮
        |
        v
请求权限
        |
        v
按需初始化模型
        |
        v
AudioWorklet 采集 PCM
        |
        v
自适应 RMS / 手动停止
        |
        v
独立 Worker
        |
        v
16 kHz 音频重采样
        |
        v
Whisper Tiny 或 Base Q8
        |
        v
兼容的 ONNX Runtime WASM
        |
        v
Whisper 处理器 + 分词器
        |
        v
最终转写文本
        |
        v
由 React 状态控制的输入框

应用应自行托管带版本标识的模型和运行时文件,在主线程之外执行推理,避免不必要的模型下载,使用完毕后释放资源,而且绝不为了语音识别上传麦克风音频。

WebGPU 加速、神经网络语音活动检测、持续生成部分转写结果以及增加模型档位等更复杂的方案,可以等实测数据证明其复杂性值得付出时再考虑。

最值得记住的经验,不是某个特定的库版本或模型大小,而是必须验证浏览器语音识别流程的每一个环节。

麦克风权限提示正常,不代表音频正在被转写;模型下载完成,不代表解码器初始化成功;ONNX 会话建立成功,不代表分词器可用;即使转写成功,也不代表应用已将文字正确写入输入框。

只有当访客能够点击麦克风、自然说话、停止录音,并在可编辑的文本框中看到识别结果,而且无需把录音发送到语音识别服务器时,这项功能才算真正完成。