◄ 所有文章
Docling

用 Docling 为法律 AI 构建知识库:实践中的经验

NW Nils Weiser •December 30, 2025 •更新于 2026-10-06 •—

如何搭建一个答案有据可查的法律知识库:用 Docling 解析,沿着“条”和“款”分块,配好引注元数据,并在每次导入后跑一遍检查。

在 Erst Recht,我们在打造一个基于德国法律、提供初步法律评估的 AI 平台。其中一个组成部分是检索增强(RAG)知识库,我们用 Docling 把法律 PDF 转换成 Markdown。这篇文章我做了彻底的改写:不再是一份工作坊式的报告,而是一份关于法律知识库的检查清单,内容来自实践和一手资料。

法律文本为什么与众不同

法律 AI 的水平取决于它的依据。“在 BGB(德国民法典)里某处”命中是不够的。答案必须指向具体的法条、款和句;涉及判决时,则要指向法院、日期、案号和段落编号。有三个特点让这件事变得困难:

忽视这些,你得到的只是一个能找到相似词语、却给不出出处的搜索。

流水线概览

从 PDF 到语义检索一共五个环节。中间的领域逻辑,决定了最后能否得到可引用的分块:

PDF Legal Documents Docling IBM Research Conversion Markdown Structured Output Processing Domain Logic Vector DB Semantic Search Step 1 Step 2 Step 3 Step 4 Step 5

解析:用 Docling,而不是单纯的文本提取

Docling 出自 IBM Research 苏黎世的 “AI for Knowledge” 团队,采用 MIT 许可证,如今由 LF AI & Data Foundation 托管。它不是“更好用的 OCR”,而是一条文档理解流水线:

单纯文本提取 / OCR Docling
输出扁平文本 输出结构化文档
丢失标题和层级 识别标题、列表和提纲
把多栏页面逐行读取 识别阅读顺序
表格变成乱码 表格依然是表格

在第一次运行之前,有三个决定值得先做:

感知结构的分块

法条:直接获取结构化数据,而不是去解析

对联邦法律来说,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 文档建议分块器和嵌入模型使用同一个分词器。如果还要再按句切分,就需要一份法律缩写清单,否则分块会在法条引注的中间断开。

引注元数据:引注是一个字段

可引用性是数据的属性,而不是提示词的属性。每个分块都应带上:

元数据要放进上下文,模型才能引用。它也要用于生成之后的核验:答案里引用的每一条法条,都应能在法条索引中解析到。解析不到的要标记出来,而不是直接交付。对法律 AI 来说,编造的法条是代价最高的错误(更多关于幻觉与校准的内容)。

每次导入后的质量检查

解析器和分块器几乎总会输出点什么。是不是对的,只有检查了才知道:

  1. 重新清点。输入了多少文档,又有多少文档产出了至少一个分块。每个没有分块的文件都应进入报告,而不是只留一行日志。
  2. 对照原件抽查。把随机抽取的分块和 PDF 页面并排对照:标题、表格和阅读顺序对不对?
  3. 找出重复出现的行。在许多页上一模一样出现的内容,通常是页眉或页脚,应在嵌入前删除。
  4. 字符卫生。Unicode 私用区里的字符(常来自符号字体)、行末的断字连字符、连字。
  5. 元数据完整性。有多少分块缺少提纲路径、页码或出处?频繁出现却没有上级节点的标题,比如 “Tatbestand”,说明层级丢失了。
  6. 以缩写结尾的分块。以 “Abs.” 或 “Nr.” 结尾的分块,暴露了一个没有法律词典的分句器。

监控检索

什么时候不值得这样折腾

结论

Docling 把解析问题解决得很好。真正的工作在后面:把结构一直带进分块,把出处作为元数据保存,每次导入后重新清点,并持续监控检索。可引用性在导入时就已决定,而不是在提示词里。


Erst Recht 用 AI 让初步法律评估触手可及。我们的多智能体系统会分析你的处境,并给出具体的下一步。


所用工具:

Docling

在处理类似的问题?正在为 RAG 准备大批文档?联系我,我很乐意在架构或实现上帮上忙。

NW
Nils WeiserAI 智能体专家 · 博登湖地区
与我合作 ▸

更多现场笔记

所有文章 ▸