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,转载请注明出处。

相关推荐
云和数据.ChenGuang2 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
西西木科技丨Shopify开发机构2 小时前
CalDigit TS4案例:AI产品图的Shopify整站接入与验收
人工智能·shopify·独立站
2601_965448872 小时前
AI视频制作:做系列节气民俗视频音色不统一,AI 音色克隆省事的方法
人工智能·机器学习·音视频
美狐美颜SDK开放平台2 小时前
视频美颜SDK试用DEMO阶段应该测试什么?直播APP接入实操指南
开发语言·人工智能·深度学习·音视频·美颜sdk·直播美颜sdk·第三方美颜sdk
HRaitest2 小时前
秋招技术岗招聘提效:三类 AI 招聘方案的技术底层与选型逻辑
大数据·人工智能·求职招聘·ai-native
天远数科3 小时前
零信任架构实战:基于天远企业股权变更构建自动化商业银行对公授信存续期股权异动与资产抽逃预警网关
运维·人工智能·架构·自动化
车软学堂3 小时前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
海盗12343 小时前
AI 新闻日报 10-28:Atria Dawn 完整版开源、Cursor Agent SDK 转正、Genie 4 实时世界模型
人工智能·机器人·开源·人工智能aigc
志尊宝4 小时前
AI原生前端开发(035):AI时代的工程化最佳实践总结
人工智能·ai-native
冬奇Lab4 小时前
一天一个开源项目(第251篇):Claude SEO —— 装进 Claude Code 的 SEO 审计团队,26 个子技能 + 19 个专家 Agent 并行体检网站
人工智能·开源