2024从零打造AI Agent记忆功能新手必读实战指南
摘要
为AIAgent设计记忆系统,实现用户信息、反馈与项目上下文的持久化存储。通过MemoryManager管
1. 开篇导读
上一篇文章我们搭建了一个可运行的最小化 AI Agent,但有一个棘手的问题:每次重启程序,Agent 瞬间丢失所有上下文,完全不知道用户是谁、有什么偏好。

本文正是为了解决这个痛点——为 Agent 植入持久化记忆系统,使其能在多轮会话间持续保留用户画像与关键信息。
2. 记忆系统架构设计
2.1 核心功能需求
设计记忆系统前,必须先明确必须满足的四大条件:
- 持久化存储:程序关闭后重启,记忆不会丢失
- 分类管理:用户信息、项目上下文、反馈纠错,各归其位
- 安全操作:追加模式比覆盖模式更稳妥,避免数据意外被抹除
- 动态加载:每次对话自动读取最新记忆,无需手动介入
2.2 文件组织方式
文件如何布局?直接看目录结构一目了然:
.claude/memory/
├── user.md # 用户角色与偏好信息
├── feedback.md # 用户反馈与纠正记录
└── project.md # 项目上下文背景
2.3 文件格式选型
格式采用 frontmatter + Markdown 组合。既能保证人类可读性,又能承载结构化元数据:
---
name: user-role
description: 用户角色描述
metadata:
type: user
---# 用户角色- 职业:后端开发者
- 技能:Go 语言
- 语言偏好:中文
3. 核心实现细节
3.1 MemoryManager 类
记忆管理的核心是 MemoryManager 类。它负责操控 .claude/memory/ 目录下的全部记忆文件:
class MemoryManager:
"""
记忆管理器核心类
负责 .claude/memory/ 目录下记忆文件的读写追加
""" def __init__(self, memory_dir: str = ".claude/memory"):
self.memory_dir = Path(memory_dir)
self._ensure_memory_dir() def load(self, memory_type: str) -> str | None:
"""加载指定类型的记忆内容"""
path = self._get_memory_path(memory_type)
if not path.exists():
return None content = path.read_text(encoding='utf-8')
_, body = self._parse_frontmatter(content)
return body.strip() def append(self, memory_type: str, new_lines: str) -> bool:
"""追加新内容到现有记忆(保留原有记录)"""
path = self._get_memory_path(memory_type)
existing = path.read_text(encoding='utf-8')
metadata, body = self._parse_frontmatter(existing) # 拼接新内容到原有文本之后
if body.strip():
updated_body = f"{body.rstrip()}n{new_lines}"
else:
updated_body = new_lines return self.sa ve(memory_type, metadata.get("name", memory_type),
metadata.get("description", ""), updated_body)
3.2 Frontmatter 解析逻辑
Frontmatter 的解析并不复杂,利用正则匹配即可实现:
def _parse_frontmatter(self, content: str) -> tuple[dict, str]:
"""解析 frontmatter 元数据与正文"""
pattern = r'^---n(.*?)n---n(.*)$'
match = re.match(pattern, content, re.DOTALL) if match:
frontmatter_text = match.group(1)
body = match.group(2)
metadata = {} for line in frontmatter_text.split('n'):
if ':' in line:
key, value = line.split(':', 1)
metadata[key.strip()] = value.strip().strip('"') return metadata, body return {}, content
4. 工具集成方案
4.1 新增三个记忆工具
为了让 Agent 能够操控记忆系统,需要注册三个工具。它们分工明确:
| 工具 | 功能 | 使用建议 |
|---|---|---|
read_memory | 读取记忆 | 查询专用 |
write_memory | 覆盖记忆 | 谨慎使用,会丢失旧数据 |
append_memory | 追加记忆 | 首选方案 |
4.2 工具注册示例
工具注册代码简洁明晰,以 append_memory 为例:
# append_memory 工具(推荐使用)
registry.register(Tool(
name="append_memory",
description="将新内容追加到现有记忆(保留原有数据,推荐使用)",
parameters={
"type": {"type": "string", "description": "记忆类型: user, feedback, project"},
"content": {"type": "string", "description": "要追加的内容"},
},
handler=ToolHandlers.append_memory
))
4.3 工具实现逻辑
每个工具的 handler 实现也清晰直观。例如 append_memory 核心逻辑就是调用 MemoryManager 的 append 方法:
@staticmethod
def append_memory(params: dict) -> ToolResult:
"""追加内容到现有记忆(保留原有内容)"""
memory_type = params.get("type", "user")
new_lines = params.get("content", "") if not new_lines:
return ToolResult(success=False, error="追加内容不能为空") mm = MemoryManager()
success = mm.append(memory_type, new_lines) if success:
return ToolResult(success=True, content=f"内容已追加到 {memory_type} 记忆")
return ToolResult(success=False, error=f"追加失败: {memory_type}")
5. 上下文集成策略
5.1 动态加载记忆至系统提示
记忆系统要想真正发挥价值,必须无缝集成到 Agent 的对话上下文中。通过 ContextManager 实现动态加载:
class ContextManager:
def __init__(self, memory_dir: str = ".claude/memory"):
self.memory = MemoryManager(memory_dir)
self.base_system_prompt = """你是一个智能 AI Agent...
重要规则:
- 如果用户提供了重要的角色、偏好或反馈,使用 append_memory 工具追加到记忆中
""" def get_system_prompt(self) -> str:
"""获取系统提示(携带当前记忆)"""
memories = self.memory.load_all()
memory_section = format_memories_for_system_prompt(memories) if memory_section:
return f"{self.base_system_prompt}nn{memory_section}"
return self.base_system_prompt
5.2 记忆格式化处理
加载出的记忆需要做格式化,才能自然地嵌入到系统提示中:
def format_memories_for_system_prompt(memories: list[MemoryInfo]) -> str:
"""将记忆格式化为系统提示的一部分"""
if not memories:
return "" sections = ["## 记忆n"] for mem in memories:
section = f"### {mem['type'].capitalize()} 记忆 ({mem['name']})n"
if mem['description']:
section += f"{mem['description']}nn"
section += mem['content']
sections.append(section) return "nn".join(sections)
6. 踩坑与解决方案
6.1 问题:覆盖操作导致数据丢失
开发过程中踩了一个典型坑。先看现象:
用户: 我是后端开发者,擅长 Go 语言
用户: 我希望你用中文回复
结果记忆文件中只保留了“语言偏好:中文”,职业信息丢失。
根本原因很明确:write_memory 是覆盖模式,第二次调用直接抹掉了第一次的内容。
解决方案也很直接:
- 新增
append_memory追加模式 - 在系统提示中强调“不要覆盖,请使用追加”
- 将
append_memory标记为推荐工具
追加模式的实现代码:
# 追加模式实现
def append(self, memory_type: str, new_lines: str) -> bool:
existing = self.load(memory_type)
if existing:
new_content = f"{existing}n{new_lines}"
else:
new_content = new_lines
return self.sa ve(memory_type, new_content)
7. 实际运行效果
7.1 测试对话流程
来看看实际跑起来的效果:
用户: 我是后端开发者,擅长 Go 语言
Agent: 已将您的职业信息记录到记忆中。
[调用 append_memory(type="user", content="- 职业:后端开发者n- 技能:Go 语言")]用户: 我希望你用中文回复
Agent: 已记录您的语言偏好。
[调用 append_memory(type="user", content="- 语言偏好:中文")]用户: 再加一条,我喜欢用 Tmux
Agent: 已记录。
[调用 append_memory(type="user", content="- 工具偏好:Tmux")]
7.2 最终记忆文件
经过三轮对话,生成的记忆文件内容如下:
---
name: user-role
description: 用户角色描述
metadata:
type: user
---# 用户角色- 职业:后端开发者
- 技能:Go 语言
- 语言偏好:中文
- 工具偏好:Tmux
可见所有信息都被完整保留,且每一条都是通过追加写入的。
7.3 重启验证
最关键的一步——重启程序,验证它是否还能记住:
$ python main.py用户: 你知道我是做什么的吗?
Agent: 根据我的记忆,您是一位后端开发者,擅长 Go 语言。
您偏好使用中文交流,并且喜欢用 Tmux 作为终端工具。
成功了。即使程序重启,Agent 也能准确调取之前记录的所有信息。
8. 架构总览
最后,将整套系统的架构图展示出来,方便对照理解:
┌─────────────────────────────────────────────────────────┐
│ Agent │
│ (ReAct 核心循环) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │LLMClient │ │ToolRegistry │ │ContextManager │ │
│ │(LLM 交互) │ │(工具系统) │ │(上下文管理) │ │
│ └─────────────┘ └──────┬──────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ToolHandlers │ │ │
│ │+memory tools│ │ │
│ └─────────────┘ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ MemoryManager │ │
│ │ .claude/memory/*.md │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
围绕 ReAct 核心循环,ContextManager 从 MemoryManager 拉取记忆,并将其注入到系统提示中。ToolRegistry 则注册了三个记忆工具,供 Agent 在运行中随时读写。
9. 后续规划
记忆系统稳定运行后,接下来的里程碑是 Plan Mode——让 Agent 在执行复杂任务前,先学会制定和执行计划。这将显著提升它的自主决策能力与可靠性。
来源:互联网
本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。