# ai-drawing **Repository Path**: JasonOblivion/ai-drawing ## Basic Information - **Project Name**: ai-drawing - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-10-16 - **Last Updated**: 2026-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 图纸解析 · 采购 BOM 生成系统 工程图纸(TIF / GeoTIFF / JPG / PNG / PDF)与物料台账(Excel)→ 自动生成采购 BOM。 ## 信息架构 系统以 **BOM 项目**为顶层容器,支持多个 BOM 并存、互不干扰: ``` BOM 列表(历史记录) └── 打开某个 BOM ├── 概览 汇总面板:关键指标 · 数量完整度 · 来源分布 · 文件明细 · 生成信息 ├── 工作流 ① 上传与预览 → ② 解析与 OCR → ③ 生成 BOM → ④ 预览与下载 └── 智能问答 预设问答智能体:BOM 数据检索 · 原图核对 · 统计计算 · 联网检索 · MCP 工具 ``` 生成的 BOM 条目支持**回溯到原图**:每条物料记录了它在原文件中对应的切分图块,表格「依据」列直接显示图块缩略图,点击可放大与 OCR 文本对照核验。 **进度实时推送**:解析、OCR 逐块进度与 BOM 生成阶段由服务端通过 SSE 主动下发,前端不再依赖高频轮询(连接不可用时自动降级为轮询,界面右上角有「实时 / 轮询」标识)。 **版本管理**:每次生成都会归档一份快照(最多保留 10 个),可查看历史版本、对比条目、一键回滚;当前生效版本在列表中标记。 每个 BOM 独立管理自己的文件、识别结果与生成结果。典型用法是**一个客户项目对应一个 BOM**(该项目的图纸 + 该项目的台账一起丢进去)。 > 服务端配置(MinerU 地址、模型、API Key)不对外暴露,只保留在 `server/.env` 与 `server/data/config.json`,界面不展示、接口不下发。 --- ## 目录结构 ``` F:\work\ai-drawing\ ├── specs/ # 设计文档与高保真原型(方案基线) │ └── reference/ # MinerU OpenAPI 原始规范 ├── server/ # 后端 Node.js 22 + TypeScript + NestJS │ └── scripts/ # 辅助脚本(样本生成、MinerU 协议验证) ├── web/ # 前端 Vite + React 18 + TypeScript + Ant Design 5 ├── samples/ # 测试样本(自动生成) └── drawing-bom/ # 早期 MVP,仅作参照,不参与构建 ``` --- ## 快速启动 ```bash # 1. 后端依赖 cd server && npm install # 2. 前端依赖 cd ../web && npm install # 3. 启动后端(端口 5185) cd ../server && npm run dev # 4. 启动前端(端口 5186,另开一个终端) cd ../web && npm run dev ``` 后端 `npm run dev` 使用 Node.js 22 内置监听模式,修改已加载的后端源码后会自动重启,并保留控制台日志。Windows 双击 `start-dev.bat` 启动同样生效;首次切换需停止旧后端,再运行一次 `npm run dev`。修改 `.env` 或 `tsconfig.json` 后仍需手动重启。 浏览器访问 **http://127.0.0.1:5186**。 前端通过 Vite 代理 `/api` 到后端,无需额外配置跨域。 --- ## 环境依赖 | 依赖 | 地址 / 版本 | 说明 | |---|---|---| | MinerU OCR | `http://124.221.125.248:18800` | 自部署,v1 异步任务 API(v4.0.0) | | 大模型 | `https://api.deepseek.com/v1` | DeepSeek 官方 API,模型 `deepseek-flash` | | API Key | `server/.env` 的 `LLM_API_KEY` | 不入版本库、不回传前端明文 | | 联网检索(可选) | `https://api.bochaai.com/v1` | 博查 Web Search,供智能问答联网用;未配置 Key 时联网工具自动停用 | 配置可在界面 ③「服务设置」中在线修改,保存在 `server/data/config.json`。 优先级:**界面配置 > .env(种子值) > 内置默认值**。 --- ## 使用流程 0. **列表页**:查看已有 BOM(文件数 / 已识别数 / 条目数 / 状态),新建、重命名、删除。 1. **① 上传与预览**:拖拽上传一个或多个文件,上传后立即生成缩略图(Excel 渲染为表格)。 2. **② 解析与 OCR**:文件任务的右上角**只放当前该做的那一个主动作**(按状态推荐:未切分 →「切分并识别」;已切分未识别 →「识别文字」;全部完成 →「全部重新识别」),次按钮为「只切分」「手动框选」「补识别」,按钮悬浮均有影响范围提示("会清空识别结果"这类副作用直接写在按钮上)。切分前弹出参数窗,可**选切分模式与块尺寸,并当场看到这张图会被切成什么样**。 - **切分模式**(应对不同图纸与不同区块类型): | 模式 | 说明 | |---|---| | **智能补偿**(默认) | 先分析页面内容分布,把切割线挪到空白处,避免把整块标注/表格劈开;某处内容密布无处可让时退回等分位置 | | 均匀网格 | 固定步长等分,速度最快,但可能正好切在区块中间 | | 横向条带 | 只沿水平方向切,每块保留整幅宽度;适合横向排布的明细表、连续标注带 | | 纵向条带 | 只沿垂直方向切,每块保留整幅高度;适合纵向长的表格与图框 | - **每个模式都带真实效果预览**:参数窗里 4 张卡片各内嵌一张「红虚线切割线 + 浅蓝图块底色」叠加在**当前这张图纸**上的预览图,随块尺寸实时更新,并显示「预计 N 块」。文字说不清的差别,看图秒懂。点「各模式的区别与效果示例」可打开详解弹窗:每个模式配**原理示意图 + 本图纸真实预览 + 适合场景 / 优点 / 注意点**。 示例截图见 `samples/split-modes-example/`(同一张图纸在四种模式下的实际切分效果)。 - **块尺寸档位**:精细 768 / 较细 1024 / 均衡 1536 / 默认 2048 / 整张 3072,也可自定义像素与重叠量。块越小单块文字越大、识别越准;块越大越不容易把明细表切断。 - **参数会被记住**:模式与块尺寸存本地,同一批图纸第二次切分不必重选。 - 逐块调用 MinerU 识别,每块完成即落盘(服务重启可断点续传)。支持「重新识别」强制重跑。 - **文件任务列表**:每行前置**文件类型图标**(工程图纸位图 / 图片 / PDF / Excel 分色),支持**按文件名搜索**、按状态(全部 / 待处理 / 进行中 / 已完成 / 有失败,各带数量)与类型过滤,并显示匹配计数。 - **查看大图**:点击任意切分图块即可放大查看(支持滚轮缩放),识别结果区提供「图 + 文本」左右对照;① 上传与预览步骤的缩略图同样可点开**大图浮层**(整页高清图,支持滚轮缩放 / 适应窗口 / 100% / 多页翻页),与框选共用同一套缩放实现。 - **细分补识别**:对「无文字」或识别失败的块,可一键**补识别**——把块按内容簇切成小区域重识别(原图是「大片留白 + 孤立小字」时,MinerU 会整块判为图片而不跑 OCR,切小后文字占比升高即能命中)。单块、按筛选批量、整个文件三个层级都有入口。 - **整页全文**:识别结果区可切「当前块 / 整页全文」,后者把该页所有块按坐标顺序拼接,**无文字的块也列出并注明**,便于通读核对"到底漏了什么"。 - **切片管理**:图块网格支持 **5 维筛选**(全部 / 无文字 / 已识别 / 失败 / 框选块)、**关键字搜索**(匹配标注、块标识与识别文本)与**排序**(位置 / 文本量),顶部显示「当前筛选 X / Y 块」;每个块可**重命名**(编辑标注),网格角标优先显示标注名。 - **手动框选**:个别区块仍被切断(或模式补偿不到)时,点「手动框选」在整页大图上拖拽出一个矩形,即可把它作为独立的解析图块(可选创建后立即识别);**不要求先切分**,上传后可直接框。框选块带「框选」角标,可单独删除。视口支持**滚轮缩放(以光标为锚点)/ 按钮缩放 / 适应窗口 / 100%**,以及**空格或中键拖动平移**,另有「平移」工具与「整页」快捷框选;底部实时显示「选区 N×N px @原图坐标」与当前显示比例。 3. **③ BOM 生成**:两阶段流水线——阶段一逐文件抽取物料条目,阶段二跨文件合并去重。 4. **④ 预览与下载**:在线预览 BOM 表格、编辑/删除/追加条目,导出 xlsx / csv / json。 ### 配置调整 界面不提供配置入口。需要改 MinerU 地址、模型或切分参数时,直接编辑 `server/.env` 与 `server/data/config.json` 后重启后端。 --- ## 智能问答助手 每个**已生成 BOM** 的项目都带一个预设问答智能体,就本项目的 BOM 结果作答。入口在 BOM 详情页右上角「智能问答」。 ### 预设智能体的硬性工作准则 写在 System Prompt 里,实测有效: 1. **数据必须落地** —— 凡涉及本项目的物料数据,必须先调用工具查真实数据再回答,不许凭上下文摘要或印象作答。 2. **数量绝不编造** —— 数量为空必须如实说明「原图未标注」;用户明确要求估算时才给,且必须标注为估算值并说明依据。 3. **外部信息标注来源** —— 联网获得的标准、材料性能、行情必须在末尾列出链接;检索不到就直说检索不到。 4. **主动补位** —— 发现数量大面积缺失、同物异名、规格含糊等异常主动指出。 ### 工具集 | 来源 | 工具 | 用途 | |---|---|---| | 内置 | `bom_stats` | 项目全景统计(文件、图块、条目、数量完整度、类别与来源分布) | | 内置 | `bom_search_items` | 按关键词/类别/材质/来源检索条目,支持「只查数量缺失」 | | 内置 | `bom_get_item` | 取单条物料详情 + 其原图依据 | | 内置 | `bom_read_source` | 读取图块 OCR 原文,用于核对图纸原始内容 | | 内置 | `bom_list_documents` | 文件清单与各自贡献的条目数 | | 内置 | `calc` | 安全算术求值(数量求和、面积、重量、金额),只放行四则运算 | | 内置 | `web_search` / `web_fetch` | 博查联网检索 / 抓取网页正文(需配置 Key,未配置则不暴露) | | MCP | `fs__*` | 读取已授权目录中的原始资料(**只读**,写操作已屏蔽) | | MCP | `memory__*` | 知识图谱式长期记忆,记住项目约定与术语习惯 | | MCP | `think__*` | 结构化多步推理(复杂报价拆解用) | ### 界面能力 - 回答**流式输出**,思考链(`reasoning_content`)可折叠查看 - **工具调用过程可视化**:每次调用显示工具名、结果摘要、耗时与成败状态 - **引用可点**:BOM 条目(悬浮看规格与数量)、原图切块缩略图(点击看大图)、网页来源、相关文件 - 会话按 BOM 持久化(`data/boms/.chat.json`),可清空;追问自动带上历史上下文 ### 启用联网 DeepSeek 官方 API 本身不联网,联网能力由**博查 Web Search API** 提供。到 申请 Key 后填入: ```bash # server/.env BOCHA_API_KEY=你的Key ``` 重启后端即生效,无需改代码。未配置时工具集里不会出现联网工具,助手会明确告知「无法联网」而不是用模型记忆冒充实时行情。 ### MCP 扩展 默认挂载三个**官方仍在维护**的参考实现(`filesystem` / `memory` / `sequential-thinking`),由后端自动以 stdio 方式拉起,无需额外部署。配置在 `server/data/chat.json` 的 `mcp.servers`,支持增删与启停: ```json { "id": "filesystem", "label": "本地文件", "enabled": true, "command": "cmd.exe", "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "F:\\path\\to\\dirs"] } ``` - `filesystem` 的**允许目录**通过环境变量 `CHAT_MCP_ALLOWED_DIRS` 设置(分号分隔),默认仅项目 `samples/` 与 `server/data/`。 - `filesystem` 的写类工具(`write_file` / `edit_file` / `move_file` / `create_directory`)**已在代码中收敛掉**,问答助手只能读、不能改本地文件。 - 界面「查看能力」里可看到每个 Server 的连接状态与工具清单,支持一键重连。 --- ## 后端主要接口 **BOM 项目** | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/boms` | 项目列表(含文件数 / 已识别数 / 条目数 / 状态) | | POST | `/api/boms` | 新建项目 `{ name? }` | | GET | `/api/boms/:id` | 项目详情(任务状态 + 文件列表,轮询用,不含结果) | | PATCH | `/api/boms/:id` | 重命名 | | DELETE | `/api/boms/:id` | 删除(软删,文件解除关联但保留在磁盘) | | POST | `/api/boms/:id/generate` | 生成 BOM(用本项目内的文件) | | GET | `/api/boms/:id/result` | 生成结果(含每条物料的 `sources` 依据标识) | | GET | `/api/boms/:id/stats` | 概览统计(文件/图块/条目/来源与类别分布) | | GET | `/api/boms/:id/events` | **实时进度(SSE)**,事件 `hello` / `doc`(文档状态与切块进度)/ `job`(生成阶段) | | GET | `/api/boms/:id/versions` | 版本列表(含 `isCurrent` 标记) | | GET | `/api/boms/:id/versions/:vid` | 版本完整快照(含全部条目) | | POST | `/api/boms/:id/versions/:vid/restore` | 回滚:把该版本恢复为当前生效结果 | | DELETE | `/api/boms/:id/versions/:vid` | 删除某个历史版本 | | GET | `/api/boms/:id/export?format=xlsx\|csv\|json&sources=1\|0` | 导出;`sources` 默认 **1**=附带来源(xlsx 增「来源文件 / 依据位置」两列 + 第二张工作表「依据明细」;csv 同样两列;json 增 `sourcesText`),传 `0` 时与旧格式一致 | | POST | `/api/boms/:id/items` | 追加条目 | | PUT / DELETE | `/api/boms/:id/items/:itemId` | 编辑 / 删除条目 | **文档** | 方法 | 路径 | 说明 | |---|---|---| | POST | `/api/upload?bomId=` | 多文件上传到指定项目(multipart,字段 `files`) | | GET | `/api/documents?bomId=` | 项目内文件列表(含进度) | | GET | `/api/documents/:id` | 文件详情(含图块与 OCR 文本) | | POST | `/api/documents/:id/parse` \| `/ocr` \| `/process` | 切分 / OCR / 二者串行(`/ocr?force=1` 强制重跑);`parse`/`process` 可传 `{mode, tileSize, overlap}` 覆盖本次切分参数 | | POST | `/api/documents/process-all?bomId=` | 项目内全部文件的解析 + OCR;body 可传 `{mode, tileSize, overlap}` 让整批套用同一套切分参数 | | GET | `/api/documents/:id/split-preview?mode&tileSize&overlap&page&width` | **切分预览图**:把该模式下的切割线叠加在整页图上返回 PNG(响应头带 `X-Split-Tiles` 等元信息) | | GET | `/api/documents/:id/pages/:page` | 页面元信息(真实像素尺寸 + 显示图地址),框选坐标换算用 | | GET | `/api/documents/:id/pages/:page/image` | 整页大图(PNG,首次生成后缓存) | | POST | `/api/documents/:id/custom-tiles` | **手动框选**:按归一化比例(0~1)裁出一块作为新解析图块 | | DELETE | `/api/documents/:id/segments/:sid` | 删除图块(含框选创建的块,同时清理磁盘图片) | | PUT | `/api/documents/:id/segments/:sid/text` | 人工修正图块文本 | | PATCH | `/api/documents/:id/segments/:sid` | 修改图块的**人工标注** `label`(传空串则清除),用于给块起名(如"明细表"/"标题栏")后筛选定位 | | POST | `/api/documents/:id/enhance` | **细分补识别**:只对文本过短(< `OCR_ENHANCE_MIN_CHARS`,默认 20)或失败的块,按内容簇切小后重新识别;结果更长才替换并标 `enhanced`(配置 `OCR_ENHANCE=0` 可关闭) | | GET | `/api/documents/:id/preview` \| `/original` | 预览图 / 原文件 | | GET | `/api/documents/:id/segments/:sid/image` | 图块图片 | | GET | `/api/documents/:id/table` | Excel 转 HTML 预览 | **系统信息(脱敏)** | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/settings` | 只返回 `mineruConfigured` / `llmConfigured` 与切分参数,**不含服务地址、模型名与密钥** | | POST | `/api/settings/test-mineru` \| `/test-llm` | 连通性自检 | **智能问答** | 方法 | 路径 | 说明 | |---|---|---| | POST | `/api/boms/:id/chat` | 流式问答(SSE over POST)。事件 `meta`/`reasoning`/`delta`/`tool_call`/`tool_result`/`refs`/`done`/`error` | | GET | `/api/boms/:id/chat` | 会话历史 | | DELETE | `/api/boms/:id/chat` | 清空会话 | | GET | `/api/boms/:id/chat/status` | 运行状态:MCP 连接情况、联网可用性、可用工具、快捷提问 | | GET | `/api/chat/settings` | 助手配置(密钥掩码) | | PUT | `/api/chat/settings` | 更新助手配置(白名单字段) | | GET | `/api/chat/mcp` | MCP Server 状态与工具清单 | | POST | `/api/chat/mcp/reload` | 重连 MCP | | POST | `/api/documents/:id/enhance` | 细分补识别(只动文本过短/失败的块) | | PATCH | `/api/documents/:id/segments/:sid` | 修改图块标注 / 文本 / **有效性**(`valid` / `resetValid`) | | PATCH | `/api/documents/:id/segments` | **批量**启用 / 停用区块(无效块管理) | | POST | `/api/documents/:id/segments/revalidate` | **扫描无效块**:对已切好的区块就地重判有效性(补标记,不重切、不动识别结果) | | POST | `/api/documents/:id/segments/restructure` | **切块重构**(切块设计器):用给定新矩形替换掉给定旧块 → 合并 / 拆分。先建后删,失败不留半成品 | 完整契约见 `specs/03-接口契约.md`。 --- ## V1.1 变更(2026-10-16) > 完整记录(根因、实测数据、验收口径)见 `specs/V1.1优化建议/`。 **功能** | 变更 | 说明 | |---|---| | 手动框选前置化 | 不再要求"先切分":上传后即可在整页大图上框选成块,框完自动把文件状态推进为 `parsed` | | 导出附带来源 | 默认输出「来源文件 / 依据位置」两列,xlsx 另加第二张工作表「依据明细」;`sources=0` 回到旧格式 | | 细分补识别 | 对短块/失败块按内容簇切小重识别(`POST /documents/:id/enhance`)。`Bezel_T1.tif` 无文字块 **7 → 3**、全文 **+263%** | | 视觉直读 | BOM 抽取请求由「纯文本」变为「文本 + 图片」(整页显示图 ≤3600px + 内容最密 4 块,上限 5 张)。`延锋geely` 平均规格型号长度 **15.4 → 30.5(+98%)** | | 大图预览 | 上传步骤缩略图可点开大图浮层,与框选共用 `ImageViewer` | | 切片管理 | 标注编辑(`PATCH .../segments/:sid`)+ 5 维筛选 / 搜索 / 排序 | | 按钮语义重构 | 「仅切分 / 仅 OCR / 重新识别 / 解析 + OCR」→「**切分并识别 / 只切分 / 识别文字 / 全部重新识别**」,主动作按状态推荐并带影响范围提示 | | 筛选栏布局 | 文件任务面板改为「搜索 → 状态分片 → 类型下拉 + 匹配计数」三段式,窄面板不再重叠 | | **切分模式语义修正** | 表格保护从「整页放弃纵切」改为**按表格带局部生效**:判据换成「宽横线簇」;`smart` 保留二维切分、`grid` 彻底不参与保护。实测四模式互不相同(2048px:smart 16 / grid 15 / row 4 / col 5) | | **区块有效性** | 切分**永远保证整页 100% 覆盖、一块内容都不丢**;空白块由系统标为**无效**(默认不参与识别),可单块或批量放回来。存量数据用「扫描无效块」就地补标记(不重切、不动识别结果) | | **切块设计器** | 把区块按原图坐标铺在原图上(顺序即原图顺序):勾选多块**合并**为一块、选中一块后**拉切割线或按预设等分拆分**;合并继承既有识别文本,拆分后子块待识别 | **切分质量提示**:切分后落盘 `splitInfo`,`穿越比 = 切割线处平均内容密度 / 全图平均密度`,超 **0.8** 时提示并给「改用横向条带」一键切换。实测(客户输入.tif,2048px):均匀网格 **1.01**(超阈)/ 智能补偿 **0.31** / 横向条带 **0.19**。 **界面**:结构按 UI 设计 v2(无侧栏、顶栏 + 内容居中 **1240px**、细线步骤条、**数据摘要条**替代成排 KPI 卡);视觉按 v3(品牌色阶唯一主色 `#0E5CE6`、页面光晕、图标底板、顶部渐变发丝线)。旧 antd 默认色系已全部收口到 v3 语义色板。 **新增配置项**(`server/.env`) | 配置 | 默认 | 说明 | |---|---|---| | `OCR_ENHANCE` | 开 | 关闭细分补识别设 `0` | | `OCR_ENHANCE_MIN_CHARS` | `20` | 判定"文本过短"的字符数阈值 | | `LLM_VISION` | 开 | 关闭视觉直读设 `0`(BOM 抽取退回纯文本通道) | | `SPLIT_MODE` | `smart` | 默认切分模式(`smart`/`grid`/`row`/`col`) | --- ## 关键设计约束 1. **BOM 数量列严禁编造** —— 原文无明确依据时必须留空,界面上标记为「待补」。此约束写在 System Prompt 中,实测有效。 2. **OCR 逐块完成即落盘** —— 服务重启后跳过已完成块、只重试失败块。 3. **大模型返回强制 JSON** —— 容错链路:`response_format` 降级 → 剥离 ```json 代码块 → 截取首尾大括号。 4. **切分保留重叠区** —— 默认 128px,防止切断文字行。 5. **剥离 MinerU 内嵌 base64 图片** —— 纯图形区域会被渲染成 data URI(markdown 与 HTML 两种形态),单块可达上百 KB,必须清洗后再送模型。 6. **Excel 按实际内容裁剪** —— 不能信任 `!ref`:实测某报价模板声明到 `XEK34`(34 行 × 16365 列),直接展开会产出 176 万字符垃圾文本。 7. **全局 OCR 并发收口** —— 多文件同时处理时共享一个并发闸门,避免 N × concurrency 个请求压垮 MinerU。 8. **超长高噪音文档必须用硬约束** —— 面对 9.8 万字符的供应商指南(14 个 Sheet 里仅 1 个含物料),模型会跑偏输出自然语言分析。System Prompt 强制「首字符必须是 `{`、禁止任何分析、无物料则输出 `{"items":[]}`」,且解析失败时用更强约束自动重试一次。 9. **每条物料必须可追溯到原图** —— 抽取时给每个图块前置全局唯一标识 `docId:segId`,模型回填 `sources`,服务端校验标识真实存在后保留(防编造),前端据此回显切分小图作为依据。 10. **问答必须走工具、不许凭印象** —— 智能问答的数据来源只能是工具返回值;且 MCP 文件工具只读(写类工具已屏蔽),保证助手不会改动本地文件。 11. **物料类别必须归类** —— `category` 限定在 9 个标准类别内(结构件 / 金属件 / 电子电气 / 紧固件 / 材料原料 / 包装材料 / 表面处理与辅料 / 外购总成 / 其他),判不准就用「其他」,不允许自创类别名。 12. **进度事件在存储层统一发布** —— 状态变更必然落盘,因此在 `writeMeta` / `writeBomProject` 处统一广播,天然覆盖上传、切分、OCR 逐块、生成各阶段,既不漏发也不侵入业务代码。 13. **服务启动必须收敛僵死状态** —— 进程被强杀时正在执行的任务不会走 `finally`,磁盘上会留下 `parsing` / `ocr_running` / 图块 `running` 中间态,表现为界面永远显示「进行中」且无法再次触发。启动时统一重置为可继续状态。 14. **新增存储一律独立目录** —— 版本快照放 `data/versions/`、问答会话放 `data/chat/`,都不与 `data/boms/` 混放(该目录按 `*.json` 扫描项目,混入同类文件会被误读为项目)。 15. **框选区域用「归一化比例」传递** —— 前端在缩放后的大图上框选,提交 0~1 的比例而非像素,由后端按切块坐标系换算。这样显示缩放、甚至窗口尺寸变化都不会影响裁出的实际区域(避免前后端分辨率不一致的经典坑)。 16. **Excel 切块没有截图** —— 依据指向 `sheet_N`(Excel 工作表)时,图片接口必定 404,前端必须走「表格内容」分支而**不能**去渲染图片;其余 tile 类型才请求截图。 17. **切分补偿必须保证「不劣化」** —— 智能补偿只在「理论位置确实切进了内容」且「附近存在明显更空的位置」时才移动切割线;判定用双门槛:既要低于全图平均密度的 1/4(纵向可比),又要显著低于理论位置本身(横向可比)。**早期版本只按「窗口内相对最稀疏」选点,实测在内容密布的真实图纸上反而比固定网格差 117%** —— 因为密集区域里"相对最空"的位置绝对密度依然很高。 18. **投影分析不能只看绝对空白** —— 图纸通常有整圈图框,会把每一列/行的投影都垫高,纯「绝对空白」阈值几乎触发不了;必须结合全图平均密度做相对判断。采样宽度取 3000(精度与耗时的平衡点)。 19. **预览与实际切分必须共用同一套切割线算法** —— 切分预览图(`split-preview`)不是另画的示意图,而是把 `planCuts()` 在同一页上算出的切割线真实叠加到页面上,否则用户看到的会与实际结果不符,白做一遍对比。投影分析结果按 `docId:page` 做内存 LRU 缓存:4 种模式共用一个投影,第 2~4 次请求几乎零开销。 20. **预览底图用已缓存的显示图,不重新栅格化** —— PDF 全分辨率渲染(13594×9535)成本很高,预览用 ≤3600px 的显示图做底,投影分析也在该分辨率上进行,切割线会与实际结果有极小位置差(分析分辨率不同),但对"能不能看清切在哪"毫无影响。 21. **切分永远 100% 覆盖,无效判断必须可逆** —— 算法不做"跳过空白"这类会留下盲区的优化:空白区域照样切成块,只把它标为**无效**(`valid=false` + `validSource='auto'`),默认不参与 OCR 与 BOM 抽取,但用户随时能放回来(`validSource='user'` 优先于系统判定,重新切分不覆盖人工设定)。这样"系统觉得没用"的每一个判断都是可逆的。 22. **表格保护只在本带生效** —— 明细表占满幅宽时纵向切割线几何上必然落在表内,因此对**检测到的表格带**放弃纵切(整行完整落进同一块),其余区域照常二维切分。**判据必须有实证**:某行墨迹横跨样本宽度 ≥45% 才算「宽横线」,相邻宽横线按 4% 全高聚簇、簇内 ≥4 条才算表格带 —— 早先用「热行跨度」统计会把整页判成表格带(实测客户输入.tif 判成 **100%**,导致 smart/grid 双双退化成横向条带)。 23. **区块矩形不能用 `!x || !y` 判空** —— 坐标 `0` 是合法值(第一行 / 第一列),那个写法会把顶行区块当成"没有坐标"而静默跳过(设计器的拆分对顶行完全失灵,实测踩到)。 --- ## 辅助脚本 ```bash cd server # 生成测试样本(2 张 TIF 图纸 + 1 个 Excel 台账)到 ../samples node scripts/make-samples.mjs # 验证 MinerU v1 协议链路(上传 → 建任务 → 轮询 → 取结果) node scripts/mineru-protocol-test.mjs # 切分模式量化对比:统计各模式下「切割线处的内容穿越量」(越小越不切断区块) node scripts/compare-split.mjs "F:/path/to/drawing.tif" # 单文件解析冒烟测试:每个 BOM 项目只放一个文件,跑通「上传 → 切分 → 逐块 OCR → 生成 BOM」 # (多文件混在一个项目里时各项指标都是聚合值,会把单格式的问题盖住,所以刻意单文件单项目) API=http://127.0.0.1:5185/api node scripts/parse-smoke-test.mjs --preset # 也可指定文件 / 覆盖切分参数 / 只测识别不生成 BOM API=... node scripts/parse-smoke-test.mjs ../samples/结构图-G-02.tif --tile=1024 --mode=smart --no-llm ``` --- ## 数据目录 运行时数据在 `server/data/`: ``` data/ ├── boms/.json # BOM 项目(名称、关联文件、任务状态、生成结果) ├── versions/.json # 该项目的版本快照(最多 10 个) ├── chat/.json # 该项目的问答会话历史(独立目录,务必与项目文件分开) ├── files// │ ├── original. # 原始文件 │ ├── preview.png # 预览缩略图 │ ├── pages/.png # 整页大图缓存(供手动框选与浏览) │ ├── units/*.png # 切分图块(含手动框选的块) │ └── meta.json # 元数据(含所属 BOM、图块与 OCR 结果,逐块实时落盘) ├── config.json # 服务端运行配置(不对外暴露) ├── chat.json # 问答助手配置(智能体、联网、MCP Server 列表) └── chat-memory.json # MCP memory 的长期记忆知识图谱 ``` 删除均为软删除(`deleted: true`),磁盘数据保留;如需彻底清理请手动删除 `server/data/files/` 与 `server/data/boms/.json`。