Files
goodbuddy/docs/features/document-processing/implementation.md
T
mesalogo d80d42b032 feat(storage): move desktop SQLite ownership into a storage utility process
Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.

- Remove the Main readWithFallback path and the supervision worker's
  separate business write connection; manifests are scanned read-only and
  committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
  unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
  ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
  storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
  directories and reclaim confirmed stale ones; stop writing duplicate
  parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
  acceptance fixtures, and maintenance/architecture documentation.

Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
2026-10-06 15:40:18 +08:00

132 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 文档处理生产实现
2026-09-19。本文描述当前源码,验收证据单独见 [progress.md](./progress.md#2026-09-19-实现与验证)。
需求 ID 沿用 [PRD](./prd.md),界面原则沿用 [UI 设计](./ui-design.md)。
## 解析与设置
`DocumentParsingService` 统一服务聊天文档、知识库、旧成果导入 API 和设置测试。成果导入界面
已移除;工作区“导入文件”只复制或上传原件,不解析、不自动加入消息。会话附件仍由下述会话
资产存储管理,不自动复制到项目目录。`HttpDocumentOcr`
在 Main 调用 PaddleOCR-VL,`render-ocr-pdf.ts` 使用 PDF.js 和本机 Canvas 渲染选中页,
不需要本地 OCR 权重。来源未配置时继续原有本地行为;产品没有测试地址默认值。
设置文件版本为 4,读取版本 1、2、3 时保留原有设置。认证支持无需认证和 Bearer API Key,
复用 Electron safeStorage,公开快照只显示是否配置。空密钥保持现值,显式清除才删除;
任务同时固定设置与凭据读取对象,中途保存不会更换在飞请求的地址或密钥。
快速文本/索引不上传。自动 PDF 只渲染需要 OCR 的页,高保真可直接发送全 PDF。
跨页整理开启时,连续待 OCR 页组成一个 PDF 请求;不连续页分组,避免把不相邻页面合表。
PPTX 仍按原生文本和直接引用的内嵌图片处理,定位保留幻灯片号与图片序号。
普通聊天图片直传;“提取图片文字”独立调用已保存来源,忽略快速工作流的自动跳过设置。
HTTP 请求固定返回 Markdown 图片、关闭调试可视化。高级布尔值保留省略、true、false;
过滤分别保存服务默认、全部保留、自定义及其排除集合。自定义隐藏后不清空未知标签。
页面处理、图表/印章/图片块识别、Markdown 格式、跨页整理和类型化版面/生成参数可配置。
部署模型能力需以用户样例确认,连接检查只执行 health 和 OpenAPI。
响应流上限 32 MiB,单张解码图上限 12 MiB、4000 万像素,文档文字上限 500 万字符。
请求超时独立于本地单页超时;错误保留 HTTP 状态、服务错误码及结构化 `errorMsg` / `logId`。
非成功响应最多读取 8 KiB,消息移除已配置密钥、整理换行后限制为 240 字符,不展示原始响应正文。
PPTX 回退提示按实际 OCR 来源显示,并在图片处理失败时包含幻灯片号与图片序号。
不下载服务返回的图片 URL,不自动重试或切换 Provider。
图片按来源页和页内键解码,以 UUID 替换 Markdown 与 HTML img 引用。同名键跨页不会覆盖。
跨页整理的变化输出作为一个带完整 `sourcePages` 的分组正文;图片按实际返回字节匹配原页。
无法唯一匹配时保留原逐页结果并警告。严格索引/高保真遇缺图或重组失败拒绝替换。
服务无置信度时字段缺省,详情显示“未提供”。
## 文件与引用
```text
<userData>/
conversation-attachments.sqlite
conversation-assets/<conversationId>/
images/<resourceId>/original.<ext>
documents/<resourceId>/original.<ext>
documents/<resourceId>/manifest.json
documents/<resourceId>/images/<imageId>
knowledge/<libraryId>/<sourceId>/<managed-original>
knowledge-assets/<libraryId>/<documentId>/<resultId>/
manifest.json
images/<imageId>
temp/document-parsing/<resourceId>/
```
S04 (2026-10-05): new results store parsed content in `manifest.json` only;
`parsed.md` is no longer written or copied. Released manifests remain readable,
including directories containing the old duplicate. Async storage-host integration
and its remaining composition work are specified in
[Desktop File Storage Integration](../../architecture/desktop-storage-files.md).
会话资产的 `request.json` 保存已转换的请求内容,用于恢复内存 context;原件字节单独保存,
缩放和 JPEG 请求副本不覆盖原图。提取图片文件使用 UUID 无扩展名,MIME 在清单记录。
图片显式 OCR 后生成解析文件。切回原图保留已有结果,并重新进入图片能力校验。
HTTP OCR、显式图片提取及结果完整程度共用 `hasExtractedDocumentText`;图片引用与生成的
缺图警告只作预览/诊断,不算正文。无正文的显式提取失败并保留原草稿;有有效插图的文档
结果仍可保存为仅图片,不因缺图警告改判为可发送文本。
`ConversationAttachmentStorage` 的专用 SQLite schema 1 保存描述、路径与
`draft/message/queue/parsing` 引用,不在这些描述字段内存入原图 Base64。纯图片无需解析清单。
Main 先保存资产,再保存草稿或接受队列;消息、分支建立独立引用。无有效引用才回收文件。
会话删除、项目删除及清空数据均核对引用,启动回收已无归属的记录和孤立 UUID 目录。
清理失败保留无引用数据库记录供下次重试,不回滚已接受的草稿。
Renderer 仅在活动会话 ID 非空时读取附件草稿;切换到尚无会话的消息通道项目时不发出
`context:get-draft` 请求。切回本地会话后照常恢复附件,Main 继续校验会话 ID 的 UUID 格式。
引用保存按输入顺序去重并查询资产。已有引用的顺序、会话归属均相同,且资产无需从临时目录
转存时,直接返回,不重复开启写事务或删除、插入引用。空引用同样适用;顺序、归属或附件
发生变化时仍按原事务更新,临时资产仍须转存。
助手库 schema 39 标记新附件元数据,阻止旧客户端将其当旧格式读取;没有批量改写历史附件。
知识库 schema 13 新增 `metadata.parsedResultId` 表达式索引,结果按主键定位,不扫描全部文档。
知识库新结果先写入独立 resultId 目录,数据库正文/chunks 发布成功后替换引用,再清理旧目录。
原件继续遵循 managed/reference 原有布局,reference 派生文件不写入外部目录。
旧正文重解析为空索引时保留旧结果;首次仅图片结果可预览,但显示无可索引文字。
会话图片从文档结果复制为独立原图文件,保存文档名、页码、resultId、imageId。
源文档重解析或删除不影响会话副本。添加批次串行,重复来源在同一草稿内返回已有项;
提交前重新读取目标草稿,数量/容量不符整批回滚。限额集中在 `attachment-limits.ts`:
每消息 8 项、内存 context 16 项和 12 MiB;原文件上限仍为图片 12 MiB、文档 20 MiB。
重解析与发送方式改变采用新文件资源替换草稿位置,旧消息/队列保持原 resourceId。
`attachmentId` 保留同一草稿附件身份,context 的 `id/resourceId` 随新产物更换,原始名称和来源保持。
图片 OCR 后再次切回原图会保留原 OCR 结果的副本,用户无需重跑 OCR 才能查看。
## 任务与队列
文件导入提供 operationId、读取/解析/保存阶段和取消。HTTP AbortSignal 贯穿页渲染、识别、
重组与提交检查。首次失败回滚本批附件,已保存原件用独立解析失败记录供重试,不作为可发送
草稿项。用户取消清理本批暂存;退出或进程中断后,启动将未完成记录标为中断,不自动请求 OCR。
旧结果重解析失败时保留旧草稿或知识索引。
队列保存接受时的附件和发送方式。入队、分发、实际执行分别检查图片能力。
分发失败保留队列及可恢复错误,队列预览只读;“恢复到草稿”先合并并保存附件引用,
再移除队列项,文字用空行追加,超限保持原队列。Renderer 请求恢复时不传入旧草稿文本,
收到队列正文后通过原会话的函数式状态更新追加到最新草稿,保留等待期间的编辑和清空。
文件附件的启动恢复不再把提前保存的用户消息误认为 Runtime 已接受并删除队列。
## 界面与边界
设置页头的测试菜单选择聊天或知识库场景,未保存禁止测试/连接检查。测试中锁定设置退出,
可取消。诊断关闭释放测试资源,没有添加到会话入口。
`DocumentResultPreview` 共用于设置测试、聊天 Modal、知识文档详情。正文支持 Markdown、
受限 HTML 表格和已登记图片;脚本、事件属性、外部资源不渲染。图片按可见区域加载 640px
缩略图,点击大图才读原图。正文/图片定位与 PageTabs 使用结果页码;详情展示保存时配置。
知识库返回列表保留查询和位置。历史附件可复制到当前草稿重新解析,不修改原消息。
附件名称、解析状态及大小与操作行分开排列;查看结果和更多操作复用 32px 图标按钮,
提供悬停与键盘焦点提示。草稿、历史和队列共用相同操作组件,菜单及 Modal 的焦点恢复
继续由共享机制处理。具体布局见 [附件操作菜单](./ui-design.md#12-附件操作菜单)。
目标会话选择显示项目与名称;新建沿用 App 的项目限制和空会话复用,保持预览。
选择、已在草稿、移除、添加、前往会话分别处理。当前没有可编辑目标时需先选择或创建。
草稿图片可提示模型/Runtime 能力问题,但允许保留;Main 才是发送与入队校验边界。
本机推理监控的模型入口直达 `model`,OCR 行直达 `document-parsing`,HTTP 服务不加入本机服务列表。
OCR 和资产管理运行于 Desktop。直连模型及托管 OpenCode 已验证实际图片输入;Agent 未增加 OCR
解析器或新附件传输协议。当前桌面消费已有图片协议;Continue、DeepSeek Harness 的本次真实视觉
调用未执行,不能由设置声明推断这些组合已经过本轮验收。