VAD 技术实践:让 AI 知道什么时候该听,什么时候该回答

举报
yd_54922183 发表于 2026/10/27 10:18:20 2026/10/27
【摘要】 本文介绍 VAD 的核心原理,并通过客服录音自动切分、实时语音助手两个 Python 案例,演示如何使用轻量级 Silero VAD 检测语音片段,优化语音识别流程。

摘要: 在语音 AI 系统中,ASR 负责把声音转成文字,但它通常不会独立决定用户什么时候开始说话、什么时候真正说完。VAD(Voice Activity Detection,语音活动检测)就是解决这个问题的技术。本文介绍 VAD 的核心原理,并通过客服录音自动切分、实时语音助手两个 Python 案例,演示如何使用轻量级 Silero VAD 检测语音片段,优化语音识别流程。

一、VAD 是什么?有了 ASR 为什么还需要它?

假设我们正在开发一个 AI 语音客服。

用户说:

你好,我想查询一下……(停顿两秒)……昨天购买的商品什么时候发货。

这时系统面临一个问题:用户在中间停顿了两秒,究竟是已经说完,还是正在思考?

如果过早调用 ASR 和 LLM,AI 可能在用户还没说完时就开始回答。

但如果每次都等待很久,交互又会显得迟钝。

VAD 的核心任务,就是检测音频中什么时候存在人声,以及人声什么时候开始和结束。

需要注意,VAD 只能识别语音活动,不能真正理解用户是否表达完了一个完整的意思。

它与 ASR 的区别非常明确:

技术 解决的问题 典型输出
VAD 什么时候有人说话? 语音起止时间
ASR 说了什么内容? 转写文本
Speaker Diarization 分别是谁在说话? 说话人标签与时间段
LLM 用户表达了什么意图? 理解结果或回答

因此,VAD 往往被放在 ASR 之前,作为语音处理的第一道关卡。

1.1 VAD 的基本原理

最简单的 VAD 可以通过音频能量判断是否有人声。

但现实中,键盘敲击、空调、汽车噪声也可能有较大的音量,仅凭能量阈值很容易误判。

现代神经网络 VAD 会学习语音的时频特征,并对短音频片段输出语音概率。

麦克风音频
     │
     ▼
音频分帧(例如32ms)
     │
     ▼
VAD 神经网络
     │
     ▼
语音概率 P(speech)
     │
     ▼
阈值与连续时长判断
     │
     ├── 检测到语音开始
     ├── 持续说话
     └── 检测到语音结束

例如,一段音频的检测结果可能是:

0.0s - 1.2s  静音
1.2s - 4.5s  有人说话
4.5s - 5.3s  静音
5.3s - 8.1s  有人说话

这就能把一段连续录音拆成两个候选语音片段。

二、有哪些值得使用的 VAD 模型?

VAD 并不一定需要大语言模型。实际上,许多成熟的 VAD 都是非常轻量的专用模型。

模型 主要特点 适用场景
WebRTC VAD 传统信号处理与统计方法,极轻量 电话、实时通信
Silero VAD 神经网络模型,支持 PyTorch、ONNX 录音切分、语音助手
pyannote.audio Segmentation 神经网络语音分割,可涉及说话人重叠 会议分析、说话人分离流水线

这里推荐 Silero VAD。

它的 JIT 模型文件约 2MB,支持 8kHz 和 16kHz 音频,可以直接在 CPU 上运行。

根据官方公布的测试,其处理一个 30 多毫秒音频块的计算耗时,在特定单线程 CPU 环境下可以低于 1ms。不过,实际端到端延迟还取决于音频缓冲、静音等待和系统调度。

官方项目:

下面通过两个案例了解它的使用方式。

三、实践案例一:自动切分客服录音

3.1 业务场景

假设我们有一段 10 分钟的客服录音,里面包含不少等待、静音和交谈间隙。

如果整段直接送入 ASR,可能增加计算和处理成本。

更合理的方法是:

完整客服录音
      │
      ▼
   Silero VAD
      │
      ▼
识别有效语音时间段
      │
      ▼
提取语音片段
      │
      ▼
     ASR
      │
      ▼
拼接转写文本

3.2 环境准备

安装依赖:

pip install silero-vad soundfile torch

准备一段单声道、16kHz 的录音文件:

call.wav

如果原始录音格式不匹配,可以先使用 FFmpeg 转换:

ffmpeg -i original.mp3 -ar 16000 -ac 1 call.wav

3.3 Python 实现

import torch
import soundfile as sf
from pathlib import Path
from silero_vad import (
    load_silero_vad,
    get_speech_timestamps
)

# 加载VAD模型
model = load_silero_vad()

# 读取音频
audio, sr = sf.read(
    "call.wav",
    dtype="float32"
)

if sr != 16000 or audio.ndim != 1:
    raise ValueError("需要16kHz单声道音频")

audio_tensor = torch.from_numpy(audio)

# 检测语音片段
segments = get_speech_timestamps(
    audio_tensor,
    model,
    sampling_rate=sr,
    threshold=0.5,
    min_speech_duration_ms=250,
    min_silence_duration_ms=500,
    speech_pad_ms=200
)

# 保存每个语音片段
Path("segments").mkdir(exist_ok=True)

for i, seg in enumerate(segments):
    start = seg["start"]
    end = seg["end"]

    clip = audio[start:end]

    sf.write(
        f"segments/speech_{i:03d}.wav",
        clip,
        sr
    )

    print(
        f"片段{i}: "
        f"{start/sr:.2f}s - "
        f"{end/sr:.2f}s"
    )

运行后,可能得到:

segments/
├── speech_000.wav
├── speech_001.wav
├── speech_002.wav
└── ...

这些就是检测出的语音片段,可以逐段交给 ASR 识别。

代码中有三个关键参数:

  • threshold=0.5:语音检测概率阈值。
  • min_silence_duration_ms=500:持续静音约 500ms 才考虑结束片段。
  • speech_pad_ms=200:在片段两端保留少量音频,降低切掉字头或字尾的风险。

注意,以上参数只是实验起点。对于客服通话,过短的静音阈值可能把一个完整句子切成多段。

另外,应当保留每段音频的原始时间戳。否则,后续 ASR 虽然能识别文字,却无法恢复完整录音的时间轴。

四、实践案例二:实时语音助手判断用户什么时候说完

4.1 业务场景

假设我们想开发一个可以直接对话的 AI 助手。

用户对着麦克风说话:

帮我查一下明天北京的天气。

系统应该自动完成:

  1. 检测用户开始说话。
  2. 持续接收语音。
  3. 检测用户停止说话。
  4. 将完整语音交给 ASR。
  5. 调用 LLM 生成回复。

这里使用 Silero 官方提供的 VADIterator,它专门用于连续音频流的检测。

4.2 Python 实现

安装麦克风采集依赖:

pip install sounddevice

然后运行:

import torch
import numpy as np
import soundfile as sf
import sounddevice as sd
from silero_vad import (
    load_silero_vad,
    VADIterator
)

SR = 16000
CHUNK = 512  # 32ms音频

model = load_silero_vad()

vad = VADIterator(
    model,
    sampling_rate=SR,
    threshold=0.5,
    min_silence_duration_ms=600
)

audio_chunks = []
speech_start = None

print("开始监听麦克风...")

with sd.InputStream(
    samplerate=SR,
    channels=1,
    dtype="float32",
    blocksize=CHUNK
) as stream:

    # 最多监听约30秒,演示一次完整发言
    for _ in range(30 * SR // CHUNK):

        data, overflow = stream.read(CHUNK)

        chunk = data[:, 0].copy()
        audio_chunks.append(chunk)

        # 实时VAD判断
        event = vad(torch.from_numpy(chunk))

        if event is None:
            continue

        if "start" in event:
            speech_start = event["start"]
            print("检测到用户开始说话")

        if "end" in event and speech_start is not None:
            speech_end = event["end"]
            print("检测到用户停止说话")

            # 取得完整语音片段
            audio = np.concatenate(audio_chunks)
            speech = audio[speech_start:speech_end]

            # 保存后可送入ASR
            sf.write(
                "user_speech.wav",
                speech,
                SR
            )

            print("已保存:user_speech.wav")
            break

这个例子运行后,会监听麦克风,在检测到一次完整的语音起止事件后生成 user_speech.wav。

随后可以调用此前介绍过的 Qwen3-ASR:

user_speech.wav
       │
       ▼
    Qwen3-ASR
       │
       ▼
    用户说话文本
       │
       ▼
       LLM
       │
       ▼
    生成回答
       │
       ▼
    Qwen3-TTS
       │
       ▼
    播放回答

这样,一个最基础的实时语音助手架构就搭建出来了。

当然,这只是单轮演示,正式系统还需要处理连续多轮对话、麦克风异常、缓冲区管理,以及用户说话中途被打断的情况。

五、VAD 落地时需要注意什么?

第一,静音不一定意味着用户说完了。

用户思考时可能停顿一两秒。如果系统把短暂停顿当成结束,就容易频繁打断用户。

更好的方案是将 VAD 与 ASR 的增量识别结果结合,通过语义判断用户是否真正表达完毕。

第二,VAD 无法区分不同说话人。

录音中只要有人说话,VAD 就可能检测到语音。它无法判断声音来自用户、旁边的同事,还是电视机。

如果要区分不同说话人,需要额外使用说话人识别或分离技术。

第三,阈值需要根据场景调整。

参数 过小可能导致 过大可能导致
threshold 把噪声识别为语音 漏掉轻声讲话
min_silence_duration_ms 语句被频繁切断 语音结束响应缓慢
speech_pad_ms 切掉语音首尾 保留过多静音

其中,VAD 对音频结束的判断需要等待一定的连续静音时间。因此,即使模型本身推理耗时不到 1ms,也不意味着系统能够在用户说完后的 1ms 内开始回答。

六、总结

VAD 并不是一个特别复杂的 AI 模型,但它在语音系统中的位置非常重要。

本文介绍的两个实践案例分别是:

  • 客服录音切分: 自动找到有效语音片段,减少不必要的 ASR 处理。
  • 实时语音助手: 判断用户什么时候开始说话、什么时候停止说话,控制语音交互流程。

结合前面介绍的模型,可以得到一套完整的语音 AI 架构:

麦克风音频
    │
    ▼
VAD 检测语音起止
    │
    ▼
ASR 语音转文字
    │
    ▼
LLM 理解与回答
    │
    ▼
TTS 语音合成
    │
    ▼
扬声器播放

当然,VAD 并不能完全解决人机对话的轮次切换问题。用户什么时候真正说完,还可能需要结合语义和上下文进行判断。

最后用一句话概括:

ASR 负责听懂用户说了什么,VAD 则负责判断什么时候应该开始听、什么时候可以尝试停止听。

在实时语音 AI 中,VAD 虽然不起眼,却直接影响着对话是否自然、流畅,以及 AI 会不会动不动就抢话。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。