三套写法抽同一批合同要素:规范书面和半口语的五个字段全是 100%。纯口语那一套里, 金额和违约金还是 100%,但起始日、到期日、年利率全掉到 0%。 补一条「厘」的规则把利率拉回 100%,日期还是 0%。再换 60 份新表述, 同一套规则一份都没抽中。
11.1 讲模型怎么练,11.2 讲医疗场景怎么落地。11.3 换到法律,第一关不是模型,是文字本身。 这个领域的输入是「人写的、每种写法都合法的中文」。
一、法律文书是模板加变体的叠加
一份借款合同,正文九成是模板。当事人、金额、期限、利率、违约金——这几个槽位填进去,合同就成了。 所以直觉上,抽取应该很简单:找到槽位,取出来。 问题在于每个槽位都有无穷多种合法写法。 金额可以写「300000 元」,可以写「叁拾万元整」, 也可以写「人民币三十万元正」(这个「正」字还经常被写成「整」)。 期限可以写「自 2026 年 3 月 1 日起至 2027 年 2 月 28 日止」, 也可以写「借款期十二个月」,或者干脆「明年二月底还清」。 这三种写法在法律上完全等价,在字符串上毫无共同点。 这就是法律文本处理的根本难点——不是语言难懂,是同一个意思的写法太多。 这一节的 Demo 造三套模板,各 100 份合同, 用纯正则(含大写数字转换)去抽五个字段,看 F1 怎么塌。 整个过程没有模型,就是为了看清「哪些能枚举、哪些不能」。
二、封闭符号抽得动,开放语义抽不动
| 字段 | 规范书面 | 半口语 | 纯口语 | 口语+规则 |
|---|---|---|---|---|
| 金额 | 100% | 100% | 100% | 100% |
| 起始日 | 100% | 100% | 0% | 0% |
| 到期日 | 100% | 100% | 0% | 0% |
| 年利率 | 100% | 100% | 0% | 100% |
| 违约金 | 100% | 100% | 100% | 100% |
| 前两列全满分。规范书面和半口语写法的日期都是「年月日」, | ||||
| 金额都带「人民币」和「元」——正则一套规则就通吃。 | ||||
| 这就是模板化文书的真相:格式固定的时候,根本不需要大模型。 | ||||
| 很多法律科技产品的第一版,就是一堆正则加上一个人工复核界面,而且它在规范合同上真的够用。 | ||||
| 第三列塌了一半:日期和利率都是 0%,金额和违约金却是 100%。 | ||||
| 差别在哪? | ||||
| 「叁拾万」和「万分之五」是封闭的符号系统——数字就那么几个字,枚举得完,做个映射表就行。 | ||||
| 「明年二月底」是开放的语义——这四个字里一个数字都没有,正则再强也抽不出来。 | ||||
| 它依赖「今年是哪一年」这个上下文,而且「二月底」是 28 号还是 29 号还要看闰年。 | ||||
| 「年息四厘三五」也一样:那个 % 号根本不存在,机器得先知道「厘」是百分点的十分之一。 | ||||
| 所以「能不能抽」的分界线,不在字段重要性,在写法是否封闭。 | ||||
| 这是一个非常有用的判断标准:拿到一份新文书类型,先问「这个字段的取值空间是有限枚举吗」, | ||||
| 再决定用规则还是用模型。 |
三、加一条规则救一类,救不了下一类
补上「年息 X 厘 YZ」这条领域规则之后,利率从 0% 回到 100%。 但日期还是 0%——「明年二月底」没有任何字符串规律可循。 更要紧的是:这条新规则只认「X 厘 YZ」这一种写法。Demo 再换 60 份合同, 利息写成「月息千分之三」「按 LPR 加点 50 个基点」「按银行同期贷款利率计息」—— 同一套扩展规则,抽中 0 份,利率字段 F1 = 0%。 这三句话在法律上都是有效的利率约定,而且都很常见。 「月息千分之三」是另一套单位(千分比、按月);「LPR 加点」要外部数据才能算出具体数值; 「银行同期贷款利率」甚至不是确定值,是一个引用。 所以法律文本抽取的工程量,不在「写一个抽取器」,在「把民间的每一种写法都枚举出来」。 而这是一个开放集合——每加一条规则,就暴露出一批还没覆盖的写法。 这就是为什么这个领域的 AI 落地,最后都变成了「规则兜底 + 人工复核」。 不是技术不行,是问题的形态决定了没有「一次做完」这个选项。
四、落地:三层,按可靠性排
① 第一层:正则 + 字典。覆盖所有封闭符号——数字、日期格式、大写金额、 固定的条款编号(「第 X 条」)。这一层的准确率可以做到接近 100%,而且结果完全可解释。 ② 第二层:模型抽开放语义。「明年二月底」「按银行同期贷款利率」这类,只能靠语义理解。 但模型输出必须带置信度,而且必须给出来源片段——律师不会接受一个「凭空出现」的数字。 ③ 第三层:人工复核界面。 不是「机器人抽、人来改」,是「机器标出它不确定的地方,人来确认」。 这两者的区别在于工作量分配——前者要人全看一遍,后者只让人看不确信的部分。 一个很实用的验收口径:把「机器抽错的」和「机器没抽到的」分开统计。 前者能在复核界面里被发现,后者是真漏。 所以宁可让机器报一个空值,也不要让它猜一个看起来合理的错值——因为空值会被 flag,错值不会。 最后一句:法律文本处理的目标不是「全自动」,是「让人只看该看的部分」。 按这个目标设计,三层结构比一个大模型实用得多。
五、动手 Demo:法律文本抽取:写成「年息四厘三五」,利率字段的 F1 就掉到 0%
造三套模板各 100 份合成借款合同:规范书面(数字带小数、日期用年月日)、 半口语(句式换了但日期仍是年月日)、纯口语(大写金额、相对日期、「年息四厘三五」)。 用正则抽金额、起止日、年利率、违约金四个字段,逐字段算 F1; 再给纯口语补一条「年息 X 厘 YZ」规则重算;最后换 60 份新表述,看这条规则能覆盖多少。
RE_AMT = re.compile(u"人民币\\s*([\\d,]+(?:\\.\\d+)?)\\s*元")
RE_AMT_CN = re.compile(u"人民币([零壹贰叁肆伍陆柒捌玖拾佰仟万]+)元")
RE_DATE = re.compile(u"(\\d{4})年(\\d{1,2})月(\\d{1,2})日")
RE_RATE = re.compile(u"年利率\\s*(?:为)?\\s*([\\d.]+)\\s*%")
RE_RATE_CN = re.compile(u"年息([零一二三四五六七八九])厘([零一二三四五六七八九]{2})")
RE_PEN = re.compile(u"万分之([零一二三四五六七八九十\\d]+)")
out[u"年利率"] = SM[a] + (SM[b[0]] * 10 + SM[b[1]]) / 100.0
# 四厘三五 -> 4 + 35 / 100 = 4.35
实跑输出:
--------------------------------------------------------------------
法律文本抽取:写成「年息四厘三五」,利率字段的 F1 就掉到 0%
--------------------------------------------------------------------
样本 每类 100 份合成借款合同
抽取方式 正则 + 大写数字转换(无模型)
比对口径 金额差小于 0.5 元、利率差小于 0.01 个点算对
字段 规范书面 半口语 纯口语 口语+规则
----------------------------------------------
金额 100% 100% 100% 100%
起始日 100% 100% 0% 0%
到期日 100% 100% 0% 0%
年利率 100% 100% 0% 100%
违约金 100% 100% 100% 100%
**前两列全满分。**
规范书面和半口语写法的日期都是「年月日」,金额都带「人民币」和「元」——**正则一套规则就通吃。**
**这就是模板化文书的真相:格式固定的时候,根本不需要大模型。**
**第三列塌了一半:日期和利率都是 0%,金额和违约金却是 100%。**
差别在哪?**「叁拾万」和「万分之五」是封闭的符号系统**——数字就那么几个字,枚举得完。
**「明年二月底」是开放的语义**——这四个字里一个数字都没有,正则再强也抽不出来。
**「年息四厘三五」也一样:那个 % 号根本不存在**,机器得先知道「厘」是百分点的十分之一。
补上这一条领域规则,**利率从 0% 回到 100%。**
**但日期还是 0%**——「明年二月底」没有任何字符串规律可循。
更要紧的是:**这条新规则只认「X厘YZ」这一种写法。**
再换 60 份合同,利息写成「月息千分之三」「按 LPR 加点 50 个基点」「按银行同期贷款利率计息」——
**同一套扩展规则,抽中 0 份,利率字段 F1 = 0%。**
**所以法律文本抽取的工程量,不在「写一个抽取器」,在「把民间的每一种写法都枚举出来」。**
**这个枚举没有尽头——这也是这个领域最后都落到「规则兜底 + 人工复核」的原因。**
要说明:合同是我**按三套模板合成的**,不是真实合同。
**真实合同里还有表格、附件、手写补充、跨页引用。**
**但「同一语义多种写法导致抽取失败」这件事,与数据真假无关。**
这张表最反直觉的地方是:纯口语写法的金额和违约金,竟然还是 100%。 因为「叁拾万」「万分之五」虽然看着不像数字,但它们是有限集合——做一个映射表就解决了。 而「明年二月底」不是有限集合。 它甚至不是「格式不同」,是「信息不在字符串里」——要知道今年是哪一年、二月底是 28 还是 29, 才能算出具体日期。 这一条可以当成判据用: 如果字段值能从字符串本身算出来(哪怕要转换),规则就能搞定; 如果字段值需要外部信息或常识,就必须上语义模型。 60 份新表述那份跑出来是 0,这个结果不意外, 但它说明的事很要紧:每加一条规则,覆盖的是一类写法,而不是一个字段。 「利率字段覆盖率 100%」这句话,在换了文书类型之后会立刻变成 0。 要说明:合同是我按三套模板合成的,不是真实合同。 真实合同里还有表格、附件、手写补充、跨页引用、甚至「见附件三」这种指向。 但「同一语义多种写法导致抽取失败」这件事,与数据真假无关。
30 秒实验
- 给纯口语那套也加上「明年 X 月底」的规则(按当前年份推算),看日期列。 起始日还是抽不到(纯口语里根本没写起始日),到期日能救回一部分。
- 把额度词「人民币」去掉再抽一次,看金额那一列。 会掉下来——很多规则都靠着这个锚点定位,锚点没了整条链就断。
- 把三套模板混在一起训练一个字符级分类器,看能不能统一。 在模板内能过拟合到很高,换一套模板立刻掉——这就是纯口语那一列的意义。
常见误区
- 「有正则就够了」。规范书面确实 100%,但纯口语的日期和利率直接归零。
- 「加规则就能逐步覆盖」。加了「厘」这一条救回利率,换 60 份新表述又是 0 份。
- 「金额大写最难抽」。「叁拾万」是 100% 抽中的,因为它是封闭集合;真正难的是「明年二月底」。
自测三问(点开对答案)
1. 为什么大写金额比「明年二月底」好抽? —— 大写金额是封闭的符号系统,有限个字符加固定规则就能转换; 相对日期需要外部信息(今年是哪年、闰年与否)才能确定。 2. 「补一条规则把利率救回 100%」说明了什么,也遗漏了什么? —— 说明规则对特定写法有效; 遗漏的是它只覆盖「X 厘 YZ」一种,换「月息千分之三」或「LPR 加点」就是 0。 3. 为什么宁可让机器报空值,也不要让它猜一个看起来合理的值? —— 空值会被复核界面 flag 出来,错值不会——错值会静默流到下游,而合同要素错了的代价远高于缺失。抽出了要素,下一个问题是「怎么找到相似的案子」。 这里的坑不在文字识别,在「像」这个词本身——字面像的,未必法律上像。
- ☐ (本节完,下一节 11.3.2 法律研究赋能)