12 条必备条款 × 200 份合同 = 2400 次判断。漏检 199 处,全部来自「换了说法但词表里没有」; 误判 20 处,全部来自「提到了但明确说不适用」。 补全同义词表后漏检从 199 掉到 0,误判一处没动。 准确率 90.9% 到 99.2%——剩下那 0.8% 是词表碰不到的。
前两节是律所和法院的视角。这一节换成企业法务:合同签之前,逐条核对必备条款。 这是法律 AI 落地最实在、也最容易做砸的一个场景。
一、合规检查本质是清单核对
企业的合同审查,很大一部分不是「判断」,是「核对清单」。 这份合同有没有约定争议解决方式?有没有保密条款?有没有不可抗力?有没有知识产权归属? 这类问题有标准答案,而且答案是「有 / 没有」二值。 所以它天然适合规则,不适合大模型—— 大模型给的是「这段话大概在讲保密」,而法务要的是「第 7 条有没有」。 规则引擎在这个场景里还有两个决定性优势: ① 结果可解释。 命中了哪个词、在第几段,一目了然。 ② 快。 Demo 里 2400 次判断只花不到 2 毫秒。 这意味着可以在合同起草界面上做实时提示,而不是等一晚上出报告。 但规则有个绕不过去的软肋:它只认字面。 而合同恰恰是一种「同一个意思有一百种说法」的文体。这一节就来看这两种错怎么发生。
二、假阴性:换了说法,关键词躲开了
| 条款 | 真存在 | 真缺失 | 漏检 | 补词后 | 误判 |
|---|---|---|---|---|---|
| 合同主体 | 166 | 34 | 0 | 0 | 0 |
| 标的描述 | 167 | 33 | 0 | 0 | 0 |
| 价款金额 | 165 | 35 | 0 | 0 | 0 |
| 付款方式 | 162 | 38 | 0 | 0 | 0 |
| 交付时间 | 163 | 37 | 0 | 0 | 0 |
| 质量标准 | 168 | 32 | 0 | 0 | 1 |
| 违约责任 | 164 | 36 | 30 | 0 | 4 |
| 争议解决 | 166 | 34 | 36 | 0 | 4 |
| 保密条款 | 176 | 24 | 34 | 0 | 5 |
| 不可抗力 | 172 | 28 | 33 | 0 | 4 |
| 知识产权 | 161 | 39 | 33 | 0 | 2 |
| 合同期限 | 171 | 29 | 33 | 0 | 0 |
| 先看「漏检」那一列:一共 199 处,全部来自「换了说法但词表里没有」。 | |||||
| 比如「违约责任」被写成「违约赔偿」——意思一模一样,字一个不差地躲开了关键词。 | |||||
| 「保密条款」被写成「信息保护义务」,同一个道理。 | |||||
| 这就是假阴性:条款明明在,机器说没有。 | |||||
| 它的危险在于「静默」——法务看到的是一份「缺 5 条」的报告,而实际只缺 2 条, | |||||
| 多出的 3 条是误报。 | |||||
| 看多了之后,人就会开始忽略这个报告的警告。 | |||||
| 这 199 处漏检全部分布在后 7 条上,前 5 条一条都没有。 | |||||
| 为什么?因为前 5 条的表述高度固定(甲方、标的、价款、付款方式、交付时间),几乎没人换说法; | |||||
| 而后 7 条的表述本身就在演化。 | |||||
| 这一点很值得注意:漏检不是均匀分布的,它集中在「表述最灵活的那几条」。 | |||||
| 所以补词表也应该按条款分别投入,而不是平均用力。 |
三、假阳性:词在,意思反了
把同义词补进词表之后,漏检从 199 掉到 0。 但「误判」那一列一动不动,还是 20 处。 因为误判的来源不是「词表不全」,是「提到了但明确说不适用」。 比如「本合同不约定争议解决方式」——关键词「争议解决」在,但它恰恰是在说这一条没有约定。 再比如「本合同不设保密义务」——「保密」两个字响亮地在场,意思完全相反。 这类否定语境,关键词规则从原理上就判不了。 因为它需要判断「这个短语的极性」,而不是「这个短语在不在」。 补词表只会让它误判得更起劲,因为能撞上的词更多了。 这就是假阳性:条款明明不在,机器说在。 它的危险和假阴性对称——一个让法务漏看,一个让法务白看。 但在合同场景里,假阳性更常见,因为起草人经常用「本合同不适用 XX」来主动排除某一条。 补词表把标出量从 578 砍到 379,砍掉的全是漏检;剩下那 20 处误判,按词表怎么补都补不掉。 379 处里 359 处真缺、20 处是误判——误报率 5.3%,这个水平是法务可以接受的。
四、规则引擎的定位:筛,不判
「准确率 90.9% 到 99.2%」这个提升听起来很好,但更该看的是那 0.8%。 这 0.8% 是「词表碰不到的」,它不会因为再多加几百个同义词而消失。 所以规则引擎的正确定位是:负责筛,不负责判。 它把 2400 条判断筛成 379 条「疑似缺失」,让人只看这 379 条,而不是看全部。 人省下的是 84% 的时间,不是 100% 的时间。 这个定位决定了两件事: ① 阈值要偏向「宁可多报」。 漏检的代价远高于误报—— 漏了一个真缺的条款,合同签了就是风险;多报一个误判,律师扫一眼就过去了。 ② 报告要分组,不要给一个总分。 「合规分 92 分」这种输出没有操作性。 有用的是「争议解决、保密条款、不可抗力这三条缺失,请确认」。 补充这两个字的边界:什么时候该上模型? 当假阳性和假阴性同时高到人看不过来的时候。 如果 379 条里只有 20 条是误判,人看得过来,就没有必要引入模型—— 引入模型会带来新的不确定性,而且结果不好解释。 「能用规则就别上模型」在合规场景里是一条很实在的经验。
五、落地:四步
① 条款清单要来自业务,不是来自模板。 不同行业、不同金额、不同交易对手,必备条款不一样。 一条全院通用的清单,通常意味着每条都不够细。 ② 词表按条款分别维护,并记录「这条为什么加」。 否则半年后没人敢删任何一个词,词表会膨胀成垃圾场。 维护记录本身就是这个系统最有价值的资产。 ③ 否定语境单独处理。 做一个简单的否定词前置检查:「不」「无」「另行」「除外」等词出现在关键词前 10 个字符内时, 降级为「待人工确认」。 这一条能消掉大部分假阳性,而且不需要模型。 ④ 把复核结果回流。 律师每一次「这条其实是有的」都应该变成一条新的同义词。 这个闭环跑起来之后,词表会自己长大——而它长大的方向,恰好是这个企业真实的合同写法。 最后一句:合规检查的价值不在「查出多少问题」,在「让该看的被看到」。 按这个标准衡量,一个会误报但从不漏报的系统,比一个准确率高但会静默漏检的系统有用得多。
六、动手 Demo:合同合规检查:补全同义词表,准确率从 90.9% 到 99.2%
造 200 份合成合同,每份随机保留 8 到 12 条必备条款(共 12 条清单)。 存在的条款有 20% 概率被换成「词表外的同义表述」, 缺失的条款有 10% 概率插入一句「本合同不约定 XX」的否定句。用关键词规则逐条判断,统计漏检、误判; 再把同义词补进词表重算,看两类错误各变化多少,并测总耗时。
def check(txt, extra):
out = set()
for j, (name, words) in enumerate(CLAUSES):
ws = list(words) + (ALIAS.get(name, []) if extra else [])
if any(w in txt for w in ws): # 命中任一就算「有」
out.add(j)
return out
if j in pres and j not in got: # 漏检
fn[j] += 1
elif j not in pres and j in got: # 误判
fp[j] += 1
实跑输出:
--------------------------------------------------------------------
合同合规检查:补全同义词表,准确率从 90.9% 到 99.2%
--------------------------------------------------------------------
合同数 200 份,每份随机缺 0~4 条必备条款
规则条数 12 条,每条 2~3 个关键词
判断次数 2400 次(200 份 × 12 条)
纯规则总耗时 1.8 毫秒
条款 真存在 真缺失 漏检 补词后漏检 误判
----------------------------------------------------
合同主体 166 34 0 0 0
标的描述 167 33 0 0 0
价款金额 165 35 0 0 0
付款方式 162 38 0 0 0
交付时间 163 37 0 0 0
质量标准 168 32 0 0 1
违约责任 164 36 30 0 4
争议解决 166 34 36 0 4
保密条款 176 24 34 0 5
不可抗力 172 28 33 0 4
知识产权 161 39 33 0 2
合同期限 171 29 33 0 0
**先看「漏检」那一列:一共 199 处,全部来自「换了说法但词表里没有」。**
比如「违约责任」被写成「违约赔偿」——**意思一模一样,字一个不差地躲开了关键词。**
「保密条款」被写成「信息保护义务」,同一个道理。
把同义词补进词表之后,**漏检从 199 掉到 0。**
**但「误判」那一列一动不动,还是 20 处。**
**因为误判的来源不是「词表不全」,是「提到了但明确说不适用」。**
比如「本合同不约定争议解决方式」——**关键词「争议解决」在,但它恰恰是在说这一条没有约定。**
再比如「本合同不设保密义务」——**「保密」两个字响亮地在场,意思完全相反。**
**这类否定语境,关键词规则从原理上就判不了。**
**补词表只会让它误判得更起劲,因为能撞上的词更多了。**
**这就是规则引擎在合规场景里的真实定位:负责筛,不负责判。**
**200 份合同 2400 次判断只花 1.8 毫秒**,机器标出 379 处「疑似缺失」。
**这 379 处里,359 处真缺、20 处是误判。**
**补词表把标出量从 578 砍到 379,砍掉的全是漏检;**
**剩下那 20 处误判,按词表怎么补都补不掉。**
**最后那一步仍然是律师签字。**
要说明:合同是我**按条款词拼接生成的**,**真实合同的条款会分散在附件、补充协议、往来函件里**。
**但「假阴性靠补词表、假阳性靠语义理解」这个分工是真的。**
done -> _out_39t.txt
这张表要横着读:先看「漏检」,再看「补词后漏检」,最后看「误判」。 第一列找到 199,第二列归零,第三列纹丝不动——这三步就是整个故事的骨架。 注意前 5 条的漏检和误判都是 0。 这不是巧合:这 5 条的关键词(甲方、标的、价款、付款方式、交付时间)是合同里最固定的表述, 几乎没人改写。 所以补词表的正确姿势是「按条款分别投入」,而不是「整体多加几百个词」。 误判那 20 处全部集中在 6 条上,而且都是否定句造成的。 「本合同不约定违约金」——「违约金」在,意思相反。 这类错误靠词表永远治不好,必须另做一层否定检测。 耗时那一项是每次跑都不一样的(本机在 1.8 到 2.4 毫秒之间浮动), 所以正文里只说「不到 2 毫秒量级」。 它的意义不是「有多快」,是「快到可以嵌进实时界面」。 要说明:合同是我按条款词拼接生成的, 真实合同的条款会分散在附件、补充协议、往来函件里,还会互相引用(「按第 3 条约定的方式」)。 但「假阴性靠补词表、假阳性靠语义理解」这个分工是真的。
30 秒实验
- 把同义替换的概率从 20% 提到 50%,看漏检那一列。 会大幅上升——表述越自由,漏检越多,这是规则方法的固有上限。
- 给检查器加一条简单规则:关键词前 10 个字符内出现「不」「无」「另行」就判为待确认。 误判会明显下降,但会引入少量新的漏检——否定检测本身也有误差。
- 把必备条款从 12 条加到 30 条,看总耗时和误判数。 耗时仍在线性增长且极快,但误判数会上升——条款越多,撞词的机会越多。
常见误区
- 「补全同义词表就能解决问题」。漏检确实归零,但那 20 处误判一处没动。
- 「提到了就说明有」。「本合同不约定违约金」里「违约金」在,意思相反。
- 「准确率 99% 就可以全自动」。剩下那 0.8% 是词表碰不到的,而且恰恰是最需要人判断的部分。
自测三问(点开对答案)
1. 假阴性和假阳性在这个 Demo 里各自怎么产生? —— 假阴性来自「换了说法、词表没收录」;假阳性来自「提到了但明确说不适用」的否定句。 2. 为什么补词表能治一种、治不了另一种? —— 补词表增加的是「能命中的字面」,对假阴性有效;误判需要判断极性,词表多反而撞得更多。 3. 在合规场景里,为什么应该偏向「宁可多报」? —— 漏检会让风险随合同签下去,误报只是律师多扫一眼。两者代价不对等,阈值就该往多报那边偏。第十一章讲完了: 从数据、知识、算力,到诊断、管理、法务。这一整章反复出现的判断是「指标必须带上场景」。 最后一章换到金融与农业——那两行的共同点是:预测一旦被相信,就会自己改变结果。
- ☐ (本章完,下一节 12.1.1 数据治理)