11 KiB
聊天附件技术说明
产品规则见 PRD 的聊天附件入口,输入区布局与反馈遵循 UI 设计系统。本文说明本地文件选择、文件粘贴和截图粘贴的实现。
文件导入链路
App.tsx 在 textarea 的 paste 事件中同步读取 clipboardData.files。没有文件时
保留浏览器默认文本粘贴。有文件时阻止默认插入,并通过 preload 的
context.getFilePath(file) 调用 Electron webUtils.getPathForFile。
有本地路径的文件调用 context.importFiles(paths),经 context:import-files IPC 到
Main。IPC 先验证可信 sender,再使用共享 contextImportFilesSchema 校验输入。
没有本地路径的截图沿用 context.addPastedImage;无路径的非图片文件和混有无路径项目
的本地文件批次显示可处理的错误,不静默丢弃项目。
ContextManager.selectFiles 打开不按文件类型过滤的多选文件对话框,再调用与粘贴共用的 importFiles。
这个方法完成路径规范化、格式及大小检查、文本读取、图片转换、文档解析与上下文暂存。
任一文件失败时删除本批次已经暂存的项目,保留之前的附件。文件系统错误不向 Renderer
暴露绝对路径。
两种文件入口均发送 context:file-selection-progress。Renderer 共用正在导入状态、
附件上限处理和附件清理逻辑,解析期间禁用附件按钮并阻止发送。成功结果加入发起操作的
会话草稿;发送时通过已有 contextIds 和 ContextManager.enrichRequest 生成请求上下文。
本次变更不修改 Agent、远端协议或 Runtime 的附件消费实现。
2026-09-19 生产更新
HTTP 来源、文件资产、持久化草稿、显式图片 OCR、选图、历史预览和队列恢复已接入。 当前行为以生产实现说明为准;本文件后续“纯图片现状与规划边界” 及“完整附件流程设计”保留 2026-09-18 的实施前分析,不能作为当前未实现清单。 历史 Electron 记录仍按原日期保留,本轮证据见进度。
纯图片现状与规划边界
当前 ContextManager 将本地图片、粘贴图片和消息通道图片交给 storeImage,通过
encodeBoundedJpeg 转成 JPEG 暂存在内存;enrichRequest 将其放入 images,不调用
DocumentParsingService。serializeForQueue 保存上下文内容,restoreFromQueue 可恢复
队列图片,但这些机制未实现原始图片的独立文件保存。
图片能力由有效模型和 Runtime 决定。例如 ModelRuntime 在未启用图片输入时拒绝图片请求;
图片工具绑定路径还可能只保留工具引用并告知聊天模型无法看图,不能由附件已添加推断模型
已看到图片。现有路径没有统一的“提取图片文字或切换视觉模型”交互,本文不声称已经实现。
已确认的目标规则见 纯图片路由需求,原图与解析 文档分开保存的目录及生命周期见 资源规划。 显式图片 OCR、原图保留、草稿引用保护和长期预览均待实现,以下历史验证不覆盖这些能力。
完整附件流程设计
本节为待实现范围,与下述既有验证记录分开。设置与预览的布局、交互及验收由 UI 设计维护,发送模式、幂等、限制及删除规则见 选图与发送,进程契约和资源提交见 HTTP 技术设计。
需要在现有聊天链路中补齐:
App.tsx的会话草稿附件增加读取持久化资源、逐附件解析状态、预览、重试和取消。 当前attachmentsByConversation与内存 contexts 不构成跨重启持久化。- 共享附件契约保存资源引用、原始名称、来源和发送方式。图片 OCR 成功只改变该草稿的 发送方式与显示名,原图仍可打开;消息与队列保留接受时的文字/图片选择。
- 文档预览添加选图与普通导入汇入同一草稿和请求消费路径。选图采用整批提交及来源幂等, 不能直接套用新增文件按剩余位置截取的 UI 逻辑。父文档全文只送一次,图片分别计数。
- 数量及容量值统一读取已有附件规则;Main 对草稿、入队和实际执行分别核验能力及有效引用, UI 不凭模型名称或图片工具引用推断已支持视觉。
- 草稿、发送和队列先保存会话资产与 DB 引用,再报告接受;从知识库复制的图片属于会话, 不依赖源库文档继续存在。移除父文档、图片、队列或会话各自只释放对应引用。
ContextFileSelectionProgress当前只有读取/解析阶段,需增加阶段、操作 ID 和取消接线; 首次批次失败仍回滚本批,新结果失败则保留旧附件。重启不自动恢复中断的 HTTP 推理。
新增图片发送路径仍需核对实际 Runtime 消费,包括远端 Agent;前文“本次变更不修改 Agent” 描述的是已完成文件粘贴工作,不是对本节未来实现范围的保证。生产实现需按仓库规则验证 受影响的远端路径,不能用历史 Windows 文件粘贴测试替代。
PPTX 图片识别与 OCR 生命周期
产品策略与限制分别见 PPTX 图片流程、 OCR 空闲资源回收。
DocumentParsingService 在非快速工作流中使用 knowledge/pptx-parser.ts 读取
演示文稿顺序、幻灯片文字、图片关系和内嵌图片。Office ZIP 读取复用
document-parser.ts 的条目数量、单条目及总展开大小检查;不读取工作区中的关联文件。
每张引用图片通过现有 DocumentOcrBroker 的图片请求契约,进入生产
document-ocr-bridge.ts 和 WASM Worker。结果合并时保留原生文字,附上幻灯片页码、
图片定位及 OCR 置信度,诊断中的 OCR 页数按页码去重。
Bridge 用待完成请求计数判断队列是否空闲,正常完成后安排一次空闲计时;
新请求和 terminateWorker 均清除计时。到期通过 Worker.terminate() 释放整个推理环境,
而不是只清空 JavaScript 引用或反复触发垃圾回收。此机制由 PDF 与 PPTX 共用,
不增加 IPC、持久化设置或模型安装状态。
解析与回收发生在 Desktop 的 Main/Renderer,gbagent 不运行此 OCR Worker。
对于 PDF、Office 文档,远程 Runtime 消费的仍是 Desktop 已提取的附件文本,没有单独的 Agent 解析器或
需要同步修改的远程 OCR 生命周期。
Electron 验证
2026-09-15 在 Windows 的 Electron 43.2.0 上,从当前源码构建隔离输出并启动完整应用,
使用独立用户数据目录,保持 sandbox: true、contextIsolation: true。
已实际验证:
- Windows
Shell.Application的文件copyverb 生成系统剪贴板后,原生粘贴事件提供File和有效路径,TXT 出现在聊天附件区。 CF_HDROP同时携带 TXT、DOCX 时,两个文件经生产 preload、IPC 和解析器成为附件; DOCX 发出reading、parsing进度,包含正确文件名及2 / 2批次序号。- 普通文本原生粘贴进入草稿,后续文件粘贴保持文字不变。
- ZIP 经生产导入路径报出不支持类型,已添加附件仍在。
- 系统图片剪贴板产生的
image.png没有本地路径,仍通过截图入口生成图片附件。
以上使用原生 webContents.paste(),未伪造 DOM 粘贴事件或替换附件实现。Windows Shell
复制测试没有操纵资源管理器窗口中的键盘。产品未增加 Win32 剪贴板读取或兼容代码。
探测程序的原生调用仅用于构造文件剪贴板和保存、恢复测试前的剪贴板对象;最终探测的
OleSetClipboard、OleFlushClipboard 均返回成功。早期一次探测的恢复调用返回错误,
无法追溯确认该次剪贴板是否完整恢复。
macOS、Linux 文件管理器的实际剪贴板行为尚未在本次环境验证。没有发起模型调用;附件
进入发送请求的 contextIds 由 Renderer 回归测试验证,完整文档内容进入请求由生产
ContextManager 的真实文件解析测试验证。
自动检查
聚焦用例位于 src/main/context-manager.test.ts、src/main/ipc.test.ts、
src/preload/context-files.test.ts、src/shared/context-contracts.test.ts 和
src/renderer/src/App.test.tsx,覆盖多文件、进度、重复粘贴、发送上下文、错误与草稿保留、
大小及数量限制、批次回滚、可信 sender、输入契约和截图/文本回归。
npx vitest run src/main/context-manager.test.ts src/main/ipc.test.ts src/preload/context-files.test.ts src/shared/context-contracts.test.ts src/renderer/src/App.test.tsx
npm test
npm run typecheck
npm run lint
本次最终拆分检查通过 6 项附件相关 Renderer 用例和 160 项 Main、IPC、preload、契约及
沙箱用例,类型检查与 lint 通过。完整 npm test 运行结果为 4214 项通过、67 项跳过、
6 项窗口恢复用例失败;这些用例属于并行修改的 window.test.ts,随后该文件单独复跑
10 项全部通过。本次没有再次完整重跑全仓测试。
2026-09-15 PPTX OCR 与空闲回收验证
在 Windows x64、Electron 43.2.0 上,从本次源码构建独立输出,使用独立用户数据目录和
本机已安装的 PP-OCRv6 Tiny 模型完成真实验证。测试仅替换系统文件选择对话框的返回路径,
保留生产 UI、preload、IPC、ContextManager、解析服务、OCR Broker 与 WASM Worker,
未替换解析或推理实现。
- 从“添加附件”一次导入 4 份图片型 PPTX,共 40 页;4 个附件均成功显示,连续处理期间 仅创建 1 个 OCR Worker。
- 队列空闲后观察到 Worker 被终止。每 5 秒读取 Electron
app.getAppMetrics(): Renderer 工作集采样高点为 733,492 KiB(约 716 MiB),回收后为 162,716 KiB (约 159 MiB),启动基线约 163 MiB。该结果是本次设备与文件上的测量,不是固定内存承诺。 - 回收后,通过生产
context.importFiles再导入一份 9 页 PPTX;新建第 2 个 Worker, 9 页均返回非空文字,共 2,462 字符,附件成功生成。 - 本次合计 49 次本地图片 OCR 请求,文本模型调用 0 次;没有发送聊天消息,也没有将文件 交给云端模型。未修改源文件。未在 macOS、Linux 或其他 OCR 模型档位上做本次实测。
新增聚焦回归覆盖 PPTX 关系与顺序、各场景路由、原生文字合并、快速模式、页数上限、 OCR 错误和取消;Worker 回归覆盖空闲到期、连续请求复用、队列保护、关闭清理与立即取消。 空闲回收后重新加载也在真实模型路径上验证,不能只用模型配置检查代替。
最终检查:npm test 4,293 项通过、67 项跳过;npm run typecheck、
npm run lint 通过。当前源码的隔离 Electron 生产构建通过;文档的 10 个相对文件链接
均有效。测试应用已关闭,包含附件草稿的独立用户数据目录已删除,原文件和原模型安装未改动。