Skip to content

文档图像识别系统 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构定位
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与资源控制
  8. 故障排查指南
  9. 结论

引言 ​

本系统为 AI Helper 扩展的“文档图像识别”能力,目标是在用户上传 Word、PDF、Excel 等文档后,把文档内嵌图片转述为文本送入主模型对话;同时提供 read_document_image(现泛化为 read_image)工具,让 Agent 按需深读某张图。其关键设计是:主模型始终消费纯文本,视觉模型仅在插件端调用;Agent 端只负责 docx 抽图落盘和按需提供下载,不解析 PDF,也不接触任何模型调用。

该功能由规划与设计文档确认,并在扩展侧与代理侧分别实现:

  • 扩展侧:浏览器端抽取 docx/pdf/xlsx 图片、压缩、视觉转述、清单块生成、session 级快照中转。
  • 代理侧:基于 Node ESM 的最小 ZIP 读取器 + docx 图片提取 + /api/documents/extract-images 接口。
  • 工具层:background 执行 read_image,从 _docImageSnapshot 取图并调用视觉 API。

章节来源

项目结构定位 ​

文档图像识别横跨两个进程:

  • Chrome 扩展端(Manifest V3,src/ 目录),版本以仓库根 package.json 为准。
  • 独立本地代理端 Agent(Node.js,agent/src/server.js 入口),npm 包名 ai-helper-agent,版本以 agent/package.json 为准。
graph TB
A["Chrome 扩展<br/>src/side_panel/doc-images.js"] --> B["background 工具执行器<br/>read_image 工具"]
B --> C["chrome.storage.local<br/>_docImageSnapshot"]
B --> D["视觉模型 API<br/>imageApiBase / imageApiKey / imageModelName"]
A --> E["浏览器 pdf.js / jszip"]
B --> F["Agent 文件服务<br/>local-agent-client.downloadFile"]
F --> G["Agent 文档处理<br/>/api/documents/extract-images"]
G --> H["agent/src/documents/*"]

图表来源

章节来源

核心组件 ​

本节聚焦四个关键模块及其职责边界。

组件位置主要职责输入输出
浏览器端文档抠图src/side_panel/doc-images.js从 docx/pdf/xlsx 中抽取图片、压缩、视觉转述、构建清单块、写入 session 快照File 对象、配置、可选依赖注入images 数组、truncated/skipped、dataUrl、预览文案
代理端 ZIP 读取器agent/src/documents/zip-reader.js零依赖解析 ZIP 中央目录,支持 stored/deflateUint8Array Buffer条目名列表、指定条目原始字节
代理端 docx 图片提取agent/src/documents/image-extractor.js按 document.xml r:embed 顺序抽取 PNG/JPEG/GIF/BMP/WebPdocx 字节缓冲images、truncated、skipped
代理端文档接口agent/src/documents/routes.js校验路径、限制大小、解析 zip、落盘 _attachments/<词干>/、写 manifest{ path, maxImages? }{ success, dir, images, truncated, skipped }

章节来源

架构总览 ​

文档图像识别的整体数据流分为三个阶段:附件上传阶段、会话快照阶段、工具深读阶段。

sequenceDiagram
participant User as "用户"
participant Panel as "侧边栏附件处理"
participant DocImg as "doc-images.js"
participant Storage as "chrome.storage.local"
participant BG as "background 工具执行器"
participant Vision as "视觉模型 API"
participant Agent as "Agent 文件服务"
User->>Panel : "上传 docx/pdf/xlsx"
Panel->>DocImg : "extractDocxImagesFromFile / extractPdfImagesFromFile / extractXlsxImagesFromFile"
DocImg-->>Panel : "{ images, truncated, skipped }"
Panel->>DocImg : "describeImages(前 N 张)"
DocImg->>Vision : "chat/completions(图片+OCR提示)"
Vision-->>DocImg : "描述文本或空"
Panel->>Storage : "saveDocImageSnapshot(sessionId, attachedFiles)"
Note over Panel,Storage : "写入 _docImageSnapshot,含 dataUrl 或 path"
BG->>BG : "executeTool(read_image)"
BG->>Storage : "读取 _docImageSnapshot[sessionId]"
alt "本地来源 dataUrl 存在"
BG->>Vision : "压缩后发送图片深读请求"
Vision-->>BG : "返回分析文本"
else "Agent 来源 path 存在"
BG->>Agent : "downloadFile(path)"
Agent-->>BG : "{ content(base64), mimeType }"
BG->>Vision : "安全校验后发送深读请求"
Vision-->>BG : "返回分析文本"
end
BG-->>User : "工具结果:成功文本或错误文案"

图表来源

章节来源

详细组件分析 ​

浏览器端文档图片抽取与视觉转述 ​

src/side_panel/doc-images.js 是扩展侧的核心模块,承担三类文档的图片抽取与视觉链路。

设计要点 ​

  • 开关统一复用 state.enableImageInput,不新增配置项。
  • 尺寸解析算法与 Agent 端 image-extractor.js 同步,避免两端编号不一致。
  • docx 使用 JSZip 解析 word/media/*,按 document.xml 中 r:embed 出现顺序编号。
  • xlsx 解析 workbook→rels→sheet→drawing→media,记录 sheet 名与单元格坐标。
  • pdf 在浏览器端用 pdfjs-dist,优先抓位图,无位图且文本稀少时整页渲染作为扫描件兜底。
  • 单图压缩采用 5 轮降级:先降 quality,再缩尺寸,仍超则放弃实体但保留清单元信息。
  • 视觉配置走 imageApiBase/imageApiKey/imageModelName,留空回落基础模型配置。
flowchart TD
Start(["开始"]) --> Type{"文档类型"}
Type --> |docx| Docx["JSZip 解析 word/document.xml 与 rels"]
Type --> |xlsx| Xlsx["workbook→rels→sheet→drawing→media"]
Type --> |pdf| Pdf["pdfjs-dist 抓取 paintImageXObject"]
Docx --> Extract["构造 images 数组<br/>index/page/mimeType/bytes/width/height/dataUrl/kind"]
Xlsx --> Extract
Pdf --> ScanCheck{"是否扫描页候选?"}
ScanCheck --> |是| RenderPage["OffscreenCanvas 整页渲染 JPEG"]
ScanCheck --> |否| SkipRender["跳过整页渲染"]
RenderPage --> Extract
SkipRender --> Extract
Extract --> Compress["compressForVision 五轮降级"]
Compress --> Describe["describeImages 并发转述前 N 张"]
Describe --> Snapshot["saveDocImageSnapshot 写入 _docImageSnapshot"]
Snapshot --> End(["结束"])

图表来源

关键函数说明 ​

  • extractDocxImagesFromFile(file, opts):浏览器端 docx 抠图,返回 images、truncated、skipped。
  • extractXlsxImagesFromFile(file, opts):浏览器端 xlsx 浮动图片抠图,带 sheet 与 cell 定位。
  • extractPdfImagesFromFile(file, opts, deps):浏览器端 pdf 抠图,支持位图去重与扫描页渲染。
  • compressForVision(dataUrl, deps):将 dataUrl 压缩到视觉上限,失败返回 null。
  • describeImages(images, count, onEach):并发调用视觉模型,返回 index→description 映射。
  • buildImageBlock(fileEntry, fileName):拼装消息内联图片清单块。
  • saveDocImageSnapshot(sessionId, attachedFiles):写入 session 级图片快照,按预算淘汰旧会话。

章节来源

代理端 ZIP 读取器与 docx 图片提取 ​

代理端不引入 jszip,而是通过 node:zlib 实现最小 ZIP 读取器,仅支持 stored 与 deflate。

ZIP 读取流程 ​

flowchart TD
Input["Uint8Array Buffer"] --> EOCD["反向扫描 EOCD 签名"]
EOCD --> Central["遍历中央目录条目"]
Central --> Match{"名称匹配?"}
Match --> |否| Next["继续下一个条目"]
Match --> |是| LocalHeader["解析本地头 nameLen/extraLen"]
LocalHeader --> Method{"压缩方式"}
Method --> |stored| Raw["直接截取 compSize"]
Method --> |deflate| Inflate["inflateRawSync 解压"]
Raw --> Output["返回 Uint8Array"]
Inflate --> Output
Next --> Central

图表来源

docx 提取流程 ​

  • 读取 word/document.xml 与 word/_rels/document.xml.rels。
  • 建立 rId→media 路径映射。
  • 按 r:embed 出现顺序收集唯一 media 路径。
  • 对 PNG/JPEG/GIF/BMP/WebP 进行头部尺寸解析,超过单图字节上限跳过。
  • 返回 images、truncated、skipped。

章节来源

代理端文档接口 /api/documents/extract-images ​

该接口用于 Agent 模式下接收 docx 文件并落盘图片。

flowchart TD
Req["POST /api/documents/extract-images"] --> Validate["校验 path 与扩展名 .docx"]
Validate --> Security["checkPath 安全检查"]
Security --> Exists{"文件存在且为文件?"}
Exists --> |否| NotFound["返回 notFound"]
Exists --> Size{"文件大小 ≤ 50MB?"}
Size --> |否| TooLarge["返回 fileTooLarge"]
Size --> Read["读取文件字节"]
Read --> ZipCheck["listEntryNames 预校验 zip 完整性"]
ZipCheck --> Extract["extractDocxImages(buf, opts)"]
Extract --> WriteDir["删除并重建 _attachments/<词干>/"]
WriteDir --> WriteFiles["写入 img-{index}.{ext}"]
WriteFiles --> Manifest["写入 manifest.json"]
Manifest --> Success["返回 { success, dir, images, truncated, skipped }"]

图表来源

章节来源

内置工具 read_image(原 read_document_image) ​

工具定义与执行逻辑位于 background 工具体系,测试用例明确其行为契约。

工具行为 ​

  • 参数二选一:{ file, image } 或 { path }。
  • 本地来源:从 _docImageSnapshot 中读取 dataUrl。
  • Agent 来源:通过 local-agent-client.downloadFile(path) 拉取 base64。
  • 安全校验:magic bytes 与单图体积上限必须通过。
  • 视觉调用:复用现有视觉配置链路与压缩逻辑。
  • 错误文案:包含文件名、可用附件、序号越界、缓存失效、Agent 离线、视觉未配置等。
sequenceDiagram
participant Tool as "tool-executor.executeTool"
participant Snap as "_docImageSnapshot"
participant DL as "local-agent-client.downloadFile"
participant Safe as "安全校验"
participant Vision as "视觉模型 API"
Tool->>Snap : "查找 sessionId 下 files"
alt "file+image 模式"
Tool->>Snap : "定位 {name, images[index]}"
alt "dataUrl 存在"
Tool->>Safe : "校验 dataUrl 大小与类型"
else "path 存在"
Tool->>DL : "downloadFile(path)"
DL-->>Tool : "{ success, content, mimeType }"
Tool->>Safe : "校验 base64 内容"
end
else "path 模式"
Tool->>DL : "downloadFile(path)"
DL-->>Tool : "{ success, content, mimeType }"
Tool->>Safe : "校验 base64 内容"
end
Safe-->>Tool : "通过 → 压缩/直传"
Tool->>Vision : "chat/completions(图片+问题)"
Vision-->>Tool : "分析文本"
Tool-->>Tool : "返回 { success, content }"

图表来源

章节来源

依赖关系分析 ​

文档图像识别涉及扩展侧与代理侧的多层依赖。

graph LR
DPkg["扩展 package.json"] --> JSZip["jszip"]
DPkg --> PDFJS["pdfjs-dist"]
DPkg --> Mammoth["mammoth"]
APkg["agent package.json"] --> Zlib["node:zlib"]
APkg --> WS["ws"]
APkg --> Archiver["archiver"]
APkg --> Zod["zod"]
SidePanel["src/side_panel/doc-images.js"] --> DPkg
AgentRoutes["agent/src/documents/routes.js"] --> ImageExt["agent/src/documents/image-extractor.js"]
ImageExt --> ZipReader["agent/src/documents/zip-reader.js"]

图表来源

章节来源

性能与资源控制 ​

系统在多个环节设置了阈值与降级策略,防止大文档与大图拖垮浏览器或外部视觉服务。

控制点阈值或策略目的
单文档图片数量MAX_IMAGES = 20限制单文档抠图上限,超出记 truncated
单图原始字节MAX_RAW_BYTES = 5MB跳过过大图片,避免内存爆炸
单图视觉上限MAX_IMAGE_BYTES = 200KB压缩后送视觉模型,降低带宽与 Token
视觉转述并发前 DESCRIBE_LIMIT = 2 张并发缩短最坏等待时间
视觉请求超时60s AbortController防止外部 API 挂起
快照预算SNAPSHOT_BUDGET = 4MB淘汰旧会话 dataUrl,保当前会话
Agent 文件上限EXTRACT_MAX_FILE_BYTES = 50MB拒绝超大 docx
PDF 扫描页阈值SCANNED_PAGE_TEXT_THRESHOLD = 50文本稀少才整页渲染,减少无效渲染

章节来源

故障排查指南 ​

以下是文档图像识别常见问题的定位思路与对应实现依据。

现象可能原因排查建议相关实现
上传 docx 后无图片ZIP 损坏或无 word/document.xml检查代理端 parseFailed 错误agent/src/documents/routes.js#L34-L43
PDF 图片为空位图不可解析或渲染失败查看 logger.warn 日志src/side_panel/doc-images.js#L373-L400
视觉转述失败未配置 imageApiBase/imageApiKey检查存储配置src/side_panel/doc-images.js#L439-L446
深读工具报“缓存已失效”跨会话或快照被清理重新附加文件test/unit/tool-read-document-image.unit.test.js#L162-L166
Agent 模式无法取图Agent 离线或 downloadFile 失败检查 Agent 连接状态test/unit/tool-read-document-image.unit.test.js#L127-L133
图片过大被拒绝超过单图上限或压缩失败检查 magic bytes 与体积闸门test/unit/tool-read-document-image.unit.test.js#L137-L147
xlsx 图片顺序异常drawing anchor 顺序或 rels 绝对路径检查测试 fixturetest/unit/doc-images-extract.unit.test.js#L84-L167

章节来源

结论 ​

文档图像识别系统以“浏览器端抽取 + Agent 端落盘 + background 工具深读”的分层架构实现,既满足主模型纯文本消费约束,又覆盖 docx、xlsx、pdf 三类常见办公文档。其关键保障包括:

  • 双端同步的 docx 编号算法与 PNG/JPEG 尺寸解析。
  • 浏览器端 pdf.js 统一处理,避免 Agent 端 canvas 依赖问题。
  • 多层阈值控制:图片数量、原始大小、视觉大小、快照预算、Agent 文件大小。
  • 工具层严格的安全校验:magic bytes、体积上限、参数二选一、视觉配置前置。
  • 完善的测试覆盖:浏览器端抽取单测与工具行为单测共同保证回归稳定性。

对于维护者而言,修改 docx 编号或尺寸解析需同时更新扩展侧与代理侧;修改视觉配置或工具参数需同步更新测试用例;Agent 端源码变更后必须重启进程,新接口才生效。