
简介一份聚焦中文法律知识的大语言模型应用案例包面向AI大模型学习者、法律领域NLP开发者及高校相关专业学生旨在解决法律文本理解、模型微调与推理落地等实践问题。压缩包共42个文件包含12个Python脚本覆盖微调、推理、评估、合并等核心流程6个JSON格式指令与配置数据5个Shell运行脚本8张JPEG示例图、2张PNG图片及2个txt说明文档整体仅3.41MB轻量而结构清晰。目前已有120人学习下载适合作为入门法律大模型的实操参考。目录按tools、assets、data、models、scripts等模块组织内容涵盖数据准备、指令微调、模型推理与WebUI演示的完整工具链附带示例输出与运行脚本便于快速复现实验流程并在此基础上进行二次开发。1. 中文法律大语言模型拆解从LoRA训练到本地部署法律场景对生成内容的“正确性”要求和通用聊天完全不同。同样是解释“合同诈骗罪”模型可以组织出通顺句子不意味着构成要件引用得对。我最近拆了一套基于中文法律知识的大语言模型工程它没有走从零预训练的路线而是把通用基座模型、LoRA增量训练、法律指令数据和推理界面串成一条可落地的流水线。这套东西适合两类人一类是要做AI大模型应用开发、想快速产出领域模型的工程师另一类是正在评估本地部署AI大模型的企业希望把模型放在自己的服务器上而不是云端API。接下来我从数据准备、训练参数、合并推理到界面部署逐个拆。2. 法律数据配方从criminal_charges.json到训练样本领域微调的第一步从来不是堆GPU而是把原始语料变成模型能消费的指令样本。这个项目里没有把整部刑法丢给模型而是先整理了罪名库、指令集和法律词表这一点很关键因为法律知识密度高、噪声也高直接拿原始法条做无监督预训练容易让模型变成“条文复读机”。2.1 资源包里的法律数据文件解压后能看到几个核心数据文件我按用途整理如下文件路径内容类型在微调中的用途data/criminal_charges.json罪名和构成要件数据生成罪名判断、要件分析类指令data/example_instruction_train.jsonAlpaca风格指令训练集主训练数据data/example_instruction_tune.json指令调优数据第二轮针对性微调data/example_infer_data.json推理评测样本用于验证训练效果resources/legal_vocab.txt法律领域词表扩展基础分词器训练集和推理集分开是我判断一个微调工程是否专业的第一信号。如果只有一个文件通常说明作者没考虑过“训练效果到底怎么验证”的问题。2.2 Alpaca模板与法律Prompt组织方式example_instruction_train.json里的样本基本是这种结构{ instruction: 请根据以下案件事实判断被告人构成什么罪并说明理由。, input: 被告人利用伪造的合同以收取保证金为名骗取他人30万元后逃匿。, output: 被告人构成合同诈骗罪。理由其以非法占有为目的在签订、履行合同过程中骗取对方当事人财物数额巨大。 }这是Alpaca的经典三段式指令、输入、输出。训练时不能把这三个字段直接拼成一长串丢给模型而是要借助prompter.py和templates/law_template.json把它转换成一个完整的对话提示词。注意templates目录里同时有alpaca.json和law_template.json。两者差异不只是名字法律场景更强调“严格解释”所以在我的使用习惯里law_template.json的开头通常会被改成一个系统级约束比如“你是一名严谨的法律顾问不确定时必须说明无法判断”。这样训练出来的模型在边界问题上更倾向给保守回答而不是一本正经地编法条。2.3 清洗与词表合并的工程处理原始JSON里难免有空白字符、超短段落和重复条目直接用会污染训练分布。项目里提供了tools/clear_law.py来做这块工作我通常这样跑python tools/clear_law.py \ --input data/criminal_charges.json \ --output data/cleaned_charges.jsonl \ --min-length 20 \ --drop-duplicates这个脚本的逻辑不复杂按行读入JSON过滤掉字符长度低于--min-length的文本再用--drop-duplicates去重。实际效果是让训练集里少掉“一句话罪名解释”这类低质量样本。如果发现清洗后的数据量缩水严重优先放宽长度阈值而不是关闭去重因为重复样本会让模型对高频罪名过拟合。中文分词是法律模型的另一个坑。通用分词器经常把“不当得利”“表见代理”这类词切得七零八落。项目里的tools/merge_vocabulary.py就是用来把legal_vocab.txt中的法律术语并入分词器的python tools/merge_vocabulary.py \ --vocab resources/legal_vocab.txt \ --tokenizer_dir models/base_models \ --output_dir models/base_models参数含义分别是法律词表路径、基座模型分词器所在目录、合并后的输出目录。这里要提醒一句models/base_models目录在安装包中只包含.gitkeep占位文件实际使用时需要先把基座模型权重放进去。词表合并也不是越多越好专门术语加到几千个已经是上限继续膨胀会稀释模型原有的token embedding表达能力。提示合并词表后基座模型和LoRA微调必须使用同一份分词器文件否则推理时会出现token id错位。3. LoRA微调中文法律大模型train_clm.sh与finetune.py的配合微调这段是工程的核心。我见过不少人把通用模型拿过来直接跑对话结果法律问题答得“很流畅但完全不可用”。问题不在模型而在训练方式。这个项目提供了train_clm.sh和finetune.py本质上是把“基座模型 LoRA 法律指令数据”组合起来。3.1 为什么选LoRA而不是全量微调一个7B参数的模型用fp16全量微调光训练状态就要几十GB显存普通团队很难负担。LoRA的思路是冻结原来的权重在旁边加一小对低秩矩阵训练时只更新这部分参数。实际可训练参数量通常只有原来的0.1%到0.5%显存占用大幅下降。更重要的是LoRA在法律场景里还有一个隐性好处它保留了基座模型的通用中文能力。如果做全量微调模型很容易在法条数据上“学得太死”日常表达和逻辑推理能力退化LoRA的低秩约束反而让增量知识像补丁一样附加在原模型上。3.2 finetune.py训练脚本的关键参数我先说明一下train_clm.py和finetune.py的分工。train_clm.py是因果语言模型的训练入口finetune.py则是封装了数据加载、prompt模板和LoRA配置的高层脚本。实际训练时我直接用finetune.py因为它已经处理好了templates/law_template.json的读取。scripts/train_clm.sh里的训练命令可以整理成下面这种形式python finetune.py \ --model_name_or_path models/base_models \ --data_path data/example_instruction_train.json \ --output_dir outputs/lora-law \ --num_train_epochs 3 \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 8 \ --learning_rate 1e-4 \ --lora_r 8 \ --lora_alpha 16 \ --lora_dropout 0.05 \ --template law_template \ --logging_steps 10 \ --save_steps 200 \ --gradient_checkpointing这里几个参数值得细调。lora_r决定低秩矩阵的维度太小拟合不了法律知识的复杂度太大会让LoRA失去省显存的意义lora_alpha是缩放系数常见经验值是alpha 2 * r所以r8配alpha16是比较稳的起点。gradient_accumulation_steps8配合per_device_train_batch_size4实际等效batch size是32这是为了在小显存卡上模拟较大batch。参数速查表参数作用调参建议lora_r低秩矩阵秩数8到16任务复杂取高值lora_alpha新权重缩放系数一般设为lora_r的两倍gradient_accumulation_steps梯度累积步数显存小时调大保持等效batch不变gradient_checkpointing用计算换显存训练必开推理不需要num_train_epochs训练轮数法律指令数据量小3到5轮足够3.3 模板与训练数据字段的映射逻辑finetune.py内部会读取templates/law_template.json把原始JSON里的instruction、input、output三个字段拼接到模板中。我在排查问题时常遇到一种情况训练loss下降正常但模型生成结果一直没有法律味道最后发现是--template参数传成了alpaca。alpaca.json不包含法律系统提示词输出自然偏向通用表达。所以在整个流程里law_template不是可选项而是法律语义注入的关键点。如果想让模型在开头就意识到自己面对的是法律咨询可以把模板的system部分设置为“你是法律AI助手请基于中国现行法律回答问题禁止编造法条。”3.4 callbacks.py在训练中的实际作用训练不是启动之后就完事了。callbacks.py这个文件在长训练中很重要它一般会实现一个保存PeftAdapter的Callback。默认HuggingFace Trainer在save_steps触发时保存的可能是完整模型状态而LoRA训练只需要保存低秩矩阵文件体积只有几十MB。项目里的callbacks.py就是干这个的每个保存点把adapter单独存一份供后续merge.py使用。如果不理解这层关系训练完在outputs/lora-law里可能找不到adapter_model.bin误以为训练失败。实际只要finetune.py日志里出现“Saving model adapter”之类字样就说明LoRA权重保存成功了。4. 合并LoRA权重、推理与法律生成质量评估训练产出的是LoRA adapter文件不是完整可推理的模型。很多把微调模型拿去上线的人在这里栽了跟头直接用adapter加载搞出一堆维度不匹配错误。正确顺序是先合并再推理最后评估。4.1 merge.py把LoRA合成回基座模型merge.py负责把基座模型和LoRA权重做一次物理合并。命令如下python merge.py \ --base_model models/base_models \ --lora_model outputs/lora-law \ --output_dir models/law-merged合并之后得到的是完整模型权重这样做有两个好处一是推理时不依赖额外逻辑去加载adapter模型加载速度和稳定性都更好二是合并后的模型可以继续接量化部署工具不用再考虑两层权重的耦合关系。如果中间改过legal_vocab.txt并合并了词表建议把合并后的分词器目录也作为--base_model传入避免出现token id映射偏移。4.2 infer.py配合prompter.py做实测模型合并完成后先用指令样本做一次人工冒烟测试。项目里的prompter.py封装了模板拼接逻辑我可以像这样直接调用from prompter import Prompter prompter Prompter(law_template) prompt prompter.generate_prompt( instruction判断以下行为是否构成盗窃罪并给出你的分析。, input行为人趁邻居外出将邻居停放在院内的电动车骑走并转卖。 ) print(prompt)这段代码做的事情是加载law_template.json把instruction和input填充进去生成完整的模型输入。直接打印prompt能让我们确认模型看到的内容是否符合预期。然后再走批量推理脚本python infer.py \ --base_model models/law-merged \ --data_file data/example_infer_data.json \ --template law_template \ --max_new_tokens 256--max_new_tokens控制生成长度。法律回答往往需要列案情分析再下结论256个token通常是最低要求。如果生成结果经常被截断可以调到512但要小心显存占用随序列长度增长。4.3 evaluate.py与评估指标的选择evaluate.py是容易被忽略但很有价值的文件。它提供了训练阶段的指标评估入口。我实测下来法律生成任务的自动评估一定要分层看待评估方式能说明的问题主要局限Perplexity模型对法律文本的拟合程度不代表生成内容合规Rouge-L与参考答案的重复覆盖度法律表述灵活分数低不代表差人工罪名命中率模型是否判对核心罪名需要人工标注成本高我的做法是用evaluate.py跑一遍Rouge做基线但不把它作为唯一标准。法律场景更该看的是“关键构成要件是否出现”比如最终结论是否落在正确罪名上。这个可以在infer.py输出后再用关键词规则或一个小分类模型去命中验证。5. webui.py快速搭本地法律问答界面以及4bit部署要点模型能跑通命令行后下一步是给业务方用。项目里的webui.py用Gradio做了一层界面封装我把它当作快速验证工具而不是生产系统。5.1 基于webui.py启动交互服务确认合并模型路径无误后直接启动WebUA服务python webui.py \ --model_path models/law-merged \ --template law_template \ --server_port 7860启动成功后浏览器访问服务器IP对应的端口就能在对话里提问。这里的--template law_template必须和训练时一致否则前端输入的instruction不会按法律模板封装生成质量会明显退化。如果只想先做一个小型演示可以把界面逻辑简化成下面的模式import gradio as gr from webui import LawWebUI app LawWebUI(model_pathmodels/law-merged) gr.Interface( fnapp.predict, inputstext, outputstext, title本地法律AI问答 ).launch(server_name0.0.0.0, server_port7860)这里LawWebUI只是一个示例封装实际项目中webui.py会加载模型和prompter。我一般先用一条“劳务合同和劳动合同争议有什么不同”做验证因为这个问题不需要专业知识也能判断回答是否合理。5.2 显存不够时的降载组合拳如果部署机器的显存不够跑全量模型优先做两件事把模型加载改为4bit再开CPU offload。下面是常见的加载方式from transformers import AutoModelForCausalLM model AutoModelForCausalLM.from_pretrained( models/law-merged, load_in_4bitTrue, bnb_4bit_quant_typenf4, bnb_4bit_compute_dtypebfloat16, device_mapauto )load_in_4bitTrue会把权重压到4bit精度nf4是量化类型device_mapauto让模型自动分布到多GPU或CPU内存。7B级模型经过这一步推理显存能降到单张消费级显卡可承担的范围。需要留意的是4bit量化后生成速度会比fp16慢同时长文本生成时的显存峰值仍然取决于max_new_tokens所以优先调小生成长度再考虑换更大显存的卡。本文还有配套的精品资源点击获取