跳到正文
Databricks Blog·· 2 小时前AI 评分36

用 ai_extract 与 MLflow Prompt Registry 提升文档抽取准确率:从局部修补到可量化收益

From spot fixes to measured gains: improving document extraction accuracy with ai_extract and MLflow Prompt Registry

AI 导读

Databricks 分享了一套基于 ai_extract 与 MLflow Prompt Registry 的文档抽取改进流程,用于从数十种 PDF 版式中抽取约 40 个结构化字段。

正文 · AI 翻译

文档提取通常从一个简单的循环开始:发现一个错误的字段,调整提示词或 schema,重新运行文档,然后检查结果。

随着字段和文档格式的增多,这种方法会变得不可靠。一次改动可能修复了一个字段,却在其他地方引入了回归;可能对一种版式有效,却在另一种版式上失败;或者仅仅因为 LLM 多次运行之间的正常波动而显得更好。

我们在最近的一个项目中看到了这一点:该项目从合作伙伴提交的订单文档中提取大约 40 个结构化字段,这些文档涵盖数十种 PDF 版式。审核人员在提取值进入下游之前对其进行纠正和批准,为我们提供了经过人工审核的评估基准真值。

挑战不在于让 ai_extract 返回这些字段,而在于在不降低系统整体表现的前提下改进提取。

例如,revision 字段有时会返回 ORIGINAL 之类的状态词,而不是 revision 数字或空白。一个小的 schema 改动将该字段的错误减少了大约一半。但在所有字段上,总提取错误增加了 9%。

教训很明确:改进一个字段并不一定改进整个提取系统。

该流水线已经具备了系统化评估改动所需的大部分组件。文档落入对象存储,并通过使用 Auto Loader 的 Lakeflow SDP 流水线流转。Bronze 层使用 ai_parse_document 解析文档,Silver 层使用 ai_extract 提取结构化字段,Gold 层将结果发布为物化视图。预测结果同步到 Lakebase,在那里一个 Databricks App 让审核人员纠正和批准字段,Lakebase CDC 将这些批准结果返回湖仓。

Document Extraction Pipeline
图 1. 带人工审核的文档提取流水线。提示词和 schema 硬编码在 Silver 层中,而审核人员的纠正尚未用于评估提取改动。

反馈循环中缺少两个部分。审核人员的批准结果已经可用,但尚未被组装成可复用的评估数据集。同时,提示词和 schema 直接嵌入在流水线代码中,因此即使对提取做很小的改动也需要代码部署。

我们通过围绕三个问题构建工作流来解决这两个问题:我们应该改什么?应该如何进行版本管理?以及需要多少证据才能将其推广?

本文涵盖该工作流的三个部分:

  • 用正确的手段改进 ai_extract:使用 schema 处理字段规则,使用全局指令处理文档逻辑,使用精确模式处理困难结构,并使用引用诊断错误。
  • 对提示词和 schema 进行版本管理:将两者存储在 MLflow Prompt Registry 中,并使用别名来部署或回滚配置,而无需编辑流水线代码。
  • 推广前先评估:在固定的、经人工审核的基准真值上比较候选方案,跟踪准确率、覆盖率和运行间波动。

这些步骤共同构成了一条从提议的提取改动到生产的可重复路径,并有证据表明该改动改进了整个系统。

从 Genie Code 开始

本文的其余部分将介绍我们推荐的做法以及每种做法为何重要。你不必自己实现所有这些做法。用于文档处理的 Genie Code 会构建 Lakeflow 作业(ai_parse_document、ai_classify 以及带引用和置信度分数的 ai_extract),并通过评估与爬山循环自动化该工作流的核心部分:

  • Measure:逐字段将当前抽取结果与你标注的表格进行评分,从而看出哪些字段较弱。
  • Improve:提出修复方案,通常是更精确的字段描述,以可审阅的 diff 形式呈现,由你批准后再应用。
  • Re-evaluate:每次接受更改后重新运行评估集,并展示更改前后的结果,使回归与改进同样可见。

例如,你可以向 Genie Code 提问:“针对我标注的表格评估抽取结果,并告诉我哪些字段最弱”,接着问“revision 字段的准确率可以更高。迭代改进 schema 和指令,重新运行评估,并向我展示更改前后的结果。”

理解基本原理仍然很重要。它有助于你判断 Genie Code 提出的更改、决定结果何时足够好可以投入生产,以及将工作流扩展到生产环境,例如在 MLflow Prompt Registry 中对提示词和 schema 进行版本管理。下面每一节都会说明一项推荐实践,并指出 Genie Code 如何为你处理它。

选择正确的 ai_extract 杠杆

ai_extract 接受三个输入:解析后的文档、JSON schema 以及可选的全局指令。

schema 和指令的用途不同:

  • Schema:定义输出字段以及各字段专属的抽取规则。
  • 全局指令:提供适用于整个文档的指导。

结果是一个结构化值,每个 schema 字段对应一个条目。你可以直接访问各个字段,例如:extracted:response.revision.value。

将字段专属规则放入 schema。

从 schema 结构开始。当某个字段具有一组封闭的有效值时,使用 enum 约束,而不是在描述中列出允许的值。数字和整数使用数值类型,重复结构使用嵌套数组或对象。

然后使用 schema 描述来表达仅靠类型无法表达的规则,例如:

  • 保留标识符中的前导零,例如 004
  • 排除状态词,例如 ORIGINAL、NEW 或 REVISED
  • 在第一个无效字符处截断标识符
  • 当值不存在时返回 null

关键:将字段规则保留在 schema 中,可以让抽取契约更易于检查,并防止全局提示词变成一堆互不相关的例外。

使用全局提示词进行文档级推理

将全局提示词保留给适用于整个文档的规则,包括:

  • 文档或领域上下文
  • 来源优先级,例如页眉优先于页脚
  • 跨字段一致性规则
  • 将多行合并为数组的指令
  • 不应作为来源的文档区域

例如,如果实体名称被错误地从样板页脚或第三方联系信息块中抽取,可以用 FORBIDDEN-SOURCES 指令告诉模型忽略这些区域。

一条有用的规则是:如果规则定义的是某个字段,就把它放入 schema。如果规则定义的是如何解释文档,就把它放入提示词。

用 Genie Code 自动化:爬山循环遵循同样的原则。它提出的修复方案通常是让 schema 中的字段描述更精确,并以 diff 形式展示,供你在应用前批准。

在下游保持确定性格式化。

并非所有转换都需要 LLM。在可能的情况下,使用 SQL 或其他确定性转换来处理大小写、分隔符移除、零填充以及诸如 TELEVISION 到 TV 的映射。

关键:使用模型来解释文档;使用代码来规范化输出。

对于结构性问题,使用精度模式。

有时指令很清晰,但文档难以遵循。大型模式、重复的行项目、复杂的表格和多页布局会使数值难以与正确的行对齐。

对于这些情况,ai_extract 2.1 版本提供了精度模式:

精度模式 为更严苛的用例带来了前沿的提取能力。在提示和模式保持不变的情况下,它往往在重复结构上最有帮助,并减少过度提取(模型在正确答案应为空的地方返回了一个值)。

在更改配置之前先诊断故障:

  • 如果模型误解了字段的含义,请更改提示或模式。
  • 如果它理解字段但难以处理数据结构,请测试精度模式。

这些方法是互补的。在我们的测试中,模式规则纠正了模型误解的字段,而精度模式则改善了从复杂表格中提取字段的效果。

使用引用(citations)来理解字段为何出错。

当提取的值错误时,引用有助于显示模型在哪里找到了答案:

引用提供了字段级边界框,可以映射回源文档中的元素跨度。这有助于区分两种不同类型的错误:

  • 提取错误: 模型选择了错误的区域、误解了字段,或返回了源不支持的数值。
  • 标注分歧: 模型从正确的位置提取,但预期答案遵循不同的审核者约定。

这些需要不同的修复方法。提取错误可能需要更改模式或提示。标注分歧应通过在审核者指南中定义预期约定来解决。

置信度分数也有助于确定人工审核的优先级:

将它们用作排序信号,例如先审核低置信度字段,而不是作为自动质量门控。高置信度的结果仍可能是错误的。

在 Genie Code 中:Genie Code 构建的提取任务包含引用和置信度分数。你可以要求它“根据评估结果为每个字段设置置信度阈值,并将低置信度行路由到审核”,并使用文档解析 UI 来可视化调试解析和提取。

将提示和模式一起进行版本管理

通过将提示和模式作为可配置、可复用的定义管理在管道代码之外,减少在 notebook 中硬编码提示和模式的摩擦。一个小的措辞更改需要代码部署,以前的版本难以重建,并且没有证据表明新版本比旧版本更好。

MLflow Prompt Registry 允许你将这些输入与管道代码分开进行版本管理。同时为提示和模式进行版本管理: 字段描述对提取的影响与全局指令一样。如果只对提示进行版本管理,那么你一半的提取逻辑仍然作为未跟踪的代码更改在传播。

超越 Genie Code 循环:Genie Code 帮助你找到合适的提示词和 schema。当你将该配置投入生产时,对其进行版本控制可以让你在不部署代码的情况下提升和回滚变更。

一个简单的别名策略可能使用:

  • @prod 用于当前已批准的版本
  • @candidate 用于正在评估的版本
  • @scr_* 用于并行比较的实验版本

然后流水线按别名加载活动版本:

随后,解析后的提示词和 schema 被传递给 ai_extract。

当候选版本获批后,mlflow.genai.set_prompt_alias 可以将 @prod 移动到新版本。流水线在下次运行时解析它,无需更改流水线代码。要回滚,请将两个别名恢复到之前记录的版本。底层版本仍保留在注册表中。

这将提取配置与运行它的代码分离。图 2 还补充了另一个缺失的部分:可复用的基准真值。

Silver reads approved prompts and schemas
图 2. Silver 从注册表读取已批准的提示词和 schema。审核者的签核汇入基准真值视图。

基准真值视图将已批准的标签与源文档和预测配对,创建用于衡量变更的参考数据集。

针对固定基准真值评估候选版本。

版本历史告诉你发生了什么变化。它不会告诉你变化是否更好。从审核者批准的结果中构建一个固定队列;我们的队列是数百份 PDF,涵盖广泛的布局。针对同一测试集对生产提示词和每个候选提示词进行评分。

对于每个版本,跟踪以下指标:

  • 整体和字段级准确率
  • 基准真值覆盖率
  • 按文档类型的性能
  • 运行间差异

在比较中保持文档和指标定义一致。否则,分数变化可能反映的是不同的测试集或评分规则,而不是不同的提取配置。

在 Genie Code 中:将 Genie Code 指向你的带标签表格,例如图 2 中的基准真值视图,并要求它评估提取结果。它对每个字段进行评分,并在每次接受的更改后重新运行相同的评估集,因此前后比较使用相同的文档。下面的噪声底、覆盖率和判定检查是投入生产时值得添加的有用护栏。

考虑正常的 LLM 波动

候选版本一次得分略高并不意味着它更好。在冻结的测试集上至少运行生产配置 3-5 次,并计算其平均准确率和标准差 (σ)。该 σ 就是你的噪声底。你的策略可能要求候选版本的改进超过 2σ 才将其视为有意义;该范围内的任何结果都是不确定的,而不是胜利。

在跟踪准确率的同时跟踪覆盖率

如果忽略缺失的预测,准确率也可能具有误导性。

假设你只对模型和审核者都提供了值的字段进行评分。一个将困难字段留空的候选版本可能看起来更准确,因为这些遗漏从分母中消失了。反过来也可能发生。当我们仅在审核者提供了 revision 值的文档上对 revision 字段进行评分时,我们的修复似乎将准确率提高了不到 3 个百分点。但当我们计算所有错误时,包括正确答案为空的情况,该修复将 revision 错误减少了大约一半。大部分改进来自于在不应有值的地方正确返回无值。

这就是为什么覆盖率应与准确率一同报告。要明确定义它,例如,将其定义为模型对非空真值字段返回预测的比例。如果准确率上升但覆盖率显著下降,在将候选版本提升之前先进行调查。

由此产生的测试框架遵循一个可重复的流程:冻结测试集、建立生产基线、在筛选别名上注册候选版本、运行重复评估,并比较准确率、覆盖率和稳定性。

Evaluation Sequence
图 3:评估流程:冻结测试集、测量生产基线、筛选候选版本,并比较准确率、覆盖率和稳定性。

将每次评估总结为一个判定结论。

一旦你测量了准确率、覆盖率和运行间波动,你的评估框架就可以将这些指标归纳为少量结果:

  • 更好:准确率超过预定义阈值,覆盖率保持稳定,且结果稳定。
  • 可疑:准确率提升,但覆盖率显著下降。
  • 不稳定:多次运行之间结果波动过大。
  • 无定论:差异太小,无法与正常波动区分。
  • 更差:准确率明显下降。

这些判定结论提供了一种一致的方式,在决定是否将候选版本提升到生产环境之前总结评估结果。

追踪修订变更的后续影响

我们在同一测试集上对每个候选版本评分三次,统计每个字段中的错误,并与原始提示词和 schema 进行对比。

变更修订错误所有字段的错误判定结论
revision 仅修复 schema-49%+9%TARGET_IMPROVED_WITH_REGRESSION
重构提示词和 schema-57%-16%BETTER
仅启用精确模式,未更改提示词或 schema-10%-24%BETTER
重构提示词和 schema + 精确模式-64%-32%BETTER

这个针对性修复在 revision 上达到了我们的预期,但模型开始在无关的明细字段(如日程详情)中编造值。编辑一个字段的描述并不是单字段实验:模型会整体读取 schema。

所以我们没有发布它。

相反,我们将字段特定规则从全局提示词中移出,放入 schema 描述中,包括相同的 revision 规则。提示词最终缩短到不足原始长度的一半,整体错误下降了 16%,其中 estimate 错误下降了 30%。

有两个字段变差了,它们被列入下一轮的目标清单。

精确模式则讲述了另一个故事。在未更改提示词或 schema 的情况下,它将错误减少了 24%,主要是通过不再在重复的明细字段中编造值。但它几乎没有改变 revision 错误,因为那是一个定义问题,只有 schema 规则才能修复它。

两者结合可以叠加:在重构后的提示词和 schema 之上启用精确模式,又消除了剩余错误的 19%。总体而言,错误下降了约三分之一(32%),而 revision 错误下降了近三分之二(64%)。

这正是 Genie Code 的循环旨在暴露的回归。因为每个被接受的变更都会重新运行完整的评估集,一个有助于修订但损害明细字段的针对性修复会出现在前后对比结果中,而不是在生产环境中。

MLflow
图 4. 每个候选版本的三次运行与 MLflow 中的原始配置并列。每次运行都会记录其提示词版本、schema 版本和引擎,因此每个结果都能追溯到产生它的确切配置。

将评估转化为晋升决策。

结论只是起点,而非最终决定。

早先的一个候选版本将估计误差降低了约三分之一,但总体结论仅为 INCONCLUSIVE。不过它也开始在几乎每份文档上填写一个是/否标志,而审核人员只在少数文档上标记了该字段。

即使聚合指标看起来可以接受,某个字段的严重退化也可能阻止晋升。因此,晋升应始终由人工控制。

如果候选版本被归类为 BETTER,负责人可以更新 @prod 别名,并记录候选版本、评估指标、作者和时间戳。

也可能存在正当理由来晋升一个 INCONCLUSIVE 变更,例如实现一项重要的业务约定,而该约定未被聚合准确率完全捕获。在这些情况下,记录覆盖原因可以保留决策背后的理由。

通过该审核和别名更新,反馈闭环得以闭合:

Fresh Pipeline Initialization Loads
图 5. 完整闭环:审核人员的批准成为真实基准,测试框架评估候选版本,负责人晋升已批准的提示词与模式对。新的流水线初始化会加载新版本。

构建可重复的提取工作流。

可靠的文档提取不在于找到一个完美的提示词。而在于选择适当的变更、跟踪变更内容,并测试结果是否有所改善。

借助 ai_extract,这意味着使用模式描述来实现字段级行为,使用全局指令来实现文档级推理,在结构成为瓶颈时使用精确模式,在需要理解提取失败原因时使用引用。

随后,MLflow Prompt Registry 为您提供管理提示词和模式的版本化路径,而固定的评估测试集则为您提供决定什么应该进入生产的证据。

这些部分共同将提示词迭代从一系列一次性编辑转变为可重复的工程工作流。

用于文档处理的 Genie Code 自动化了该闭环的核心(测量、改进、重新评估),因此大多数团队可以从那里开始,然后随着流水线进入生产,添加 Prompt Registry 版本控制、噪声下限检查和晋升门控。

浏览 Databricks AI Functions 文档 以了解有关 ai_extract 的更多信息,并查看 MLflow Prompt Registry 文档 以了解提示词版本控制和别名管理。试用用于文档处理的 Genie Code。从 Jobs and Pipelines 打开 Genie Code,将其指向您的文档和带标签的表,并要求它评估和改进您的提取。

来源:Databricks Blog · databricks.com