跳到主要内容
返回

Agent memory 的四个层级

AI

Conversation history、working memory、semantic recall 与 observational memory — Mastra 真正记住事情的方式

LLM 会忘。把最近 N 条 messages 塞进每次 request,十轮还能撑。遇到 tool dumps、新 thread,或任何停在第一条 message 的 goal,就会崩。

这是 Mastra 的四级阶梯,按他们推出的顺序。Alex Becker 在 Four levels of agent memory 里走的是同一套 stack。这不是 CoALA taxonomy(working / episodic / semantic / procedural)。那些名字描述的是知识种类。这四个层级描述的是tokens 住在哪里,以及你为留下它们付了什么代价。

所有层级都需要 storage。不带 options 的 new Memory() 已经是第一级。其余都是同一个 object 上的 flags。



1. Conversation history

Thread 是一次 conversation。Resource 是 owner:一个 user、一个 org、一个 project。Studio 会两边都生成。自己调用 generate 或 stream 时,你要传进去。

Mastra 从 storage 加载最近 N 条 messages,放进 context window。默认 lastMessages 是 10。


ts
import { Agent } from "@mastra/core/agent"
import { Memory } from "@mastra/memory"

export const agent = new Agent({
  id: "chat-agent",
  name: "Chat agent",
  instructions: "You are a helpful assistant.",
  model: "openai/gpt-5-mini",
  memory: new Memory({
    options: {
      lastMessages: 10,
    },
  }),
})

  • 从 client 只送新 message。Mastra 已经有 thread。把完整 history 再寄一遍是多余的,而且 client timestamps 会跟 store 里的 messages 抢顺序。
  • Window 是滑动切割。若用户的 goal 在第一条 message,接着烧掉 30 轮 tool turns,goal 就没了。
  • 新 thread 没有 history。同一个 agent、同一个人、空的 context。那是 session cookie,不是 intelligence。
  • Tool results 会让切割来得比你预期更早。一次 llms.txt fetch 就可以是 10k tokens。

Failure: 把 history 当成一套 memory 系统。它对短聊天是正确的默认。



2. Working memory

Working memory 是一块 scratchpad,model 每一轮都能看见——即使几百条 messages 之后,即使换了新 thread(只要 scope 是 resource)。

你给它空字段。Agent 用 updateWorkingMemory 填进去。填好的 block 注入 system instruction,用户看不见。


ts
memory: new Memory({
  options: {
    workingMemory: {
      enabled: true,
      scope: "resource",
      template: `# User Profile
- **Name**:
- **Preferences**:
- **Current Goal**:
`,
    },
  },
})

  • 它补充 history。它不取代 history。用来放不变的 facts:名字、preferences、当前 goal。Coding agents 和任何 long-horizon task 都活在这里。
  • scope: "resource" 是默认:每个 user 跨 threads 共用一块 scratchpad。scope: "thread" 把它隔离到这次 conversation。切换 scopes 不会迁移数据。
  • 可以用 Zod schema,而不是 Markdown template。不能两个都用。Templates 每次 update 会替换整块。Schemas 会 merge:只送改过的 fields;把 field 设成 null 来删除。
  • Working memory 故意很小。你必须预先定义 fields。当 observational memory 打开时,observationalMemory.observation.manageWorkingMemory 让 Observer 写 scratchpad,主 agent 就不必记得那个 tool。

Failure: 把 conversation summaries 塞进 template,或把它养成 event log。那是第四级。



3. Semantic recall

Semantic recall 是针对 message history 的 RAG,不是针对你的 product corpus。

每条新 message 都会被 embed,之后的 messages 会查询那个 vector store 找相似 turns。「I like dogs, mine is called Nas Barkley。」后来,在另一个 thread:「what animals do I like?」Lookup 按 meaning。Mastra 把 hits 注入成额外的 system block。


ts
memory: new Memory({
  storage: new LibSQLStore({ id: "agent-storage", url: "file:./local.db" }),
  vector: new LibSQLVector({ id: "agent-vector", url: "file:./local.db" }),
  embedder: new ModelRouterEmbeddingModel("openai/text-embedding-3-small"),
  options: {
    semanticRecall: { topK: 3, messageRange: 2, scope: "resource" },
  },
})

  • topK 是命中数量。messageRange 是每个 hit 要一并拉回的周围 turns。太多,model 会淹死。太少,你会错过那个 fact。
  • scope: "resource" 搜索该 user 的所有 threads;LibSQL、Postgres、MongoDB、OracleDB 与 Upstash 支持它。
  • 默认关闭。它需要 vector store 与 embedder。Write 与 query 用同一个 embedding model,规则跟 document RAG 一样。
  • Recall 不精确——你得按产品调 topK 与 messageRange。你现在要跑 embedder 与 vector store。每一轮都有 latency。
  • 如何搭建一套 RAG 系统 里的 org-scoped document index 是 agent 用 tool 搜索的 library。Message RAG 是「我们说过什么」。不同 indexes。不同 tenancy。不同 tools。

Failure: 注入的 system block 会随 query 改变。Prompt prefix 永远不够稳定,打不中 cache。在 production,cached input tokens 通常是最大的节省。Semantic recall 把它们花掉。



4. Observational memory

Observational memory 建模的是人如何记得,以及如何忘记。两个 ambient agents:Observer 与 Reflector。它们一直在。它们不总是在跑。

当 message tokens 越过阈值(默认 30,000;demos 常用 2k 好让你看着它发生),Observer 把 raw history 压成一份 dense observation log:priority markers、timestamps,以及仍然重要的那一丝。一次 10k-token 的 tool result 可以变成约 160 tokens。

当 observation log 本身越过它的阈值(默认 40,000),Reflector 重写整份 log。它丢掉低优先级行、合并相关 facts,并让 window 保持有界。Reflections 不会叠成第三层无限层。每次 reflection 就是 新的 log。新的 observations 接在它后面。


text
Recent messages → Agent context
Recent messages
  -(crosses messageTokens)→ Observer → Observation log → Agent context
Observation log
  -(crosses observationTokens)→ Reflector → Observation log

  • Observations 是 stable 的。它们 append。它们不会每一轮都重排 system prefix——这是对 semantic recall 那个 cache 论点的反转。
  • Observer 与 Reflector 在背景跑。在 Studio 里你可以点它们做 demo。在真正的 agent 里它们是 async、non-blocking。Coding harness 里的 compaction 常常让用户停一分钟,还丢掉错误的细节。这个 loop 两边都不该做。

ts
memory: new Memory({
  storage: new LibSQLStore({ id: "memory-storage", url: "file:./memory.db" }),
  options: {
    observationalMemory: { model: "google/gemini-2.5-flash" },
  },
})

  • observationalMemory: true 把 Observer/Reflector model 默认成 google/gemini-2.5-flash。Storage 是必需的。目前支持的 adapters:@mastra/pg、@mastra/libsql、@mastra/mysql、@mastra/mongodb、@mastra/convex、@mastra/oracledb。
  • 影片里 Mastra 的 LongMemEval 数字,前三行用同一个 model:working memory 约 55%(不是为这个 benchmark 设计的)、semantic recall 约 80%、observational memory 约 84%。Gemini 2.5 Flash 在 OM 上被报成约 95%。把这些当成 Mastra 公布的分数,不是独立 bake-off。
  • 这是能撑过 noisy tool calls 的层级:page snapshots、llms.txt、MCP dumps。它也会累积——一个连续几周写活动文案的 workshop-helper,会把「second person, short hook, no hype」留成 observations,而不是你记得放进 template 的一个 field。
  • retrieval: true 给 agent 一个 recall tool,可以回到产生 observation 的原始 messages。{ vector: true } 再给那个 store 加上 semantic search。Compression 不必等于原文消失。
  • observation.manageWorkingMemory 让 OM 接管 scratchpad。Working memory 保持小而 structured;OM 让主 agent 不必为此花一次 tool call。


5. Which level


LevelPrimitive适用场景何时失效
Conversation historylastMessages短 threads、UI transcriptGoal 滑出 window;新 thread
Working memoryworkingMemory稳定 facts 与当前 goal你需要 event log 或未声明的 fields
Semantic recallsemanticRecall跨很长、多 thread history 的稀疏 facts你需要 prompt cache,或 recall 太模糊
Observational memoryobservationalMemoryLong horizon、noisy tools、cache-stable context你拒绝跑 storage adapter

  • Mastra 目前对 long-context agents 的建议是 observational memory。前面几级仍然存在,仍然可以组合。
  • History 是 model 此刻看见的。Working memory 是表单。Semantic recall 是搜索。Observational memory 是让 window 保持小、又不至于空白的方式。
  • 不带 options 的 new Memory() 就是 conversation history。这个 repo 里的 weather agent 就是这样。当 thread 不再只是聊天时,再升级。


6. Invariants

  1. 通过 storage adapter 持久化。Memory 不是 context window。
  2. Client 送新 message。Server 加载 thread。绝不要两边都做。
  3. thread 是 conversation。resource 是 owner。跨 thread recall 是一次 resource query,不是缺了 threadId。
  4. Working memory 是一块小的、始终在线的 block。不要把它养成日记。
  5. 针对 messages 的 semantic recall 不是 document RAG。不同 index,不同 tenant 故事。两边 write 与 query 都用同一个 embedding model。
  6. Observational memory 靠 append observations 保持可 cache 的 prefix。Semantic recall 是故意打爆那个 prefix。
  7. Isolation 是 server 的事。resource 不是 model 可以自己挑的。

四个层级。同一个 Memory object。精妙之处在于你留下哪些 tokens,以及你愿意忘掉哪些。


Recap Q&A

阅读下一篇笔记
OAuth 2.0 与 OIDC 解释