Skip to content

[feature] 语音快捷键触发 Claude Code / OpenCode Agent,并支持语音润色模型配置 #579

Description

@HKLHaoBin

背景

OpenLess 当前已经支持热键录音、ASR、LLM 润色、文本插入、Selection Ask QA 面板。希望在此基础上新增一个“语音调用代码 Agent”的独立能力:

  1. 用户触发专用快捷键后,OpenLess 开始录音。
  2. 再次触发快捷键后结束录音,ASR 得到文本。
  3. 系统可按配置先用“语音润色大语言模型”整理语音转写内容。
  4. 后端把最终文本发送给 Claude Code 或开源 OpenCode Agent。
  5. Agent 输出结果流式返回前端面板展示。

当前 OpenLess README 中说明产品主要负责文本整理,不负责回答问题、执行任务或分析项目。本功能会扩展为独立的 coding-agent 模式,应保持和普通听写、翻译、Selection Ask QA 分离,避免改变现有默认听写行为。

目标

新增一个独立功能:Voice Coding Agent。

默认行为:

  • 默认关闭,需要用户在设置页开启。
  • 使用独立快捷键,默认建议:
    • Windows/Linux: Ctrl+Shift+Enter
    • macOS: Cmd+Shift+Enter
  • 第一次按下开始录音。
  • 第二次按下停止录音并进入 ASR。
  • ASR 结果可先经过语音润色模型整理。
  • 整理后的指令发送给 configured coding agent。
  • 前端显示:
    • 原始 ASR 文本
    • 润色后的指令
    • Agent 当前状态
    • 流式输出
    • 错误、超时、取消状态
  • 默认不自动插入文本到当前光标位置。
  • 默认不允许 Agent 修改本地文件;权限模式默认为只读/计划模式。
  • 用户可在高级设置中切换为更强权限模式。

非目标

  • 不改变普通听写热键行为。
  • 不改变 Selection Ask QA 的现有交互。
  • 不把 Claude Code 输出默认插入到其他应用。
  • 不默认启用 bypassPermissions
  • 不把 API key、access token、命令输出中的敏感字段写入前端事件、日志或历史记录。

调研结论

  • OpenLess 当前主处理路径已经具备热键录音、ASR、LLM 润色、历史保存、QA 面板流式回答能力,可复用大部分基础设施。
  • 现有 Selection Ask QA 功能最接近目标:qa_hotkey 负责打开面板,主听写热键在面板可见时开始/结束 QA 录音,后端用 ASR 得到文本,再调用当前 LLM provider,前端通过 qa:state 接收流式回答。
  • 当前 UserPreferences 已有 active_llm_providerllm_thinking_enabledqa_hotkey 等字段;凭据通过 OS vault 保存,LLM provider 支持 Ark、OpenAI-compatible、Gemini、Codex OAuth、custom 等。
  • 官方 Claude Code 可通过 CLI 和 SDK 集成,关键参数包括 claude -p/--print--output-format text|json|stream-json--model--fallback-model--permission-mode--allowedTools--disallowedTools--mcp-config
  • 官方 Claude Code 未定位为可 fork 的开源实现;开源优先方案应增加 opencode 适配器。OpenCode 是开源终端 coding agent,适合做二次开发优先落点,同时保留官方 claude-code-cli 适配器满足 Claude Code 用户。
  • 相关 Issue 不构成重复:[insertion] 新增送出前文字處理 Hook #575 是“送出前文字处理 Hook”,[area] 无法现在大模型,也不知道哪个处于启用状态 #578 是模型选择状态问题;本需求是独立的语音触发代码 Agent 功能,但需要兼容 [insertion] 新增送出前文字處理 Hook #575 的文本处理扩展思路。

参考资料:

Claude Code / OpenCode 集成策略

Provider 抽象

新增后端 trait:

#[async_trait]pubtraitCodingAgentProvider:Send + Sync{asyncfnrun(&self,request:CodingAgentRequest,sink:CodingAgentEventSink,cancel:CancellationToken,) -> Result<CodingAgentResult,CodingAgentError>;}

核心类型:

pubstructCodingAgentRequest{pubsession_id:String,pubprompt:String,pubraw_transcript:String,pubpolished_prompt:Option<String>,pubcwd:PathBuf,pubmodel:Option<String>,pubfallback_model:Option<String>,pubpermission_mode:CodingAgentPermissionMode,puballowed_tools:Vec<String>,pubdisallowed_tools:Vec<String>,pubtimeout_secs:u64,pubextra_system_prompt:Option<String>,}pubenumCodingAgentEvent{Started{session_id:String},TranscriptReady{text:String},PromptReady{text:String},Delta{text:String},ToolEvent{name:String,status:String},Completed{text:String},Cancelled,Error{message:String},}

Provider IDs:

  • claude-code-cli
  • opencode-cli
  • 预留:claude-code-sdk

Claude Code CLI 适配器

新增 ClaudeCodeCliProvider

  • 默认可执行文件:claude
  • 支持用户配置可执行文件路径。
  • 使用非交互 print 模式:
    • claude -p
    • --output-format stream-json
    • --model <model>
    • --fallback-model <fallback_model>
    • --permission-mode <mode>
    • --add-dir <cwd> 如用户配置了额外目录
    • --mcp-config <path> 如用户配置 MCP
    • --append-system-prompt <prompt> 如配置了额外系统提示词
  • stdin 写入最终 prompt。
  • stdout 按行解析 stream-json。
  • stderr 独立收集,只在错误状态中返回摘要。
  • timeout 到期后 kill 子进程并发送 agent:error
  • 用户取消时 kill 子进程并发送 agent:cancelled
  • 子进程退出码非 0 时返回 CodingAgentError::ProcessExit

默认权限:

permission_mode = CodingAgentPermissionMode::Plan
timeout_secs = 300
allowed_tools = []
disallowed_tools = []

高级权限选项:

  • plan
  • default
  • acceptEdits
  • bypassPermissions

UI 中 bypassPermissions 必须放在高级区,附带明确风险文案,不能作为默认值。

OpenCode 适配器

新增 OpenCodeCliProvider

  • provider id: opencode-cli
  • 作为开源优先落点。
  • 默认可执行文件:opencode
  • 先实现同一 CodingAgentProvider trait。
  • 命令参数由适配器内部封装,保持前端配置和 Claude Code 适配器一致:
    • cwd
    • model
    • permission_mode
    • timeout_secs
    • extra_system_prompt
  • 若当前 OpenCode CLI 版本缺少稳定的非交互 stream 输出协议,先以实验 provider 暴露,并在 UI 中显示“experimental”。

后端实现

Preferences

UserPreferences 增加字段:

pub coding_agent_enabled: bool,pub coding_agent_hotkey:Option<ShortcutBinding>,pub coding_agent_provider:CodingAgentProviderId,pub coding_agent_workdir:Option<String>,pub coding_agent_permission_mode:CodingAgentPermissionMode,pub coding_agent_model:Option<String>,pub coding_agent_fallback_model:Option<String>,pub coding_agent_timeout_secs: u64,pub coding_agent_show_panel_on_start: bool,pub coding_agent_save_history: bool,pub coding_agent_use_voice_polish: bool,pub voice_polish_model_mode:VoicePolishModelMode,pub voice_polish_provider_id:Option<String>,pub voice_polish_thinking_enabled: bool,

默认值:

coding_agent_enabled = false
coding_agent_hotkey = CtrlOrCmd + Shift + Enter
coding_agent_provider = "claude-code-cli"
coding_agent_workdir = None
coding_agent_permission_mode = Plan
coding_agent_timeout_secs = 300
coding_agent_show_panel_on_start = true
coding_agent_save_history = true
coding_agent_use_voice_polish = true
voice_polish_model_mode = SystemDefault
voice_polish_provider_id = None
voice_polish_thinking_enabled = false

新增 enum:

pubenumVoicePolishModelMode{SystemDefault,CustomProvider,}pubenumCodingAgentPermissionMode{Plan,Default,AcceptEdits,BypassPermissions,}

同步更新:

  • Rust defaults
  • serde rename
  • TypeScript UserPreferences
  • IPC mock settings
  • 设置页初始值
  • 所有 i18n locale key

语音润色模型配置层

新增“语音润色大语言模型”配置,不直接复用全局 active LLM 的写入命令,避免改变普通听写配置。

配置模式:

  1. SystemDefault
    • 读取现有 active LLM provider。
    • 使用现有润色 prompt 和 style pack。
    • 不新增凭据。
  2. CustomProvider
    • 使用独立 provider id:voice-polish-custom
    • 凭据仍写入 OS vault 的 LLM providers map。
    • 保存字段:
      • displayName
      • apiKey
      • baseURL
      • model
      • temperature
      • extraHeaders
    • 前端只显示 hasApiKey,不回传明文 key。
    • 提供校验按钮,调用后端真实发起轻量模型请求。

新增后端命令:

get_voice_polish_model_config() -> VoicePolishModelConfigWireset_voice_polish_model_mode(mode:VoicePolishModelMode)set_voice_polish_provider_config(config:VoicePolishProviderConfigWire)validate_voice_polish_provider_config(config:VoicePolishProviderConfigWire) -> ProviderValidationResultlist_voice_polish_provider_models(config:VoicePolishProviderConfigWire) -> Vec<ModelInfo>

新增 provider factory:

fnbuild_voice_polish_provider(prefs:&UserPreferences,vault:&CredentialsVault,) -> Result<Box<dynLLMProvider>,ProviderError>

规则:

  • SystemDefault 走现有 ActiveLLMProvider
  • CustomProvider 通过 provider id 精确读取 vault entry。
  • 不修改 active_llm_provider
  • 不使用 ...storedSettings 展开旧字段。
  • 保存 preferences 时仅写白名单字段。

Hotkey

新增 hotkey watcher 字段:

coding_agent_hotkey:Arc<Mutex<Option<ShortcutBinding>>>

新增命令:

get_coding_agent_hotkey_label()set_coding_agent_hotkey(binding:Option<ShortcutBinding>)

更新冲突检测:

  • dictation hotkey
  • translation hotkey
  • selection ask hotkey
  • switch style hotkey
  • open app hotkey
  • coding agent hotkey

冲突时返回当前已有的快捷键名称。

Coordinator 状态机

新增状态:

pubenumCodingAgentPhase{Idle,Recording,Transcribing,Polishing,AgentRunning,}

新增 session state:

pubstructCodingAgentSessionState{pubsession_id:String,pubphase:CodingAgentPhase,pubstarted_at:Instant,pubraw_transcript:Option<String>,pubpolished_prompt:Option<String>,pubcancel_token:CancellationToken,}

快捷键处理:

  1. Idle 且 feature enabled:
    • 校验 workdir 已配置且存在。
    • 校验 ASR provider 可用。
    • 打开 Agent panel。
    • 启动 recorder + ASR session。
    • phase -> Recording
  2. Recording
    • stop recorder。
    • send last frame。
    • phase -> Transcribing
    • 等待 ASR final。
  3. ASR final 为空:
    • phase -> Idle
    • 前端显示空转写错误。
  4. coding_agent_use_voice_polish = true
    • phase -> Polishing
    • 调用 voice polish provider。
    • 失败时按配置 fallback:
      • 默认 fallback 到 raw transcript,并在 UI 显示 warning。
  5. 调用 coding agent:
    • phase -> AgentRunning
    • 将 stream event 转成 Tauri event。
  6. 完成:
    • 保存历史。
    • phase -> Idle
    • 前端显示完成。

事件名建议:

  • coding-agent:state
  • coding-agent:level
  • coding-agent:dismiss
  • coding-agent:history

前端事件 payload:

typeCodingAgentStateEvent=|{kind: "phase";phase: CodingAgentPhase;sessionId: string}|{kind: "transcript";text: string;sessionId: string}|{kind: "prompt";text: string;sessionId: string}|{kind: "delta";text: string;sessionId: string}|{kind: "answer";text: string;sessionId: string}|{kind: "error";message: string;sessionId: string}|{kind: "cancelled";sessionId: string};

所有事件必须带 sessionId,前端丢弃旧 session 的延迟事件。

前端

新增页面/组件:

  • VoiceCodingAgentPanel.tsx
  • VoiceCodingAgentSettings.tsx
  • 设置页新增分组:“语音调用代码 Agent”
  • provider 设置页新增分组:“语音润色大语言模型”

面板展示:

  • phase badge
  • raw ASR transcript collapsible block
  • polished prompt collapsible block
  • streaming markdown answer
  • stop button
  • copy answer button
  • copy prompt button
  • open settings button
  • error banner

设置项:

  • 启用 Voice Coding Agent
  • 快捷键录制器
  • Agent provider:Claude Code CLI / OpenCode CLI
  • Agent executable path
  • 工作目录选择
  • model
  • fallback model
  • permission mode
  • timeout
  • 是否先语音润色
  • 语音润色模型模式:系统默认 / 自定义
  • 自定义 base URL
  • 自定义 model
  • 自定义 API key
  • 校验模型按钮

安全与错误处理

  • 默认权限为 Plan
  • 未配置 workdir 时不启动 Agent,前端提示用户先选择项目目录。
  • 未找到 claudeopencode 时显示安装提示。
  • API key 永不通过普通 state event 回传。
  • 日志中屏蔽 bearer token、API key、account id。
  • 子进程必须支持取消和 timeout。
  • 快速连续按快捷键时只允许当前 session 生效。
  • ASR、润色、Agent 任一阶段失败都要恢复 phase 到 Idle
  • 旧 session 的 late event 必须被丢弃。
  • 历史记录只保存用户允许保存的 raw transcript、polished prompt、answer、provider id、model、时间戳,不保存 secret。

测试计划

Rust:

  • preferences 默认值和 serde 兼容测试。
  • hotkey collision 测试,覆盖所有已有快捷键。
  • voice polish custom provider 精确读取 provider id,不修改 active LLM。
  • Claude Code CLI args 构造测试。
  • stream-json parser 测试。
  • timeout kill 子进程测试。
  • cancel kill 子进程测试。
  • session id guard 测试。
  • 空 ASR 文本测试。
  • Agent process exit 非 0 测试。

Frontend:

  • UserPreferences 类型同步测试。
  • settings 初始渲染测试。
  • system default/custom voice polish 切换测试。
  • API key redacted 显示测试。
  • panel stream delta 合并测试。
  • stale session event 丢弃测试。
  • error banner 和 phase reset 测试。

手动验收:

  • 普通听写热键仍可录音、润色、插入。
  • Selection Ask QA 仍可打开、录音、返回回答。
  • Voice Coding Agent 关闭时快捷键无动作。
  • Voice Coding Agent 启用但未配置 workdir 时给出设置提示。
  • 使用系统默认语音润色模型能完成一次完整流程。
  • 使用自定义强模型能完成一次完整流程。
  • Claude Code CLI 不存在时给出可理解错误。
  • Claude Code 输出能流式显示。
  • 取消运行能终止子进程。
  • 超时能终止子进程。
  • 权限模式默认为计划/只读,不默认修改文件。
  • 切换到 OpenCode provider 后能走同一 UI 与事件协议。

验收标准

  • 用户可以在前端配置“语音润色大语言模型”,支持系统默认和自定义强模型。
  • 用户可以配置 Voice Coding Agent 的快捷键、provider、模型、工作目录、权限模式。
  • 触发快捷键后,ASR 内容能发送给 Claude Code CLI。
  • Claude Code 处理结果能返回前端并流式展示。
  • OpenCode provider 作为开源优先方案接入同一 provider trait。
  • 功能不破坏普通听写、翻译、Selection Ask QA。
  • 所有 secret 仍由 OS vault 保存,不进入前端事件和历史。
  • 默认权限不会修改用户项目文件。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions