这个仓库能给你一套中文法规语料、2,475 条带条款级引用的问答,以及一套现成的召回/生成评测脚本。但它不是下载即用:数据有版本差、偏移量失效、章节结构大量丢失,脚本也跑不起来。这篇按“背景 → 数据 → 脚本 → 能做什么实验”四块拆开,重点说清楚哪些数据可信、哪些只能当参考。
1. 仓库背景:它是什么,和论文什么关系
仓库地址:legal-dc/Legal-DC。
git clone --depth 1 https://github.com/legal-dc/Legal-DC.git |
克隆下来约 51 MB,其中原始 docx 占 18 MB。仓库本身很轻:
| 项 | 值 |
|---|---|
| 提交历史 | 3 个 commit,都在 2024-11-01 |
| README | 两行占位 |
| LICENSE | 没有 |
| 目录 | 仓库根下再套一层同名 Legal-DC/ |
| 代码 | 混合检索主流程、5 个模型调用脚本、若干评测脚本 |
| 数据 | 539 篇 docx、结构化语料 JSON、问答全集与 5 个分片 |
它对应论文《Legal-DC: Benchmarking Retrieval-Augmented Generation for Legal Documents》(arXiv:2603.11772,2026-03)。两者的关系要分清:
| 论文 | 仓库 |
|---|---|
| 提出 Legal-DC 基准、LegRAG 框架、自动化评测方法 | 提供数据集、常规混合检索基线、评测脚本 |
| LegRAG 的条款边界索引、双路自反思 | 不在仓库里 |
| 2026-03 挂出 | 代码停在 2024-11-01 |
所以结论是:数据集和评测口径可以用;论文里的 SOTA 不能从这个仓库复现。 仓库快照还早于论文,论文表格里的数字未必对应当前 commit。
另外,没有 LICENSE。内部研究没问题,商用或再分发前先确认授权。
还有一点容易被名字误导:它不是合同数据集。标题里带“合同”的只有 4 篇,主体是市场监管、食品/药品/医疗器械、质量安全、广告网络、知识产权等行政法规和规章。要做商业合同条款抽取,CUAD 那类数据更合适;这里评的是“法规咨询”。
2. 数据:有什么,怎么理解,哪些可信
2.1 三个文件口径
| 文件 | 文档 | 问答 | 说明 |
|---|---|---|---|
struct_document_539_final.json |
539 | — | 语料池,比 QA 覆盖多 59 篇 |
pro_LawQA.json |
480 | 2,474 | 单文件全集,无空引用 |
pro_LawQA-1..5.json |
480 | 2,475 | 5 个分片,含 1 条空引用 |
记住三句话:2,475 是分片口径;单文件全集少 1 条;语料池比 QA 覆盖多 59 篇。
2.2 语料:539 篇,27,176 个 chunk
语料文件是一个列表,每篇文档三个字段:编号、标题、正文。正文 不是字符串,而是“段落序号 → 文本”的字典:
{ |
这 27,176 个值,就是仓库默认的检索单元。它们不是按句子或固定长度切的,而是以 docx 段落为基础、按“第X条”做了聚合:
- 521 篇有条文结构的文档共 26,323 个 chunk,其中 26,175 个(99.4%)以“第X条”开头。
- 18 篇没有条文结构的文档(补充规定、代码标准、抽查细则等)按非空段落逐段保留。
- 文首标题、公布令、章/节标题大多被丢掉;超长条文会在句号或段落边界处再切一刀。
这套切法有两个明显后果:
| 指标 | 实测 |
|---|---|
| chunk 能在 docx 原文里逐字找到 | 26,942 / 27,176 = 99.14% |
| 章/节标题保留 | 3,295 个只剩 5 个(0.15%) |
| 表格单元格保留 | 2,029 / 4,567 = 44.4% |
| 含 ≥2 个完整条文头的 chunk | 237 个(0.87%),分布在 66 篇 |
| 整篇只有 1 个 chunk 的文档 | 3 篇 |
具体例子:
- 《合同违法行为监督处理办法》16 条被合成 1 个 1,706 字的 chunk。
- 《公司法》《保险法》《企业破产法》出现“10 条一块”。
- 《专利法》第十九条又被切成 187 字 + 43 字两段。
- 《专利代理条例》的“第二章 专利代理机构和专利代理师”被删,前后条文直接粘连。
怎么理解:可以把每个 正文 值大致当成“一条法规”,但不能假设它严格等于一条。 要做严格的条款级评测,最好回到 docx 重建。
2.3 问答:2,475 条,四种题型
pro_LawQA.json 每条七个字段:query、answer、document、title、document_start、document_end、class。
query是问题;answer是自然语言参考答案;document是条款级标准答案列表,不是文档名;class是题型。
实测统计:
| 指标 | 实测值 |
|---|---|
| 条数 | 全集 2,474;分片 2,475 |
| 覆盖文档 | 480 |
| 引用总数 | 2,812 |
| 每题引用数 | 1 条 2,278;2 条 143;3 条 23;4 条 15;5 条 8;最多 24 条 |
| 问题长度 | 中位数 20 字,P90 34 字,最长 79 字 |
| 答案长度 | 中位数 75 字,P90 201 字,最长 672 字 |
| 每篇 QA 数 | 2–8 条,中位数 5 条 |
题型分布:
| 题型 | 条数 | 占比 | 特点 |
|---|---|---|---|
| 概括归纳型 | 1,614 | 65.2% | 从长条款里提炼要点 |
| 逻辑推理型 | 527 | 21.3% | 需要多步判断 |
| 概念解释型 | 268 | 10.8% | 解释术语或法律概念 |
| 其他 | 65 | 2.6% | 施行时间、文号等事实型 |
注意两个代码级陷阱:
class字段里“逻辑推理型”前面有一个空格,527 条全是' 逻辑推理型'。用class == '逻辑推理型'精确过滤会一条都选不到,先.strip()。- 9 组问题文本重复出现(每组 2 次)。做评测时可以去重,也可以保留;保留更接近真实流量,去重则避免同一问题被重复计分。
2.4 哪些可信,哪些不可信
| 对象 | 可信度 | 依据 | 怎么用 |
|---|---|---|---|
| 539 份 docx | 高 | 原始文档层,包含标题、公布令、章节、表格 | 作为语料源头,重建 chunk |
正文 chunk |
中 | 99.1% 原文可核,但章节/表格大量丢失,边界不一致 | 做第一版检索基线可以;严格评测需修复 |
query / answer |
中高 | 问题像真实咨询,答案贴着法条;但仓库没交代构建流程 | 当评测集,别当法律标准答案库 |
document gold |
高(归一化后) | 2,812 条非空引用(全集口径),docx 可达 99.89%,JSON 可达 98.76% | 归一化后映射到段落/条 |
document_start / document_end |
不可信 | 2,812 条引用逐条切正文,0 条命中;抽 44 条对 docx 也是 0 条 | 别用 |
| 论文里的 SOTA 数字 | 不可复现 | LegRAG 代码不在仓库,仓库快照早于论文 | 只当背景,不当复现目标 |
2.5 五个必须先处理的坑
坑 1:全集和分片不是同一份数据。 以分片为基准:分片多 1 条(引用是空字符串,全集里没有);2,294 条完全相同;180 条在引用切分或格式上不同,其中 176 条只是字面量 \n 的差异,2 条是引号/合并切分(定量包装、肉制品),2 条内容真的不一样(茶叶包装、野生药材)。全集还有 191 条引用带字面量 \n(反斜杠 + n,不是换行),分片里没有。
坑 2:偏移量是摆设。 document_start / document_end 和仓库里的 JSON、docx 都对不上,别用。定位 gold 只能靠归一化后的子串搜索。
坑 3:直接匹配只有 66%,归一化后能到 98.8%。 把 gold 引用直接到“该文档所有段落拼起来的正文”里做子串匹配,只有 1,855 / 2,812 = 66.0% 命中;去掉空白、标点、全半角括号差异后到 92.0%;再去掉字面量 \n 后到 98.8%。剩下的 35 条才是真正对不上的,主要是标题、公布令、章节标题、目录、表格和跨段拼接。
坑 4:2.0% 的引用跨段,仓库的 Recall 会绕过 chunk 边界。 归一化后,2,722 / 2,812 条引用(96.8%)完整落在某一个段落内;55 条(2.0%)跨段;35 条不可达。仓库的 RetrievalRecall 是把检索到的所有片段拼成一个大字符串,再检查每条 gold 是否出现——跨段引用也能算命中。做严格 chunk 级评测时,要自己实现更严格的口径。
坑 5:标题、章节、表格缺得比想象中多。 356 篇有章/节标题,共 3,295 个,JSON 只留下 5 个;539 篇里 450 篇的首行标题、379 篇的公布令在 JSON 里找不到;18 篇表格文档的 4,567 个非空单元格只有 2,029 个(44.4%)进入 JSON。35 条不可达 gold 里,7 条指向章/节标题、9 条指向标题、8 条指向公布令,其余是目录、表格和跨段拼接。
一个最小的 gold 对齐代码:
import json, re |
这段代码用分片跑是 2474 36,用全集跑是 2474 35。分片多出的 1 条,是肉制品那条引用在分片里的拼法和正文对不上。
3. 脚本:能做什么实验
3.1 脚本地图
| 文件 | 作用 | 状态 / 注意 |
|---|---|---|
hybrid_rag.py |
主流程:Chroma 向量检索 + ES BM25 + bce-reranker 重排 + 生成 | 默认跑 pro_LawQA-2.json;输出到 data/result/,目录不在仓库 |
rag_ERNIE.py |
用文心 ERNIE 基于已有检索结果生成 | 只跑前 5 条;路径和 API Key 要改 |
rag_qwen.py |
通义 qwen1.5-110b-chat 生成 | 只跑前 5 条 |
rag_Qwen2-7B-Instruct.py |
本地 Qwen2-7B(OpenAI 兼容端点)生成 | 端点写死在代码里 |
rag_baichuan.py |
智谱 Baichuan3-Turbo 生成 | API Key 硬编码在 chat_model.py |
rag_wenxin.py |
百度“文心插件”自带知识库 | 作为产品基线,不是用本仓库语料 |
src/bm_25.py |
Elasticsearch BM25 检索(match on content) |
仓库没给建索引脚本 |
src/chat_model.py |
5 个模型客户端封装 | 含硬编码凭据和内网地址,只读不抄 |
src/prompt.py |
RAG 提示词:无关拒答 + 强制输出引用编号 | 值得借鉴的设计 |
src/evaluate.py |
Recall / MRR / BLEU-2 / ROUGE-L 一体评估 | 主评测脚本 |
evaluate/evaluate_for_retrivel.py |
只算检索 Recall / MRR | 可用 |
evaluate/evaluate_for_class.py |
分题型评估 | 用 == '逻辑推理型' 会漏掉 527 条 |
evaluate/llm_retrieval_eval.py |
解析“参考检索结果:1,2,3”算引用准确率 | 只统计 isRecall 为 1 的题,口径偏乐观 |
src/retrieval_recall.py / retrieval_mrr.py |
独立检索脚本 | 对当前数据跑不通:document 是 list,却调用了 .split('\n') |
src/qa_Accuracy.py |
只算回答质量(BLEU / ROUGE) | 不看检索 |
3.2 仓库自带的评测口径
| 指标 | 实现 | 注意 |
|---|---|---|
| Recall | 把检索片段拼成一个大字符串,去掉空格和字面量 \n,要求每条 gold 都出现 |
绕过 chunk 边界,跨段引用也能命中 |
| MRR | 只看前 5 个片段,按 Σ(1/(rank+1)×命中数)/gold 数 计算 |
分母是 gold 条数,不是题目数 |
| BLEU-2 | jieba 分词,weights=(0.6, 0.4),> 0.4 记准确 | 只测字面相似 |
| ROUGE-L | rouge-chinese,> 0.6 记准确 |
同上 |
| 引用准确率 | 解析模型输出的“参考检索结果:1,2,3”,映射回片段后与 gold 双向包含匹配 | 法律场景最该看的一项 |
| 分题型 | 概念解释 / 概括归纳 / 逻辑推理 分开算 | 注意 class 前导空格 |
src/prompt.py 里有三个设计值得直接抄:与问题无关就别参考、没有相关内容就拒答、强制输出引用编号。最后一条是引用准确率能自动化的前提。
3.3 跑之前要知道的坑
requirements.txt是 UTF-16 LE + CRLF,pip install -r会解析失败;chromadb、BCEmbedding、jieba、rouge-chinese、dashscope等实际用到的包还没写全。- 没有 ES 建索引脚本,没有
docker-compose,data/result/和data/evaluate/目录也不在仓库里。 hybrid_rag.py的异常分支只print不赋值,模型调用失败后仍会用rag_answer['result'],直接NameError。- reranker 写死
device='cuda:0';没有 GPU 要改。 chat_model.py里有明文凭据和内网地址,抄代码时必须删掉。evaluate_for_class.py用精确匹配'逻辑推理型',而数据里是' 逻辑推理型',逻辑推理型会被整类漏掉。retrieval_recall.py/retrieval_mrr.py对当前数据跑不通,能用的是src/evaluate.py里的版本。
建议:数据可直接用,脚本只读不抄。 它更像一份“评测口径说明书”,不是能一键复现的工程。
3.4 用这些脚本能做什么实验
| 实验 | 怎么做 | 产出 |
|---|---|---|
| 检索策略消融 | BM25 / dense / hybrid / hybrid + rerank 各跑一遍 | Recall@k、MRR@k、Doc Recall@k |
| Embedding / reranker 选型 | 换 bce 之外的模型,固定生成模型和 prompt | 检索增益、显存/延迟成本 |
| 生成模型对比 | 同一份检索结果喂 5 家模型 | BLEU / ROUGE / 引用准确率 |
| Prompt 实验 | 加/减拒答约束、引用编号、无关过滤 | 拒答率、引用准确率、答案长度 |
| 分题型分析 | 概念 / 概括 / 逻辑分开报告 | 看推理题短板 |
| 语料范围实验 | 480 篇 vs 539 篇(59 篇当干扰项) | 检索难度和召回变化 |
| 背题检测 | 不给检索,直接 closed-book 回答 | 判断分数来自记忆还是检索 |
4. 六个值得做的 RAG 评测方向
4.1 检索策略消融:先把“检索”和“生成”拆开
问题:BM25、dense、hybrid、rerank 各带来多少增益?
设计:固定 chunk(先用 正文 的 27,176 块)、固定生成模型,只换检索器;语料分别用 480 篇和 539 篇跑两组。
指标:Recall@5/10/20、MRR@5、Doc Recall@k;再补一个严格 chunk 级 Recall(多引用题分 any / all 两种口径)。
价值:这是最便宜、最能说明问题的基线。仓库自带的 Recall 是拼接口径,容易虚高;自己实现严格口径后,才能看出 rerank 到底有没有用。
4.2 Chunking 对比:直接针对这份数据的最大短板
问题:按条切、固定窗口、small-to-big、条款边界切,哪种更适合法律 RAG?
设计:从 539 份 docx 重建四种语料:
- 按条切(保留“第X条”作为 metadata);
- 固定 512 字窗口;
- 按段切 + 命中后带邻居段;
- 章 → 节 → 条层级切分(条款边界)。
指标:Recall@k、MRR、上下文总字数、跨段 gold 命中率、答案引用准确率。
价值:这份数据里 2.0% 的 gold 跨段、3,295 个章节标题只剩 5 个、237 个 chunk 含多条——正好用来证明“切分策略会直接改变召回上限”。这也是最有可能做出差异化结论的方向。
4.3 中文法律域的 Embedding / Reranker 选型
问题:通用 embedding 在法律术语上够不够?reranker 值不值这个延迟?
设计:固定 chunk 和生成模型,比较 3–5 个 embedding(含 bce)和 2–3 个 reranker;分题型看增益。
指标:Recall@k、MRR、nDCG@k;再加每次查询的延迟和显存。
价值:法律 query 很短(中位数 20 字),条款又长又专业,通用模型容易在“相似但不相关”的条款上翻车。分题型报告能看出 reranker 对逻辑推理题是否更有效。
4.4 引用忠实度与幻觉:别只看答案像不像
问题:模型答对了,是真的用了检索结果,还是在背法条?
设计:
- 用
src/prompt.py的引用编号约束,算 Citation Accuracy; - 加一组 closed-book(不给检索)对照;
- 把“答案正确但引用对不上”的样本单独挑出来。
指标:引用准确率、答案-BLEU/ROUGE、closed-book 与 RAG 的分差、无依据回答比例。
价值:法律场景最怕“答案看着对、依据是编的”。公开法条很可能在预训练数据里,closed-book 分数不会低;如果 RAG 和 closed-book 差不多,说明你测到的是记忆,不是检索。
4.5 分题型能力评估,尤其是逻辑推理
问题:65% 的概括归纳题会不会掩盖逻辑推理题的短板?
设计:按 class 分三组报告;对逻辑推理题做错误分类(没召回到、召回到了但判断错、引用错)。
指标:分题型 Recall、答案准确率、引用准确率;逻辑推理题单列。
价值:这份数据里逻辑推理只占 21.3%,但它是合同/合规场景最接近真实工作的部分。总分好看不代表推理题能用。
4.6 长文档与上下文预算:RAG 还是整篇喂?
问题:法条很长(docx 最长 10 万字,单 chunk 最长 3,136 字),top-k 拼接和整篇长上下文,哪个更划算?
设计:同一批题跑三种上下文策略:
- top-5 chunk;
- top-5 + 邻居段;
- 整篇法规(或按章截断)直接喂长上下文模型。
指标:答案质量、引用准确率、输入 token、延迟、成本。
价值:这份数据的 chunk 长度分布很极端(中位 95 字,P99 557 字,最长 3,136 字),按“块数”算预算会严重失真。这个实验能给出“法律 RAG 的上下文到底该怎么花”的实测答案。
4.7 如果只做一件事
先做4.1 检索消融 + 4.2 Chunking 对比:用 docx 重建一版条级语料,跑 BM25 / dense / hybrid / rerank 四组基线,同时报严格 chunk Recall 和可达子集分数。这两步做完,你才知道后面换模型、换 prompt 的收益是不是真的。
最小流程:
| 步骤 | 做什么 | 产出 |
|---|---|---|
| 0 | 冻结版本:分片去空引用,或全集 + 清洗 | 2,474 条基准 |
| 1 | 从 539 份 docx 重建语料:章 → 节 → 条,补标题/表格 | 检索库 |
| 2 | 对齐 gold:归一化 + 映射到条/段 id | 可判定的 gold |
| 3 | 跑基线:BM25 / dense / hybrid / rerank | Recall@k、MRR@k |
| 4 | 评生成:ROUGE-L / BLEU-2 / 引用准确率 | 分题型报告 |
| 5 | 防背题:closed-book 对照 | 泄漏评估 |
5. 收尾
Legal-DC 的价值,不是“2,475 条”这个数字,而是它把“中文法规语料 + 条款级引用 + 题型分层 + 一套评测口径”放在了一起。但它是一份需要先修的数据:两个版本、偏移失效、章节和表格大量丢失、chunk 边界不一致。
如果只带走三件事:
- docx 是源头,JSON 是便利层;严格评测从 docx 重建,别直接信
正文的边界。 - 先归一化再匹配,别用
document_start/document_end;把 32 条在 docx 可达、在 JSON 不可达的引用单独列出来。 - 先跑检索基线,再谈生成;同时报严格 chunk Recall 和可达子集分数,别让数据噪声冒充模型差距。
6. 参考资料
- 仓库:legal-dc/Legal-DC
- 论文:arXiv:2603.11772
- 2026-10-28 内部调研:仓库源码与数据通读
- 2026-11-02 本地复测:539 篇 docx / 27,176 个 chunk / 2,474(全集)与 2,475(分片)/ 2,812 条引用;逐字覆盖率、章节/表格保留率、chunk 边界、gold 可达率均为逐条核对结果