KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
配套项目:把每个记忆概念落到代码里 — keel 龙骨
Persistent Memory 的配套项目:配套项目:把每个记忆概念落到代码里
从数据契约开始,逐步把保存、读取、检索、上下文装配和 Harness 接口连起来。不要在第一章就通读所有源码;每理解一个概念,再打开对应模块,代码为什么存在会更清楚。
1. 准备环境
建议使用 Python 3.11 或更高版本:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
四个示例会自动找到 src 目录,不需要手动设置 PYTHONPATH。
需要真实模型的示例还要求本机运行 Ollama:
ollama serve
ollama pull qwen3:8b
ollama pull qwen3-embedding:8b
2. 按章节运行和阅读
完成第 03 章后:看数据契约
打开 src/persistent_memory_course/contracts.py,只看四个对象:
MemoryScope 当前数据属于哪个租户和用户
MemorySource 这条信息真正来自哪里
MemoryCandidate 模型提出、但还没有写入权限的候选
MemoryRecord 通过门禁后保存的正式记录
先尝试回答每个字段由模型、Harness、策略还是存储层提供,再继续看代码。
完成第 04 章后:看 SQLite 生命周期
python examples/01_store_and_recall.py
$env:PYTHONPATH = "src"
python -m pytest tests/test_repository.py -q
对应源码是 repository.py。按这个顺序阅读:
_create_tables() -> 数据怎样落盘
add() -> 主记录和审计怎样一起提交
list_active() -> 作用域、状态和过期怎样过滤
supersede() -> 新旧版本怎样在同一事务中切换
delete() -> 记录怎样停止参与正常查询
完成第 05 章后:看 Harness 接口
打开 service.py。MemoryService 不是完整 Harness,而是 Harness 调用的记忆应用服务:
remember() 写路径:策略 -> embedding -> 正式记录
search() 读路径:作用域过滤 -> 相似度 -> 排序
forget() 删除入口:身份作用域 -> 状态变化
重点观察 scope 是调用参数,而不是模型输出。
完成第 06 章后:接入真实 Ollama
python examples/02_extract_candidate.py
$env:PYTHONPATH = "src"
python -m pytest tests/test_extractor_and_context.py -q
对应源码是 extractor.py。它只负责把自然语言转换成 MemoryCandidateBatch,没有 repository,也不能写数据库。
完成第 07 章后:观察检索和上下文
python examples/03_semantic_recall.py
python examples/04_build_context.py
03 使用真实 qwen3-embedding:8b;04 使用确定性的测试 provider,方便反复修改上下文预算。
对应源码:
embeddings.py Ollama provider、测试 provider、余弦相似度
service.py 过滤后的检索和简单重排
context_builder.py 去重、预算、来源标记和数据边界
3. 一次完整调用怎样穿过这些模块
flowchart LR
A[OllamaMemoryExtractor] --> B[MemoryCandidate]
B --> C[MemoryWritePolicy]
C --> D[MemoryService]
D --> E[EmbeddingProvider]
D --> F[SQLiteMemoryRepository]
F --> G[MemoryService.search]
G --> H[build_memory_context]
H --> I[Harness 的模型调用]
图中每条箭头都传递明确对象,而不是任意字符串。这样才能分别测试抽取错误、策略拒绝、数据库失败、检索误召回和上下文超预算。
4. 测试不是附加内容
运行全部测试:
python -m pytest -q
测试覆盖:
- 不同 tenant/owner 之间的隔离;
- 过期记录不参与查询;
- 删除和版本替代;
- 用户与 Agent 来源需要确认;
- restricted 内容被拒绝;
- 程序性记忆只接受 policy 来源;
- Structured Outputs 返回后的再次校验;
- Context Builder 的去重和预算。
学习时可以故意破坏一条规则,再观察哪个测试失败。你会看到生产约束究竟由哪段代码维护。
5. 这个项目没有假装解决什么
它适合学习和小规模实验,但没有假装已经完成全部生产基础设施:
- SQLite 中的 JSON 向量不适合大规模近邻搜索;
- 示例没有部署队列、HTTP API 和多进程 worker;
- 敏感信息策略只是最小演示,不替代组织的数据分类体系;
- 逻辑删除没有自动清理缓存、备份和外部向量库;
- 简单重排公式必须用真实数据校准。
这些限制被明确留下,是为了让你先学会记忆系统的稳定边界,再根据真实规模替换实现。