
简介基于神经网络的法律智能问答系统是一份面向初学者的完整项目资源适合用于毕业设计、课程设计或工程实训。资源围绕法律领域问答场景提供了从数据处理、文本特征构建到模型训练与回答匹配的完整实现方案。压缩包共30个文件大小37.48MB其中包含11个CSV数据文件涉及劳动法、劳动合同、工伤事故、辞退解雇等法律语料6个Python脚本涵盖图形交互界面、文本预处理、相似度匹配、模型训练等核心模块以及2个已训练好的model模型文件、停用词表txt和词向量缓存文件另有pyc缓存文件。项目代码结构清晰数据文件与脚本分离便于读者按模块学习也能直接运行界面脚本体验问答效果。数据集中包含问题文本与回答文本可了解语料组织方式。已有125人学习下载适合希望快速上手神经网络智能问答项目的小白或进阶学习者作为课程设计或初期项目立项的参考实现。1. 法律智能问答到底在解决什么问题先分清检索、抽取和生成把「基于神经网络的法律智能问答系统」做成一个能上线的产品和你在 demo 里跑通一个问答接口是两回事。法律场景下用户问的是「盗窃三万块判几年」「工伤认定不服怎么办」系统要做的不是从互联网上抄一段话而是给出有法条依据、可回溯、有时效性的回答。这里神经网络承担的不是「聊天」角色而是把非结构化的法条、判例、问答对转化成可检索、可匹配、可生成的结构化知识。这个标题真正指向的需求是用神经网络替代或补强传统关键词检索让法律问答系统能听懂口语化提问能把问题映射到具体条文能在答案里附上引用来源。适合正在做知识库问答、RAG 落地、法律信息化系统的工程师也适合想从检索式问答转向生成式问答的团队。你要做的不只是调一个模型而是先想清楚数据长什么样、模型放在链路哪个位置、答错时责任怎么界定——这些都想清楚了剩下的才是调参。2. 数据与任务形态法律问答的语料从哪来神经网络到底在学什么2.1 法律问答的三种数据形态法条、判例、问答对法律问答和开放域问答最大的差别在于知识源是封闭且权威的。数据主流的形态有三类做法也不同。第一类是结构化法条。这是最干净的数据每一条罪名或法律条文可以拆成「条文内容 构成要件 对应罚则」。比如盗窃罪条文内容是刑法第二百六十四条构成要件是「以非法占有为目的秘密窃取公私财物」罚则是「处三年以下有期徒刑、拘役或者管制并处或者单处罚金」。这类数据适合做匹配和抽取神经网络在这里学到的是「用户的自然语言问法」和「条文书面表达」之间的语义对应。第二类是判例文书。裁判文书里有完整的案情描述、争议焦点、法院认定和适用法条。这类数据量最大但也最脏一个案件文书动辄几千字里面只有一小段和用户问题真正相关。神经网络在这类数据上的任务通常被建模成抽取式问答给定一个问题和一个长文本模型从文本里标出答案起止位置。第三类是人工整理的法律问答对。这类数据质量最高、数量最少通常来自律师咨询记录或普法问答库。Question 是口语化表述Answer 是律师或法务写的完整答复包含法条引用和法律分析。一个完整的法律问答系统通常以这类数据做终端的生成或检索目标。从落地的角度看我的经验是最先别盲目追求大而全的数据。先把三类数据的优先级排好法条数据用于建立知识骨架问答对数据用于训练匹配和生成判例数据用于补充长尾问题。数据规模不需要多大几千条法条加几千个问答对就能先把流程跑通。2.2 神经网络在问答链路里的三个位置一个典型的法律智能问答系统有三个环节需要神经网络参与对应三种不同的网络结构。召回端。用户问题进来后先从法条库里找出候选条文。传统做法是 BM25 这类词频匹配但法律问题里大量存在「同义改写」和「说法切换」用户问「借钱不还算不算诈骗」条文里写的是「以非法占有为目的虚构事实隐瞒真相」。词面不重叠关键词匹配直接失效。这里一般用双塔结构的神经网络模型把问题和条文分别编码成向量再算相似度。双塔结构的优势是条文向量可以提前算好存起来线上只算问题向量时延可控。抽取端。定位到具体条文或判例后要从长文本中找出准确答案片段。这里用的是阅读理解模型输入是问题和段落输出是答案的起止下标。常见做法是在预训练模型如 RoBERTa 类的中文模型顶上接两个线性分类头分别预测答案起点和终点。这个环节对长文本依赖很强所以模型通常要配滑动窗口把超长文本按段切分。生成端。如果你做的是生成式问答最后要把检索到的条文和用户问题拼成提示词交给生成模型输出自然语言答案。这里的神经网络是 Transformer 解码器需要兼顾流畅性和法条引用正确性。两者常常冲突所以不能指望生成模型自己记住法条而要通过检索结果把它「喂」进去。这三段不是每个系统都要做全。最小可用方案可以先做前两段召回加抽取。生成环节用规则模板代替保证答案永远不胡说。2.3 一个可以落地的数据组织方式我习惯在项目初期就把数据组织成一个统一的 JSON 结构方便后面同时喂给召回、抽取和评估模块。{ id: criminal-264, source_type: statute, legal_basis: 中华人民共和国刑法第二百六十四条, content: 盗窃公私财物数额较大的处三年以下有期徒刑、拘役或者管制并处或者单处罚金数额巨大或者有其他严重情节的处三年以上十年以下有期徒刑并处罚金。, keywords: [盗窃, 数额较大, 三年以下, 罚金], valid_from: 2021-03-01, valid_to: null, related_questions: [ 盗窃多少钱会判刑, 偷东西被抓大概怎么判, 盗窃罪的量刑标准是什么 ], answer_template: 根据{legal_basis}盗窃公私财物数额较大的处三年以下有期徒刑、拘役或者管制并处或者单处罚金。 }每个字段都有用途。valid_from 和 valid_to 用来处理法条时效性问题这是一个后面会单独讲的坑。related_questions 是人为扩写的用户问法可以在模型训练前做一小部分数据增强。answer_template 保证即使生成模型抽风也能用模板给出一个不犯错的标准答案。数据有了之后按 8:1:1 切训练集、验证集、测试集。注意切分时要以条文 id 为单位不要让同一条文既出现在训练集又出现在测试集否则模型只是背题评估结果虚高。提示法律数据的标注成本很高初期可以用规则先粗标一轮比如用正则抽法条编号再人工校正。不要一上来就追求标注精度先保证链路是通的。3. 模型选型为什么法律问答最终倒向「预训练 混合结构」3.1 从 BP、LSTM 到 Transformer网络结构选型的演进逻辑标题里有「神经网络」四个字很多人第一反应是搭一个 BP 网络或者 LSTM。我最早做法律文本分类的时候也这么干过一个前馈神经网络加词袋特征对短的法律咨询问题做意图分类确实可行——输入是一个 300 维的 TF-IDF 向量中间接一个 256 维全连接层输出是几十个案由类别准确率能做到接近 90%。但一旦问题变长、需要结合上下文理解BP 网络就明显不够用了。原因是 BP 网络和 LSTM 对序列建模的方式有硬伤。LSTM 处理短序列问题确实优雅门控机制让信息能跨多个时间步传递但法律条文和判决书动不动上千字LSTM 的长期依赖能力撑不住而且无法并行训练迭代一场要等到地老天荒。这是我在一个罪名分类项目里踩过的坑用 LSTM 跑了两周效果反而不如把文本切成小段后用卷积神经网络做 n-gram 特征提取。卷积网络能抓住局部短语特征比如「以非法占有为目的」「数额较大」这类关键短语但对全句逻辑关系的建模能力有限。真正改变局面的是 Transformer 结构下的预训练模型。它的自注意力机制让任意两个词之间都能直接建立依赖配合位置编码长文本的上下文建模能力远超之前的方案。在预训练模型上做下游适配只需要在顶层加一个分类头或者抽取头这在工程上被称作「微调」。到了这一步模型选型基本不是问题问题是「选哪个预训练模型」。还有一个值得关注的方向是图神经网络。法律文本之间天然存在引用关系上位法引用下位法量刑指导意见引用具体条文判例引用法条。这些关系用文本向量的余弦相似度是表达不了的而图神经网络可以把条文建模成节点、引用关系建模成边学出条文的结构化向量。如果你处理的是法规体系比较复杂的场景比如行政处罚问答我会建议在召回环节加一层图神经网络重排它能识别出「这条法规已经被另一条修订取代」这类结构信息。下面对四种主流结构的选型做一个直接对比网络结构擅长任务参数量级落地成本主要缺陷BP / 前馈神经网络短文本分类、意图识别数十万到百万极低CPU 可跑无法建模序列与长距离依赖LSTM / RNN 循环神经网络中等长度序列标注、文本分类百万级低可 CPU 训练长文本记忆衰退训练慢Transformer 预训练模型语义匹配、抽取式问答、生成亿级起步需要 GPU存储开销大推理时延高部署成本高图神经网络法条引用关系建模、节点分类百万到千万中依赖图构建质量图构建难需要人工定义边3.2 为什么我会先用一个小前馈网络做基线正式上预训练模型之前我的习惯是先用一个极简的前馈神经网络把整个数据管线跑通。这个「笨」方案的价值在于验证数据切分、样本构造、评估流程是不是通的而不是追求效果。import torch import torch.nn as nn class LegalFFN(nn.Module): def __init__(self, vocab_size, hidden_size256, num_classes32): super().__init__() self.net nn.Sequential( nn.Linear(vocab_size, hidden_size), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_size, num_classes) ) def forward(self, x): return self.net(x) # 用法把每条法律问题文本转成词袋向量维度等于词表大小 # x: [batch_size, vocab_size] model LegalFFN(vocab_size30000, hidden_size256, num_classes32) logits model(torch.randn(4, 30000)) # 模拟一个 batch这里的关键参数是 vocab_size、hidden_size 和 num_classes。vocab_size 取决于你的分词词表法律场景下我建议保留所有法条编号和人名地名不要粗暴去掉低频词否则「刑法」「第二百六十四条」这类关键实体就丢了。hidden_size 设 256 够用再大对小样本数据没有收益反而容易过拟合。Dropout 设 0.3 是一个保守值如果你的训练数据只有几千条要把它提到 0.5。这个基线模型能让你确认一件事数据管线里没有 bug。当 loss 能正常下降、验证集准确率能稳定在某个水平时再换预训练模型就不会出现「数据喂错了」这种让人抓狂的问题。这一步看起来多余但省下来的调试时间远超写基线模型的那一天。3.3 预训练模型的选择与落地约束真正上预训练模型后选型主要看三个维度领域适配度、推理成本、中文分词兼容性。领域适配度上通用中文预训练模型可以做但法律词汇密集的场景下我建议优先考虑在法律语料上继续预训练的领域模型。它学过的语料里有大量法条和判决书所以对「法言法语」的表示更准确。这一点在语义匹配任务上尤其明显通用模型会把「拘役」和「拘留」当成高度相似但领域模型能区分它们是两种不同的强制措施。推理成本上要认清一个现实法律问答系统不是搜索引擎它没有每秒扛上万请求的压力但也要控制在几百毫秒内。我见过一个团队为了效果上了 7B 的生成模型结果单条问答耗时超过 10 秒用户直接流失。一个务实的思路是「大模型离线、小模型在线」离线用大模型批量生成候选答案或扩写问法线上用小模型做召回和匹配生成环节用模板兜底。中文分词兼容性上要确认该模型用的分词器和词表覆盖了常见法律术语。有些模型用的是字符级切分对「抢劫罪」这类三字罪名没有问题但对「非法吸收公众存款罪」这种长罪名字符级切分偶尔会丢失边界信息这时需要自己在词表里手动加入高频罪名和法条编号。注意不要迷信模型越大越好。法律问答的准确性主要取决于召回是否命中正确法条而不是生成模型有多大。法条都没找对再大的模型也只能一本正经地胡说。4. 最小可运行实现从法条库到答案输出的完整代码闭环4.1 整体架构召回 重排 模板生成一个能快速上线的最小闭环我的选择是「向量召回 阈值过滤 模板答案」。不用生成模型不搞复杂微调第一步先把「用户问一句话系统回一句有法条依据的答案」跑通。这个架构里有两个环节是神经网络一个是句向量编码模型把用户问题和法条都转成向量另一个是相似度计算本质上是两个向量做点积或余弦相似度。召回之后的重排可以用一个轻量的交互式匹配模型再算一遍精排分也可以直接截取相似度最高的前三条。我推荐向量召回而不是 BM25 的原因在前面说过法律问答里同义表达太多向量匹配能跨过字面差异直击语义。代价是召回存在丢召回的风险所以我会把阈值调低一点宁可多给候选也不要漏掉正确法条。4.2 一个能跑的最小代码实现下面这段代码可以直接在本地跑通不需要 GPU。它做的事情是把法条库编码成向量接收用户问题计算相似度输出命中条文和模板答案。import json import numpy as np from sentence_transformers import SentenceTransformer # 加载多语言句向量模型中文效果可用内存占用小 model SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) # 法条库实际项目中从 JSON 读取 statutes [ { id: 刑法264, text: 盗窃公私财物数额较大的处三年以下有期徒刑拘役或者管制并处或者单处罚金。, answer: 根据刑法第二百六十四条盗窃公私财物数额较大的处三年以下有期徒刑、拘役或者管制并处或者单处罚金。 }, { id: 刑法266, text: 诈骗公私财物数额较大的处三年以下有期徒刑拘役或者管制并处或者单处罚金。, answer: 根据刑法第二百六十六条诈骗公私财物数额较大的处三年以下有期徒刑、拘役或者管制并处或者单处罚金。 } ] # 离线阶段编码法条库 corpus_texts [s[text] for s in statutes] corpus_emb model.encode(corpus_texts, normalize_embeddingsTrue) def ask(question, top_k3, threshold0.45): q_emb model.encode([question], normalize_embeddingsTrue)[0] scores np.dot(corpus_emb, q_emb) idxs np.argsort(scores)[::-1][:top_k] hit False for i in idxs: if scores[i] threshold: continue hit True s statutes[i] print(f命中法条: {s[id]}, 相似度: {scores[i]:.3f}) print(f答案: {s[answer]}) print(---) if not hit: print(未命中任何法条请补充人工复核。) # 用户上来问 ask(偷了几千块钱会怎么判)代码逻辑分三段。第一段是加载模型并定义法条库实际项目中法条库会存在数据库或文件里启动时一次性编码并缓存到内存。第二段是编码和相似度计算关键函数是np.dot(corpus_emb, q_emb)因为编码时设置了normalize_embeddingsTrue向量模长为 1点积值就是余弦相似度范围在 -1 到 1 之间。第三段是结果过滤和输出threshold低于阈值的候选直接丢弃。4.3 参数怎么设模型、阈值、top_k 与文本长度这个最小闭环只有三个关键参数要调。第一个是句向量模型的选择。paraphrase-multilingual-MiniLM-L12-v2适合起步因为模型小、加载快、中文通用场景不差。如果后续发现法言法语匹配不准我的建议是换用中文领域微调过的向量模型然后重新编码法条库代码不改只换模型名。要注意的是换模型后所有向量都需要重新生成所以法条库的编码任务要写成独立的离线脚本。第二个是相似度阈值。这个值直接决定「宁缺毋滥」还是「宁滥毋缺」。法律问答场景我会偏向后者因为答错了比答不出来后果更严重。建议阈值初始设为 0.45然后在测试集上画相似度分布图把阈值定在「正确命中的最低分」和「错误命中的最高分」之间的中点。这一步是纯玄学每个数据集的值都不一样必须实测。第三个是 top_k。最小闭环里设 3 就够了如果后续接生成模型top_k 可以调到 5 甚至 8给生成模型更多上下文材料。但 top_k 调大后要同步调高重排门槛否则一些无关法条会被塞进生成模型干扰答案。文本长度也要注意。句向量模型一般有输入长度上限法律条文动辄几百字超长会被截断导致语义丢失。常见做法是按句子或按段落切分每条文本控制在 256 字以内切分后每段保留条文编号前缀「刑法264-1」这样召回后能定位到具体段落而不是整条文。5. 避坑与排查法律问答系统最常见的 5 个翻车点5.1 法条时效性问题回答了已经废止的条文现象用户问一个合同纠纷问题系统引用了旧合同法条文给出的结论和现行民法典冲突而且引用里没有标注失效日期。这在人工审核时一眼就能看出来但线上系统不会自己发现。原因法条库文本只存了条文内容没有记录版本和生效状态。向量召回只管语义相似度不管法律效力。旧条文和新条文在描述同一件事时语义相似度极高模型天然会同时召回排序时旧条文可能排前面。解决给法条库加上valid_from和valid_to字段召回阶段做硬过滤只保留当前有效的条文。再加一个定时任务每月更新一次法条库更新时不仅增删文本还要刷新向量索引。具体做法是在ask函数里加一个过滤条件if statute[valid_to] is not None and statute[valid_to] today: continue。这一步用规则解决比用网络解决靠谱得多。5.2 长文本截断条文太长模型只看了一半现象用户问了没有标准答案的综合性问题系统召回了一条完整的行政处分条例但输出的答案内容残缺只涵盖了条例的前半部分恰好漏掉了最关键的处罚规定。原因预训练模型的输入长度有上限通常是 512 个 token法条全文超过长度限制后被直接截断尾部信息丢失。句向量模型和抽取式模型都有这个问题。解决入库阶段做段落级切分每段限制在 200 到 300 字保留条文编号作为前缀。召回时先定位到条文再定位到具体段落。另外在切分时要按语义边界切不要硬按字符数切否则会把「情节较轻的处三年以下有期徒刑」这类完整句切成两段。这里我的经验是优先按句号切句号过长再按逗号切。5.3 生成模型幻觉引用了不存在的条文现象生成式回答里出现了类似「根据刑法第二百六十四条规定」的表述但实际刑法条文根本不是这个内容甚至数字是编的。这在对话里很难被用户甄别却是最危险的一类错误。原因生成模型没有「记忆」能力它只是根据上下文概率分布逐字生成。训练语料里「根据刑法第X条」这个句式出现频率很高模型学会了形式没学会内容。越大的模型编造得越流畅越难被发现。解决强制生成模型只做「提炼」不做「创作」。把检索到的法条原文拼进提示词并在提示词里明确写「只能引用上述材料中的条文禁止编造其他法条」。最保险的做法是干脆不用生成模型用我前面写的模板答案。如果一定要用生成模型后端必须加一个「引用校验」模块用正则从答案里抽出「第X条」再去法条库反向查这个条文的实际内容和答案内容是否一致不一致就拦截。5.4 评估上偏差测试集和训练集数据泄漏现象离线测试准确率做到 92%上线后真实问答体验明显差一截用户反复投诉答非所问。原因很多人做数据切分时直接用 random split同一个条文及其改写问法同时落进训练集和测试集。模型在训练时见过几乎一样的问法测试时等于开卷考试分数虚高。真实用户的问题表达和训练语料差异很大准确率立刻回落。解决按条文 id 切分数据而不是按样本行切分。同一个 document 的所有相关问题必须进同一个集合。更严格的做法是按时间切分拿最近三个月的问答做测试集之前的做训练集模拟「模型对未来问题的泛化能力」。我在一个法律援助项目里就是被这个坑教训过重切数据后准确率从 92% 掉到 78%才是真实水平。5.5 数据稀疏长尾罪名样本太少现象高频罪名如盗窃、诈骗样本充足模型效果不错。但一些低频罪名如「拒不支付劳动报酬罪」「破坏电力设备罪」训练样本只有几十条模型几乎学不到东西用户一问就翻车。原因法律问答的数据天然呈现长尾分布。头部几十个罪名覆盖了大多数真实咨询但长尾的几千个罪名虽然每个样本少加起来总量不少覆盖不了就意味着系统边界明显。解决长尾数据要靠判例文书补齐。一个有效的做法是先人工梳理出高频问法模板再用弱监督规则从裁判文书里抽取「案情描述 引用法条」对抽出来的对子做向量召回验证和已有人工标注数据的相似度高于阈值才入库。另一个思路是少样本学习每个长尾类别保留 20 条以上的种子样本用预训练模型做小样本微调虽然达不到头部类别的效果但至少能让召回率从 20% 升到 60%。6. 进阶用图神经网络编码法条引用关系并搭一套可落地的验证方法法条之间不是孤立的一条法条可能被另一条修正也可能在判例中被反复引用。当你处理的法条库超过一千条时向量召回很容易把相互矛盾的旧版条文和新版条文同时推荐给用户。我的进阶做法是把这些引用关系构造成一张图用图神经网络学习条文的结构向量。具体实现是把每条法条作为图节点节点特征用句向量模型的输出初始化边来自三类关系——「修正关系」「援引关系」和「上位法与下位法关系」。图神经网络经过两层消息传递后输出每条文的结构化向量。推理时把用户问题的向量和条文结构向量拼接再过一层全连接判断该条文是否适合回答当前问题。这一步能把「旧版条文虽然语义相近但效力已被替代」这类结构信息加入判断显著减少误召回。图构建的工程量不小但效果是对体系性问题的回答质量提升明显值得在第二阶段做。验证方法上我建议搭一套三层验证体系别只盯着一个总体准确率。第一层做召回验证用百条规模的标注问题集统计 top-5 召回率低就直接去看向量模型和切分逻辑。第二层做答案验证按「引用法条是否正确」「答案要素是否齐全」「表述是否可直接使用」三个维度分别打分每类问题抽 50 条做人工评估。第三层做线上抽检系统上线后每天随机抽 30 条问答重点检查当日法条更新后有没有出现引用已失效条文的情况。验证层级核心指标工具/方式通过标准召回验证top-5 召回率标注问题集 脚本统计不低于 85%答案验证引用正确率 / 要素齐全率人工抽评 50 条引用正确率不低于 95%线上抽检失效条文引用次数日志 每日抽查连续一周为 0我最后的教训是这个系统里最值钱的不是神经网络模型而是法条库的版本管理和评估集。我第一版只优化模型不管数据版本结果法条一更新所有离线指标全部作废。现在每次法条库变更我都强制重跑一遍三层验证。把验证做成习惯系统才不会在无人看管时悄悄翻车希望帮到你。本文还有配套的精品资源点击获取