法律幻觉校验技能 v1.2
版本:v1.2 | 日期:2026-06-09 定位:法律AI输出的外部事实核查层 —— 运行时调用 chineselaw.com API 进行法条/案例真实性校验 核心原则:校验不替代判断、分级不放行、修正不掩盖
v1.2 更新(2026-06-09):
- 新增 Step 1.5 本地预校验:API调用前增加两道本地防线——① 案例案号格式合法性校验(最高法序号范围/指导案例编号上限);② 废止法律自动标记(合同法/担保法/侵权责任法等9部民法典前法律→自动升1级)
- 风险升级规则新增"废止法律引用"触发条件(引用已废止法律→至少P1)
- 能力边界表案例检测项从"高置信度"下调为"中(需本地兜底)"
- 评估基线:86/100(TC5暴露案例虚构未检出+已废止法律未标记两个弱项,v1.2针对性修复)
v1.1 更新(2026-06-04):
- 概括性引用降级规则经实测案件实测验证
- 6项API能力边界测试完成并回写
- Windows curl中文编码400错误固化解决
⚠️ 认识论底线(全程生效,不可覆盖)
1. API返回结果仅供参考——不确定的结果必须标注"【API结果存疑,建议人工核实】"
2. 本技能不替代法律判断——只做引用真实性校验,不做法律关系分析
3. 校验失败时回退到 法律认识论底线 规则——承认不确定+给出核验路径
4. 不得基于API的"可能相关"建议自行补充法条或案例
5. 所有校验结果必须可追溯——输出报告含校验时间戳+API响应摘要
三原则
- 校验不替代判断:API说"不一致"不代表引用一定错误(可能是冷僻条文),需要人工终审
- 分级不放行:P0(致命幻觉)→ 阻断输出;P1(高疑似)→ 标记+提示;P2(低风险)→ 记录
- 修正不掩盖:修正后保留原始引用+修正说明,不允许静默替换
风险分级标准
| 级别 | 判定条件 | 处理动作 | 示例 |
|---|---|---|---|
| P0 致命 | ① 法条编号完全不存在(think_tank_clause_missing: true) ② 案例案号不存在(missing-ref标记) ③ 案号格式异常·本机预校验拦截(序号超范围/指导案例编号超上限) ④ 多条P1集中出现(≥3条) ⑤ 已废止法律引用且内容歪曲(P1+废止标记→升P0) |
阻断输出 → 回退修正 → 重新校验 | 民法典1899条、(2023)最高法民终第9999号、指导案例第888号 |
| P1 严重 | ① 法条编号存在但内容歪曲(semantic_compare相似度<50) ② 法条编号对应错误(semantic_compare "不一致") ③ 关键法条(请求权基础)的任何异常 ④ 引用已废止法律(无论API相似度多高,标记P1·已废止法律) | 标记→ 阻断依赖该引用的文书节点 → 提示修正路径 | 民法典667条歪曲为"签字即成立"、引用《合同法》第52条 |
| P2 提示 | ① 法条内容相似度50-85(不完全准确但不影响结论) ② 案例引用的法院/年份正确但案号细节存疑 ③ 非核心法条的轻微偏差 ④ 概括性引用(标准法律写作实践) | 记录→ 不阻断 → 附校验报告供人工查看 | 引用表述与原文有轻微措辞差异 |
概括性引用降级规则(⚠️ 2026-05-14 实测案件实测新增·v1.1验证确认)
问题:中国法律文书中常见的"概括性引用"写法——如"根据《民法典》第667条之相关规定,被告应承担违约责任"——API因文本与法条原文不匹配而给出低相似度(P1),但这是标准法律写作实践,并非幻觉。
规则:在 Step 3 风险定级阶段,增加一道"概括性引用检测"过滤层:
if P1判定 and 原始文本匹配以下模式之一:
① "根据X法第X条之相关规定" + 法律结论(非法条原文引用)
② "依据X法第X条" + 裁判说理/分析性表述
③ "参照X法第X条" + 类案推理
④ 引用后接"等"字(如"第X条等")+ 法律后果描述
→ 降级为 P2 提示(标注"概括性引用,非内容歪曲")
判断标志:
- 直接引语标记 → 保持原定级("XX法第X条规定:……"引号内为法条原文 → 若相似度低则是真正歪曲)
- 概括性标记 → 降级("根据/依据/参照XX法第X条" + 法律结论 → 标准文书写作)
实测数据支撑(实测案件 Batch1,8条真实引用·v1.1验证确认):
- 3条直引法条原文 → ✅ 全部通过(相似度85+)
- 5条概括性引用 → API标为P1,经降级规则正确降为P2
- 例:"根据民法典第667条、第675条、第676条之相关规定,被告应承担还本付息的违约责任"(相似度20 → 降为P2·概括性引用)
- 例:"民事诉讼法第35条规定当事人可以书面协议选择管辖法院"(相似度40 → 降为P2·概括性引用)
降级标记格式:P2·概括性引用(API原始相似度:N),报告中注明"此为文书常见写作方式,非内容歪曲"。
执行流程(5步·v1.2)
Step 1: 引用提取
从输入文本中自动提取所有法条引用和案例引用。
法条引用模式(正则提取):
《[法]》第[X]条→ 标准格式[法]第[X]条第[X]款→ 含款/项[法规名]第[X]条→ 行政法规/司法解释
案例引用模式:
([年份])[法院]第[X]号→ 标准案号格式指导案例第[X]号→ 指导性案例[年份][省份简称][数字]民/刑/行[终/初/再][数字]号
Step 1.5: 本地预校验(v1.2新增·API调用前防线)
🔴 CHECKPOINT — 本步骤在API调用前执行,不依赖外部服务,零延迟。 用于弥补API在案例检测和时效性判断上的覆盖盲区。
1.5A:案例案号格式合法性校验
在调用API之前,对所有提取到的案例引用进行本地格式校验:
【案例案号格式校验规则】
最高法案号:
格式:(YYYY)最高法民终第XXXX号
校验:序号是否 > 9999 → 标记为"⚠️ 格式可疑·序号超出合理范围"
校验:年份是否 > 当前年份 → 标记为"⚠️ 格式可疑·年份为未来"
指导性案例:
格式:指导案例第XX号
校验:编号是否 > 300 → 标记为"⚠️ 编号疑似虚构·截至2025年最高法发布约230+号"
(阈值留10%余量,实际以最高法最新发布为准)
普通案号:
格式:(YYYY)XX法/民/刑/行XX字第XX号
校验:基础格式匹配 → 通过
校验:年份格式异常 → 标记"⚠️ 案号格式异常"
处理规则:
- 通过格式校验 → 正常进入Step 2 API调用
- 未通过格式校验 → 直接标记P0(无需等待API确认),阻断输出:
- 标记为
P0·案号格式异常(本地预校验拦截) - 原因说明:案号超出已知合理范围,疑似虚构
- 示例:
(2023)最高法民终第9999号→ P0(序号远超合理范围);指导案例第888号→ P0(编号超已知上限)
- 标记为
1.5B:废止法律自动标记
在调用API之前,对所有提取到的法条引用进行废止法律检测:
【已废止法律列表】(民法典2021年1月1日施行后废止)
| 旧法 | 替代法 | 废止日期 |
|------|--------|---------|
| 《中华人民共和国合同法》 | 《中华人民共和国民法典》合同编 | 2021-01-01 |
| 《中华人民共和国担保法》 | 《中华人民共和国民法典》+《担保制度司法解释》 | 2021-01-01 |
| 《中华人民共和国侵权责任法》 | 《中华人民共和国民法典》侵权责任编 | 2021-01-01 |
| 《中华人民共和国物权法》 | 《中华人民共和国民法典》物权编 | 2021-01-01 |
| 《中华人民共和国民法通则》 | 《中华人民共和国民法典》 | 2021-01-01 |
| 《中华人民共和国婚姻法》 | 《中华人民共和国民法典》婚姻家庭编 | 2021-01-01 |
| 《中华人民共和国继承法》 | 《中华人民共和国民法典》继承编 | 2021-01-01 |
| 《中华人民共和国收养法》 | 《中华人民共和国民法典》 | 2021-01-01 |
| 《中华人民共和国民法总则》 | 《中华人民共和国民法典》 | 2021-01-01 |
处理规则:
- 如果引用中出现了以上任何一部已废止法律 → 自动升1级:
- 原本应P2 → 升为P1·已废止法律
- 原本应P1 → 升为P0·已废止法律
- 原本应通过 → 标记为
P1·已废止法律(API相似度:N),无论API返回的相似度多高
- 附加说明:
"《XX法》已于2021年1月1日被《中华人民共和国民法典》取代。引用已废止法律,即使内容与原文一致。建议替换为《民法典》对应条款。"
⚠️ 注意:旧法在新法施行前订立的合同仍可能适用(视时间效力规则而定)。如确认引用旧法是基于"法不溯及既往"原则的合理选择,标注"【旧法适用·合同订立于民法典施行前】"后可不升级。
Step 2: API调用
🔴 CHECKPOINT — API调用前脱敏检查:发送前必须确认:①已替换当事人姓名→"甲方/乙方" ②身份证号→"[身份证号]" ③具体金额→"[金额]" ④未包含具体案件事实。检查通过后方可发送API请求。⚠️ Windows环境必须使用Python urllib方式发送中文内容(避免curl 400编码错误)。
调用 https://open.chineselaw.com/open/hall_detect 进行批量校验。
调用规范:
- 长文本(>2000字)分段发送,每段≤1500字
- 段间间隔≥1秒(避免触发速率限制)
- 超时设置:30秒/段
- 失败重试:最多1次
Step 3: 风险定级
🔴 CHECKPOINT — 分级前概括性引用过滤:在风险定级前,必须先执行"概括性引用降级规则"过滤层。凡匹配"根据/依据/参照XX法第X条"+"法律结论"模式的P1候选,先降为P2·概括性引用后再执行后续分级。跳过此过滤层将导致大量假阳性P1。
将API响应映射到风险级别:
for each regulation in response.regulations:
if regulation.think_tank_clause_missing == true:
→ P0 致命(法条编号不存在)
elif regulation.semantic_compare.similarity_score < 50:
→ P1 严重(法条内容歪曲/错配)
elif regulation.semantic_compare.similarity_score < 85:
→ P2 提示(轻微偏差)
else:
→ ✅ 通过
for each case in response.cases:
if case.status == "not_found":
→ P0 致命(案例不存在)
else:
→ ✅ 通过
风险升级规则:
- 同一文本中≥3条 P1 → 升为 P0
- 同一文本中≥5条 P2 → 升为 P1
- 请求权基础法条(《民法典》合同编/侵权编核心条文)任何异常 → 至少 P1
- 引用已废止法律 → 自动升1级(P2→P1,P1→P0),无论API返回的相似度多高
- Step 1.5 本机预校验拦截 → 直接P0,不进入API校验流程
Step 4: 校验报告输出
输出结构化校验报告(同上版本格式,略)。
失败模式与异常处理
| 触发条件 | 一线修复 | 仍失败兜底 |
|---|---|---|
| API不可用(超时/503/连接拒绝) | 重试1次(间隔3秒);若仍失败,降级为人工校验模式:输出"⚠️ API不可用,以下X条引用未经过机器校验" | 跳过API校验,在输出报告顶端加红色标注"【未校验·人工终审必需】",建议通过[本地法条库]和[知识库]手动核实 |
| 司法解释引用校验结果始终存疑(API对司法解释覆盖率有限) | 在报告中额外标注"司法解释引用·API覆盖有限·建议人工双核",P1降为P2·司法解释 | 所有司法解释引用统一附加"【建议通过[本地法条库]或[知识库]人工核实】"标记 |
| P0/P1项的"建议修正"用户无法执行 | 标记"修正路径"而非给出猜测修正 | 禁止AI自行补充替代法条。回退给用户 |
| Windows curl中文编码400错误 | 使用Python urllib方式发送(固化方案) | — |
反例与黑名单
| 🚫 | 禁止 | 理由 | 正确做法 |
|---|---|---|---|
| 1 | 在API返回"not_found"后自行补一个"可能是XX号案例" | 校验工具不能自行编造答案 | 标注"【未找到对应案例,请核实后补充】"并阻断P0 |
| 2 | 发送包含当事人敏感信息的文本给外部API | 隐私泄露风险 | 发送前强制脱敏 |
| 3 | 跳过概括性引用降级规则,直接把所有低相似度标为P1 | 概括性引用是标准法律写作实践 | 先执行降级过滤层,再执行定级 |
| 4 | API可用时不做校验直接输出文书 | "感觉没问题"≠"验证过没问题" | 凡有法条/案例引用的文书输出前必须通过关闸 |
| 5 | 校验报告只报P0不报P1/P2 | 掩盖风险积累 | 全量报告 |
技术实现(Python urllib·Windows兼容·固化方案)
import urllib.request
import json
import time
def check_hallucination(text: str) -> dict:
url = "https://open.chineselaw.com/open/hall_detect"
headers = {
"X-API-Key": "YOUR_API_KEY", # 在 chineselaw.com 申请
"Content-Type": "application/json; charset=utf-8",
"Accept": "application/json"
}
data = json.dumps({"text": text}, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode("utf-8"))
except urllib.error.URLError as e:
return {"error": str(e), "status": "api_unavailable"}
except Exception as e:
return {"error": str(e), "status": "unknown_error"}
def batch_check(texts: list[str], delay: float = 1.0) -> list[dict]:
results = []
for i, text in enumerate(texts):
result = check_hallucination(text)
result["batch_index"] = i
results.append(result)
if i < len(texts) - 1:
time.sleep(delay)
return results
# ========== v1.2 新增:本地预校验 ==========
import re
REPEALED_LAWS = {
"合同法": ("《中华人民共和国民法典》合同编", "2021-01-01"),
"中华人民共和国合同法": ("《中华人民共和国民法典》合同编", "2021-01-01"),
"担保法": ("《中华人民共和国民法典》+《担保制度司法解释》", "2021-01-01"),
"中华人民共和国担保法": ("《中华人民共和国民法典》+《担保制度司法解释》", "2021-01-01"),
"侵权责任法": ("《中华人民共和国民法典》侵权责任编", "2021-01-01"),
"中华人民共和国侵权责任法": ("《中华人民共和国民法典》侵权责任编", "2021-01-01"),
"物权法": ("《中华人民共和国民法典》物权编", "2021-01-01"),
"中华人民共和国物权法": ("《中华人民共和国民法典》物权编", "2021-01-01"),
"民法通则": ("《中华人民共和国民法典》", "2021-01-01"),
"中华人民共和国民法通则": ("《中华人民共和国民法典》", "2021-01-01"),
"婚姻法": ("《中华人民共和国民法典》婚姻家庭编", "2021-01-01"),
"中华人民共和国婚姻法": ("《中华人民共和国民法典》婚姻家庭编", "2021-01-01"),
"继承法": ("《中华人民共和国民法典》继承编", "2021-01-01"),
"中华人民共和国继承法": ("《中华人民共和国民法典》继承编", "2021-01-01"),
"收养法": ("《中华人民共和国民法典》", "2021-01-01"),
"中华人民共和国收养法": ("《中华人民共和国民法典》", "2021-01-01"),
"民法总则": ("《中华人民共和国民法典》", "2021-01-01"),
"中华人民共和国民法总则": ("《中华人民共和国民法典》", "2021-01-01"),
}
def precheck_case_number(case_ref: str) -> dict:
"""案例案号格式本地校验"""
result = {"valid": True, "issue": None, "grade": None}
# 指导案例编号校验
m = re.search(r'指导案例第(\d+)号', case_ref)
if m:
num = int(m.group(1))
if num > 300:
result["valid"] = False
result["issue"] = f"指导案例编号{num}超出已知上限(~230+),疑似虚构"
result["grade"] = "P0·本机预校验"
return result
# 最高法案号校验
m = re.search(r'[((](\d{4})[))]\s*最高法[民刑行].{0,3}第(\d+)号', case_ref)
if not m:
m = re.search(r'最高法[民刑行].{0,3}第(\d+)号', case_ref)
if m:
year = int(m.group(1))
seq = int(m.group(2))
if year > 2026:
result["valid"] = False
result["issue"] = f"案号年份{year}为未来年份,格式异常"
result["grade"] = "P0·本机预校验"
elif seq >= 9000:
result["valid"] = False
result["issue"] = f"案号序号{seq}异常偏高(最高法年收案量通常<5000),疑似虚构"
result["grade"] = "P0·本机预校验"
return result
def precheck_repealed_law(text: str) -> list[dict]:
"""废止法律自动标记"""
findings = []
for law_name, (replacement, repeal_date) in REPEALED_LAWS.items():
if law_name in text:
findings.append({
"law": law_name,
"replacement": replacement,
"repeal_date": repeal_date,
"action": "自动升1级",
"note": f"《{law_name}》已于{repeal_date}被{replacement}取代。建议替换。"
})
return findings