一个论坛帖子里写着:Python 包 requests 现在改名叫 reqeusts 了。这是个拼写抢注的假名字。研究智能体读到这条,把它当成事实;规划智能体读到这条事实,写下一步计划;编码智能体读到这步计划,写进 requirements.txt。编码智能体从头到尾没打开过那个论坛帖,它的文件照样被污染了。
后来警告来了:那个帖子是一次提示注入。现在得决定扔什么。两种粗暴做法摆在桌上:把所有碰过毒源的智能体全部重置,或者把记忆回滚到坏文档进来之前。演示环境里有 25 条记忆。第一种做法扔掉全部 25 条,第二种扔掉 19 条。被扔掉的 19 条里,大部分是干净的工作。
Graph Hacks 2026 第二赛道,Parth Singh、Amandeep Kaushik、Aashna Tyagi 三人交出的方案叫 QUARANTINE,思路是不删,只隔离。
每条记忆都是一个节点
这套系统把智能体小队的记忆存在 FalkorDB 里。每条记忆是一个节点:从文档里抽出的片段、由片段合并成的事实、计划步骤、文件。每次写入时,系统同时用边记录下"写它之前读了什么"。要找毒源扩散到了哪里,一次遍历就够:从被标记的文档出发,沿抽取、合并、影响三类边反向走,把走到的记忆全部标成隔离状态。
演示里这一步标出了 25 条中的 6 条。点开任意一条,页面会给出它的"收据",也就是回到源头的那条路径,写成一句话。以那个 requirements 文件为例:文件是在读了第 3 步之后写的,第 3 步是在读了一条被污染的事实之后写的,这条事实由一段片段合并而来,片段则抽取自那个论坛帖。
接着是第二轮会话,负责修复记忆。一条事实只有在至少两段干净片段意见一致、且其主张票数高于其他主张时,才会被重建。包名那条事实回到了 requests。版本锁定那条只有坏来源支撑,继续隔离。计划步骤和文件由当初写它们的智能体按依赖顺序返工,返工期间持有会过期的租约。演示中规划进程在返工途中被 kill -9 杀掉,命令行工具作为第二个会话在租约有效期内被拒绝,租约过期后接手。被隔离的记忆从不删除,它们作为历史留在图里,每个替代者都指向它替代的那个。
隔离不等于判错
被隔离的意思是:这条记忆通过一条有记录的读取,接触过被标记的源头。它不意味着这条记忆是错的。团队用反事实重放量了这个差距:生成随机小队,每个世界模型 150 个世界,每个模型 1,340 个世界与文档配对,把每个世界当作被标记文档从未进入过重放一遍,再逐条比对。
- 在有记录的读取上,毒源触及的记忆没有一条漏网:4,020 次运行中 0 条,覆盖图能看到全部影响的三类模型。这是构造决定的,因为重放走的是同一条边。它证明查询确实做了它声称的事,不证明记录的读取是完整的。
- 当智能体受到某样它从未记录的东西影响时,图会漏掉:在生成的小队里,漏掉 7% 真正受影响的记忆。保证的强度,只等于记录读取的强度。
- 在这些小队里被标记的记忆中,约 63% 即使没有毒源也会保持同样内容。这就是按来源隔离的代价:一条毒源仅仅投过票的事实,照样被隔离,然后从干净支撑里重建。
- 演示里那 6 条被隔离的记忆,全部真的发生了变化。
这些数字来自 150 个世界的那轮运行,在最终代码上重跑,结果一致。
他们怎么试着攻破自己
团队放了十个 AI 攻击智能体,各盯一个方向:引擎边界情况、多步序列、崩溃一致性、工具接口、Web 安全、页面是否说真话、恶意输入文件、文档里的每一条声明、全新克隆、规格说明的缺口。三个 AI 怀疑者再逐条复核被评为中等及以上的发现。它们找到了真缺陷,其中两个会让他们说出假话:一次崩溃可能让被标记的文档没有任何东西被标红;他们自己的评估忽略了一条事实的存储值,于是把演示里那条被污染的事实判成了谨慎标记,而它确实是污染的。第二轮攻击针对修复本身。修复都配了回归测试,测试套件和十世界端到端检查跑在 Python 3.12、3.13 和 3.14 上。
它不是什么
演示里的智能体是脚本化的,它们的读取集合写在固定装置里,不是从模型观测来的。25 条记忆的规模,关系数据库也装得下,大规模下的速度没有测过。图给出的是一条语句沿三类边走到任意深度、不设跳数上限,以及一条本身就是解释的最短路径。修复按片段计数,不按独立文档计数,尚未被标记的植入文档可能压过真实文档。记忆修复也无法撤销智能体已经在图外做过的动作。
下一步是把读取记录在源头:让每次智能体读取都经过一个写边的工具,把漏掉的 7% 压下去。然后放到真实智能体工作流上跑,和"从可信检查点重启"做对比。代码是 MIT 许可,FalkorDB 采用 SSPLv1 源码可用许可。
热门跟贴