教程中心进阶
进阶

用 AI 智能体做文档结构化抽取:PDF / 网页 → 表格

2026.07.19· 6 个步骤 · 18 分钟阅读· 📑 结构化抽取

合同、发票、研报、竞品官网——里面全是关键信息,但都是「人读得懂、机器读不动」的非结构化文本。你每天花大量时间把 PDF 里的字段一个一个敲进表格。本教程教你用 AI 智能体把任意文档抽成整齐的结构化表格:定义字段、调用抽取工具、批量跑、校验落库。这一步也是上一节 Firecrawl 抓取之后最自然的下游。

📑 本教程适合:需要把大量文档/网页转成表格的同学——财务对账、合同归档、竞品参数对比、研报摘要。需要一点点 Python 基础(不会也能看思路)。

先搞懂:什么是「结构化抽取」

大白话:你给模型一张「表格模板」(叫 schema),它负责把文档里对应内容填进格子里。和「让模型随便总结」不同,抽取要求固定字段、可校验、可入库

方式输出能不能直接进数据库
自由总结一段文字不能,还得人再整理
结构化抽取固定字段的 JSON能,直接转 DataFrame/表

抽取得稳,关键在 schema 写得够具体。「价格」这种字段要写明单位、币种、是否为区间,否则模型会自由发挥,下游表格就乱了。

Step 1:定义你的字段 schema

1 先画「表格模板」

以「从发票 PDF 抽字段」为例,schema 越细越稳:

schema = {
  "发票号码":   "字符串,形如 INV-2026-001",
  "开票日期":   "YYYY-MM-DD 格式日期",
  "销售方":     "公司全称",
  "金额_含税":  "数字,单位人民币元,不含『元』字",
  "税率":       "数字,如 0.13 表示 13%",
  "明细行数":   "整数"
}
💡 给每个字段写「格式约束」比写「描述」更有用。模型对格式敏感,对语义容易自由发挥。

Step 2:把 PDF / 网页读成文本

2 先拿到干净的原文

PDF 用解析库,网页可复用上一节的 Firecrawl:

# PDF → 文本
from pypdf import PdfReader
text = "\n".join(p.extract_text() for p in PdfReader("发票.pdf").pages)

# 网页 → 文本(用 Firecrawl 拿干净 Markdown)
# 见本中心《Firecrawl 实战》教程
markdown = app.scrape_url(url, params={"formats":["markdown"]})["markdown"]

扫描件/图片型 PDF 抽不出文字,需要先做 OCR(如 PaddleOCR / 云 OCR)。纯文本 PDF 才能直接抽。

Step 3:调用模型的「结构化输出」能力

3 让模型按 schema 吐 JSON

主流模型都支持「JSON / 函数调用」模式,强制输出符合 schema:

from openai import OpenAI
client = OpenAI()

resp = client.chat.completions.create(
  model="gpt-4o-mini",
  messages=[
    {"role":"system","content":"你负责从文档抽取字段,只返回 JSON。"},
    {"role":"user","content": f"字段要求:{schema}\n文档:{text}"}
  ],
  response_format={"type":"json_object"}  # 强制 JSON
)
import json
record = json.loads(resp.choices[0].message.content)
🔑 关键技巧:response_format=json_object 是「防跑题」的开关。没有它,模型可能回你一段解释+JSON 混在一起,下游解析就崩。

Step 4:批量处理一堆文档

4 一个文件夹批量变表格
import pandas as pd, glob, os

rows = []
for f in glob.glob("invoices/*.pdf"):
    text = read_pdf(f)
    rec = extract(text, schema)      # Step 3 的函数
    rec["_来源文件"] = os.path.basename(f)
    rows.append(rec)

df = pd.DataFrame(rows)
df.to_excel("发票汇总.xlsx", index=False)

批量一定要加「来源文件名」列——哪条记录出问题,你才追得回去。别只存结果,丢了溯源。

Step 5:校验与纠错,别全信模型

5 抽完必须过一道关

模型偶尔会幻觉出不存在的字段值。加一层规则校验:

def validate(rec):
    errs = []
    if not rec["发票号码"].startswith("INV"):
        errs.append("发票号码格式异常")
    if not (0 <= rec["税率"] <= 1):
        errs.append("税率超出 0~1 范围")
    if rec["金额_含税"] <= 0:
        errs.append("金额为非正数")
    return errs

# 有问题的单独导出人工复核
bad = [r for r in rows if validate(r)]
if bad:
    pd.DataFrame(bad).to_excel("待复核.xlsx", index=False)
🛡️ 重要:结构化抽取是「自动化第一道」,金额、法律条款这类高风险字段务必留人工复核通道。全自动 ≠ 零校验。

Step 6:把它封装成 Agent 的「抽取工具」

6 让 Agent 自己决定抽什么

和 Firecrawl 一样,把抽取包成 Tool,Agent 接到文档就能自动结构化:

@tool
def extract_document(text: str, schema: str) -> str:
    """把一段文档文本按给定 schema 抽成 JSON 字段。"""
    return json.dumps(extract(text, eval(schema)))

# 组合拳:Firecrawl 抓取 → extract_document 抽取 → 写库
# 一个「竞品参数对比 Agent」就这么成型了

小结

结构化抽取是 Agent 从「会聊天」走向「会干活」的关键一步:把散落的文档变成可查询、可分析、可入库的表格。记住三件事——schema 写细、输出锁 JSON、结果必校验。配合《Firecrawl 实战》抓取、《Agent 反馈闭环》持续优化,你的文档处理流水线就闭环了。