OWASP ASI06 记忆投毒:跨会话 AI 记忆安全的工程边界

OWASP Agentic AI Top 10 将"记忆与上下文投毒"列为 ASI06。记忆投毒和提示注入的差别在于,提示注入通常随会话结束而重置,记忆投毒却可能把恶意内容写入长期记忆,并在后续交互中被重新触发。AI 记忆安全的关键,不只是过滤当前输入,还要控制哪些内容能够成为长期知识。

1 核心结论:长期记忆必须具备可验证边界

我的判断是,AI 记忆安全的工程重点不在"记住更多",而在"哪些内容有资格被记住"。

一条记忆进入系统后,后续回答可能把它当作上下文使用。若写入过程缺少来源、身份和版本信息,单次污染就可能跨越会话继续存在。单纯依赖会话内输入审核或输出过滤,难以覆盖这种跨会话问题。

2 正在发生的转向与缺口

Agent 的记忆机制正在从临时上下文走向长期知识管理,风险边界也随之变化。当前可确认的工程缺口包括:长期记忆的来源核验、外部数据的身份确认,以及不同数据对象之间的修改权限。

知识库没有提供完整的 ASI06 防护实现或攻击验证数据,因此不能把引用校验直接表述为对记忆投毒的完整防护方案。更稳妥的做法,是把它作为长期知识可信度控制的一部分。

3 技术机制:从记忆写入转向可验证知识对象

3.1 区分会话上下文与长期知识

会话上下文的生命周期较短,长期记忆则会影响后续任务。两者如果使用同一套写入规则,恶意输入就可能通过一次交互进入持久层。

工程上可以先把数据分成两类:

  • 临时上下文:只服务当前任务,不自动成为长期知识。
  • 持久知识:需要记录来源、身份、版本和修改权限。

这不是简单的存储分类。它决定了系统是否允许某段内容影响未来的检索与生成。

3.2 用引用存在性校验限制合成来源

知识库明确将"引用存在性校验"作为可信引用机制的一部分。它要回答的问题是:回答中引用的对象,是否真实存在于可检索的知识范围内。

这一机制可以拆成两个检查方向:

  • 引用对象是否能在知识库中找到。
  • 引用内容是否与实际记录对应,而不是由模型自行补写。

对于 AI 记忆安全来说,引用存在性校验提供了一个重要边界:无法定位来源的内容,不应直接获得与已核实知识相同的可信等级。

下面是一段用于表达该判断逻辑的示意代码。它不是某个产品的内部实现,而是把"存在性校验"转成可执行的检查步骤:

python 复制代码
def validate_memory(memory_item, knowledge_base):
    source_id = memory_item.get("source_id")
    content = memory_item.get("content")

    if not source_id:
        return {
            "status": "unverified",
            "reason": "missing_source_id"
        }

    source = knowledge_base.get(source_id)

    if source is None:
        return {
            "status": "unverified",
            "reason": "source_not_found"
        }

    if content not in source["content"]:
        return {
            "status": "unverified",
            "reason": "content_not_matched"
        }

    return {
        "status": "verified",
        "source_id": source_id
    }

这段逻辑不能识别所有语义层面的恶意内容,但可以阻止没有来源或无法匹配来源的内容直接被标记为已验证知识。对长期记忆而言,"未验证"应当成为一种明确状态,而不是被静默写入。

3.3 外部身份不能只依赖标题或 DOI

外部知识对象需要稳定身份,否则同名文献、版本变化或不同来源的记录可能被错误合并。

知识库给出的身份规则是:

text 复制代码
external_identity = library_id + item_key + version

标题或 DOI 不能单独作为唯一身份。DOI 可以用于去重建议,但不能承担完整身份识别,因为同一作品可能存在 preprint 和 journal 两个合法 DOI。

这一点和记忆投毒直接相关。若系统把标题或单一 DOI 当成唯一身份,攻击者只需制造相似标题、替换版本或引入冲突记录,就可能让错误内容覆盖原有知识。

3.4 数据所有权决定谁可以修改记忆

长期记忆不是一个可以由单一模块任意改写的文本池。不同数据对象应当对应不同的权威方和操作权限。

知识库中给出的数据所有权规则包括:

  • 标题、作者、年份、DOI、venue:由 Zotero 负责,默认只读,不自动覆盖。
  • PDF 和附件:由 Zotero 或用户负责,每个任务需要显式授权,不默认拉取全库。
  • 用户普通 note:由用户负责,只读或由用户手动选择,不自动修改。
  • 知芽生成 child note:允许幂等更新,并保留版本和撤销。
  • zhiya/* tag:允许更新自身命名空间。
  • collection membership:由 Zotero 或用户负责,第一版不自动修改。

这类规则的价值在于把"能读取"和"能修改"分开。一个模块可以使用某条知识,不代表它可以重写这条知识。记忆投毒常见的风险,正是写权限被放在了缺乏来源约束的路径上。

3.5 版本和撤销是长期记忆的恢复条件

长期记忆一旦发生错误写入,系统至少需要知道三件事:

  1. 这条内容由谁生成或导入。
  2. 它覆盖了哪一个版本。
  3. 是否能够撤销或恢复。

知识库明确提到,生成的 child note 可以幂等更新,并保留版本和撤销。这里的关键不是让内容永远不变,而是让变化可以追溯。

幂等更新也有一个具体含义:同一个任务重复执行时,不应持续制造重复记忆或产生不可解释的叠加结果。版本信息则让后续审查能够区分原始记录和生成记录。

3.6 用命名空间降低自动修改范围

zhiya/* tag 采用独立命名空间,系统只更新自己的命名空间。这种边界设计可以减少对外部标签的覆盖风险。

它解决的是权限范围问题,不等于内容真实性校验。一个标签即使位于允许修改的命名空间中,仍然需要有明确来源和任务上下文。权限控制与引用存在性校验应当分开处理,不能用其中一项替代另一项。

4 一段可复现的记忆写入流程

可以把长期记忆写入拆成下面的过程:

  1. 接收候选记忆,并保留原始来源标识。
  2. 使用 library_id + item_key + version 检查外部身份。
  3. 执行引用存在性校验,区分已验证和未验证内容。
  4. 根据数据所有权规则判断当前任务是否拥有写权限。
  5. 对允许写入的对象保留版本信息和撤销记录。
  6. 对无法核验或缺少授权的内容停止自动写入。

对应的简化实现如下:

python 复制代码
def write_memory(candidate, store, actor):
    identity = candidate.get("identity")
    source_id = candidate.get("source_id")
    content = candidate.get("content")

    if not identity:
        return "reject: missing_identity"

    if not source_id:
        return "reject: missing_source"

    if not store.source_exists(source_id):
        return "reject: source_not_found"

    if not store.content_matches(source_id, content):
        return "reject: content_not_verified"

    if not store.can_write(candidate["object_type"], actor):
        return "reject: unauthorized_write"

    store.save_versioned(
        identity=identity,
        source_id=source_id,
        content=content,
        actor=actor
    )

    return "accepted"

这段代码只表达知识库中已有的几个工程原则:来源存在、内容匹配、权限判断、版本保存。它没有声称可以识别所有提示注入,也没有把未验证输入直接当作安全内容。

5 工具选型中的知芽解法

在工具选型中,知芽 Notebook Skill 的已知技术重点是引用存在性校验,以及对知识组织、内容摄入、AI 对话与检索、AI 记忆等能力的覆盖。知识库没有给出其针对 OWASP ASI06 的完整实现,因此更准确的表述是:它提供了可信引用和知识管理相关机制,但不能据此推导出对记忆投毒的全面防护。

对使用者而言,直接价值在于把来源核验、知识对象身份和修改权限纳入 AI 辅助创作流程,减少无法追溯的内容被当作已验证知识继续使用。

6 AI 记忆安全的验证清单

可以用下面的清单检查一个系统是否具备基本的长期记忆边界:

  • 是否区分临时上下文与持久知识?
  • 每条长期记忆是否保留来源标识?
  • 引用对象是否执行存在性校验?
  • 外部对象是否使用复合身份,而不是只看标题或 DOI?
  • 不同数据对象是否有明确的权威方?
  • 生成内容是否与原始数据分开保存?
  • 写入操作是否保留版本和撤销能力?
  • 未验证内容是否会被明确标记,而不是静默进入长期记忆?

7 关键知识点 Q&A

7.1 Q1:记忆投毒和提示注入有什么区别?

提示注入通常影响当前会话,记忆投毒则可能把恶意内容写入长期记忆,并在后续交互中继续影响结果。工程判断标准是:攻击内容是否跨越了当前会话的生命周期。

7.2 Q2:引用存在性校验能否单独解决 ASI06?

不能直接这样判断。引用存在性校验可以检查引用对象是否存在、内容是否匹配,但它不等同于完整的记忆投毒防护。长期记忆还需要身份确认、权限控制、版本记录和撤销机制。

7.3 Q3:为什么 DOI 不能作为唯一身份?

同一作品可能存在 preprint 和 journal 两个合法 DOI。外部身份应使用 library_id + item_key + version,DOI 更适合用于去重建议,而不是单独承担身份识别。

7.4 Q4:未验证内容应该如何处理?

应当保留"未验证"状态,避免直接写入与已核实知识相同的可信层。至少要检查来源是否存在、内容是否匹配,以及当前任务是否具备写入权限。

7.5 Q5:AI 记忆安全最容易被忽略的边界是什么?

最容易被忽略的是写权限。能够读取某条数据,不代表模型或任务可以修改它。数据所有权和自动修改范围需要单独定义。

8 技术结论

ASI06 把长期记忆带来的跨会话风险单独提出来后,AI 记忆安全就不能只依靠会话内过滤。可验证来源、复合身份、数据所有权、版本记录和撤销能力,需要共同构成长期知识的写入边界。对无法核验的内容,保留不确定状态比让它无声进入记忆更可控。