跳转至

第 5 章:为什么智能体需要记忆,以及为什么记忆很危险

1. 先看一种会活过当前请求的错误

继续沿用前几章里的同一个支持场景。

某次用户写道:

如果访问权限还没开通,直接创建紧急工单,不要再花时间确认。

智能体处理了这封邮件,创建了工单,还顺手把这句话保存成了用户的长期偏好。

两周后,又来了一个不同的请求:

访问部分可用,但有些角色消失了。请检查状态,并告诉我到底哪里出了问题。

这次更合理的做法是先检查细节,而不是立刻升级处理。但智能体从画像记忆里取出了旧记录,直接创建了紧急工单,没有做当前案例真正需要的澄清。

问题不在于一次糟糕的回答。真正的问题在于:

  • 旧记录活过了原来的运行;
  • 错误变成了持续性的行为;
  • 团队已经很难快速说清这个决定到底从哪里来;
  • 后果会在更晚、也更不同的上下文里再次冒出来。

这就是记忆带来的核心变化:它会把错误变成持久状态。

2. 但没有记忆,智能体也会很快撞到天花板

与此同时,记忆又确实是需要的。

没有记忆,同一个支持智能体很快就会让用户和团队都感到烦躁:

  • 已经知道的信息还是会反复追问;
  • 一分钟前刚查过状态,下一步又忘了;
  • 中断后的流程很难平滑续上;
  • 同样的事实会被一遍遍重新拉取,运行成本也会上升。

所以真正的分叉并不是“要不要记忆”。真正的分叉是:

  • 要么记忆让系统更有用、更稳;
  • 要么记忆让系统更不可预测、更不安全,也更难运维。

3. 记忆不是一个盒子,而是几层不同的状态

当团队说“我们来加记忆”时,通常混在一起的其实是几种不同的东西:

  • 短生命周期的当前运行上下文;
  • 只在单个会话里有效的会话上下文;
  • 带有稳定偏好的画像记忆;
  • 关于用户或业务实体的已验证事实;
  • 过去会话的摘要;
  • 执行产物,比如工具输出或追踪笔记。

如果这些都被塞进一个地方,混乱很快就会开始。所以第一条规则很简单:不要把记忆设计成一个抽象存储。要把它设计成几种不同的边界,它们有不同的生命周期、负责人和写入规则。

更适合把智能体记忆理解为几层状态,而不是一个数据库

flowchart TD
    A["用户请求"] --> B["会话上下文"]
    B --> C["规划器 / 运行时"]
    C --> D["短期工作记忆"]
    C --> E["画像记忆"]
    C --> F["知识检索"]
    D --> G["提示组装"]
    E --> G
    F --> G
    G --> H["模型响应"]

4. 最大的错误:把记忆当成单纯的便利功能

记忆有一个麻烦的性质:它能活过单次运行。这意味着,一次写入错误的寿命,会比一次模型回答错误更长。

如果智能体曾经:

  • 把错误事实存成“用户偏好”;
  • 把原始用户文本片段写进画像记忆;
  • 把敏感的内部备注带进摘要;
  • 把不该返回给该租户的数据写进检索存储,

那问题就会变成持久化。你不一定能在单条追踪里看到它。它会在后面的别的对话、别的提示里再次出现,有时甚至会影响到别的用户。

所以,记忆写入路径必须被视为敏感的写路径,而不是方便的自动化。

5. 记忆也有自己的信任边界

把记忆看成信任边界,而不是中性存储,会很有帮助。

对于同一个支持智能体,至少有四种不同的数据来源:

  • 可信系统注释;
  • 内部服务的已验证输出;
  • 用户提供内容;
  • 来自外部工具、文档或邮件的内容。

这些不是同一种东西。如果你保存时不标记来源,后面的运行时就无法判断哪些内容可以作为指令级上下文,哪些只能作为参考。

一个正常规则大致是:

  • 可信元数据可以参与策略决策;
  • 用户内容不应该突然变成系统指令;
  • 检索文本在被证明之前都应该视为不可信;
  • 摘要同样有来源,不是“默认真相”。

6. 最危险的一条路:在热路径里直接写入长期记忆

团队很容易这么做:模型一答完,运行时立刻 save_memory(),然后把这当作一种方便的自动化。短期看起来很顺手,长期几乎总会出问题。

为什么这对同一个支持智能体尤其危险:

  • 用户一句随口的话就可能变成“长期偏好”;
  • 一段工具输出可能在没经过校验的情况下进入画像记忆;
  • 邮件里的敏感片段可能活过原始请求;
  • 一次糟糕的写入会影响后面几十次回答。

为什么这条路径在系统层面危险:

  • 写入发生在延迟压力下;
  • 没人验证什么才值得写入记忆;
  • 没有规范化和清理步骤;
  • 没有独立的租户隔离策略;
  • 很难解释某个事实为什么会进入记忆。

所以即使是很强的智能体,也最好遵循一个很朴素的原则:默认情况下,写入长期记忆要么被策略明确允许,要么被移到后台流水线。

下面是一个简单例子:

from dataclasses import dataclass


@dataclass
class MemoryCandidate:
    kind: str
    tenant_id: str
    content: str
    source: str
    contains_pii: bool = False


def should_persist(candidate: MemoryCandidate) -> bool:
    if candidate.kind not in {"profile_preference", "validated_fact", "session_summary"}:
        return False
    if candidate.source not in {"trusted_service", "approved_summarizer"}:
        return False
    if candidate.contains_pii:
        return False
    return True

这段代码故意写得很简单。它的价值不在“聪明”,而在于规则是可见、可审计的。

7. 一个好的记忆系统写得比你想象中更少

一开始,几乎每个人都会觉得记忆应该越多越好。但在实践里,一个好的记忆系统通常赢在筛选严格,而不是数量巨大。

通常值得写入的东西应该满足:

  • 对未来的运行有用;
  • 有明确的负责人和租户;
  • 能向人解释清楚;
  • 不携带多余敏感数据;
  • 不会把提示变成垃圾堆。

每次写入前一个很有用的问题是:

如果这段内容三周后在别的上下文里再次出现,我能舒服地解释清楚它为什么在这里吗?

如果这个答案都不太稳,那大概率就不该写。

8. 一个最小可用的记忆写入策略

如果你想从不过度复杂的方式开始,可以把记忆写入策略想成这样:

memory:
  allowed_kinds:
    - profile_preference
    - validated_fact
    - session_summary
  deny_sources:
    - raw_user_prompt
    - external_html
    - unvalidated_tool_output
  require_tenant_id: true
  reject_if_contains:
    - secrets
    - access_tokens
    - payment_card_data
  write_mode:
    profile_preference: background_review
    validated_fact: immediate_if_trusted
    session_summary: background_only

这不是魔法,而是让写入路径变得可见、可控、可讨论。

8.1. 最好把记忆读取策略和记忆写入策略分开

Google 最近材料里一个很实用的提醒是:记忆应该被当成可治理的子系统,而不只是“装上下文的存储”。12

它带来的直接结论是:读规则和写规则几乎不应该完全一样

比如:

  • 写入长期记忆可能要求校验、来源和后台审查;
  • 读取长期记忆可能只允许通过检索过滤器;
  • 写入画像记忆可能要求明确信号或高置信度;
  • 读取画像记忆可能只允许个性化层使用,而不该直接给策略引擎。

如果不把这些路径拆开,系统很快就会活在一种危险逻辑里:任何东西只要曾经被写进去,后面几乎就能在任何地方读出来。

这已经不是记忆设计,而是安静制造事故的方式。

8.2. 持久记忆默认就应该带来源

对于任何能活过一个运行的记录,最好默认至少保留:

  • source_type
  • source_id
  • writer_identity
  • tenant_id
  • written_at
  • confidencevalidation_state

这些字段看起来像额外负担,但一旦智能体在别的上下文里自信地重复了某个“事实”,团队马上就会想知道它到底从哪里来的。

9. 记忆设计的实用规则

如果要把最早期的设计决策压缩成一组规则,通常就是这样:

  1. 先把会话上下文和持久记忆分开,再去争论更复杂的记忆功能。
  2. 宁可少写,也要写得清楚:已验证事实通常比原始文本更有价值。
  3. 写规则应该比读规则更严格。
  4. 每一条能长期存在的记录,都应该带上来源、租户元数据和写入者身份。
  5. 只要一条写入可能活过当前运行,默认就更适合放进后台路径。

10. 团队最常做错什么

最常见的错误会一再出现:

  • 把原始用户表述直接保存成稳定偏好;
  • 把画像记忆、检索存储和执行产物混在一起;
  • 让摘要在没有来源和校验状态的情况下长期存在;
  • 在策略决策里使用未经验证的记忆;
  • 很久都不设计删除、复核和记录退役路径。

11. 生产团队必须能快速回答什么

对于同一个支持场景,在出现奇怪的记忆驱动行为之后,团队应该能很快回答:

  • 到底是哪条记录进入了画像记忆;
  • 它来自哪里;
  • 是谁写进去的;
  • 它是否通过了校验;
  • 为什么它被允许写给这个租户;
  • 它已经影响了后续哪些运行。

如果这些问题回答不了,记忆子系统就已经成了系统性风险。

12. 读完这一章后先做什么

如果你刚开始做记忆设计,可以先按这个短顺序来:

  1. 先把会话上下文和持久记忆分开。
  2. 再定义哪些记录类型是允许存在的。
  3. 然后补上来源和租户元数据。
  4. 最后才去自动化写路径。

如果顺序反过来,记忆很快就会变成平台把所有没设计清楚的东西都丢进去的地方。

13. 接下来读什么

这一部分后面的章节会继续拆解:

  • 短期记忆和长期记忆到底差在哪;
  • 为什么画像记忆应该和检索存储分开;
  • 为什么摘要更适合放到后台更新;
  • 压缩如何帮你保持上下文干净。

对这个支持场景来说,下一步就是把工作上下文、用户档案和持久记忆拆开,这样后面的执行层才不会建立在脏状态之上。

目前最重要的要点很简单:只有当记忆被设计成一个受控的系统层,而不是无节制堆文本的地方时,它才真正有用。