专题调研
资料复核与本地实验:2026-09-10。业务背景:用户拨号呼入 → 播放导航 → 开放按键 → 按键被接受并路由 → 进入 AI 客服 → 多轮对话。默认系统录音在右声道;真实系统必须验证角色映射。
结论
如果目标严格是“右声道持续没有可听声音”,先用工程脚本:它能确定性地给出区间、时长和阈值,成本与部署复杂度较低。如果目标进一步是“AI 该回答时没有说话”,还必须知道 AI 接管时间和应答机会;若右轨有底噪、音乐或无关播报,需要 VAD 或多模态理解补充。多模态适合复核应答语义,不应替代精确的声学计时。
推荐落地顺序是:工程基线全量扫描 → VAD 识别有声无语音 → 对疑似窗口及抽样正常窗口做音频多模态复核 → 回听与事件日志确认。另保留模型独立检测组做对照,防止工程漏检被混合方案完全继承。
到底从哪一秒开始算
“已经可以按键”不代表 AI 已接管。按键可能无效、重复、走其他菜单或进入排队;DTMF 也可能以 RTP telephone-event 传送,普通音频录音未必包含按键声。[S3] 最可靠的起点是业务事件 ai_enter。其次是人工标注;仅凭导航内容和音频推断时应标注阶段不确定。
对业务规定必须说的欢迎语,在 ai_enter 建立机会。对后续话轮,在 turn_commit 或人工确认的用户语义结束建立机会。两种起点度量的含义不同,不混在同一个延迟指标里。用户正在讲、尚未提交的短停顿、电话结束,以及已按业务规则标注的等待不机械算作漏答。
举例:0–4 秒导航,5 秒按键,8 秒进入 AI,9.5–11 秒用户提问。右轨从 9 秒起安静,但应答预算从第 11 秒开始算。5 秒预算在第 16 秒达到;若第 17 秒系统才出声,则是 6 秒延迟候选。若用户一直讲到第 22 秒,在此之前不能因为右轨安静就认定 AI 漏答。若第 14 秒已挂机,则未等满 5 秒,属于观察不足。
两条路线如何选择
脚本方案以 NumPy 和 FFmpeg 为基础。FFmpeg silencedetect 支持音量与持续时间条件,也支持按声道分别检测;这并不等于理解客服是否应该出声。[S1] Silero 提供语音活动识别,官方支持 8/16 kHz;它检测语音活动,不直接判断业务漏答。[S2]
模型方案提供 Gemini 的 REST 适配作为可执行入口。官方音频文档说明多声道合并为单声道,因此必须先分轨,再用文本明确标记两个文件是同一时段的并行录音。[S4] 两段音频本身仍可能被模型误解,需用角色对照样本实测。不能把“多模态”理解为任意支持图片的模型都能听音频,也不能用波形截图或删掉停顿的 ASR 文本代替原始声音。
工程判定与误报控制
本例的“无声”定义为:20 ms 帧内去均值 RMS 低于 −45 dBFS,且没有持续至少 160 ms 的系统轨活动。5 秒阈值、160 ms门限、120 ms活动合并间隔都是可审查的开发参数。这个定义会抑制短爆音,却可能漏掉极短的有效回答;需用实际短词样本校准。
右声道中的噪声、导航、等待音、提示音、串音都可能满足能量阈值,所以 RIGHT_ACTIVITY_OBSERVED 只说明有能量。能量基线的缺陷在 noise 合成样本中直接展示,不以“测试通过”掩盖。VAD 也会漏掉弱语音、误识别音乐,最终验收需要人工标注的真实电话音频。
阶段与机会证据不足时仍可输出声学候选,但不能输出“AI 已正常回应”。超时之前录音结束的机会标为删失,不能从失败分母中静默删除,也不能当作成功。已经排除的工具等待应单独保留统计,避免通过排除将长时间等待隐藏。
多模态提示词与实战边界
完整提示词见实战包 multimodal.py,离线命令会生成 prompt.txt。提示词包含业务阶段、角色来源、两轨并行时间轴、原始时间偏移、结果枚举、未知处理和证据限制。模型独立组不接收工程判断;混合组只提供测量区间及参数,不把工程候选冒充真值。
输出需要明确是疑似漏答、正常等待、发现系统语音还是未知,并提供原因和缺失证据。模型自报 confidence 不作为已校准概率。时间越界、空响应、拒答、非 JSON 和截断都作为未完成;对脚本认为正常的窗口也要抽检,否则混合链路的召回率无法估计。
安装、代码与运行
完整操作手册:实战 README 。包含 macOS、Ubuntu、Windows 的安装命令、下载链接、虚拟环境、NumPy/FFmpeg、可选 Torch/Silero、事件格式、单通/批量命令、模型密钥与请求命令,以及逐字段输出解释。
主脚本:detect.py ;批量:batch_detect.py ;VAD:vad_review.py ;音频模型:multimodal.py 。
如何验证它有业务价值
校准集与测试集按通话拆分。分别标注角色、接管、应答机会、是否必须回答、有效系统语音起点、已知等待与未知。用同一组留出数据比较工程、模型独立、工程加模型三种策略。至少给出机会级查准率与召回率、通话级误报率、无法判断比例、漏掉的故障类型、实际处理时长、模型实际费用和人工回听量。
本轮没有用户真实录音、人工业务标签或实际付费模型调用,因而不声称生产精度或经济收益。合成测试只证明实现对已定义场景的行为一致。录音采集点无声也不能独立证明用户没听到,或模型没生成:应关联编排、模型、TTS、下行媒体、录音侧事件定位。
官方来源与证据日期
证据正文和访问日志保存在 practice-hub/inbound-silence-bench/evidence/,实际执行记录保存在 results/。模型、系统安装与可选 VAD 的未验证范围分别披露。
从零安装到执行
更新:2026-09-10。先运行工程基线,再用语音活动检测或音频多模态模型复核。默认左轨为用户、右轨为导航/系统/AI;真实角色必须用受控通话核验。5 秒、−45 dBFS 均为开发起点,不是行业标准。
本包包含可以直接执行的 Python 脚本,不需要 Web 后端。工程基线只依赖 NumPy,其他格式解码使用 FFmpeg。多模态请求默认保存在本地,只有加 --send 才发送到 Google 并可能产生费用。
1. 下载完整实战包
在本专题页点击「下载完整实战包」。ZIP 含脚本、13 组双轨合成样本、事件 JSON、测试与结果;请先下载,再复制到需要运行的电脑或服务器。
macOS / Linux 下载并解压:
复制命令 # 在本专题页点击「下载完整实战包」,将 ZIP 保存到当前目录
unzip inbound-silence-bench.zip
cd inbound-silence-bench
Windows PowerShell 下载并解压:
复制命令 # 在本专题页点击「下载完整实战包」,将 ZIP 保存到当前目录
Expand-Archive inbound-silence-bench.zip -DestinationPath .
Set-Location inbound-silence-bench
当前工作区可直接进入:
复制命令 cd "/path/to/inbound-silence-bench"
2. 安装 Python、FFmpeg 与基础依赖
推荐 Python 3.11 或 3.12。以下系统安装命令各选一组。已有合适版本时跳过系统安装,直接建虚拟环境。
macOS
Homebrew 官方安装页 。未安装 Homebrew 时,按其官网执行安装并完成页面给出的 brew shellenv 设置;其官方安装命令为:
复制命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install python@3.12 ffmpeg
python3.12 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -r requirements.txt
Ubuntu 22.04 / 24.04
复制命令 sudo apt-get update
sudo apt-get install -y python3 python3-venv python3-pip ffmpeg unzip
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -r requirements.txt
Windows 10 / 11 PowerShell
复制命令 winget install --id Python.Python.3.12 -e
winget install --id Gyan.FFmpeg -e
安装后新开 PowerShell,回到解压目录:
复制命令 py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install --upgrade pip
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
后文使用 .venv/bin/python;Windows 对应替换为 .\.venv\Scripts\python.exe。无需激活虚拟环境,也不需要更改 PowerShell 执行策略。
检查安装:
复制命令 .venv/bin/python --version
.venv/bin/python -c "import numpy; print(numpy.__version__)"
ffmpeg -version
ffprobe -version
依赖链接:Python 下载 · venv · NumPy 安装 · FFmpeg 下载及各平台发行版 · WinGet 。本轮实跑环境记录在 results/verification.json;系统安装命令未在所有操作系统分别执行。
3. 一分钟跑通工程基线
复制命令 .venv/bin/python make_demo.py
.venv/bin/python -m unittest discover -v
.venv/bin/python detect.py samples/silent.wav --events samples/silent.events.json --out results/my-silent.json
.venv/bin/python detect.py samples/normal.wav --events samples/normal.events.json --out results/my-normal.json
.venv/bin/python detect.py samples/late.wav --events samples/late.events.json --out results/my-late.json
预期分别为:1 个持续未出声候选、0 个超时候选、1 个延迟出声候选。normal 结果只说明有符合阈值的系统轨声音;不宣称合成音是 AI 回答。
这 13 个场景用固定频率信号模拟声学活动,播放时听到的是合成音,不是真人或真实客服录音。它们验证规则和边界,不验证 VAD、人声识别、中文理解或生产准确率。
4. 换成真实双声道录音
首先确认输入:
复制命令 ffprobe -v error -select_streams a:0 -show_entries stream=codec_name,sample_rate,channels,channel_layout -show_entries format=duration -of json call.wav
需要 channels=2。双声道数量不代表角色正确,也不排除两轨内容重复。受控拨测时,让用户说一句已知文字、系统播一句不同文字,分别监听两轨并记录映射。右轨通常同时含 IVR 和 AI,不能把整条右轨都算作 AI 对话。
分轨试听,保留全部静音与同一起点:
复制命令 ffmpeg -nostdin -i call.wav -af "pan=mono|c0=c0" -c:a pcm_s16le user.wav
ffmpeg -nostdin -i call.wav -af "pan=mono|c0=c1" -c:a pcm_s16le system.wav
需要统一为 16 kHz PCM16 双声道时:
复制命令 ffmpeg -nostdin -i call.wav -map 0:a:0 -ar 16000 -c:a pcm_s16le call-16k.wav
不能先加 -ac 1,否则左右信息会混合。不能用 silenceremove 删除静音,否则质检时间轴失真。脚本直接读取 PCM16 双声道 WAV;其他编码通过 FFprobe/FFmpeg 解码,并拒绝把单声道复制成双声道。
有接管事件和应答事件:可筛查漏答候选
准备 call.events.json。下面是一通 24 秒通话的完整示例;换成真实录音时必须替换时间与标识,不能照抄:
复制命令 {
"schema_version": "1.0",
"call_id": "example-call",
"system_channel": 1,
"channel_verified": true,
"ai_start_s": 8.0,
"ai_end_s": 24.0,
"opportunities": [
{"id": "turn-1", "at_s": 11.0, "expects_response": true}
],
"user_speech": [{"start_s": 9.5, "end_s": 11.0}],
"exclusions": []
}
所有时间均为相对原始录音起点的秒数。若平台给的是墙钟时间,先减去录音起点并核对偏移;不可直接把不同机器的时钟相减。
ai_start_s:已确认的 AI 接管时刻,不是电话接通时刻、允许按键时刻,也不必等于 DTMF 到达时刻。
at_s:该轮应该开始等待系统应答的时刻。推荐使用已确认的 turn_commit;人工精标的用户语义结束也可用,但必须统一指标口径。不要把普通 VAD 停顿直接等同于用户说完。
若业务规定 AI 接管后先说欢迎语,额外加入 {"id":"greeting","at_s":8.0};若 AI 应先听用户,就不要添加这条机会。
ai_end_s:AI 阶段结束、转人工或挂机时刻。它在文件结束前发生时,文件后续不再算该 AI 阶段。
user_speech:用户真实发言区间;在应答窗口中重新开口时,截断旧机会。缺这些标注时,脚本不能自动知道后续用户正在讲话。
exclusions:经业务口径允许排除的等待区间,每项为 start_s/end_s/reason。工具正在运行本身不是排除理由;应记录用户已获告知且可接受的等待。保留这些区间,避免用排除掩盖体验问题。
window_end_s:可选,单次机会的人工观察截止。下一次应答机会、用户重新开口、排除区间或 AI 阶段结束也会截断旧窗口。
channel_verified:真实角色已经验证才能填 true。默认未知时脚本不会输出 AI 漏答判断。
运行:
复制命令 .venv/bin/python detect.py call.wav --events call.events.json --threshold-db -45 --timeout 5 --out results/call.json
如果系统在左轨,同时修改事件文件 system_channel 并加 --system-channel 0。映射不一致会明确报错。
只有 AI 接管时间:先找静音区间
复制命令 .venv/bin/python detect.py call.wav --ai-start 8 --threshold-db -45 --timeout 5 --out results/acoustic-only.json
这时只有声学静音候选;没有应答事件,就不输出“应该回答却没回答”的结论。没有 AI 接管时间也能扫描整通录音,但会明确报告阶段未知。不要使用“接通后固定第 N 秒”替代每通实际接管时间,除非已验证流程固定且无重播、无效按键或排队分支。
5. 如何读结果
acoustic_silences 是 AI 阶段中符合设定的长静音段;它可能发生在用户发言中。opportunities 才逐轮判断是否超过应答预算。两者时间起点不同。
字段 threshold_reached 指本窗口是否已经达到预算;latency_s=null 表示没观察到声音,不能替换为 0;observed_until_s 保存删失点。fault_confirmed 固定为 false,因为录音不能独立完成根因定责。right_activity_at_s 是历史命名,实际指命令指定的系统轨;换到左轨时同样适用。
算法按 20 ms 帧去均值后计算 RMS/dBFS;连续活动不足 160 ms 的短声不重置等待,相邻活动的短间隙最多合并 120 ms。结尾静音会正常结算。直流偏置会提示并去均值处理。以上是“持续可听输出”工程口径,不是数字采样全零检测。
FFmpeg 可作独立的粗筛对照:
复制命令 ffmpeg -hide_banner -nostdin -i call.wav -af "pan=mono|c0=c1,silencedetect=noise=-45dB:d=5" -f null - 2> right-silence.log
它不理解呼叫阶段或应答机会,且与本脚本的去均值、短声门限处理不同,因此不要求所有时间戳完全一致。分轨后只有一个通道,日志里看到 channel 0 仍然代表原来的右轨。
6. 批量运行
把音频和同名事件文件放在一个目录,如 calls/001.wav 与 calls/001.events.json:
复制命令 .venv/bin/python batch_detect.py calls --out results/batch
输出每通 JSON 和 summary.csv。支持 WAV/MP3/FLAC/OGG/M4A,非 PCM16 格式需要 FFmpeg。缺少事件文件时只做声学扫描并提示,不能把 0 个业务候选当作合格。坏文件写入 ERROR,不会从统计中消失;存在错误或没有输入时返回非零退出码。脚本不自动重试外部模型。
7. 可选:用 Silero VAD 找“有噪声、没人声”
能量基线回答“有没有达到阈值的声音”;VAD 回答“是否像人声”。如果右轨持续底噪超过 −45 dBFS,能量基线不会发现无语音,所以需要这一层。
官方链接:Silero 仓库与安装 · PyTorch 按平台安装 。CPU 路径足够做离线试验,无需 GPU。
macOS 的虚拟环境中:
复制命令 .venv/bin/python -m pip install torch torchaudio silero-vad
.venv/bin/python vad_review.py call.wav --events call.events.json --out results/call-vad.json
Linux x86-64 CPU 环境:
复制命令 .venv/bin/python -m pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
.venv/bin/python -m pip install silero-vad
.venv/bin/python vad_review.py call.wav --events call.events.json --out results/call-vad.json
Windows CPU 环境:
复制命令 .\.venv\Scripts\python.exe -m pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
.\.venv\Scripts\python.exe -m pip install silero-vad
.\.venv\Scripts\python.exe vad_review.py call.wav --events call.events.json --out results/call-vad.json
脚本自行用 WAV/FFmpeg 读取双轨,不调用 torchaudio 的文件加载接口;VAD 每轨分别重置状态,8/16 kHz 直接分析,其他采样率经 FFmpeg 转为 16 kHz。默认 VAD 概率阈值 0.5,系统语音响应预算为 5 秒。NO_TIMELY_SPEECH_CANDIDATE 仍然要复核:弱语音、音乐、串音、电话带宽都可能影响 VAD。此可选路径本轮做了语法检查,未安装 Torch/Silero 或执行模型推理。
8. 多模态方案:直接听音频复核
选择支持音频输入理解 的模型,只有文字、图片或只支持 TTS 输出的模型不适用。这里提供 Gemini generateContent REST 适配,不绑定某个可能变化的模型名称。当前可用模型需在自己的账户中确认。
官方链接:音频理解 · 模型目录 · API 密钥 · generateContent/Interactions 迁移说明 。本轮通过 Context7 的官方文档索引核对音频合并规则与请求结构,Google 原站直连超时;未验证任何账户模型额度与价格。
Gemini 文档说明多声道输入会合并为单声道。因此脚本先截取同一起止点的用户轨和系统轨,分别作为两段有标签的单声道音频;保留中间所有静音,并告诉模型它们是并行时间轴。不能将两轨按先后拼接,不能只上传波形截图,也不能只靠 ASR 文本判断无声。
先生成请求包,不需要密钥、不会联网。offline-schema-check 仅用于离线校验请求包,不是真实模型 ID:
复制命令 .venv/bin/python multimodal.py samples/silent.wav --events samples/silent.events.json --start 6 --end 22 --model offline-schema-check --out results/model-dry
输出 user.wav / system.wav / prompt.txt / context.json / request.json。两条录音都是 16 秒,起点均是原通话第 6 秒。输出目录必须为空,避免覆盖已保存证据。
实际调用前,换成真人/真实合成语音的业务样本与账户可用模型。macOS / Linux 设置密钥和模型 ID:
复制命令 export GEMINI_API_KEY='替换为你的API密钥'
export GEMINI_MODEL='替换为账户可用的音频输入模型ID'
.venv/bin/python multimodal.py call.wav --events call.events.json --start 6 --end 22 --out results/model-review-01 --send
Windows PowerShell:
复制命令 $env:GEMINI_API_KEY='替换为你的API密钥'
$env:GEMINI_MODEL='替换为账户可用的音频输入模型ID'
.\.venv\Scripts\python.exe multimodal.py call.wav --events call.events.json --start 6 --end 22 --out results/model-review-01 --send
默认 --mode model 是独立听音路线,不提供工程判定标签。加 --mode hybrid 会提供测得的静音区间、配置和证据缺口,适合对工程筛查结果复核。模型依然可能受工程结果诱导;做方案对照时,模型独立组不能看到工程标签和人工答案。
返回 response.raw.json 与通过字段/时间范围校验的 review.json。拒答、截断、空响应、非 JSON、时间越界都作为未完成,不当成合格。decision 为 suspected_no_answer / normal_wait / has_system_speech / uncertain,模型自报 confidence 不是校准后的概率。
脚本限制每次 90 秒、序列化请求 18 MB,为小文件内联留出余量。长通话按窗口切片并保留 3–5 秒前后文,按原始时间合并和去重;本示例不实现长录音自动模型批处理。已验证本地分轨、请求包和结果校验,尚未进行真实 API 调用,不能声称模型准确率已验证。
9. 故障定位与常见问题
工程记录建议最少带 call_id / turn_id / recording_start / ai_enter / turn_commit / model_start / model_end / tts_first_chunk / outbound_audio / hangup。录音只证明这个采集点观察到什么:有模型文字但无 TTS,查合成;有 TTS 无下行,查播放/媒体;有下行但此录音无声,还需查录音点和媒体链路。不能仅据右轨安静就确定 AI 模型没生成。
10. 上真实数据后的验收
先选覆盖正常、真实无声、弱语音、音乐/底噪、等待、无效按键、转接、挂机、打断与串音的样本。以通话为单位划分校准集与留出测试集;同一通话切片不能分散到两组。由人工标注角色、接管时刻、应答机会、是否必须回答、首个有效系统语音和证据不足。
按同一批机会比较三组:工程基线、模型独立复核、工程筛查加模型复核。报告查准率 TP/(TP+FP)、召回率 TP/(TP+FN)、正常通话误报率、无法判断占比、人工回听量、耗时与实际账户计费。仅复核工程已发现的候选不能估计召回率,必须抽查被筛掉的正常池。零分母输出未知,不能填 100%。
先保证漏检和误报都能被解释,再调整声学阈值、160 ms 门限和业务超时预算。没有真实业务集时,本包不提供“准确率 95%”一类数字。