从零打造 AI Agent (二)—— 让 AI 拥有记忆

1. 前言

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

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


2. 记忆系统设计

2.1 核心需求

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

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 是覆盖模式,第二次调用覆盖了第一次的内容。

解决

  1. 添加 append_memory 追加模式
  2. 系统提示强调"不要覆盖,使用追加"
  3. 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 从零到一系列文章。

相关推荐
PeterLi13 小时前
LangChain v1.x 最新官方完整教程(六大核心组件全解析+生产级代码示例)
langchain·agent
零壹AI实验室13 小时前
2026年5月AI编程工具横评:GPT-5.5、Claude Opus 4.7、Qwen3.7-Max谁最强
gpt·ai编程
十正13 小时前
Hermes记忆预取机制深度解析
python·ai·agent·hermes
JaydenAI13 小时前
[MAF预定义ChatClient中间件-04]ReducingChatClient——通过精减对话实施又不丢失基本语义
ai·c#·agent·maf·chatclient管道·对话历史压缩
Tech-Wang13 小时前
零基础AI编程之鸿蒙app开发
ai编程
财经资讯数据_灵砚智能13 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月27日
大数据·人工智能·python·信息可视化·自然语言处理·ai编程·灵砚智能
程序员柒叔13 小时前
Dify 一周动态-2026-W22
人工智能·大模型·github·agent·知识库·dify
kkkliaoo14 小时前
2026年AI编程Token消耗优化:从月费500到月费5的成本控制实战
人工智能·ai编程
Bigger14 小时前
mini-cc 的记忆引擎:让 AI 别再当金鱼了
前端·ai编程·claude