ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

中文法律大模型实战:从基座选择到RAG增强的部署避坑指南

中文法律大模型实战:从基座选择到RAG增强的部署避坑指南 简介LexiLaw是一款面向中文法律领域微调的大语言模型资源包基于ChatGLM-6B架构针对法律条款解读、案例分析、法规咨询等任务优化。适合法律科技开发者、NLP研究者和法律专业学生快速搭建智能问答原型解决通用模型在法律场景下专业度不足的问题。压缩包内共60个文件、约1.48MB以Python源码为主导28个py覆盖模型加载、数据预处理、LoRA/P-tuning/Freeze等多种微调脚本、推理接口与Web演示资源同时提供sh启动脚本、json配置与Markdown说明便于直接运行和二次开发。文件结构围绕训练、推理、测试、demo等模块组织目录清晰。已有339人学习/下载。借助这套代码可复现LexiLaw的微调全流程掌握法律指令数据的构造方法与参数调优经验内置的推理脚本和知识库创建工具可快速生成面向具体业务的法律问答服务。对于希望将大模型落地到中文法律领域的团队这些实现与经验具有很强的参考价值。1. 为什么一个敢拿出来用的中文法律大模型这么少问题定位与 LexiLaw 的思路做法律信息化项目最头疼的不是模型不够大而是它引的法条“看起来都对实则张冠李戴”。我一个做合同审核系统的朋友拿通用大语言模型答法律问题先是把《民法典》生效后的合同纠纷答成“根据《合同法》”接着又把“过失致人死亡”和“故意伤害致人死亡”的构成要件揉成一团。他当时问我一句话中文法律问答到底该用什么模型LexiLaw 就是冲着这个缺口来的——一个面向中文法律智能问答场景的大语言模型资源走的路径是预训练基座加领域指令微调再配检索增强把“现行有效法条”变成外挂记忆。它解决的问题很具体法条时效性识别、条-款-项层级引用、罪名与案由区分这类法律问答。适合三类人法律信息化团队的工程师、给大模型做垂直落地的 NLP 从业者、拿法律 NLP 做毕设或课题的学生。2. 大语言模型落地法律问答LexiLaw 的技术栈与选型逻辑2.1 法律问答为什么难领域数据分布与通用模型的偏差通用大模型在开放域聊天里表现好是因为它见到的文本足够多但“足够多”不等于“法律上正确”。翻开一份判决书你会发现“根据《中华人民共和国刑法》第二百六十四条之规定”这种表述里法条编号、条款层级、引用顺序都是强约束。通用模型训练语料里中文法律文本占比通常很低而且大量内容是新闻转述、问答社区讨论不是严格意义的法条原文和裁判说理。于是模型学到的是一种“法律腔”而不是法律结构它知道该说“根据某法第几条”但具体第几条完全靠生成概率蒙。法律问答的诉求和开放聊天还不一样它分三种结论型、依据型、适用边界型。结论型问“这个合同能不能解除”模型容易过度肯定依据型问“依据是什么”模型容易引错款把第二款答成第一款“适用边界型”问“什么情况下例外”模型最容易翻车因为它不是在背法条而是在预测一个“看起来合理的句子”。真实场景里用户不会只问“盗窃罪判几年”他们会问“盗窃亲属财物是否构成犯罪”“公司末位淘汰是否合法”——这些问题的答案藏在一串关联法条和司法解释里通用模型根本没有那个数据密度。所以法律领域需要的不只是“更大的模型”而是“更懂法律分布的模型”。LexiLaw 这类资源通常提供一个完整链路继续预训练把法律语料的知识塞进参数指令微调让模型学会“结论依据”的输出格式再交给检索增强去解决时效性。理解了这个逻辑你才不会干出“把通用模型名字改成法律助手就上线”这种事。2.2 LexiLaw 的架构选择基座模型、中文分词与领域适配LexiLaw 的常见落地形态是基于开源中文基座Qwen、Baichuan、Yi 或 Llama 的中文增强版做增量继续预训练再做指令微调。选基座的时候我一般只看三点中文语料占比、上下文长度、license 是否允许商用。法律问答的场景输入通常是“事实描述问题”动辄几百到上千字基座上下文低于 8K 会非常难受如果项目要卖给客户而不是自用license 必须提前确认不然后面全是坑。继续预训练不是说零基础上法学——模型已经会语言只是不懂法律分布。常见做法是让模型在参数里记住“法条-概念-情形”的关联数据配比上通用语料 30%-50%、法律语料 50%-70%法律语料里再分三块现行有效法律法规、司法解释、裁判文书说理段落。裁判文书一定要做脱敏和格式清洗否则模型会学到一堆当事人信息噪声。LexiLaw 资源包里一般会把这些预处理脚本和数据格式说明一起给出来而不是只给一个黑匣子权重。分词这块很多人想歪了觉得应该自己加 jieba 词典。现在的基座都是 BPE 或其变体早就能把“掩饰、隐瞒犯罪所得、犯罪所得收益罪”切成合理子词。真正要注意的是“第X条”的序列稳定性训练时“第264条”和“第 264 条”如果混着出现推理时模型就可能在中间插空格或丢数字。我的习惯是把法条数字统一转成阿拉伯数字并去掉空格保持一种形态贯穿训练和推理检索和引用匹配的准确率会明显提升。2.3 检索增强RAG嵌入把法条变成模型的“外挂记忆”为什么法律场景绕不开检索增强因为模型参数里的知识有截止日期。新出台的司法解释、地方性法规、指导性案例模型不可能知道你也不可能为了一个条例更新就去重训一遍模型。RAG 的思路是让模型不再“背法条”而是“在给定条文的情况下做推理”。这里最关键的决策是文本切分策略按“条-款-项”切而不是按固定字符切。我见过太多人直接把法条全文按 512 个字符切成 chunk结果“第二百六十四条”和“第二款”被切到两个块里检索召回率直接崩掉。正确做法是用正则锚定“第X条”把每条条文作为一个基本单元条文内部保留款和项的编号。下面这段代码是构建索引的常见写法import json import faiss import numpy as np from sentence_transformers import SentenceTransformer # 1. 按条切分法律文本以“第X条”为正则锚点 def split_by_article(text): import re pattern re.compile(r(?Particle第[一二三四五六七八九十百千0-9]条)) parts [] last_pos 0 for m in pattern.finditer(text): if m.start() last_pos: parts.append(text[last_pos:m.start()]) parts.append(text[m.start():m.end()]) last_pos m.end() parts.append(text[last_pos:]) return [p.strip() for p in parts if p.strip()] # 2. 用中文向量模型把条文编码成向量 model SentenceTransformer(BAAI/bge-m3) # 常见中文embedding可替换 articles split_by_article(law_doc_text) vecs model.encode(articles, normalize_embeddingsTrue, batch_size16) # 3. 建faiss索引inner product对应余弦相似度 dim vecs.shape[1] index faiss.IndexFlatIP(dim) index.add(vecs.astype(np.float32))这段代码里有两个细节很多人第一次会踩第一normalize_embeddingsTrue必须和IndexFlatIP内积组合使用这样内积结果就是余弦相似度第二law_doc_text必须是你自己整理的现行有效法条全文而不是网上爬来的未校对稿件——脏数据进索引后面所有检索结果都是脏的。当法律库全文规模超过几十万条时IndexFlatIP的暴力检索还能撑住再大就该换IndexIVFFlat做倒排分片了代价是召回率略有损失。检索出来的 top-k 条文怎么进 prompt同样有讲究。我的模板是先给模型一段不可变更的“检索片段”再给问题明确要求“仅依据上述条文回答”。一旦你让模型自由发挥它就会开始“缝合”——把检索到的条文中没有的内容也编出来。这条约束直接关系到第 4 章要讲的幻觉问题先在这里埋个伏笔。3. 把 LexiLaw 跑起来环境准备、模型加载与第一轮问答3.1 环境与依赖Python 版本、CUDA 与显存规划本地部署 LexiLaw 的链路并不复杂但环境组合错了会浪费一整天。我常用的组合是 Python 3.10、CUDA 11.8 或 12.1、PyTorch 2.1 以上transformers 和 accelerate 都装最新版。不要用系统自带的 Python 3.8部分中文基座的 modeling 文件会用到较新的类型注解语法老版本直接语法报错。conda create -n law-llm python3.10 -y conda activate law-llm pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install transformers accelerate bitsandbytes sentencepiece protobuf这里有几个参数含义要说明--index-url指定的是 PyTorch 官方 CUDA 12.1 的 wheel 源避免默认源装成 CPU 版bitsandbytes是 4bit/8bit 量化的底层库显存不够时的后悔药sentencepiece和protobuf是部分中文分词器和模型配置读取的依赖版本冲突时优先把 protobuf 降到 3.20.x这是我踩过最多的坑之一。显存规划可以按下面这个表快速估算模型规模bf16 权重int8int4生成时额外开销7B约 14GB约 8GB约 4-5GBKV cache 约 1-3GB13B约 26GB约 13GB约 7-8GBKV cache 约 2-5GB表里“生成时额外开销”取决于max_new_tokens和batch_size回答越长开销越大。所以如果你只有一张 16GB 的卡又想跑 7B推荐 int8 而不是 int4——int4 下法条里的数字容易错位法律场景对“第X条第X款”的精确度要求极高省那 3GB 显存不值得。3.2 加载模型与分词器关键参数逐行拆解加载模型这一步不同基座的细节有差异但大框架一致。下面是配合 4bit 量化加载 LexiLaw checkpoint 的常见写法import torch from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig model_path path/to/lexilaw-checkpoint # 替换为你下载的LexiLaw权重目录 # 4bit量化配置显存不足时的最常用方案 bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.bfloat16, bnb_4bit_quant_typenf4, ) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, quantization_configbnb_config, device_mapauto, trust_remote_codeTrue, torch_dtypetorch.bfloat16, ) model.eval() print(model.hf_device_map) # 确认每一层被放到哪张卡参数含义逐个说load_in_4bitTrue表示用 4bit 量化加载权重显存占用直接砍到四分之一bnb_4bit_compute_dtypetorch.bfloat16是计算时反量化为 bf16兼顾精度与速度device_mapauto会自动把模型各层分配到所有可见 GPU 上单卡环境不需要手动指定trust_remote_codeTrue是中文基座最常见的坑——它允许执行权重目录里的自定义 modeling 文件不开就报“找不到模型实现”开了又怕执行到恶意代码所以务必确认权重来源可靠。加载完成后先print(model.hf_device_map)看一眼每层分到哪张卡这一步能提前发现“模型全被塞到第一张卡”的隐性 OOM。分词器这边没有额外参数但要注意检查tokenizer.eos_token_id如果为空生成时可能停不下来。3.3 第一轮问答指令格式与输出解析模型加载好之后第一轮问答别急着问复杂问题先跑一个单条样本验证链路通不通。法律问答的 prompt 模板和通用对话不一样我一般会做“角色输出格式三条约束”prompt 你是中文法律助手。回答时先给结论再引用现行有效法律条文。 若不确定直接回答“未找到依据”不要编造。 问题员工连续旷工三天公司能否据此解除劳动合同 回答 inputs tokenizer(prompt, return_tensorspt).to(model.device) output model.generate( **inputs, max_new_tokens512, do_sampleFalse, # 法律问答建议关闭采样输出确定性更强 temperature1.0, # do_sampleFalse时temperature不生效保留为默认即可 top_p1.0, repetition_penalty1.1, ) ans tokenizer.decode(output[0][inputs[input_ids].shape[1]:], skip_special_tokensTrue) print(ans)这里的核心参数是do_sampleFalse。通用聊天场景喜欢开采样让回答有“人味”法律场景恰恰相反——同一个问题今天答和明天答应该完全一致否则客户没法信任你。贪心解码让每步都取概率最高的 token输出确定性最强。max_new_tokens控制生成长度法条引用比较充分的回答通常在 300-600 token设 512 是一个折中repetition_penalty1.1防止模型在引用完第一条之后陷入“根据某某法第……根据某某法第……”的死循环。输出结构示意非法律意见请以现行有效文本为准结论可以解除。 依据《中华人民共和国劳动合同法》第三十九条第二项……其余条文引用略这套推理代码本身不长但很多人忽略了一点LexiLaw 资源包里的微调脚本用的是另一套生成配置训练阶段的temperature和推理阶段完全不是一回事。不要把推理阶段的低随机性参数直接搬去微调那会影响梯度更新的探索性。4. 法律大模型部署避坑显存、幻觉与数据污染的三个重灾区4.1 显存规划与量化选择现象加载 7B 模型特意买了 16GB 显存的卡模型加载到一半就 OOM日志里满屏 CUDA out of memory。原因7B 权重 bf16 约 14GB听起来 16GB 刚好塞得下但生成时的 KV cache 会随序列长度持续膨胀第一条回答只要超过 200 token额外开销就能吃掉 1-2GB16GB 根本不够用。解决4bit 量化是最直接的方案BitsAndBytesConfig里把load_in_4bit打开显存占用降到 5GB 以内剩下来的空间全给 KV cache。同时把max_new_tokens从 1024 降到 512batch_size保持 1这是最保守也最稳的组合。如果你在 int4 下发现“第二百六十四条”被答成“第二百六十五四条”别死磕量化参数切回 int8多花 3-4GB 显存换回数字稳定。还有一个隐藏问题CPU offload 不是后悔药是最后一根稻草。device_mapauto在显存不足时会把部分层 offload 到内存模型能跑起来但生成速度可能掉到每秒 0.5 个 token验证流程还能忍想做服务化就是灾难。我的边界判断标准是如果 offload 之后生成速度低于 5 token/s直接放弃单机推理转 vLLM 做多卡张量并行或者砍模型规模。4.2 幻觉问题模型“一本正经胡说八道”现象问某个 2024 年才生效的新司法解释模型答得头头是道编造了一个根本不存在的条文编号还煞有介事地引用“第X条”。原因模型参数里的知识截止到训练语料快照它不知道新法更麻烦的是如果微调阶段的“问答对”没有约束答案引用的法条必须真实存在模型就会靠概率缝合条文——把“比较像”的条款拼在一起。解决分三层。推理侧prompt 里强制写“仅依据下面给出的检索片段回答检索片段未包含的信息一律回答未找到依据”从源头锁死。后处理侧做一个简单的引用校验把输出里所有“第X条”抽取出来和检索到的条文做比对import re def check_citation(output_text, retrieved_articles): cites re.findall(r第[一二三四五六七八九十百千0-9]条, output_text) low_conf [c for c in cites if not any(c in a for a in retrieved_articles)] if low_conf: print(疑似幻觉引用:, low_conf) # 常见做法返回给模型重答或在日志里标记 return False return True这个规则很粗糙但极实用。注意“第 264 条”和“第264条”在字符串匹配时不一致抽出来之前先做空格归一化。训练侧在 SFT 样本里故意加入“无依据”样本教模型在检索不到信息时输出“未找到依据”而不是硬编造。这块做得好不好直接决定你上线之后被客户打脸的概率。4.3 数据污染与法条时效性现象民法典 2021 年已经生效模型回答合同纠纷问题还在引用《合同法》而且引用得“一本正经”。原因预训练或微调数据里新旧法同时存在模型没有学过“效力状态”这个维度旧法文本在参数里占的权重还很高它根本不知道“已废止”意味着什么。解决数据准备阶段给每条法条打上结构化的效力标记——现行有效/已废止/生效日期/废止日期微调样本里只保留现行有效条文外加少量“新旧法对照”样本让模型学会识别“依据已废止”。推理侧更直接RAG 语料库里只放现行有效文本用检索把旧法文本压到看不见。这里最容易被忽略的是测试集设计你至少要放两组用例一组是“新法生效后旧法是否适用”一组是“已被废止法条是否还能引用”否则你根本不知道模型什么时候会在旧法上翻车。还有一个更隐蔽的坑很多“法律大模型”翻车不是模型笨是数据快照本身过期。LexiLaw 这类资源一般会标明训练数据截止日期部署前先核对这个日期和你关心的法条版本是否对齐。比如你做婚姻家事场景就必须确认语料里民法典婚姻家庭编是完整的最新版本而不是草案或旧版。5. 把 LexiLaw 用出价值本地知识库增强与效果验证5.1 查询时检索把条文真正“给”到模型手里索引建好后真正上线时是查询时检索问题先进向量索引取 top-k 条文再拼进 prompt 交给模型。我自己的实践里有一个铁律检索到的条文只能原样拼接不允许模型“概括”条文——你让它概括它就可能把“应当”概括成“可以”语义就变了。query_vec model.encode([question], normalize_embeddingsTrue).astype(np.float32) scores, idx index.search(query_vec, k3) retrieved [articles[i] for i in idx[0]] retrieved_text \n.join(retrieved) prompt f仅依据下面的条文回答禁止使用条文以外信息 {retrieved_text} 问题{question} 回答top_k别贪多3-5 条最合适。条文太多 prompt 会变得很长模型反而“挑一条顺眼的”忽略其他关联条款这是检索增强最常见的隐性翻车点。5.2 用 200 条回归集拦住回退法律问答系统最怕的不是第一次做不好是改一次 prompt 之后旧的错误又回来了。我习惯维护一个 200 条左右的标注回归集每条包含问题、预期引用条文、预期结论三部分里面强制塞入四组负面样本新法生效、相近罪名区分、合同效力争议、但书例外。每次调整数据切分策略或 prompt都跑一遍回归指标计算方式关注点正确率模型结论与标注结论一致比例结论质量引用完整率答案引用与预期条文完全覆盖比例引用是否漏条拒答率无依据时正确回答“未找到依据”比例幻觉控制以前我拿到模型第一件事是调 prompt后来发现法律场景真正的命门是知识库的版本管理。那条新法生效的回归用例我上线前错过了一次被客户把截图甩到脸上。从那以后每次更新知识库我都强制走一遍新旧法对照测试数据不干净宁愿不上线。希望帮到你。本文还有配套的精品资源点击获取
返回列表