第19部分:基于SQLite的智能体记忆
SDD将项目意图存储在仓库中:QWEN.md、AGENTS.md、specs/、CHANGELOG.md。但智能体开发还有另一个记忆层:会话中发生了什么、哪些错误反复出现、用户确认了哪些偏好、哪些命令生效了、哪些决策需要智能体从上下文中推断。
Qwen Code已内置记忆:QWEN.md、自动记忆、/remember、/forget、/dream。对于大多数项目,这已经足够。但如果你想自己掌控记忆、查看审计日志、在不同运行环境之间迁移记忆、并使用自己的清理规则,可以基于SQLite构建本地记忆层。
现代智能体记忆方法的核心思想很简单:
flowchart TD
A["Qwen Code事件<br/>请求、工具、结果"] --> B["钩子<br/>log_event.py"]
B --> C[("SQLite<br/>events表")]
C --> D["后台归纳<br/>/dream 或 dream_sqlite.py"]
D --> E[("SQLite<br/>memories表")]
E --> F["添加上下文<br/>inject_memory.py"]
F --> G["新会话<br/>或新请求"]
E --> H{"记忆是否已成为规则?"}
H -- "是" --> I["迁移到specs/<br/>QWEN.md 或技能"]
H -- "否" --> E重要限制:这种记忆不能替代规范。它是对规范的补充。影响产品的决策必须进入specs/或QWEN.md,而不能只存在于数据库中。
具体该记住什么
不需要把请求上下文中的所有内容都保存下来。可以存储很多,但添加到上下文中的应该很少。
有用的类别:
- 用户持续稳定的偏好;
- 已确认的项目命令;
- 智能体反复出现的错误;
- 检查后的结论;
- 外部文档引用;
- 关于活跃分支和进程的备注;
- 需要迁移到规范中的决策。
无用的类别:
- 机密信息;
- 不必要的大量完整会话记录;
- 随机的中间想法;
- 没有有效期的过时变通方案;
- 智能体可以轻松从代码中读取到的内容。
SQLite 架构
创建目录:
mkdir -p .qwen/hooks .qwen/memory
架构已提取到单独文件中:examples/sqlite-memory/schema.sql。
在哪里获取示例文件。 下文假设教程仓库 sdd-qwen-code-ru/ 位于你的项目旁边,且变量 TUTORIAL_DIR 指向它。如果教程是单独克隆的,请在执行以下命令前设置 export TUTORIAL_DIR=/path/to/sdd-qwen-code-ru。如果你以其他方式获取了示例文件(下载zip、手动复制),只需替换路径前缀即可。
初始化:
cp "$TUTORIAL_DIR/examples/sqlite-memory/schema.sql" .qwen/memory/schema.sql
sqlite3 .qwen/memory/agent-memory.db < .qwen/memory/schema.sql
钩子1:事件日志记录
脚本已提取到单独文件中:examples/sqlite-memory/hooks/log_event.py。
将其复制到项目中并设置为可执行:
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/log_event.py" .qwen/hooks/log_event.py
chmod +x .qwen/hooks/log_event.py
钩子2:添加相关记忆
脚本已提取到单独文件中:examples/sqlite-memory/hooks/inject_memory.py。
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/inject_memory.py" .qwen/hooks/inject_memory.py
chmod +x .qwen/hooks/inject_memory.py
在Qwen Code中配置钩子
配置示例已提取到 examples/sqlite-memory/settings-hooks.example.json。
如果项目中已有设置,请手动合并JSON。不要覆盖模型和认证设置。
记忆的后台归纳
后台归纳不应在每个钩子中运行。它读取累积的事件并创建紧凑的记忆记录。可以手动运行、通过cron运行,或在完成重要阶段后运行。
最简单的无API方案:让Qwen Code执行整合,并通过SQL写入结果。
/clear
通过sqlite3读取.qwen/memory/agent-memory.db的最后几行。
将稳定的结论压缩到以下记忆路径:
- profile/preferences.md
- project/agentclinic.md
- workflow/sdd-validation.md
- tools/qwen-code.md
不要包含机密信息和原始记录。
写入前先展示建议的记忆条目。
可以进一步自动化:dream_sqlite.py 获取最新事件,通过你的API客户端调用模型,并更新或插入 memories 中的记录。
两个现成文件:
- examples/sqlite-memory/dream_sqlite_skeleton.py — 带有空
summarize_with_llm适配器的骨架。适合作为连接任何提供商时的参考。 - examples/sqlite-memory/dream_sqlite_qwen_example.py — 可工作的示例,调用与第4部分中Qwen Code相同的OpenAI兼容端点DashScope(变量
BAILIAN_API_KEY,模型qwen3-coder-plus)。这足以在没有单独集成的情况下启动归纳。
安装:
cp "$TUTORIAL_DIR/examples/sqlite-memory/dream_sqlite_qwen_example.py" .qwen/memory/dream_sqlite.py
运行:
python .qwen/memory/dream_sqlite.py --since 24h --dry-run
python .qwen/memory/dream_sqlite.py --since 24h
这与Anthropic的后台归纳有何关联
VentureBeat的文章描述了一个原则:智能体不修改模型权重,而是定期回顾过往会话,提取重复出现的模式、错误和有效的工作方法,然后让这些笔记可供未来会话使用。对于SDD,这在检查之后特别有用:
- 智能体两次忘记更新
roadmap.md;
- 用户每次都要求先展示差异;
- 项目中的测试命令与标准不同;
- 某种特定类型的迁移经常出问题;
- 有效的检查清单值得转化为技能。
在SQLite版本中,这成为一个完全本地化和可审计的流程:原始事件保留在 events 中,压缩后的笔记保留在 memories 中,而人可以打开两个表查看。
记忆应让位于规范的地方
如果后台归纳发现了一条新的稳定规则:
workflow/sdd-validation.md
始终在合并功能分支前更新CHANGELOG.md。
需要将其迁移到 QWEN.md 或变更日志技能中。
如果后台归纳发现了一项产品约定:
project/agentclinic.md
AgentClinic中的反馈记录必须是公开且讽刺性的,
而不是私密的客服投诉。
需要将其迁移到功能规范或 mission.md 中。
记忆帮助发现规则。规范使规则成为强制性的。
为什么更少的上下文往往效果更好
智能体记忆很容易变成「曾经讨论过的一切」的仓库。这是个坏主意。近年来的研究和实践记录了一种称为「上下文退化」(context rot)的现象:在大量输入中,模型更难选择相关片段。300个token的简短、精确的上下文,往往比数万个token的大量不相关内容效果更好。
由此得出连接记忆的简单规则:
- 在
QWEN.md中混入的不是「想起的一切」,而是仅与当前任务相关的记录(例如按标签/类别); - 限制注入长度——几个简短的项目优于长列表;
- 如果条目失去相关性,删除它,而不是「细化」它;
- 如果同一条记录每次都需要——这已经不是「记忆」,而是规则,它应该放在
QWEN.md或章程中。
同样的原理解释了为什么角色之间使用 /clear 有效:你有意识地将会话缩小到对下一个角色重要的内容,而不是在一个会话中累积所有内容。关于技能和会话中的上下文卫生,参见第14部分。
检查和查看
事件列表:
sqlite3 .qwen/memory/agent-memory.db \
"select event_name, tool_name, substr(prompt,1,80), timestamp from events order by id desc limit 20;"
记忆条目列表:
sqlite3 .qwen/memory/agent-memory.db \
"select path, updated_at from memories order by path;"
搜索:
sqlite3 .qwen/memory/agent-memory.db \
"select path, snippet(memory_fts, 1, '[', ']', '...', 12) from memory_fts where memory_fts match 'validation OR changelog';"
手动记录:
sqlite3 .qwen/memory/agent-memory.db < "$TUTORIAL_DIR/examples/sqlite-memory/manual-memory-example.sql"
隐私和安全
智能体记忆很容易变成多余物品的仓库。设定规则:
- 不记录环境变量;
- 截断工具响应;
- 不保存机密和个人数据;
- 不向上下文添加超过3-5个记忆块;
- 定期删除过时记录;
- 只提交架构和脚本,不提交
.qwen/memory/agent-memory.db。
.gitignore 的规则示例已提取到 examples/sqlite-memory/gitignore.example。
实践
- 创建SQLite架构。
- 将
log_event.py连接到UserPromptSubmit、PostToolUse和Stop。
- 将
inject_memory.py连接到SessionStart和UserPromptSubmit。 - 完成一个SDD功能。
- 查看
events。 - 创建2-3条手动记忆条目。
- 启动新的Qwen Code会话,检查相关记忆是否被添加到上下文中。
- 检查后,手动或通过脚本对记忆进行简要归纳。
- 将记忆中的强制性规则迁移到
QWEN.md、规范或技能中。
思考题
- 为什么原始事件和稳定记忆条目应该是不同的表?
- 哪些信息不能保存在智能体记忆中?
- 为什么记忆的后台归纳最好离线运行,而不是在每个钩子中运行?
- 什么时候需要将记忆迁移到
specs/? - 如何限制添加的上下文,避免记忆污染请求?