本章参考代码:
Ch7 的 /compact 解决的是同一会话越来越长的问题。Memory 解决的是另一件事:哪些信息值得跨会话保留。
本章实现一个最小但可用的文件系统记忆:
memory/
├── MEMORY.md # 自动生成的索引
├── preferences.md # 用户偏好
└── project.md # 项目事实
Agent 不会自动记住所有对话。它只在模型明确写入 memory/*.md 时保存长期信息,并通过 MEMORY.md 索引把可用记忆注入 system prompt。
每条记忆是一个 Markdown 文件,顶部带 frontmatter:
---
type: preference
summary: 用户喜欢中文回答
updated: 2026-07-02
---
回答时默认使用中文,除非用户明确要求英文。三个字段的含义:
| 字段 | 用途 |
|---|---|
type |
记忆类型,例如 preference、project、workflow |
summary |
写入 MEMORY.md 的短摘要 |
updated |
最后更新时间,方便判断信息是否过期 |
不要把短期任务状态、密钥、令牌、私人联系方式写进 memory。Memory 是长期上下文,不是临时草稿。
本章新增 memory.py,负责三件事。
第一,解析 frontmatter:
def parse_frontmatter(text: str) -> tuple[dict[str, str], str]:
if not text.startswith("---\n"):
return {}, text
end = text.find("\n---", 4)
if end == -1:
return {}, text
raw = text[4:end]
body = text[end + 4 :].lstrip("\n")
metadata = {}
for line in raw.splitlines():
if ":" in line:
key, value = line.split(":", 1)
metadata[key.strip()] = value.strip().strip("\"'")
return metadata, body第二,扫描 memory/*.md 并生成索引:
def refresh_memory_index() -> str:
MEMORY_DIR.mkdir(parents=True, exist_ok=True)
lines = ["# Memory Index", ""]
for path in sorted(MEMORY_DIR.glob("*.md")):
if path.name == "MEMORY.md":
continue
metadata, _ = parse_frontmatter(path.read_text(encoding="utf-8"))
memory_type = metadata.get("type", "note")
summary = metadata.get("summary", path.stem)
lines.append(f"- {memory_type}: {summary} (`{path.name}`)")
index = "\n".join(lines).rstrip() + "\n"
(MEMORY_DIR / "MEMORY.md").write_text(index, encoding="utf-8")
return index第三,生成 system prompt 片段:
def build_memory_prompt_section() -> str:
index = load_memory_index()
return (
"# Memory System\n"
"Use memory files only when they are relevant to the current task.\n"
"To save durable preferences or project facts, write Markdown files "
f"under `{MEMORY_DIR}` with frontmatter fields `type`, `summary`, "
"and `updated`.\n\n"
f"{index}"
)prompt.py 在模板末尾新增 {{memory}}:
from memory import build_memory_prompt_section
SYSTEM_PROMPT_TEMPLATE = """\
...
{{claude_md}}
{{memory}}"""构建 prompt 时替换这个占位符:
replacements = {
"{{memory}}": build_memory_prompt_section(),
}这样每次请求模型前,它都能看到当前 memory 索引,以及什么时候该保存长期信息。
如果模型用 write_file 写入 memory/*.md,索引应该立刻更新。否则本轮后续请求仍然看不到新记忆。
tools.py 在成功写入或编辑文件后调用:
def maybe_refresh_memory_index(file_path: str) -> None:
path = Path(file_path).resolve()
memory_dir = memory.MEMORY_DIR.resolve()
if path.suffix == ".md" and memory_dir in path.parents:
memory.refresh_memory_index()注意它只刷新 memory 目录下的 Markdown 文件。普通项目文件不应该触发 memory 索引重建。
agent.py 在 REPL 中新增一个辅助命令:
if user_input == "/memory":
print_info(load_memory_index())
continue它不调用模型,只把当前 MEMORY.md 打印出来,方便你确认长期记忆里到底有什么。
先运行 Chapter 8:
cd examples/chapter-08
python agent.py让模型保存一条偏好:
请记住:这个项目的交流默认使用中文。把它保存到 memory/preferences.md。
然后输入:
/memory
预期能看到类似内容:
# Memory Index
- preference: 这个项目的交流默认使用中文 (`preferences.md`) - updated 2026-07-02
退出后重新运行 python agent.py,system prompt 仍会加载 memory/MEMORY.md。这就是本章的跨会话记忆。
-
memory.py能解析 frontmatter。 -
refresh_memory_index()能生成memory/MEMORY.md。 -
build_memory_prompt_section()会把索引注入 system prompt。 -
write_file/edit_file修改 memory Markdown 后会刷新索引。 - REPL 支持
/memory。
| 省略内容 | 原因 | 后续落点 |
|---|---|---|
| 语义召回 | 需要额外模型调用和排序策略,本章先只做索引注入 | 进阶练习 |
| 后台记忆提取 | 需要 Sub-Agent 才自然 | Ch11 之后 |
| 多级 memory 目录 | 当前教程保持一个项目级 memory/ |
进阶练习 |
下一章:Skills — 把可复用工作流按需加载进 Agent。