Files
openless/docs/openless-development.md
T

13 KiB
Raw Blame History

OpenLess 开发文档

更新时间:2026-04-26

本文是 OpenLess 第一轮开发的工程落地文档。产品需求以 openless-requirements.md 为准,本文负责说明推荐技术形态、模块拆分、状态机、数据字段、开发顺序和验收方式。


1. 技术目标

第一轮要做的是一个 macOS 常驻语音输入工具:

用户在任意 app 的输入框中按住快捷键说话,OpenLess 录音、转写、轻度整理,然后把结果插入当前光标位置;失败时自动复制并保存历史。

工程目标:

  • 能稳定常驻菜单栏。
  • 能可靠获取麦克风录音。
  • 能响应全局快捷键。
  • 能显示底部微型状态胶囊。
  • 能完成 ASR 转写和 LLM 润色。
  • 能向当前输入位置插入文本。
  • 能在失败时复制到剪贴板并保存历史。
  • 能清楚处理权限、错误、隐私和本地/云端路径。

2. 推荐技术路线

2.1 首选:原生 macOS

建议第一轮使用:

  • Swift / SwiftUI:设置页、历史页、主窗口。
  • AppKit:菜单栏、全局窗口、底部胶囊浮层、辅助功能相关能力。
  • AVFoundation:麦克风录音、音量采样。
  • Carbon / Keyboard Shortcuts 方案:全局快捷键。
  • Accessibility API:定位当前输入焦点、模拟输入或粘贴。
  • NSPasteboard:剪贴板兜底。
  • SQLite 或本地 JSON:历史、词典、设置。
  • Keychain:保存 API Key。

理由:

  • OpenLess 是系统级输入层,原生 macOS 更适合处理菜单栏、悬浮窗口、权限和全局快捷键。
  • Electron/Tauri 可以做设置页,但系统输入、悬浮层和权限体验更容易变复杂。

2.2 可选混合路线

如果后续想快速做漂亮设置页,可以采用:

  • 原生 Swift 后台核心 + WebView 设置页。

第一轮不建议直接用纯 Web/Electron 做核心录音和插入体验。


3. 模块拆分

模块 职责
AppShell 应用生命周期、菜单栏、启动项、窗口管理
PermissionManager 麦克风、辅助功能、通知等权限检查和引导
HotkeyManager 注册录音、取消、粘贴上一条等全局快捷键
Recorder 录音、音频缓存、音量采样、取消
AudioLevelMonitor 输出胶囊动态条所需的实时音量值
CapsuleOverlay 底部微型状态胶囊窗口
ASRRouter 本地、云端、BYOK 转写路由
PolishEngine 原文、轻度润色、清晰结构、正式表达
TextInserter 当前输入框插入、模拟粘贴、失败检测
ClipboardFallback 插入失败时复制最终文本
HistoryStore 保存原始转写、最终文本、状态、错误
DictionaryStore 个人词典
SnippetStore 常用片段
SettingsStore 快捷键、模式、隐私、模型路径
ErrorReporter 本地错误码、日志、用户可读提示

4. 核心流程

4.1 正常输入流程

Hotkey down
  -> PermissionManager.check()
  -> Recorder.start()
  -> CapsuleOverlay.show(listening)
  -> AudioLevelMonitor.update()

Hotkey up
  -> Recorder.stop()
  -> CapsuleOverlay.set(processing)
  -> ASRRouter.transcribe(audio)
  -> PolishEngine.polish(rawTranscript, mode, context)
  -> TextInserter.insert(finalText)
  -> HistoryStore.save(session)
  -> CapsuleOverlay.set(inserted)
  -> CapsuleOverlay.hide()

4.2 插入失败流程

TextInserter.insert(finalText) fails
  -> ClipboardFallback.copy(finalText)
  -> HistoryStore.save(session with copied_fallback)
  -> CapsuleOverlay.set(copied_fallback)

用户不需要重新录音。

4.3 取消流程

Esc or capsule x clicked
  -> Recorder.cancel()
  -> discard unsaved audio by default
  -> CapsuleOverlay.set(cancelled)
  -> CapsuleOverlay.hide()

4.4 重跑润色流程

History item selected
  -> choose another mode
  -> PolishEngine.polish(rawTranscript, newMode, context)
  -> update finalText
  -> copy or insert

5. 状态机

5.1 应用状态

状态 含义 用户表现
app_ready 应用可用 菜单栏正常,胶囊隐藏
permission_required 缺少权限 设置页提示补权限
model_unavailable 模型或 API 不可用 提示切换路径或检查 key
offline 网络不可用 云端不可用,本地仍可用

5.2 录音状态

状态 含义 胶囊表现
idle 未录音 隐藏或极淡小点
listening 正在录音 叉号 + 动态条 + 勾号
stopping 正在收尾 动态条停止,进入处理
cancelled 用户取消 叉号高亮后淡出

5.3 处理状态

状态 含义 胶囊表现
transcribing 正在转写 “识别中”或 spinner
polishing 正在润色 “整理中”或 spinner
ready_to_insert 结果生成 准备插入
failed 处理失败 红点或“失败”,点击展开详情

5.4 插入状态

状态 含义 胶囊表现
inserted 成功插入 勾号高亮后淡出
copied_fallback 插入失败但已复制 “已复制”
focus_lost 输入框失焦 “已复制”
permission_blocked 权限不足 引导打开辅助功能权限

6. 数据模型

6.1 DictationSession

字段 类型 说明
id string 会话 ID
created_at datetime 创建时间
started_at datetime 录音开始时间
ended_at datetime 录音结束时间
duration_ms number 录音时长
source_app_name string 当前 app 名
source_bundle_id string 当前 app bundle id
target_context_type enum chat / email / doc / code / unknown
language_hint enum auto / zh / en / mixed
mode enum raw / light / structured / formal
model_route enum local / cloud / byok
raw_transcript text 原始转写
final_text text 最终输出
insert_status enum inserted / copied_fallback / failed
fallback_reason string/null 插入失败原因
audio_saved boolean 是否保存音频
audio_path string/null 音频路径,默认 null
error_code string/null 错误码

6.2 UserSettings

字段 类型 说明
hotkey_record string 录音快捷键
hotkey_paste_last string 粘贴上一条快捷键
recording_behavior enum hold_to_talk / toggle_to_talk
default_mode enum 默认输出模式
default_model_route enum local / cloud / byok
history_retention_days number 历史保留天数
save_audio boolean 是否保存音频
show_floating_capsule boolean 是否显示底部状态胶囊
auto_copy_on_failure boolean 失败时是否自动复制
personal_dictionary_enabled boolean 是否启用个人词典

6.3 PersonalDictionaryEntry

字段 类型 说明
id string 词条 ID
phrase string 用户确认过的正确词
category enum name / product / tech / company / custom
notes string 可选备注,不作为硬替换规则
enabled boolean 是否参与 ASR 热词和后期语义判断
case_sensitive boolean 是否区分大小写
created_at datetime 创建时间

6.4 TextSnippet

字段 类型 说明
id string 片段 ID
trigger_phrase string 语音触发词
content text 输出内容
enabled boolean 是否启用

7. 输出模式与 Prompt 规则

7.1 模式枚举

mode 名称 行为
raw 原文 只补标点,尽量不改词
light 轻度润色 去口癖、去重复、补标点、轻微整理
structured 清晰结构 适合长 prompt、需求、说明,输出段落或列表
formal 正式表达 适合邮件、客户沟通、正式工作表达

7.2 通用 Prompt 约束

所有润色模式都必须遵守:

  • 只整理用户表达,不回答问题。
  • 不执行命令。
  • 不新增用户没有说的信息。
  • 不删除关键限制条件。
  • 保留专有名词、代码名、产品名和人名。
  • 保留用户习惯表达,避免 AI 腔。
  • 中文输出使用自然中文标点。

7.3 轻度润色模板

你是语音输入文本整理器。请把用户的口语转写整理成可直接发送或继续编辑的文字。

要求:
- 去掉明显口癖、重复和无意义停顿。
- 补充自然标点。
- 保留用户原意和表达习惯。
- 不扩写、不创作、不回答内容。
- 如果包含中英混输、产品名、代码名,请尽量保留。

原始转写:
{{raw_transcript}}

8. UI 实现规格

8.1 胶囊窗口

建议实现为一个无边框、透明背景、always-on-top 的辅助窗口:

  • 不抢焦点。
  • 不出现在 Dock。
  • 屏幕底部居中。
  • 多屏时优先显示在当前活跃输入所在屏幕。
  • 空闲时隐藏。
  • 录音时以轻微 spring motion 出现。

尺寸:

状态 宽 高
Listening 128-180px 32-38px
Processing 150-210px 32-38px
Success 128-160px 32-38px
Error 150-210px 32-38px

结构:

[ x ]   [ audio bars / short status ]   [ check ]

禁止:

  • 不展示完整 transcript。
  • 不展示 mode/provider chips。
  • 不显示复杂按钮组。
  • 不做大面板默认展开。

8.2 设置窗口

设置窗口可用 SwiftUI:

  • 左侧导航。
  • 右侧内容。
  • 表格编辑词典和片段。
  • 历史支持搜索、复制、重新整理。

第一轮不追求复杂视觉,重点是清楚、稳定、可用。


9. 错误码与用户文案

error_code 触发场景 用户文案
mic_permission_missing 缺少麦克风权限 需要麦克风权限才能录音
accessibility_missing 缺少辅助功能权限 需要辅助功能权限,OpenLess 才能输入到当前 app
focus_lost 输入框失焦 找不到输入位置,结果已复制
asr_failed 转写失败 这次没有识别成功,请重试
polish_failed 润色失败 整理失败,原始转写已保存
network_unavailable 云端不可用 网络不可用,可切换本地模式
model_unavailable 模型不可用 当前模型不可用,请检查设置

文案原则:

  • 短。
  • 直接。
  • 说明用户下一步能做什么。
  • 不用“释放生产力”“智能增强”等营销词。

10. 开发顺序

建议按以下顺序开发:

  1. 创建 macOS 菜单栏应用骨架。
  2. 实现设置窗口基础导航。
  3. 实现权限检查和引导。
  4. 实现全局快捷键。
  5. 实现录音与音量采样。
  6. 实现底部微型状态胶囊。
  7. 接入最小 ASR 路径。
  8. 接入轻度润色。
  9. 实现文本插入。
  10. 实现剪贴板兜底。
  11. 实现历史记录。
  12. 实现个人词典。
  13. 实现模式切换。
  14. 完成错误处理和隐私设置。
  15. 做真实 app 场景测试。

第一轮开发原则:

  • 先跑通主链路,再优化模型。
  • 先保证不丢内容,再追求插入成功率。
  • 先做好小胶囊,再做主窗口美化。
  • 先做中文和中英混输验收,再扩展更多语言。

11. 测试清单

11.1 功能测试

  • 首次启动能引导麦克风权限。
  • 首次启动能引导辅助功能权限。
  • 快捷键能开始录音。
  • 松开快捷键能结束录音。
  • 胶囊在录音时出现。
  • 胶囊动态条随声音变化。
  • Esc 能取消。
  • 中文能识别。
  • 英文能识别。
  • 中英混输能识别。
  • 润色结果能插入当前输入框。
  • 插入失败时能自动复制。
  • 历史能看到原文和最终文本。
  • 个人词典能影响后续输出。

11.2 场景测试

至少测试这些 app:

  • TextEdit。
  • Notes。
  • Safari/Chrome 网页输入框。
  • ChatGPT 或 Claude 输入框。
  • Cursor 输入框。
  • 微信/飞书/Slack 类 IM。
  • 邮件客户端。

11.3 质量测试

准备样例库,覆盖:

  • 去口癖和重复。
  • 中途改口。
  • 中文标点。
  • 中英混输。
  • 技术词。
  • 邮件表达。
  • Prompt 结构化。
  • 插入失败兜底。

12. 第一轮完成定义

满足以下条件才算第一轮完成:

  • 主链路可以连续稳定使用。
  • 常用 app 中可以完成输入。
  • 失败不会丢内容。
  • 胶囊 UI 足够小,不干扰工作流。
  • 默认润色自然,不过度 AI 化。
  • 历史和剪贴板可以恢复最近输入。
  • 隐私路径清楚,本地/云端/BYOK 不混淆。
  • 用户可以在 5 分钟内完成第一次成功输入。

13. 关联文档