Hana v2 App(App 形态) · 文档与图片内容提取工具
PDF / Word / PPT / Excel / HTML / 图片 → 结构化 Markdown
只讲用户需要知道的部分。工具名、参数、返回结构与 v1 完全一致,下面这些用法说明照用。
怎么装(v2 与 v1 不同)
- 把
doc-intake整个目录放到<HANA_HOME>/apps/下(v1 在<HANA_HOME>/plugins/)。 - 先停用 v1 插件
doc-intake。两个版本注册的工具名相同,同时启用会冲突。 - 重启 Hana,在市集「已安装」→ App 类目的「待批准」区块批准一次。首次批准会确认两项能力:
app/process.spawn(调用外部 Python)和app/tools.expose-to-model(工具进入模型循环)。 批准后可在设置窗「安全」→「应用能力」里单独开关。 - 设置要重填。v2 的设置与 v1 分开存储(v1 在
plugin-data/doc-intake/config.json, v2 在user/preferences.json的settings_contributions["v2-doc-intake"])。 至少填pythonPath,Token、降级链、保存路径按需补。 - 设置位置不变:设置左侧「Doc Intake」。
两处配置项写法变了
| 配置项 | v1 | v2 |
|---|---|---|
pdfBackendChain |
数组,如 ["mineru","local"] |
字符串,用 > 或 ; 分隔,如 mineru>paddleocr>local;留空 = 自动 |
savePath |
默认写死 D:\Agent |
留空 = 系统文档目录下的 doc-intake(按各自机器算,不写死盘符) |
和 v1 一样的地方:文件夹路径照旧自动展开、媒体提取、公式转 LaTeX、降级链、保存策略、
doc_intake 工具的名字与参数全部不变。
(v2 的 App 进程跑在 Node 权限模型下,读文件的工作已下放到被 spawn 的 Python 子进程完成,功能不受影响。)
和 v1 不一样的地方:v1 的 doc_intake_validate 工具已移除。Token 有效性检测改成设置页里每个
Token 字段旁的「检测」按钮,由插件自己发请求探测(纯 Node,不需要 Python 环境)。理由:验证凭证是
配置环节的一次性动作,不该占一个模型工具位。
office_read-document 是 Hana 内置的 Office 文档读取工具,适合快速读取 .docx、.xlsx、.pptx 等文件的纯文本内容。但它的能力边界很清晰:
| 能力 | office_read-document |
doc-intake |
|---|---|---|
| 纯文本提取(Word/Excel/PPT) | ✅ | ✅ |
| PDF 文本提取 | ✅(仅数字 PDF) | ✅(数字 PDF + 扫描件 OCR) |
| 扫描件 / 图片型 PDF OCR | ❌ | ✅ MinerU VLM |
| 图片文字识别(109+ 语言) | ❌ | ✅ PaddleOCR |
| 公式识别 → LaTeX | ❌ | ✅ OMML + MathType |
| 表格结构识别 | ❌ | ✅ MinerU / PaddleOCR |
| 文档内媒体提取(图片/视频/音频) | ❌ | ✅ 按类型分别编号 |
| EMF/WMF 矢量图 → PNG | ❌ | ✅ |
| MathType OLE → LaTeX | ❌ | ✅ MTEF 解析 |
| HTML 完整提取(元数据/链接/代码块) | 基础 | ✅ 结构化 |
| 长图智能分割 | ❌ | ✅ 空白行检测 |
| 大 PDF 自动切割 | ❌ | ✅ 按页数分块 |
| 多 Token 轮询 + 自动降级 | ❌ | ✅ |
| 批量并行处理 | ❌ | ✅ 双池并发 |
简单说:office_read-document 拿到的是"文字",doc-intake 拿到的是"内容"。如果你只需要读个 Word 纯文本,前者够用;如果涉及扫描件、公式、图片识别、媒体提取、批量处理,用 doc-intake。
doc-intake 是 Hana 的文档/图片内容提取插件,目标是把各种格式的文件转化为结构化 Markdown,同时把文档中的媒体(图片、视频、音频)提取到本地。
旧版 Word .doc、Excel .xls 和 PowerPoint .ppt 会先通过独立的旧格式转换层转为 .docx、.xlsx、.pptx,再复用现有 OOXML 提取器。Windows 默认使用 Microsoft Office COM;LibreOffice 只能通过显式配置作为替代 provider。转换结果会在 metadata 中保留原始格式、转换后格式、provider、状态和兼容性警告。
核心设计原则:
-
敏感配置隔离:MinerU / PaddleOCR 凭证通过一次性 stdin 管道传给 Python,不再注入
DOC_INTAKE_SETTINGS环境变量;旧环境变量仅保留给手动兼容调用 -
敏感错误脱敏:第三方异常、降级警告和日志会遮蔽已知完整 Token,避免凭证随错误文本回显
-
后端降级链:PDF 默认只走本地 PyMuPDF,避免无 Token 时隐式上传原件;用户可在设置中显式配置
MinerU → PaddleOCR → local等链,失败自动降级到下一档 -
多 Token 轮询:每个云端 API 支持配置多个 Token,单个失效自动切换下一个,不中断批处理
-
双池并发:云端 API 和本地解析分别走独立的并发池,互不抢资源
-
归一化输出:所有后端产出统一为 Markdown + 媒体文件,调用方不需要关心底层用的是哪个引擎
| 人群 | 典型场景 |
|---|---|
| 学术研究者 | 扫描版论文 PDF 提取、国标 GB/T 文件解析(Founder 方正字体乱码修复)、公式转 LaTeX、表格结构识别 |
| 技术文档维护者 | 批量提取 Word/PPT/Excel 内容转 Markdown 归档、提取文档内嵌图片和音视频 |
| Hana 插件/Skill 开发者 | 在自己的插件中调用 doc_intake 获取文档内容,无需重复造轮子 |
| 数据采集与知识管理 | 网页 HTML 完整提取(元数据/链接/代码块/远程媒体)、长截图文字识别 |
| 经常处理中文 PDF 的用户 | 中文 OCR(109+ 语言)、扫描件表格识别、公式识别,覆盖 MinerU / PaddleOCR 双引擎 |
如果你只是偶尔读个 Word 纯文本,Hana 内置的 office_read-document 够用,不需要装这个插件。
- Python 环境:在插件设置页的「基础项」填写
Python 可执行文件路径(conda 环境填<conda 目录>/envs/<环境名>/python.exe) - Python 依赖:
pip install -r python/requirements.txt依赖清单(以 python/requirements.txt 为准,与设置页「环境依赖管理」卡片一致):
必装:
Pillow— 图片处理:长图分割、EMF→PNG 转换、图片读写PyMuPDF— PDF 本地兜底提取,以及大 PDF 按页分块requests— 云端 API 调用、远程图片下载、HTML 抓取
可选:
mineru-open-sdk— MinerU 云端 PDF 提取;缺失则降级链跳过 MinerU 这一档html-to-markdown— HTML → Markdown 转换;缺失则 HTML 解析不可用pywin32— 旧版 .doc/.xls/.ppt 经 Office COM 转换(仅 Windows)xlrd— 旧版 .xls(BIFF)数值读取
v2 是 App 形态,Python 依赖同样装在你自己的环境里,不动系统 Python。
- 云端 Token(可选):
- MinerU Token:https://mineru.net/apiManage/token
- PaddleOCR Token:https://aistudio.baidu.com/account/accessToken
没有 Token 也能用:PDF 默认走本地 PyMuPDF,不会隐式调用云端;如需 MinerU/PaddleOCR,请在设置中显式配置后端链并提供对应凭证。图片 OCR 仍需要 PaddleOCR Token。
如果需要使用MinerU,并且常年挂着机场/梯子,建议将以下链接纳入直连规则:
- 'DOMAIN,aistudio.baidu.com,DIRECT'
- 'DOMAIN,www.paddleocr.ai,DIRECT'
- 'DOMAIN,cdn-mineru.openxlab.org.cn,DIRECT'
- 'DOMAIN,mineru.net,DIRECT'
- 'DOMAIN,mineru.org.cn,DIRECT'
- 'DOMAIN,sso.openxlab.org.cn,DIRECT'
设置页分三段,改动即时生效、无需重启:
- 基础项(默认展开):
pythonPath、默认解析后端、PDF 降级链、MinerU / PaddleOCR 两个 Token、默认语言、是否提取媒体、自动保存与保存路径。必填项和常用项都在这一层。 - 高级设置(默认收起,点标题展开):MinerU / PaddleOCR 细节、PDF 切割、图片分割、并发与重试、旧版 Office 转换、HTML 提取、Excel 行列上限、Agent 返回容量、日志。默认值都调过,不确定就别改。
- 环境依赖管理:用你填的
pythonPath逐项探测依赖装没装,列出「库名 / 功能 / 必装或可选 / 当前状态」。缺依赖时可以直接复制pip install命令,也可以点「一键安装缺失的必装依赖」就地安装(安装会改动你那个 Python 环境,页面内会先确认)。
检测是只读的:探测脚本只做导入判断与版本查询,不安装任何东西;只有你点了安装按钮,插件才会调用
pip install,且只允许安装依赖清单里登记过的包。
插件支持用户显式配置 MinerU → PaddleOCR → 本地 PyMuPDF 等 PDF 降级链,图片 OCR 只有 PaddleOCR 一档。要让扫描件和图片具备云端识别能力,需要配置对应 Token:
- MinerU Token:获取地址 → 填入插件设置面板的
mineruCredentials(多个用分号分隔) - PaddleOCR Token:获取地址 → 填入插件设置面板的
paddleTokens(多个用分号分隔)
⚠️ MinerU Token 有效期 90 天,到期后提取会失败并自动降级到 PaddleOCR / 本地档。建议日历提醒自己续期。
没有 Token 也能用:PDF 默认走本地 PyMuPDF,不会隐式调用 MinerU Flash;如需云端识别,请显式配置云端后端并提供对应 Token。图片 OCR 仍需要 PaddleOCR Token。
设置页里 MinerU 和 PaddleOCR 两个 Token 字段下面各有一个「检测」按钮。填好直接点,不用先保存; 结果逐行列出(第几个 · 掩码后的 Token · 结论),哪一把失效一眼能看到。
检测只回答「这把凭证能不能过鉴权」,不发真实解析任务,不占解析额度。
Hana 自带 office-documents skill,它的设计思路是「一个 skill 干所有事」——读、写、转换、格式化全包。结果是每个场景都只能做到「能用」级别:扫描件是空白、公式丢失、媒体不提取、批量无并发。
doc-intake 和 office-cli 是反过来的思路:把读和写拆成两个独立工具,各自做到专精。
office-documents |
doc-intake + office-cli |
|
|---|---|---|
| 读文档 | 能读,但不支持 OCR/公式/媒体 | doc-intake:扫描件 OCR、公式转 LaTeX、媒体提取全覆盖 |
| 写文档 | 能写,但格式控制粗糙 | office-cli:格式精确、排版可控 |
| 格式转换 | 基础转换 | office-cli:支持更多格式和高级选项 |
| 并发 | 无 | doc-intake:双池并发 + 多 Token 轮询 |
操作:在 Hana 的 Skills 管理界面中卸载 office-documents。卸载后 Agent 不会再优先调用它,文档请求会走 doc-intake。
- 推荐关闭HanaAgent自带的office-read-document工具(如果您已开启) 具体位置:设置-助手(然后一直往下划,找到“工具”)-关闭Office 此工具(Hana自带的office读取工具)会让agent优先调用内置工具读取,发现无法读取则会私自写py脚本,不调用doc-intake插件
- 推荐将助手切换为“无”,因为“无”会更听话一点。不管是平时让他干活,还是让他遵循本插件的skill表现都会更好一些
Hana平台对插件返回信息有字数限制,大于32k将被略去中间部分。所以推荐选择一个保存路径,保存到本地让agent自行读取。
注:2.0版本及以后插件已用特殊手段绕过了此限制,详情见[[#Agent 返回机制说明]](可通过更改插件中的“返回文本块数量上限”与“每个返回文本块的最大大小”提高返回内容上限。“返回文本块数量上限=8”+“每个返回文本块的最大大小=28k”已能满足绝大多数需求)。但依旧推荐保存到本地,因为这样可以让Hana看产出的媒体,获得更好的效果。
┌─────────────────────────────────────────────────┐
│ JS 层 (Node.js) │
│ │
│ doc_intake.js ─→ service.js ─→ Python spawn │
│ │
│ 职责: 参数校验 / 文件级并发控制 / 批量摘要 │
│ / 结果格式化 │
└──────────────────────┬──────────────────────────┘
│ 配置走 stdin,结果走 stdout
▼
┌─────────────────────────────────────────────────┐
│ Python 层 (main.py) │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ MinerU │ │ PaddleOCR │ │ Local │ │
│ │ Client │ │ Client │ │ Extractors│ │
│ └────┬─────┘ └──────┬───────┘ └─────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ cloud API cloud API PyMuPDF / │
│ (vlm/pipeline) (HTTP API) python-docx / │
│ openpyxl / │
│ html2markdown │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Shared Infrastructure │ │
│ │ KeyPool · api_retry · ImageSplitter │ │
│ │ EMF→PNG · OMML→LaTeX · MathType→LaTeX │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
doc-intake/
├── manifest.json # v2 清单:图标、能力、设置 schema 与自定义设置页、版本
├── index.js # defineApp 入口:读设置 + 注册工具
├── README.md
├── assets/
│ └── icon.png # 图标(512×512)
│
├── routes/settings.js # 设置页后端路由:配置读写 + 依赖检测 + 一键安装 + Token 检测
├── ui/ # 自定义设置页(settings.html + assets:样式、脚本、内置 SDK)
│
├── tools/ # Hana 工具入口(JS)
│ └── doc_intake.js # 主工具:文档/图片提取
│
├── lib/ # JS 公共模块(调度、设置、输出格式、日志、并发)
├── sdk/ # 随包分发的 @hana/app-sdk 闭包
├── skills/doc-intake/SKILL.md # Agent 技能描述(触发条件、参数、使用场景)
├── doc/ # 开发文档(MTEF 支持说明、接手与迭代、图标设计)
├── test/ # 自测与体检脚本(自包含、零绝对路径)
│
├── python/ # Python 后端
│ ├── main.py # 入口:参数解析、后端选择、链式降级、结果格式化
│ ├── mineru_client.py # MinerU 云端 API 客户端(多 Token KeyPool)
│ ├── paddle_client.py # PaddleOCR HTTP API 客户端(多 Token KeyPool)
│ ├── key_pool.py # 多 credential 轮询 + 失败跳过
│ ├── api_retry.py # HTTP 重试:指数退避、429/5xx 自动重试
│ ├── image_splitter.py # 长图智能分割(空白行检测 + 色差容忍)
│ ├── pdf_splitter.py # 大 PDF 内存分块(不落 chunk 文件)
│ ├── split_cli.py # 旧版手动 PDF 切割 CLI(主流程不调用)
│ ├── list_sources.py # 路径展开(文件夹 → 文件列表、类型过滤)
│ ├── utils.py # 图片归一化(URL/本地/对象 → {stem}_media/)
│ ├── probe_deps.py # 依赖探测(设置页「环境依赖管理」用)
│ ├── logger.py # Python 端日志(stderr)
│ ├── mathtype_converter.py # MathType OLE → LaTeX
│ ├── requirements.txt # Python 依赖
│ │
│ ├── extractors/ # 本地提取器
│ │ ├── base.py # 基类 BaseExtractor + ExtractionResult
│ │ ├── _utils.py # 公共件:媒体分类/命名/渲染、XML、表格、公式文本规范化
│ │ ├── docx_extractor.py # DOCX(文本/表格/公式/图/视/音)
│ │ ├── pptx_extractor.py # PPTX(同上 + 旧版转换)
│ │ ├── xlsx_extractor.py # XLSX/XLSM/旧版 XLS(表格/图片锚定)
│ │ ├── legacy_converter.py # DOC/XLS/PPT → OOXML 临时转换层
│ │ ├── html_extractor.py # HTML(元数据/链接/代码块/远程媒体)
│ │ ├── pdf_extractor.py # PDF 本地兜底(PyMuPDF)
│ │ ├── emf_converter.py # EMF/WMF → PNG
│ │ ├── omml_converter.py # OMML(Office 公式)→ LaTeX
│ │ └── mathtype_filter.py # MathType 预览图过滤
│ │
│ └── mathtype/ # MTEF 解析与渲染(v3 与 v5 两条独立路线)
│ ├── chars.py # 字符映射
│ ├── mtef_v3.py / mtef_v3_latex.py
│ ├── mtef_v5.py / mtef_v5_latex.py
│ ├── ole_util/ # OLE 复合文件读取
│ └── samples/ # 自测样本
| 格式 | 扩展名 | 后端 | 说明 |
|---|---|---|---|
.pdf |
MinerU → PaddleOCR → Local | 链式降级,扫描件走 OCR | |
| Word | .docx |
Local | 文本/表格/公式/图片/视频/音频 |
| Word(旧版) | .doc |
Local(Office COM → .docx) |
转换后复用 DOCX 提取器;复杂内容可能有兼容性差异 |
| PowerPoint | .pptx |
Local | 文本/表格/公式/图片/视频/音频 |
| PowerPoint (旧版) | .ppt |
Local(Office COM → .pptx) |
需要 pywin32,自动转 pptx 后处理 |
| Excel | .xlsx / .xlsm |
Local | 表格/图片锚定 |
| Excel(旧版) | .xls |
Local(Office COM → .xlsx) |
转换后复用 XLSX 提取器;宏不保留,复杂对象需验证 |
| HTML | .html / .htm |
Local | 元数据/链接/代码块/远程媒体 |
| 图片 | .jpg .jpeg .png .bmp .tiff .tif .webp .gif |
PaddleOCR | 自动长图分割 |
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
source |
string[] |
✅ | — | 文件路径列表。支持单个文件、多个文件、文件夹路径。文件夹自动展开为所有支持格式的文件。 |
outputDir |
string |
❌ | 由 autoSave + savePath 配置决定 |
保存目录。未指定时:若 autoSave=true 或 summaryOnly=true 则保存到 savePath,否则不保存文件。 |
backend |
"auto" | "mineru" | "paddleocr" | "local" |
❌ | "auto" |
强制指定后端。auto 按文件类型走默认降级链。 |
pageRange |
string |
❌ | 全部页 | PDF 页码范围,格式 "1-5,10,15-20"(1-based)。仅 PDF 有效。 |
language |
string |
❌ | "zh" |
文档语言,传递给 OCR / 云端 API。影响 PaddleOCR 和 MinerU 的识别语言。 |
includeMedia |
boolean |
❌ | true |
是否提取媒体文件(图片/视频/音频)。默认提取。只有用户明确要求纯文本、不要提取或不要保存媒体时才设为 false;“保存 JSON”“正文可省略”“只返回摘要”都不能解释为 false。 |
saveJson |
boolean |
❌ | false |
是否额外保存结构化 JSON 文件(与 .md 并列)。JSON 包含 markdown 内容 + metadata;其中 metadata.mediaPaths 保存相对路径,Agent 返回的媒体路径仍为绝对路径。 |
summaryOnly |
boolean |
❌ | false |
只返回每个文件的成功/失败状态和输出路径,不返回 Markdown 正文;提取和保存流程仍正常执行。成功但带 warnings 的文件仍算成功。 |
splitOnly |
boolean |
❌ | false |
仅做图片分割测试,不调用后端。调试用,正常提取不要传。 |
Token 有效性检测已从工具中移除。它不是「提取文档」这一类工作,而是配置环节的一次性检查, 现在放在设置页:MinerU / PaddleOCR 两个 Token 字段下面各有一个「检测」按钮。
数据面是设置页路由的 POST /validate,由 lib/validate.js 纯 Node 发探测请求,不 spawn Python。
以下所有配置项均在插件设置页中修改,不需要手动编辑配置文件。页面上,「基础项」放最常用的那批,其余按功能分组收在「高级设置」里;下面的分类是功能视角,与页面的展开状态无关。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pythonPath |
string |
"" |
Python 可执行文件路径(如 conda 环境的 python.exe)。必填,不填则无法运行。 |
defaultBackend |
"auto" | "mineru" | "paddleocr" | "local" |
"auto" |
默认解析后端。 |
defaultLanguage |
string |
"zh" |
默认语言(zh / en 等),传递给 OCR 和云端 API。 |
includeMedia |
boolean |
true |
默认是否提取媒体(图片/视频/音频)。 |
saveJson |
boolean |
false |
默认是否保存结构化 JSON。 |
autoSave |
boolean |
false |
默认是否自动保存到本地。 |
savePath |
string |
"" |
默认保存路径。留空 = 系统文档目录下的 doc-intake(按各自机器算,不写死盘符)。 |
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mineruCredentials |
string |
"" |
MinerU Token,多个用分号分隔。获取地址:https://mineru.net/apiManage/token |
mineruModelVersion |
"vlm" | "pipeline" | "MinerU-HTML" |
"vlm" |
MinerU 模型版本。vlm 精度最高,pipeline 速度快。 |
mineruEnableOCR |
boolean |
true |
MinerU 是否开启 OCR(仅 pipeline / vlm 有效)。 |
mineruEnableFormula |
boolean |
true |
MinerU 是否开启公式识别。 |
mineruEnableTable |
boolean |
true |
MinerU 是否开启表格识别。 |
mineruFlashMaxMB |
number |
10 |
MinerU Flash 模式最大文件大小(MB)。无 Token 时走 Flash。 |
mineruFlashMaxPages |
number |
20 |
MinerU Flash 模式最大页数。 |
mineruPrecisionMaxMB |
number |
200 |
MinerU Precision 模式最大文件大小(MB)。有 Token 时走 Precision。 |
mineruPrecisionMaxPages |
number |
200 |
MinerU Precision 模式最大页数。 |
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
paddleTokens |
string |
"" |
PaddleOCR Token,多个用分号分隔。获取地址:https://aistudio.baidu.com/account/accessToken |
paddleUseDocOrientationClassify |
boolean |
false |
文档方向分类。 |
paddleUseDocUnwarping |
boolean |
true |
文档去扭曲。 |
paddleUseChartRecognition |
boolean |
true |
图表识别。 |
paddleUseSealRecognition |
boolean |
false |
印章识别。 |
paddleUseTableRecognition |
boolean |
true |
表格识别。 |
paddleUseFormulaRecognition |
boolean |
true |
公式识别。 |
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pdfBackendChain |
string |
"" |
PDF 后端降级链。用 > 或 ; 分隔,如 mineru>paddleocr>local;留空 = 自动(mineru>paddleocr>local)。按顺序尝试,失败自动降级到下一档。(v1 这里是数组,v2 改成了字符串) |
autoSplitLargePDF |
boolean |
true |
自动切割超限 PDF(在插件 Python 进程内按 splitChunkPages 生成内存 PDF bytes,直接上传云端,不创建 *_chunks 文件夹)。 |
splitChunkPages |
number |
180 |
内存分块时每块页数(小于 MinerU 200 页限制)。 |
大 PDF 的切割只用于云端上传,chunk 会在 Python 进程内以 PDF bytes 存在,不会在源文件目录创建 *_chunks 文件夹或 chunk PDF。所有 chunk 的识别结果会按 chunkIndex 在插件内部按原 PDF 顺序合并,再统一保存为一个 Markdown/JSON 文件;媒体也会直接写入原 PDF 对应的唯一 {原文件名}_media/ 目录,并使用 chunk 前缀避免重名。插件不会先生成 chunk PDF 再上传,也不会先生成 chunk Markdown/JSON 再做文件级后处理。
local 是 PDF 降级链的最末档 — 基于 PyMuPDF 直接读 PDF 内嵌文本和图片。它不调用云端 API,但有以下能力边界:
- 不做 OCR:扫描版(无文本层)只能拿到空白,需走 PaddleOCR / MinerU
- 不做公式识别 / 表格识别 / 分栏重排
- Founder 方正 PDF 的 ToUnicode CMap 可能缺失:这类 PDF(很多国标 GB/T 文件)在 PyMuPDF 提取后会出现「犐犆犛」、「犌犅」之类乱码字符(实际是 ASCII 'I''C''S''G''B' 等被错误 fallback 到 CJK 扩展区)。这是 PDF 文件本身的缺陷,本地档无法修复
本地档会在 warnings 里检测 Founder 方正特征乱码(Unicode U+7280–U+72FF)并给出明确提示,建议改用云端 OCR 后端重新提取。看到此警告请改传 backend="mineru" 或 backend="paddleocr"。
本地档会把每页内嵌图片按 page.get_image_info() 给出的 y 坐标,插入到对应文字 block 之前 / 之后,输出 markdown 时按视觉顺序拼接:
- 同 y 行的多张图按 x0 升序排(左到右),容忍 ±5px 浮点误差
- 同一 xref 在一页内只引用一次
- inline image(xref=0)跳过:PyMuPDF 不暴露原始 bytes,极少出现
- "纯资源引用"(在
get_images但get_image_info没显示)不抽
例:GB+4674-2009.pdf 的第 7 页有 7 张图(6 张并排示意图 + 1 张页脚图标),markdown 输出会按视觉位置穿插在对应段落之间。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
splitImageThreshold |
number |
1.2 |
高度/宽度超过此值才启用分割。 |
splitImageTolerance |
number |
15 |
分割色差容忍度(欧氏距离)。 |
splitImageBlankRatio |
number |
0.98 |
一行中多少比例的像素相近才算空白行。 |
splitImageMinBlank |
number |
5 |
连续多少行空白才作为切割点。 |
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxConcurrent |
number |
4 |
云端 API 后端并发数(MinerU / PaddleOCR)。 |
maxConcurrentLocal |
number |
8 |
普通本地解析并发数(DOCX / PPTX / HTML 等)。旧版 Office 转换使用独立并发限。 |
legacyConversionProvider |
string |
auto |
旧版 .doc / .xls / .ppt 转换 provider:auto、office_com、libreoffice、disabled。 |
libreOfficePath |
string |
"" |
LibreOffice 可执行文件绝对路径;仅选择 libreoffice 或 auto 且无 Office COM 时使用。 |
legacyConversionConcurrency |
number |
1 |
旧版 Office 转换并发上限。COM 默认串行。 |
legacyConversionTimeoutMs |
number |
180000 |
LibreOffice 旧格式转换超时时间(毫秒)。 |
maxRetries |
number |
3 |
API 临时错误最大重试次数(429 / 5xx / 网络抖动)。 |
retryBaseDelayMs |
number |
1000 |
重试基础退避毫秒(5xx/network 按 base × 2^attempt;429 独立用 8s base)。 |
keyRetryOnFailure |
boolean |
true |
Key 失败时自动重试下一个。 |
notifyKeyFailure |
boolean |
true |
Key 失效时通知用户。 |
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
htmlExtractMetadata |
boolean |
true |
提取元数据(title、author、description 等)。 |
htmlExtractLinks |
boolean |
true |
提取所有链接列表。 |
htmlExtractImages |
boolean |
true |
提取并下载所有图片。 |
htmlExtractCodeBlocks |
boolean |
true |
提取代码块并标注语言。 |
htmlHeadingStyle |
"ATX" | "SETEXT" |
"ATX" |
Markdown 标题风格。 |
xlsxMaxRows |
number |
100 |
Excel 表格最大提取行数,适用于 .xls、.xlsx、.xlsm;0 表示不限制,正数表示具体上限。 |
xlsxMaxCols |
number |
50 |
Excel 表格最大提取列数,适用于 .xls、.xlsx、.xlsm;0 表示不限制,正数表示具体上限。 |
maxRemoteImagesPerHtml |
number |
100 |
HTML 页面最多下载多少远程图片。 |
SKILL.md 只保留 Agent 调用工具时需要执行的规则;本节记录返回层的设计,供维护者理解实现,不作为 Agent 的调用指令。
Hana 对单个 content[type="text"] 有约 32 KiB 的 UTF-8 字节限制。单块直接达到限制时,平台可能对正文做头尾截断,因此插件采用低于限制的独立文本块承载 Markdown:
- 默认每块
28672字节,即 28 KiB; - 默认最多 4 块,总容量约 112 KiB;
- 每个块都保持在单块限制以下,并按 UTF-8 边界切分;
- 完整结构化数据仍放在
details.data,正文文本放在多个content[type="text"]块中。
这个方案来自实际探针验证,不代表 4 是 Hana 的硬上限。增加块数只能扩大工具结果的承载量,仍会受到 Agent 上下文、模型上下文窗口、provider 请求限制和输入成本影响。当前默认值优先取稳定余量,而不是追求最大块数。
Excel 行列上限和 Agent 返回容量是两个独立层次:
xlsxMaxRows/xlsxMaxCols作用于 Excel 解析阶段,统一适用于.xls、.xlsx、.xlsm。正数决定解析器最多把多少行、多少列写入 Markdown;设置为0时不限制,保存 Markdown 或 JSON 会包含源表中的全部可提取内容。inlineBlockBytes/inlineBlockCount只作用于工具结果返回给 Agent 的正文容量。它们不会减少解析器已经提取的数据,也不会缩短已保存的 Markdown 或 JSON。
返回层按最终 Markdown 内容大小判断,不按文件数量判断:
- 内容能放入
inlineBlockBytes × inlineBlockCount:返回完整 Markdown,必要时拆成多个 text block。 - 内容超限且已有本地保存结果:返回处理摘要和路径,完整正文从
mdPath读取。 - 内容超限且没有本地保存结果:跨多个 text block 保留开头和结尾,中间插入省略提示,并标记结果不完整。
因此,xlsxMaxRows / xlsxMaxCols 控制“提取多少数据”,inlineBlockBytes / inlineBlockCount 控制“当场发送给 Agent 多少正文”。本地 Markdown 文件是大文档的完整兜底,但它也只包含 Excel 行列提取上限以内的内容。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
inlineBlockBytes |
number(4096–30720) |
28672 |
每个返回文本块的最大大小,按 UTF-8 字节计算;默认 28 KiB。 |
inlineBlockCount |
number(1–8) |
4 |
返回文本块数量上限;默认 4 个,默认总容量约 112 KiB。 |
mediaPathReturnLimit |
number(≥0) |
20 |
Agent 返回媒体绝对路径的数量上限。媒体数不超过上限时返回 mediaPaths,超过上限时返回 mediaCount。 |
旧版 maxInlineReturnBytes 会作为 inlineBlockBytes 的兼容 fallback,但新配置优先。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
logLevel |
"DEBUG" | "INFO" | "WARNING" | "ERROR" |
"INFO" |
日志级别。 |
logFile |
string |
"" |
日志文件路径(留空则只输出到控制台 stderr)。 |
summaryOnly=true 时会优先返回状态摘要,不受正文大小限制:
- 每个文件返回
status: "success"或status: "failed"。 - 失败项返回
error,成功但存在 warnings 的文件仍算成功。 - 如果指定了
outputDir或启用了自动保存,状态项保留mdPath、mediaDir等路径。 details.data保留完整结构化结果;content末尾附带独立的机器可读doc_intake_paths路径块,供 Agent 直接读取。- 保存到磁盘的 JSON 结构不受 Agent 路径返回字段影响;JSON 中的
metadata.mediaPaths使用相对路径,Agent 路径块中的mediaPaths使用绝对路径。 - 媒体数量不超过
mediaPathReturnLimit时返回mediaDir和每个媒体的绝对mediaPaths;超过上限时只返回mediaDir和mediaCount。如果上游媒体路径缺少可用基准,则保留媒体总数量并返回警告,不静默丢弃。 - 只有实际生成 JSON 文件时才返回
jsonPath。 - 不返回 Markdown 正文,适合批量处理时只确认文件是否成功。
未设置 summaryOnly 时,返回策略按内容大小和文本块容量判断,不按文件数量判断:
- 结果不超过
inlineBlockBytes × inlineBlockCount:返回全部 Markdown,必要时拆成多个 text block。 - 结果超限且已保存到本地:只返回处理摘要和
mdPath/mediaDir等路径信息。 - 结果超限且未保存到本地:在多个 text block 中保留开头和结尾,中间插入省略提示,并明确提示内容不完整。
返回完整 markdown + metadata,Agent 可直接读取内容:
{
"name": "report.pdf",
"markdown": "# report.pdf\n\n提取的完整内容...",
"metadata": {
"mediaPaths": ["/path/to/report_media/image_001.png"],
"format": "pdf",
"reader": "mineru",
"backendChain": ["local"],
"warnings": [],
"usedBackendInChain": true
}
}只返回摘要 + 文件列表,不返回正文;完整内容从 mdPath 读取:
{
"markdown": "处理完成:12/16 个文件成功\n\n✅ a.pdf\n✅ b.docx\n❌ c.pdf — 所有后端都失败",
"metadata": { "batch": true, "count": 16, "success": 12 },
"files": [
{ "name": "a.pdf", "metadata": { "mdPath": "D:\\out\\a.pdf.md", "mediaDir": "D:\\out\\a_media", "mediaCount": 86 } }
]
}大 PDF 的分块只在插件内部以内存 PDF bytes 完成,源文件目录不会出现 *_chunks 文件夹或 chunk PDF。
outputDir/
├── 文件名.pdf.md ← 完整合并后的 Markdown
├── 文件名.pdf.json ← 完整合并后的 JSON(仅 saveJson=true 时)
└── 文件名_media/ ← 唯一媒体目录
├── image_001.png
├── image_002.jpg
├── video_001.mp4
└── audio_001.flac
媒体命名规则:所有后端统一按类型分别连续编号,image_NNN.ext / video_NNN.ext / audio_NNN.ext;云端 MinerU/PaddleOCR 返回的超长原始媒体名会被插件丢弃。分块 PDF 会增加 chunk_NNN_ 前缀,例如 chunk_001_image_001.jpg,避免并发分块之间覆盖。云端媒体重命名后,运行时 Markdown、Agent路径和保存的 JSON会同步使用新名称。
- 图片:
 - 视频:
<video controls src="文件名_media/video_001.mp4" title="alt"></video> - 音频:
<audio controls src="文件名_media/audio_001.flac" title="alt"></audio>
# 聊天中读取文档
doc_intake(source=["report.pdf"])
# 用户指定保存位置
doc_intake(source=["report.pdf"], outputDir="D:/Output")
# 只提取前 5 页
doc_intake(source=["report.pdf"], pageRange="1-5")
# 只要文本,不抽媒体
doc_intake(source=["document.docx"], includeMedia=false)
# 强制用本地后端(不走云端)
doc_intake(source=["paper.pdf"], backend="local")# 提取整个文件夹
doc_intake(source=["D:\\各种文件"], outputDir="D:\\输出")
# → 返回:16 个文件处理完成,12 成功 4 失败 + 文件列表
# 手动指定多个文件
doc_intake(source=["a.docx", "b.pptx", "c.xlsx"], outputDir="D:\\输出")不在工具里做了。设置页 → 应用 → Doc Intake,MinerU / PaddleOCR 两个 Token 字段下面各有一个 「检测」按钮,点一下逐把出结果。
如果你在开发自己的插件或 Skill,需要调用 doc_intake 提取文档内容,直接使用上面的参数即可。在你的 SKILL.md 中写明调用方式,Agent 会自动路由到这个插件。
最小调用:
doc_intake(source=["文件路径"])典型调用(带保存):
doc_intake(source=["文件路径"], outputDir="保存目录")PDF 指定页码范围:
doc_intake(source=["report.pdf"], pageRange="1-10,15")强制降级到本地(无 API 时):
doc_intake(source=["document.pdf"], backend="local")| 错误码 | 含义 | 处理方式 |
|---|---|---|
PYTHON_PATH_NOT_CONFIGURED |
未配置 pythonPath |
去插件设置面板填写 |
INVALID_SOURCE |
文件路径无效 | 检查路径是否存在、格式是否支持 |
| 链式降级全失败 | 所有后端都返回空/错误 | 返回的 markdown 包含错误提示,warnings 列出每档失败原因 |
SPAWN_FAILED |
Python 进程启动失败 | 检查 pythonPath 是否正确、Python 环境是否完整 |
PYTHON_ERROR |
Python 进程非零退出 | 查看 stderr 输出定位具体问题 |
MIT