几乎所有企业级 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_map 和 parse_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_fields、ambiguous_fields、source_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、没校验,楼搭得越快,塌得越整齐。