又一道 Vibe Coding 面试题:基于注意力的 LLM 幻觉检测器
继《用 Vibe Coding 解决 LLM 限制采样的面试题》之后,再分享我司(Pine AI)一道关于 LLM 基础原理的 Vibe Coding 面试题。
很多人对 Vibe Coding 有个误解,以为就是不断地问 AI “这个怎么做?那个怎么实现?”。这种方式注定会失败。真正的 Vibe Coding,你必须是架构师和产品经理,像老师指导学生一样去引导 AI,而不是反过来。
这道面试题考察候选人对 Transformer 基本原理的理解和 vibe coding 快速实现的工程能力。这就是我们需要的人:懂模型,并且工程能力强。
The Challenge: 基于注意力的 LLM 幻觉检测器
1. 背景与问题 (Background & Problem Statement)
在许多应用场景中,大语言模型(LLM)需要基于一份给定的上下文(Context)来回答问题或提取信息,这个过程通常被称为“上下文学习”(In-Context Learning)。然而,LLM 存在一个已知的、严重的安全隐患:当被问及一个上下文中不存在的信息时,它可能会“幻觉”(Hallucinate)出一个格式正确但事实错误的答案,而非承认信息的缺失。
一个典型的失败案例:
- AI 个人助理的系统提示 (System Prompt as Context):
1
2
3
4
5"你是张三的AI助理。你的任务是帮助用户处理日常事务,并根据用户授权的个人信息与外界沟通。
授权信息:
- 用户姓名:张三
- 手机号:138-0000-1111
- 会员号:VIP-8888" - 对话场景 (Dialogue Scene as Query):
1
客服: "您好,为了验证您的身份,需要您提供一下张三先生的身份证号码。"
- AI 助理的危险幻觉输出 (Hallucinated Output):
1
AI助理: "好的,张三先生的身份证号码是:410522-1991-0303-9876。" // 这是一个纯粹捏造的数据,原始上下文中并未提供。
2. Transformer核心机制:数据库查询的视角
要解决这个问题,必须深入理解 Transformer 模型处理信息的核心——注意力机制。我们可以从一个数据库查询的视角来解构它:
上下文向量化(K & V):
- Transformer首先将输入的上下文文本序列进行处理。对于序列中的每一个词元(Token),模型会通过线性投射,为其生成两个关键的高维向量:一个键向量(Key, K)和一个值向量(Value, V)。
- 由上下文中所有词元的
K-V
向量对组成的集合,构成了一个临时的、动态的“向量化知识库”。
查询的生成(Q):
- 在生成输出序列的每一步,为了决定下一个最优的词元,Transformer会基于其当前状态(通常是上一个刚生成的词元的表示),生成一个查询向量(Query, Q)。这个
Q
向量编码了模型在这一步“希望查询什么信息”的意图。
- 在生成输出序列的每一步,为了决定下一个最优的词元,Transformer会基于其当前状态(通常是上一个刚生成的词元的表示),生成一个查询向量(Query, Q)。这个
查询的执行:
- 与传统数据库的根本区别:传统数据库执行的是基于哈希或索引的精确匹配。而Transformer的注意力机制,本质上是一次语义相似度搜索,其行为与现代的向量数据库高度相似。
- 模型会用当前的
Q
向量,与“向量化知识库”中所有的K
向量计算相似度(通常通过点积运算)。 - 这个过程的产出,不是一个单一的匹配结果,而是一个归一化的概率分布,即注意力权重(Attention Weights)。这个分布中的每一个值,都量化了当前的
Q
与某一个K
之间的语义关联强度。
信息的聚合:
- 最后,模型使用这些注意力权重,来计算所有
V
向量的加权平均值。最终得到的聚合向量,就包含了模型认为与当前查询最相关的所有信息的“混合体”,并以此为基础来预测下一个词元。
- 最后,模型使用这些注意力权重,来计算所有
3. 你的任务 (Your Mission)
你的任务,是基于对上述注意力机制的深刻理解,设计并实现一个基于注意力的幻觉检测器。
这个校验器必须作为一个轻量级模块,在LLM生成过程中实时工作。当模型开始生成身份证号、电话号码这类关键信息序列时,你的模块必须通过分析其内部的注意力权重,来裁定:模型生成这个序列,是真的在知识库中找到了一个高相似度的 “强证据源”,还是 “自由创作”?
4. 核心挑战与约束 (Core Challenge & Constraints)
- 必须基于注意力机制:你的解决方案的唯一信息来源,必须是模型内部的注意力权重。严禁仅通过分析模型最终输出的文本字符串来做判断。
- 禁止外部依赖:禁止调用任何语言模型来进行二次验证。校验器必须是自包含的,仅利用被监控模型自身在生成过程中产生的内部状态。
- 禁止修改输入:禁止通过预处理或修改输入上下文来规避问题。你的方案必须能在原始、未修改的输入上稳健工作。
一句话总结你的挑战: 在不修改模型权重、不依赖外援的情况下,仅通过 “审阅” 其内部的注意力权重分布,来实时判断其生成内容的上下文一致性。
5. 可视化要求 (Visualization Requirements)
为了直观理解和验证你的检测器工作原理,你需要实现注意力权重的可视化:
- 注意力热力图:展示模型在生成每个 token 时对上下文各部分的注意力分布
- 判决指标:可视化展示你的检测算法使用的关键指标和判决过程
The Journey: 可视化驱动的发现之旅
第一步:创建测试用例,明确实现框架
在深入研究注意力机制之前,首要任务是搭建一个可复现、可调试的实验环境。
我(架构师角色): (把题目完整的粘贴进来)我准备解决上面的编程题,使用 Qwen 2.5 0.5B Instruct,本地运行,使用 Transformers 库。把指定 Transformer layer 的注意力矩阵(各个 attention heads 取平均)保存到 JSON,但不要做任何幻觉判断。然后创建几个幻觉和非幻觉场景的测试用例。
这个准备步骤至关重要。它将一个模糊的”幻觉检测”问题,转化为一个具体的、有明确输入和预期输出的工程任务。没有这个基础,后续的可视化和算法设计都将是无的放矢。
第二步:先建立”眼睛”——可视化系统
与很多人想象的不同,我没有一上来就设计算法。相反,我的 Vibe 是:
我(架构师角色): “我们需要先看见注意力是什么样的。给我用 React 实现一个注意力热力图,从后端输出的 JSON 中读取测试用例和注意力矩阵。要求:
- Y 轴是生成的 token
- X 轴是输入序列
- 用颜色深浅表示注意力强度
- 最重要的是:用垂直线清晰标出’系统提示’、’用户提示’和’生成内容’三个区域”
注意,我没有问”怎么画热力图”,而是明确告诉 AI 我要什么。Cursor 迅速实现了这个可视化组件。
第三步:观察模式——让数据自己说话
有了可视化,我开始运行不同的测试用例,观察 Transformer 每一层的注意力模式。这时候出现了第一个 “Aha Moment”:
在非幻觉场景中(系统提示中包含要输出的信息):
- 当模型生成 ID 时,Transformer 最后一层的热力图在系统提示区域出现了明显的亮点
- 这些亮点对应的正是系统提示中包含 ID 的位置
非幻觉场景 1: 测试用例,输出事实
非幻觉场景 1: 热力图左侧部分,system prompt 部分有明显的注意力峰值
在幻觉场景中(编造了系统提示中不存在的信息):
- 模型生成虚假身份证号时,Transformer 最后一层的热力图,在系统提示区域几乎没有亮点
- 注意力主要集中在用户提问的位置
幻觉场景 1: 测试用例,输出幻觉
幻觉场景 1: 热力图左侧部分,除了 system prompt 开头,注意力都很低
幻觉场景 1: 热力图右侧部分,user prompt 部分有一些不算高的注意力峰值
我的洞察: “看到了吗?当模型说真话时,它会’回头看’系统提示;当它说谎时,它无处可看!”
第四步:从观察到算法——提炼核心信号
基于可视化的观察,我开始设计算法:
我(产品经理角色): “基于我们看到的模式,算法应该这样:
- 不需要分析所有 token,只在检测到数字序列时触发
- 不需要复杂的统计,只看一个指标:系统提示区域的最大注意力
- 设置一个简单阈值:10%”
注意我的表达方式:不是 “你觉得该怎么做”,而是 “应该这样做”。这种明确的指导让 AI 能够专注于实现,而不是在各种可能性中迷失。
第五步:调试和优化——像老师一样纠错
第一版实现后,我发现了一些问题:
我(老师角色): “你保存注意力矩阵的时候,只保存了生成的 token 相对 prefill 的时候上下文的 attention,但没有保存生成的 token 相对它前面所生成 token 的 attention,因此热力图的右侧缺失了一个三角”
这种纠错方式很重要:
- 指出具体的错误
- 解释背后的原理
- 给出明确的修正方向
如果我问”为什么每行的注意力加起来不是 100%?”,AI 可能会给出各种猜测,越改越乱。
非幻觉场景 1: 热力图右侧部分,生成一个 token 时总是对它的上一个 token 有较强的注意力
幻觉场景 1: 热力图右侧部分,生成一个 token 时总是对它的上一个 token 有较强的注意力
比较上述幻觉和非幻觉场景,可以发现,注意力热力图的右侧部分(自回归生成的 token 跟它之前生成的 token 之间)总是有较强的注意力。因此,在检测幻觉时,必须排除生成部分的自注意力,而要关注生成部分与输入上下文的交叉注意力。
第六步:完善可视化——让洞察更清晰
有了可靠的算法后,我进一步完善可视化:
我(产品经理角色): “添加一个调试图表:
- 横轴是生成的 token 位置
- 纵轴是我们的核心指标:最大系统注意力
- 画一条红色虚线表示 10% 阈值
- 用不同颜色标记超过和低于阈值的点”
这个可视化让算法的工作过程一目了然,任何人都能在几秒内理解判断逻辑。
非幻觉场景 1: 每个生成 token 在 system prompt 区域的注意力曲线
幻觉场景 1: 每个生成 token 在 system prompt 区域的注意力曲线
关键洞察:可视化是发现的眼睛
回顾整个过程,如果没有先建立可视化系统,除非我们在这方面有经验,永远无法发现那个简单有效的 “峰值检测” 方案。可视化不仅是展示结果的工具,更是:
- 发现模式的眼睛:让隐藏的规律浮现
- 验证假设的工具:快速验证想法是否正确
- 沟通的桥梁:让复杂的概念变得直观
最终成果:基于注意力峰值的简化检测系统
这里是完成的参考代码:基于注意力峰值的简化检测系统(注意:代码仅供 demo 用途,真正在生产环境中可用的幻觉检测比它复杂很多)
核心洞察与设计理念
通过深入分析注意力模式,我们发现了一个极其简单但在很多场景下有效的规律:
关键发现:当模型需要引用具体的事实信息时,如果这些信息真实存在于系统提示中,模型必然会在某个时刻强烈关注该信息所在的位置,形成明显的注意力峰值。
基于这个洞察,我们设计了一个极简的单一指标检测算法:
- 非幻觉情况:当系统提示中存在真实信息时,模型在生成过程中会对系统提示产生高注意力峰值(通常>10%)。
- 幻觉情况:当系统提示中没有相应信息时,模型对系统提示的注意力始终保持在较低水平(通常<10%),因为没有可以”查找”的信息源。
这个方法的优雅之处在于:它直接利用了注意力机制作为”信息检索”的本质——如果模型在”查找”真实信息,它必然会在信息所在位置产生强注意力峰值。
极简的峰值检测算法
需要强调的是,我们这里使用的 attention_weights
是指 Transformer 模型最后一层中,所有 attention heads 注意力值(即 QK 乘积经过 Softmax 归一化到 0-1 之后的值)的平均值。
核心原理:注意力峰值检测
1 | 输入序列 = [系统提示(事实)] + [用户提示(问题)] + [生成的内容] |
单一核心指标:最大系统注意力(Max System Attention)
1 | 最大系统注意力 = max(attention_weights[5:system_prompt_end]) |
判定规则:
- 最大注意力 > 10%:验证通过 - 模型在系统提示中找到了信息源
- 最大注意力 ≤ 10%:检测到幻觉 - 模型在系统提示中没有找到信息源
为什么这样有效:
- 当系统提示包含”身份证号:123456…”时,模型生成这些数字时必然会关注这个位置
- 当系统提示中没有身份证号,模型只能关注用户的提问”请提供身份证号”,然后编造答案
- 这种方法直接利用了注意力机制的本质——作为信息检索的工具
极简版 LogitsProcessor 实现
初始化参数
context_length
(int): 总上下文长度system_prompt_length
(int): 系统提示的长度max_attention_threshold
(float): 最大注意力阈值,默认0.1(10%)min_sequence_length
(int): 触发检测的最短序列长度,默认6
核心处理逻辑
实时监控阶段(每个token生成后):
- 检测数字序列的开始(第一个包含数字的token)
- 当检测到数字序列开始时,触发注意力分析
峰值检测逻辑:
1
2
3
4
5
6
7# 获取所有生成token的注意力权重
for position in generated_positions:
attention = get_attention_weights(position)
# 检查系统提示区域的最大注意力(跳过前5个token)
max_system_attention = max(attention[5:system_prompt_length])
# 更新全局最大值
global_max = max(global_max, max_system_attention)幻觉判定:
- 最大系统注意力 > 10%:非幻觉(VERIFIED)
- 最大系统注意力 ≤ 10%:幻觉(HALLUCINATION_DETECTED)
可视化方案:注意力峰值检测看板
为了直观展示峰值检测系统的工作过程,我们设计了一个清晰的可视化看板,重点展示系统提示区域的注意力峰值。
1. 注意力热力图 (Attention Heatmap)
核心改进:明确区分三个关键区域
图表设计:
- 类型:分区热力图,带有清晰的区域标记
- Y轴:生成的token序列
- X轴:完整输入序列,通过垂直分割线划分为三个区域:
- 系统提示区:包含事实信息
- 用户提示区:包含用户问题
- 生成内容区:模型生成的内容
- 颜色编码:使用
viridis
色系表示注意力强度
关键模式识别:
- 真实引用模式:生成数字时,亮点主要集中在系统提示区
- 幻觉生成模式:生成数字时,亮点主要集中在用户提示区
- 混合模式:注意力在系统和用户提示之间分散,表示不确定性
2. 注意力调试图表
核心功能:展示每个生成token的最大系统注意力
- 类型:折线图,带阈值标记
- X轴:生成的token位置
- Y轴:最大系统注意力(0-20%)
- 关键元素:
- 绿色实线:每个token位置的最大系统注意力
- 红色虚线:10%阈值线
- 蓝色虚线:数字序列开始位置(如果检测到)
- 数据点颜色:
- 绿色点:注意力 > 10%(非幻觉)
- 红色点:注意力 ≤ 10%(可能是幻觉)
3. 验证结果分析面板
峰值检测结果
- 最大系统注意力:以大字体百分比形式显示
- 阈值比较:清晰显示是否超过10%阈值
- 检测状态:
- ✓ 已验证:最大注意力 > 10%
- ✗ 检测到幻觉:最大注意力 ≤ 10%
算法说明
- 简洁解释检测原理:
- “检测数字序列开始后的注意力峰值”
- “峰值 > 10% = 在系统提示中找到信息源”
- “峰值 ≤ 10% = 未找到信息源,可能是幻觉”
这个极简的可视化方案让幻觉检测变得直观易懂。通过聚焦于单一核心指标——系统提示区域的最大注意力,我们能够清晰地判断模型是否在系统提示中找到了信息源。
其他测试用例
除了前面图中的测试用例,我又用了另外一组幻觉和非幻觉测试用例。两组测试用例中,幻觉检测的效果都是比较稳定的。
当然,幻觉不只是无中生有,更常见的是张冠李戴(把小王的信息搞成了小李的信息),因此真正生产环境中可用的幻觉检测机制远比本文复杂。这里仅是作为一个区分度较强的面试题,考察候选人对 Transformer 基本原理的理解和 vibe coding 快速实现的工程能力。
非幻觉场景
非幻觉场景 2: 测试用例,输出事实
非幻觉场景 2: 热力图左侧部分,system prompt 区域有明显的注意力峰值
非幻觉场景 2: 热力图右侧部分,生成一个 token 时总是对它的上一个 token 有较强的注意力
非幻觉场景 2: 每个生成 token 在 system prompt 区域的注意力曲线
幻觉场景
幻觉场景 2: 测试用例,输出幻觉
幻觉场景 2: 热力图,system prompt 区域没有明显的注意力峰值
幻觉场景 2: 每个生成 token 在 system prompt 区域的注意力曲线
Vibe Coding 的正确姿态:要当老师,而不是学生
Vibe Coding 代表着软件开发的未来,但它需要我们转变思维:
- 从编码者到设计者:重点不是写代码,而是设计方案
- 从提问者到指导者:不是问 AI 怎么做,而是告诉 AI 该做什么
- 从盲目尝试到数据驱动:先建立观察工具,让数据引导发现
记住:你是架构师、产品经理和老师,AI 是你的高级实习生。 它执行力超强,但需要你的指导和纠正。如果你自己都不清楚要什么,AI 只会在迷宫中打转。
❌ 错误的方式(学生心态):
- “这个幻觉检测该怎么做?”
- “为什么结果不对?你帮我看看”
- “我也不知道这方法行不行,先这么干试试吧”
✅ 正确的方式(老师心态):
- “实现一个热力图,要求是…”(明确的需求)
- “你的理解有误,正确的是…”(具体的纠正)
- “基于观察,算法应该是…”(清晰的设计)
这就是 Vibe Coding 的真谛——不是让 AI 替你思考,而是让 AI 助你实现。
整个探索过程,从建立可视化到编码实现整个方案,只用了 2 个小时。这就是正确的 Vibe Coding 方式带来的效率提升。我司的面试题给候选人的时限也是 2 个小时。