Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第九章:记忆与任务——AI 的跨会话笔记本

📌 本章李明轩在做什么:付费社群每周要发一篇周报,要引用本周社群里讨论过的话题。一周横跨 7 天,他不可能让 AI 在一次会话里搞定。他需要让 AI 把"本周已经引用过哪些话题"、"会员最常问的问题"这些经验记到磁盘上,下次会话自动读回来。

为什么 AI 需要记忆

在第三章里,我们提到了"新鲜上下文即可靠性"原则——每次迭代 AI 都从零开始读取信息。这引出了一个矛盾:

  • 新鲜上下文意味着每次 AI 都要重新理解环境
  • 但重要的经验和决策不应该在每次迭代后消失

Ralph 通过两套持久化机制解决这个矛盾:

  1. 记忆文件(memories.md):AI 跨会话的"学习笔记"
  2. 任务系统(tasks.jsonl):工作进度的"代办清单"
📅 会话 1(周一晚) 上下文窗口(新鲜) 装入本轮任务上下文…… 会话结束 → 上下文清空 💨 窗口里的东西全没了 📅 会话 2(周二晚) 上下文窗口(新鲜) 装入本轮任务上下文…… 会话结束 → 上下文清空 📅 会话 3(周三晚) 上下文窗口(新鲜) 装入本轮任务上下文…… 会话结束 → 上下文清空 💾 磁盘(永久保存) 📝 .ralph/agent/memories.md 项目模式 / 已修复的坑 / 重要决策 📋 .ralph/agent/tasks.jsonl 待办 / 进行中 / 已完成的任务 写入经验 读回经验

一句话上下文窗口每次都清空(保证新鲜),磁盘每次都保留(保证不遗忘)——两者配合才能既避免老对话的遗忘,又积累项目经验。


记忆文件:AI 的学习笔记本

记忆文件位于 .ralph/agent/memories.md,是一个普通的 Markdown 文本文件,由 AI 自己维护。

记忆的内容

AI 会把以下类型的信息写入记忆:

项目模式(Patterns)

## Project Patterns
- 这个项目使用 pytest 进行测试,测试文件放在 tests/ 目录
- 数据库查询使用 SQLAlchemy ORM,不直接写 SQL
- API 响应格式:{"status": "success|error", "data": {...}, "message": "..."}
- 所有新功能需要在 CHANGELOG.md 里添加条目

已修复的问题(Fixes)

## Fixes Applied
- 导入 requests 库时,需要先激活虚拟环境:source .venv/bin/activate
- pytest-asyncio 版本问题:在 pyproject.toml 里固定到 0.21.x
- 数据库迁移前必须先运行 alembic upgrade head

重要决策(Decisions)

## Decisions
- 选择 FastAPI 而不是 Flask,原因:需要原生 async 支持
- 用户认证使用 JWT,不用 session,原因:支持移动端
- 配置文件使用环境变量,不硬编码,原因:安全考虑

重复出现的错误(Recurring Issues)

## Recurring Issues
- 在这个项目里,datetime 对象必须带时区信息,否则比较会报错
- API 测试时记得先启动 mock 服务器:make mock-server

记忆是如何被使用的

每次迭代开始时,Ralph 会把 memories.md 的内容注入到 AI 的上下文里,放在一个特殊的 <memories> 标签里。AI 在做决策时会参考这些记忆,避免重复犯同样的错误。

手动添加记忆

不只是 AI,你也可以手动向记忆文件添加内容:

# 用命令行添加记忆
ralph tools memory add "注意:这个项目的 API 密钥不能出现在任何测试文件里"

# 或者直接编辑文件
vim .ralph/agent/memories.md

这是"用信号导航"法则的直接体现:当 AI 反复在某个地方犯错时,把正确的做法写入记忆,比在帽子指令里写复杂规则更有效。

记忆的清理

随着项目进行,记忆文件会越来越长。记忆文件是普通的 Markdown 文本,直接用编辑器打开删除不再相关的条目即可:

# 直接编辑记忆文件,删除过时的条目
vim .ralph/agent/memories.md

如果想清理整个 .ralph/ 工作目录(例如重新开始一个项目),可以使用:

# 预览会删除什么(不实际删除)
ralph clean --dry-run

# 清理诊断日志
ralph clean --diagnostics

注意:ralph clean 只会清理 .ralph/agent/ 下的记忆与任务文件;.ralph/ 下的事件日志(events-*.jsonl)、循环注册表(loops.json)、锁文件(loop.lock)等不会被它动到,更不会删除你的项目代码。


任务系统:进度跟踪的代办清单

任务系统通过 .ralph/agent/tasks.jsonl 文件跟踪所有工作的进度。

任务的生命周期

pending(待开始)
    ↓
in_progress(进行中)
    ↓
closed(完成)
    或
reopened(重新打开,需要修改)

任务文件的结构

每行一个任务,完整字段示例(真实输出,略作格式化):

{"id":"task-1705300801-a3f2","title":"创建项目骨架","description":"scaffold the repo","key":"code-assist:my-project:step-01:scaffold","status":"closed","priority":3,"blocked_by":[],"loop_id":"primary-20240115-100001","created":"2024-01-15T10:00:01+00:00","started":"2024-01-15T10:00:05+00:00","closed":"2024-01-15T10:12:44+00:00"}
{"id":"task-1705301453-7c01","title":"实现核心功能","description":"core logic","key":"code-assist:my-project:step-02:core","status":"open","priority":3,"blocked_by":[],"loop_id":"primary-20240115-100001","created":"2024-01-15T10:10:53+00:00","started":"2024-01-15T10:11:39+00:00"}
{"id":"task-1705302021-bb44","title":"完善测试","description":"write tests","key":"code-assist:my-project:step-03:tests","status":"open","priority":3,"blocked_by":["task-1705301453-7c01"],"created":"2024-01-15T10:20:21+00:00"}

任务 ID 格式是 task-<Unix 时间戳>-<4 位 16 进制>,由 Ralph 运行时生成;后文示例里出现 task-1705301453-7c01 这类 ID,都是占位符,实际使用时请用 ralph tools task list 看到的真实 ID 替换。

查看任务状态

运行时任务通过 ralph tools task 子命令管理(注意:ralph task 是生成 .code-task.md 文件的命令,功能不同):

# 列出所有任务
ralph tools task list

# 只列出待处理的任务
ralph tools task ready

# 查看某个任务的详细信息(把示例 ID 换成你本地的真实 ID)
ralph tools task show task-1705301453-7c01 --format json

任务在恢复中的作用

假设你的电脑在任务运行到一半时断电了。重新启动后:

# 重新启动 Ralph
ralph run -p "继续上次的任务"

Ralph 会读取任务文件,知道哪些已经完成、哪些正在进行,从断点处继续,而不是从头开始。


记忆与任务的协作:一个完整的例子

让我们看看在一个多天的项目里,记忆和任务如何协同工作:

第一天

你:ralph run -p "为这个 Python 项目添加邮件发送功能"

Ralph 运行了 5 次迭代:
- 任务1:安装 smtplib 配置 (完成)
- 任务2:实现发送函数 (完成)  ← 在这里遇到了 SSL 证书问题,
- 任务3:添加重试机制 (完成)    AI 记录到记忆里
- 任务4:编写测试 (完成)
- 背压检查:全部通过
- 循环完成

.ralph/agent/memories.md 现在包含:

## Fixes Applied
- 发送邮件时需要设置 ssl_context=ssl.create_default_context(),
  否则在某些服务器上会有 SSL 错误
- SMTP 测试使用 unittest.mock.patch('smtplib.SMTP') 模拟,
  不要真正发送邮件

第二天(新的会话)

你:ralph run -p "添加邮件发送的队列功能,异步处理"

Ralph 在新的迭代里,自动读取了第一天的记忆:
- 知道测试要用 mock,不会真的发邮件
- 知道 SSL 的处理方式,不会再踩同样的坑

代码任务文件(.code-task.md)

除了运行时任务(存在 .jsonl 文件里),Ralph 还支持另一种任务形式:代码任务文件.code-task.md)。

这是一个 Markdown 格式的任务规格文档,适合用于较复杂的功能:

---
status: pending
created: 2024-01-15
started: null
completed: null
---
# 任务:添加邮件验证功能

## 描述
实现一个邮件地址验证函数,用于用户注册时的输入验证。

## 背景
目前注册接口不验证邮件格式,导致脏数据进入数据库。

## 技术要求
1. 验证 RFC 5322 标准格式
2. 检查域名是否存在(可选,可关闭)
3. 返回详细的错误信息(不只是"格式错误")

## 验收标准

1. **基本格式验证**
   - 给定:一个标准格式的邮件地址
   - 当:调用 validate_email() 函数
   - 则:返回 True

2. **无效格式拒绝**
   - 给定:各种格式错误的邮件地址
   - 当:调用 validate_email() 函数
   - 则:返回 False 和具体的错误说明

3. **测试覆盖**
   - 给定:完整的测试套件
   - 当:运行 pytest
   - 则:所有测试通过,覆盖率 > 90%

把这个文件放在 .ralph/tasks/ 目录里,然后运行:

ralph run -H builtin:code-assist \
  --prompt ".ralph/tasks/validate-email.code-task.md"

Ralph 会按照这个文档里的验收标准来判断任务是否完成。


任务的手动管理

在某些情况下,你可能需要手动管理任务状态:

# 创建/更新一个任务(ensure:存在则更新,不存在则创建)
# 语法:ralph tools task ensure --key <KEY> <TITLE> [--description <DESCRIPTION>]
ralph tools task ensure --key "fix:login-css" "修复登录页 CSS" \
  --description "修复登录页面的 CSS 问题"

# 以下示例里的 task-id 形如 task-1776737176-532e(Ralph 生成),
# 复制你在 `ralph tools task list` 看到的完整 ID 替换即可
# 手动关闭一个任务
ralph tools task close task-1776737176-532e

# 重新打开一个已关闭的任务(发现了新问题)
ralph tools task reopen task-1776737176-532e

# 查看任务详情
ralph tools task show task-1776737176-532e

什么不应该存入记忆

记忆文件的价值在于跨会话的持久学习,以下内容不适合放入记忆:

  • 临时的调试信息正在调试 issue #123(这是当前会话的临时状态)
  • 已经修复并提交的 bug:这些信息在代码和 commit message 里已经有了
  • 可以从代码库里直接读取的信息:比如"使用 Python 3.11"(代码里有 pyproject.toml)

记忆里应该存的是那些不写下来 AI 就容易忘记或弄错的东西:隐性约束、踩过的坑、反常识的决策。


本章小结

  • memories.md 是 AI 的跨会话学习笔记,自动积累项目经验
  • tasks.jsonl 是工作进度的代办清单,支持跨会话恢复
  • .code-task.md 文件提供结构化的任务规格,适合复杂功能
  • 可以手动向记忆添加内容,作为"信号"指导 AI 行为
  • 记忆越精准有用,AI 在这个项目里的工作质量越高

记忆和任务系统让 AI 的工作可以跨夜延续。但李明轩还有个现实问题——白天上班时他没法打开终端看循环跑到哪里了。他需要一个手机上能看的面板。下一章讲可视化仪表盘。