跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Working memory

消息历史和语义召回帮助 agent 记住对话,而工作记忆则让 agent 能够在多次交互之间持续维护关于用户的信息。

工作记忆是 agent 的活动便笺:它保存着关于用户或任务的关键信息,随时可用。它可以在对话期间记住一个人的姓名、偏好或其他重要细节。

这适用于维护始终相关、且应始终对 agent 可用的持续状态。

如果你使用 Observational Memory,observationalMemory.observation.manageWorkingMemory 可以让 OM 代为更新 agent 的工作记忆。

📹 观看视频

观看 Mastra working memory,了解 agent 如何在多次交互之间保持用户上下文可用。

工作记忆可以在两种不同的作用域下持久化:

  • Resource 作用域(默认):记忆在同一用户的所有对话 thread 之间持久化
  • Thread 作用域:记忆按对话 thread 隔离

注意: 在作用域之间切换意味着 agent 将看不到另一作用域的记忆 —— thread 作用域记忆与 resource 作用域记忆完全独立。

快速开始
快速开始的直接链接

以下是一个启用工作记忆的 agent 的最小示例:

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

// Create agent with working memory enabled
const agent = new Agent({
id: 'personal-assistant',
name: 'PersonalAssistant',
instructions: 'You are a helpful personal assistant.',
model: 'openai/gpt-5.6-sol',
memory: new Memory({
options: {
workingMemory: {
enabled: true,
},
},
}),
})

工作原理
工作原理的直接链接

工作记忆是一块 Markdown 文本,agent 可以随时间更新它,以存储持续相关的信息。

记忆持久化作用域
记忆持久化作用域的直接链接

工作记忆可以在两种不同的作用域下运行,让你选择记忆如何在对话之间持久化:

Resource 作用域记忆(默认)
Resource 作用域记忆(默认)的直接链接

默认情况下,工作记忆在同一用户(resourceId)的所有对话 thread 之间持久化,从而实现持久的用户记忆:

const memory = new Memory({
storage,
options: {
workingMemory: {
enabled: true,
scope: 'resource', // Memory persists across all user threads
template: `# User Profile
- **Name**:
- **Location**:
- **Interests**:
- **Preferences**:
- **Long-term Goals**:
`,
},
},
})

使用场景:

  • 记住用户偏好的个人助理
  • 维护客户上下文的客服机器人
  • 跟踪学生进度的教育应用

与 agents 一起使用
与 agents 一起使用的直接链接

使用 resource 作用域记忆时,请确保在 memory 选项中传入 resource 参数:

// Resource-scoped memory requires resource
const response = await agent.generate('Hello!', {
memory: {
thread: 'conversation-123',
resource: 'user-alice-456', // Same user across different threads
},
})

Thread 作用域记忆
Thread 作用域记忆的直接链接

Thread 作用域记忆将工作记忆隔离到各个对话 thread。每个 thread 维护自己独立的记忆:

const memory = new Memory({
storage,
options: {
workingMemory: {
enabled: true,
scope: 'thread', // Memory is isolated per thread
template: `# User Profile
- **Name**:
- **Interests**:
- **Current Goal**:
`,
},
},
})

使用场景:

  • 关于不同主题的不同对话
  • 临时或会话特定的信息
  • 每个 thread 都需要工作记忆,但 thread 是临时的且彼此不相关的工作流

存储适配器支持
存储适配器支持的直接链接

Resource 作用域工作记忆需要支持 mastra_resources 表的特定存储适配器:

支持的存储适配器
支持的存储适配器的直接链接

  • libSQL(@mastra/libsql)
  • PostgreSQL(@mastra/pg)
  • OracleDB(@mastra/oracledb)
  • Upstash(@mastra/upstash)
  • MongoDB(@mastra/mongodb)

自定义模板
自定义模板的直接链接

模板指导 agent 在工作记忆中跟踪和更新哪些信息。如果未提供模板,Mastra 会使用默认模板。为你的 agent 使用场景定义自定义模板,以便它记住最相关的信息。对于多个用户共享的 thread,请参阅多用户 thread。

以下是一个自定义模板示例。在这个示例中,一旦用户发送包含任何相关信息(姓名、位置、时区等)的消息,agent 就会存储这些内容:

const memory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `
# User Profile

## Personal info

- Name:
- Location:
- Timezone:

## Preferences

- Communication Style: [e.g., Formal, Casual]
- Project Goal:
- Key Deadlines:
- [Deadline 1]: [Date]
- [Deadline 2]: [Date]

## Session state

- Last Task Discussed:
- Open Questions:
- [Question 1]
- [Question 2]
`,
},
},
})

设计有效的模板
设计有效的模板的直接链接

结构良好的模板能让 agent 轻松解析和更新信息。把模板视为你希望助手持续维护的一份简短表单。

  • 标签简短、聚焦。 避免段落或过长的标题。保持标签简洁(例如 ## Personal Info 或 - Name:),这样更新内容更易读,也更不容易被截断。
  • 使用一致的大小写。 大小写不一致(Timezone: 与 timezone:)会导致 更新混乱。标题和列表标签应统一使用 Title Case 或小写。
  • 尽量减少占位文本。 使用 [e.g., Formal] 或 [Date] 等提示,帮助 LLM 填写正确的位置。
  • 缩写过长的值。 如果你只需要简短形式,可以加入类似 - Name: [First name or nickname] 或 - Address (short): 的指引,而不是完整的法定文本。
  • 在 instructions 中说明更新规则。 你可以直接在 agent 的 instructions 字段中 指示如何以及何时填写或清除模板的某些部分。

其他模板风格
其他模板风格的直接链接

如果你只需要少量条目,可以使用更短的单一区块:

const basicMemory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `User Facts:\n- Name:\n- Favorite Color:\n- Current Topic:`,
},
},
})

如果你更喜欢叙事风格,也可以用短段落格式存储关键事实:

const paragraphMemory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `Important Details:\n\nKeep a short paragraph capturing the user's important facts (name, main goal, current task).`,
},
},
})

结构化工作记忆
结构化工作记忆的直接链接

工作记忆也可以使用结构化 schema 来定义,而不是 Markdown 模板。可以是 Standard JSON Schema(Zod、Valibot、ArkType 等)。使用 schema 时,agent 会以符合 schema 的 JSON 对象形式查看和更新工作记忆。

注意: 必须指定 template 或 schema 之一,但不能同时指定两者。

示例:基于 schema 的工作记忆
示例:基于 schema 的工作记忆的直接链接

import { z } from 'zod'
import { Memory } from '@mastra/memory'

const userProfileSchema = z.object({
name: z.string().optional(),
location: z.string().optional(),
timezone: z.string().optional(),
preferences: z
.object({
communicationStyle: z.string().optional(),
projectGoal: z.string().optional(),
deadlines: z.array(z.string()).optional(),
})
.optional(),
})

const memory = new Memory({
options: {
workingMemory: {
enabled: true,
schema: userProfileSchema,
// template: ... (do not set)
},
},
})

提供 schema 后,agent 会以 JSON 对象形式接收工作记忆。例如:

{
"name": "Sam",
"location": "Berlin",
"timezone": "CET",
"preferences": {
"communicationStyle": "Formal",
"projectGoal": "Launch MVP",
"deadlines": ["2025-07-01"]
}
}

基于 schema 记忆的合并语义
基于 schema 记忆的合并语义的直接链接

基于 schema 的工作记忆使用合并语义,意味着 agent 只需包含想要添加或更新的字段。已有字段会自动保留。

  • 对象字段会深度合并: 只更新提供的字段,其他字段保持不变
  • 将字段设为 null 可删除它: 这会显式地从记忆中移除该字段
  • 数组会整体替换: 当提供数组字段时,它会替换现有数组(数组不会逐个元素合并)

并发更新
并发更新的直接链接

使用 scope: 'resource' 且存储适配器支持原子合并(目前为 @mastra/pg)时,每次合并都在行锁定的数据库事务中运行。来自不同进程的对不同字段的并发更新都会被保留。其他适配器在应用层执行合并,仅能在单个进程内串行化写入者。

如果需要从你自己的代码中合并部分更新,请调用 memory.mergeWorkingMemory():

await memory.mergeWorkingMemory({
threadId: 'thread-1',
resourceId: 'user-123',
workingMemory: { preferences: { tone: 'formal' } },
})

如果工作记忆不是基于 schema 且不是 resource 作用域,或者存储适配器不支持原子合并,它会抛出错误。如果需要降级处理,请先检查 await memory.supportsAtomicWorkingMemoryMerge()。

在模板和 schema 之间选择
在模板和 schema 之间选择的直接链接

  • 如果你希望 agent 以自由文本块的形式维护记忆(例如用户画像或便笺),请使用模板(Markdown)。模板使用替换语义:agent 每次更新都必须提供完整的记忆内容。
  • 如果需要结构化、类型安全、支持验证和程序化访问的 JSON 数据,请使用 schema。workingMemory.schema 字段接受任何兼容 PublicSchema 的 schema,例如 Zod v3 或 v4。JSON Schema 和已是标准格式的 schema 也受支持。当 agent 只提供要更新的字段时,合并语义会保留现有字段。
  • 同一时间只能启用一种模式:不支持同时设置 template 和 schema。

示例:多步保留
示例:多步保留的直接链接

以下是 User Profile 模板在一段简短用户对话中如何更新的简化视图:

# User Profile

## Personal info

- Name:
- Location:
- Timezone:

--- After user says "My name is **Sam** and I'm from **Berlin**" ---

# User Profile
- Name: Sam
- Location: Berlin
- Timezone:

--- After user adds "By the way I'm normally in **CET**" ---

# User Profile
- Name: Sam
- Location: Berlin
- Timezone: CET

现在,agent 可以在后续响应中引用 Sam 或 Berlin,而无需再次询问这些信息,因为它们已存储在工作记忆中。

如果你的 agent 未按预期更新工作记忆,可以在 agent 的 instructions 设置中添加系统指令,说明如何以及何时使用该模板。

设置初始工作记忆
设置初始工作记忆的直接链接

虽然 agent 通常通过 updateWorkingMemory 工具更新工作记忆,但你也可以在创建或更新 thread 时以编程方式设置初始工作记忆。这适用于注入希望 agent 无需在每次请求中传入即可使用的用户数据(例如姓名、偏好或其他信息)。

通过 thread 元数据设置工作记忆
通过 thread 元数据设置工作记忆的直接链接

创建 thread 时,可以通过元数据中的 workingMemory 键提供初始工作记忆:

src/app/medical-consultation.ts
// Create a thread with initial working memory
const thread = await memory.createThread({
threadId: 'thread-123',
resourceId: 'user-456',
title: 'Medical Consultation',
metadata: {
workingMemory: `# Patient Profile
- Name: John Doe
- Blood Type: O+
- Allergies: Penicillin
- Current Medications: None
- Medical History: Hypertension (controlled)
`,
},
})

// The agent will now have access to this information in all messages
await agent.generate("What's my blood type?", {
memory: {
thread: thread.id,
resource: 'user-456',
},
})
// Response: "Your blood type is O+."

以编程方式更新工作记忆
以编程方式更新工作记忆的直接链接

你也可以更新现有 thread 的工作记忆:

src/app/medical-consultation.ts
// Update thread metadata to add/modify working memory
await memory.updateThread({
id: 'thread-123',
title: thread.title,
metadata: {
...thread.metadata,
workingMemory: `# Patient Profile
- Name: John Doe
- Blood Type: O+
- Allergies: Penicillin, Ibuprofen // Updated
- Current Medications: Lisinopril 10mg daily // Added
- Medical History: Hypertension (controlled)
`,
},
})

直接更新记忆
直接更新记忆的直接链接

或者,直接使用 updateWorkingMemory 方法:

src/app/medical-consultation.ts
await memory.updateWorkingMemory({
threadId: 'thread-123',
resourceId: 'user-456', // Required for resource-scoped memory
workingMemory: 'Updated memory content...',
})

只读工作记忆
只读工作记忆的直接链接

在某些场景下,你可能希望 agent 能访问工作记忆数据但无法修改它。这适用于:

  • 路由 agent:需要上下文但不应更新用户画像
  • 多 agent 系统中的子 agent:应引用但不拥有该记忆

要启用只读模式,请在 memory 选项中设置 readOnly: true:

const response = await agent.generate('What do you know about me?', {
memory: {
thread: 'conversation-123',
resource: 'user-alice-456',
options: {
readOnly: true, // Working memory is provided but cannot be updated
},
},
})

选择启用状态信号(实验性)
选择启用状态信号(实验性)的直接链接

默认情况下,工作记忆作为系统消息的一部分传递给模型。你可以通过设置 useStateSignals: true,选择将其作为状态信号传递:

const memory = new Memory({
storage: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
options: {
workingMemory: {
enabled: true,
template: '# User\n- name:\n- location:',
useStateSignals: true, // experimental: deliver as state signal
},
},
})

变化之处:

  • 存储完全相同。 读取和写入的仍是相同的 resource/thread workingMemory 字段。
  • 工具形状相同,但以新名称暴露。 写入仍通过同一底层工具进行;在此路径下它注册为 setWorkingMemory(而不是 updateWorkingMemory)。重命名可避免旧的 strip 过滤器移除工具调用部分,使它们作为正常审计轨迹持久化,并让下一步模型调用自动获取新值。
  • 仅改变传递方式。 Memory 不再将其折叠进系统提示,而是自动附加一个 WorkingMemoryStateProcessor,将当前工作记忆作为 state 信号发出,其中 stateId: 'working-memory'。

你将继承标准状态信号的优势:thread 作用域的跟踪元数据、cacheKey 去重(相同快照只发出一次),以及当较旧快照滚出上下文窗口时通过 contextWindow.hasSnapshot 重新注入。

默认值(useStateSignals: false)保持现有的系统消息行为不变。模板工作记忆的 version: 'vnext' 不支持 useStateSignals。

示例
示例的直接链接