AtomVoice(原子微语)是一款 macOS 14+ 菜单栏语音输入 App,纯 Swift + AppKit。设计目标是离线优先、隐私可控、低占用。
主要能力:
- 按住快捷键说话,松手把识别结果上屏(支持任意编辑控件)
- 三套 ASR 引擎可选:Apple Speech / Sherpa-ONNX 本地 / 豆包云端
- 可选 LLM 润色(支持 OpenAI / Anthropic / DeepSeek 兼容 endpoint)
- 8 语言界面:中(简/繁)、英、日、韩、西、法、德
源码完全开源,欢迎所有形式的贡献。
AtomVoice 当前由 1 人主力开发,架构刚完成一轮系统化解耦:
- 录音状态机:reducer + 单一 dispatch 入口
- ASR 引擎:统一
RecognitionSessionlifecycle,三家(Apple / Sherpa / Doubao)实现可插拔 - 胶囊 UI:动画 strategy 与 view 状态隔离
- 设置:typed stores(可注入 backend,便于测试)
- 测试:160+ 条架构测试,CI 自动跑
但项目仍然不是十全十美的开源项目:
- 部分模块仍由单一 owner 持有(
AudioEngineController/TextInjector/LLMRefiner/ Sherpa 模型),改动需要小心 - 自动化测试覆盖纯逻辑和状态转移,UI 与系统调用仍需手动验证
- 部分行为依赖 macOS 版本(15+)和具体硬件(USB DAC、AirPods、有线耳机)
- 英文版文档不齐全
如果你能接受"边贡献边补齐",欢迎来。如果你期待一个工业级框架,建议先在自己的 fork 里实验一段时间再提 PR。
# 克隆
git clone https://github.com/<your-fork>/AtomVoice.git
cd AtomVoice
# 编译 + 签名(需 Apple Development 证书)→ dist/Test/AtomVoice.app
make dev
# 直接运行(release 模式)
make run
# 跑测试
make test- 签名失败:
make dev默认用本机 Apple Development 证书。如果你没有 Apple ID 或不想配证书,改用swift build -c release验证能编译即可,不能直接 run(macOS 不让未签名 app 录音、用辅助功能)。 make test失败:先swift --version确认 Swift 5.9+。当前测试入口是自定义AtomVoiceArchitectureTestsrunner,不是系统 XCTest。- Sherpa 模型不会下载:首次启动会引导下载。如果想本地测试不依赖网络,可直接用 Apple Speech 引擎。
Sources/AtomVoice/
├── App/ # AppDelegate 组合根
├── ASR/ # 三家 ASR 引擎、RecognitionSession lifecycle
├── Audio/ # AVAudioEngine、AudioRouter、AudioAnalyzer、VolumeController
├── Input/ # FnKeyMonitor、HeadphoneMonitor、触发键
├── Menu/ # 菜单栏 controller
├── Models/ # 数据结构、本地化
├── Permissions/ # PermissionService
├── Recording/ # RecordingSessionController、RecordingStateMachine
├── Settings/ # AppSettings + typed stores
├── Text/ # TextInjector、TextOutputSink、LLMRefiner、Finalizer
├── Update/ # UpdateChecker
└── Windows/ # OOBE / 设置 / About / 胶囊 / 权限窗口
详细的目录约定见 localdoc/reference/source-layout.md。
主链路简化版:
FnKey 按下
→ RecordingSessionController.dispatch(.triggerPressed)
→ RecordingStateMachine.reduce → SideEffect[]
→ RecordingSideEffectExecutor 执行
→ RecognitionSession.start
→ ASR partial / final 回调
→ RecognitionResultFinalizer 决定上屏方式
→ TextOutputSink.deliver / TextInjector.inject
- 翻译完善:8 个 lproj 任何一个有 missing / 有更好表达,直接 PR。跑
make lint-loc可以看到当前覆盖情况。- 简体中文:
Resources/zh-Hans.lproj/Localizable.strings - 其他语言类似
- 简体中文:
- 新 ASR 引擎接入:实现
RecognitionSessionprotocol,在ASREngineProvider.recognitionSession(for:audioEngine:)注册一个新 case。推荐先在 issue 里讨论引擎选型。 - 设备兼容性修复:USB DAC / AirPods / 有线耳机的特殊行为反馈和补丁。
localdoc/archive/2026-05/2026-05-18-headphone-may-debug.md记录了 MOONDROP MAY 的踩坑过程,可作为参考。 - Sherpa 模型预设扩展:
SherpaModelDownloader的 catalog 可以加新模型,只要符合"小于 1GB、支持 macOS arm64、量化版本可用"等约束。
- 新 LLM provider:
LLMRefiner目前支持 OpenAI / Anthropic / 兼容 endpoint。如果想加 Google / 通义等,先开 issue 说明 API 兼容性。 - 新触发键模式:目前支持单 Fn / 双击 / 长按。若要加新模式,涉及
FnKeyMonitor和RecordingSessionController状态机,需要讨论。 - macOS 13 兼容性:目前最低 14。如果想下探到 13,需要解决
SFSpeechRecognizer部分 API 差异,工作量不小,先讨论。
- iOS / iPadOS 移植:这是个 macOS 菜单栏 app,触控/键盘事件机制完全不同。
- Apple Intelligence 直连:目前不打算依赖 macOS 15.1+ 独占能力。
- 付费功能 / 订阅系统:AtomVoice 是免费开源软件,不接受加入付费墙的 PR。
- 重型 UI 重写:菜单栏 + 胶囊 + 设置窗口足够覆盖核心交互,不打算引入 SwiftUI 重写或新 onboarding 流程。
- 开 issue 讨论方向(除了翻译和拼写修正)。一行代码的修补可以直接 PR,但若涉及超过 50 行,先开 issue 对齐方案,免得做完发现方向不符。
- 跑
make test+make lint-loc通过。CI 也会跑,但你先跑可以省一轮。 - 跑
make dev启动一次,手动验证你的改动确实工作。测试覆盖纯逻辑,UI 行为仍需要你的眼睛。 - 提交信息用英文,简短描述"为什么"。例:
Fix headphone double-tap sticking when fallback engaged。
## 改了什么
(1-3 句)
## 为什么这么改
(1-3 句,关键是 motivation)
## 怎么验证
- [ ] make test 通过
- [ ] make lint-loc 通过
- [ ] 手动验证场景 A
- [ ] 手动验证场景 B- 代码注释用中文(项目主开发者用中文思考)
- 提交信息、PR 描述用英文(便于国际协作)
- 新增用户可见字符串必须
loc(),必须同步 8 个 lproj(CI 的make lint-loc会拦) - Debug-only 代码必须包在
#if DEBUG_BUILD,release 构建不传DEBUG_BUILD - 不要新增
UserDefaults/Keychainkey 字符串(会破坏已发版用户设置兼容) - 详细规则见
CLAUDE.md
- 不带测试的 reducer 状态机改动
- 顺手大重构(refactor + 修 bug 混在一个 PR)
- 改 UserDefaults key 字符串(破坏已发版用户兼容)
- 改默认值但没说明动机
- 给 Sherpa 模型加新依赖(C++ runtime / 新 framework)
- 引入 Sentry / Crashlytics / 第三方 telemetry SDK(AtomVoice 是隐私优先项目,无第三方上报)
GitHub Issues 走起。好的 bug 报告包含:
- macOS 版本(
sw_vers) - AtomVoice 版本(菜单 → About)
- 触发条件(按了什么键、在什么 app、用什么 ASR 引擎、用什么输入/输出设备)
- 期望行为 vs 实际行为
- 如果有 Debug 构建(
make dev产物),附~/Library/Logs/...里的日志 - 如果是 USB / 蓝牙耳机问题,加上设备名
不需要截图、视频(除非 UI 错位类问题)。日志比截图更有用。
详见 SECURITY.md。简要:
如果发现:
- 任意 app 可以提取 AtomVoice 存储的 API key
- AtomVoice 把语音音频或文本意外上传
- 用户设置可以被恶意 app 篡改
- 任何能绕过 SHA256 + 签名校验的更新路径
不要开公开 issue。发邮件到项目主开发者(邮箱见 GitHub profile),标题包含 [SECURITY]。会在 14 天内回复并协调披露。
短版:对事不对人,公开记录决策,不允许冒犯他人的言论。
详细的就先用 Contributor Covenant 2.1。
- PR 7 天内首次回复(即使是"我看到了,这周末处理")。
- 小修补(< 50 行、有测试)2 周内合或拒。
- 大改动可能拖更长,会在 issue 里同步进度。
- 不会无声关 PR;关之前一定给理由。
- 不会要求贡献者签 CLA 转让权利。
- 不会未经讨论就把贡献者代码改写后归到自己名下。
感谢所有贡献者。AtomVoice 用到的开源组件见 About 窗口的开源致谢页(完整致谢索引待补)。
- GitHub Issues:首选
- Email:见维护者 GitHub profile(只在安全披露 / 邮件偏好场景使用)
- 不开微信群、不开 Discord —— 项目讨论留在 GitHub 上可追溯