diff --git a/docs/feature-pic-design.md b/docs/feature-pic-design.md index 3a0fea7..3db3207 100644 --- a/docs/feature-pic-design.md +++ b/docs/feature-pic-design.md @@ -268,3 +268,74 @@ vision: { **这次验证没有覆盖到的**:正式打包(deb)流程、设置页手动切换开关的 UI 操作(是直接改 `config.json` 打开的 `vision.enabled`,没有走设置页表单)、长时间使用的稳定性。 + +## 11. 架构调整:接入本地知识库检索(2026-09-03 二次设计,未实现) + +**状态:设计已定稿,代码尚未开始改。** 本节内容推翻 §3 "不做检索" 的原始决定,是当前的 +权威设计,实现时以本节为准,§3/§5.2/§5.3/§6 保留作为"第一版为什么这么想"的历史记录。 + +### 11.1 为什么要改 + +第一版 MVP 是"拍照直接问视觉模型",图片内容和本地知识库(维修手册文档)完全没有关联。 +但这个应用的核心价值是本地知识库问答,拍照应该是**新增一种提问入口**,而不是一条完全独立 +于知识库之外的旁路。新方案让拍照提问最终也能从知识库里查到相关手册内容来回答。 + +### 11.2 新流程 + +``` +用户拍照 + 可选输入问题 + │ +[主进程] 调视觉模型一次,用固定默认提示词(不是用户问题):"请用一句话简要描述这张图片 + 中的部件、故障现象或异常情况,不要展开解释、不要客套。" + → push 'vision-query:stage' {stage:'正在识别图片...'} + → 得到一段简短描述 caption(目标 5-10 秒内,靠提示词要求"一句话"+"不展开"来压时长, + 而不是硬截断 max_tokens——见 §11.3) + → push 'vision-query:caption' {caption} ← 渲染层立刻展示"识别到:{caption}",简洁一行 + │ +组装检索查询 retrievalQuery: + - 用户打字了问题 → `${caption}\n${question}`(图像描述 + 用户问题拼接,扩大召回面) + - 用户没打字 → caption 本身 + │ +[主进程] 复用现有 embedOne(retrievalQuery) → search(dbPath, vector, topK) + → push 'vision-query:stage' {stage:'正在检索...'} + → push 'vision-query:context' {chunks} + │ +生成用的问题 genQuestion: + - 用户打字了问题 → 用户原始问题(不用 retrievalQuery,避免 caption 把回答带偏, + 检索阶段负责扩大召回,生成阶段保持聚焦用户真实意图) + - 用户没打字 → caption 本身(这种情况下 caption 就是唯一的"意图"来源,没有更聚焦的选项) + │ +[主进程] 复用现有 streamGenerate(genQuestion, chunks, systemPrompt, callbacks)—— + **纯文字生成,图片到这一步已经不再传给模型**(第③步转成文字后,图片就完成使命了) + → push 'vision-query:stage' {stage:'正在生成...'} + → push 'vision-query:delta' 流式吐字,跟文字问答体验一致 + → push 'vision-query:done' +``` + +### 11.3 关键决策记录 + +| 决策点 | 结论 | 理由 | +|---|---|---| +| 图像理解默认提示词 | "请用一句话简要描述这张图片中的部件、故障现象或异常情况,不要展开解释、不要客套。" | 单句、明确要求"不展开",从提示词层面主动压生成长度,比硬 `max_tokens` 截断更不容易截出语义不完整的半句话 | +| 图像理解耗时目标 | 5-10 秒 | 用户明确的目标区间;实测极简回答("黑色"两个字)2.6s,§2.2 记录的极简测试图基准可作为下限参考 | +| 最终生成是否带图 | 不带,纯文字生成 | 图片信息已经浓缩进 caption,复用现有纯文字 `streamGenerate`,不需要每次生成都重新过一遍视觉编码的固定开销(见 §2.2,~800 token/次),更快 | +| 检索查询 vs 生成问题是否用同一段文字 | 不同:检索用 `caption+question` 拼接(更大召回面),生成用用户原始 `question`(无问题时退化为 caption) | 检索要"宁可多召回",生成要"保持聚焦",两个阶段的目标不一样,没必要共用一份文本 | +| 中间结果是否展示给用户 | 展示,"识别到:xxx",一行、简洁 | 5-10 秒的图像理解本身就是一段等待,把中间结果先亮出来能实质性缓解等待焦虑,而不是纯转圈 | +| 加载态设计 | 三段式:正在识别图片... → 正在检索... → 正在生成...,复用现有 `query:stage` 这套推送机制的样式 | 用户已经熟悉这套"正在xxx..."的提示语汇,不需要发明新的交互语言 | + +### 11.4 技术实现要点(待写代码时展开,这里先定方向) + +- `vision-query` 这个 IPC handler 内部变成"两段式":先调一次视觉模型拿 caption,再原样调用 + 现有 `embedOne`/`search`/`streamGenerate`(`queryHandler.ts` 已有的三步,直接复用,不用照抄一遍) +- `generator.ts` 的 `streamGenerateVision()`(第一版为了直接视觉问答新增的函数)现在只在 + "第③步图像理解"这一次性调用里用得上;原来那种"图+文一起发给视觉模型直接生成完整答案"的 + 用法不再需要,`ContentPart`/`image_url` 那套改造保留(第③步仍然要用),但调用方式变成 + "只问一句话、只要一个简短 caption",跟原来"生成完整长回答"的调用方式不一样,需要单独判断 + 是否要一个新的轻量函数(比如 `describeImage()`)而不是复用 `streamGenerateVision` 的完整流式 + 长回答路径 +- 新增 `vision-query:caption` 这个推送事件(原来只有 delta/done/error),渲染层要新增监听 + 和展示逻辑 +- `ChatPanel.vue` 的 `sendVisionQuery()` 需要重写内部流程以匹配上面的多阶段推送,但外层交互 + (拍照、缩略图、发送按钮)不用动 +- 是否需要给"识别图片"这一步单独设一个超时/失败兜底(比如识别失败了要不要还能继续走检索, + 还是直接报错终止)——待实现时决定,本节暂不展开