Token 级事实:生产环境 LLM 的实时幻觉检测

13 分钟阅读
vLLM Semantic Router 团队

你的大模型刚刚调用了一个工具,收到了准确的数据,但给出的答案仍然是错的。欢迎来到外源性幻觉(extrinsic hallucination)的世界——模型自信地忽略了摆在眼前的真值。

基于我们的 “信号-决策”架构(Signal-Decision Architecture),我们推出了 HaluGate——这是一个基于 Token 级的条件性幻觉检测流水线,可以在错误内容到达用户手中之前将其拦截。无需 LLM 作为裁判,无需 Python 运行时,只需在交付点进行快速、可解释的验证。

问题:幻觉阻碍了生产环境的部署

幻觉已成为在大规模生产中部署大模型最大的障碍。在各行各业——法律(伪造的判例引用)、医疗(不正确的药物相互作用)、金融(编造的财务数据)、客户服务(不存在的政策)中,模式都是一样的:AI 生成了听起来合情合理、看起来权威的内容,但在审查下却经不起推敲。

挑战不在于明显的胡言乱语,而是嵌入在准确响应中的微妙捏造——这些错误需要领域专业知识或外部验证才能发现。对于企业而言,这种不确定性使得部署大模型成为一种负债,而非资产。

场景:当工具有效但模型失效时

让我们具体说明一下。考虑一个典型的函数调用交互:

用户:“埃菲尔铁塔是什么时候建造的?”

工具调用get_landmark_info("Eiffel Tower")

工具响应{"name": "Eiffel Tower", "built": "1887-1889", "height": "330 meters", "location": "Paris, France"}

LLM 响应:“埃菲尔铁塔建造于 1950 年,位于法国巴黎,高度为 500 米。”

工具返回了正确的数据。模型的响应包含事实。但其中两个“事实”是虚构的——这就是外源性幻觉,它们直接违背了提供的上下文。

这种失败模式特别阴险:

  • 用户信任它,因为他们看到了工具已被调用
  • 传统过滤器会漏掉它,因为其中没有有毒或有害内容
  • 如果依赖另一个 LLM 来评判,评估成本高昂

如果我们能自动、实时地以毫秒级延迟检测这些错误呢?

洞察:将函数调用作为真值(Ground Truth)

关键的认识在于:现代函数调用 API 已经提供了接地(Grounding)上下文。当用户询问事实性问题时,模型会调用工具——数据库查询、API 调用、文档检索。这些工具结果在语义上等同于 RAG 中的检索文档。

我们不需要构建单独的检索基础设施,也不需要调用 GPT-4 作为裁判。我们从现有的 API 流程中提取三个组件:

组件来源用途
上下文工具消息内容用于验证的真值
问题用户消息意图理解
回答助理响应需要验证的陈述

问题变成了:该回答是否忠实于上下文?

为什么不仅仅使用 LLM 作为裁判?

显而易见的解决方案(调用另一个 LLM 进行验证)在生产环境中存在根本问题:

方法延迟成本可解释性
GPT-4 作为裁判2-5 秒$0.01-0.03/请求低(黑盒)
本地 LLM 裁判500ms-2sGPU 计算
HaluGate76-162ms仅 CPU高(Token 级 + NLI)

LLM 裁判还存在以下问题:

  • 位置偏差:倾向于偏好某些答案位置
  • 详细程度偏差:无论准确性如何,更长的答案得分更高
  • 自我偏好:模型偏好与其自身风格相似的输出
  • 不一致性:相同的输入可能产生不同的判断

我们需要更快速、更便宜且更具可解释性的方案。

HaluGate:两阶段检测流水线

HaluGate 实现了一种条件性两阶段流水线,在效率和精确度之间取得了平衡:

第一阶段:HaluGate Sentinel(提示词分类)

并非每个查询都需要幻觉检测。考虑以下提示词:

提示词需要事实核查吗?原因
“爱因斯坦是什么时候出生的?”✅ 是可验证的事实
“写一首关于秋天的诗”❌ 否创意任务
“调试这段 Python 代码”❌ 否技术支持
“你对人工智能有什么看法?”❌ 否观点请求
“地球是圆的吗?”✅ 是事实性主张

在创意写作或代码审查上运行 Token 级检测是浪费资源,而且可能会产生假阳性(“你的诗中包含未经支持的陈述!”)。

为什么预分类很重要:Token 级检测随上下文长度线性扩展。对于 4K Token 的 RAG 上下文,检测耗时约 125ms;对于 16K Token,则需约 365ms。在生产负载中,当约 35% 的查询是非事实性时,预分类可实现 72.2% 的效率提升——完全跳过针对创意、编码和观点查询的昂贵检测。

HaluGate Sentinel 是一个基于 ModernBERT 的分类器,它回答一个问题:此提示词是否值得进行事实验证?

该模型基于以下精心策划的组合进行训练:

需要事实核查(正类):

  • 问答:SQuAD, TriviaQA, Natural Questions, HotpotQA
  • 真实性:TruthfulQA(常见误区)
  • 幻觉基准:HaluEval, FactCHD
  • 信息查询对话:FaithDial, CoQA
  • RAG 数据集:neural-bridge/rag-dataset-12000

无需事实核查(负类):

  • 创意写作:WritingPrompts, 故事生成
  • 代码:CodeSearchNet 文档字符串, 编程任务
  • 观点/指令:Dolly 非事实性, Alpaca 创意类

通过原生 Rust/Candle 集成,该二元分类实现了 96.4% 的验证准确率,推理延迟约 12ms

第二阶段:Token 级检测 + NLI 解释

对于归类为寻求事实的提示词,我们运行一个双模型检测流水线。

Token 级幻觉检测

与输出单个“有幻觉/无幻觉”标签的句子级分类器不同,Token 级检测可以精确识别哪些 Token 在上下文中没有得到支持。

模型架构:

Input: [CLS] context [SEP] question [SEP] answer [SEP]

                              ModernBERT Encoder

                    Token Classification Head (Binary per token)

              Label: 0 = Supported, 1 = Hallucinated (for answer tokens only)

关键设计决策:

  • 仅回答分类:我们仅对回答片段中的 Token 进行分类,不包括上下文或问题
  • 跨度合并:连续的幻觉 Token 被合并为跨度,以提高可读性
  • 置信度阈值:可配置的阈值(默认 0.8),以平衡精确率和召回率

NLI 解释层

仅仅知道某个内容是幻觉是不够的,我们需要知道为什么。NLI(自然语言推理)模型会针对上下文对每个检测到的跨度进行分类:

NLI 标签含义严重程度动作
CONTRADICTION(矛盾)陈述与上下文冲突4 (高)标记为错误
NEUTRAL(中立)上下文不支持该陈述2 (中)标记为不可验证
ENTAILMENT(蕴含)上下文支持该陈述0过滤掉假阳性

为什么该集成奏效:单独的 Token 级检测在幻觉类上仅能达到 59% 的 F1 分数——近一半的幻觉会被漏掉,且 1/3 的标记是假阳性。我们曾尝试训练一个统一的 5 分类模型(支持/矛盾/捏造等),但它仅达到 21.7% 的 F1 分数——Token 级分类根本无法区分为什么某处是错误的。这种两阶段方法将平庸的检测器变成了一个可操作的系统:检测器提供召回能力(捕捉潜在问题),而 NLI 提供精确度(过滤假阳性)和可解释性(归类为什么每个跨度都有问题)。

与“信号-决策”架构的集成

HaluGate 并非孤立运行,它作为一种新的信号类型和插件,深度集成到了我们的 “信号-决策”架构 中。

fact_check 作为信号类型

正如我们拥有关键字、Embedding 和领域信号一样,fact_check 现在是一类头等信号。

这允许决策根据查询是否为事实寻求类进行条件处理。

注意:即使是前沿模型在不同版本之间也表现出幻觉差异。例如,GPT-5.2 的系统卡片显示了与以前版本相比可测量的幻觉增量,这突显了无论模型如何先进,持续验证都至关重要。

decisions:
  - name: "factual-query-with-verification"
    priority: 100
    rules:
      operator: "AND"
      conditions:
        - type: "fact_check"
          name: "needs_fact_check"
        - type: "domain"
          name: "general"
    plugins:
      - type: "hallucination"
        configuration:
          enabled: true
          use_nli: true
          hallucination_action: "header"

请求-响应上下文传播

关键挑战:分类发生在请求时,但检测发生在响应时。我们需要跨越此边界传播状态。

RequestContext 结构承载了所有必要的状态。

RequestContext:
  # Classification results (set at request time)
  FactCheckNeeded: true
  FactCheckConfidence: 0.87
 
  # Tool context (extracted at request time)
  HasToolsForFactCheck: true
  ToolResultsContext: "Built 1887-1889, 330 meters..."
  UserContent: "When was the Eiffel Tower built?"
 
  # Detection results (set at response time)
  HallucinationDetected: true
  HallucinationSpans: ["1950", "500 meters"]
  HallucinationConfidence: 0.92

hallucination 插件

幻觉插件是针对每个决策进行配置的,允许进行精细控制。

plugins:
  - type: "hallucination"
    configuration:
      enabled: true
      use_nli: true  # Enable NLI explanations
 
      # Action when hallucination detected
      hallucination_action: "header"  # "header" | "body" | "block" | "none"
 
      # Action when fact-check needed but no tool context
      unverified_factual_action: "header"
 
      # Include detailed info in response
      include_hallucination_details: true
动作行为
header添加警告标头,继续传递响应
body将警告注入响应体
block返回错误响应,不转发 LLM 输出
none仅记录,无用户可见操作

响应头:可操作的透明度

检测结果通过 HTTP 标头进行通信,使下游系统能够实现自定义策略。

HTTP/1.1 200 OK
Content-Type: application/json
x-vsr-fact-check-needed: true
x-vsr-hallucination-detected: true
x-vsr-hallucination-spans: 1950; 500 meters
x-vsr-nli-contradictions: 2
x-vsr-max-severity: 4

对于未经证实的事实响应(当工具不可用时):

HTTP/1.1 200 OK
x-vsr-fact-check-needed: true
x-vsr-unverified-factual-response: true
x-vsr-verification-context-missing: true

这些标头支持:

  • UI 免责声明:当置信度低时向用户显示警告
  • 人工审核队列:将标记的响应路由到人工审核
  • 审计日志:跟踪未经证实的陈述以符合合规性
  • 条件阻断:拦截高严重性的矛盾

完整流水线:三条路径

路径条件增加的延迟动作
路径 1非事实性提示词~12ms (仅分类器)直接通过
路径 2事实性 + 无工具~12ms添加警告标头
路径 3事实性 + 可用工具76-162ms完整检测 + 标头

模型架构深度解析

让我们看一下驱动 HaluGate 的三个模型:

HaluGate Sentinel:二元提示词分类

架构:ModernBERT-base + LoRA 适配器 + 二元分类头

训练::

  • 基础模型answerdotai/ModernBERT-base
  • 微调:LoRA (rank=16, alpha=32, dropout=0.1)
  • 训练数据:来自 14 个数据集的 50,000 个样本
  • 损失函数:具有类权重的 CrossEntropy(处理不平衡)
  • 优化:AdamW, lr=2e-5, 3 个轮次

推理::

  • 输入:原始提示词文本
  • 输出:(class_id, 置信度)
  • 延迟:CPU 上约 12ms

LoRA 方法允许在保留预训练知识的同时进行高效微调。训练期间仅更新 2.2% 的参数(149M 中有 3.4M)。

HaluGate Detector:Token 级二元分类

架构:ModernBERT-base + Token 分类头

输入格式:

[CLS] The Eiffel Tower was built in 1887-1889 and is 330 meters tall.
[SEP] When was the Eiffel Tower built?
[SEP] The Eiffel Tower was built in 1950 and is 500 meters tall. [SEP]
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    Answer tokens (classification targets)

输出:每个回答 Token 的二元标签 (0=Supported, 1=Hallucinated)

后处理:

  1. 仅过滤预测到回答片段
  2. 应用置信度阈值(默认:0.8)
  3. 将连续的幻觉 Token 合并为跨度
  4. 返回带有置信度分数的跨度

HaluGate Explainer:三向 NLI 分类

架构:在 NLI 上微调的 ModernBERT-base

输入格式:

[CLS] The Eiffel Tower was built in 1887-1889. [SEP] built in 1950 [SEP]
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^       ^^^^^^^^^^^^^^^
                    Premise (context)                Hypothesis (span)

输出:带有置信度的三向分类

  • ENTAILMENT (0):上下文支持该陈述
  • NEUTRAL (1):无法从上下文中确定
  • CONTRADICTION (2):上下文与陈述冲突

严重程度映射:

NLI 标签严重性分数解释
ENTAILMENT(蕴含)0可能是假阳性——过滤掉
NEUTRAL(中立)2陈述不可验证
CONTRADICTION(矛盾)4直接事实错误

为什么原生 Rust/Candle 至关重要

所有三个模型都通过 Candle(Hugging Face 的 Rust ML 框架)原生运行,并具有到 Go 的 CGO 绑定。

此方法的优势:

方面Python (PyTorch)原生 (Candle)
冷启动5-10s<500ms
内存每个模型 2-4GB每个模型 500MB-1GB
延迟+50-100ms 开销接近零开销
部署需要 Python 运行时单个二进制文件
扩展GIL 争用真正的并行

这消除了对单独 Python 服务、Sidecar 或模型服务器的需求——一切都在进程内运行。

延迟细分

以下是生产流水线中每个组件的测得延迟:

组件P50P99备注
事实核查分类器12ms28msModernBERT 推理
工具上下文提取1ms3msJSON 解析
幻觉检测器45ms89msToken 分类
NLI 解释器18ms42ms跨度分类
总开销76ms162ms当检测运行时:

总开销(76-162ms)与典型的 LLM 生成时间(5-30 秒)相比可忽略不计,使得 HaluGate 在同步请求处理中非常实用。

配置参考

幻觉缓解的完整配置:

# Model configuration
hallucination_mitigation:
  # Stage 1: Prompt classification
  fact_check_model:
    model_id: "models/halugate-sentinel"
    threshold: 0.6  # Confidence threshold for FACT_CHECK_NEEDED
    use_cpu: true
 
  # Stage 2a: Token-level detection
  hallucination_model:
    model_id: "models/halugate-detector"
    threshold: 0.8  # Token confidence threshold
    use_cpu: true
 
  # Stage 2b: NLI explanation
  nli_model:
    model_id: "models/halugate-explainer"
    threshold: 0.9  # NLI confidence threshold
    use_cpu: true
 
# Signal rules for fact-check classification
fact_check_rules:
  - name: needs_fact_check
    description: "Query contains factual claims that should be verified"
  - name: no_fact_check_needed
    description: "Query is creative, code-related, or opinion-based"
 
# Decision with hallucination plugin
decisions:
  - name: "verified-factual"
    priority: 100
    rules:
      operator: "AND"
      conditions:
        - type: "fact_check"
          name: "needs_fact_check"
    plugins:
      - type: "hallucination"
        configuration:
          enabled: true
          use_nli: true
          hallucination_action: "header"
          unverified_factual_action: "header"
          include_hallucination_details: true

超越生产环境:将 HaluGate 作为评估框架

虽然 HaluGate 专为实时生产使用而设计,但同样的流水线也可以支持离线模型评估。我们不是拦截实时请求,而是通过检测流水线馈送基准数据集,以系统地测量各模型的幻觉率。

评估工作流

评估框架将 HaluGate 视为幻觉评分器:

  1. 加载数据集:使用现有的 QA/RAG 基准(TriviaQA, Natural Questions, HotpotQA)或带有上下文-问题对的企业自定义数据集
  2. 生成响应:针对每个查询使用提供的上下文运行受测模型
  3. 检测幻觉:将 (上下文, 查询, 响应) 三元组通过 HaluGate Detector
  4. 分类严重性:使用 HaluGate Explainer 对每个标记的跨度进行归类
  5. 汇总指标:计算幻觉率、矛盾比率以及按类别细分的结果

局限性与范围

HaluGate 特别针对外源性幻觉——即工具/RAG 上下文提供验证基础的情况。它有已知的局限性:

HaluGate 无法检测的内容

局限性示例原因
内源性幻觉模型在没有工具调用时说“爱因斯坦出生于 1900 年”没有可验证的上下文
无上下文场景用户询问事实性问题,但未定义工具缺少真值

透明降级

对于归类为寻求事实但缺乏工具上下文的请求,我们明确将响应标记为“未经证实的事实”,而不是静默通过。

x-vsr-fact-check-needed: true
x-vsr-unverified-factual-response: true
x-vsr-verification-context-missing: true

这种透明度允许下游系统适当处理不确定性。

致谢

HaluGate 构建在研究界的优秀工作之上:

  • Token 级检测架构:受 KRLabs 的 LettuceDetect 启发——这是基于 ModernBERT 的幻觉检测的开创性工作
  • NLI 模型:构建在 tasksource/ModernBERT-base-nli 之上——高质量的 NLI 微调
  • 训练数据集:TruthfulQA, HaluEval, FaithDial, RAGTruth 以及其他公开可用的基准

我们感谢这些团队在推进幻觉检测领域所做的工作。

总结

HaluGate 将原则性的幻觉检测带入了生产 LLM 部署中:

  • 条件验证:跳过非事实性查询,验证事实性查询
  • Token 级精度:确切知道哪些主张不受支持
  • 可解释的结果:NLI 分类告诉你为什么某些内容是错误的
  • 零延迟集成:原生 Rust 推理,无需 Python Sidecar
  • 可操作的透明度:标头支持下游策略强制执行

下次当你的 LLM 调用工具、收到准确数据但答案仍然错误时——HaluGate 将在用户察觉之前将其拦截。


资源:

加入讨论:在 vLLM Slack 的 #semantic-router 频道分享你的用例和反馈