大模型辅助智能合同审阅与条款抽取落地

日期:2026-07-21

合同是企业经营里风险最密集、又最容易被忽视的文档。我们当时接手这个项目时,法务与采购团队每个月要人工过上千份合同,标准不统一、关键义务记不住、违约时限常常漏看。本文从工程实践角度,复盘我们如何用大模型 + RAG + 抽取管线把这件事做成可落地的系统。

一、项目背景

企业法务与采购长期依赖人工审阅合同,痛点非常具体:一是版式杂,同一个采购框架协议,不同供应商给的 Word、PDF、扫描件、甚至是拍照件,结构天差地别;二是条款杂,付款、交付、质保、违约、保密、管辖等要素散落在几十页里,人工逐条比对模板既慢又容易疲劳漏看;三是标准不一,三位律师可能给出三套风险提示口径,管理上没法沉淀统一规则;四是关键义务与时限难追踪,比如"收到发票后 30 日内付款""质保期 24 个月"这类节点,签完就丢进档案,到期没人提醒,最后被动违约。

我们这个项目里,核心诉求是把"人海战术"变成一个可复用、可审计、可分级的系统:先把合同结构化,再抽取关键要素,再和模板比对,最后给出分级风险与到期提醒。底层我们采用的是自研的 AI 私有化部署底座(vLLM 推理框架 + Milvus 向量库 + RAG 知识库层),所有合同数据不出内网,满足合规要求。

二、落地场景

系统上线后覆盖了四类高频场景:

第一,合同要素抽取。对每份合同自动抽取主体信息、标的、金额、付款方式、交付节点、质保期、生效/到期日、签约方等结构化字段,落库后可检索、可台账化。

第二,风险条款标注。对违约、赔偿上限、单方解除、管辖争议、不可抗力、保密义务等条款做识别与高亮,并给出模型的风险判断依据。

第三,与标准模板比对。把抽取出的要素和企业的标准合同模板做差异比对,标出"缺项""表述偏差""偏离阈值"三类问题。

第四,关键义务提醒。把"付款日""回款日""质保到期""续约窗口"等时间点转成任务,接入日历与工单,到期前主动推送。

这套能力最终以 API 形式挂在我们采用的 AI 网关后面,法务系统、采购系统通过统一 REST 接口调用,网关负责鉴权、限流和计量。

三、关键技术挑战与解决思路

挑战一:多版式合同解析。 我们面对的不只是标准 PDF,还有扫描件、双栏排版、带表格和印章的图片。直接丢给大模型,OCR 噪声和版式错位会严重干扰抽取。解决思路是先用分层解析:文本型 PDF 走 pdfminer 抽取带坐标的文本块;扫描件走我们底座里的 OCR 服务做版面分析(Layout Analysis),把标题、段落、表格区域切出来;表格用单元格坐标重建行列关系。解析阶段只做"还原版式",不急着抽取,输出一份带 block 坐标的半结构化 JSON 给下游。我们的解析配置片段如下:

parser:
  pdf_text: pdfminer          # 文本型优先
  ocr_fallback: true          # 扫描件回退 OCR
  layout:
    model: layoutlmv3
    detect_table: true
    detect_seal: true         # 印章区域单独标注,避免干扰正文
  output:
    format: blocks_json       # 带坐标的 block 列表
    keep_page_no: true

挑战二:条款要素抽取与对齐。 合同里"付款"可能叫"结算""支付""对价",表述不一。我们做法是两阶段:先用小模型做条款级分类(把合同切成 clause,打上"付款/交付/违约/保密"标签),再让大模型在已定位的 clause 内做槽位填充(slot filling)。这样把"在哪"和"是什么"解耦,比整篇丢给大模型稳定得多。抽取提示词里我们强制输出 JSON Schema,并对金额、日期做正则后校验。关键对齐逻辑代码片段如下:

# 把 clause 分类结果与模板字段做对齐
for clause in classified_clauses:
    field = ROUTER[clause.label]          # 付款/交付/违约...
    slot = llm_slot_fill(clause.text, SCHEMA[field])
    if not validate(slot):                # 金额/日期正则校验
        slot = retry_with_constraint(slot)
    contract_record[field].append(slot)

挑战三:风险条款识别与分级。 风险不是"有/无"二元问题,同一句"乙方承担一切损失"对不同合同级别含义不同。我们落地时建立了一张风险规则表(可配置),结合模型判断做分级。模型负责"这段文字是否涉及某类风险",规则引擎负责"按合同类型/金额阈值决定严重级别"。例如采购框架协议里"无限连带赔偿"判为高危,而小额服务合同里同等表述判为中危。我们用的分级路由规则:

-- 风险分级映射(示意)
SELECT r.rule_id, r.clause_type, r.severity
FROM risk_rule r
JOIN contract c ON r.contract_kind = c.kind
WHERE r.match_label = 'INDEMNITY'
  AND c.amount >= r.amount_threshold;   -- 金额超阈值才升到高危

挑战四:与标准模板比对。 模板本身也在迭代,不能写死。我们把标准模板也切片向量化,存进 Milvus;抽取出的要素做 embedding 后做近邻检索,找不到对应模板条款的抽取项就标记"缺项"。对于"表述偏差",用句子级相似度打分,低于阈值(我们设 0.82)的标黄,供律师复核。比对结果写回网关的计量层,便于统计每类合同的平均偏离度。

案例片段(已脱敏): 某省级国资集团在试点阶段,一份 42 页的设备采购框架协议,模型第一版抽取把"质保期 24 个月"误读为"2 年质保金",原因是条款中"质保"与"质保金"相邻。我们加了 clause 级定位后复测:clause[付款与担保]: "供方提供合同金额 5% 的质保金,质保期 24 个月" 修复后抽取:  guarantee_deposit: "合同金额 5%"  warranty_period:  "24 个月"        # 不再与质保金混淆 复测 30 份同类合同, 要素抽取准确率 0.91 -> 0.96

四、效果数据

以下为脱敏示意数据,口径为某省级国资集团采购线试点前后对比(约 3 个月窗口):

指标改造前改造后说明
审阅自动化率约 12%约 78%要素抽取+模板比对自动完成,人工仅复核
风险条款捕获率约 64%约 95%模型识别+规则分级,漏看明显减少
单合同审阅工时下降基准 45 分钟降至约 9 分钟含复核时间,复杂合同仍人工介入
漏审率约 9%降至约 1.5%关键义务到期提醒补齐盲区

需要说明的是,自动化率不是"全自动签署",而是把重复劳动压缩到复核层面;约 22% 的复杂/高争议合同仍走全人工,这也是分级策略的预期结果。

五、可复用经验总结

第一,抽取先结构化,别急着让大模型"读懂整篇"。我们踩过最大的坑就是把整本合同塞给模型要它输出全部字段,版式和噪声会让结果不可控。先解析、再切 clause、再槽位填充,链路可调试、可回退,是这套系统能稳定上线的前提。

第二,风险分级而非一刀切。把"模型识别风险"和"规则决定级别"拆开,规则表交给法务配置,模型只做语义判断。这样既保留模型泛化能力,又让业务能控制"什么算高危",避免误报轰炸。

第三,模板比对用向量检索做"软对齐"。标准模板会迭代,硬编码比对规则维护成本极高;把模板向量化后做近邻检索,新增模板零改动即可生效,是性价比最高的设计。

第四,所有调用走统一网关。鉴权、限流、计量都在网关收敛,模型实例的弹性扩缩容由网关限流信号反向驱动,形成算力—流量联动闭环,运维侧心智负担小很多。这个项目里我们最终沉淀的,不是某个模型,而是一条"解析—抽取—比对—分级—提醒"的可复制管线。