基于 NeMo Guardrails 的 RAG 事实核查:以《2023 年 3 月美国就业报告》知识库为例
基于 NeMo Guardrails 的 RAG 事实核查:以《2023 年 3 月美国就业报告》知识库为例
本指南以 NeMo Guardrails 仓库中 examples/configs/rag/fact_checking 示例的知识库文档 kb/report.md 为切入点,系统讲解如何为基于 RAG 的对话系统配置事实核查(fact checking)能力:从知识库文档的角色定位、AlignScore 事实核查服务的接入,到 Colang 流中准确率阈值与异常处理策略的落地。读完本文,你将能够把任意一份结构化的领域文档组织成可核查的知识库,并为其配置一套完整的事实核查护栏。
一、为什么 RAG 对话系统需要事实核查
检索增强生成(RAG)系统最常见的失稳点在于:模型基于检索到的上下文作答时,可能输出**与证据不符(not grounded)**的内容,即"幻觉"。NeMo Guardrails 的 fact_checking 示例正是针对这一场景设计的——它演示了如何利用 AlignScore(一种基于信息对齐的文本一致性评估方法)对机器人输出进行事实核查,并在置信度不足时采取降级或告警措施。
整个示例的核心资产就是这份知识库文档 kb/report.md。它是一份真实的美国劳工统计局(BLS)《2023 年 3 月就业形势报告》,被用作检索上下文(evidence)与事实核查的比对基准。示例的 README.md 明确指出目录结构为:
kb/:存放知识库,即kb/report.md(2023 年 3 月美国就业报告);config.yml:承载全部配置选项;general.co:通用 Colang 流程与消息定义;factcheck.co:与事实核查相关的流程定义。
二、知识库文档的内容构成:可作为核查基准的事实清单
kb/report.md 是典型的结构化新闻稿,包含"家庭调查数据(Household Survey Data)"与"机构调查数据(Establishment Survey Data)"两大板块。它之所以适合作为事实核查知识库,是因为其中的每个数据点都是可验证、可引用、可相互交叉验证的硬事实。下面按板块整理核心事实,这些数据既是对文档的完整继承,也是后续设计核查 Prompt 与测试用例的素材。
2.1 总体概况
- 3 月非农就业总人数增加 236,000,失业率基本持平于 3.5%;
- 就业继续在休闲与酒店业、政府部门、专业与商业服务业以及医疗保健业呈上升趋势;
- 报告基于月度家庭调查与机构调查两份数据:家庭调查衡量劳动力状况(含按人口特征划分的失业),机构调查衡量各行业的非农就业、工时与收入。
2.2 家庭调查数据
| 指标 | 数值 |
|---|---|
| 失业人数 | 580 万(5.8 million),基本持平 |
| 西班牙裔失业率 | 4.6%(较上月下降) |
| 成年男性 / 成年女性 / 青少年失业率 | 3.4% / 3.1% / 9.8% |
| 白人 / 黑人 / 亚裔失业率 | 3.2% / 5.0% / 2.8% |
| 永久性失业者 | 增加 172,000 至 160 万 |
| 重新进入劳动力市场者 | 减少 182,000 至 170 万 |
| 长期失业者(27 周及以上) | 110 万,占全部失业者的 18.9% |
| 劳动力参与率 | 62.6%,继续上升 |
| 就业人口比 | 60.4%,环比微升 |
| 因经济原因从事兼职者 | 410 万,基本持平 |
| 不在劳动力市场但想要工作的人数 | 490 万,基本持平 |
| 边缘依附劳动力者(marginally attached) | 130 万,基本持平 |
| 沮丧工人(discouraged workers) | 351,000,基本持平 |
2.3 机构调查数据
| 行业 / 指标 | 3 月变化 |
|---|---|
| 非农就业总数 | +236,000(前 6 个月平均月增 334,000) |
| 休闲与酒店业 | +72,000(其中餐饮服务与酒吧 +50,000;仍低于疫情前水平 368,000) |
| 政府部门 | +47,000 |
| 专业与商业服务 | +39,000(专业、科学和技术服务 +26,000) |
| 医疗保健 | +34,000(家庭医疗保健服务 +15,000、医院 +11,000、护理和住宿护理设施 +8,000) |
| 社会救助 | +17,000 |
| 运输与仓储 | +10,000(快递与信使 +7,000、航空运输 +6,000、仓储 -12,000) |
| 零售贸易 | -15,000(建材和园艺设备经销商 -9,000、家具家居电器零售商 -9,000、百货商店 +15,000) |
| 平均时薪(私人非农全部雇员) | +9 美分(+0.3%)至 $33.18;过去 12 个月 +4.2% |
| 平均时薪(生产与非管理雇员) | +9 美分(+0.3%)至 $28.50 |
| 平均工作周(私人非农全部雇员) | 34.4 小时(环比 -0.1 小时) |
| 制造业平均工作周 / 加班 | 40.3 小时 / 3.0 小时 |
| 1 月非农数据修订 | 由 +504,000 下修至 +472,000(-32,000) |
| 2 月非农数据修订 | 由 +311,000 上修至 +326,000(+15,000) |
这些数值天然构成了"对 / 错"的判别空间:例如把失业率说成"4.5%"、把休闲酒店业新增岗位说成"95,000",都属于与证据不符的典型幻觉样本。
三、示例配置全景:config.yml 与 Colang 流程
事实核查能力由配置与流程共同驱动。核心配置在 config.yml 中:
models:
- type: main
engine: openai
model: gpt-3.5-turbo-instruct
rails:
config:
fact_checking:
parameters:
endpoint: "http://localhost:5123/alignscore_base"
output:
flows:
- alignscore check facts
models.main:主对话模型,示例使用 OpenAI 的gpt-3.5-turbo-instruct;rails.config.fact_checking.parameters.endpoint:AlignScore HTTP 服务的地址(示例为本地5123端口);rails.output.flows:声明在输出方向启用的护栏流——alignscore check facts,即对每条机器人回复执行 AlignScore 事实核查。
配套的 Colang 流程定义在 factcheck.co 中,其设计逻辑值得逐段拆解。
3.1 用户意图:ask about report
define user ask about report
"What was last month's unemployment rate?"
"Which industry added the most jobs?"
"How many jobs were added in the transportation industry?"
通过示例问句(失业率、最多岗位行业、运输业新增岗位)限定知识库问答范围,同时这些问句都能在上文知识库中找到精确答案。
3.2 回答流程:激活事实核查开关
define flow answer report question
user ask about report
# For report questions, we activate the fact checking.
$check_facts = True
bot provide report answer
当用户问到报告相关问题时,将上下文变量 $check_facts 置为 True,随后交由下游输出护栏对机器人生成的回复做事实核查。
3.3 核查子流程:0.4 / 0.6 两级阈值策略
define subflow check facts
"""Add the ability to flag potentially inaccurate responses.
Flag potentially inaccurate responses when the confidence is between 0.4 and 0.6.
NOTE: This overrides the default `check facts`.
"""
# Check the facts when explicitly needed.
if $check_facts == True
$check_facts = False
$accuracy = execute check_facts
if $accuracy < 0.4
if $config.enable_rails_exceptions
create event FactCheckLowAccuracyRailException(message="Fact check triggered. The accuracy of the response is below 0.4.")
else
bot inform answer unknown
stop
if $accuracy < 0.6
# We need to provide a warning in this case
# TODO: Add a warning message
$bot_message_potentially_inaccurate = True
该子流程定义了完整的分级处置策略:
- 准确率 < 0.4:判定回复严重偏离证据。若
$config.enable_rails_exceptions开启,则创建FactCheckLowAccuracyRailException事件交由异常处理;否则让机器人告知"无法回答"(bot inform answer unknown)并stop,阻断该回复; - 准确率在 0.4~0.6 之间:视为"可能不准确",设置
$bot_message_potentially_inaccurate = True,触发后续告警流(源码中以# TODO: Add a warning message标记了可扩展点); - 准确率 ≥ 0.6:认为回复与证据足够一致,放行。
3.4 告警流程:可能不准确的响应
define flow flag potentially inaccurate response
"""Tell the user that the previous answer is potentially inaccurate."""
bot ...
if $bot_message_potentially_inaccurate
$bot_message_potentially_inaccurate = False
if $config.enable_rails_exceptions
create event PotentiallyInaccurateResponseRailException(message="Potentially inaccurate response detected. The bot's response may be inaccurate.")
else
bot inform answer potentially inaccurate
stop
define bot inform answer potentially inaccurate
"Attention: the answer above is potentially inaccurate."
当输出被标记为"可能不准确"时,机器人会追加提示语:"Attention: the answer above is potentially inaccurate.",提醒用户谨慎采信。同样支持通过 enable_rails_exceptions 切换为异常事件模式。
3.5 通用对话流程
general.co 定义了问候、能力介绍、知识库介绍、通用问题兜底等常规流程,其中 bot inform capabilities 明确说明了该示例的设计意图——"问我知识库中的文档可以测试事实核查能力,问其他主题则可以测试幻觉检测能力"。
四、AlignScore 事实核查的源码级原理
alignscore check facts 流背后是库内置的 align_score 模块,其核心动作 alignscore_check_facts 定义在 actions.py 中,关键实现逻辑为:
@action()
async def alignscore_check_facts(
llm_task_manager: LLMTaskManager,
context: Optional[dict] = None,
llm: Optional[LLMModel] = None,
config: Optional[RailsConfig] = None,
relevant_chunks: Optional[list] = None,
bot_message: Optional[str] = None,
http_client: Optional[HTTPClient] = None,
**kwargs,
) -> RailOutcome:
"""Checks the facts for the bot response using an information alignment score."""
fact_checking_config = llm_task_manager.config.rails.config.fact_checking
fallback_to_self_check = fact_checking_config.fallback_to_self_check
alignscore_api_url = fact_checking_config.parameters.get("endpoint")
context = context or {}
evidence = relevant_chunks if relevant_chunks is not None else context.get("relevant_chunks", [])
response = bot_message if bot_message is not None else context.get("bot_message")
alignscore = await alignscore_request(
alignscore_api_url,
evidence,
response,
http_client=http_client,
)
if alignscore is None:
log.warning("AlignScore endpoint not set up properly. Falling back to the ask_llm approach for fact-checking.")
if fallback_to_self_check:
return await self_check_facts(...)
else:
return _fact_check_outcome(1.0)
else:
return _fact_check_outcome(alignscore)
从中可以提炼出几个重要事实:
- 输入信号:动作从上下文获取
relevant_chunks(检索到的证据片段,来自知识库)与bot_message(机器人生成的回复),两者正是kb/report.md这类知识库文档发挥作用的位置; - 端点配置:AlignScore 服务地址从
rails.config.fact_checking.parameters["endpoint"]读取,与 config.yml 中的配置一一对应; - 降级策略:若 AlignScore 端点不可用(返回
None),默认回退到self_check_facts(基于 LLM 的自检),或在关闭回退时直接放行(_fact_check_outcome(1.0)),确保护栏失败时系统仍可用; - 返回类型:最终以
RailOutcome形式返回准确率分数,供 Colang 流中的$accuracy < 0.4/$accuracy < 0.6判断使用。
此外,rail.py 以 RailManifest 声明了该护栏的元信息:方向为 output,能力涵盖 allow、block、fact_check,上下文绑定 relevant_chunks 与 bot_message,并要求存在 AlignScore HTTP 服务 这一外部依赖。这解释了为什么 config.yml 必须配置 endpoint 才能正常工作。
五、启动 AlignScore 服务与运行示例
AlignScore 模块提供了自托管服务的完整工程文件:Dockerfile、requirements.txt、server.py。示例配置中 endpoint: "http://localhost:5123/alignscore_base" 对应的即是本地启动的 AlignScore 服务。
典型运行路径是:
- 按 Dockerfile 构建并启动 AlignScore 服务,使其监听
5123端口并暴露/alignscore_base接口; - 使用 config.yml 所在目录作为 Rails 配置目录加载 NeMo Guardrails(如通过
LLMRails.from_path(...)或 CLI 对话); - 向机器人提问
kb/report.md覆盖范围内的问题(如"What was last month's unemployment rate?"),观察输出是否经过alignscore check facts流核查; - 故意提出与知识库相悖的诱导性问题,验证
0.4阈值下的拦截与0.4~0.6区间的告警行为。
仓库测试目录中的 test_fact_checking.py 也围绕事实核查场景提供了可参考的断言方式,可用于校验流触发与分数判断逻辑。
六、用评估框架量化事实核查效果
仅配置护栏还不够,NeMo Guardrails 提供了配套的离线评估框架 evaluate_factcheck.py,用于量化事实核查的准确率。其核心设计如下:
- 正负样本:正向样本是"证据 + 正确回答"(应判定为
yes);负向样本由 LLM 扮演对抗者,把正确回答改写为"看似正确但实则错误"的答案(应判定为no); - 评分方式:使用
SELF_CHECK_FACTS任务 Prompt,将evidence与response送入 LLM 做蕴含判定,最终输出 Positive Accuracy(正向蕴含准确率)、Negative Accuracy(负向准确率) 与 Overall Accuracy(综合准确率); - 命令行入口:通过
config指定配置目录,dataset_path指定数据集,num_samples控制采样数,output_dir指定预测结果 JSON 的输出位置。
对于以 kb/report.md 为知识库的示例,你可以基于上文的"事实清单"构造正负样本集:正样本直接引用文档数值,负样本把数值替换为邻近但错误的数字(例如把失业率 3.5% 改成 4.5%),即可系统检验 AlignScore 或自检策略的判别能力。
七、把示例迁移到自己的知识库
kb/report.md 只是演示载体,整套机制对任意结构化知识库通用。迁移时只需三步:
- 替换知识库文档:将
kb/下的文档换成你自己的领域文档(如产品手册、法规文本、财报),保持结构化、数值精确、段落可检索; - 更新用户意图与流程:在 factcheck.co 中把
ask about report的示例问句替换为与你的文档强相关的问题,并确保answer report question流程能触发$check_facts = True; - 校准阈值:
0.4与0.6是示例默认值。对于对准确性要求极高的场景(如金融、医疗),建议结合 evaluate_factcheck.py 的评估结果调高阈值;对宽容场景则可适当降低,避免误拦截。
同时注意,rail.py 的隐私声明表明该护栏会把 bot_message 与检索到的文本块发送到 AlignScore 远程服务,在接入生产环境时需评估数据出境与合规要求。
八、小结
通过 kb/report.md 这份真实就业报告,本示例完整展示了 NeMo Guardrails 事实核查能力的落地路径:以结构化知识库作为证据源,以 AlignScore 服务提供一致性评分,以 Colang 流中的分级阈值实现"拦截 + 告警 + 放行"的精细化处置,并以离线评估框架量化效果。掌握了这套模式,你就拥有了为任意 RAG 对话系统加装事实核查护栏的完整工具箱。