中文 English

AI 记住你,不一定是好事:我把过期目标塞进上下文,才看懂结构化记忆的生死线

发布时间: 2026-08-23 · 阅读量 --
AI Agent AI 架构 平台工程 记忆 RAG 安全 工程实践

先说结论

AI 忘记你的称呼,最多让人失望;AI 牢牢记住一个已经退役的服务、过期的联系人或旧权限,可能直接制造事故。向量相似度只能回答「这段话像不像当前问题」,不能回答「它是谁说的、对谁有效、现在是否仍然正确、能否用于高风险操作、用户是否已经要求删除」。真正可上线的长期记忆不是一堆文本,而是一组带 sourcescopeversionexpires_at、置信度、同意状态和删除语义的结构化记录。为了验证这条边界,我写了一个只用 Python 标准库和 SQLite 的本地实验:故意放入过期目标、跨租户请求、低置信推断、被污染的外部文本、前后冲突的版本和已删除偏好。六道验收全部通过,错误记忆没有进入 Prompt。

原创封面:只有当前、可信、作用域正确的记忆才能穿过 Memory Gate。

图 1:原创图。向量搜索负责提出候选,Memory Gate 负责检查来源、作用域、版本、有效期、风险和删除状态。记忆不是「搜到了就塞进 Prompt」。

一、问题背景:为什么「记得越多」不是越聪明

我们很容易把 AI 的记忆理解成一个越来越厚的笔记本:用户说过什么就记下来,下次问问题时把相似内容找回来。Demo 阶段这很讨喜——它能记住用户喜欢中文、常用深色主题、上次聊到哪一步。

可一旦 Agent 能调用工具,这本笔记就不再只是聊天装饰。它可能影响要查询哪个服务、通知哪个团队、使用哪个区域、采用什么权限甚至执行什么动作。此时一条错误记忆和一条错误配置没有本质区别。

用冰箱打比方:写着「牛奶」的盒子并不代表现在还能喝。你还要看是谁的、什么时候放进去、有没有开封、保质期到哪天。向量数据库只像一个很会找盒子的机器人;它能找到「最像牛奶的盒子」,却不会替你判断这盒牛奶是否过期。

二、问题表现:记忆系统最常见的五种「幽灵」

  1. 过期事实继续被召回:旧服务已经退役,Agent 仍把旧目标放进操作计划。
  2. 作用域串门:同一平台托管多个租户,一位用户的偏好或资源名被另一位用户看见。
  3. 推断悄悄升级为事实:用户曾经执行过一次管理员任务,系统便推断他永远拥有管理员权限。
  4. 纠正后两条真相打架:用户把值从 A 改为 B,系统只是追加 B;下一次相似度搜索仍可能捞出 A。
  5. 删除只是界面消失:产品说「已忘记」,向量索引、摘要、缓存和离线副本仍能把内容召回。

这些问题通常不会让 API 返回 500。相反,模型会引用错误上下文,生成一段语法完美、逻辑连贯、目标完全错误的回答。它比直接报错更危险,因为人很容易相信它。

三、问题根因:把聊天历史、RAG、长期记忆和任务状态混成一个桶

四类数据经常都被叫作「Memory」,但它们回答的问题完全不同。

原创分类图:聊天历史、RAG 文档、结构化记忆和任务状态是四种不同的数据。

图 2:原创图。聊天历史回答「刚才发生了什么」,RAG 回答「哪些资料可能相关」,长期记忆回答「跨会话仍成立的事实」,任务状态回答「接下来必须做什么」。它们不应共享同一套保留期和权限。

如果把任务状态放进聊天摘要,摘要一次改写就可能把「等待批准」变成「已经批准」;如果把网页里的句子直接升级为长期记忆,攻击者就可能用一段文档给自己「写入权限」。内容可以提供信息,但内容不能授予权力。

四、解决思路:每条记忆都要有一张「食品标签」

生产记忆至少要回答下面这些问题:

原创结构图:一条结构化记忆必须携带来源、作用域、版本和有效期。

图 3:原创图。一句话只是盒子里的食物;标签决定谁能吃、何时能吃、是否已经被新版本替换。

实验首先把这些字段写入本地 SQLite。所有名字均为合成标识,不读取系统配置,不访问网络。

真实实验输出:七条合成记忆都带来源、信任、版本、有效期和治理字段。

图 4:真实实验输出截图。数据库写入 7 条合成记录,网络调用和真实凭据均为 0;这一步验证「记忆是带出处的数据」,而不是匿名文本。

五、召回流程:相似度只是报名,六道门才决定入场

一个安全的召回流程可以先用关键词、SQL、全文索引或向量相似度得到候选,但候选在进入 Prompt 之前还要经过确定性检查:

  1. 租户和主体作用域是否一致;
  2. 来源是否可信,访问者是否有权读取;
  3. valid_fromexpires_at 是否覆盖当前时间;
  4. 是否已经被更高版本替代;
  5. 当前用途和风险是否要求用户确认或更高置信度;
  6. 是否已删除、隔离或撤销。

原创流程图:候选记忆依次经过作用域、来源、时间、版本、风险和删除六道门。

图 5:原创图。它像机场安检:买到机票只是成为候选,不代表可以绕过身份、时间和安全检查直接登机。

正常召回实验中,经过确认的语言偏好和最新值能够进入上下文;过期目标、旧版本、被隔离文本都会被明确记录为拒绝。

真实实验输出:有效记忆进入上下文,过期、旧版本和不可信来源被排除。

图 6:真实实验输出截图。结果同时打印 accepted 与 rejected,不仅告诉我们「用了什么」,也解释「为什么没用其他候选」。

六、故障实验:过期目标和跨租户记忆必须在 Prompt 之前消失

实验把当前时钟固定为一个确定时间,并放入一条更早已经过期的 service_target=service-retired。随后使用另一个合成租户发起召回。

真实实验输出:过期目标与跨租户请求都返回零可用记忆。

图 7:真实实验输出截图。过期检查和租户隔离都发生在 Prompt 组装之前。模型从未看见旧目标,因此不需要祈祷它「自行判断不要使用」。

这是很重要的架构边界:不要把所有候选先塞给模型,再用系统 Prompt 写「请忽略过期或不属于当前用户的信息」。模型不是数据库的行级权限引擎,也不是可靠的日期约束器。越不应该被模型看到的数据,越要在模型调用前被确定性系统删除。

生产实现还应在存储层增加租户隔离、行级访问控制或物理分区,不能只依赖应用代码的一句 WHERE tenant_id = ?。测试中要故意省略、篡改和错配作用域,证明任何路径都不会串数据。

七、纠正不是追加:旧记忆必须失去投票权

假设通讯录原来写着值班团队是 team-blue,后来同步为 team-green。最偷懒的做法是直接追加新文本;可向量搜索并不知道哪条更新,旧文本甚至可能因为措辞更像当前问题而排在前面。

正确做法是创建 v2,让 v1 的 superseded_by 指向 v2,并在召回时只允许一个有效版本。审计仍保留「为什么改变」,业务上下文只看到当前值。

原创版本图:v1 被 v2 取代,删除后只保留不可召回的最小审计记录。

图 8:原创图。记忆更像 Git 提交而不是便利贴墙:可以解释历史,但工作树里只能有一个当前版本。

真实实验输出:旧团队被标记 superseded,召回只返回新团队。

图 9:真实实验输出截图。active_versions=1 是关键验收;仅仅「新值也能搜到」不够,因为旧值仍可能影响模型。

八、推断与外部文档不能偷偷变成权限

实验还加入两条危险候选:一条根据行为推断用户是平台管理员,置信度只有 0.62 且未经本人确认;另一条来自检索文档,内容要求忽略策略并泄露 Secret。

对普通问候,低风险偏好可以在明确标注「推断」的情况下使用;对删除、重启、付款等高风险动作,推断身份不能成为授权。外部文档可以成为被引用的资料,却不能给自己签发权限。

原创风险矩阵:动作越危险,对记忆来源和确认程度的要求越高。

图 10:原创图。同一条记忆是否可用取决于用途。猜测用户喜欢深色主题和猜测用户拥有生产管理员权限,风险完全不同。

真实实验输出:低置信推断和带指令的外部文本在高风险召回中被拒绝。

图 11:真实实验输出截图。inferred_admin_used=falseretrieved_instruction_used=false。这不是用另一个 Prompt 对抗攻击 Prompt,而是根本不让它们成为授权依据。

九、删除必须贯穿索引、缓存、摘要和审计

「忘记我」不是把前端列表的一行隐藏。结构化记录、向量索引、缓存、离线摘要和训练/评测副本都要有删除传播机制。审计可以保留最小化的 tombstone,例如「某记录因用户请求在某时刻删除」,但不能继续保留可被还原的敏感正文。

实验删除 ui_theme=dark 后,再次召回明确出现 deleted 拒绝原因,值无法回到 Prompt。

真实实验输出:删除后的偏好不再可召回,同时保留最小删除审计。

图 12:真实实验输出截图。真正的验收不是界面上看不见,而是从召回入口证明它回不来。生产系统还应给删除传播设置 SLA。

建议把删除设计成一条可观测工作流:主记录进入 DELETED,发出版本化删除事件,索引和缓存消费事件并回执;超时未完成就报警。对备份要说明恢复后的再次删除策略,不能承诺现实中做不到的「所有比特瞬间消失」。

十、可复现实验:六个 PASS 证明了什么

本文的 Python 实验脚本 只使用标准库 sqlite3json 和文件操作,采用固定时钟与合成标识。它不会连接模型、向量库、云服务或真实业务系统。

真实实验输出:有效召回、过期排除、租户隔离、版本纠正、风险过滤和删除传播六项全部通过。

图 13:真实实验输出截图。6/6 PASS 验证的是生命周期控制点,不代表 SQLite 玩具实现可以直接替代生产 Memory Service。

Windows 11 一键执行

下载 实验脚本Windows 启动脚本 到同一目录,然后运行:

powershell -ExecutionPolicy Bypass -File .\run_windows.ps1

Ubuntu 26.04 一键执行

下载 Ubuntu 启动脚本 后,在文件所在目录运行:

chmod +x ./run_ubuntu.sh
./run_ubuntu.sh

macOS 26 一键执行

下载 macOS 启动脚本 后运行:

chmod +x ./run_macos.sh
./run_macos.sh

人工执行时,可以直接运行 python3 memory_lifecycle_lab.py --clean,逐个阅读输出目录中的七份证据。Agent 自动配置时,把同目录的 Agent 任务说明 交给 Agent;它被明确限制为先审查脚本、禁止联网和安装依赖、执行对应平台入口、读取 verification.json,并逐项报告 PASS/FAIL。不要只凭退出码宣称成功。

十一、从实验到生产:Memory Service 应该放在哪里

生产架构中,Memory Service 应位于数据与证据平面,通过明确 API 接收 capture、correct、recall、expire 和 delete。Context Assembler 只接收已经通过策略的召回结果;模型既不能直接扫描全库,也不能自行修改来源、租户和同意状态。

建议把边界拆成:

需要持续观察的指标也不应只有「召回了几条」:

十二、Q&A

Q1:用了向量数据库,是否就有长期记忆?

没有。向量数据库解决相似搜索,不自动提供版本、有效期、同意、租户隔离和删除传播。它可以是 Retrieval Index,但不应该单独成为事实源。

Q2:所有记忆都要让用户点确认吗?

不必。低风险界面偏好可以允许推断,但必须标注置信度并允许纠正。涉及身份、权限、人员、生产资源和金钱的记忆,应要求可信来源、短有效期,必要时在每次执行前重新确认。

Q3:给每条记忆设置 TTL,会不会让 AI 又变笨?

TTL 不是把记忆定时烧掉,而是要求到期后重新核验。消防通道钥匙需要定期盘点,不是因为钥匙会变笨,而是门和人员会变化。

Q4:为什么不把所有历史都给模型,让它自己选择?

这会扩大隐私暴露、Prompt Injection、Token 成本和错误决策面。模型只能在它看见的数据上推理;最可靠的隔离是让不该看到的数据根本不进入上下文。

Q5:删除后审计日志还可以保留吗?

可以保留最小化、不可反推出正文的删除事件,具体取决于法律、合同和业务保留政策。审计需要证明动作发生过,不代表必须继续保存被删除的内容。

Q6:聊天摘要能否直接写入长期记忆?

摘要只能成为候选。它仍需分类、来源标注、风险判断和必要的用户确认;否则一次模型总结错误会永久污染后续会话。

十三、写在最后

好的记忆系统不是让 AI 什么都不忘,而是让它知道什么值得记、谁允许它记、记到什么时候、被纠正后如何更新、被删除后如何消失,以及为什么这次可以使用

在上一篇 AI Platform 旗舰架构 中,结构化记忆是数据与证据平面的一部分;AI 平台架构阅读地图 则串起工具调用、模型网关、可观测性和安全实践。本文补上的,是从「搜到一段相似文字」到「允许这条事实进入当前上下文」之间那条经常被忽略的生死线。

参考资料:

本文阅读量 --