中文法律大模型实战:从数据处理到QLoRA微调与RAG增强

发布时间:2026/10/9 14:21:52
中文法律大模型实战:从数据处理到QLoRA微调与RAG增强
简介面向中文法律领域的人工智能大模型应用资源包适合自然语言处理研究者与法律科技开发者参考。内含中文法律大模型ChatLaw相关成果覆盖模型框架说明、评测数据、演示脚本与运行脚本并配有架构图、效果对比图及微信交流群二维码截图便于快速理解构建思路与落地演示方式。资源共35个文件以架构图与演示截图等图片为主辅以JSON/JSONL法律数据文件、Markdown说明文档、Python脚本与Shell一键运行脚本包含法律概念与法律咨询等演示数据集压缩包约7.78MB目录组织清楚。已有270人学习下载适合希望借助实际案例快速上手大模型应用、进行法律场景技术验证的读者。1. 中文法律大模型先搞清楚 zip 里是什么再决定值不值得投入拿到一个《AI大模型应用》-中文法律大模型.zip先别急着解压。这个压缩包可能装着权重、训练代码、数据集也可能三样都有但不管里面是什么它指向的是一个明确的需求用大模型处理法律文本让机器能读法条、写文书、回答法律咨询。这类项目的难点不在“跑起来”而在“跑得准”——法律场景对引用精确度和逻辑严谨性的要求远高于通用聊天场景。适合谁做法律科技产品、律所内部工具、法律咨询服务的团队和个人开发者。如果你只是想凑一个能聊天的机器人通用 API 就够但如果你要让模型引用具体法条、按判决书格式输出就得自己下场训。下面按选型、数据、微调、排错、验证的顺序把我自己做过一遍的路讲清楚。2. 为什么通用大模型搞不定法律场景三个能力缺口与选型判断做法律大模型先别急着找训练脚本。我见过不少团队把通用模型拉起来就问“合同有哪些风险”前几个回合表现不错一旦追问“依据是哪一条”模型就开始顾左右而言他。这不是模型笨而是通用模型的能力结构跟法律任务压根不对齐。这章把三个能力缺口讲透顺带算一笔账什么情况下值得自己训。2.1 能力缺口一法条记忆是“模糊”的法律要求“精确到款”通用模型对法条的记忆是“意会”式的。它能说出“定金罚则大致是定金不超过主合同标的额百分之二十”但当你问“民法典第五百八十七条规定的是什么”它可能把定金和违约金混在一起甚至编出一个不存在的“第五百八十八条第X款”。法律写作的引用规范是精确到条、款、项差一个字都可能被认定为引用错误。通用模型的训练语料里法条原文出现的次数远低于小说和新闻模型只能凭上下文猜测。微调阶段要做的事就是让模型大量、反复地看“法条原文 适用场景”的组合把模糊记忆扳成精确记忆。具体到数据制作法条原文必须重复出现在训练集里。我一般会把一部法律按章切块每一章的法条原文刷三遍再配三倍数量的“用户提问 法条引用”样本让模型形成“问到什么就带出哪条”的条件反射。半对的回答比全错更危险——“经济补偿每满一年一个月工资”看着对漏了“月工资是指劳动合同解除前十二个月平均工资”这一句用户照做就会算错钱。2.2 能力缺口二法律推理要“三段论”不是“跳跃联想”法律适用的基本路径是大前提法条构成要件→ 小前提案件事实→ 结论。一份合格的判决书“本院认为”部分必须先把法条要件列出来再对照事实逐一分析最后才给出裁判结果。通用模型走的是“联想”路线。你给它案情它直接跳出一个大概率结论中间的要件分析往往被压缩成一句“经审查符合法律规定”。这在聊天里没问题在法律文书里就是逻辑硬伤。训练样本如果只有“问题—答案”没有中间推理链模型学不会三段论。数据构造时至少要有一部分样本带上完整的“法条摘录 → 要件拆解 → 事实比对 → 结论”链条。我还会把要件类型提前枚举出来比如“主体适格”“合同效力”“违约事实”“损失数额”让模型逐项填空而不是自由抽取。逐项填空的格式错误率比自由抽取低一半以上这是文本结构化任务里最值得复用的技巧。2.3 能力缺口三输出格式有“强制结构”不是“自由生成”法律文书是强格式文本。判决书分首部、诉辩意见、事实认定、本院认为、判决主文起诉状有当事人信息、诉讼请求、事实与理由、此致法院合同审查意见有风险点、修改建议、依据条款。每类文书的结构基本固定用户拿到一份结构混乱的文书哪怕内容正确也大概率退回重做。通用模型的格式服从训练语料里的混合风格今天给你“您好关于您咨询的问题……”明天给你“根据相关法律法规……”完全不可控。所以微调数据里必须有结构模板最好用特殊标记把各段分开让模型把“结构”当成一种技能来学。我常用的做法是在样本的输出字段里直接用换行和“一、二、三”带出段落对应起诉状的诉讼请求、事实与理由、法律依据。模型看到这种成对样本多了会自发把结构迁移到没见过的案由上。2.4 先算账再动手微调、RAG 和通用 API 怎么选很多人把“中文法律大模型”理解为“必须训模型”其实先要做的是方案选型。我的判断标准很简单如果任务是“回答法律常识”通用 API 加一份提示词就够如果任务是“能指出依据是哪个法条第几款”先做 RAG 检索把法条原文召回后拼进上下文只有当任务涉及“生成整篇文书”或“按本院认为的格式输出”才需要动微调。业务场景推荐方案数据要求主要成本法律常识问答通用 API 提示词无需自备数据API 调用费法条精确问答通用 API RAG 检索法条库清洗向量库 API起诉状/合同意见生成微调 RAG5000 条以上高质量成对样本单卡 A100 数天判决预测 / 类案推送微调10000 条以上带标注数据数据标注 训练我一般会把需求占比先盘点一遍如果 80% 是问答、20% 是文书生成那就用通用 API 加 RAG少量文书模板做提示词微调而不是一上来就训 14B成本差一个量级。如果数据少于 5000 条或者连一个能跑通 RAG 的向量库都没有微调可以先放一放。法律大模型的数据成本远高于训练成本这一步省了后面全是坑。3. 法律语料处理从法条和裁判文书造出合格的训练样本训练法律大模型真正占时间的是数据不是训练。裁判文书网的原始文书动辄几十万份但直接拿去微调模型学到的多半是“本院认为”的套话而不是法律推理。这章按清洗、构造、去重三步走每一步都给可执行的代码和参数。3.1 法律数据清洗先去掉噪声再谈训练原始语料常见三类噪声HTML/PDF 残留、当事人信息、法院程序性套话。清洗顺序是先格式、后脱敏、再过滤。import re def clean_court_doc(raw: str) - str: # 去 HTML 标签与实体 text re.sub(r[^], , raw) text re.sub(r[a-z];, , text) # 去全角空格、零宽字符、BOM text text.replace(\u3000, ).replace(\ufeff, ) text re.sub(r[\u200b-\u200d\u2060], , text) # 压缩连续空行为单行 text re.sub(r\n\s*\n, \n, text) return text.strip()这段代码的作用是先把非文本内容清掉。第 2 行的正则把 HTML 标签整体剔除第 4 行处理常见的 HTML 实体比如nbsp;第 6 行处理 Word 粘贴常见的全角空格和 BOM第 8 行压缩空行。跑完这步后文书变成紧凑的纯文本方便下一步脱敏。脱敏不能只换名字。裁判文书里至少包含三类敏感信息当事人姓名、身份证号/住址、代理律师所在律所。姓名可以用正则替换成“张某某”但更稳的做法是给每个当事人分配一个随机占位符这样同一个当事人在整篇文书中保持一致模型不会把“张三”“李四”当成两个不同的人。提示法院名称和法官姓名建议保留那是说理依据的一部分换了会影响模型对“本院认为”的理解。清洗后先统计一遍文本长度分布。法律文书长尾很多超过 1.2 万字的单独挑出来做滑窗切分每窗 8000 字、重叠 500 字防止后续训练时max_seq_len装不下。这一步不花多少时间但能救回大量长判决书样本。3.2 从原始文书构造三类训练样本清洗完的文书还不能直接进训练集。通用模型的对话格式是 instruction/input/output法律任务也要落到这个格式。样本从哪来最常见做法是把公开裁判文书按案由分桶用规则抽“本院认为”段落法条问答对则从法律法规库按“条”切块。如果预算允许用大模型把案情改写成第一人称咨询问法能显著提升模型的真实对话能力——但改写后的内容要有人工复核否则数据里混进幻觉模型就学歪了。我一般把样本分成三类比例按 3:5:2。第一类是“案情摘要 → 裁判要旨”从文书的“本院认为”段抽出来案情做 input裁判理由做 output目的是让模型学会从事实到结论的映射。第二类是“用户提问 → 法条原文 解析”问题用真实的用户问法输出必须是“法条编号 原文 适用条件”三段式。第三类是“文书要素 → 成型文书”输入是当事人、诉讼请求、事实概述等结构化字段输出是完整的起诉状或合同审查报告。{instruction: 请依据以下事实要件出具一份民事起诉状, input: 原告张三被告李四双方于2023年签订借款合同……, output: 民事起诉状\n原告张三……\n诉讼请求……\n事实与理由……\n此致\nXX人民法院\n具状人张三}构造时要注意两点。一是输出里的法条必须和 input 里的金额、期限、情节对得上不能出现“判赔 50 万”但引用的是“不超过 30%”的条文。二是输出篇幅要可控裁判要旨尽量控制在 500 字内起诉状控制在 1200 字内超过这个长度的样本要拆成多轮样本否则训练时长会翻倍而且模型容易在中间段开始复述输入。3.3 去重与防泄漏一个做不好就白训的环节法律文本的重复率高得惊人。同一地区法院对同类案件的“本院认为”经常大段相同如果不去重训练集里会出现几百份几乎一样的样本。from datasketch import MinHashLSH def build_lsh(texts, num_perm128, threshold0.7): lsh MinHashLSH(thresholdthreshold, num_permnum_perm) for idx, text in enumerate(texts): # 按 4-gram 切分生成 MinHash m MinHash(num_permnum_perm) for i in range(len(text) - 3): gram text[i:i 4] m.update(gram.encode(utf-8)) lsh.insert(fdoc_{idx}, m) return lshMinHashLSH 的思路是先把文本切成 4-gram 集合再算集合的 Jaccard 相似度超过阈值的样本直接丢弃。num_perm越大相似度估算越准128 对中文文本够用threshold在 0.7 附近比较稳太低会把不同案由的样本错杀。如果不想引入外部依赖也可以用 Bloom filter 做轻量查重它只能告诉你去重后的集合大小不能给出相似度样本量到几十万时可以作为第一道粗筛再用 MinHash 做精筛。实际项目中我两件套都用粗筛把重复量杀掉大半精筛卡相似度阈值。这一步除了去重还有一个作用防止测试集和训练集“串味”。切分测试集时按审结日期排序拿后半年的数据做测试保证模型没见过同期的相似案件。4. 用 QLoRA 微调中文法律大模型训练脚本与关键参数4.1 基座模型选择7B 起步14B 封顶常见做法是基于 Qwen2.5-7B-Instruct 这类中文底座做 QLoRA。选择理由有三方面中文指令遵循能力成熟7B 权重文件在 15G 左右单卡可跑Llama 系列需要做中文词表扩充和继续预训练成本更高。如果你要用 14B先确认显存不低于 24GQLoRA 4bit 量化下也要 16G 起步。基座模型的知识截止日期也要查。法律领域法条更新频繁比如新公司法和各类司法解释基座模型如果不知道这些微调时就得放更多相关语料去压过底座里的旧记忆。我一般会在数据准备阶段先跑几条“地标性问题”比如“公司法认缴期限最长为几年”看底座是否已经更新。还有一点容易被忽略基座模型的词表里要有法律术语。Qwen 这类中文模型基本没问题但某些英文模型把“抗辩”翻成 defense 再转回来术语就歪了。选底座前先跑一遍术语回环测试给模型一段判决书让它复述看术语是否保持中文原样。4.2 QLoRA 训练脚本与关键参数训练框架可以用 HuggingFace 的 peft transformers或者直接用 LLaMA-Factory 的 qlora 子命令。下面这版是直接用 transformers 写的方便按需改。CUDA_VISIBLE_DEVICES0 python train_qlora.py \ --model_name_or_path Qwen/Qwen2.5-7B-Instruct \ --dataset_path data/legal_train.jsonl \ --output_dir output/legal-lora \ --lora_rank 64 \ --lora_alpha 128 \ --lora_dropout 0.05 \ --learning_rate 2e-4 \ --num_train_epochs 3 \ --max_seq_len 8192 \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 8 \ --bf16 True \ --logging_steps 20 \ --save_steps 500针对法律场景这几个参数必调参数我的常用值说明lora_rank64法律任务涉及术语和格式低秩 8-16 容易记不住法条编号lora_alpha128一般设为 rank 的两倍决定微调强度learning_rate2e-4QLoRA 常用区间 1e-4 到 3e-4太大 loss 抖动明显max_seq_len8192判决书一段经常超过 2000 字4096 太小num_train_epochs3法律样本重复度高超过 5 轮基本过拟合batch size 我给 1配上 8 的梯度累积等效 batch 为 8。显存不足时先降max_seq_len到 4096再降lora_rank最后才动 batch。反过来如果你开的是 80G 显存把per_device_train_batch_size提到 2训练时间能缩短约三分之一。数据集里如果有对话历史要做多轮样本。法律咨询经常是连续追问用户先说“公司欠我三个月工资”再问“能申请仲裁吗”两个问题要放到同一条样本里否则模型上线后接不住上下文。多轮样本的比例我控制在 15% 左右太高会稀释单轮指令学习。注意如果训练到一半断电或 OOM把save_steps改小一点用resume_from_checkpoint接着跑不要从头再来。这个套路在 ai 大模型应用开发里非常通用。4.3 训练中看什么loss 曲线与三个异常信号训练过程中的观察比跑通本身更重要。loss 从 2.x 降到 0.5 附近是法律微调的正常轨迹降得太快比如 100 步内就低于 0.4说明样本太简单或重复度高需要提前停。第二看评估集 loss如果训练 loss 继续降、评估 loss 回升这是典型过拟合应该回去盘数据去重而不是调小模型。第三看“生成样本”而不是只看 loss——每 500 步用同一组固定 prompt 生成几条答案肉眼扫一眼格式是否稳定。LoRA 参数选择在我这里一半经验一半玄学rank 从 64 起步遇到记不住法条就往上加到 128遇到输出格式混乱优先检查数据里的格式标记不用急着改 rank。如果生成结果出现“本院认为”之后直接复述案情多半是样本里 output 和 input 字段顺序搞混了去检查 JSONL 的字段映射而不是调训练参数。4.4 合并 LoRA 权重并导出微调后的 LoRA 适配器要合并回基座才能正常部署。python merge_adapters.py \ --base_model Qwen/Qwen2.5-7B-Instruct \ --adapter_path output/legal-lora \ --output_path models/legal-7b-merged \ --quantize 4bit合并后用 vLLM 部署推理参数里 temperature 调到 0.7 以下法律文书生成要降低随机性。合并后务必再跑一遍 4.3 的固定 prompt 集因为合并和量化过程偶尔会改变采样行为输出格式可能发生微妙变化。这一步常被我用来做“后悔药”——合并前保存一份适配器合并后效果不对还能退回去调数据。5. 法律大模型避坑指南五类翻车现场与排查思路法律大模型项目十个有九个翻车在细节上。这章按数据、训练、评估三个阶段写五条我实际踩过或帮别人排查过的坑。排查顺序建议固定先看压缩包和数据再看训练日志最后才动评估口径不要一上来就怀疑模型架构。5.1 压缩包与数据侧的坑现象一下载的法律大模型压缩包解压报错caused by: invalid zip archive: could not find eocd。原因zip 的 end of central directory 记录在文件末尾报这个错几乎都是下载过程被截断——文件没传完或网盘中转出错不是格式不支持。解决先看下载文件和标注大小差多少字节差几十 KB 就重下重下还报错用zip -FF修复或者换压缩软件。批量解压时先解压一个文件别一次全解不然一个坏包连累整个流程。现象二训练集和测试集高度雷同模型“背答案”却测不出来。原因裁判文书同类案由表述相似度极高按随机比例切分时同一个案件的不同文书片段会同时掉进两个集合。解决按案号或审结日期做时间维度切分测试集只取后半年的数据再做一次 MinHash 去重把训练集对测试集的相似度上限卡到 0.5 以下。排查时先算重叠分布把训练集和测试集的文档交并比跑出来超过 0.1 就说明切分有问题。5.2 训练与生成侧的坑现象三模型振振有词地引用“《民法典》第六千六百六十六条”。原因基座模型没见过这条微调数据里法条原文覆盖不全模型为了回答就编条文。解决法条原文单独扩写进样本“用户问法条内容 → 法条编号 原文”成对样本占总数据量的 20% 以上。上线前把常用法条清单逐条问一遍标记所有“查无此条”的输出回头补数据。现象四长文书生成到一半戛然而止输出截断。原因decoding 阶段的max_new_tokens设得太小或者模型学会了在“此致 XX 法院”后直接结束。解决生成时把max_new_tokens放开到 2048同时微调样本里确保“此致”之后还有落款和日期还要检查样本结尾有没有统一的 EOS 标记没有的话模型学到的结束符是乱的。另一个隐蔽表现是模型在“本院认为”之后开始说套话这是反向截断——output 里混入了大量模板开头模型把模板当成了正文。5.3 评估与上线侧的坑现象五自动评估 BLEU 分数挺高人工一看全是不敢用的废话。原因法律文书的参考答案偏短BLEU 对“关键词命中”敏感模型只要说出“合同”“违约金”“解除”几个词分数就上去了但引用条款和金额全错。解决评估指标换成“法条引用准确率 要件覆盖度”。引用准确率的做法是让模型输出的“第 X 条第 Y 款”和标准答案做精确匹配要件覆盖度的做法是把案件事实的必查点做成 checklist模型输出里提到就算过。人工复核流程也省不得抽 20 条真实用户问题把模型输出按“引用正确、要件完整、可直接用”三个档位过一遍两档及以上通过才放行。6. 把知识库接进模型RAG 增强与效果验证技巧6.1 法条库检索让模型引用前先查证微调出来的模型知识是静态的法条更新一次就要重训一次。更经济的做法是把法条库做成 RAG让模型先检索再回答。我一般把法规按“条”切块每条生成向量metadata 存颁布日期和效力级别检索时只召回当年生效的条文。from sentence_transformers import SentenceTransformer import chromadb model SentenceTransformer(uer/bge-small-zh-v1.5) client chromadb.PersistentClient(path./legal_db) collection client.get_or_create_collection(laws, metadata{hnsw:space: cosine}) collection.upsert( ids[flaw_{i} for i in range(len(laws))], documents[law[text] for law in laws], metadatas[{effective: law[date], level: law[level]} for law in laws], embeddingsmodel.encode([law[text] for law in laws]).tolist(), )检索时把 top-5 的原文拼进 prompt再让模型回答。这一步能把“引用准确率”从 60% 拉到 90% 上下。注意 embedding 模型要用中文语料调过的版本通用英文 embedding 对法条效果会明显偏差。再往上走把“用户提问 → RAG 检索法条 → 模型生成文书 → 结构化校验”串成链路这也是 ai 智能体 应用案例里最常见的编排方式。6.2 一套低成本验收清单模型上线前至少过一遍这份清单10 条高频法条问答的引用准确率10 份真实文书的格式合规率5 条新颁布法条的“不知道就检索”兜底表现20 条对抗样本故意问“押金不退怎么办”这类口语化问题。我自己在这类项目上栽过的跟头是只盯着 BLEU 调了一周上线后被律师一眼看出“判决金额和法条对不上”。后来把所有评估指标换成引用准确率和要件覆盖度项目才真正交付得了。如果你刚开始做中文法律大模型建议先跑通“法条 RAG 7B 微调”这个最小组合再谈更大底座。希望帮到你。本文还有配套的精品资源点击获取