Sidekick 项目展示纯本地语音交互方案:OS 原生录音采集后,以 faster-whisper int8 量化模型在 CPU 上完成转写,文本直接注入 prompt,全程数据不离开设备。
云端语音助手有个尽人皆知却无人点破的秘密:你的声音会上传到服务器。每一次"嘿,助手"都是一次上传。Sidekick 的 sk talk 在本地硬件上完成整个闭环——系统原生采集、本地转录、转录结果可直接编辑地注入 prompt。本系列第三篇讲的就是这条流水线是如何工作的,以及里面那些不起眼却恼人的 bug。
按下 Enter 开始录音,再按 Enter 停止。底层是系统原生录音器(Linux 上是 arecord/ALSA,macOS 上是 sox/CoreAudio,兜底用 ffmpeg)写入 16kHz 的 wav 文件,然后本地 faster-whisper 进行转录——int8 量化、仅用 CPU、模型下载一次后缓存备用:
if model_size not in _model_cache:
_model_cache[model_size] = WhisperModel(model_size, device="cpu", compute_type="int8")
model = _model_cache[model_size]
segments, _ = model.transcribe(wav_path, beam_size=5)
text = " ".join(s.text.strip() for s in segments).strip()
if not text:
raise RuntimeError(
"heard only silence — speak louder/closer, or run `sk mic-test` to check levels"
)
没有硬依赖:arecord 随系统自带,sox/ffmpeg 通过 brew 安装,faster-whisper 在首次使用时自动装进运行环境(uv pip install faster-whisper,同时为不可避免的边缘情况准备了"仍然无法导入——重启再试"的兜底路径)。在 TUI 里,Ctrl+G 或麦克风药丸按钮执行同样的操作。--stt-model base 以精度换取速度,适用于对实时性要求更高的场景。
看这些失败信息:"录音几乎为空——麦克风可能静音了,运行 sk mic-test 检查"。"只听到了静音——请大声点或靠近麦克风"。每条信息都指出了可能的物理原因和下一步操作。这来自对真实会话的观察:转录失败几乎从来不是模型的问题,而是环境的问题——麦克风静音、设备选择错误、笔记本风扇的微弱噪音等。所以 sk mic-test 会录制三秒并返回判断——静音、安静、良好——同时显示分贝峰值,并在操作系统混音器误导用户时给出 alsamixer 提示。
隐私保护是结构性的,不是承诺出来的。录音是临时文件,每次录制后都会删除——它们根本没有积累的机会,因为整条流水线里没有任何环节需要往服务器发送数据。最强的隐私保证就是架构本身上传路径。
第一个:Python 3.13 移除了 audioop,所以麦克风电平测量需要一个手写的 PCM 统计函数,用 struct 实现——代码里有一条关于字节序在格式字符串中放置位置的注释,看起来像是有人为此折腾了一个下午。
第二个是 spawn guard。faster-whisper 的依赖树会启动一个 multiprocessing 子进程,而在 macOS 上,被回收的文件描述符导致 CPython 拒绝 spawn,报出 ValueError: bad value(s) in fds_to_keep。修复方案是对 spawnv_passfds 进行 monkeypatch,清理文件描述符列表——去重、排序、丢弃已关闭的——这个 patch 只在 STT 工作期间安装一次且仅此一次。由于调用方把失败压缩成一行信息,掩盖了真正的崩溃位置,现在完整的堆栈跟踪会追加到 ~/.sidekick/voice-errors.log。教会你错误报告有问题的那个 bug,永远是至少两个 bug。
第四部分将深入工具安全模型:19 个工具、审批门、硬拒绝和审计账本。
由 Sidekick 社区构建。仓库:https://github.com/Faisal-Fayaz/sidekick