跳到主要内容

架构设计

整体流水线

altgo 采用线性流水线架构,由键盘事件驱动:

按键监听 → 状态机 → 录音 → ASR 转写 → LLM 润色 → 剪贴板 + 悬浮窗 + 通知 + 本地转写历史(纯文本 JSON)

转写历史:仅保存文本(原始转写 rawText 与当前展示 text),不保存录音;数据文件为 ~/.config/altgo/history.json。转写成功后(以 raw_text 非空为准)写入一条记录并广播 history-updated 事件,供主窗口历史页刷新。

各模块之间通过 tokio::sync::mpsc 异步通道解耦,每个处理阶段作为独立的 tokio::spawn 任务运行。

模块结构

以下为 Tauri 核心目录 src-tauri/src/(节选):

src-tauri/src/
├── main.rs # 薄入口,调用 `lib::run()`
├── lib.rs # Tauri 构建入口;AppState(含 history_path)
├── cmd.rs # IPC:配置、管道、模型、转写历史等
├── config.rs # TOML 配置加载(serde)
├── history.rs # 转写历史 persistence(history.json)
├── state_machine.rs # 按键状态机
├── audio.rs # WAV 编解码,线程安全 Buffer
├── transcriber.rs # Transcriber trait
├── sherpa.rs # 本地 SenseVoice(内嵌 sherpa-onnx)
├── polisher.rs # LLM 文本润色
├── pipeline.rs # 音频处理核心(转写 + 润色)
├── key_listener/ # 按键监听
│ ├── mod.rs # 公共 trait + PlatformListener
│ └── linux.rs # xinput test-xi2(X11)
├── recorder/ # 音频录制
│ ├── mod.rs # 公共 trait + PlatformRecorder
│ └── linux.rs # parecord(PulseAudio)
└── output/ # 剪贴板 + 通知
├── mod.rs # 公共 trait + PlatformOutput
└── linux.rs # xclip / xsel / wl-copy

按键状态机

状态机管理 5 个状态之间的转换:

按下
Idle ──────→ PotentialPress
↑ │
│ 长按超时 双击超时
│ ↓ ↓
│ Recording WaitSecondClick
│ │ │
│ 松开 再次按下
│ ↓ ↓
└──────── ContinuousRecording ←─┘
  • 长按(> 200ms)→ 进入 Recording,松开停止
  • 双击(< 300ms 间隔)→ 进入 ContinuousRecording,单击停止
  • 使用 tokio::select! 同时等待按键事件和超时

平台策略

Linux:通过子进程调用 CLI 工具(parecordxclipxinput),简化构建和依赖管理。

平台模块暴露统一 trait(KeyListenerRecorderOutput),管道消费 Box<dyn Trait> 实现与平台解耦。

转写引擎

当前唯一生产实现是 SherpaTranscriber:内嵌 sherpa-onnx 的本地 SenseVoice,模型常驻内存。

润色引擎

  • 支持 4 个级别:none / light / medium / heavy
  • 使用 OpenAI 兼容 chat API
  • 指数退避重试(最多 3 次)

历史页可对单条记录基于 rawText 再次调用当前润色配置(polish_history_entry)。

前端路由(主窗口)

  • React Router Hash 模式:/ 首页、/history 历史、/settings 设置。

测试策略

  • config.rs / audio.rs / history.rs — 全面的单元测试
  • sherpa.rs — SenseVoice 模型存在性与加载失败路径
  • polisher.rs — 使用 mockito 模拟 OpenAI/Anthropic 润色 API
  • 平台模块 — 仅构造/冒烟测试
  • CI 在 Linux amd64arm64 上运行:fmtclippybuild --releasetest