# 呼入导航后 AI 无声质检：安装与实战

更新：2026-09-10。先运行工程基线，再用语音活动检测或音频多模态模型复核。默认左轨为用户、右轨为导航/系统/AI；真实角色必须用受控通话核验。5 秒、−45 dBFS 均为开发起点，不是行业标准。

本包包含可以直接执行的 Python 脚本，不需要 Web 后端。工程基线只依赖 NumPy，其他格式解码使用 FFmpeg。多模态请求默认保存在本地，只有加 `--send` 才发送到 Google 并可能产生费用。

## 1. 下载完整实战包

在本专题页点击「下载完整实战包」。ZIP 含脚本、13 组双轨合成样本、事件 JSON、测试与结果；请先下载，再复制到需要运行的电脑或服务器。

macOS / Linux 下载并解压：

```bash
# 在本专题页点击「下载完整实战包」，将 ZIP 保存到当前目录
unzip inbound-silence-bench.zip
cd inbound-silence-bench
```

Windows PowerShell 下载并解压：

```powershell
# 在本专题页点击「下载完整实战包」，将 ZIP 保存到当前目录
Expand-Archive inbound-silence-bench.zip -DestinationPath .
Set-Location inbound-silence-bench
```

当前工作区可直接进入：

```bash
cd "/path/to/inbound-silence-bench"
```

## 2. 安装 Python、FFmpeg 与基础依赖

推荐 Python 3.11 或 3.12。以下系统安装命令各选一组。已有合适版本时跳过系统安装，直接建虚拟环境。

### macOS

[Homebrew 官方安装页](https://brew.sh/)。未安装 Homebrew 时，按其官网执行安装并完成页面给出的 `brew shellenv` 设置；其官方安装命令为：

```bash
/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

```bash
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

```powershell
winget install --id Python.Python.3.12 -e
winget install --id Gyan.FFmpeg -e
```

安装后新开 PowerShell，回到解压目录：

```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 执行策略。

检查安装：

```bash
.venv/bin/python --version
.venv/bin/python -c "import numpy; print(numpy.__version__)"
ffmpeg -version
ffprobe -version
```

依赖链接：[Python 下载](https://www.python.org/downloads/) · [venv](https://docs.python.org/3/library/venv.html) · [NumPy 安装](https://numpy.org/install/) · [FFmpeg 下载及各平台发行版](https://ffmpeg.org/download.html) · [WinGet](https://learn.microsoft.com/windows/package-manager/winget/)。本轮实跑环境记录在 `results/verification.json`；系统安装命令未在所有操作系统分别执行。

## 3. 一分钟跑通工程基线

```bash
.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. 换成真实双声道录音

首先确认输入：

```bash
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 对话。

分轨试听，保留全部静音与同一起点：

```bash
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 双声道时：

```bash
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 秒通话的完整示例；换成真实录音时必须替换时间与标识，不能照抄：

```json
{
  "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 漏答判断。

运行：

```bash
.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 接管时间：先找静音区间

```bash
.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` 才逐轮判断是否超过应答预算。两者时间起点不同。

| 状态 | 含义 | 下一步 |
|---|---|---|
| `NO_AUDIBLE_OUTPUT_CANDIDATE` | 确认的应答窗口已达阈值，尚未观察到合格系统轨声音 | 回听、VAD/模型复核并关联日志 |
| `LATE_RIGHT_ACTIVITY` | 有系统轨声音，但首次出现已达到或超过阈值 | 判断是否为迟到回复或无关声音 |
| `RIGHT_ACTIVITY_OBSERVED` | 预算内出现系统轨声音 | 只排除严格无声，不证明存在有效回答 |
| `CENSORED` | 尚未等满阈值就挂机、被用户新发言或新机会截断 | 观察不足，不计通过或漏答 |
| `EXCLUDED` | 在 AI 阶段外、不需要回答或属于明确排除时段 | 保留排除依据 |
| `INSUFFICIENT_EVIDENCE` | 角色、阶段等关键证据不足或两轨重复 | 先补证据 |

字段 `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 可作独立的粗筛对照：

```bash
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`：

```bash
.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 仓库与安装](https://github.com/snakers4/silero-vad) · [PyTorch 按平台安装](https://pytorch.org/get-started/locally/)。CPU 路径足够做离线试验，无需 GPU。

macOS 的虚拟环境中：

```bash
.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 环境：

```bash
.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 环境：

```powershell
.\.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 适配，不绑定某个可能变化的模型名称。当前可用模型需在自己的账户中确认。

官方链接：[音频理解](https://ai.google.dev/gemini-api/docs/generate-content/audio) · [模型目录](https://ai.google.dev/gemini-api/docs/models) · [API 密钥](https://ai.google.dev/gemini-api/docs/api-key) · [generateContent/Interactions 迁移说明](https://ai.google.dev/gemini-api/docs/migrate-to-interactions)。本轮通过 Context7 的官方文档索引核对音频合并规则与请求结构，Google 原站直连超时；未验证任何账户模型额度与价格。

Gemini 文档说明多声道输入会合并为单声道。因此脚本先截取同一起止点的用户轨和系统轨，分别作为两段有标签的单声道音频；保留中间所有静音，并告诉模型它们是并行时间轴。不能将两轨按先后拼接，不能只上传波形截图，也不能只靠 ASR 文本判断无声。

先生成请求包，不需要密钥、不会联网。`offline-schema-check` 仅用于离线校验请求包，不是真实模型 ID：

```bash
.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：

```bash
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：

```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. 故障定位与常见问题

| 现象 | 处理 |
|---|---|
| `ModuleNotFoundError: numpy` | 用同一个 `.venv/bin/python -m pip install -r requirements.txt`，不要混用系统 pip |
| 找不到 ffmpeg / ffprobe | 完成系统安装，新开终端确认 PATH；PCM16 WAV 本身不依赖 FFmpeg 解码 |
| `must be stereo` / 单声道错误 | 回录音系统重新导出真实双轨；禁止复制单轨伪造角色分离 |
| `INSUFFICIENT_EVIDENCE` | 核对 AI 接管时刻、角色验证、重复轨；不能改字段让它强行通过 |
| 噪声场景没有静音候选 | 符合能量方法边界，改用 VAD/音频模型复核语音 |
| 短回答未被计为声音 | 160 ms 连续活动门限可能漏短词；先回听和标注，再按目标口径调整实现 |
| 声音跨越应答事件 | 已在播放的导航、等待音、串音可能被计为活动；通过阶段、VAD/语义和播放 generation 复核 |
| 模型 400/404 | 核对模型是否支持音频及 `generateContent`、JSON 输出；查 `api-error.txt`，不能自动换模型形成不可比结果 |
| 模型 401/403/429 | 核对 API Key、项目权限、地区/配额；脚本不自动重试 |
| TLS / 网络超时 | 请求视为未完成；不记录成“无声”或“正常” |

工程记录建议最少带 `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%”一类数字。
