issue #118 v2 完整闭环。 ## 多轮对话 - QaSessionState 加 messages: Vec<QaChatMessage> + panel_visible flag。 - Cmd+Shift+/ 现在 toggle 浮窗可见性(不再启动录音);浮窗可见时 rightOption 路由到 QA recording,不可见时仍是主听写。open_qa_panel / close_qa_panel 分离。 - 第一轮 user message 嵌选区原文(# 选区原文 / # 我的问题),之后只送提问。 - 前端 QaPanel 渲染气泡列表(user 蓝色靠右 / assistant 灰色 markdown 靠左)。 ## 流式输出 - chat_completion_history → chat_completion_history_streaming:开 stream:true + SSE chunk 解析,on_delta 回调把每 chunk emit 成 qa:state{kind:"answer_delta"}。 - 前端 streamingAnswer state + StreamingAssistantBubble(带蓝色闪烁 caret)。 - answer 事件落定后清空 buffer,最终 messages 接管。 ## macOS 浮窗修复(v1 一直存在的雷) - capabilities/default.json 加 "qa" 到 windows 数组。否则前端 listen("qa:state") 被 Tauri 权限拦截,浮窗永远收不到事件。 - show_qa_window 用 NSWindow.orderFrontRegardless 替代 window.show() (后者在 macOS 走 makeKeyAndOrderFront 抢 frontmost)。这样 capture_selection 的 AX read / Cmd+C fallback 能稳定从原 app 读到选区。 - 所有 ObjC msg_send 必须 wrap 进 app.run_on_main_thread —— macOS 26 对 NSWindow 主线程要求是硬断言(违反直接 SIGTRAP)。 - NSWindow.movableByWindowBackground=YES:浮窗任意空白处可拖。 - QA hotkey 注册在主线程:global-hotkey crate 在 macOS 用 Carbon RegisterEventHotKey,事件回调要靠主 run loop dispatch。 ## 胶囊状态同步 - QA 录音 / 转写 / 思考 同步推 capsule:state,让用户在底部胶囊也看到状态。 - schedule_capsule_idle 同时检查 dictation 和 QA 都 Idle 才隐藏,避免旧 dictation Done timer 把 QA 的胶囊误关。 - open_qa_panel 进入时先 emit Idle 清掉 dictation 残留 message + insertedChars。 ## 设置页 - SelectionAsk 独立成一级 tab;预设减到 4 个 cmd+shift+ 系列(Option 跟主听写 共用,不放进 QA hotkey)。 - howto / hotkey / privacy 文案重写,反映 v2 的「先开浮窗、再 Option 录音」流程。 - prefs 默认 qaHotkey = cmd+shift+;。 ## 后续路线图 - docs/qa-reasoning-roadmap.md:思考能力 v2.2 设计稿(C 路线 reasoner model 切换 + reasoning_content delta + 折叠思考块)。
3.7 KiB
3.7 KiB
划词追问:思考能力(Reasoning)路线图
创建于 2026-05-01。流式输出(v2.1)已完成,思考能力(v2.2)暂未实施——这份文档是后续迭代的设计稿。
关联:issue #118 v2、PR #119、
openless-all/app/src-tauri/src/polish.rs、openless-all/app/src/pages/SelectionAsk.tsx。
背景与决策
用户提出:"QA 应该让 LLM 进行思考后再回复,并且可以设置思考强度"。
讨论了 3 条路径:
| 方案 | 实现 | 优 | 劣 |
|---|---|---|---|
| A | prompt-engineered(system prompt 加 <thinking> 块要求) |
0 配置改动;现 model 即可 | 思考质量受小模型限制;不可控 |
| B | OpenAI 标准 reasoning_effort: low/medium/high 字段 |
标准化 | DeepSeek-v4-flash 不识别该字段 |
| C | 切换 reasoner 模型(deepseek-r1 / o1 / claude extended thinking) | 真思考;可视化推理过程 | 用户得多配一个 model;UI 复杂度高 |
结论:选 C。A/B 在当前 provider 下等于无效。
实施分解
后端
-
凭据存储:
CredentialAccount加两条ArkReasonerModelId(如deepseek-r1、doubao-seed-1.6-thinking)- 复用现有
ArkApiKey/ArkEndpoint(同一 provider 不同 model)
-
prefs:
Preferences加字段qa_reasoning_effort: ReasoningEffort枚举Off | Low | Medium | High- 默认
Off(与现行为一致)
-
answer_chat_streaming重载:根据 effort 决定走 chat 还是 reasoner endpointOff:走 v2.1 现路径(chat 模型 + stream)Low/Medium/High:走 reasoner 模型;强度通过 system prompt hint 调("简短思考即可" / "详细思考" / "深度推理多角度")- SSE 解析时同时收
delta.content+delta.reasoning_content,两者通过不同事件 emit:qa:state {kind:"reasoning_delta", chunk}qa:state {kind:"answer_delta", chunk}(已存在)
-
answer_chat 拼装最终 message 时,
reasoning_content不写入messages数组(只显示用,不进上下文)。多轮提问只把最终答案带回上下文。
前端
-
SelectionAsk.tsx 新增配置块:
- 「思考强度」下拉:关闭 / 浅 / 中 / 深
- 「思考模型」输入框(model id;默认
deepseek-r1) - i18n:zh-CN / en
-
QaPanel.tsx 新增「思考过程」可折叠区块:
- 在 user 气泡下方、最终 assistant 气泡上方
- 默认折叠,标题
思考中…/思考过程(X 字),点击展开 - 流式期间:实时拼接
reasoning_delta,气泡有打字 caret - 答案完成:折叠收起;用户随时可点开看推理
-
types.ts:
QaStateKind加'reasoning_delta';payload 加reasoning_chunk?: string
边界与风险
- Provider 兼容性:火山 Ark 的 deepseek-r1 / doubao-thinking 都返回
reasoning_content;OpenAI o1 通过 thinking blocks(不是 reasoning_content),需要单独 adapter - Token 成本:reasoner 模型 token 价格高 5-10x;用户开「深度」就是真烧钱,UI 应该有提示
- 延迟:reasoner 首 token 可能 > 5s(思考阶段无 content 输出)。要在 UI 上区分「思考中」(reasoning streaming)vs「答题中」(content streaming),避免用户以为卡了
工作量估算
- 后端 reasoner 通路 + SSE 双流解析:~2h
- 前端折叠思考区块 + 打字 caret + 状态切换:~1.5h
- prefs / SelectionAsk 配置 UI + i18n:~0.5h
- 端到端测试(三档强度 × 单/多轮 × 错误回退):~1h
- 总计:~5h
实施先决条件
- 用户配置好一个 reasoner model(deepseek-r1 / doubao-thinking-pro 等)
- 后端凭据 vault 写入对应 model id
- v2.1 流式输出已稳定(已完成 ✅)