## 背景
之前一直用 Typeless 做 macOS 上的语音输入,体验确实好——按住快捷键说话,文字直接出现在光标处。但 $8/月(约 ¥60 )让我觉得有点贵,本质上就是个 API 调用的壳。于是自己动手写了一个。
## SpeakMore 是什么
菜单栏常驻的语音输入工具。按住 Fn (或自定义热键)说话,松开后文字流式插入到当前光标位置。任何 App 都能用——VS Code 、Slack 、浏览器、终端、微信,都行。
## 跟 Whisper 方案的区别
大多数语音输入走的是:`录音 → Whisper 转文字 → 后处理清理`
SpeakMore 不走 ASR ,直接把音频丢给多模态大模型( Gemini Flash 、通义千问等),一次 API 调用同时完成识别和优化。关键是连带发送的上下文:
**三层上下文系统:**
1. **实时上下文** — 通过 macOS Accessibility API 读取当前 App 名称、窗口标题、文档路径。在 VS Code 里编辑 `deployment.yaml` 时说 "Kubernetes",不会被识别成"库伯内提斯"
2. **短期记忆** — 每 10 次输入分析最近 1 小时的转录,提取话题和高频词。聊了半小时数据库迁移,不会把 "Postgres" 听成 "post grass"
3. **长期画像** — 每天从 7 天数据中构建用户画像(职业、领域、语言习惯),PII 自动脱敏
效果就是:专业术语不需要手动加词典,用着用着就准了。
## 踩坑最多的地方:文本插入
在 macOS 上往别人的文本框里塞字,比想象中难多了。最后做了三级回退:
1. **Accessibility API** — 直接设 AXValue ,最快最干净,但不是所有 App 都支持
2. **CGEvent 键盘模拟** — 逐字符模拟按键,兼容性好但长文本慢
3. **剪贴板粘贴** — 保存剪贴板 → 写入文字 → Cmd+V → 恢复剪贴板,万能但不优雅
几个坑:
- iTerm2/Terminal.app 用 Accessibility 插入会字符重复,得检测终端 Bundle ID 直接跳到 CGEvent
- 中文输入法激活时 CGEvent 会被 IME 拦截,要先发 Escape 关掉候选框
- SSE 流式传输在弱网下会截断 JSON ,做了行级缓冲+容错
老实说文本插入的调试时间比其他所有功能加起来都多。
## 技术细节
- 纯 Swift 5.9 ,SwiftUI + AppKit ,零外部依赖
- 音频:AVAudioEngine → 16kHz 单声道 PCM → 手动构建 WAV 头 → Base64 → HTTPS
- SSE 流式响应,30ms 缓冲刷新,文字边生成边出现
- 状态机架构:空闲 → 录音 → 转录 → 插入
- 安装包 ~2MB ,没有 Electron ,没有捆绑模型
## 支持的服务商
| 服务商 | 说明 |
|--------|------|
| Google Gemini | gemini-2.5-flash ,速度快 |
| 通义千问 (DashScope) | qwen-omni-turbo ,国内访问友好 |
| OpenRouter | 一个 Key 用多家模型 |
| 自定义 | 任意 OpenAI 兼容接口 |
自带 API Key ,单次转录成本大约 ¥0.01 以内。
## 链接
GitHub: https://github.com/Maxwin-z/SpeakMore-macOS
MIT 协议,macOS 14+ 。
有问题随时问,尤其是文本插入那块,可以聊很久(苦笑

