☰
法律智能问答系统实战:从神经网络选型到RAG落地避坑
2026/10/1 4:12:59 网站建设 项目流程

简介:基于神经网络的法律智能问答系统是一份面向初学者的完整项目资源,适合用于毕业设计、课程设计或工程实训。资源围绕法律领域问答场景,提供了从数据处理、文本特征构建到模型训练与回答匹配的完整实现方案。压缩包共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_size=256, num_classes=32): 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_size=30000, hidden_size=256, num_classes=32) 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_embeddings=True) def ask(question, top_k=3, threshold=0.45): q_emb = model.encode([question], normalize_embeddings=True)[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_embeddings=True,向量模长为 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

我最后的教训是:这个系统里最值钱的不是神经网络模型,而是法条库的版本管理和评估集。我第一版只优化模型,不管数据版本,结果法条一更新,所有离线指标全部作废。现在每次法条库变更,我都强制重跑一遍三层验证。把验证做成习惯,系统才不会在无人看管时悄悄翻车,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询