Skip to content

09. 长期记忆:让代理"记住"你

从"金鱼"到"老朋友"的进化

引言

普通的 AI 助手像一条"金鱼"——每次对话都是全新的开始,它不记得你是谁、你喜欢什么、你们之前聊过什么。

长期记忆让代理变成"老朋友":

  • 记住你的名字和偏好
  • 积累使用中学到的知识
  • 在新对话中应用过去的经验
  • 随着时间推移变得更了解你

从"金鱼"到"老朋友"——长期记忆让代理跨对话记住用户信息

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   对话 1                对话 2                对话 3        │
│  ┌───────┐            ┌───────┐            ┌───────┐       │
│  │ 你好  │            │ 天气?│            │ 写代码│       │
│  └───┬───┘            └───┬───┘            └───┬───┘       │
│      │                    │                    │           │
│      ▼                    ▼                    ▼           │
│  ┌───────────────────────────────────────────────────┐    │
│  │                   长期记忆                         │    │
│  │  • 用户:张三,前端工程师                          │    │
│  │  • 偏好:简洁回复,TypeScript                     │    │
│  │  • 项目:正在开发电商网站                         │    │
│  └───────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

实现原理

DeepAgents 通过 CompositeBackend + StoreBackend 实现长期记忆:

  1. 代理将记忆写入特定路径(如 /memories/
  2. CompositeBackend 将该路径路由到 StoreBackend
  3. StoreBackend 使用 LangGraph 的 BaseStore 跨线程持久化
  4. 新对话时,代理从持久化存储中读取记忆

CompositeBackend 路由机制——将 /memories/ 路径导向持久化存储

┌─────────────────────────────────────────────────────────────┐
│                     CompositeBackend                        │
│                                                             │
│   write_file("/memories/prefs.md")                          │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────────────────────────────────────┐      │
│   │        路由规则:/memories/* → StoreBackend      │      │
│   └─────────────────────────────────────────────────┘      │
│            │                                                │
│            ▼                                                │
│   ┌─────────────────────────────────────────────────┐      │
│   │              LangGraph Store                     │      │
│   │         (跨线程持久化存储)                        │      │
│   └─────────────────────────────────────────────────┘      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基础配置

步骤 1:创建 Store

typescript
import { InMemoryStore } from "@langchain/langgraph";

const store = new InMemoryStore();

注意InMemoryStore 适合开发测试,生产环境应使用 PostgresStore 等持久化实现。

步骤 2:配置 CompositeBackend

typescript
import { createDeepAgent } from "deepagents";
import { CompositeBackend, StateBackend, StoreBackend } from "deepagents/backends";

const agent = createDeepAgent({
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),  // 默认后端:临时存储
    {
      "/memories/": new StoreBackend(rt),  // 记忆路径 → 持久化
    }
  ),
  store: store,  // 必须提供!
});

步骤 3:指导代理使用记忆

typescript
const agent = createDeepAgent({
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    { "/memories/": new StoreBackend(rt) }
  ),
  store: store,
  systemPrompt: `你是一个具有长期记忆能力的助手。

## 记忆系统

你可以在 /memories/ 目录下保存和读取跨会话持久化的信息。

### 记忆文件
- /memories/user_profile.md - 用户基本信息
- /memories/preferences.md - 用户偏好设置
- /memories/notes.md - 重要笔记

### 工作流程

1. **对话开始时**:
   - ls("/memories/") 检查是否有现有记忆
   - read_file 加载相关记忆

2. **对话过程中**:
   - 发现用户新偏好 → 更新 preferences.md
   - 用户提到重要信息 → 更新 user_profile.md
   - 完成重要任务 → 记录到 notes.md

3. **记忆原则**:
   - 只保存真正有价值的信息
   - 避免重复存储相同内容
   - 定期整理和更新
`
});

三步配置长期记忆:创建 Store → 配置 CompositeBackend → 定义记忆协议

记忆类型

四种记忆类型:用户档案、用户偏好、项目笔记、历史摘要

1. 用户档案

存储用户的基本信息:

markdown
# 用户档案

## 基本信息
- 姓名:张三
- 职业:前端工程师
- 公司:ABC科技

## 技术背景
- 主要语言:TypeScript, JavaScript
- 框架:React, Next.js
- 工具:VS Code, Git

## 联系方式
- 时区:Asia/Shanghai

2. 用户偏好

存储用户的使用偏好:

markdown
# 用户偏好

## 回复风格
- 简洁明了
- 包含代码示例
- 使用中文

## 代码风格
- 函数式编程优先
- 使用 TypeScript 类型
- ESLint + Prettier

## 其他偏好
- 不喜欢长篇大论
- 喜欢用表格对比

3. 项目笔记

存储项目相关信息:

markdown
# 项目笔记

## 当前项目:电商网站

### 技术栈
- 前端:Next.js 14 + TypeScript
- 后端:Node.js + Prisma
- 数据库:PostgreSQL

### 进度
- [x] 用户认证模块
- [ ] 商品列表页
- [ ] 购物车功能

### 注意事项
- API 路径规范:/api/v1/...
- 组件命名:大驼峰

4. 历史摘要

存储对话历史的摘要:

markdown
# 对话历史

## 2024-01-15
- 帮助用户配置 ESLint
- 解决了 TypeScript 类型报错问题

## 2024-01-14
- 讨论了项目架构
- 推荐使用 Next.js App Router

记忆协议

为了让代理有效地使用记忆系统,建议在系统提示词中定义清晰的"记忆协议":

记忆协议双流程:启动时读取记忆 + 对话中更新记忆

启动检查协议

typescript
systemPrompt: `...

## 启动协议

每次对话开始时,执行以下步骤:

1. 检查记忆目录
   ls("/memories/")

2. 如果存在记忆,按优先级加载:
   - read_file("/memories/user_profile.md")  // 必读
   - read_file("/memories/preferences.md")   // 必读
   - read_file("/memories/current_project.md") // 如果讨论项目

3. 根据记忆调整行为
   - 使用用户偏好的回复风格
   - 引用相关的项目背景
   - 避免询问已知信息
`

记忆更新协议

typescript
systemPrompt: `...

## 记忆更新协议

在对话过程中,当发现以下情况时更新记忆:

### 触发条件
- 用户明确告知个人信息 → 更新 user_profile.md
- 用户表达偏好("我喜欢..."、"我不喜欢...") → 更新 preferences.md
- 完成重要任务 → 记录到 notes.md
- 对话有价值的结论 → 追加到 history.md

### 更新方式
- 使用 edit_file 进行精确修改
- 避免覆盖现有重要信息
- 添加时间戳便于追踪

### 示例
当用户说:"我喜欢简洁的回复"
→ edit_file("/memories/preferences.md", {
     old_string: "## 回复风格",
     new_string: "## 回复风格\\n- 简洁明了"
   })
`

完整示例

带记忆的个人助手

typescript
import { createDeepAgent } from "deepagents";
import { CompositeBackend, StateBackend, StoreBackend } from "deepagents/backends";
import { InMemoryStore, MemorySaver } from "@langchain/langgraph";
import { v4 as uuidv4 } from "uuid";

const store = new InMemoryStore();
const checkpointer = new MemorySaver();

const assistant = createDeepAgent({
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    { "/memories/": new StoreBackend(rt) }
  ),
  store,
  checkpointer,
  systemPrompt: `你是一个具有长期记忆能力的个人助手。

## 核心能力
你可以记住用户的信息、偏好和历史交互,在后续对话中提供个性化服务。

## 记忆系统

### 目录结构
- /memories/user_profile.md - 用户基本信息
- /memories/preferences.md - 用户偏好
- /memories/notes.md - 重要笔记

### 启动协议
1. ls("/memories/") 检查现有记忆
2. 如果有记忆,读取并加载
3. 根据记忆调整回复风格

### 记忆原则
- 只保存有价值的信息
- 用户明确告知的信息优先保存
- 推断的信息标记为 [推测]
- 定期整理,避免冗余

### 记忆格式
使用 Markdown 格式,包含:
- 清晰的分类标题
- 时间戳(必要时)
- 来源标注

## 行为准则
1. 不要重复询问已知信息
2. 主动应用用户偏好
3. 在合适时机引用历史交互
4. 保护用户隐私
`
});

async function chat(message: string, threadId: string) {
  const config = { configurable: { thread_id: threadId } };
  
  const result = await assistant.invoke(
    { messages: [{ role: "user", content: message }] },
    config
  );
  
  return result.messages[result.messages.length - 1].content;
}

async function main() {
  // 第一次对话:自我介绍
  const thread1 = uuidv4();
  console.log(await chat("你好,我是张三,我是一名前端工程师", thread1));
  // 代理会创建 /memories/user_profile.md 保存信息

  // 继续第一次对话:表达偏好
  console.log(await chat("我喜欢简洁的回复,代码用 TypeScript", thread1));
  // 代理会创建/更新 /memories/preferences.md

  // 新的对话(不同线程):测试记忆
  const thread2 = uuidv4();
  console.log(await chat("你还记得我是谁吗?", thread2));
  // 代理会读取 /memories/ 下的文件,识别用户身份

  // 测试偏好应用
  console.log(await chat("帮我写一个 Hello World", thread2));
  // 代理会根据偏好,使用 TypeScript 编写简洁代码
}

main();

跨线程记忆演示:Thread 1 学习用户信息,Thread 2 记住并应用偏好

预期交互

用户: 你好,我是张三,我是一名前端工程师
助手: 你好张三!很高兴认识你。我已经记下你是一名前端工程师了。
      有什么我可以帮你的吗?
      [内部操作: write_file("/memories/user_profile.md", ...)]

用户: 我喜欢简洁的回复,代码用 TypeScript
助手: 明白了,我会尽量简洁,代码示例用 TypeScript。
      [内部操作: write_file("/memories/preferences.md", ...)]

--- 新对话 ---

用户: 你还记得我是谁吗?
助手: 当然记得,张三!你是一名前端工程师,喜欢简洁的回复和 TypeScript。
      [内部操作: ls("/memories/"), read_file("/memories/user_profile.md"), ...]

用户: 帮我写一个 Hello World
助手: ```typescript
      console.log("Hello World");
      ```
      [根据偏好:简洁 + TypeScript]

记忆 vs 技能

记忆 vs 技能:来源、内容、可修改性和作用域的核心差异

特性记忆 (Memory)技能 (Skills)
来源代理从交互中学习开发者预定义
内容用户偏好、事实、历史操作指南、模板、知识
可修改✅ 代理可读写❌ 通常只读
作用域特定用户/线程全局共享
存储StoreBackend (持久化)文件系统或嵌入
加载时机对话开始时按需加载

何时用记忆 vs 技能

┌───────────────────────────────────────────────────────────┐
│                    信息类型                                │
│                                                           │
│   用户偏好  ──────────────────────────────→ 记忆          │
│   用户信息  ──────────────────────────────→ 记忆          │
│   项目约定  ──────────────────────────────→ 记忆          │
│   对话历史  ──────────────────────────────→ 记忆          │
│                                                           │
│   编程指南  ──────────────────────────────→ 技能          │
│   代码模板  ──────────────────────────────→ 技能          │
│   API 文档  ──────────────────────────────→ 技能          │
│   最佳实践  ──────────────────────────────→ 技能          │
│                                                           │
└───────────────────────────────────────────────────────────┘

生产环境配置

使用 PostgresStore

typescript
import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres";

const store = new PostgresStore({
  connectionString: process.env.DATABASE_URL
});

const agent = createDeepAgent({
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    { "/memories/": new StoreBackend(rt) }
  ),
  store,
});

部署到 LangSmith

部署到 LangSmith 时,Store 会自动配置:

typescript
const agent = createDeepAgent({
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    { "/memories/": new StoreBackend(rt) }
  ),
  // store 会自动配置,无需手动指定
});

最佳实践

1. 结构化记忆格式

使用一致的 Markdown 格式:

markdown
---
updated: 2024-01-15T10:30:00Z
confidence: high
source: user_stated
---

# 用户偏好

## 回复风格
- 简洁明了
- 包含代码示例

## 代码风格
- TypeScript
- 函数式编程

2. 记忆分类

最佳实践:记忆文件目录结构与四大实践要点

将不同类型的记忆存储在不同文件:

/memories/
├── user_profile.md    # 用户基本信息
├── preferences.md     # 用户偏好
├── projects/          # 项目相关
│   ├── project_a.md
│   └── project_b.md
├── notes.md           # 通用笔记
└── history.md         # 对话历史摘要

3. 定期整理

指导代理定期整理记忆:

typescript
systemPrompt: `...

## 记忆维护

### 整理触发条件
- 记忆文件超过 2000 字
- 发现重复或过时信息
- 用户明确要求整理

### 整理操作
1. 读取现有记忆
2. 删除过时信息
3. 合并重复内容
4. 重新组织结构
`

4. 隐私保护

typescript
systemPrompt: `...

## 隐私原则
- 不保存密码、密钥等敏感信息
- 不保存用户明确要求不记录的内容
- 敏感信息只保存在当前会话
`

小结

本文介绍了 DeepAgents 长期记忆的实现原理和使用方法:

概念说明
CompositeBackend路径路由,将 /memories/ 导向持久化存储
StoreBackend使用 LangGraph Store 跨线程持久化
记忆协议指导代理何时读取/更新记忆
记忆类型用户档案、偏好、笔记、历史

关键步骤

  1. 创建 Store(InMemoryStore 或 PostgresStore)
  2. 配置 CompositeBackend 路由
  3. 在 systemPrompt 中定义记忆协议
  4. 让代理在对话中读取和更新记忆

与技能的区别

  • 记忆 = 代理学习的个人信息
  • 技能 = 开发者预定义的专业知识

下一步

在下一篇文章中,我们将学习技能系统——如何为代理添加可复用的专业能力。

实践任务

  1. 创建一个带长期记忆的助手,让它能记住你的名字和偏好
  2. 测试跨线程记忆:在不同对话中验证记忆是否保留
  3. 设计一个记忆协议,定义什么信息应该保存、什么不应该

参考资源

读文档、看源码、写代码,理解 AI Agent 本质 🤖