法律推理辅助系统在类案检索、合同审查和裁判预测中经常出现结论不稳定,表面上是准确率波动,实质是推理过程缺少可解释的要件约束和历史判例锚定。把法律适用拆分为构成要件判断,再用判例匹配补充具体情境,是当前能够落地并且可校验的解决路径。

一、法律推理漏洞从哪里来:要件缺失与语境割裂
法律适用并不是简单的法条检索,大前提、小前提、结论的三段论在真实案件中经常失效,因为法条中的不确定概念需要结合事实进行解释。典型漏洞有三类:第一是要件遗漏,例如判断合同诈骗时只关注非法占有目的,忽略虚构事实、隐瞒真相和数额较大等构成要件;第二是循环论证,先得出有罪或违约结论,再回头挑选事实填充要件;第三是孤立解释法条,脱离类似案例的裁判语境,把需要综合判断的问题简化为单个词语匹配。
要减少这类漏洞,系统需要先理解一个法律争点由哪些构成要件组成,每个要件对应哪些事实片段,然后找到历史案件中相同或相似要件组合的裁判结果。这样形成要件分析与判例匹配的双通道结构。单纯依赖生成式大模型并不稳妥,因为模型可能跳过要件、编造法条或直接给出不可溯源的结论。可解释的规则结构与历史案例参照,恰好能够补充生成模型的短板。
二、要件分析:从法条文本到可判断的结构
要件分析的目标是把自然语言法条转换为树形或规则结构。以民间借贷为例,成立要件通常包括借贷合意、款项交付、利息约定合法。每个要件可以继续拆出子条件、证据来源和例外情形。工程上可以设计一个要件节点结构,记录要件名称、法条依据、是否必须满足、是否允许反驳以及关联的事实片段。这样后续校验时不再依赖整段法条文本,而是对每个节点单独判断。
class RequirementNode:
def __init__(self, name, law_basis, required=True, children=None):
self.name = name
self.law_basis = law_basis
self.required = required
self.children = children or []
def missing_nodes(self, facts):
missing = []
if self.required and not facts.get(self.name):
missing.append(self.name)
for child in self.children:
missing.extend(child.missing_nodes(facts))
return missing
# 构建民间借贷核心要件
loan_rule = RequirementNode(
"借贷关系成立",
"民法典第六百六十七条",
required=True,
children=[
RequirementNode("借贷合意", "民法典第六百六十七条", True),
RequirementNode("款项交付", "民法典第六百六十七条", True),
RequirementNode("利息约定合法", "民法典第六百八十条", False),
],
)
facts = {"借贷合意": True, "款项交付": False}
print(loan_rule.missing_nodes(facts))
要件抽取可以采用规则模板和模型抽取相结合。高频案由适合使用人工维护的规则模板,长尾案由可以由大模型先抽取要件,再经人工审核入库。尤其要注意法条中的但书和除外结构,例如除当事人另有约定外、法律另有规定除外,这些结构需要转换为反向条件或例外节点,否则系统会把例外情形错误判断为要件缺失。要件校验还要分层进行,先审查基础请求权要件,再审查抗辩要件,最后审查再抗辩,避免一开始就陷入对抗主张的循环。
在实际系统中,仅仅判断要件是否存在还不够,还需要记录每个要件对应的事实原文和证据位置。例如款项交付要件不能只填写真或假,而应关联转账记录、收条、证人陈述等证据片段。这样当系统输出某一要件不成立时,审查者可以立即定位到具体证据缺口,而不是面对一个笼统的结论。
三、判例匹配:从关键词检索到向量召回与规则复核
判例匹配并不是传统的案号或法条关键词匹配。历史判例的价值在于事实模式与裁判规则之间的对应关系,因此需要先把案件事实抽取为结构化要素或向量表示,再在案例库中检索相似案件。向量召回可以捕捉语义相近但措辞不同的事实描述,不过相似度检索天然容易误匹配,必须增加规则复核层,对案由、法律争点和关键要件进行二次比对。下面是一个基本的余弦相似度召回示例。
import numpy as np
def cosine(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9)
def recall_cases(query_vec, case_vecs, threshold=0.75):
results = []
for case_id, vec in case_vecs.items():
score = cosine(query_vec, vec)
if score >= threshold:
results.append({"case_id": case_id, "vector_score": score})
results.sort(key=lambda x: x["vector_score"], reverse=True)
return results
向量相似度只能说明事实描述在语义空间上接近,不能直接等同于法律争点相似。两个案件可能都涉及车辆买卖,但一个是消费欺诈适用惩罚性赔偿,另一个是普通违约仅涉及合同解除。因此还需要计算要件重合度,常用的方法是Jaccard相似度,即两个案件要件集合的交集除以并集。将向量相似度与要件重合度加权融合,可以兼顾语义接近与法律结构一致。
def jaccard(set_a, set_b):
if not set_a or not set_b:
return 0.0
return len(set_a & set_b) / len(set_a | set_b)
def combined_score(vector_score, jaccard_score, w1=0.6, w2=0.4):
return w1 * vector_score + w2 * jaccard_score
case_requirements = {
"case_1": {"借贷合意", "款项交付"},
"case_2": {"借贷合意", "利息约定合法"},
}
query_req = {"借贷合意", "款项交付"}
vec_score = 0.81
for case_id, reqs in case_requirements.items():
jac = jaccard(query_req, reqs)
final_score = combined_score(vec_score, jac)
print(case_id, jac, final_score)
规则复核的作用是拦截高相似但关键要件冲突的候选案例。复核项可以包括案由是否一致、基础请求权是否相同、是否存在相反裁判规则、案例来源法院层级是否适配。如果候选案例在关键要件上不匹配,即使向量分数很高,也应当降权或直接排除。这样比单纯依赖嵌入模型更稳健,也能让审查者看到为什么某个案例被排除或保留。
四、双通道校验:把漏洞拦截在结论之前
系统落地时不应一步到位输出最终结论,而是采用三阶段流程:先进行要件校验,再召回类案,最后做冲突检测。要件校验未通过时,直接提示证据不足或要件缺失,不进入判例匹配环节。类案召回后需要对每个候选案例生成差异分析,如果候选案例与待决案件在关键要件上存在冲突,就标记为高风险。整个过程可以用统一流程编排。
def run_legal_check(facts, law_rule, case_bank):
missing = law_rule.missing_nodes(facts)
if missing:
return {"status": "blocked", "missing": missing}
query_vec = encode_facts(facts)
candidates = recall_cases(query_vec, case_bank)
validated = []
for case in candidates:
diff = compare_requirements(facts, case)
if diff["conflict"]:
case["risk"] = "high"
validated.append(case)
validated.sort(
key=lambda x: combined_score(x["vector_score"], x["jaccard_score"]),
reverse=True,
)
return {"status": "ok", "candidates": validated}
防漏洞清单可以进一步把常见错误转成可执行的检查项。例如忽略但书、混淆构成要件与抗辩事由、用生活经验替代法律判断、引用不权威或已失效案例。每完成一次推理,系统都应当记录触发的规则、检索到的案例、排除的案例以及最终排序依据。这样即使结论出错,也能快速回溯是要件抽取错误、向量召回偏差还是规则复核遗漏。
工程上还建议按案由分层维护要件模板和判例库。不同案由的构成要件差异很大,统一模型很难同时覆盖。判例库需要持续更新裁判要旨,而不是只保存原始判决文本。大模型在抽取要件和事实时要输出引用原文,不允许只给出结论。法律推理系统的目标不是替代法律人作出裁判,而是暴露推理断点、补全要件视角,让最终判断仍然由人完成。