第 6 章:短期记忆、长期记忆与用户画像记忆¶
怎样读这一章
不用一开始就把所有定义背下来。先抓住一个简单问题:
- 这次运行里哪些东西只是现在有用;
- 哪些东西以后还会有用;
- 哪些东西才真正属于稳定的用户偏好,而不是偶然噪音。
这三类一旦混在一起,记忆层就不再是在帮系统,而是在悄悄把系统写坏。
1. 为什么“记忆”这一个词反而会妨碍你¶
在贯穿全书的支持场景里,这一点很具体。一次运行结束后,团队很容易想把一切都存下来:
- 中间检查步骤;
- 临时的案例摘要;
- 用户的交流语言;
- 其实根本不该跨过一次运行的偶然观察。
也正是在这里,记忆类型不再只是术语表,而开始变成架构决策。
只要系统里出现了记忆,团队就会很容易用一个词去指代所有“当前提示装不下的东西”。这对讨论来说很方便,但对架构来说是个糟糕抽象。
在实践里,你几乎总会有至少三层不同的东西:
short-term memory,用于当前运行或一小串步骤;long-term memory,用于长期知识、摘要和事实;profile memory,用于某个具体用户或账号的偏好、角色和习惯。
贯穿案例:给支持状态分类
在支持分诊案例里,当前工单状态属于短期记忆;一次运行结束后的规范化备注,可能进入长期记忆;用户稳定的沟通语言,可能进入用户画像记忆。但“以后总是创建紧急工单”这种原始句子,不应该进入任何一层,除非经过策略支持的审查明确确认。
如果这几层不拆开,智能体很快就会:
- 把太多旧噪音重新塞回提示;
- 把用户偏好和世界事实混为一谈;
- 把瞬时观察存成长期真相;
- 破坏可解释性,因为你已经说不清某段上下文到底从哪来。
2. 短期记忆是工作台,不是档案馆¶
最适合把短期记忆想成智能体的工作台。它不是“永远的历史”,而是帮助智能体不丢失当前线索的地方。
通常这里会放:
- 中间计划;
- 最近的工具调用结果;
- 临时假设;
- 为当前运行选出的上下文片段;
- 多步工作流的状态。
一个好的短期记忆有三个特征:
- 大小是受限的;
- 生命周期很短;
- 任务结束后就算丢掉,也不会太痛。
如果“永久记录”开始住进短期记忆,那通常说明你没有数据模型,只有一个被塞满的缓冲区。
3. 长期记忆不是拿来装一切的,而是装稳定知识的¶
长期记忆适合保存那些价值能跨过一次对话或一次工作流的记录。
它可能包括:
- 某个业务实体的已确认事实;
- 上一次会话摘要;
- 某个长期 case 的累积知识;
- 为未来检索提前提取并规范化的笔记。
但这里有一个重要过滤条件:如果某条记录离开完整原始上下文以后就无法被合理复用,那它大概率不该进入长期记忆。
团队经常会在这里高估“原始保存”的价值。长期记忆更适合存有意义、可类型化的记录,而不是无穷无尽的原始流。
4. 画像记忆不是知识库¶
画像记忆特别容易被做坏,因为它听起来很无害,好像只是“用户偏好”。但到了生产,它很快就会变成一个敏感层。
这里通常包括:
- 交流语言;
- 回复格式;
- 工作角色;
- 允许的动作渠道;
- 稳定的界面或交互偏好。
关键区别是,画像记忆回答的问题是“系统应该怎样更好地和这个人协作”,而不是“世界上什么是真的”。
如果智能体开始往里面塞任意事实,画像记忆很快就会变成个性化、流言和偶然观察的混合物。
4.1. Durable agent state 不是 memory¶
Cloudflare Agents SDK 很好地暴露了另一条边界:一个 stateful agent instance 可以拥有 persistent state,这些 state 会自动保存,跨 restart 和 hibernation 保留,并在 WebSocket clients 之间同步。1 这是很有用的运行时能力,但不应该自动被当成长程记忆或 profile memory。
state 回答的是“这个 named agent instance 现在处于什么状态”:游戏分数、打开的 case、当前 workflow、客户端可见设置、last-active marker。memory 回答的是“哪些信息之后值得被检索、总结,或作为知识复用”。如果混淆这两种角色,runtime state 就会像 validated knowledge 一样进入 retrieval,而 memory records 也会被拿来当作可变的 UI/session state。
实用规则很简单:durable state 应该有 owner instance、schema version、serialization constraints 和 sync policy;memory record 应该有 class、provenance、tenant boundary、retention rule 和 retrieval semantics。两个层都可以存放在 durable storage 中,但它们的 operational contract 不同。
不同类型的记忆解决不同问题,不应该挤进同一个存储
flowchart LR
A["当前运行"] --> B["短期记忆"]
A --> C["长期记忆"]
A --> D["画像记忆"]
B --> E["规划器状态"]
B --> F["最近工具输出"]
C --> G["已验证事实"]
C --> H["会话摘要"]
D --> I["偏好"]
D --> J["用户约束"] 5. 好的架构会给每一层一个明确问题¶
这会极大帮助设计:
short-term memory:为了不丢任务,现在必须记住什么?long-term memory:什么值得被保存,因为以后还会有用?profile memory:关于这个用户或账号,什么是真正稳定而且有用的?
如果一条记录对这些问题都答不上来,那它可能根本不该被保存。
6. 每一层都应该有自己的读写规则¶
这里最常见的架构错误,是同一个流水线用完全一样的方式去读写所有记忆类型。但这几乎总是错的。
例如:
- 短期记忆可以更自由地读,但保存时间应该极短;
- 长期记忆应该要求来源和租户检查;
- 画像记忆应该对隐私、同意和可解释性更严格。
一个正常系统不仅知道自己存了 什么,还知道每一类东西 如何 进入提示。
memory_classes:
short_term:
ttl: "2h"
read_path: "runtime_only"
write_policy: "immediate"
long_term:
ttl: "90d"
read_path: "retrieval_with_filters"
write_policy: "validated_only"
profile:
ttl: "365d"
read_path: "personalization_only"
write_policy: "explicit_or_high_confidence"
这段 YAML 不一定就是最终实现,但它逼着团队做一个重要决定:记忆不能在“反正就是数据库里的一段文本”这个层面被管理。
6.1. 每一层都应该有自己的修订规则¶
再往成熟架构走一步,很有帮助的一点是:不同的记忆类别应该有不同的更新与修正规则。
比如:
short-term memory往往可以直接替换或丢弃;long-term memory更适合通过新修订版本更新,而不是悄悄覆盖旧值;profile memory往往需要特别谨慎的合并,因为个性化很容易被写坏。
如果根本没有修订记录,后面你看到的就只剩“当前这条记录长什么样”,却回答不了这些问题:
- 是谁改了它;
- 为什么改;
- 之前是什么版本;
- 这次更新是经过验证,还是只是某次运行的副作用。
6.2. 来源最好和记忆类别一起设计¶
来源最好不要等到后面再补,而是应该和记忆类别同时设计。2
在实践里,这通常意味着:
long_term记录几乎总该带来源链接或来源 ID;profile记录需要有可解释的理由,说明系统为什么认为这是稳定偏好;short_term记录的来源可以轻一些,但运行时仍然应该知道它从哪里来。
下面是一个很紧凑的例子:
memory_classes:
short_term:
revision_mode: replace
provenance: minimal_runtime_metadata
long_term:
revision_mode: append_revision
provenance: source_link_required
profile:
revision_mode: merge_with_history
provenance: explicit_signal_or_review
这就把讨论从“文本存在哪”推进到了“这段知识的历史是什么、它到底值不值得信任”。
7. 什么通常应该放进短期记忆¶
一个很有用的实用规则:短期记忆应该帮助智能体现在行动,而不是变成长期真相的来源。
好的候选:
- 当前计划;
- 子任务状态;
- 最近两三次工具调用的结果;
- 关于哪些事情已经检查过的工作笔记;
- 临时的候选摘要。
不好的候选:
- “永远有效”的用户偏好;
- 没清洗过的原始文档;
- 巨大的日志;
- 没有 TTL 的敏感数据;
- 后面会被重复当成真相的不确认事实。
8. 什么通常应该放进长期记忆¶
长期记忆的意义是复用知识,而不是归档所有活动。
合理的内容包括:
- 已确认事实;
- 带来源的仔细摘要;
- 长期案例的状态;
- 规范化后的知识记录;
- 指向文档的链接,而不是把巨大载荷原样塞进去。
一个很实用的原则是:在长期记忆里,通常更值得存一个紧凑记录加来源链接,而不是把存储层做成永久转储。
9. 什么通常应该放进画像记忆¶
画像记忆最有价值的时候,是它让智能体更好用,但不会开始基于不可靠猜测替用户做决定。
好的例子:
- “偏好简短回答”;
- “通常用俄语工作”;
- “修改生产数据时需要确认”;
- “喜欢在一天结束时收到报告”。
不好的例子:
- 对动机或性格的推断;
- 只来自一次会话的随机猜测;
- 没有明确理由的敏感个人数据;
- 后面会被当成事实使用的猜测。
10. 一个简单的记忆路由代码模板¶
下面是一个很简单的例子,展示核心想法:记录先分类,再进入对应的存储。
from dataclasses import dataclass
@dataclass
class MemoryRecord:
kind: str
content: str
confidence: float
def select_memory_bucket(record: MemoryRecord) -> str | None:
if record.kind in {"plan_step", "tool_result", "working_note"}:
return "short_term"
if record.kind in {"validated_fact", "session_summary", "case_state"} and record.confidence >= 0.8:
return "long_term"
if record.kind in {"language_preference", "format_preference", "approval_preference"}:
return "profile"
return None
这个例子故意写得很直白。实践里规则会更丰富,但核心思想不该变:记忆要先分类,而不是直接丢进一个共享容器。
11. 常见错误¶
常见问题通常长这样:
- 画像记忆开始替代授权;
- 长期记忆被噪音填满;
- 短期记忆变得又大又贵;
- 检索返回内容时不考虑类别;
- 没人能解释为什么这段上下文会出现在答案里。
这些都不是“模型问题”,而是记忆层的架构问题。
12. 现在就该做什么¶
先过一遍这份短清单,把所有回答为“否”的地方单独记下来:
- 你是否真的理解短期记忆和长期记忆的区别?
- 画像记忆是否有独立语义,而不只是独立表?
- 每种记录类型的 TTL 能不能解释清楚?
- 哪一层记忆会直接进提示,哪一层只能通过检索,是否清楚?
- 长期记录是否有来源?
- 记录能不能被安全删除或修正?
如果这些问题都很难回答,那记忆架构值得被简化,并按职责重新拆开。
13. 下一步做什么¶
先把记忆类别和保留规则拆清楚,再进入检索、压缩和后台更新。
这一部分接下来的自然步骤,就是讨论智能体如何把正确片段拉回提示,以及为什么压缩有时候比“更多检索”更重要。