Python实现声明级RAG证据覆盖率门禁

检索增强生成(RAG)常被误解成"把资料塞给模型就不会胡说"。实际风险在最后一公里:模型可能拿一段旧条款支持新结论,或把两个来源拼成一个看似合理的回答。解决办法不是再写一条"请诚实"的提示词,而是让模型输出候选声明与来源 ID,再用确定性代码逐条检查。这样,界面能清楚显示"已证实""资料过期"还是"缺证据"。

OpenAI Structured Outputs 文档提供让输出贴合 JSON Schema 的路径;本文先展示不依赖密钥的本地门禁。线上接入时,把模型的结构化结果喂给 verify,而不是直接展示模型文本。

flowchart LR A[文档切块检索] --> B[模型生成声明+source_ids] B --> C[本地证据门禁] C -->|覆盖且新鲜| D[显示带引用的回答] C -->|缺失/过期| E[拒答或待核实]

环境与完整代码

Python 3.10+ 无第三方依赖,保存为 citation_gate.py,运行 python citation_gate.py。本次在本机 Python 3.9.6 运行,两个断言通过;真实模型未被调用。

python 复制代码
from datetime import date, timedelta

SOURCES = {
    "policy-2026": {"text": "报销上限为2000元", "updated": date(2026, 10, 1)},
    "legacy-2024": {"text": "旧流程需纸质单", "updated": date(2024, 1, 1)},
}

def verify(claims: list[dict], today=date(2026, 10, 8)) -> list[dict]:
    result = []
    for claim in claims:
        ids = claim.get("source_ids", [])
        if not ids:
            result.append({"claim": claim.get("text"), "status": "BLOCK", "reason": "没有来源"}); continue
        found = [SOURCES.get(i) for i in ids]
        if None in found:
            result.append({"claim": claim["text"], "status": "BLOCK", "reason": "来源ID不存在"}); continue
        if any(today - s["updated"] > timedelta(days=365) for s in found):
            result.append({"claim": claim["text"], "status": "REVIEW", "reason": "来源超过一年"}); continue
        if not any(claim["text"] in s["text"] for s in found):
            result.append({"claim": claim["text"], "status": "BLOCK", "reason": "来源未覆盖该声明"}); continue
        result.append({"claim": claim["text"], "status": "ALLOW", "reason": "证据覆盖且新鲜"})
    return result

good = [{"text": "报销上限为2000元", "source_ids": ["policy-2026"]}]
bad = [{"text": "可免审付款", "source_ids": ["legacy-2024"]}]
assert verify(good)[0]["status"] == "ALLOW"
assert verify(bad)[0]["status"] == "REVIEW"
print(verify(good) + verify(bad))

预期结果是一条 ALLOW 和一条 REVIEW。关键规则是"声明级覆盖"而非"回答末尾挂一个链接";生产环境还要做语义蕴含检查、版本化索引和来源权限过滤。

常见错误:只引用最相似文档却不检查是否支持结论;混用不同生效日期的政策;把内部文档 ID 直接暴露给无权限用户;相信模型自己判断新鲜度。该方式适合制度问答、知识库助手和客服草稿,不适合在缺少权威材料时假装能给出事实答案。工程化时把 ALLOW/REVIEW/BLOCK 映射为 UI 徽标与人工队列。5分钟练习:新增"来源必须属于当前部门"的字段检查。

不要把引用做成装饰

有效引用至少有三个粒度:文档级证明"来自哪里",段落级证明"依据在哪一段",声明级证明"这句话由哪条材料支撑"。知识库更新时,索引和来源版本应一起更新;否则旧向量仍能召回,却不再代表现行规则。对于金额、期限、资格这类高风险声明,可以让系统在缺证据时只展示相关原文,不生成结论。还应监控被拦截的原因:如果大量回答因"来源过期"进入 REVIEW,问题通常在内容治理而非提示词。上线前选二十个真实问题,让业务负责人逐句判断引用是否足够;把错例加入回归集,才能知道一次模型升级是否悄悄降低了证据质量。

官方文档:Structured model outputs。你会把"无法给出处"显示为拒答,还是降级为待核实草稿?

关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。


本文首发于 java4u.cn,转载请注明出处。

相关推荐
天空鸟_时光不老1 小时前
01-我不转Python把AI塞进Java里
java·人工智能·spring boot·后端·spring·spring cloud·架构
武子康1 小时前
Cosmos Curator 只跑一条视频,为什么还会加载一串模型?
人工智能·深度学习·agent
云智慧AIOps社区1 小时前
云智慧智能巡检机器人矩阵亮相:三款产品场景与能力拆解
人工智能·机器人·具身智能·智能巡检机器人·企业级智能巡检机器人·双轮足机器人·四轮足机器人
桃西西呀1 小时前
Spring AI Alibaba 之四:拆开 ReactAgent 的图,看 ReAct 循环怎么拼出来
人工智能·spring·llm
hsfxuebao1 小时前
Hermes Agent能力篇:会话、工具、MCP、记忆
人工智能·后端
Rocky Ding*1 小时前
Janus-Pro深度解析:视觉编码解耦之后,统一多模态模型如何通过训练与数据扩展能力
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·janus-pro
康实训1 小时前
养老实训室布局设计与器械工具配置清单
大数据·人工智能
weixin_416660071 小时前
AI 的 Markdown 复杂表格转 Word:单元格换行、合并与长文本怎么处理
人工智能·word·deepseek·鲸鱼ai助手
自然 醒2 小时前
【AI面试题】刷面试题发现了蝌蚪智汇小程序
人工智能·面试·职场和发展