1. 前言
上篇文章我们实现了一个最小可运行的 AI Agent,但存在一个问题:每次重启后,Agent 就像失忆了一样,不记得你是谁、你的偏好是什么。
本文将实现一个记忆系统,让 Agent 能够跨会话记住重要信息。
2. 记忆系统设计
2.1 核心需求
- 持久化存储:退出后再次启动,记忆仍然存在
- 分类管理:用户信息、项目上下文、反馈纠正分开放
- 安全操作:追加模式优于覆盖,避免数据丢失
- 动态加载:每次对话自动读取最新记忆
2.2 文件结构
bash
.claude/memory/
├── user.md # 用户角色与偏好
├── feedback.md # 用户反馈与纠正
└── project.md # 项目上下文
2.3 文件格式
使用 frontmatter + Markdown 格式,兼顾可读性和结构化:
markdown
---
name: user-role
description: 用户角色描述
metadata:
type: user
---
# 用户角色
- 职业:后端开发者
- 技能:Go 语言
- 语言偏好:中文
3. 核心实现
3.1 MemoryManager 类
python
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.save(memory_type, metadata.get("name", memory_type),
metadata.get("description", ""), updated_body)
3.2 Frontmatter 解析
python
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 新增三个记忆工具
| 工具 | 功能 | 使用建议 |
|---|---|---|
read_memory |
读取记忆 | 查询用 |
write_memory |
覆盖记忆 | 慎用,会丢数据 |
append_memory |
追加记忆 | 推荐使用 |
4.2 工具注册
python
# 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 工具实现
python
@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 动态加载记忆到系统提示
python
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}\n\n{memory_section}"
return self.base_system_prompt
5.2 记忆格式化
python
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']}\n\n"
section += mem['content']
sections.append(section)
return "\n\n".join(sections)
6. 踩坑记录
6.1 问题:覆盖导致数据丢失
现象:
makefile
用户: 我是后端开发者,擅长 Go 语言
用户: 我希望你用中文回复
最终只保留了"语言偏好:中文",职业信息丢失了。
原因 :使用 write_memory 是覆盖模式,第二次调用覆盖了第一次的内容。
解决:
- 添加
append_memory追加模式 - 系统提示强调"不要覆盖,使用追加"
- 将
append_memory标记为推荐工具
python
# 追加模式实现
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.save(memory_type, new_content)
7. 使用效果
7.1 测试对话
makefile
用户: 我是后端开发者,擅长 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 最终记忆文件
markdown
---
name: user-role
description: 用户角色描述
metadata:
type: user
---
# 用户角色
- 职业:后端开发者
- 技能:Go 语言
- 语言偏好:中文
- 工具偏好:Tmux
7.3 重启后验证
bash
$ python main.py
用户: 你知道我是做什么的吗?
Agent: 根据我的记忆,您是一位后端开发者,擅长 Go 语言。
您偏好使用中文交流,并且喜欢用 Tmux 作为终端工具。
8. 架构总结
scss
┌─────────────────────────────────────────────────────────┐
│ Agent │
│ (ReAct 核心循环) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │LLMClient │ │ToolRegistry │ │ContextManager │ │
│ │(LLM 交互) │ │(工具系统) │ │(上下文管理) │ │
│ └─────────────┘ └──────┬──────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ToolHandlers │ │ │
│ │+memory tools│ │ │
│ └─────────────┘ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ MemoryManager │ │
│ │ .claude/memory/*.md │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
9. 下一步
下一个里程碑我们将实现 Plan Mode,让 Agent 在执行复杂任务前先制定计划。
如果对你有帮助,欢迎点赞、评论、转发。 关注我,持续更新 AI Agent 从零到一系列文章。