31_Document处理通用Skill:多格式抽取+结构化输出+校验层

几乎所有企业级 Agent 做到后面,都会撞上一堵同样的墙:你以为真正困难的是"让模型更聪明",结果真正把系统拖垮的,往往是输入根本不干净。现实世界里的输入不是规整 JSON,而是 PDF、Word、Excel、扫描件、图片、网页截图、邮件附件,甚至是一份页码顺序错乱、表格被拆碎、印章压住文字的合同。很多团队前面已经把某个垂直场景写得像模像样,一旦接上真实文档流,立刻退回原始状态: 把整份材料塞给大模型,祈祷它能自己看懂、自己提取、自己别胡编。

这种做法在演示里偶尔能过,在生产里基本注定翻车。原因并不复杂。文档处理不是一个"前置小步骤",它本身就是一层独立的基础能力。如果你没有把抽取、结构化、校验拆开,下游所有法务审查、投研摘要、教育批改、设计走查,都会建立在一层不稳定的泥地上。前面写得越漂亮,后面摔得越狠。

这篇文章要解决的,不是"怎么让 Agent 读文件",而是"怎么把文档输入做成一个所有业务都能复用、还能追责的通用 Skill"。重点不在炫某种 OCR 或解析器,而在于把系统边界讲清楚:抽取层负责把原始文档拆成稳定的中间表示,结构化层负责映射到业务 schema,校验层负责回答下游最关键的问题: 这份结果到底能不能信。

一、先把几个常见误区打掉

1.1 文档处理不是 OCR 一下就结束

很多人对文档处理的理解停留在"先 OCR,再让模型总结"。这句话听起来没错,问题是它故意跳过了最关键的一层: 从可读文本到可用结构,中间隔着非常长的一段工程距离。

把字识别出来,只说明你把像素变成了字符;它并不等于你知道哪一列是金额、哪一段是免责条款、哪一页的表格延续到下一页、哪个字段其实缺失、哪个数字因为扫描倾斜识别错了一位。真正有业务价值的,从来不是"看见了文本",而是"抽出了字段、保留了来源、识别了缺口、拦住了风险"。

所以文档处理的目标不该写成"把内容读出来",而应该写成"把文档变成后续系统能消费、能追溯、能验证的输入对象"。这两个目标差了一个生产系统的距离。

1.2 多格式支持的难点不在能不能读,在怎么统一输出

PDF 的问题是页面和块结构复杂;Word 的问题是样式语义和实际内容常常不一致;Excel 的问题是单元格关系和跨表头语义;扫描件和图片的问题则是 OCR 置信度和版面恢复。它们都能"读",但读出来的内部表示完全不同。

下游 Skill 不可能为每种文档单独适配一套消费逻辑。法务审查不会希望知道这一段来自 PDF 的 text block,投研系统也不该关心这个数字是 Excel 单元格还是网页表格抄来的。下游只关心统一对象: 字段是什么、值是什么、证据在哪、是否完整、是否可信。

所以多格式支持真正的难点,是把不同来源收敛到统一 schema,而不是简单罗列"支持 PDF/Word/Excel/JPG"。只会读很多格式,不代表系统强;能把很多格式读成一套一致输出,才叫能力层。

1.3 没有校验层,抽取层越强,系统死得越快

这句话听起来有点反直觉,但它很重要。很多团队会把预算和精力都砸在抽取层,想方设法提高识别率、召回率、字段覆盖率。结果抽得越多,风险越大。因为一份错误的结构化输出,比一份"我看不懂"更危险。

看不懂的输入,至少会拦住流程;看错但自信输出的结果,会被下游系统当真,会进入评分、审批、报价、审查、决策链路。这时候问题就不再是单个 Skill 出错,而是错误开始系统性传播。

所以真正成熟的文档处理系统,核心竞争力不是"抽得多",而是"知道哪些能信,哪些不能信,哪些必须退回人工复核"。这也是为什么校验层不能省,它不是附加功能,而是整个输入底座的可信边界。

二、文档处理为什么必须拆成三层

2.1 抽取层的职责:生成中间表示,而不是直接给业务结论

抽取层最容易被误写成"大而全"。很多团队会让第一层既负责 OCR,又负责抽字段,又负责判断风险,最后还顺手给个摘要。这样写出来的东西短期看很省事,长期一定不可维护。

抽取层应该足够克制。它只做一件事: 把原始文档拆成稳定的中间表示。这个表示至少要包含文本块、表格块、基础元信息、版面定位,以及解析失败标记。它不是业务结论,也不应该去猜业务语义。它的价值是把原始混乱输入,变成一个后续层可以复用的、相对稳定的对象。

一个更像样的抽取层定义,大概会长这样:

yaml 复制代码
name: document_ingestion_parser
description: >
  读取多种文档格式,输出统一的文本块、表格块、元信息和页码定位。
inputs:
  - raw_document
  - file_type
  - parsing_profile
outputs:
  - text_blocks
  - table_blocks
  - metadata
  - location_map
  - parse_warnings
constraints:
  - 解析失败页必须显式标记
  - 图片 OCR 置信度低时不得伪装成正常文本
  - 表格合并失败时必须保留原始块结构
  - 不得在本层输出业务结论字段

这里最关键的不是 text_blocks,而是 location_mapparse_warnings。前者让你后面能追溯"这句话来自哪一页哪一块";后者让你知道"这部分内容并不是稳定解析出来的"。如果这两个信息没有留下,后面任何漂亮的结构化输出都只是看起来很像真的。

还有一个容易被忽略的问题是解析配置。不同文档类型、不同业务模板,往往需要不同的 parsing profile。比如发票、合同、财报、课程大纲,对表格和段落的敏感点不一样。抽取层如果不允许按文档类型配置解析策略,最后就会退化成一套模糊规则硬套所有输入,准确率迟早掉到底。

2.2 结构化层的职责:把内容落到业务 schema 里

抽取层完成后,很多团队会兴奋地说"好了,后面让大模型从文本块里自己找答案"。这恰恰是第二次偷懒。因为业务系统真正需要的,不是"文档大概在说什么",而是明确字段、空值约定、来源定位、缺失说明。

结构化层要做的,是把中间表示映射到目标 schema。这里的关键不是"尽量填满字段",而是"明确哪些字段能确认、哪些不能确认、为什么不能确认、证据在哪"。字段为空不是失败,乱填才是失败。

一个通用的 schema mapper 可以写成这样:

yaml 复制代码
name: document_schema_mapper
description: >
  将解析得到的内容映射到指定业务字段结构,并标记缺失项与来源位置。
inputs:
  - parsed_document
  - target_schema
  - extraction_rules
outputs:
  - structured_fields
  - missing_fields
  - ambiguous_fields
  - source_locations
constraints:
  - 字段无法确认时必须置空并说明原因
  - source_locations 必须可追溯到页码或块编号
  - 同名字段多处冲突时不得静默取其一
  - schema 变更必须版本化

这里面最重要的设计,不是 structured_fields,而是 missing_fieldsambiguous_fieldssource_locations。很多团队喜欢只输出"结果对象",因为这样看起来干净。但在真实系统里,下游最需要知道的,往往是哪些东西没抽到、哪些东西有冲突、哪些值的来源可以复核。没有这些元信息,你做不出可信的审批,也做不出可靠的人机协同。

再往前一步,结构化层最好支持按业务 schema 明确字段级规则。比如金额字段要求标准化币种格式,日期字段要求统一 ISO 格式,合同相对方要求实体名清洗,课程章节要求保留层级结构。把这些要求留给下游各自处理,等于你根本没有真正的通用底座。

2.3 校验层的职责:回答"能不能信"而不是"像不像对"

真正区分 Demo 和生产的,是校验层。没有校验层的系统,看起来最省步骤,实际上最不负责任。因为你把一个充满不确定性的对象,直接交给了下游。

校验层要做的不是重新抽取,而是对结构化结果进行完整性、一致性、格式、交叉引用和风险等级判断。它最后输出的,应该是一个使用建议,而不只是几个分数。下游最需要的并不是"置信度 0.82",而是"可以继续自动处理""建议人工复核""必须阻断"。

一个更接近生产的校验层可以这样定义:

yaml 复制代码
name: extraction_quality_gate
description: >
  对结构化字段进行完整性、一致性和格式校验,输出可用性结论。
inputs:
  - structured_fields
  - required_field_policy
  - validation_rules
outputs:
  - validation_status: pass | revise | block
  - issue_list
  - confidence_summary
  - review_recommendation
constraints:
  - 关键字段缺失时不得 pass
  - 跨页冲突字段必须进入人工复核
  - 多个高风险问题叠加时必须 block
  - confidence_summary 不得替代 issue_list

这里故意把 validation_status 做成离散决策,而不是单一连续分值。因为很多业务系统根本不需要一个数学上的漂亮分数,它们需要一个流程上的动作建议。pass 是可自动流转,revise 是需要补充或复核,block 是禁止继续。这种输出才真正能和后续 Skill、审批流、人工介入机制接起来。

三、为什么 location_map 和 source_locations 是可信度核心资产

很多团队做文档处理时,会把定位信息看成"可有可无的附加字段"。这是一个非常危险的认知。没有定位信息,系统所有"可追溯"都只是口头承诺。

所谓 location_map,本质上是在原始文档和中间块结构之间建立映射关系。所谓 source_locations,则是在结构化字段和原始证据之间建立映射关系。前者解决"这段内容来自哪",后者解决"这个字段为什么是这个值"。

这两层映射一旦缺失,会出现三个很现实的问题。

第一,人工复核会极度低效。审核人拿到一个结构化对象,只知道"合同期限是 24 个月",却不知道来自第几页哪个段落,那他只能重新翻整份文档。这样一来,系统不但没有提效,反而把人工时间浪费在二次定位上。

第二,下游无法建立信任分层。比如同样是金额字段,一个值有明确页码和表格坐标,另一个值只是模型从多段描述里"推断"出来,它们的可信度显然不一样。没有定位链路,这种差异根本无法表达。

第三,错误分析几乎做不动。你无法知道问题是出在 OCR、表格恢复、字段映射还是规则校验。系统出了错只能统称"抽取不准",最后没有任何层能被真正优化。

说白了,定位信息不是锦上添花,而是整个输入底座能不能被运营、被排障、被审计的前提。

四、真实工程里最容易翻车的几个点

4.1 读不到不是最坏情况,读错并静默通过才是

这是文档处理里最该反复强调的一句。很多团队会把成功率看得很重,觉得"至少系统跑通了"。但在输入层,静默错误的危害远大于显式失败。

举个最常见的例子,OCR 把 8 识别成 3,或者把 2026-01-08 识别成 2026-01-03。如果系统没有冲突检测、格式校验、来源追踪,这个值会像真的一样一路流进报价、审查、审批。最后你看到的不是一条报错,而是一条错误决策。

所以从系统设计上,宁可多一些 revise,也不要为了"自动化率"强行把不稳定结果推成 pass。输入层过度乐观,等于把风险贷款发给全系统。

4.2 Schema 一旦漂移,所有通用能力都会碎

文档处理通用 Skill 真正的难点之一,不是第一次写出来,而是后面如何稳定演进。很多团队前期为了赶进度,经常直接改字段名、改空值语义、改层级结构。今天叫 effective_date,下周变成 start_date,再过两周又加一个 contract_effective_date。一开始大家觉得只是小改,几轮之后,下游全部开始靠猜。

所以 schema 必须版本化。字段含义、必填规则、来源要求、单位格式,都需要有明确版本。否则所谓"通用 Skill"只是表面通用,实际上每个调用方都绑死在某个历史阶段。

4.3 文档类型差异必须前置,不要事后补锅

合同、发票、简历、病例、研报,虽然都叫文档,但它们的结构差异非常大。很多团队喜欢先上一套通用抽取逻辑,等问题多了再一点点打补丁。这个路径几乎一定会把系统养成巨型 if-else。

更理性的做法是从一开始就承认: 通用层不等于忽略差异。你应该有统一的抽取接口和统一的输出对象,但允许不同的解析 profile、字段模板、校验规则。通用的是框架,不是所有细节都一样。

五、评估文档处理,不要只看识别率

很多团队评估文档处理时,最爱报一个"总体准确率"。这个指标不是完全没用,但它极其容易误导。因为系统真正影响业务的,不是平均表现,而是关键字段能不能用、缺失能不能识别、错误能不能被拦住。

更有价值的指标,通常包括下面几类:

  • 关键字段抽取准确率
  • 缺失字段标记准确率
  • 冲突字段识别率
  • 校验层拦截错误率
  • 不同格式文档的可用输出比例
  • 人工复核平均定位时间
  • 从输入到结构化输出的平均延迟

这些指标的共同点是: 它们衡量的是可用性和运营性,而不是单纯"识别像不像"。如果一个系统 OCR 总体准确率很高,但关键字段经常错,人工复核又找不到证据位置,那它依然是个生产事故制造机。

六、一个更完整的组合方式

把前面的三层串起来,一个更稳的通用 Document Skill 组合,大概可以写成这样:

yaml 复制代码
skill_chain:
  - name: document_ingestion_parser
    responsibility: raw_to_intermediate
  - name: document_schema_mapper
    responsibility: intermediate_to_business_schema
  - name: extraction_quality_gate
    responsibility: usability_decision

global_rules:
  - 每一层都必须保留可追溯元信息
  - 不确定字段必须显式暴露而不是隐式补全
  - 下游只能消费 pass 或带明确 revise 说明的结果
  - block 结果必须进入人工或补料流程

注意这里最重要的一点: 三层不是为了把流程写复杂,而是为了让每一层失败时都能被单独理解、单独优化、单独治理。抽取错了改解析,映射错了改 schema 规则,校验弱了补 gate 逻辑。没有这种分层,最后所有问题都会被压到一句"模型不稳定"上,系统永远长不大。

七、总结

Document 处理通用 Skill 的真正价值,不是让 Agent 会"读文件",而是让所有下游能力都站在一个统一、可校验、可追溯的输入底座上。没有这个底座,前面写再多垂直场景,后面也都会被输入质量拖垮。

说得更直白一点: 文档处理不是杂活,它是生产系统的地基。地基没分层、没 schema、没校验,楼搭得越快,塌得越整齐。

相关推荐
搜佛说2 小时前
03-第3章-基础CRUD操作
数据库·物联网·边缘计算·iot·嵌入式实时数据库
清水白石0082 小时前
《从缓存到数据库:一致性之痛与工程之道》
数据库·python·缓存
cTz6FE7gA2 小时前
Oracle RMAN物理备份Web系统
数据库·oracle
APguantou3 小时前
NCRE-三级数据库技术-第14章-数据仓库与数据挖掘
数据库·数据仓库·数据挖掘
刘~浪地球4 小时前
Redis 从入门到精通(十):管道技术
数据库·redis·缓存
fzb5QsS1p7 小时前
MySQL 事务的二阶段提交是什么?
数据库·mysql
清风徐来QCQ11 小时前
Lombok/SSM/devTools
数据库
LaughingZhu11 小时前
Product Hunt 每日热榜 | 2026-04-05
前端·数据库·人工智能·经验分享·神经网络
2601_9498146911 小时前
使用mysql报Communications link failure异常解决
数据库·mysql