docs: 复查确认 er_backend 已支持多模态,解除拍照提问的阻塞项

现场重新查询 er_backend 状态,确认已换成带 --mmproj 的多模态配置
(allow_image=1,vision 896x896),并对 ER_BACKEND_URL/v1/chat/completions
做了端到端真实请求验证(HTTP 200,正确识别测试图片颜色),证明前端
可以直接按现有代理路径发送 image_url content-part,不需要额外适配层。

顺带记录实测性能数据(极简图片 prompt_tokens=808,prefill ~2s)用于
后续加载态 UI 设计参考,更新分阶段计划:阶段0 已完成,阶段1/2 可并行开始。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ssurui 2026-09-03 14:33:58 +08:00
parent ee9b1153d5
commit 2ae01c2050

View File

@ -1,6 +1,7 @@
# 拍照提问模式 —— 设计方案
状态:**草案,未实现**。本文档只做设计,`feature_pic` 分支尚未修改任何业务代码。
唯一的硬阻塞项(后端视觉能力)已于 2026-09-03 复查解除,见 §2.2。
## 1. 背景与目标
@ -23,30 +24,49 @@ Chromium/Electron 的 `navigator.mediaDevices.getUserMedia()` 原生支持 UVC
一台设备接了两个摄像头,需要在 UI 里做设备选择(`enumerateDevices()` 列出后选 `deviceId`),
或者约定固定用其中一个(待确认哪个摄像头是安装时实际对外的那个)。
### 2.2 推理后端视觉能力 —— **当前不支持,是最大阻塞项**
### 2.2 推理后端视觉能力 —— **已确认可用(2026-09-03 复查更新)**
现场查询 er_backend(`http://127.0.0.1:17700/api/generate/status`),当前加载模型是文本模型,
llama-server 启动日志明确打印:
初次核实时(见本文档历史)当前模型是纯文本的,`allow_image=0`;**复查时 er_backend 已经换成带
`--mmproj` 的多模态配置**:
```
=model_desc = qwen35 9B Q4_0
=allow_image = 0
/home/work/houmo-HLIELLama-xh2-2.1.0/bin/llama-server \
-m /home/work/models/LLM/HiModel_xh2_qwen3.5-9b_w4a8_256_256k_b1_1chip_2cores_v1.4.0_20260708.gguf \
--mmproj /home/work/models/LLM/mmproj_xh2_qwen3.5-9b_w4a8_2cores_vit_896x896_v1.4.0_20260708.gguf \
-c 8192 --parallel 1 --host 127.0.0.1 --no-warmup --port 8081 --presence_penalty 1.5 --reasoning-budget 0
```
启动日志确认:
```
loaded multimodal model, '.../mmproj_xh2_qwen3.5-9b_w4a8_2cores_vit_896x896_v1.4.0_20260708.gguf'
==========model info===========================
=allow_image = 1
=allow_audio = 0
=vision_width = 0
=vision_height= 0
=vision_width = 896
=vision_height= 896
=model_desc = qwen35 9B Q4_0
==========model info===========================
```
跑在揽月 NPU 定制版 llama-server(`houmo-HLIELLama-xh2-2.1.0`)上。**`allow_image=0` 是运行时读出的模型能力,
不是配置开关**——要让这个后端接受图片输入,前提条件是:
**并且做了端到端真实请求验证**(不只是看模型加载日志),直接对 `http://127.0.0.1:17700/v1/chat/completions`
(即前端 `generator.ts` 实际会调用的同一个地址)发了一个带 `image_url` content-part 的请求:
1. er_backend 那边要换成一个视觉多模态模型(如 Qwen2-VL / MiniCPM-V / LLaVA 系列的 GGUF + mmproj),
且这个模型要能在揽月 NPU(HoumoNPU LQ50-24GB)上跑起来;
2. Houmo 这个定制版 llama-server 分支要支持 `--mmproj` 加载和 `/v1/chat/completions` 的
`image_url` content-part 输入(这是 upstream llama.cpp 的能力,定制分支是否移植了未知);
3. 这两点都不是 `elec_rag_ui` 这个前端仓库能决定或实现的,需要跟 er_backend / 硬件适配团队确认。
```
HTTP 200,耗时 2.6s
返回:{"content": "黑色"} —— 测试图片本身就是 1x1 黑色像素,模型回答正确,
证明图片被真实理解,不是被忽略/幻觉回答
```
**结论:这是一个跨团队依赖,不是前端待办项。** 在没有确认可用的视觉模型之前,前端这边能做的是把
UI、拍照、请求组装、流式渲染这套壳子先搭好、可 mock 验证,等后端具备能力后接上真实模型联调。
**结论:阻塞项已解除**,现在的 er_backend 代理路径(`ER_BACKEND_URL/v1/chat/completions`)原生支持
OpenAI 风格的 `image_url` content-part,前端可以直接照 §5.2 的格式发请求,不需要额外适配层。
**顺带记录的性能数据**(用于 §6 加载态 UI 设计参考):这次测试用的是 1×1 像素的极简测试图,
`prompt_tokens` 却达到了 **808**——印证了启动日志里的警告"Qwen-VL models require at minimum
1024 image tokens",即**无论照片内容/分辨率如何,视觉编码本身有一个较高的固定 token 开销**
(896×896 的 ViT 编码器决定的,不是按源图片大小线性变化)。prefill 速度约 373 tokens/s,
换算下来光是"看懂一张图"这一步就要 ~2 秒起,生成回答的时间还要另外算——真实拍照场景(图片本身
+ 问题文字)单次问答预计在 3-8 秒量级,UI 必须要有明确的"识别中"等待态,不能让用户以为卡死了。
## 3. 功能范围(建议先做 MVP)
@ -175,27 +195,28 @@ vision: {
| 阶段 | 内容 | 前置条件 |
|---|---|---|
| 0 | 跟 er_backend/硬件团队确认视觉模型可用性(模型+mmproj+定制llama-server支持) | 无,最先做,可能耗时最长 |
| 1 | 摄像头取景/拍照 UI(`CameraCapture.vue`),先只做交互不接后端,用假数据验证体验 | 无 |
| 2 | IPC + `visionGenerator.ts` 打通,`generator.ts` content 类型改造 | 阶段0 有阶段性结果(哪怕是测试环境有一个能跑的视觉模型) |
| 3 | 配置项、设置页开关、真机联调(含真实摄像头 + 真实视觉模型) | 阶段1、2完成 |
| 0 | ~~跟 er_backend/硬件团队确认视觉模型可用性~~ **已完成**(2026-09-03,见 §2.2,端到端验证通过) | — |
| 1 | 摄像头取景/拍照 UI(`CameraCapture.vue`),先只做交互,用假数据验证体验 | 无 |
| 2 | IPC + `visionGenerator.ts` 打通,`generator.ts` content 类型改造 | 无(阶段0已就绪,可直接对真实后端联调) |
| 3 | 配置项、设置页开关、真机联调(含真实摄像头 + 真实视觉模型,重点测真实照片而非1x1测试图的延迟/效果) | 阶段1、2完成 |
| 4 | 打包验证(照片相关无新增大文件,不影响现有打包体积) | 阶段3完成 |
阶段 1 可以和阶段 0 并行——UI 壳子不依赖后端是否具备视觉能力。
阶段 0 已解除,1/2 可以直接开始且互不依赖,建议并行推进。
## 8. 风险与未决问题
1. **(阻塞)视觉模型可用性**:见 §2.2,需要外部确认,是本功能能否上线的唯一决定因素。
2. Electron 权限处理器对摄像头请求的 `permission` 字符串需要实测确认(§5.1)。
3. 两个摄像头哪个是实际安装朝向的那个(500万 vs 800万像素那颗),需要现场看一下物理安装位置,
1. Electron 权限处理器对摄像头请求的 `permission` 字符串需要实测确认(§5.1)。
2. 两个摄像头哪个是实际安装朝向的那个(500万 vs 800万像素那颗),需要现场看一下物理安装位置,
UI 上是默认写死还是给用户切换待定。
4. 图片 base64 塞进 `/v1/chat/completions` 请求体,走 er_backend 代理转发时有没有请求体大小限制
(nginx/代理层常见的 `client_max_body_size` 之类),需要实测一次较大分辨率照片是否会被拒绝,
§5.4 的 `maxImageDimension` 压缩正是为了控制这个风险。
5. 揽月 NPU 跑视觉模型的推理速度未知(多模态模型通常比纯文本模型更吃算力/显存),首次联调时要
实测响应时延,评估是否需要专门的等待态 UI 提示。
3. 图片 base64 塞进 `/v1/chat/completions` 请求体,走 er_backend 代理转发时有没有请求体大小限制
(nginx/代理层常见的 `client_max_body_size` 之类),已验证极小图片可以正常通过代理,但**没测过
真实分辨率照片**(896×896 甚至更大的 base64 体积),§5.4 的 `maxImageDimension` 压缩正是为了
控制这个风险,阶段3要专门测一次。
4. 揽月 NPU 视觉推理延迟:见 §2.2 实测数据,极简图片仅 prefill 就要 ~2s,真实照片 + 生成完整回答
预计 3-8 秒量级,UI 必须要有明确的"识别中"等待态。
5. `--reasoning-budget 0` 这个启动参数暗示当前配置关闭了推理/think 模式,视觉问答场景要不要开
thinking(现有 ChatPanel 的 `<think>` 块渲染能力目前用不上),待联调时确认输出格式是否有变化。
## 9. 下一步
不写业务代码,先推进 §7 阶段 0(跟 er_backend/硬件团队确认视觉模型)—— 这是唯一的硬阻塞项,
其余都是 `elec_rag_ui` 仓库内部能自主推进的常规前端开发工作。
阻塞项已解除,可以直接开始 §7 阶段 1(UI 壳子)和阶段 2(后端联调),两者互不依赖,建议同时推进。