1. 前言

上篇文章我们实现了一个最小可运行的 AI Agent,但存在一个问题:每次重启后,Agent 就像失忆了一样,不记得你是谁、你的偏好是什么。

本文将实现一个记忆系统,让 Agent 能够跨会话记住重要信息。


2. 记忆系统设计

2.1 核心需求

  1. 持久化存储:退出后再次启动,记忆仍然存在
  2. 分类管理:用户信息、项目上下文、反馈纠正分开放
  3. 安全操作:追加模式优于覆盖,避免数据丢失
  4. 动态加载:每次对话自动读取最新记忆

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 类

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 解析

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 工具注册

# 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 工具实现

@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 动态加载记忆到系统提示

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 记忆格式化

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 问题:覆盖导致数据丢失

现象

用户: 我是后端开发者,擅长 Go 语言
用户: 我希望你用中文回复

最终只保留了"语言偏好:中文",职业信息丢失了。

原因:使用 write_memory 是覆盖模式,第二次调用覆盖了第一次的内容。

解决

  1. 添加 append_memory 追加模式
  2. 系统提示强调"不要覆盖,使用追加"
  3. 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.save(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 作为终端工具。

8. 架构总结

┌─────────────────────────────────────────────────────────┐
│                      Agent                              │
│                   (ReAct 核心循环)                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │LLMClient    │  │ToolRegistry │  │ContextManager   │  │
│  │(LLM 交互)   │  │(工具系统)   │  │(上下文管理)     │  │
│  └─────────────┘  └──────┬──────┘  └────────┬────────┘  │
│                          │                  │           │
│                          ▼                  │           │
│                   ┌─────────────┐           │           │
│                   │ToolHandlers │           │           │
│                   │+memory tools│           │           │
│                   └─────────────┘           │           │
│                                             ▼           │
│                    ┌─────────────────────────────────┐ │
│                    │         MemoryManager          │ │
│                    │   .claude/memory/*.md          │ │
│                    └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

9. 下一步

下一个里程碑我们将实现 Plan Mode,让 Agent 在执行复杂任务前先制定计划。


如果对你有帮助,欢迎点赞、评论、转发。 关注我,持续更新 AI Agent 从零到一系列文章。