docs: 拍照提问架构调整为接入本地知识库检索(二次设计,未实现)

推翻 §3 "不做检索" 的原始决定。新流程:拍照后先用固定默认提示词
调一次视觉模型拿简短图像描述(目标5-10s,中间结果"识别到:xxx"
展示给用户),描述+用户问题拼接去检索本地知识库,再用用户原始问题
(不是拼接文本)+ 检索到的上下文做纯文字生成——图片到检索这步之后
就不再传给模型。三段式加载态复用现有 query:stage 推送机制的样式。

只是设计文档更新,代码未改动,等用户确认后再实现。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ssurui 2026-09-03 16:30:36 +08:00
parent b918a0c53f
commit 87b9ffbab5

View File

@ -268,3 +268,74 @@ vision: {
**这次验证没有覆盖到的**:正式打包(deb)流程、设置页手动切换开关的 UI 操作(是直接改 **这次验证没有覆盖到的**:正式打包(deb)流程、设置页手动切换开关的 UI 操作(是直接改
`config.json` 打开的 `vision.enabled`,没有走设置页表单)、长时间使用的稳定性。 `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()` 需要重写内部流程以匹配上面的多阶段推送,但外层交互
(拍照、缩略图、发送按钮)不用动
- 是否需要给"识别图片"这一步单独设一个超时/失败兜底(比如识别失败了要不要还能继续走检索,
还是直接报错终止)——待实现时决定,本节暂不展开