OpenClaw document-extract 插件完全指南PDF 本地文本提取与页面图像回退机制【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclawdocument-extract是 OpenClaw 内置的本地文档提取插件负责从本地文档附件中提取文本并在文本不足时回退渲染页面图像供视觉模型继续分析。本文以插件参考文档为核心骨架结合 核心实现、插件清单 与配套测试源码深入讲解该插件的架构、提取流水线、预算分配策略及其与pdf工具的集成方式读完即可掌握其工作原理、配置项与可验证行为。插件概览与定位插件参考文档给出了三个关键信息它们共同定义了document-extract在 OpenClaw 生态中的位置Packageopenclaw/document-extract-pluginInstall routeincluded in OpenClaw随主项目分发无需单独安装Surface契约面documentExtractorsdocumentExtractors是插件向运行时暴露的契约contract通过它OpenClaw 的核心服务可以在不感知具体实现细节的情况下调用文档提取能力。插件清单 openclaw.plugin.json 中显式声明了该契约的注册内容{ id: document-extract, activation: { onStartup: false }, enabledByDefault: true, name: Document Extraction, description: Extract text and fallback page images from local document attachments., contracts: { documentExtractors: [pdf] }, configSchema: { type: object, additionalProperties: false, properties: {} } }几个值得注意的设计点enabledByDefault: true插件默认启用用户无需任何配置即可获得 PDF 提取能力activation.onStartup: false插件不在启动阶段激活而是按需惰性加载避免拖慢启动速度contracts.documentExtractors: [pdf]目前契约下只注册了一个提取器即id为pdf的提取器configSchema为空对象该插件本身不接收用户配置全部行为由调用方如pdf工具通过请求参数控制。参考文档还关联了 PDF 工具文档这是理解本插件实际用途的关键入口——document-extract是pdf工具提取回退模式的底层实现。插件入口与运行时分流设计插件的入口文件 index.ts 非常精简只负责向 OpenClaw 注册插件元数据import { definePluginEntry } from openclaw/plugin-sdk/plugin-entry; export default definePluginEntry({ id: document-extract, name: Document Extraction, description: Extract text and fallback page images from local document attachments., register() { // Runtime is exposed through document-extractor.ts so document hot paths can // load only the narrow extractor artifact instead of the full plugin entrypoint. }, });这里的register()故意留空源码注释明确解释了设计动机文档提取属于热路径如每封邮件附件、每次 PDF 分析都会触发让运行时直接加载窄粒度的提取器模块可以避免加载完整的插件入口及其依赖树从而降低内存占用与启动开销。实际可用的提取器在 document-extractor.ts 中定义。包定义 package.json 显示该插件唯一的运行时依赖是clawpdf0.3.1——一个基于 PDFium WebAssembly 的 PDF 引擎文本与图像提取全部由它完成插件本身只是封装与策略层。提取器声明PDF 提取器如何被识别document-extractor.ts 末尾导出了工厂函数createPdfDocumentExtractor()export function createPdfDocumentExtractor(): DocumentExtractorPlugin { return { id: pdf, label: PDF, mimeTypes: [application/pdf], autoDetectOrder: 10, extract: extractPdfContent, }; }提取器描述符的四个字段定义了它如何被运行时发现与调度字段值含义idpdf提取器唯一标识与插件清单中的契约条目对应labelPDF人类可读的名称mimeTypes[application/pdf]该提取器受理的 MIME 类型用于按附件类型自动路由autoDetectOrder10自动检测优先级数值越大越优先多个提取器同时命中时据此排序测试 document-extractor.test.ts 中的declares PDF support用例对描述符做了精确断言锁定了id、label、mimeTypes、autoDetectOrder的值防止后续改动破坏契约。核心提取流水线文本优先图像回退提取逻辑集中在extractPdfContent()document-extractor.ts整体遵循文本优先图像回退的两阶段策略与参考文档摘要中 Extract text and fallback page images 的描述一一对应。第一步引擎懒加载与文档打开const MAX_EXTRACTED_TEXT_CHARS 200_000; const MAX_RENDER_DIMENSION 10_000; async function loadPdfEngine(): PromisePdfEngine { if (!pdfEnginePromise) { pdfEnginePromise import(clawpdf) .then(({ createEngine }) createEngine()) .catch((err: unknown) { pdfEnginePromise null; throw new Error(Dependency clawpdf is required for PDF extraction, { cause: err }); }); } return pdfEnginePromise; }clawpdf引擎通过动态import()懒加载首次调用时初始化之后复用缓存的pdfEnginePromise若加载失败会重置缓存允许下次重试模块级常量MAX_EXTRACTED_TEXT_CHARS 200_000与MAX_RENDER_DIMENSION 10_000分别是单次文本抽取的字符上限与页面渲染的最大边长像素构成提取过程的硬预算。打开文档时支持可选密码document-extractor.tsasync function openPdfDocument(params: { engine: PdfEngine; input: Uint8Array; password?: string; }): PromisePdfDocument { try { return params.password ? await params.engine.open(params.input, { password: params.password }) : await params.engine.open(params.input); } catch (err) { if (isPdfPasswordError(err)) { throw new Error(PDF requires a password or password is incorrect., { cause: err }); } throw err; } }当底层引擎抛出code password的错误时会被归一化为语义清晰的统一错误信息方便上层如pdf工具判断并向用户提示。第二步页面选择与文本抽取const pages request.pageNumbers ? request.pageNumbers .filter((p) Number.isInteger(p) p 1 p pdf.pageCount) .slice(0, request.maxPages) : undefined; if (request.pageNumbers?.length pages?.length 0) { throw new Error(No requested PDF pages exist in this ${pdf.pageCount}-page document.); } const pageSelection pages ? { pages } : { maxPages: request.maxPages }; const textResult await pdf.extract({ mode: text, ...pageSelection, maxTextChars: MAX_EXTRACTED_TEXT_CHARS, }); const text textResult.text;若请求指定了pageNumbers会先做合法性过滤必须是1到pdf.pageCount之间的整数再按maxPages截断全部越界时直接抛错避免空转未指定页码时用maxPages限制参与抽取的页数上限文本抽取以mode: text执行受maxTextChars硬上限约束。第三步文本不足时按页渲染图像文本抽取完成后判断是否达到阈值if (text.trim().length request.minTextChars) { return { text, images: [] }; }若text.trim().length request.minTextCharsminTextChars由调用方传入说明文本足够直接返回{ text, images: [] }完全不渲染图像。这是最常见的性能路径。否则进入图像回退分支这里有一段值得细读的工程决策注释// clawpdfs image render budget (maxPixels) is shared across every page in one // extract() call: the first page consumes it and later pages collapse to 1x1 // PNGs that vision models reject. Render each page separately, allocating the // remaining aggregate budget across pages that still need rendering.意思是如果一次性调用extract({ mode: images })渲染多页maxPixels总预算会被共享且第一页会耗尽它后续页面会退化成 1x1 的 PNG——这是视觉模型无法接受的。因此插件改为逐页单独渲染并把剩余总预算按剩余页数均摊const imagePages pages ?? Array.from({ length: Math.min(pdf.pageCount, request.maxPages) }, (_, i) i 1); const images: DocumentExtractedImage[] []; let remainingPixels request.maxPixels; for (const [index, pageNumber] of imagePages.entries()) { if (remainingPixels 0) break; const pagesRemaining imagePages.length - index; const maxPixelsPerPage Math.max(1, Math.ceil(remainingPixels / pagesRemaining)); const imageResult await pdf.extract({ mode: images, pages: [pageNumber], image: { maxDimension: MAX_RENDER_DIMENSION, maxPixels: maxPixelsPerPage, forms: true, }, }); for (const image of imageResult.images) { images.push(toDocumentImage(image)); remainingPixels - image.width * image.height; } }每次只渲染一页maxPixelsPerPage max(1, ceil(remainingPixels / pagesRemaining))让靠后的页面不被饿死forms: true表示渲染时保留表单字段内容每页渲染后从总预算中扣除实际像素数width * height预算耗尽即停止渲染出的PdfImage通过toDocumentImage()转为 base64 的DocumentExtractedImagedocument-extractor.ts最终随文本一起返回给模型。第四步资源释放与失败降级整个流程包在try/finally中pdf.destroy()保证无论成功失败都释放引擎资源document-extractor.ts。图像渲染失败时document-extractor.ts若调用方提供了onImageExtractionError回调会收到渲染异常可用于监控告警若此时已提取到非空文本降级返回{ text, images: [] }只丢图像不丢内容若连文本都没有则抛出PDF image extraction failed with no extractable text.明确告知上层本次提取完全失败。请求参数全解综合 document-extractor.ts 与测试 document-extractor.test.ts 的request()构造DocumentExtractionRequest支持以下字段字段类型说明bufferUint8ArrayPDF 文件的二进制内容提取入口mimeTypestring文档 MIME 类型如application/pdfpasswordstring?加密 PDF 的打开密码可选pageNumbersnumber[]?指定参与处理的页码1 起可选未指定则处理前maxPages页maxPagesnumber最大处理页数控制文本抽取与图像渲染范围maxPixelsnumber图像回退渲染的总像素预算按剩余页数动态均摊minTextCharsnumber文本达标阈值超过它即跳过图像渲染onImageExtractionError(err) void?图像渲染失败时的通知回调可选在 pdf 工具中的实际应用参考文档指出的相关文档 PDF 工具 揭示了document-extract的真实调用场景pdf工具分析 PDF 时对支持原生 PDF 输入的提供商当前为 Anthropic、Google直接走Native provider mode对其他所有提供商走Extraction fallback mode其流水线正是本节讲解的插件通过内置document-extract插件从选中页面提取文本默认最多20页见agents.defaults.pdfMaxPages若提取文本短于200字符即调用方传入的minTextChars 200将同一批页面渲染为 PNG 图像渲染预算为4,000,000像素maxPixels文本已足够的页面完全跳过渲染将提取文本及必要时渲染出的图像连同 prompt 一起发送给目标模型。与之配套的配置项定义在agents.defaults下配置参考{ agents: { defaults: { pdfModel: { primary: anthropic/claude-opus-4-6, fallbacks: [openai/gpt-5.4-mini], }, pdfMaxMb: 10, pdfMaxPages: 20, }, }, }KeyDefaultMeaningagents.defaults.pdfModelunset显式指定 PDF 主模型与回退模型未设置时依次回退到imageModel、会话模型agents.defaults.pdfMaxMb10单个 PDF 的大小上限MBagents.defaults.pdfMaxPages20每个 PDF 最多处理的页数其他行为联动加密 PDF 通过password参数打开目标模型不支持图像且无文本可提取时报错图像渲染失败时丢弃图像仅保留文本目标模型为纯文本模型时同样丢弃图像只发文本。由此可见document-extract插件的每次参数注入都对应着工具层的显式策略决策。测试驱动的行为验证document-extractor.test.ts 用 Vitest 对核心行为做了完整覆盖是理解插件语义的最佳佐证文本优先skips image fallback when enough text is extracted用例断言当文本达标时extract只被调用一次仅文本模式图像渲染被完全跳过逐页像素预算extracts text first and renders each fallback page with its own pixel budget用例验证总预算100像素被均摊为每页50并以maxDimension: 10_000、forms: true逐页调用渲染密码与错误归一化opens encrypted PDFs with the request password与normalizes clawpdf password errors分别验证密码透传与统一错误信息页面过滤filters selected pages and renders them one page per image call验证非法页码被剔除、越界页码抛错失败降级reports image fallback failures and returns extracted text验证渲染失败时回调触发且文本仍被保留surfaces image fallback failures for empty PDF text则验证空文本 渲染失败时抛出PDF image extraction failed with no extractable text.并携带原始PdfBudgetError作为cause。这些用例将本文前述的每一步流程都固化为可回归的契约任何改动导致行为偏离都会在 CI 中被捕获。小结document-extract插件是 OpenClaw 本地 PDF 分析能力的地基它以documentExtractors契约为界面向运行时暴露pdf提取器通过文本优先、图像回退的两阶段策略和逐页像素预算分配在保证提取质量的同时严格控制资源消耗。对使用者而言无需任何配置即可默认获得该能力对开发者而言document-extractor.ts 中的预算常量、错误归一化与降级逻辑以及 document-extractor.test.ts 中的行为断言共同构成了一份可读、可验证的参考实现。若需调节页面数、文件大小或指定专用模型请在agents.defaults下配置pdfModel、pdfMaxMb与pdfMaxPages详见 PDF 工具文档。【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考