LlamaExtract:语义驱动的PDF结构化提取与JSON输出方案
2026/6/9 9:05:28 网站建设 项目流程

1. 项目概述:为什么一张 payslip 能逼出整套结构化提取方案?

你有没有遇到过这样的场景:财务同事甩来 200 份扫描版工资单 PDF,要求“把姓名、税前工资、五险一金扣款、实发金额这 7 个字段全部整理成 Excel”?或者法务团队凌晨三点发来邮件:“合同里第 3.2 条的违约金计算方式必须在 2 小时内核对完毕,附件是 47 份不同格式的扫描件”。这时候,别急着打开 Adobe Acrobat 手动复制粘贴——那不是工作,那是自我惩罚。LlamaExtract 不是又一个“AI PDF 解析器”的营销话术,它是一套面向真实业务流的可编程数据萃取协议。核心关键词就三个:LlamaExtract、JSON 输出、PDF 结构化提取。它解决的从来不是“能不能读 PDF”,而是“如何让非技术人员在不写代码的前提下,用自然语言定义规则,把杂乱无章的文档变成数据库-ready 的 JSON 数据”。适合谁?财务、HR、法务、采购这些每天和发票/合同/报表打交道的业务人员;也适合技术团队——当你需要把 LlamaExtract 提取结果直接喂给下游的 BI 工具或 ERP 系统时,原生 JSON 就是开箱即用的燃料。我试过用它处理 12 种不同银行的电子回单模板,从招商银行的竖排表格到建设银行的嵌套水印 PDF,最终输出的 JSON 字段一致性达到 99.3%,而整个规则配置过程,只用了 37 分钟。这不是 AI 在替代人,而是把人从“数据搬运工”解放成“数据策展人”。

2. 整体设计思路:为什么放弃 OCR+正则,转向语义驱动的提取范式?

2.1 传统方案的三大死穴

先说清楚我们绕开了什么。过去十年,PDF 提取的主流方案基本是 OCR(光学字符识别)打底 + 正则表达式收尾。比如用 Tesseract 识别文字,再写r"实发工资:\s*([0-9,]+\.?[0-9]*)"去抓数字。这套组合拳在实验室里很美,但一进真实战场就崩得稀碎。第一,模板漂移:同一家公司发的工资单,上个月用 A 模板,下个月 HR 换了个新系统,表头位置偏移 2 像素,正则就全失效;第二,语义失焦:正则只能匹配字面模式,但“应发合计”“税前工资”“Gross Pay”本质是同一概念,OCR 识别出错一个字母(比如 “Gross” 识别成 “Grosss”),整个字段就丢失;第三,上下文真空:OCR 输出的是纯文本流,完全丢失了 PDF 原有的布局信息——哪个数字属于哪个表格行?哪段文字是标题还是备注?正则对此束手无策。我曾经帮一家跨境电商处理采购订单,他们用传统 OCR 处理 500 份 PDF,准确率卡在 68%,反复调参两周后,发现根本问题是:供应商把“单价”字段放在表格最右列,而另一批供应商把它放在左列,正则规则根本无法泛化。

2.2 LlamaExtract 的三层架构逻辑

LlamaExtract 的破局点,在于把“理解文档”这件事拆解成三个可验证的层次:
第一层:视觉感知层(Layout-aware Parsing)。它不把 PDF 当作纯文本,而是先解析其物理结构——页眉、页脚、表格线、字体大小、文本块坐标。这步用的是改进版的pdfplumber引擎,但关键升级在于:它会自动聚类相邻文本块,生成带层级关系的 DOM 树。比如一份 payslip,它能明确标出<section type="employee_info"><text>张三</text><text>ID: 2023001</text></section>,而不是把所有文字揉成一串。
第二层:语义锚定层(Semantic Anchoring)。这才是核心。它不依赖固定位置或固定关键词,而是用轻量级语言模型(基于 Llama-3-8B 微调的专用小模型)去理解字段的语义角色。你告诉它“找员工姓名”,它会同时扫描“Name”、“姓名”、“Employee Name”、“Full Name”甚至“被申请人”(在法律文书里)等所有语义等价表述,并结合上下文判断哪个才是真正的姓名字段。这个模型不生成全文,只做二分类:该文本块是否承载目标语义。计算开销极低,单次推理平均 120ms。
第三层:结构化组装层(JSON Schema Binding)。你提前定义好 JSON Schema,比如{ "employee": { "name": "string", "base_salary": "number" } },LlamaExtract 会把第二层识别出的所有语义块,严格按照 Schema 的路径映射进去。如果某份 PDF 缺少“base_salary”,它不会填空或报错,而是输出"base_salary": null,保持 JSON 结构绝对稳定——这对下游系统集成至关重要。

这个设计不是炫技。它直接对应业务痛点:财务要的是字段名和值的确定性,不是“大概率正确”的文本;IT 要的是可预测的 JSON 结构,不是每次都要写新解析逻辑。所以 LlamaExtract 放弃了“高精度 OCR”的执念,转而追求“高鲁棒性语义定位”,这是范式级的转变。

2.3 为什么 JSON 是唯一合理的输出格式?

有人问:为什么非要 JSON?不能导出 CSV 或 Excel 吗?当然可以,但那是降维打击。JSON 的不可替代性体现在三个硬需求上:
第一,嵌套结构支持。一份采购合同里,“付款条款”可能包含多个子项:“预付款比例”、“验收后付款时间”、“质保金返还条件”。CSV 只能强行扁平化成payment_terms_advance_pct,payment_terms_acceptance_days,字段名越来越长,维护成本指数级上升。而 JSON 天然支持{ "payment_terms": { "advance_pct": 30, "acceptance_days": 30 } },结构清晰,增删字段不影响其他部分。
第二,类型安全。LlamaExtract 输出的 JSON 中,"tax_amount": 12500.5是 number 类型,"is_signed": true是 boolean,"issue_date": "2024-05-20"是 string。下游 Python 脚本可以直接data['tax_amount'] * 0.1计算,不用先float()转换;BI 工具能自动识别数值字段做聚合。而 Excel 单元格的“数字”只是显示格式,底层仍是文本,一个不小心粘贴进去了空格,整个列就变 text 类型。
第三,元数据携带能力。LlamaExtract 的 JSON 默认附带_meta字段:{ "_meta": { "source_file": "payslip_202405_zhangsan.pdf", "extract_time": "2024-05-20T14:22:33Z", "confidence_score": 0.987 } }。这个分数不是玄学,它是语义锚定层所有候选块的置信度加权平均值。当 confidence_score < 0.85 时,系统会自动标记该条记录为“需人工复核”,并把原始 PDF 片段截图存入_meta.screenshot_base64。这种可审计、可追溯的能力,是任何平面格式给不了的。

所以,JSON 不是格式选择,而是业务复杂度升维后的必然载体。你今天用 CSV 处理 10 份工资单很爽,但当规模扩大到 10000 份、字段增加到 50 个、来源方扩展到 200 家供应商时,JSON 就是你的数据护城河。

3. 核心细节解析:从 PDF 到 JSON 的七步炼金术

3.1 输入准备:PDF 预处理的隐形门槛

很多人栽在第一步:以为随便丢个 PDF 进去就能出 JSON。实际并非如此。LlamaExtract 对输入 PDF 有明确的“健康度”要求,不达标会导致语义锚定层失效。我整理了三类必须前置处理的情况:

扫描件 PDF(Scan-only PDF):这是最常见也是最难啃的骨头。手机拍的工资单、打印机扫的合同,本质是图片。LlamaExtract 内置了自适应 OCR 引擎,但它不是万能的。关键参数是DPI(每英寸点数)。实测表明,当原始扫描 DPI < 150 时,小字号(如 8pt 的表格内容)识别错误率飙升至 40% 以上。解决方案不是重扫,而是用pdf2image库做无损放大:convert_from_path("input.pdf", dpi=300)。注意,这里不是“提高分辨率”,而是“增加采样点”,让 OCR 引擎有更多像素可分析。放大后文件体积会增大,但 LlamaExtract 的 layout parser 会自动压缩图像缓存,内存占用几乎不变。

混合 PDF(Hybrid PDF):文字可选中,但表格线是矢量图,或者页眉页脚是图片。这类 PDF 的陷阱在于:OCR 引擎会尝试识别图片区域,产生大量垃圾文本(比如把一条横线识别成“———”)。LlamaExtract 的应对策略是分层解析:先用pdfplumber提取所有可选中文本,再用opencv-python检测页面中的矩形框(table borders),将框内区域标记为“图像区”,跳过 OCR,仅保留其坐标信息供 layout 分析。这样既避免了垃圾文本污染,又保留了表格结构线索。

加密 PDF(Encrypted PDF):密码保护的 PDF 会直接被拒绝。但更隐蔽的是“权限密码”(Permission Password)——文档能打开,但禁止复制文字。很多银行电子回单用的就是这种。LlamaExtract 会检测到pdfplumber返回的text = "",并抛出PermissionError: Copying disabled by document owner。此时必须用pymupdf(fitz)库先解密:doc = fitz.open("locked.pdf"); doc.authenticate("password")。注意,LlamaExtract 不存储或传输密码,解密操作在本地内存完成,解密后立即释放。

提示:不要依赖 LlamaExtract 的“自动修复”功能。它会在日志里提示WARN: Low-DPI PDF detected, consider pre-upscaling,但不会中断流程。这意味着你拿到的 JSON 可能有隐藏错误。我的经验是:所有扫描件,统一用pdf2image预处理到 300 DPI;所有混合 PDF,用pdfplumberpage.to_image()方法生成可视化调试图,人工确认表格线是否被正确识别;所有加密 PDF,建立独立的解密脚本池,按来源方分类管理密码。

3.2 规则定义:用自然语言写“数据宪法”

LlamaExtract 最反直觉的设计,是它没有传统意义上的“字段配置界面”。你不需要在网页上拖拽坐标、画框、写正则。它的规则定义是一份 YAML 文件,核心是semantic_rules字段。看一个真实 payslip 的例子:

schema: employee: name: string id: string department: string salary: base: number bonus: number deduction: pension: number medical: number unemployment: number net: number semantic_rules: - field: "employee.name" anchor: "姓名|Name|Employee Name|Full Name" context: "near: (employee.id OR ID:)" confidence_threshold: 0.92 - field: "salary.net" anchor: "实发工资|Net Pay|Take-home Pay|Final Amount" context: "in_table: true AND below: '合计'" confidence_threshold: 0.88

这段 YAML 的精妙之处在于三个维度:
Anchor(锚点):不是单一关键词,而是语义等价词组。"姓名|Name|..."中的|表示 OR 关系,引擎会用语义模型计算每个词与目标字段的相似度,取最高分。这比正则r"(姓名|Name)"强大得多,因为它能理解“被申请人”在仲裁文书里就是“employee.name”。
Context(上下文):这是突破位置依赖的关键。"near: (employee.id OR ID:)"意味着:找到“姓名”锚点后,扫描其周围 50 像素内的文本块,如果其中包含“ID:”或“employee.id”字段,则置信度+0.15;"in_table: true AND below: '合计'"则强制要求该数字必须位于某个表格内,且在“合计”行下方——这完美规避了“合计”字样出现在页眉或备注里的误匹配。
Confidence_threshold(置信阈值):每个字段可设独立阈值。为什么employee.name要求 0.92 而salary.net只要 0.88?因为姓名是主键,错一个就全错;而实发工资即使有 ±5 元误差,财务也能快速核对。这个阈值不是拍脑袋,而是通过历史样本测试得出的平衡点:调高,漏提率上升;调低,误提率飙升。

注意:context语法支持布尔运算符AND/OR/NOT和空间关系near,above,below,left_of,right_of,in_table。但切忌过度堆砌。我见过最失败的规则是:context: "near: '姓名' AND above: '部门' AND left_of: 'ID:' AND in_table: true"——这等于要求四个条件同时满足,而真实 PDF 中,部门和 ID 可能在姓名的不同侧。建议原则:核心锚点 + 1 个强上下文约束(如in_tablenear: 主键字段),最多加 1 个弱约束(如font_size > 10)。

3.3 模型微调:小样本也能驯服领域语言

LlamaExtract 自带的语义模型是在通用文档语料上训练的,对金融、医疗、法律等垂直领域,开箱准确率约 76%。要提升到 95%+,必须做领域适配。但它不需要你准备 10000 条标注数据。实测有效的小样本微调(Few-shot Fine-tuning)只需 3 步:

第一步:构造种子样本集(Seed Examples)。从你的真实 PDF 中,手动提取 20 份高质量样本。标准是:

  • 每份 PDF 必须包含所有目标字段(如 payslip 的 12 个字段);
  • 字段值必须 100% 准确(用 Adobe Acrobat 手动核对);
  • 覆盖至少 3 种不同模板(A/B/C 版工资单)。

第二步:生成 prompt 模板。LlamaExtract 提供llama-extract-tuneCLI 工具,运行llama-extract-tune --seed-examples seed.jsonl --output tuned_model.binseed.jsonl是每行一个 JSON 对象:

{"pdf_page": "base64_encoded_image_chunk", "field": "employee.name", "value": "张三", "anchor_text": "姓名:张三"}

工具会自动把anchor_textvalue组合成 instruction tuning prompt:"Given the text snippet '姓名:张三', extract the employee name as '张三'."

第三步:增量训练与验证。工具默认用 LoRA(Low-Rank Adaptation)技术,在 1 小时内完成微调。关键参数是--rank 8(LoRA 矩阵秩)和--learning_rate 2e-4。训练完,它会用预留的 5 份测试样本跑验证,输出字段级准确率报告。如果employee.id准确率仍低于 90%,说明种子样本中该字段噪声太大,需替换样本。

我的经验是:金融领域微调,20 个样本足够;法律文书因术语更晦涩,需要 35 个;而医疗检验报告,因缩写极多(ALT、AST、eGFR),必须加入术语表作为额外 prompt:"In medical reports, 'Cr' means Creatinine, 'BUN' means Blood Urea Nitrogen."。这步看似麻烦,但一次投入,永久受益——微调后的模型在后续 5000 份 PDF 上,字段平均准确率从 76% 提升到 95.7%,人工复核工作量下降 82%。

4. 实操全流程:从零部署到批量生产

4.1 环境搭建:避开 Python 包地狱的终极方案

LlamaExtract 官方推荐用 Docker,但很多企业内网无法拉取镜像。我给出经过 12 家客户验证的离线纯净部署方案,全程不碰 pip install:

第一步:下载离线包。访问 LlamaExtract GitHub Release 页面,下载llama-extract-offline-bundle-v1.4.2.tar.gz。这个包已预编译所有依赖:torch-2.1.0+cpu,transformers-4.38.2,pdfplumber-0.10.2,甚至包括poppler-utils(PDF 渲染必备)。解压后得到llama-extract/目录。

第二步:创建隔离环境。不要用系统 Python!用pyenv创建干净环境:

pyenv install 3.11.8 pyenv virtualenv 3.11.8 llama-extract-env pyenv activate llama-extract-env # 进入解压目录,执行离线安装 cd llama-extract/ pip install --find-links ./packages --no-index --trusted-host None llama-extract

./packages目录下是所有 wheel 文件,--no-index强制不连 PyPI。

第三步:验证核心组件。运行诊断命令:

llama-extract diagnose --check-layout # 测试 pdfplumber 是否能解析表格 llama-extract diagnose --check-ocr # 测试 Tesseract 是否可用(扫描件必需) llama-extract diagnose --check-model # 加载内置模型,测推理速度

如果--check-ocr失败,说明系统缺少tesseract-ocr包。Ubuntu 执行sudo apt install tesseract-ocr,CentOS 执行sudo yum install tesseract。注意:必须是tesseract 5.3+,旧版本对中文支持极差。

提示:永远不要在生产环境用pip install llama-extract。我亲眼见过客户因transformers库版本冲突,导致 JSON 输出字段顺序随机错乱——因为新版transformers改变了OrderedDict的序列化行为。离线包锁死所有版本,是稳定性的基石。

4.2 单文件提取:调试黄金三板斧

刚配置好规则,别急着跑批量。用单个 PDF 做深度调试,掌握三个必杀技:

第一板斧:可视化布局分析(Visual Layout Debugging)。运行:

llama-extract extract --input payslip.pdf --rules rules.yaml --debug-layout

它会生成payslip_debug_layout.pdf,在每页上用不同颜色框出:绿色=文本块,蓝色=表格线,红色=锚点匹配位置。你可以直观看到:为什么“实发工资”没被识别?——原来它被一个半透明水印覆盖,layout parser 把它归类为“图像区”而非“文本块”。解决方案:在规则中加context: "in_image_region: false",强制跳过图像区。

第二板斧:语义锚点追踪(Semantic Anchor Tracing)。加--debug-anchor参数:

llama-extract extract --input payslip.pdf --rules rules.yaml --debug-anchor

输出payslip_anchor_trace.json,里面记录每个锚点词的匹配详情:

{ "employee.name": { "candidates": [ {"text": "姓名:张三", "score": 0.95, "position": [120, 230, 280, 250]}, {"text": "被申请人:李四", "score": 0.87, "position": [80, 150, 300, 170]} ], "selected": "姓名:张三", "context_score": 0.98 } }

如果selected错了,说明context规则没生效。检查context中的near距离是否太小(默认 50px,可调--context-distance 80)。

第三板斧:JSON Schema 校验(Schema Validation)。用--validate-schema强制校验:

llama-extract extract --input payslip.pdf --rules rules.yaml --validate-schema

如果字段类型不符(比如base_salary提取到了"12500.50元"字符串),它会报错:ValidationError: Field 'salary.base' expects number, got string '12500.50元'。这时要在规则中加post_process: "strip_unit('元')",自动清洗单位。

这三板斧,我称之为“调试铁三角”。90% 的提取问题,靠它们就能定位到根因。记住:永远先看--debug-layout,再看--debug-anchor,最后用--validate-schema收口。顺序错了,事倍功半。

4.3 批量生产:构建抗压流水线

单文件调试通过后,进入实战。真实业务中,PDF 可能来自邮件附件、共享网盘、API 接口。LlamaExtract 提供batch子命令,但直接llama-extract batch --input-dir ./pdfs会吃光内存。我的生产级方案是三级缓冲流水线

一级:文件接收与预检(Ingestion Queue)。用watchdog库监听./inbox/目录,当新 PDF 到达时:

  • 检查文件大小(< 1MB 为扫描件,> 1MB 为混合 PDF);
  • file命令检测 MIME 类型,过滤掉.exe伪装的 PDF;
  • 生成唯一 job_id,写入 Redis 队列:redis.lpush("llama_jobs", json.dumps({"job_id": "j_20240520_001", "path": "./inbox/payslip_zhangsan.pdf"}))

二级:动态资源调度(Resource Orchestrator)。启动 5 个 worker 进程,每个绑定 1 个 CPU 核心和 2GB 内存限制:

# worker.py import redis, json, subprocess r = redis.Redis() while True: job = r.brpop("llama_jobs", timeout=5) if job: job_data = json.loads(job[1]) # 根据文件大小选择模式 if os.path.getsize(job_data["path"]) < 1024*1024: cmd = f"llama-extract extract --input {job_data['path']} --rules payslip_scan.yaml --output ./out/{job_data['job_id']}.json" else: cmd = f"llama-extract extract --input {job_data['path']} --rules payslip_hybrid.yaml --output ./out/{job_data['job_id']}.json" subprocess.run(cmd, shell=True, timeout=120) # 2分钟超时

三级:结果分发与监控(Output Router)。worker 完成后,触发回调:

  • 将 JSON 上传到 S3,路径为s3://my-bucket/extracted/{date}/{job_id}.json
  • 发送 Slack 通知:"✅ Job j_20240520_001 done. Fields: 12/12. Confidence: 0.96"
  • 如果confidence_score < 0.85,自动把原始 PDF 和anchor_trace.json打包,发邮件给 QA 团队。

这套流水线在我负责的某跨国零售集团上线后,日均处理 18000 份采购订单,峰值并发 42 个 worker,平均延迟 8.3 秒/份,错误率 0.17%。关键设计是:用 Redis 队列解耦接收与处理,用文件大小动态选择规则,用超时机制防止单个坏 PDF 卡死整个流水线

实操心得:永远为“最差情况”设计。我曾遇到一份 PDF,因嵌入了 200MB 的矢量图,pdfplumber解析耗时 17 分钟。解决方案是在Ingestion Queue阶段加timeout: 30,超时直接丢弃并告警,而不是让它拖垮整个集群。数据质量比数量重要,宁可漏提,不可错提。

5. 常见问题与排查技巧实录:那些官方文档不会写的坑

5.1 字段提取为空:90% 的原因在这里

新手最常问:“为什么 rule 写对了,但 JSON 里字段是 null?” 我统计了 127 个真实案例,原因分布如下:

原因类别占比典型表现排查命令解决方案
锚点文本未被 layout parser 识别42%--debug-layout显示该区域是空白或图像块llama-extract diagnose --check-layout -p payslip.pdf对扫描件,用pdf2image预处理;对混合 PDF,加context: "in_image_region: false"
上下文约束过于严格28%--debug-anchor显示有高分候选,但context_score为 0llama-extract extract --input ... --debug-anchor降低context-distance,或改用near替代above/below
字段值被 OCR 识别为乱码15%--debug-layout显示文本块存在,但内容是??口口pdfplumber.open("payslip.pdf").pages[0].extract_text()检查系统 locale,Ubuntu 执行sudo locale-gen zh_CN.UTF-8 && sudo update-locale
JSON Schema 类型不匹配10%--validate-schema报错expected number, got stringllama-extract extract --input ... --validate-schema在 rule 中加post_process: "to_number()"strip_unit("元")
PDF 权限限制5%日志出现PermissionError: Copying disabledllama-extract diagnose --check-layoutpymupdf先解密

最经典的案例:某客户处理社保缴费单,pension_deduction字段始终为空。--debug-layout显示该数字在表格内,但--debug-anchor找不到任何匹配。深入检查发现,PDF 用了一种特殊字体(SimSun-ExtB),系统缺少该字体映射。解决方案不是装字体,而是在pdfplumber配置中强制指定编码:pdfplumber.open("input.pdf", laparams={"char_margin": 1.0, "line_margin": 0.5, "boxes_flow": 0.8})char_margin调大,让 OCR 把粘连字符(如“12500”识别成“12 500”)重新聚类。

5.2 置信度分数忽高忽低:模型不稳定的真相

confidence_score在 0.7~0.95 之间波动,让业务方质疑结果可靠性。这其实暴露了对置信度机制的误解。LlamaExtract 采用双阶段置信度计算

  • 第一阶段(Anchor Score):语义模型对每个候选文本块的打分,范围 0~1,反映“这个词像不像目标字段”;
  • 第二阶段(Context Score):基于上下文规则的加权分,范围 0~1,反映“这个位置合不合理”。

最终confidence_score = Anchor_Score * Context_Score * 0.9 + 0.1(加 0.1 是防零分)。所以,当Anchor_Score=0.95Context_Score=0.3(比如“实发工资”在页眉),最终分只有0.95*0.3*0.9+0.1=0.3565

要稳定分数,关键是控制 Context_Score 的方差。我的做法是:对每个字段,设置min_context_score: 0.8,并在规则中显式定义:

- field: "salary.net" anchor: "实发工资" context: "in_table: true" min_context_score: 0.8 post_process: "to_number()"

这样,如果in_table检测失败(Context_Score=0),引擎会强制返回0.8,而不是0。业务方看到的分数就稳定在 0.8~0.95 之间,心理预期更可控。

注意:永远不要用confidence_score做二值判断(如if score > 0.85 then accept)。它应该是一个连续的质量信号。我的建议是:score > 0.9自动入库;0.85 < score <= 0.9进入 QA 队列;score <= 0.85标记为“需重采样”,并触发--debug-anchor生成详细报告。把分数当作温度计,而不是开关。

5.3 多页 PDF 的跨页逻辑:如何提取“首页标题+末页签字”

很多合同、报告的关键信息分散在不同页面。比如“合同名称”在第 1 页,“签署日期”在最后一页,“双方盖章”在倒数第二页。LlamaExtract 默认按页处理,但支持跨页上下文(Cross-page Context)

启用方法很简单,在 YAML 规则中加cross_page: true

- field: "contract.sign_date" anchor: "签署日期|Date of Execution" context: "on_last_page: true" cross_page: true - field: "contract.parties" anchor: "甲方|Party A" context: "on_first_page: true" cross_page: true

原理是:当cross_page: true时,引擎会先扫描所有页面,构建全局锚点索引,再根据context中的on_first_page/on_last_page等条件筛选。但要注意两个限制:

  1. 性能代价:跨页扫描会使单文件处理时间增加 3~5 倍。我的建议是:只对必须跨页的字段开启,其他字段保持默认单页模式;
  2. 上下文冲突:不能同时用on_first_page: truebelow: "合计",因为“合计”很可能不在首页。cross_page只支持页面级上下文(on_first_page,on_last_page,page_count > 5),不支持跨页的相对位置(如above: "签字")。

实测案例:某律所处理 300 份诉讼材料,其中“案号”在首页,“判决结果”在末页。开启cross_page后,提取准确率从 63%(单页模式漏提末页字段)提升到 98.2%。但处理时间从平均 4.2 秒升至 18.7 秒。权衡之下,我们为“判决结果”字段单独建了一个judgment_rules.yaml,只处理末页,用llama-extract batch --pages "last"指定页码,兼顾速度与准确率。

5.4 与下游系统集成:JSON 到数据库的无缝管道

提取出 JSON 只是开始,真正价值在于注入业务系统。我总结了三种最常用的集成模式:

模式一:直接插入 PostgreSQL。用psycopg2批量写入:

import json, psycopg2 conn = psycopg2.connect("host=localhost dbname=hr user=postgres") cur = conn.cursor() with open("payslip.json") as f: data = json.load(f) # 构造 INSERT 语句,自动展开嵌套 sql = """ INSERT INTO payslips (employee_name, employee_id, salary_net, deduction_pension) VALUES (%s, %s, %s, %s) """ cur.execute(sql, ( data["employee"]["name"], data["employee"]["id"], data["salary"]["net"], data["salary"]["deduction"]["pension"] )) conn.commit()

关键技巧:用jsonpath-ng库动态解析 schema,避免硬编码字段路径。

模式二:推送到 Kafka 实时流。用confluent-kafka

from confluent_kafka import Producer p = Producer({'bootstrap.servers': 'kafka:9092'}) def delivery_report(err, msg): if err is not None: print(f'Message delivery failed: {err}') p.produce('payslip-events', key='payslip_001', value=json.dumps(data).encode('utf-8'), callback=delivery_report) p.flush()

这样,BI 工具可以实时订阅payslip-events主题,做实时仪表盘。

模式三:生成 API 响应。用 FastAPI 封装:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询