给语音消息省钱:Hermes Agent 云 STT 静音修剪 + 本地 Whisper 空闲卸载


语音消息是 Hermes Agent 在 Telegram、Slack 等平台上最自然的输入方式——按住说话,转成文字,剩下的交给 Agent。但如果你用的是云端语音识别(STT),每条语音消息都在悄悄花两份钱:停顿

一条 13 秒的语音消息,可能只有 6 秒是真正的人声,剩下 7 秒是思考时的沉默。云端 Whisper 按音频分钟计费,静音和人声一个价(OpenAI 是 $0.006/分钟,静音照收);更糟的是,Whisper 在纯静音段上会“脑补”出根本不存在的词——这就是云 STT 转写结果里偶尔冒出莫名其妙句子的原因。

Hermes Agent 最近合并的两个 PR 正好解决了这两个问题,而且都是开箱即用(一个默认开启,一个加一行配置):

  1. #77581 — 云 STT 上传前静音修剪:上传前用 ffmpeg 把长停顿压掉,实测一条 13.2 秒的语音消息修剪到 6.2 秒(-53%),转录结果完全等价。
  2. #81027 — 本地 Whisper 空闲卸载:本地语音识别模型在空闲 5 分钟后自动卸载,释放约 370MB 内存/VRAM,下一条语音消息到达时透明重载。

背景:本地和云,两条不同的 STT 路线

在讲新功能之前,先理解 Hermes Agent 的 STT 架构。配置在 config.yamlstt 段,provider 决定走哪条路线:

stt:
  enabled: true
  provider: local          # local | groq | openai | mistral | xai | elevenlabs | deepinfra
  language: "en"           # 全局语言提示,避免短语音被误判语种

本地路线local):用 faster-whisper 在你自己机器上转写,免费、数据不出门,但模型常驻内存。之前的版本已经给它加上了 Silero VAD(语音活动检测),静音根本不会进入模型。

云路线groq/openai/mistral/xai/elevenlabs/deepinfra):音频原样上传到第三方 API 转写,按音频分钟计费。问题在于——云路线一直没有 VAD 保护,原始音频(包括所有停顿)被原样上传。

两个新 PR 正是补齐这两条路线的短板:云路线补上静音修剪,本地路线补上空闲卸载。

一、云 STT 静音修剪:上传前把停顿压掉

怎么开

默认就是开启的,三个配置项:

stt:
  cloud_trim_silence: true      # false = 总是上传原始音频
  cloud_trim_threshold_db: -40  # 低于这个音量的音频视为静音
  cloud_trim_keep_ms: 300       # 每个停顿保留 300ms,保住词边界和自然节奏

工作流程是:上传前,对超过 12 秒的音频片段,用 ffmpeg 的 silenceremove 过滤器把长停顿折叠成 300ms 的短停顿(-40dB 以下算静音),然后上传修剪后的版本。ffmpeg 本来就是这条路线的既有依赖(CAF 格式转换要用),所以没有新增任何依赖

实测数据(来自 PR 作者的真实运行)

original: 13.15 s   (语音 3s + 停顿 7s + 语音 3s)
INFO Trimmed silence from voicenote.wav before cloud STT upload (13.2s -> 6.2s, -53%)
trimmed:  6.24 s

修剪后的音频用 faster-whisper 验证过,两段语音的转写结果和原始音频完全一致——停顿被压掉,人声完好。

12 秒门槛:为什么短语音不修剪

你可能注意到上面说“超过 12 秒才修剪”。这是刻意的成本控制:短片段做一次 ffprobe(约 50ms)就跳过编码。原因很实在——不到 12 秒的片段,最多也就能省 10% 也就是 1 秒左右的音频,而 Groq 这类服务按请求最低收 10 秒的费用,编码开销根本赚不回来。只有长到值得修剪的片段才会付出编码成本。

严格 best-effort:修剪永远不会让转写失败

这一点是设计核心:修剪是尽力而为,任何失败都直接上传原始音频,转写流程绝不会因为修剪而挂掉:

情况 行为
cloud_trim_silence: false 上传原始音频
ffmpeg/ffprobe 缺失 上传原始音频
片段短于 12 秒 上传原始音频(只做一次探测,不编码)
修剪命令失败/超时 上传原始音频
修剪结果近乎为空(几乎全是静音) 上传原始音频——由服务商而非客户端的 dB 阈值来判断有没有语音
修剪节省不足 10% 上传原始音频

最后两条值得多说一句:一条几乎全静音的录音,该不该算“有人说话”,这个决定权留给服务商(它们有自己的语音检测),客户端不做武断判断;而节省不足 10% 时重新编码纯属白费功夫,直接放弃。

什么时候该关掉它

silenceremove-40dB 阈值把安静环境当成静音处理。如果你的语音消息经常是音乐、环境音、白噪音这类非人声内容(比如让 Agent 识别一段歌曲或现场录音),把 cloud_trim_silence 设为 false 恢复原始上传行为——和本地路线的 vad: false 是同一个思路。

二、本地 Whisper 空闲卸载:370MB 的悄悄流失

问题:模型加载一次,就再也不释放

本地 faster-whisper 模型是个单例:第一条语音消息到达时加载,之后整个进程生命周期内都常驻内存base 模型大约占 370MB——即使好几个小时、好几天都没有语音消息,这 370MB 也一直占着。

对长期运行的 Hermes gateway 进程来说尤其浪费,特别是和本地 LLM 抢同一块 GPU 的机器:Whisper 占着的 VRAM,本地大模型用不了。

怎么开

stt:
  local:
    model: "base"           # tiny | base | small | medium | large-v3
    unload_after_idle_seconds: 300   # 0=永不卸载(默认);300 = 空闲 5 分钟后卸载

默认 0 表示永不卸载——现有用户零行为变化。设为 300(推荐给 gateway 场景)后:

  • 守护线程每 30 秒检查一次空闲时长,超过阈值就把模型引用置空,交给 GC 回收
  • 下一条语音消息到达时,通过既有的懒加载路径透明重载——用户完全无感知,只是多等一次模型加载时间
  • 配置是每轮循环实时读取的:改完 config.yaml 里这个值,一个检查周期内生效,不需要重启进程;中途改成 0 也会在空闲时“取消卸载”

内存真相:GPU 和 CPU 不一样

PR 作者做了诚实的实测,值得引用:

  • CUDA/GPU:模型对象被 GC 后,VRAM 真正归还给设备——这是共享 GPU 场景的最大收益。
  • CPU(macOS 实测):Python 引用释放、内存可复用,但 ctranslate2 的 C++ 分配器不会把页面还给操作系统,所以 RSS 数值几乎不变(实测 388MB 前后一致)。Linux 上 glibc 的 malloc_trim 行为可能归还一部分。

翻译成人话:GPU 上是真释放,CPU 上主要是“让内存可复用”——模型不再钉住几百 MB 的活对象,之后换一个不同尺寸的模型也能装进回收的内存空间,而不是让进程继续膨胀。无论哪种情况,都不再是“加载一次占一辈子”。

三、组合使用:不同场景的推荐配置

场景 配置建议
长期运行的 gateway + 本地 LLM 共享 GPU local.unload_after_idle_seconds: 300(强烈推荐,VRAM 真释放)
桌面/CLI 偶尔用语音,机器内存紧张 local.unload_after_idle_seconds: 600,空闲 10 分钟卸载
语音消息频繁、转写延迟敏感 保持 0 不卸载,避免首条消息的模型加载等待
云 STT(groq/openai 等) cloud_trim_silence 保持默认 true 即可,成本立省
语音消息经常是音乐/环境音 cloud_trim_silence: false

改完配置后用 hermes config edit 打开配置文件,或者用 hermes config get stt.local.unload_after_idle_seconds 验证当前值。

四个实用技巧

  1. 云 STT 配短语音命令最划算:12 秒门槛意味着普通语音命令(“查一下明天的天气”)根本不会触发修剪,也不会多花编码时间——修剪只对长语音消息生效。
  2. 语言提示别忽略stt.language: "en" 这样的全局提示对云 STT 同样生效(逐服务商配置优先),短语音命令经常因为 Whisper 自动检测误判语种而出错。
  3. 先查再改hermes config get stt.cloud_trim_silence 能直接确认当前生效值,改完 hermes config check 校验语法。
  4. 本地 + 云可以按需切换provider 字段随时可改。想省钱就 local(免费但占内存,配合空闲卸载),想要多语言高精度就云路线(配合静音修剪压成本)。

总结

两个 PR 加起来,把语音输入的两条路线都收拾利索了:云路线不再为停顿付费、不再被静音幻觉污染转写结果;本地路线不再让 370MB 内存/VRAM 常驻空转。都是小配置、零新依赖、纯增量收益——语音重度用户(尤其是 gateway 常驻 + 云 STT 的用户)值得立刻打开。

想进一步了解 Hermes Agent 的语音能力和相关配置,可以看看我们之前的 v0.19.1 Voice Patch 发布说明安装指南v0.20.0 Herald 发布说明