如何搭建一个答案有据可查的法律知识库:用 Docling 解析,沿着“条”和“款”分块,配好引注元数据,并在每次导入后跑一遍检查。
在 Erst Recht,我们在打造一个基于德国法律、提供初步法律评估的 AI 平台。其中一个组成部分是检索增强(RAG)知识库,我们用 Docling 把法律 PDF 转换成 Markdown。这篇文章我做了彻底的改写:不再是一份工作坊式的报告,而是一份关于法律知识库的检查清单,内容来自实践和一手资料。
法律文本为什么与众不同
法律 AI 的水平取决于它的依据。“在 BGB(德国民法典)里某处”命中是不够的。答案必须指向具体的法条、款和句;涉及判决时,则要指向法院、日期、案号和段落编号。有三个特点让这件事变得困难:
- 层级很深:法律、章节、条、款、句、项。一款脱离了它所属的条,就无法作为依据。
- 缩写密集:“Abs.”“S.”“Nr.”“i.V.m.”“a.F.”。任何朴素的分句器都会在这些地方栽跟头。
- 引用链:法条援引法条,判决引用判决。缺少上下文的分块会把这条链扯断。
忽视这些,你得到的只是一个能找到相似词语、却给不出出处的搜索。
流水线概览
从 PDF 到语义检索一共五个环节。中间的领域逻辑,决定了最后能否得到可引用的分块:
解析:用 Docling,而不是单纯的文本提取
Docling 出自 IBM Research 苏黎世的 “AI for Knowledge” 团队,采用 MIT 许可证,如今由 LF AI & Data Foundation 托管。它不是“更好用的 OCR”,而是一条文档理解流水线:
| 单纯文本提取 / OCR | Docling |
|---|---|
| 输出扁平文本 | 输出结构化文档 |
| 丢失标题和层级 | 识别标题、列表和提纲 |
| 把多栏页面逐行读取 | 识别阅读顺序 |
| 表格变成乱码 | 表格依然是表格 |
在第一次运行之前,有三个决定值得先做:
- 只在需要时开启 OCR。数字生成的 PDF 带有文本层,扫描件没有。Docling 支持多种 OCR 引擎,包括 EasyOCR、Tesseract 和 RapidOCR。按文档来源决定,而不是一刀切。
- 开启表格识别。期限表、收费表和概览表在法律文件里比想象中更常见。Docling 为此配有专门的表格模型,提供快速和精确两种模式。
- Markdown 用来检查,DoclingDocument 用来分块。Markdown 导出非常适合人工目检结果。分块时,结构化的 DoclingDocument 是更好的基础,因为它知道每个元素的层级和来源,包括页码。
感知结构的分块
法条:直接获取结构化数据,而不是去解析
对联邦法律来说,PDF 是在绕远路。Gesetze im Internet 提供法律的 XML 下载。其 DTD 把内容组织成 norm 元素,带有简称(jurabk)、单元名称(enbez)、标题、编章单元和版本说明等元数据。法条正文放在段落元素(P)里。
分块方式几乎水到渠成:每一款一个分块,编号列举跟随所属的款,各句的位置记录在分块里。一个法条分块可以长这样:
{
"quelle": "gesetz",
"gesetz": "BGB",
"norm": "§ 626",
"titel": "Fristlose Kündigung aus wichtigem Grund",
"absatz": 1,
"gliederung": "…",
"stand": "…",
"text": "Das Dienstverhältnis kann von jedem Vertragsteil …"
}
有两件事,现在省事,日后会付出高昂代价。第一是版本:法律会修改,所以要存下版本说明;更新时,新分块替换旧分块,而不是和它并排放进去。否则同一条法条的两个版本会同时出现在索引里。第二是援引:如果某一款通过 “i.V.m.”(结合)援引了另一条法条,那条法条应作为元数据挂在分块上。这样检索就能有针对性地把被援引的法条一并取回。
判决:沿着判决书的结构切分
德国民事判决遵循法定结构:§ 313 ZPO 要求判决书包含判决主文、事实部分(Tatbestand)和判决理由(Entscheidungsgründe)等内容。切分就沿着这些部分进行:分块只在各部分内部生成,绝不跨越边界。裁判要旨(Leitsätze)如有,单独成块;段落编号(Randnummern)用作引注的锚点。
最关键的是事实部分和判决理由之间的边界。根据 § 313 ZPO,事实部分陈述当事人提出的请求以及攻击和防御手段,判决理由则包含法院的考量。如果两者落进同一个分块,模型很可能把一方当事人的陈述当成法院的法律观点来引用。
元数据应包括法院、日期、案号,以及已分配的欧洲判例法识别码(ECLI)。据联邦行政法院介绍,ECLI 由国家代码、法院、年份和本地标识符组成。在德国,联邦宪法法院、各联邦最高法院、联邦专利法院以及各州法院都参与了 ECLI。这样每一份带有 ECLI 的裁判都有唯一的地址。
其他文档:Docling 的分块器
对于文章、说明材料或合同,Docling 自带分块器。HybridChunker 沿着文档结构切分,把过长的部分拆开,把标题相同的过短部分合并,并考虑分词器。contextualize() 会在每个分块前加上它所属的标题,元数据则指向来源文档元素,包括页码。一个中性的示例:
from docling.document_converter import DocumentConverter
from docling.chunking import HybridChunker
doc = DocumentConverter().convert("info_sheet.pdf").document
chunker = HybridChunker()
for chunk in chunker.chunk(dl_doc=doc):
text = chunker.contextualize(chunk=chunk) # 标题 + 正文,用于嵌入
path = chunk.meta.headings # 提纲路径
pages = {p.page_no for item in chunk.meta.doc_items for p in item.prov}
Docling 文档建议分块器和嵌入模型使用同一个分词器。如果还要再按句切分,就需要一份法律缩写清单,否则分块会在法条引注的中间断开。
引注元数据:引注是一个字段
可引用性是数据的属性,而不是提示词的属性。每个分块都应带上:
- 出处:法律、条、款、句;或法院、日期、案号、ECLI、段落编号。
- 提纲路径:从顶层一直到该分块的所有标题,而不只是最后一个。
- 版本:法律的哪个版本,判决的哪个日期。
- 来源:文档、页码、指向原始出处的链接。
- 受控词表:法律领域和文档类型来自固定清单,并在导入时校验。否则一个带拼写错误的过滤条件会悄无声息地返回空结果。
元数据要放进上下文,模型才能引用。它也要用于生成之后的核验:答案里引用的每一条法条,都应能在法条索引中解析到。解析不到的要标记出来,而不是直接交付。对法律 AI 来说,编造的法条是代价最高的错误(更多关于幻觉与校准的内容)。
每次导入后的质量检查
解析器和分块器几乎总会输出点什么。是不是对的,只有检查了才知道:
- 重新清点。输入了多少文档,又有多少文档产出了至少一个分块。每个没有分块的文件都应进入报告,而不是只留一行日志。
- 对照原件抽查。把随机抽取的分块和 PDF 页面并排对照:标题、表格和阅读顺序对不对?
- 找出重复出现的行。在许多页上一模一样出现的内容,通常是页眉或页脚,应在嵌入前删除。
- 字符卫生。Unicode 私用区里的字符(常来自符号字体)、行末的断字连字符、连字。
- 元数据完整性。有多少分块缺少提纲路径、页码或出处?频繁出现却没有上级节点的标题,比如 “Tatbestand”,说明层级丢失了。
- 以缩写结尾的分块。以 “Abs.” 或 “Nr.” 结尾的分块,暴露了一个没有法律词典的分句器。
监控检索
- 用真实查询做健康检查。不要只检查集合是否存在,而要嵌入一个已知的测试问题,并要求返回预期的命中。
- 度量空结果。统计没有命中或低于分数阈值的查询占比,并按过滤值细分。一个永远命中不了的过滤条件就是 bug。
- 混合检索。遇到 “§ 626 BGB”,精确命中才是关键。常见的向量数据库为此提供全文过滤,以及结合稠密向量与稀疏向量的混合检索。
- 模型生命周期。服务商会弃用嵌入模型。把模型名称和向量存在一起,并把完整的重新索引当作计划内的操作,而不是紧急事故。
- 黄金测试集。由真实问题和预期出处组成。衡量的是正确的分块是否排在前面,以及有检索的答案是否比没有检索的更好。我们如何做系统化评估,可以参考我们寻找最适合法律分析的模型的过程。
什么时候不值得这样折腾
- 语料很小。如果能放进上下文窗口,就该直接放进提示词,而不是向量数据库。
- 你只需要法条原文。那就直接处理 XML,解析 PDF 在这里是绕远路。
- 扫描质量很差。那么首先要解决的是文字识别,而不是分块。
- 没有人做度量。没有黄金测试集,RAG 就只是额外的延迟和复杂度,却拿不出它有用的证据。
结论
Docling 把解析问题解决得很好。真正的工作在后面:把结构一直带进分块,把出处作为元数据保存,每次导入后重新清点,并持续监控检索。可引用性在导入时就已决定,而不是在提示词里。
Erst Recht 用 AI 让初步法律评估触手可及。我们的多智能体系统会分析你的处境,并给出具体的下一步。
所用工具:
在处理类似的问题?正在为 RAG 准备大批文档?联系我,我很乐意在架构或实现上帮上忙。