- AI 应用
- 人工智能
- AI Agent
- 本地部署
- 前端
- 后端
- 工作流自动化
【免费下载链接】ekko-studio
Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.
导读
本文以 Ekko Agent 内置的pdf技能(Skill)为骨架,系统讲解如何在 AI Agent 工作流中完成 PDF 的文本抽取、表格提取、文档创建、页面合并拆分、AcroForm 表单的创建与填充、水印盖章、元数据与附件管理、AES-256 加密解密,以及基于页面栅格化的视觉验证。读完本文,你将掌握一套可直接复用的 PDF 处理命令行工具链(全部脚本位于 packages/ekko-agent/skills/pdf/scripts),并理解其背后的 pypdf、reportlab、pdfplumber、pypdfium2 技术实现与安全准则。
1. Skill 定位与能力边界
pdf技能是 Ekko Agent 内置技能之一(技能目录见 packages/ekko-agent/skills),其能力声明(frontmatter)为:读取、创建、合并、拆分、填充、水印、盖章、加密、检查、渲染和视觉验证 PDF 文件,并覆盖 AcroForm 表单工作流。元数据关键词为.pdf、pdf form、merge pdf、create pdf,方便按文件扩展名与任务类型自动路由。
技能声明(见 SKILL.md)明确该技能适用于:
- PDF 内容提取(text / tables / metadata / form fields);
- PDF 生成(从结构化输入创建新文档);
- 页面级操作(合并、拆分、旋转、压缩);
- 表单操作(AcroForm 的检查、填充、创建);
- 元数据与文件附件管理;
- 加密/解密(基于口令);
- 视觉审查(页面渲染成图片供多模态模型检查)。
一个关键的设计原则是:默认输出到新文件,除非用户明确要求,绝不覆盖或删除源 PDF;对输入先检查再变换,并默认保留页面顺序、方向、尺寸、注释和表单意图。
2. 环境准备:依赖安装与渲染后端
技能文档要求先检查依赖,且不得静默安装——缺少依赖时应明确告知用户安装命令。常用依赖为:
python3 -m pip install pypdf reportlab pdfplumber各库职责如下:
| 库 | 用途 | 对应脚本 |
|---|---|---|
| pypdf | 合并/拆分/填充/加密/元数据/附件的 PDF 对象级操作 | pdf_merge.py、pdf_split.py、pdf_fill_form.py、pdf_secure.py、pdf_meta.py |
| reportlab | 从零创建 PDF 文档与 AcroForm 控件(platypus / canvas.acroForm) | pdf_create.py、pdf_make_form.py、pdf_stamp.py |
| pdfplumber | 文本与表格提取、扫描页启发式判断 | pdf_read.py |
2.1 页面渲染后端:pypdfium2 → pdftoppm 回退链
页面渲染(pdf_page_image.py、pdf_form_layout.py --render-overlay)依赖统一的栅格化模块 _raster.py,其回退链为:
- pypdfium2(pip 安装):
PdfDocument.render(scale=dpi/72.0)渲染后转 PIL RGB 图像; - Poppler 的 pdftoppm(系统包
poppler-utils):以pdftoppm -png -r DPI -f page -l page逐页输出。
当两个后端都不可用时,脚本不会崩溃,而是输出{"rendered": false, "missing": [...]}的 JSON 并返回退出码 0,让调用方可以分支处理而不是中断。缺失提示会给出安装建议:
["python3 -m pip install pypdfium2", "poppler-utils (provides pdftoppm), e.g. apt-get install poppler-utils"]3. 运行方式:skill_view、baseDirectory 与 terminal_exec
技能文档规定,使用本技能的第一步是调用skill_view查看本 Skill,并从返回结果中取得baseDirectory,所有内置辅助脚本都位于<baseDirectory>/scripts下。执行辅助脚本统一使用terminal_exec工具,命令为python3加参数数组,必要时先用--help查看精确参数。
典型调用示例(文档原文保留):
python3 <baseDirectory>/scripts/pdf_read.py input.pdf --json python3 <baseDirectory>/scripts/pdf_page_image.py input.pdf -o rendered --pages 1-3 python3 <baseDirectory>/scripts/pdf_merge.py first.pdf second.pdf -o merged.pdf python3 <baseDirectory>/scripts/pdf_split.py input.pdf -o pages注:上例中的
--json与-o是技能文档的示意写法,实际参数以各脚本--help为准(例如pdf_read.py使用--text/--tables/--meta/--fields互斥模式,pdf_page_image.py使用--out-dir)。凡涉及表单操作,应先通过skill_view阅读 references/forms.md。
所有脚本统一约定:输出 UTF-8 JSON 到 stdout(脚本内通过sys.stdout.reconfigure(encoding="utf-8")保证中文与 Unicode 内容不乱码),错误信息走 stderr,并以非零退出码表达失败。
4. 辅助脚本总览
技能文档给出的 12 个辅助脚本按职责分类如下:
| 脚本 | 能力 | 依赖 |
|---|---|---|
| pdf_read.py | 抽取文本、表格、元数据、表单字段 | pdfplumber、pypdf |
| pdf_page_image.py | 将选中页渲染为 PNG | pypdfium2 / pdftoppm |
| pdf_create.py | 从结构化 JSON 创建 PDF | reportlab |
| pdf_merge.py / pdf_split.py | 页面合并与拆分 | pypdf |
| pdf_fill_form.py / pdf_form_layout.py / pdf_make_form.py | AcroForm 检查、填充、创建 | pypdf、reportlab、PIL |
| pdf_watermark.py / pdf_stamp.py | 页面叠加层 | pypdf、reportlab |
| pdf_meta.py | 元数据与附件操作 | pypdf |
| pdf_secure.py | 口令加密/解密 | pypdf |
每类脚本都配套了无网络依赖的端到端测试,见 tests/test_pdf_skill.py,可作为用法参考与回归保障。
5. 核心工作流(5 步标准流程)
技能文档定义了处理任意 PDF 任务的标准工作流,这是整个技能的方法论骨架:
- 检查:按需检查文本、元数据、页数、页面尺寸、加密状态与表单字段;
- 变换:执行用户要求的最小变换,输出到新文件;
- 复读:重新读取输出,验证页数、文本、元数据、附件或字段值是否与预期一致;
- 渲染审查:将每个被修改的页面(大型生成文档则取代表性样本)渲染成图片,用
view_image检查裁剪框(crop box)、旋转、裁切、字体渲染、叠加层与表单外观; - 交付:返回输出文件的绝对路径,并列出验证中存在的限制。
OCR 路由原则:只有当普通文本提取为空或明显不完整时才使用 OCR。扫描件应转交ocr-and-documents技能处理(pdf_read.py --meta的likely_scanned_pages字段正是为这一路由决策提供依据,详见第 6 节)。
6. PDF 读取与检查:pdf_read.py
pypdf 读取脚本 pdf_read.py 提供四种互斥模式,均输出 JSON:
python3 pdf_read.py input.pdf --text # 逐页文本 python3 pdf_read.py input.pdf --tables # 表格(可写 CSV) python3 pdf_read.py input.pdf --meta # 元数据、页面尺寸、加密/扫描标记 python3 pdf_read.py input.pdf --fields # AcroForm 字段与值 python3 pdf_read.py input.pdf --tables --csv-dir ./csvs # 同时把每张表写成 CSV python3 pdf_read.py input.pdf --meta --password <pw> # 加密文件需口令各模式的输出要点(结合源码):
- --text:用 pdfplumber 逐页
extract_text(),返回{"page_count": N, "pages": [...]}; - --tables:逐页
extract_tables(),返回{"table_count", "tables": [{page, index, rows}]},配合--csv-dir会把每张表按pageN_tableN.csv命名落盘; - --meta:返回加密状态
encrypted、页数、DocInfo 元数据(key 去掉/前缀)、每页width/height/rotation,并做扫描页启发式判断——某页无文本但有图片时标记为likely_scanned_pages,同时提示转交 OCR 技能; - --fields:返回
{"field_count", "fields": {name: {type, value, options}}},字段类型映射常量在源码第 103 行:
"/Tx" -> "text", "/Btn" -> "button", "/Ch" -> "choice", "/Sig" -> "signature"对加密文件,--meta与--fields模式下口令缺失或错误会分别以退出码 2/3 明确报错(--meta对加密文件若未给口令只返回提示 note 而非崩溃)。
7. 从零创建 PDF:pdf_create.py
pdf_create.py 基于 reportlab platypus,从 UTF-8 JSON 规格生成 PDF:
python3 pdf_create.py spec.json -o output.pdf规格示例(源码 docstring 中的完整格式):
{ "title": "Example Report", "author": "example-author", "page_size": "A4", "page_numbers": true, "elements": [ {"type": "heading", "text": "Section 1", "level": 1}, {"type": "paragraph", "text": "Body text..."}, {"type": "table", "rows": [["H1", "H2"], ["a", "b"]], "header": true}, {"type": "image", "path": "chart.png", "width": 400}, {"type": "pagebreak"} ] }元素类型与实现细节:
| type | 说明 | 源码要点 |
|---|---|---|
| heading | 标题,level限制在 1–3 | 使用 reportlab 样例样式表Heading{level} |
| paragraph | 正文段落 | BodyText样式 + 6pt 间距 |
| table | 表格,header: true(默认)时首行加浅灰背景与粗体、跨页重复表头(repeatRows=1) | 统一 0.5pt 灰色网格线、顶部对齐 |
| image | 图片,只给width时按原图宽高比自动计算height | img.imageHeight / img.imageWidth保持比例 |
| pagebreak | 分页 | PageBreak() |
page_size默认 A4,传入"letter"时切换为 letter;page_numbers默认true,会在每页底部居中绘制Page N(Helvetica 9pt)。- 未知元素类型不会中断,而是打印
Warning: unknown element type ... skipped。 - 输出 JSON:
{"output": path, "elements": N};缺少 reportlab 时返回退出码 2 并给出安装提示。
端到端测试 test_pdf_skill.py 中report_pdffixture 演示了包含标题、段落、表格、图片、分页符的完整创建用例,并验证页数、Page 1页脚、标题元数据、文本与表格抽取结果。
8. 页面编排:合并与拆分(pdf_merge.py / pdf_split.py)
8.1 合并 pdf_merge.py
python3 pdf_merge.py first.pdf second.pdf -o merged.pdf python3 pdf_merge.py a.pdf b.pdf -o merged.pdf --bookmarks # 每个源文件加一个顶层书签- 按参数顺序逐页合并;
--bookmarks时为每个输入文件按其文件名去扩展名添加一个顶层大纲书签(add_outline_item); - 任一输入已加密则直接报错并提示先用
pdf_secure.py --decrypt解密; - 输出
{"output", "inputs": N, "page_count": 总数}。
8.2 拆分 pdf_split.py
python3 pdf_split.py input.pdf --pages "1-3,5,9-" -o part.pdf # 1-based 页码范围 python3 pdf_split.py input.pdf --pages "2-3" --rotate 90 -o part.pdf python3 pdf_split.py input.pdf --pages "1-2" --compress -o part.pdf- 页码范围语法为
1-3,5,9-(1-based、含端点,9-表示到末页),parse_pages会校验越界并报错(退出码 2); --rotate必须是 90 的倍数(顺时钟旋转,作用于被提取页);--compress对内容流做 deflate 压缩——源码注释明确这是适度压缩,不会重压图片;- 加密输入需
--password,否则退出码 3。
9. 水印与盖章:pdf_watermark.py / pdf_stamp.py
两个脚本都基于 pypdf 的page.merge_page()实现叠加,区别在于:
| 脚本 | 能力 | 适用场景 |
|---|---|---|
| pdf_watermark.py | 把另一个单页 PDF 的第 1 页叠到输入每一页 | 整本文档的"机密/DRAFT"水印 |
| pdf_stamp.py | 用 reportlab 在内存中构建文字或图片叠加层,按坐标盖到选定页 | "在此签名"箭头、斜向 DRAFT 横幅、页角标签 |
# 整本覆盖水印(--under 表示垫在正文下面) python3 pdf_watermark.py in.pdf --stamp stamp.pdf -o out.pdf python3 pdf_watermark.py in.pdf --stamp stamp.pdf -o out.pdf --under # 指定页盖章:文字 python3 pdf_stamp.py in.pdf -o out.pdf --text "DRAFT" --x 200 --y 400 \ --font-size 60 --rotation 45 --opacity 0.3 --color "#cc0000" # 指定页盖章:图片(签名图) python3 pdf_stamp.py in.pdf -o out.pdf --image sig.png --x 400 --y 60 \ --width 120 --pages 3pdf_stamp.py的关键参数(均以 PDF 点为坐标,原点在页面左下角):
--text与--image互斥必选其一;文字可用--font(默认 Helvetica)、--font-size(默认 24)、--color(#RRGGBB);--rotation逆时针旋转角度;--opacity0.0–1.0(依赖 reportlab 的setFillAlpha/setStrokeAlpha,老版本会自动忽略);--pages支持1-3,5语法,默认全部;只给图片--width时用 PIL 读原图保持宽高比;- 按页面尺寸缓存叠加层(
overlay_cache),同尺寸页面复用同一 overlay,避免重复构建; --under将盖章垫到内容之下(等价于背景水印)。
测试覆盖了文字盖章(验证仅目标页出现标记)、旋转+透明盖章(因旋转字形会干扰 pdfplumber 断行,改用 pypdf 提取验证)以及图片盖章(通过对比 XObject 图片数量断言仅目标页新增图片)。
10. 元数据与附件:pdf_meta.py
pdf_meta.py 提供五种互斥模式:
python3 pdf_meta.py in.pdf --set-meta -o out.pdf --title "New" --author "me" \ --subject "Testing" --keywords "alpha, beta" python3 pdf_meta.py in.pdf --clear-meta -o out.pdf # 清除全部 DocInfo 元数据 python3 pdf_meta.py in.pdf --attach payload.txt -o out.pdf # 嵌入文件附件 python3 pdf_meta.py in.pdf --list-attachments # 列出附件名 python3 pdf_meta.py in.pdf --extract-attachments ./dir # 抽出全部附件源码中的两个重要实现事实:
- 元数据写入的是经典 DocInfo 字典(Title/Author/Subject/Keywords)。源码注释明确:如果 PDF 中还有 XMP 元数据,本脚本不会重写它,高级阅读器可能显示不一致——这是文档化限制;
- 附件操作走 pypdf 的
attachments接口,--extract-attachments会自动建目录并用os.path.basename清洗文件名防止路径穿越。
写模式(--set-meta/--clear-meta/--attach)必须有-o,否则退出码 4。
11. 加密与解密:pdf_secure.py
pypdf 加密脚本 pdf_secure.py 使用AES-256算法:
# 加密:用户口令 + 所有者口令(都从文件读取,避免出现在命令行/日志) python3 pdf_secure.py in.pdf --encrypt -o enc.pdf \ --user-password-file user.txt --owner-password-file owner.txt # 解密:用已知口令移除加密 python3 pdf_secure.py enc.pdf --decrypt -o dec.pdf --password-file pw.txt口令传入有两种方式:--user-password/--owner-password/--password(直传,但会暴露在进程参数中)与对应的--*-file(从 UTF-8 文件读取并去除首尾换行)。加密时若未提供 owner 口令,默认与 user 口令相同;对已加密文件再次加密、对未加密文件执行解密都会报错。
技能文档特别强调的安全事实:PDF 加密时写入的权限标志(禁止打印/复制等)只是建议性的(advisory)——阅读器可能遵守,但任何 PDF 库都可以剥离它们;真正拦截内容访问的只有 user 口令。因此不要把这些权限标志描述为强访问控制。测试test_encrypt_decrypt_roundtrip验证了"加密→--meta报告 encrypted=true→解密→文本可读"的完整回路。
12. AcroForm 表单工作流(技能核心)
表单是本文档的技术重心,完整规格说明见 references/forms.md。其关键设计是:同一份 JSON spec 同时驱动布局校验(pdf_form_layout.py)与表单构建(pdf_make_form.py)。
12.1 表单规格(spec)格式
坐标单位为PDF 点,原点在页面左下角(1 pt = 1/72 英寸;A4 = 595.27 × 841.89,letter = 612 × 792)。完整示例:
{ "title": "Example Intake Form", "author": "example-author", "page_size": "A4", "page_count": 1, "fields": [ {"name": "surname", "type": "text", "page": 1, "label": "Surname", "label_box": [72, 700, 150, 714], "entry_box": [160, 696, 400, 716], "value": "", "tooltip": "Family name"}, {"name": "agree", "type": "checkbox", "page": 1, "label": "I agree", "label_box": [72, 660, 150, 674], "entry_box": [160, 658, 176, 674], "checked": false}, {"name": "color", "type": "radio", "page": 1, "label": "Color", "label_box": [72, 620, 150, 634], "entry_box": [160, 616, 400, 636], "options": ["red", "blue"], "value": "blue"}, {"name": "size", "type": "dropdown", "page": 1, "label": "Size", "label_box": [72, 580, 150, 594], "entry_box": [160, 576, 300, 596], "options": ["small", "large"], "value": "small"} ] }字段约束:
page_size:"A4"、"letter"或[width, height](点数数组);page_count:可选,会自动扩展为所有字段的最高页码;- 盒子均为
[x0, y0, x1, y1]且要求x0 < x1、y0 < y1; label作为静态文本绘制在label_box附近;省略label(及label_box)即为无标签字段;radio:选项按钮在entry_box内从左到右布局,每选项一个槽位并带小型静态说明文字;value按导出名预选;dropdown映射为 AcroForm choice(combo)字段。
12.2 字段类型与填充值格式
forms.md 给出了 spec 类型、PDF 内部类型(/FT)与填充后值的对应表:
| Spec 类型 | /FT(pdf_read.py --fields 报告的类型) | 填充后的值 |
|---|---|---|
| text | /Tx(text) | 字符串本身 |
| checkbox | /Btn(button) | /Yes或/Off |
| radio | /Btn(button) | /<export>,如/red |
| dropdown | /Ch(choice) | 选项字符串 |
用pdf_fill_form.py填充时的取值规则:checkbox 接受true/false;radio 值需要带前导斜杠(如"/red");dropdown 值为纯选项字符串。
12.3 布局校验(pdf_form_layout.py)与视觉审查循环
pdf_form_layout.py 在构建之前校验 spec,逐字段检查:
- 盒子必须良构且在页面边界内;
- entry box 最小 8×8 pt(text/dropdown 最小高度 12 pt);
- 同一页上任意两个 entry box 不得重叠(重叠簇中第二个及以后的字段会被标记);
- label 必须位于其 entry box 150 pt 以内且不得与 entry box 重叠。
退出码0 = 全部通过,1 = 至少一个问题;JSON 报告逐字段给出problems。源码常量即文档规则本身:MIN_W=8.0、MIN_H=8.0、MIN_TEXT_H=12.0、MAX_LABEL_GAP=150.0。文档建议:先 lint 再构建——改 JSON 里的数字比调试渲染出的 PDF 便宜得多。
视觉审查循环(重点):
python3 scripts/pdf_form_layout.py spec.json --render-overlay overlay.png [--pdf built.pdf]- 红色矩形= entry box(标注字段名),蓝色矩形= label box;
- 不带
--pdf时在空白页上绘制叠加层(纯 PIL,永远可用);带--pdf时会把真实页面栅格化垫在底层(需要 pypdfium2 或 pdftoppm,否则报告"rendered": false并给出安装提示); - 用
view_image加载 PNG,专门询问碰撞、对齐和游离标签等问题。
测试中test_form_layout_detects_problems精确验证了越界、重叠、标签过远、过小四类问题的检出,且确认重叠簇中第一个字段仍报 clean(by_name["one"]["ok"])——这正对应"第二个及以后字段被标记"的规则。
12.4 表单创建(pdf_make_form.py)
pdf_make_form.py 消费同一份 spec,用 reportlab 的canvas.acroForm构建真实 AcroForm:
python3 pdf_make_form.py formspec.json -o built_form.pdfpage_count不足时按字段最大页码自动扩展(page_count = max([page_count, *by_page.keys()]));- 四类字段分别映射到
form.textfield、form.checkbox、form.radio、form.choice,统一borderWidth=0.5, forceBorder=True; - radio 的按钮尺寸按
min(eh, ew/len(options)*0.5, 16)计算,槽位宽ew/len(options); - 输出 JSON 报告
{"output", "pages", "fields": [{name, type, page}]}。
12.5 表单填充(pdf_fill_form.py)
pypdf 填充脚本 pdf_fill_form.py 从扁平 JSON 读取字段值:
python3 pdf_fill_form.py form.pdf --fields-json values.json -o filled.pdf python3 pdf_fill_form.py form.pdf --fields-json values.json -o flat.pdf --flattenvalues.json 形如{"surname": "Smith", "agree": true, "color": "/red", "size": "large"}。源码实现要点:
- checkbox 布尔值归一化:pypdf 无法可靠地把裸
True映射到 on-state,脚本会读取字段的/_States_,取第一个非/Off状态作为 on-state(默认回退/Yes),true → on-state、false → /Off; - 字段名不存在时跳过并警告而非失败(
Warning: fields not found in form, skipped: [...]); - 显式设置NeedAppearances,让符合规范的阅读器在缺少外观流时也能重新生成字段外观;
--flatten:把字段标记为只读(flags=1)并尝试合并外观;失败时警告"output keeps interactive fields"并继续输出——源码注释提醒异形 widget 需视觉验证。
12.6 radio 组的已知怪癖(reportlab + pypdf)
forms.md 记录了三条经过测试证实的坑:
- reportlab 要求每个 radio 组至少两次
radio()调用,单选项 radio 组会产生损坏字段; - 预选在构建期通过
"value"完成;之后用pdf_fill_form.py改选需要斜杠导出名("/red"); - 部分阅读器在 pypdf 填充后对 reportlab radio 的外观渲染不一致——必须用
--fields(数据真相)+ 渲染页面图(视觉真相)双重验证,不能只看其一; - flatten radio 组是最不可靠的展平场景——交付前务必检查输出图片。
测试test_make_form_fill_roundtrip验证了构建→填充→复读的完整回路(radio 值/red、checkbox 值/Yes、dropdown 值large全部断言通过),且test_make_form_lists_all_fields验证 label 文本是绘制在页面上的真实文本(extract_text()能读到 "Surname"/"Size"),而非仅存于控件内。
13. 密码与敏感 PDF 的安全准则
技能文档给出四条硬性安全规则(Agent 执行时必须遵守):
- 不要把 PDF 口令写进聊天、日志或命令行参数;
- 把用户提供的口令写入带限制权限的临时文件,通过
--password-file、--user-password-file或--owner-password-file传入,操作完成后删除该临时文件; - PDF 权限标志只是建议性的(见第 11 节),不得描述为强访问控制;
- 未经用户授权,不得把敏感 PDF 内容发送到外部服务(例如不得擅自把保密文档交给第三方 OCR 或解析 API)。
14. 渲染与视觉验证:pdf_page_image.py
pypdf 页面渲染脚本 pdf_page_image.py 是工作流第 4 步(渲染审查)的支撑工具:
python3 pdf_page_image.py input.pdf --pages 1-3,5 --dpi 150 --out-dir rendered --prefix page python3 pdf_page_image.py input.pdf --pages 1-2 --out-dir imgs # 默认全页、150 DPI--pages支持1-3,5,9-语法(1-based、含端点,9-到末页),越界页码报错退出码 4;- 输出按
{prefix}{页码:03d}.png命名(如page001.png);无栅格后端时返回{"rendered": false, "missing": [...]}而不是崩溃; - 典型用途:视觉模型验证渲染效果,以及把纯图片(扫描)页导出后转交
ocr-and-documents技能; - 测试还验证了 A4 页面 72 DPI 渲染尺寸约 595×842 px。
15. 质量保障:端到端测试一览
tests/test_pdf_skill.py 无需网络,用临时目录串联全部脚本,构成了本技能的"验收清单",也适合作为 Agent 自测参考:
| 测试 | 验证内容 |
|---|---|
| test_create_and_meta / test_extract_text / test_extract_tables | 创建 2 页报告、页脚页码、标题元数据、文本与表格抽取、CSV 落盘 |
| test_form_fill_unicode_roundtrip | 填充含西里尔字母与引号的 Unicode 文本并复读验证 |
| test_merge_split_rotate | 合并 4 页 + 书签,拆分 2-3 页旋转 90°,--meta断言 rotation |
| test_watermark | 每页都出现 DRAFT 水印 |
| test_encrypt_decrypt_roundtrip | 加密→encrypted=true→解密→文本可读 |
| test_compress | --compress后文本仍完整 |
| test_make_form_* | 表单字段齐全、label 落页、构建后填充复读 |
| test_form_layout_valid_spec / test_form_layout_detects_problems | lint 通过/四类问题检出、重叠簇首字段不误报 |
| test_form_layout_overlay* / test_page_image_export | 叠加层渲染(有/无栅格后端两种分支)、页面导出尺寸 |
| test_stamp_* | 文字盖章仅命中目标页、旋转+透明、图片盖章仅目标页新增 XObject |
| test_meta_set_and_clear / test_attachments_roundtrip | 元数据设置与清除、附件嵌入/列出/抽取 |
16. 最佳实践与常见坑小结
综合 SKILL.md、forms.md 与源码,可沉淀如下工程经验:
- 先检查、再变换、后验证:任何变换前先
pdf_read.py摸清页数/加密/字段,变换后必须复读 + 渲染图片双重确认; - 新文件优先:默认输出新文件,不覆盖源 PDF,除非用户明确要求;
- 表单先 lint 再 build:用
pdf_form_layout.py校验盒子的边界、尺寸、重叠与标签配对,修改 JSON 比调试渲染结果便宜; - radio 用斜杠导出名填充:
"/red"而非"red";单选 radio 组是 reportlab 的雷区,尽量避免; - 渲染后端按需安装:pypdfium2 优先、poppler-utils 兜底,缺后端时脚本会优雅降级返回
rendered: false,据此提示用户而非中断任务; - 口令走文件、用完即删:任何口令参数都不要直传命令行;
- OCR 只在文本提取为空时启用,且交给
ocr-and-documents技能,避免在pdf技能内重复造轮子; - 所有输出都是机器可读 JSON(stdout)+ 明确的退出码,非常适合 Agent 工具链自动解析与分支处理。
这套工具链让 Ekko Agent 可以在本地完成从"读取一份陌生 PDF"到"生成并填充一份 AcroForm 表单"再到"加密交付"的完整闭环,且全程不依赖外部网络服务,符合本地优先(local-first)的工作区理念。
- AI 应用
- 人工智能
- AI Agent
- 本地部署
- 前端
- 后端
- 工作流自动化
【免费下载链接】ekko-studio
Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.
相关推荐
Codewhale PDF 技能指南:面向 Agent 的 PDF 读取、处理与验证工作流
Codewhale PDF 技能指南:面向 Agent 的 PDF 读取、处理与验证工作流 Codewhale 是一个用 Rust 构建的开源终端编码智能体,其
人工智能AI Agent代码智能体CLI工具调用MCP Clientspypdf 表单字段模型详解:基于 `pypdf.generic.Field` 读取与操作 PDF 交互式表单
pypdf 表单字段模型详解:基于 pypdf.generic.Field 读取与操作 PDF 交互式表单 pypdf 作为纯 Python 的 PDF 处理库
后端FastMCP 技能库 PDF 处理实战:基于 pypdf 的文本提取、表单填写与文档合并
FastMCP 技能库 PDF 处理实战:基于 pypdf 的文本提取、表单填写与文档合并 本文是 FastMCP 技能示例体系中 pdf processing
人工智能MCP 服务MCP Clients工具调用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考