打开一款AI会议助手,参会者刚说完一句话,屏幕上就出现了对应的文字。随着讨论继续,系统不断更新字幕;等到会议结束,又能得到一份相对完整、带有标点和时间信息的会议逐字稿。
这两种功能看起来都属于ASR(Automatic Speech Recognition,自动语音识别),但在工程实现中,它们面临的约束并不相同。
实时字幕首先要解决延迟问题,而会后转写更关注识别结果的完整性、准确率和可编辑性。
例如,一段30分钟的会议录音,使用离线模型花三分钟完成转写,可能已经具有不错的处理效率。但如果把同样的处理方式用于实时字幕,用户显然无法接受三分钟后才看到发言内容。
更复杂的是,实时识别经常会出现文字反复变化的情况:系统先输出“这个方案可以”,随后变成“这个方案可能”,最后才确定是“这个方案可能还需要调整”。
这不是简单的页面刷新问题,而是流式语音识别在上下文尚不完整时必须面对的技术挑战。
从Streaming ASR到离线后处理,现代AI会议助手通常需要在延迟、准确率、计算资源和用户体验之间建立一套完整的处理机制。
一、实时语音识别与离线转写,技术差异究竟在哪里?
理解两种处理方式,可以先从输入数据的区别入手。
离线ASR面对的是一段已经录制完成的音频。系统能够获取完整的语音内容,可以对音频进行分段、批量推理、文本修正和时间戳对齐。
实时ASR则不同。
麦克风持续产生新的音频数据,模型必须在后续内容尚未到达时,就尽可能输出当前识别结果。
这意味着,模型无法无限等待未来的语音上下文。
假设某位参会者正在说:
我们预计下周三……
在这句话尚未结束时,系统可能无法判断接下来是“完成开发”,还是“开始测试”。
离线模型能够利用后续完整语音辅助判断;实时模型则需要在信息不足的情况下,决定是否立即输出文字。
两种方式的主要区别可以概括如下:
| 对比维度 | 实时ASR | 离线ASR |
|---|---|---|
| 输入方式 | 持续接收音频流 | 完整音频或已切分片段 |
| 首要目标 | 降低输出延迟 | 提高完整性与识别质量 |
| 上下文 | 当前和历史语音,可有有限前瞻 | 可利用更完整的语音上下文 |
| 输出形式 | 增量结果、临时结果或最终结果 | 相对稳定的转写文本 |
| 处理方式 | 流式解码与状态维护 | 分段、批处理及后处理 |
| 典型场景 | 实时字幕、语音交互 | 会议归档、录音整理 |
这里需要澄清一点:实时与离线并不一定对应两套完全不同的模型。
一些ASR模型能够同时提供流式和离线推理模式,区别体现在推理接口、上下文使用策略和后处理流程上。
因此,真正需要区分的是两套处理思路,而不是必须部署两份独立模型。
二、Streaming ASR:把音频切成小块为什么还不够?
实时语音识别的常见做法,是将连续音频组织为多个Chunk(音频块),按时间顺序送入模型。
假设麦克风采用16kHz采样率、单声道、16位PCM格式,每秒钟产生的音频数据量为:
16000 × 2 = 32000 Bytes
也就是约31.25KiB/s。
如果每100毫秒发送一次音频,那么每次需要传输3200字节。
从网络通信角度看,这样的数据量并不大。但音频块越小,并不意味着识别结果就一定越快出现。
1. 音频传输块不等于模型推理块
一个常见误区是:只要客户端每100毫秒发送一次音频,ASR系统就能够每100毫秒输出一批准确文字。
实际上,模型可能需要累计更长时间的语音,才能进行有效解码。
例如,客户端以100毫秒为单位上传音频,服务端可以先将这些数据放入缓冲区,累计到模型要求的长度后,再执行一次推理。
这里至少存在三个不同的时间单位:
- 传输Chunk:客户端向服务器发送音频的间隔。
- 推理Chunk:模型每次进行增量解码所使用的音频长度。
- 输出间隔:用户界面实际收到文字更新的频率。
三者并不一定相等。
如果模型需要累计600毫秒的音频才能产生一次新结果,那么将网络传输间隔从100毫秒缩短到20毫秒,也未必能够显著降低实际字幕延迟。
另外,模型还可能依赖一定的Lookahead,即向后观察一小段音频来提高识别稳定性。
Lookahead越长,通常能够提供更多上下文信息,但也会增加等待未来音频的时间。
2. Cache:为什么流式模型需要保存历史状态?
假设模型正在处理如下语音:
我们今天先确认接口……
当下一段音频“调用方式”到来时,模型不能完全忘记前面已经处理过的内容。
因此,流式ASR通常需要维护一定的历史状态。
不同模型的缓存机制存在差异。例如,基于Transformer的流式编码器可能保留历史注意力计算所需的Key/Value状态,也可能维护卷积状态或一定长度的历史特征。
对于使用增量解码的模型,还可能需要保存已经生成的文本Token和解码上下文。
这种机制能够减少重复计算,并帮助模型在处理后续音频时利用之前的信息。
FunASR中的Paraformer Streaming就采用分块处理与Cache机制,能够在多次推理之间维护必要的上下文。
不过,Cache并不是越大越好。
过长的历史上下文可能增加显存占用和计算开销;过短又可能导致语句衔接不自然,甚至在音频块边界附近出现漏字或重复。
对于长时间会议,系统还需要控制每一路会话的缓存生命周期,不能让状态无限增长。
三、为什么实时字幕会反复修改?Partial Result与Final Result
使用实时字幕时,用户有时会看到一种现象:
系统刚显示的文字,过一会儿又发生变化。
例如:
第一次输出:
项目明天上线
第二次输出:
项目明天上线测试
第三次输出:
项目明天上线测试环境
这种变化并不一定代表模型出现故障。
对于支持临时识别结果的Streaming ASR系统而言,模型在上下文不完整时输出的是当前最有可能的识别假设。随着后续语音到达,识别假设可能被重新修正。
因此,实时系统常常需要区分两类结果。
Partial Result:临时识别结果。
这类结果可以快速展示给用户,但后续可能发生修改。
Final Result:当前语音片段的最终识别结果。
当系统判定当前语音片段已经结束,或者达到既定的提交条件后,可以将结果标记为Final。
需要注意,这里的Final通常只是指当前流式识别阶段不再修改该片段,并不意味着它已经通过会后离线校正。
1. 为什么不能简单地把新识别结果追加到文本末尾?
假设服务端连续返回:
{"segment_id":"seg001","is_final":false,"text":"我们明天发"}随后返回:
{"segment_id":"seg001","is_final":false,"text":"我们明天发布"}如果客户端直接把第二次结果追加到第一次结果后面,就会显示:
我们明天发我们明天发布
显然,这是错误的。
对于这种“返回当前完整假设”的接口,客户端应当根据segment_id替换同一片段的临时文本,而不是不断追加。
但也有一些流式API返回的是真正的增量文本,因此必须根据实际协议确定处理方式,不能把所有Streaming ASR接口当成同一种格式。
2. 用Python演示字幕的更新逻辑
下面用一段不依赖第三方库的Python代码,模拟支持Partial/Final状态的识别结果管理。
classLiveTranscript:def__init__(self):self.final_segments={}self.partial_segments={}defupdate(self,event):sid=event["segment_id"]text=event["text"]ifevent["is_final"]:self.final_segments[sid]=text self.partial_segments.pop(sid,None)elifsidnotinself.final_segments:self.partial_segments[sid]=textdefget_text(self):segments={**self.final_segments,**self.partial_segments}return"\n".join(segments.values())events=[{"segment_id":"seg001","is_final":False,"text":"我们明天发"},{"segment_id":"seg001","is_final":False,"text":"我们明天发布"},{"segment_id":"seg001","is_final":True,"text":"我们明天发布测试版本"},{"segment_id":"seg002","is_final":True,"text":"下周继续验证"}]transcript=LiveTranscript()foreventinevents:transcript.update(event)print("当前字幕:")print(transcript.get_text())print("------")最终得到:
我们明天发布测试版本 下周继续验证这段代码展示的是字幕状态管理,而非真实ASR模型推理。
在生产环境中,还需要考虑网络重连、消息乱序、事件去重、会话终止等问题。尤其当多个异步任务同时处理音频时,最好为每个结果增加序列号和修订版本,避免较早产生的结果覆盖较新的识别内容。
对于用户而言,字幕频繁跳动会影响阅读体验;但如果完全不允许修改临时结果,又可能保留更多识别错误。
因此,实时字幕实际上还涉及一项交互设计问题:什么时候允许文字变化,什么时候将文字固定下来。
四、VAD与双遍识别:如何兼顾实时性和最终准确率?
实时ASR还有一个关键问题:系统如何判断一句话已经结束?
传统方案可能根据固定时长切分音频,但固定切分容易将完整语句拆开。
例如:
我认为这个版本应该……(短暂停顿)……先完成回归测试。
如果系统在停顿位置直接截断,就可能把后半句话当成新的独立语句。
这也是为什么许多实时语音系统会结合VAD(Voice Activity Detection)和Endpoint Detection(语音端点检测)。
1. VAD不是简单地检测有没有声音
VAD主要判断当前音频是否包含语音活动。
在实际系统中,还需要进一步决定什么时候开始一个语音片段,什么时候将其结束。
例如,可以设置一定长度的静音持续时间作为结束条件,但阈值过短可能导致一句话被拆成多段;阈值过长则会让系统迟迟无法提交最终结果。
实际还需要考虑短促语音、停顿、噪声和语速变化。
因此,端点检测不是单独追求快速切分,而是需要在识别延迟与语音片段完整性之间取得平衡。
2. Two-pass ASR:实时输出之后,再进行一次校正
对于同时要求实时字幕和较高转写质量的系统,可以采用Two-pass ASR(双遍语音识别)的架构。
其基本思路是:
第一遍:Streaming ASR
持续处理输入音频,优先提供低延迟的在线识别结果。
第二遍:Offline ASR
在一个语音片段结束后,对已经获得的完整片段重新识别或校正,提高最终文本质量。
FunASR的2pass运行模式就采用了这类设计:实时模型提供在线结果,语句结束后使用离线识别结果进行修正。
例如:
实时显示:
这个接口需要重新适配
第二遍修正:
这个接口需要重新压测
对于用户来说,会议过程中可以先看到字幕;在当前语句结束后,系统再更新较为稳定的版本。
当然,双遍识别也有额外成本。
首先,同一段音频可能需要经过两次不同形式的推理,从而增加计算开销。
其次,在线识别结果和离线修正结果必须通过明确的片段标识进行关联,不能简单按照文本长度或到达顺序合并。
最后,如果后续还有说话人识别和会议纪要生成,就需要明确这些模块使用的是在线临时文本,还是校正后的正式文本。
否则,ASR虽然完成了修正,下游系统却仍然保存旧版本内容。
这也是实时AI会议系统经常需要引入消息队列、异步任务和结果版本管理的原因。
五、实时ASR的性能指标,不能只看RTF
评价离线语音识别性能时,RTF(Real-Time Factor,实时率因子)是经常使用的指标。
计算公式为:
RTF = 音频处理耗时 / 音频时长
例如,60秒音频在12秒内完成推理,RTF就是0.2。
它说明该模型在对应测试条件下,处理音频的速度快于音频产生速度。
但RTF较低,并不代表实时字幕延迟一定较低。
假设一个系统必须等到用户说完10秒钟的语音,才能开始执行识别。即使推理只耗时0.5秒,用户依然无法在这10秒内看到任何文字。
因此,实时语音系统应该分别测量以下指标:
| 指标 | 关注的问题 |
|---|---|
| First Partial Latency | 从指定语音起点到首个临时结果出现的耗时 |
| Finalization Latency | 语句结束到最终结果提交的耗时 |
| CER / WER | 转写文本的字符或词错误率 |
| Revision Rate | 已显示的临时结果发生修订的频率 |
| P95 Latency | 95%的测量样本延迟不超过的数值 |
| RTF / 吞吐量 | 模型处理速度及整体资源效率 |
不同系统对延迟起点的定义可能不同,例如从音频采集、首帧到达服务器或VAD检测到语音开始计时。因此,性能比较时必须统一测量口径。
多路会议并发时,还会出现额外的调度问题
假设一台服务器同时处理八路会议音频,每一路都持续产生音频块。
如果所有流都直接竞争GPU资源,某些音频块可能在推理队列中等待较长时间。
可以采用异步队列、批处理调度和资源隔离等方法优化吞吐量,但这些措施也存在取舍。
例如,批量推理通常有利于提高GPU利用率,但如果为了凑齐一个Batch而额外等待,就可能影响实时字幕延迟。
因此,系统调度需要在吞吐量和尾部延迟之间取得平衡,而不是只追求GPU满载。
与此同时,生产系统还需要监控队列积压情况。如果音频持续以实时速度进入,但服务端长期处理不过来,即使短时间内没有报错,字幕延迟仍会不断增加。
在内网或本地部署环境下,这些问题尤其需要根据实际CPU、GPU、内存以及会议并发规模进行压力测试。
六、从开源ASR到国产AI会议助手,实际产品如何处理两种需求?
目前,开源语音模型已经为实时和离线两种处理方式提供了不同选择。
例如,FunASR提供Paraformer的流式与非流式模型,并具有2pass推理模式,可以作为学习双遍语音识别架构的参考。
Qwen3-ASR则采用支持流式与离线识别的统一模型路线。其官方开源实现提供0.6B和1.7B模型,并支持流式推理。不过,按照当前项目文档,流式推理依赖vLLM后端,而且暂不支持在流式接口中直接返回时间戳;如果业务需要词级或字符级时间对齐,还要结合Forced Aligner等方案设计处理流程。
这说明,模型是否支持Streaming只是选型的一项条件,还必须检查实际推理接口、时间戳、并发和部署环境是否符合业务需求。
而在面向普通用户的产品层面,实时转写与会后内容整理已经被组合进不同形态的AI会议工具。
例如,飞书妙记将会议语音转写、记录回顾和智能纪要与办公协作结合起来,适合需要会中记录和会后共享的团队。
腾讯会议提供实时字幕和转写功能,同时支持会议录制、内容回顾与智能纪要。其产品设计更重视视频会议与文字信息之间的衔接。
讯飞听见同时提供录音过程中的实时转写和已有音视频文件的转写整理功能,覆盖会议记录、访谈、培训等不同音频处理场景。
面向企业专用部署环境的国产AI会议助手,也需要解决同样的问题。例如,熙瑾会悟具备实时语音转写、说话人区分、声纹识别及智能会议纪要能力,支持企业内网部署,并完成银河麒麟、统信UOS及国产算力环境适配。
对于这种完整的会议应用而言,实时语音处理只是前端能力,会议结束后的文本整理、发言人关联和纪要生成同样属于整个业务流程。
不过,不能仅凭产品同时具有实时转写和会议纪要功能,就认定其内部一定采用了Two-pass ASR、某种流式缓存算法或特定开源模型。这些属于具体的技术实现,需要通过厂商技术文档或部署信息单独确认。
从工程角度来看,成熟的会议系统还需要处理音频采集、实时推理、数据存储、任务状态和模型异常恢复之间的协调关系。
尤其在企业内网环境下,系统既要避免依赖外部网络服务,又需要对持续运行、并发负载和资源消耗进行管理。
因此,一套实际可交付的AI会议助手,其工程复杂度往往明显高于单独运行一个ASR推理脚本。
结语:实时和准确,并不是只能二选一
实时语音识别与离线语音识别解决的是同一个基础问题,但受到不同的系统约束。
实时ASR需要在语音尚未结束时输出结果,因此必须面对上下文不足、识别回改和延迟控制等问题;离线ASR能够利用更完整的音频信息,更适合进行转写校正、时间对齐和会后资料整理。
Streaming ASR、上下文缓存、VAD端点检测和Two-pass识别,为结合两种能力提供了不同的工程手段。
与此同时,音频Chunk设计、推理队列、GPU调度和结果版本管理,也会直接影响最终用户体验。
对于国产AI会议助手而言,随着开源语音模型与本地推理框架不断完善,模型本身已经不再是唯一需要解决的问题。
真正的挑战在于,如何让语音处理系统在有限计算资源下持续运行,并在会中提供及时、可读的文字,在会后输出相对完整、准确且便于核对的会议记录。
实时转写解决的是“现在能不能看到”,会后转写解决的是“最终能不能准确记录”。
一套完整的AI会议系统,需要同时回答这两个问题。
技术资料参考
FunASR:Streaming ASR与Two-pass运行机制
https://github.com/modelscope/FunASRFunASR:在线SDK及2pass使用说明
https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_tutorial_online.mdQwen3-ASR:官方开源项目及Streaming Inference文档
https://github.com/QwenLM/Qwen3-ASRNVIDIA Riva:Streaming Speech Recognition API
https://docs.nvidia.com/deeplearning/riva/user-guide/docs/reference/protos/riva_asr.proto.htmlTurning Whisper into Real-Time Transcription System(2023)
https://arxiv.org/abs/2307.14743