← 返回目录
p7.2动手⏱ 约 16 分钟

LLM 结构化抽取:让输出可校验,而不是让模型更聪明

四道关卡拦下 12 次调用里的 9 次;其中「幻觉」这一类,靠一次字符串包含检查就全挡住了

🔎 最后验证 2026-07📚 来源:四类输出 × 四道关卡的拦截实测,2026-07-31 于 ECS 实测,输出见 code/outputs/stdout.txt🧰 Python 3.12.13(离线 mock,不调用任何 API)
为什么学这个

这一节的对象不是模型,是管线

理由很直接:模型半年一换,而校验管线不用重写。

四道关卡跑一遍,12 次调用拦下 9 次:

▸ 合格输出              3 篇  ✅ 全部通过
▸ 少了 evidence 字段    3 篇  ❌ 关卡一拦下
▸ 幻觉:引用了原文没有的句子  3 篇  ❌ 关卡二拦下
▸ 数值与引用对不上       3 篇  ❌ 关卡三拦下

其中最危险的那一类——模型编造了一个原文里根本没有的数字——靠的只是一次字符串包含检查

把"编造数字"变成可自动拦截的错误,不需要更聪明的模型,只需要要求它附上原文依据。

💡 打个比方

报销时要求"附发票原件"。

不是因为财务比你更懂这笔钱花在哪,而是因为"有没有这张发票"是一个机械可查的事实。

evidence 字段就是那张发票。

一、四道关卡

关卡检查什么拦住什么说明
是不是合法 JSON格式崩坏结构化输出模式能基本消除这一关
JSON Schema:字段名/类型/必填/枚举字段缺失、类型错、单位越界能挡结构问题,挡不住内容编造
evidence 必须逐字出现在原文里幻觉挡幻觉最有效的一关,且完全机械
value 能否在自己的 evidence 里找到单位错、抄错位数值一致性
字段间的逻辑关系净利润 > 营业收入这类矛盾挡"看起来都对但合不拢"

关卡二的实现只有三行:

for f in obj.get("fields", []):
    if f.get("evidence") and f["evidence"] not in text:
        issues.append(f"「{f['name']}」的 evidence 不在原文中")
⚠️ 避坑

关卡二是这套管线的核心,而它常常被跳过。

原因是它"看起来太笨":只做字符串包含检查,不理解语义。

但正因为,它才可靠:

  • 不依赖模型的能力——再强的模型也躲不过"这句话原文里有没有"
  • 不会随模型升级而失效
  • 零成本——不需要第二次调用去"检查第一次"

用 LLM 检查 LLM,是一个很自然但很贵的想法:成本翻倍,而且第二个模型也会幻觉

能用字符串比较解决的,不要用模型解决。

二、每道关卡各自拦住了什么

实测(每类故障 3 篇文档):

故障类型被哪一关拦下拦截率
少了 evidence 字段关卡一 Schema3/3
引用了原文没有的句子关卡二 原文引用3/3
数值与引用对不上关卡三 数值一致3/3
合格输出0/3(全部通过)

这张表的用处不是"拦截率 100%",而是告诉你哪一关不能省。

去掉关卡一,少字段的会漏过去;去掉关卡二,幻觉会直接进入数据库

🔧 动手做:让 LLM 的输出必须先过关,才算数(5 分钟)

python extract_pipeline.py。它对 LLM 抽取的结果设了四道关卡(必填字段、引用必须能在原文找到、类型校验、范围校验),故意喂进 12 次问题输出。

你会看到(实测):四道关卡共拦下 12 次里的 9 次。其中最要命的“幻觉引用”(模型编了一段原文里没有的话),被关卡二一次字符串包含检查全挡住了(3/3)

想明白:对付 LLM 出错,靠的不是"换个更聪明的模型",是让它的输出可被机械校验——每条结论必须带上能在原文里定位的证据,不能定位就打回。校验管线不用随模型升级重写,这才是能沉淀下来的资产。别让 AI 的产出直接进你的系统,先让它过关。

三、成本控制:拦截 ≠ 白跑

被拦截的输出要重试,而重试是要花钱的——这就是 p7.1 计算器里那 10%。

三条能显著降本的做法:

  1. 把 Schema 写进请求(结构化输出模式),而不是靠提示词描述 → 关卡一失败率大降
  2. 只重试失败的字段,而不是整篇重来 → 重试成本降一个量级
  3. 失败样本存档:同一类失败反复出现,说明是提示词问题,不是模型问题

没有存档,你会一直在为同一个提示词缺陷付重试的钱。

四、管线拦不住的那部分

四道关卡全过,仍然可能是错的:

  • evidence 是原文的句子,但抽错了字段(把去年的数当成今年的)
  • 数值对得上,但漏抽了同样重要的另一条
  • 文档类型判错,导致下游用错口径

所以管线之外必须有固定比例的人工抽检,并且:

  • 抽检结果要记录,用来估计真实错误率(而不是凭感觉)
  • 抽到的错误要回灌成新的校验规则或测试用例——这就是 p4.10 的回归测试思路

能自动化的部分自动化,不能自动化的部分抽样——而不是假装它不存在。

五、换成真实 API 时要改什么

只改一个函数。

def fake_llm(doc_id, mode):     # ← 换成真实调用
    ...
# 下面的四道关卡,一行都不用改

真实调用版本还要补三件事(都与模型无关):

做什么为什么
落盘存证记录模型名、版本、温度、提示词哈希、原始返回p7.1 的"确定性"维度;p1.5 的血缘纪律
限频退避429 时指数退避,而不是死循环重试p7.1 的"速率与并发"维度
提示词版本化提示词进 git,改了就升版本否则无法解释"为什么上个月的结果不一样"
❓ 测验
你打算用「让第二个模型检查第一个模型的输出」来防幻觉。本节会怎么评价这个方案?
✏️ 填空
防幻觉最有效也最机械的一关是:要求模型附上 evidence,并检查它是否 ___ 出现在原文里。

📌 免责:本课为技术教学,样本公告为虚构主体(甲/乙公司),不涉及任何真实上市公司;不构成投资建议。

✅ 小结

四件事:

  1. 这一节的对象是管线,不是模型——模型半年一换,校验管线不用重写。换真实 API 时只改一个函数,四道关卡一行不动
  2. 四道关卡实测拦下 12 次调用里的 9 次:关卡一挡字段缺失、关卡二挡幻觉(3/3)、关卡三挡数值对不上;合格输出 3/3 全部通过
  3. 关卡二是核心,而它常被跳过——因为它"看起来太笨"。但正因为笨才可靠:不依赖模型能力、不随模型升级失效、零成本能用字符串比较解决的,不要用模型解决
  4. 管线拦不住的部分要靠抽检:抽错字段、漏抽、类型判错都能过四关。抽检结果要记录用于估计真实错误率,抽到的错误要回灌成新规则或测试用例(p4.10 的回归测试思路)

成本上三条降本做法:Schema 写进请求 · 只重试失败字段 · 失败样本存档没有存档,你会一直为同一个提示词缺陷付重试的钱。

下一节换一个方向:不用 LLM,用经典机器学习直接预测涨跌——并且诚实呈现结果

下一节 → sklearn 预测涨跌:诚实呈现结果
🔎 来源与核验· 1 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「四类输出 × 3 篇文档 = 12 次调用,四道关卡共拦截 9 次、通过 3 次;其中「少 evidence 字段」由关卡一全部拦下(3/3)、「幻觉引用」由关卡二全部拦下(3/3)、「数值与引用对不上」由关卡三全部拦下(3/3)」
📚 本节 code/extract_pipeline.py,2026-07-31 于 ECS 实测(离线 mock,不调用任何 API),输出见 code/outputs/stdout.txt✓ 已核验 2026-07
样本公告为虚构主体(甲/乙公司),结构仿真实公告格式,不涉及任何真实上市公司;mock 模型仅用于演示校验管线,不代表任何真实模型的错误率。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p7.3
用 sklearn 预测涨跌:9 个模型里 6 个跑不过基线
继续读下一节 →