
简介该项目是一套基于神经网络的法律智能问答系统面向希望学习自然语言处理与智能问答的初学者和进阶者适合作为毕业设计、课程设计或项目实训。系统围绕法律领域常见场景构建覆盖劳动合同、工伤保险、劳动法、员工权益、维权方式等知识维度可帮助用户快速检索相关法律法规与维权途径。资源共含30个文件核心包括11个CSV数据文件、6个Python脚本、5个文本文件、5个编译后的pyc文件以及2个模型文件压缩包整体约37.48MB。其中CSV与文本数据提供了法律条款、问答语料、问题/回答数据及停用词表Python脚本涵盖GUI界面、文本预处理、相似度匹配、模型训练与分类等环节并附带训练好的分类模型可加载后直接进行问答测试与二次开发。目前已有126人学习下载适合需要实战参考与快速搭建法律问答原型的学习者借助这套资源可以掌握从语料整理、模型训练到界面交互的完整链路也能为后续研究法律NLP提供良好的起步基础。1. 法律智能问答系统为什么先做检索、再做生成法律智能问答系统表面上是神经网络 法律文本 问答但真要在律所或企业法务环境里落地第一关不是模型选型而是回答的可追溯性。一个直接面向当事人的法律问答如果模型自信地引用一条已经废止的法规或者把应当解读成可以那已经不是技术事故而是执业风险。所以我给这类系统的第一原则是能检索到原文的绝不靠模型背出来模型只负责理解问题、匹配条文、组织语言。只要做的是法律领域就必须面对三个特殊约束法条文本高度结构化但语义密度大、一个咨询问题往往横跨多部法规、同一个事实在不同情形下的法律后果完全不同。这也决定了给个BERT跑一下远远不够你需要一整套从数据切分、样本构造到召回排序的工程方案。这篇文章把整套落地路径拆开讲架构怎么选、数据怎么切、模型怎么训、阈值怎么定、上线前怎么验每一步都给出能直接抄的参数和代码。适合正在做法律咨询系统、智能客服知识库或者想把法律文本做成问答服务的从业者。2. 选型检索式、生成式还是混合式法律场景凭什么先做检索2.1 三种架构的能力边界从问题反推架构法律问答不是只有一个入口。当事人问合同违约了怎么办和执业律师问最高人民法院关于买卖合同司法解释第几条适用于商品房买卖是两个完全不同的问题。前者要的是路径指引后者要的是精确条文。所以在定架构之前先把问题图谱画出来这决定了系统走哪条技术路线。常见做法有三种。第一种是检索式用问题去匹配法条库、判例库把原文吐给用户系统只做相关度排序。优点是绝对可追溯每个回答都能指到依据《XX法》第X条缺点是不能应对怎么办这类开放式咨询。第二种是生成式用seq2seq或大语言模型直接组织答案流畅度高但法律场景最怕的就是幻觉编造法条、曲解语义在律师看来是不可接受的。第三种是混合式先检索召回候选条文再让生成模块基于召回结果组织回答生成模块被关在铁笼子里不许超纲发挥。我一般会建议从检索式起步。原因很现实法律文本的权威性决定了系统首要指标不是流畅度而是引用准确率。检索式的错误是可解释的——排错了就调整排序策略生成式的错误是不可解释的——你无法知道模型哪一层把定金和订金的规则记错了。混合式可以作为第二阶段演进目标但第一版必须能回答依据在哪。2.2 语义匹配的选型为什么 CNN / RNN 在这里力不从心很多做过文本分类的同学会惯性地说先上CNN跑个baseline再说。但法律条文匹配不是情感分类它有两个特点让传统结构很吃亏。第一法条是长文本。一部法规动辄几十条单条也可能几百字CNN固定卷积窗口对长距离依赖无能为力双向LSTM虽然能捕捉序列关系但计算成本高而且对问题-条文这种非连续片段匹配没有结构优势。第二法律语义的相似不等于字面相似。未按期履行债务和迟延给付说的是同一件事但字面重合度极低这需要模型有能力做深层语义对齐。实际落地时我推荐双塔结构的深度语义匹配模型。问题塔和条文塔分别编码最后算相似度。最关键的设计是两塔的编码器共用权重而不是各自独立。原因在于法律问答里问题和条文的空间高度重叠共享权重可以显著减少参数、降低过拟合风险。如果两塔独立训练很容易出现问题塔学了一堆口语表达、条文塔学了一堆规范表达、两者根本不落在同一个向量空间的尴尬——你是让两个不同语言的人在对话。3. 数据工程法律文本的清洗、切片与问答对构造3.1 法条库的层级结构怎么切出处信息不许丢法律文本和普通文档最大的区别是它有严格的效力层级和引用规范。做检索系统你不能把一整部《民法典》当成一个文档存进去那样召回出来的是一坨无法定位的大块文本用户拿不到第几条。法律文本的天然结构是法律 → 编/章 → 条 → 款 → 项。我做项目时切分粒度定在条这一级每一条作为独立的检索单元。原因款和项常常依附于条存在切成条可以保证语义完整而切成章则粒度太粗用户问违约金上限你召回一整章违约责任用户没法快速定位。每条写入向量库时元数据至少带四样法规名称、章节路径、条文序号、时效状态有效/已废止/已被修改。这四样在后面的引用生成和展示环节里一个都不能少。切分时有个坑法律文本里经常出现第XX条之一第XX条之二这种增补条文正则切分时如果不做特殊处理很容易把第二十四条之一误切成第二十四条和之一两个噪声片段。常见的做法是预置一个条文编号的正则解析器主编号和附编号用不同捕获组。3.2 问答对的正负样本配比决定模型上限法律问答系统没有现成的百万级标注数据集训练数据是靠标注 构造两条腿走。正样本来源裁判文书网上有大量的本院认为段落里面浓缩了案件事实和对应法条法律咨询平台的公开问答也是素材但清洗成本较高需要人工过滤掉答非所问的回复。负样本是比正样本更关键的工程点。如果用随机采样做负样本模型会学出一个偷懒策略——只看字面重合度扛不住同义改写。我常用的构造法有三种。第一是BM25硬负样本给定问题用BM25从法条库里召回分数靠前但语义不对应即人工判定无关的条文这些是看起来像、其实不是的魔鬼负样本。第二是跨领域挖矿A领域的法条作为B领域问题的负样本来源同一批语料的不同分片。第三是难负样本改造把正样本条文中的核心词做近义替换比如违约金换成赔偿金模型必须学会识别这种替换后的语义漂移。配比上我一般把正负样本控制在1: 4。负样本太少模型学不紧负样本超过1: 8模型会被带偏成万事不相似召回率掉得厉害。4. 模型实现用双塔结构把法律问答压成向量匹配4.1 双塔输入侧的处理问题与法条的差异化编码模型的输入两侧是不对等的。问题通常是口语化的短文本租赁合同到期了房东不退押金怎么办而法条是规范化的正式文本租赁期限届满承租人继续使用租赁物出租人没有提出异议的原租赁合同继续有效。这两侧如果做同样的预处理效果会打折扣。问题侧要保留完整口语表述不做术语归一化因为模型需要学会把口语对着术语法条侧则要稍微清洗比如去掉下列情形之一这种纯列举引子否则它会把注意力分到无效的引导词组上。切词策略也要分侧。中文法律文本里不可抗力情势变更这类四字及以上术语普通分词器容易切开切开后向量语义就散了。我习惯在法条侧用领域词表做最大匹配法先保术语完整问题侧则用常规中文分词。你可以直接用通用预训练模型但要在预训练的基础上用法律语料做领域适配微调否则定金和订金这两个概念在通用模型里的向量距离会被压缩得很小。4.2 训练代码共享权重的双塔一个能跑的最小样例下面是一个用 PyTorch 实现的最小双塔训练脚本编码器以常见预训练模型为 backbone输出端接余弦相似度做对比学习。这不是一个能直接上生产的完整工程但结构完整能让你在 12 小时内跑通一条基线。import torch import torch.nn as nn import torch.nn.functional as F from transformers import AutoTokenizer, AutoModel class LegalTower(nn.Module): 单塔编码器输入句子输出一个归一化后的文本向量 def __init__(self, model_name, hidden_size768): super().__init__() self.encoder AutoModel.from_pretrained(model_name) self.pool_head nn.Linear(hidden_size, hidden_size) self.norm nn.LayerNorm(hidden_size) # 注意冻结底层参数只微调高层能显著降低显存占用 for param in self.encoder.embeddings.parameters(): param.requires_grad False def forward(self, input_ids, attention_mask): # 取 [CLS] 位置的输出作为句子向量 last_hidden self.encoder( input_idsinput_ids, attention_maskattention_mask ).last_hidden_state[:, 0, :] pooled self.pool_head(last_hidden) pooled self.norm(pooled) # 归一化到单位向量方便后续只算余弦相似度 return F.normalize(pooled, p2, dim1) class LegalPairModel(nn.Module): 共享权重的双塔结构question 塔和 article 塔是同一个网络 def __init__(self, model_name): super().__init__() # 关键只实例化一个塔两侧共用而不是新塔 self.tower LegalTower(model_name) def forward(self, q_input_ids, q_mask, a_input_ids, a_mask): q_vec self.tower(q_input_ids, q_mask) a_vec self.tower(a_input_ids, a_mask) return q_vec, a_vec def calc_contrastive_loss(q_vec, a_vec, temperature0.05): 对比学习损失拉近正样本对推远负样本对 # 余弦相似度矩阵行是问题列是法条 sim_matrix torch.matmul(q_vec, a_vec.T) / temperature # 对角线是正样本对其他都是负样本batch 内负样本 batch_size q_vec.size(0) labels torch.arange(batch_size, deviceq_vec.device) return F.cross_entropy(sim_matrix, labels)参数说明三点。temperature0.05是对比学习的常用温度值数值越小相似度分布越尖锐模型越倾向于拉大正负样本的差距法律语义差异常常很微妙定金和订金只差一个字温度要调低一些但不要低于 0.02否则训练不稳定。冻结 embedding 层是省显存、防过拟合的关键操作法律词汇丰富embedding 层可训练参数太多小数据集下容易过拟合到高频词上。batch size 建议 3264太大负样本太简单太小正样本对太少如果显存不够优先减小 max_len 而不是 batch size。4.3 推理时的 top-k 召回与阈值策略模型训完之后推理阶段要做两件事。第一把全量法条库过一遍塔生成向量存进向量数据库里这一步是离线的每天更新一次即可因为法条库的变动频率远低于咨询流量。第二线上每来一个问题只跑问题塔算它和全库向量的余弦相似度取 top-k 召回。top-k 我一般取 816。取太小正确法条排在 10 名以后就彻底漏了取太大后面排序模块的负担重。召回之后还要做一次精排精排不是模型而是元数据校验法条时效性是否为有效、效力层级是否匹配问题主体普通民事纠纷不应先推行政法规、条文序号是否对应正确的法规名称。这一层校验的优先级高于模型分数——哪怕模型的相似度给了 0.92如果这条法条已经废止也必须在展示前拦截下来。5. 避坑法律问答系统上线前后最容易翻车的五个问题5.1 已废止法规被模型热情地答出来现象用户咨询劳动纠纷系统引用了一条《劳动法》条文但该条文早已被《劳动合同法》相关内容替代。原因法条向量库里同时存在废止和现行版本模型只学了语义相似不知道时效性。解决建一个法条时效性词典在检索后校验阶段按法规名称条文序号查表命中已废止状态直接丢弃同时在离线入库时对已废止条文设置is_activeFalse向量检索后统一过滤不要等上线后再查。5.2 多轮对话里问那违约金呢上下文丢了现象用户先问房东不退押金怎么办系统给出了合同法依据用户紧接着问那违约金呢——系统把这个问题当成独立问题召回了一堆违约金法条忽略了押金退还纠纷这个前提。原因纯双塔模型没有对话状态管理每个问题都是孤立的。解决在问题塔输入侧拼接对话历史摘要字段。具体做法是维护一个最近 2 轮的结构化摘要——实体房东/租客/押金、案由房屋租赁纠纷、当前焦点退还押金。拼接时用 [SEP] 分隔让模型知道当前问题 上文焦点。这条路比硬上带记忆的生成模型成本低得多效果也稳定。5.3 判例库的参考价值被高估现象检索模块把裁判文书网里的历史判决也纳入召回用户问借条没写还款日期怎么办系统推了一条 2015 年基层法院的判决作为依据。原因判例不等于法条它受个案事实、地域、审级影响极大且不是每个判决都能作为参考依据。解决判例单独建库、单独标签按案由、法院级别、审理年份分桶。检索结果里法条优先展示判例作为参考案例折叠展示不直接作为依据。这个产品逻辑必须写死在排序策略里不能靠模型自行领悟。5.4 相似度得分高不等于答案正确现象模型把某条相似度给了 0.86 的法条排在第一位但人工看觉得不如排在 0.71 的那条准确。原因法律文本常有否定式表述比如不属于不可抗力不承担赔偿责任模型把不可抗力识别成了关键实体忽略了前置的不属于。解决训练阶段针对性地做否定词扰动把正样本中的不属于改写成属于构造生成的负样本同时在精排阶段用一个轻量规则检查器扫描高分段文本里是否出现不/未/非等否定词与问题中的关键词共同出现如果出现在排序上加惩罚项。这个规则检查器不解决所有问题但能拦截很大一部分语气型误判。5.5 答非所问且自信的兜底策略现象用户问了一个完全不在知识库覆盖范围内的问题比如外籍人士在中国设立合伙企业的税收优惠模型随机召回了几个高相似法条硬凑了一段回答。原因检索引擎没有我不知道这个选项。解决设置可用性阈值。当最高相似度低于某个值我一般定 0.500.55系统不返回匹配结果而是输出引导话术——当前问题可能涉及多个法律领域的交叉建议转人工咨询。这条阈值不能拍脑袋定而是在评测集上画相似度分布曲线取错误回答率突然上升的拐点位置。6. 验证与迭代用一份20题的评测集把系统打回原形上线前最重要的动作不是压测并发而是建立一份人工评测集。我每次接手这类项目第一周都在做这件事找 2030 个真实咨询问题脱敏后每个问题标注正确答案对应的法条序号、错误答案类型法条错/序号错/依据过时/答非所问、可接受的最低回答模式。评测时记录整体命中率、top-1 命中率和 top-5 命中率。法律问答我只看一个核心指标回答中引用的每一条法条都能对应到正确的法规名称、条文序号和有效状态。这三个要素任何一个错了整条回答就判错没有酌情分。迭代时最有效的诊断工具是相似度分布直方图。把错误答案的相似度分数捞出来画直方图如果错误回答集中在 0.800.90 的高分区说明模型对语义的区分度还不够需要更多难负样本如果错误回答集中在 0.500.70 的低分区说明知识库覆盖有缺口需要补数据而不是调模型。这个诊断习惯帮我避过很多次盲目调参的无效工作。最后说一句我的教训最早做这类系统时我迷信生成模型的流畅度结果在真实咨询里翻了不少车——模型能把未满十八周岁和无民事行为能力在逻辑上混为一谈当事人照着做法律后果非常严重。后来我把架构改成检索为主、生成为辅引用准确率从七成拉到了九成五以上才敢说自己做的系统能见人。先让系统学会不乱说再让系统学会说得好这条路法律行业尤其走不得捷径。希望帮到你。本文还有配套的精品资源点击获取