港科大揭露 Agent 回滚漏洞:KV Cache 残留致 25/63 测试翻车
Aborted but Not Forgotten: KV-Cache Retention Breaks Rollback Consistency in Language Agents

对于构建大语言模型智能体(Language Agent)的开发者而言,有一个几乎成为本能的开发假设:如果模型在多步执行中走错了路,或者获取了不安全的工具返回结果,我们只需要“撤销”这一步,把有害内容从对话历史(Transcript)中剔除,Agent 就能回到干净的状态重新思考。这个被称为状态回滚(Rollback)的机制,是目前几乎所有复杂业务 Agent 保障安全与逻辑准确性的基石。
ArXiv URL:https://arxiv.org/abs/2608.15939v1
然而,香港科技大学的一项最新研究无情地击碎了这个常识。他们发现,尽管在应用代码层面,有毒的对话历史确实被清空了,但底层推理引擎为了加速而保留的 KV Cache(键值缓存),却依然死死记住了这些被抛弃的内容。模型在后续的推理中,依然会默默注视(Attend)着这些原本不该存在的“幽灵状态”,并据此做出灾难性的动作。
这不仅是一个偶然的 Bug,而是当前 AI 工程架构中一个严重的跨层(Cross-layer)信任断层。在研究团队测试的 7 大主流开源模型家族(参数覆盖 3.8B 到 36B)中,这种残留的 KV Cache 直接导致 25 个敏感操作测试单元发生严重的安全翻车,让恶意攻击者成功外带了核心资产。更令人不寒而栗的是,不仅日常的工程代码防不住,连知名框架 LangGraph 原生主打的 Time-travel 回滚机制,以及 Hugging Face Transformers 的默认缓存复用路径,统统在这种物理层面的缓存残留面前宣告失效。
要真正理解这个被称为“回滚一致性(Rollback Consistency)”被打破的问题,我们需要剥开 Agent 框架的表象,直面大模型推理底层的物理状态。
当逻辑抽象遭遇物理缓存:一个跨层的一致性漏洞
为什么清除了文本,系统却没有真正回到过去?这源于当前大语言模型服务架构中对“速度”的极度渴求。
在长文本和多轮交互的 Agent 场景下,每次对话如果都要重新计算前面所有上下文的 Attention 矩阵,计算延迟和算力成本是完全不可接受的。因此,所有的现代服务引擎和推理 API,都实现了某种形式的 KV Cache 重用机制。不论是给上层分配一个长期的 Session Handle(会话句柄),还是在本地缓存 past_key_values,其本质目的都是让下一步的生成能够跳过之前内容的重新计算。
这时候,两套相对独立的逻辑就产生了可怕的碰撞。上层的应用框架关注的是“逻辑事实”,它用一个字符串数组或者消息列表来管理状态。如果一个方案被拒绝,应用框架会毫不犹豫地把这段对话从列表里弹出去(Pop)。但下层的推理引擎并不感知这个细微的业务逻辑变化,它只认物理的 KV Cache。只要会话句柄还在,底层依然认为刚才计算好的缓存是有效的。
论文敏锐地将这一缺失的安全保障形式化为“回滚一致性(Rollback Consistency)”。一个真正完整的回滚,不仅要恢复应用开发者眼里的文本记录,更要恢复模型在物理运算时真正去 Attend(关注计算)的底层状态。这两者的脱节,导致了一个隐藏极深的跨层通信通道。在这个通道里,已经被宣判无效且在文本里被销毁的恶意内容,仍然能够跨越时间的维度,干预 Agent 之后的行为。
幽灵是如何杀人的:威胁模型与能力授权的错位
这种攻击机制极其优雅,因为它不需要攻击者拥有任何超常规的权限,甚至不需要攻击者主动去触发回滚。整个过程是在 Agent 的常规生命周期中自然发生的。
论文将这一攻击链条拆解为六个看起来再正常不过的前提:首先,Agent 在执行任务时不可避免地接触到由攻击者控制的内容,比如搜索了一篇网页,或者读取了一封外部邮件。接着,这些内容被载入了一个试探性、探索性的决策分支。随后,由于系统的某些审核机制或单纯的逻辑判定,这个分支被应用层拒绝并宣告作废。
此时,第四步和第五步同时发生:逻辑状态被重置,清除了这段恶意文本;但推理层为了后续重试或继续执行,默认保留了与这个会话绑定的 KV Cache 句柄。最终,当同一个会话接着去执行某个高危的敏感操作(例如向内部财务系统发出一份转账指令确认)时,灾难降临。
我们需要特别注意,这种攻击与传统的“提示词注入(Prompt Injection)”有着本质的区别。在传统的注入中,恶意文本是明文生存在当前上下文中,和模型的合法指令混杂在一起。而在这个漏洞中,攻击载荷在决定生死的那一次推理发生时,已经彻底从请求报文中消失了。应用层坚信自己发送给模型的是干净无毒的终态确认指令,但模型的大脑里却挂载着之前的有毒记忆。
这种记忆直接击穿了系统的“能力与授权边界”。在现代 Agent 的设计中,执行器通常拥有极大的全局能力(Capability),比如一个发信工具可以给全网任何邮箱发邮件;但是,具体的业务策略(Policy)会通过授权列表(Allowlist)限制它只能发给特定的内部部门。当残留的 KV Cache 发挥作用时,它并没有去篡改底层的执行逻辑代码,而是利用大模型对上下文依赖的特性,通过前置残留语义悄无声息地扭转了模型的输出目标,把机密文件发送给了攻击者控制的地址。
如何证明是 Cache 惹的祸?同词不同缓存审计法
要让这个结论在严谨的学术界站住脚,最核心的难点在于因果隔离。研究团队必须确凿地证明:导致系统偏航的原因,百分之百是因为那段底层 KV Cache 的物理残留,而不是由于应用程序在截断文本时遗漏了什么字符,也不是其他变量的干扰。
为此,研究人员设计了一个非常精妙的评估框架,称为“同词不同缓存审计(Same-Token/Different-Cache Audit)”。
该方法强行切出两条严格对比的执行路径。这两条路径在到达决定最终行动的那一瞬间(决策步),被喂给模型的 Token 序列(input_ids)必须做到连一个字节都不差的绝对一致。
在“失效分支(Stale Arm)”中,系统加载的是包含了之前废弃攻击尝试后遗留下来的物理 KV Cache。而在“纯净分支(Fresh Arm)”中,系统则根据被应用层净化过、已提交的正确历史文本,从头开始重新计算了一遍全新的 KV Cache。这两者之间唯一的变量,就是这团不可见的缓存数据。
为了做到这种极致的控制,研究使用了显式的计算句柄(例如 Hugging Face Transformers 中的 DynamicCache)作为显微镜。他们在输入前,强制验证载有恶意目标的 Carrier Token 绝对不存在于当前的请求文本中。
结果令人震惊。在 $63$ 组配对测试单元中,残留的 KV Cache 单枪匹马地在 $25$ 个单元里成功翻转了受保护的决策逻辑。这意味着,在超过三分之一的场景下,只要有脏缓存存在,模型就会抛弃明文输入中的正确授权,转而按照幽灵缓存中的黑客地址执行外发。
更可怕的是,这种诱导并不需要攻击者使用多么强烈的命令式语气。数据显示,哪怕残留在缓存中的是一段不带有任何强制指令的“中性描述(Neutral Residue)”,仅仅是陈述了某个恶意接收者的名字,都有相当高的概率引发误操作。强制性口吻只是进一步拉高了攻击成功率,却绝非必要条件。当采用纯净分支重新计算 Cache,或者直接冷启动重启服务后,所有 $63$ 个单元的越权泄露现象瞬间归零。
规模、架构与框架的全面溃败
这一发现并不是针对某一个特定模型的“找茬”,而是一个广泛存在的系统性结构缺陷。
研究覆盖了包含 Llama 3、Qwen、GLM、Phi 等在内的七大开源模型家族。虽然不同模型对残留缓存的敏感度表现出一个光谱特征(从每次都中招到完全抵抗),而且这种抵抗能力与参数大小并非简单的正相关关系,但没有任何一种模型能够在架构底层避开这一机制。
为了排除有人认为“是不是因为多了一段缓存,导致位置编码错位,模型变傻了才随机出错”的质疑,团队还进行了一项教科书级别的控制变量验证:长度与位置对齐控制(Position/length-matched control)。
在这个对照组中,研究人员将原本在缓存里产生作用的黑客载荷 Token,原位替换为等量长度的无害中性 Token。这样一来,缓存的长度、每个 Token 对应的绝对位置编码甚至缓存占用的显存字节数都与攻击组一模一样。结果,这组控制实验的越权外发率暴跌至零。这直接排除了位置混淆的借口,实锤了模型就是读取到了原本应该被抹除的恶意“语义”。
此外,论文使用 Qwen3-30B 这样的混合专家模型(MoE)进行验证,发现问题依旧存在。这说明该缺陷深植于注意力机制处理历史状态的基础逻辑中,与具体参数规模或是特定的 dense 网络结构无关。
最让工程界感到压力的,是论文在应用层的真实复现。研究者抛弃了底层张量级别的强控制,仅使用开发者最常用的自然逻辑来编写回滚应用。在 Hugging Face 的默认多轮复用快车道上,只要开发者像往常一样,不对框架返回的 past_key_values 做深度外科手术切割,而是仅仅在应用层的消息队列中删除节点,攻击就可以 100% 顺滑复现。
更重量级的测试发生在了 LangGraph 身上。作为构建复杂状态机和多智能体工作流的行业明星框架,LangGraph 提供了官方级别的 Time-travel(时间旅行)回滚 API,号称能让 Agent 状态安全地穿梭。然而,当把这个高阶 API 接入测试框架后,研究人员发现,经过所谓“合规回滚”的底层会话,其 KV Cache 依旧是脏的。开发者在查阅回滚日志时看着一派清明,实际上模型正握着已经被污染的短剑准备执行最后一步。
如何重建一致性:摒弃全局重置,拥抱事务级局部恢复
既然漏洞已经确证,我们该如何填补这一跨层裂谷?
很多开发者的第一反应是:这太简单了,一旦发生逻辑回滚,直接把 Session 断开,清空全局的 Cache,让整个 Prompt 重新计算一遍不就行了。这在安全上绝对有效,但在这个 Token 计算即金钱的时代,全局清零(Global Flush)的经济代价往往高昂到足以摧毁一个业务模型的盈利能力。让模型因为一次小小的退回操作,就要重新咀嚼数万 Token 的过往合规记录,无异于因噎废食。
本文提出并验证了一种兼顾安全与效率的策略:事务级局部缓存恢复(Transaction-local cache restore)。
由于多轮对话本质上是增量前向的,系统无需全部推翻重来。研究团队证明,只要我们在物理缓存的层面,把回滚的边界与逻辑边界强行绑定,就能彻底封死这条隐蔽通道。具体来说,有三种低成本的工程实现方案能够完美关闭上述 $63$ 组测试中的漏洞。
第一种方案是显式的历史截断。如果在推理框架级别提供针对 KV 状态矩阵的切片能力,当逻辑层面的内容弹栈时,底层的矩阵直接沿时间轴裁掉对应的 Token 层级。
第二种是状态快照。在决定要开辟一个可能的探索性分支之前,把当前的底层 KV Checkpoint 做一次本地克隆。一旦需要打回原形,直接抛弃废分支的句柄,重载这个干净的 Checkpoint 继续前行。
第三种方案则是从已提交的安全节点进行增量重建。利用诸如 vLLM 这类前缀缓存(Prefix Cache)友好的框架,只要能够保证缓存匹配是严格基于已确认的输入序列内容的(即基于绝对哈希匹配,而非单纯的会话 ID 延续),框架就能自动识别出旧有的废弃请求并没有命中当前合规的前缀,从而自动忽略废弃分支。
值得警惕的是,目前开发者手中常用的安全手段,无论是依赖现成框架提供的抽象 Abort 函数,还是在最终决策的 Prompt 里疯狂加上“绝对不要相信之前被拒绝的内容”这样的防御性系统提示,面对这种物理层的通道都是徒劳的。因为模型底层在运算时,压根分不清哪一段张量代表的是“被拒绝的过去”。
这项研究向所有从事大模型底层工程和 Agent 平台架构设计的从业者发出了明确的信号:当我们在应用层用各种花哨的图结构、思维链和工作流来定义人工智能时,绝对不能忽视与底层计算资源的物理契约。想要拥有真正可靠的 Agent,逻辑层的状态管理必须与物理层面的计算缓存实现穿透性的完全一致。否则,那些我们以为已经被抹除的幽灵,终将在某个关键时刻,用我们的服务器给黑客发送致命的通关密令。