Files
agcore/design/pdd/14-phase7-sqlite-store.md
T
徐涛 28ca43ccb2 chore(docs): 将设计文档从 docs 移至 design 目录
将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
2026-07-23 05:45:53 +08:00

28 KiB
Raw Blame History

Phase 7 — SqliteStore 持久化实现方案

  • 文档编号14
  • 标题Phase 7 — SqliteStore 持久化实现方案
  • 日期2026-07-05
  • 状态:已定稿
  • 涉及模块memory/store
  • 关联文档roadmap.md, 6-memory-system.md

背景与目标

Phase 7 的核心任务是完成 MemoryStore trait 的 SQLite 后端实现,使 Agent 进程重启后记忆数据不丢失。这是 v0.2.0 从"内存玩具"走向"可用工具"的关键门槛,也是后续 Phase 8MVP 出口)和 Phase 10ContextSlot)的前置依赖。

成功标准

  • SqliteStore 完整实现 MemoryStore trait4 个方法:save/get/delete/list
  • 进程关闭后重新打开同一数据库文件,数据完整可读
  • 与现有 InMemoryStore 通过 MemoryStore trait 可互换,消费者零改动
  • 所有现有测试保持通过,clippy 0 警告

Scope & Non-goals

范围 内容
包含 单表 CRUD + prefix/since/offset/limit 查询 + WAL 并发 + Mutex 串行化 + 错误映射
不包含(Phase 7 淘汰策略(EvictionPolicy,仅 InMemoryStore 持有,需 v0.3 纳入 SqliteStore
不包含(Phase 7 Schema 迁移框架(PRAGMA user_version 足矣,不引入 refinery/sea-query
不包含(Phase 7 批量写入 / 事务 API(N+1 clear 延迟可接受,优化后置)
不包含(Phase 7 跨进程共享同一数据库文件(Mutex 为单进程设计)

当前状态分析

现有实现

  • MemoryStore trait 已在 v0.1 Phase 3 就绪,定义 4 个异步方法
  • InMemoryStore 实现稳定运行,使用 Mutex<HashMap> 作为后端
  • 全量测试 191 个通过,clippy 0 警告
  • 项目当前无 SQLite 或其他数据库依赖

现有消费者

通过 crate::memory::store::MemoryStore 路径引用的模块:

模块 文件 使用方式
Agent Builder agent/builder.rs RuntimeBundle 中引用 MemoryStore
Session Memory agent/session_memory.rs SessionMemory 实现
Agent Runtime agent/runtime.rs 类型标注
Agent Session agent/session.rs 默认 InMemoryStore 兜底
Conversation memory/conversation.rs ConversationMemory 测试
Knowledge memory/knowledge.rs KnowledgeStore 测试
Retriever memory/retriever.rs MemoryRetriever 测试

所有消费者均通过 MemoryStore trait 访问,不依赖具体实现类型,因此新增 SqliteStore 不会产生编译或运行时影响。

目录结构现状

src/memory/
├── mod.rs
├── store.rs         ← 包含 MemoryStore trait + InMemoryStore + EvictionPolicy
├── conversation.rs
├── knowledge.rs
├── retriever.rs
└── vector.rs

store.rs 目前是一个单体文件,同时承载 trait 定义和 InMemoryStore 实现。


调研发现

MemoryStore trait 定义

#[async_trait]
pub trait MemoryStore: Send + Sync {
    async fn save(&self, item: MemoryItem) -> Result<(), MemoryError>;
    async fn get(&self, id: &str) -> Result<Option<MemoryItem>, MemoryError>;
    async fn delete(&self, id: &str) -> Result<(), MemoryError>;
    async fn list(&self, filter: &MemoryFilter) -> Result<Vec<MemoryItem>, MemoryError>;
}

关键类型

类型 定义
MemoryItem { id: String, content: String, metadata: Value, created_at: OffsetDateTime }
MemoryFilter { prefix: Option<String>, since: Option<OffsetDateTime>, offset: Option<usize>, limit: Option<usize> }
MemoryError 变体:NotFound / Storage / Serialization / InvalidInput / RetrievalError
EvictionPolicy None / Ttl { ttl_secs } / Capacity { max_items }
EvictionConfig { policy, check_interval }

并发模型参考

InMemoryStore 当前使用 Mutex<HashMap> 实现 Send + Sync。SqliteStore 将遵循相同模式,使用 Arc<Mutex<Connection>> + spawn_blocking 满足异步 trait 约束。

Schema 设计考虑

  • created_at 使用 TEXT(ISO 8601) 存储——.to_string() 零转换,字典序与时间序一致(前提:所有时间戳归一化到 UTC;OffsetDateTime::to_string() 在 UTC 下输出 "2026-07-05T12:00:00Z" 格式,字典序与时间序严格对应)
  • 初始 schema 即创建 created_at 索引,避免后续大数据量全表排序
  • Schema 版本管理通过 PRAGMA user_version 实现,零外部依赖,后续加字段只需追加 if version < N { ALTER TABLE }

可选方案

A. rusqlite + Mutex<Connection>(推荐)

维度 评估
新增依赖 1 个(rusqlite 0.32 + bundled features
实现量 ~200 行
SQL 支持 原生支持 prefix LIKE 过滤 + ORDER BY 排序
性能 有索引时查询 O(log n),写入串行化
并发 WAL 模式 + Mutex 串行化写入,适合单进程 Agent
事务支持 完整 ACID
崩溃安全 WAL 模式,崩溃恢复有保障

适用场景:单进程 Agent 本地持久化、嵌入式场景、需要关系查询能力的通用存储。

外部依赖评估

  • rusqlite 0.32 — 最新稳定版(2025-12 发布),维护活跃(月均 2+ 次提交),Apache-2.0 许可证
  • bundled feature 编译 SQLite 源码(Public Domain)进二进制,无系统级 SQLite 依赖,零外部 C 库安装步骤
  • 供应链风险:bundled 模式依赖 crate 发布节奏同步 SQLite 安全更新;SQLite 安全公告频率极低(年均 <3 例),此风险可接受

B. JSONL 文件

维度 评估
新增依赖 0
实现量 ~150 行
get() 复杂度 O(n) 全量扫描
delete() 复杂度 O(n) 全量重写
并发 需文件锁(flock
事务支持
崩溃安全 无保障,写入中断可能丢失或损坏数据

否决理由:核心的 get() 查询场景不可接受 O(n) 性能;在 Agent 运行时频繁读写记忆的场景下,全量扫描的成本会随着数据积累线性增长,不符合可用性要求。

C. sled 嵌入式 KV

维度 评估
新增依赖 1 个(纯 Rust
实现量 ~150 行
prefix scan 原生支持
排序 需手动实现
关系模型 不如 SQL 匹配当前查询模式
社区成熟度 较新,API 仍在演进

否决理由:当前查询模式(prefix 过滤 + 按 created_at 排序)在关系模型中用一条 SQL 即可表达,引入 KV 存储反而需要手动处理排序逻辑。非必要不引入新存储范式。


推荐方案

总体方向:方案 Arusqlite + Mutex<Connection>

选择理由:

  1. 最少依赖,最高匹配:1 个新增依赖即可完整支持 MemoryFilter 的所有查询维度(prefix LIKE、created_at 范围、offset/limit
  2. 生产就绪rusqlite 是 SQLite 的 Rust 绑定事实标准,bundled 模式免去系统 SQLite 依赖
  3. Schema 演进简单PRAGMA user_version + 逐版本迁移,零外部迁移工具依赖
  4. 与 InMemoryStore 语义一致Mutex 串行化 + spawn_blocking 适配 async trait,与现有并发模型同构

关键设计决策

决策 选择 理由
Schema 版本管理 PRAGMA user_version 零外部依赖,~20 行,后续 ALTER TABLE 即可
created_at 存储格式 TEXT(ISO 8601) + UTC 归一化 零转换代码;UTC 下输出 "2026-07-05T12:00:00Z",字典序与时间序严格一致
Upsert SQL 策略 INSERT ... ON CONFLICT(id) DO UPDATE SET ... 保留调用方传入的 created_at,避免被 DEFAULT 覆盖
性能索引 初始 schema 加 created_at 索引 避免大数据量全表排序
配置参数 pathbusy_timeout=5s 其余内置默认值;5s 超时避免 SQLITE_BUSY 快速失败
Mutex 中毒恢复 .lock().unwrap_or_else(|e| e.into_inner()) 不 panic,恢复执行
批量操作 不加 N+1 clear ~250ms(N=50),可接受,优化后置
spawn_blocking 取消安全性 短事务模式(auto-commit 每个操作独立事务,取消时后台 task 自然完成/panic,不 Cross 操作持有 Mutex

我们放弃了什么(集中 Trade-off 记录):

  • 写入串行化Mutex<Connection> 确保 SQLite 写入安全,代价是同一时刻只能有一个写入者。Agent 场景下写入频率低(每次 LLM 调用触发 1-2 次),串行化不构成瓶颈
  • 单进程锁:无法跨进程共享同一数据库文件。多进程场景需要网络后端(PostgreSQL/Redis
  • 无横向扩展:单文件 SQLite 无分片能力。需扩展时切换到分布式后端

Schema 定义(初始版本)

CREATE TABLE IF NOT EXISTS memory_items (
    id         TEXT PRIMARY KEY,
    content    TEXT NOT NULL,
    metadata   TEXT NOT NULL DEFAULT '{}',
    created_at TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_memory_items_created_at
    ON memory_items(created_at);

数据完整性防御

异常场景 防御措施 错误映射
数据库文件损坏 migrate() 中执行 PRAGMA quick_check;失败时 open() 返回 MemoryError::Storage Storage
created_at 解析失败 get()/list()OffsetDateTime::parse 失败不 panic,返回 MemoryError::Serialization Serialization
content / metadata 为 NULL get() 中检测 SQLite 返回值,NULL 时返回 MemoryError::Storage Storage
约束冲突(PRIMARY KEY / NOT NULL 映射为 MemoryError::InvalidInput InvalidInput
序列化/反序列化失败 serde_json::to_string/from_str 错误映射为 MemoryError::Serialization Serialization

性能预算(目标延迟,单条操作)

操作 目标延迟 说明
save(1KB item) < 5ms 含 serde_json 序列化 + spawn_blocking + SQLite INSERT
get(1KB item) < 3ms 含 SQLite SELECT + 反序列化
list(prefix 匹配 100 行) < 20ms 含索引 B-tree 遍历 + ORDER BY + LIMIT
并发 10 writer p99 < 50ms Mutex 串行化排队,每 writer 等待 9×5ms 内

实施后通过 Step C 测试验证以上预算。未达标时不阻塞发布,但记录为可观测告警阈值。


实施建议

实施计划

Step A — 目录重构(纯搬移,零行为变化)

目标:将单体 store.rs 拆分为模块目录架构,为新增 SqliteStore 做准备。

src/memory/
├── store.rs                   ← 模块根:MemoryStore trait + EvictionPolicy/EvictionConfig
│                                + pub mod in_memory;
│                                + pub mod sqlite_store;
│                                + pub use in_memory::InMemoryStore;
├── store/
│   ├── in_memory.rs           ← InMemoryStore 提取至此(struct + impl + 6 个内联测试)
│   └── sqlite_store.rs        ← 新增 SqliteStore

模式参考:llm/provider.rsllm/provider/{openai,anthropic,ollama}.rs

外部消费者的导入路径不变crate::memory::store::MemoryStore),零改动风险。

重构步骤:

  1. 创建 src/memory/store/ 目录
  2. 创建 src/memory/store/in_memory.rs,从原 store.rs 提取 InMemoryStore 全部代码(struct + impl + Default + 6 个测试)
  3. 修改 src/memory/store.rs:保留 MemoryStore trait + EvictionPolicy/EvictionConfig,加 pub mod in_memory; + pub use in_memory::InMemoryStore;
  4. 验证:cargo test --all-targets 全绿,测试数量不变(191 pass)

Step B — SqliteStore 实现

  1. Cargo.toml 添加 rusqlite = { version = "0.32", features = ["bundled"] }
  2. 创建 src/memory/store/sqlite_store.rs,实现:
    • SqliteStore 结构体:{ conn: Arc<Mutex<Connection>> }
    • SqliteStore::open(path) 构造函数,支持 ":memory:"
    • lock_conn() 辅助方法(Mutex 中毒恢复)
    • migrate() Schema 初始化 + 版本管理
    • MemoryStore trait 的 4 个方法
    • From<rusqlite::Error> for MemoryError
  3. 修改 src/memory/store.rs:加 pub mod sqlite_store; + pub use sqlite_store::SqliteStore;
  4. 修改 src/memory.rs:加 pub use store::SqliteStore;
  5. 编写测试覆盖:
    • CRUD 基本操作
    • Upsert(同 id 重复 save 覆盖)
    • prefix 过滤
    • 由于/until 时间范围过滤
    • 并发 10 个 writer × 10 次操作
    • 持久化恢复(write → drop → reopen → read
  6. 验证:cargo test --all-targets 全绿 + cargo clippy --all-targets -- -D warnings 0 警告

Step C — 验证确认

  1. 确认现有 memory 模块内测试全部通过
  2. 确认 agent/llm/tools/prompt 模块不受影响
  3. 确认 SqliteStore 与 InMemoryStore 通过 MemoryStore trait 可互换
  4. 确认 clippy 无新增警告

Commit 安排

顺序 类型 Scope 描述
1 refactor memory 将 store.rs 拆分为模块目录,仅结构搬移
2 feat memory 实现 SqliteStore 持久化

风险与缓解

风险 严重度 缓解措施
Mutex 中毒导致后续操作全部失败 lock_conn() 使用 .lock().unwrap_or_else(|e| e.into_inner()) 恢复模式,不 panic
spawn_blocking 取消后连接状态不一致 每个操作使用短事务(auto-commit),不跨操作持有 Mutex;取消时遗留 task 自然完成或 panicMutex 通过 .into_inner() 恢复
WAL 文件无限增长 内置 auto-checkpoint 阈值 + 启动时执行 PRAGMA wal_checkpoint(TRUNCATE)
list 无索引导致全表扫描 中(大数据量) 初始 schema 即创建 idx_memory_items_created_at 索引
父目录不存在导致 open 失败 open() 内部调用 fs::create_dir_all() 确保目录存在
clear() N+1 删除性能 不走 trait 接口的逐条删除,可后续优化为直接 DELETE FROM memory_items
数据库文件损坏 migrate() 中执行 PRAGMA quick_check;失败时返回 MemoryError::Storage,调用方可切换 InMemoryStore

可观测性(实施时落实)

  • 所有 MemoryStore 方法通过 tracing::instrument 记录延迟和结果(info! 正常完成,warn! 超过性能预算阈值,error! 操作失败)
  • list() 返回行数通过 tracing::debug 记录(调优参考)
  • WAL 文件大小在 migrate() 后检查一次,超过 100MB 时记录 warn!
  • 操作计数(读写次数、错误率)暂不暴露为独立 metrics,v0.3 按需添加

已知假设

假设 验证状态 Fallback
单进程独享 SQLite 文件,无跨进程竞争 设计前提(Mutex 为单进程设计) 多进程场景使用网络后端(PostgreSQL/Redisv0.3+
ISO 8601 TEXT 字典序等价于时间序 条件成立(需 UTC 归一化) 若时区异常,切换 INTEGER(unix_timestamp) 存储后重建索引
SqliteStore 初始化失败可 fallback 到 InMemoryStore 调用方自行控制 open() 返回 MemoryError,消费者 catch 后改用 InMemoryStore::new()
N+1 clear ~250ms(N=50) 可接受 🟡 未实测(基于 N×5ms 推算) 若成为瓶颈,SqliteStore 内部加 delete_by_prefix() 方法(不走 trait 接口)
rusqlite bundled SQLite 版本足够新 0.32 版内置 SQLite 3.46 如需特定版本,切换 bundled 为指定版本或使用系统 SQLite
busy_timeout=5s 覆盖所有竞争场景 🟡 未实测(WAL 下写写冲突概率低) 若观测到 SQLITE_BUSY,增大超时或在重试逻辑中处理
spawn_blocking 线程池不会被耗尽 默认 512 线程,Agent 场景占用 ≤10 若观测到阻塞任务排队,启动时 tokio::task::spawn_blocking 已有兜底排队机制

参考来源

  • rusqlite crate — 官方文档
  • SQLite PRAGMA user_version — Schema 版本管理机制
  • SQLite WAL mode — 并发读写性能优化
  • docs/6-memory-system.md — Phase 3 MemoryStore trait 原始设计
  • docs/roadmap.md — 项目里程碑规划(Phase 7/8/10 依赖关系)
  • src/llm/provider.rssrc/llm/provider/ — 目录重构模式参考

实施计划

任务总览

3 个阶段、8 个任务单元、2 个 Commit。

阶段一:目录重构

Task A1 — 创建 store/ 目录并提取 InMemoryStore

项目 内容
任务描述 创建 src/memory/store/ 目录,新建 store/in_memory.rs,从 store.rs 完整提取 InMemoryStore 结构体、impl MemoryStore、impl Default、6 个内联测试
涉及文件 src/memory/store.rs → 分割到 src/memory/store/in_memory.rs(新增)
前置依赖
预估工作量 S< 1h
风险等级 低 — 纯搬移,编译器可验证
验收条件 cargo build 通过(此时 store.rs 尚未修改,store/in_memory.rs 应被 crate 忽略)

注意:需要先在 store.rs 顶部添加 pub mod in_memory; 声明,否则子模块不会被编译。或者可以先创建目录和文件,等 Task A2 再统一加声明路径。

实际做法:先创建文件但不声明,A2 统一声明。这样 A1 和 A2 之间可以有一个无编译的中间状态。

Task A2 — 修改 store.rs 模块根

项目 内容
任务描述 修改 store.rs 为纯模块根:保留 MemoryStore trait、EvictionPolicyEvictionConfig;添加 pub mod in_memory; + pub use in_memory::InMemoryStore;;删除已提取到 in_memory.rs 中的代码
涉及文件 src/memory/store.rs(修改)
前置依赖 Task A1(文件已存在)
预估工作量 S< 1h
风险等级 低 — 保留部分不变,提取部分在子模块中
验收条件 cargo test --all-targets 全绿,测试数量不变(191 pass),clippy 0 warning

Task A3 — 验证阶段一

项目 内容
任务描述 运行全量测试链确认目录重构零行为变化
涉及文件 全量
前置依赖 Task A2
预估工作量 XS(验证)
风险等级
验收条件 cargo test --all-targets 191 pass、cargo clippy --all-targets -- -D warnings 0 警告、cargo build 通过

阶段二:SqliteStore 实现

Task B1 — 添加 rusqlite 及 dev-dependencies

项目 内容
任务描述 Cargo.toml 中添加依赖:[dependencies]rusqlite = { version = "0.32", features = ["bundled"] }[dev-dependencies]tempfile = "3"(用于测试隔离);运行 cargo build 确认编译通过,cargo test --no-run 验证 dev-dependencies 可用
涉及文件 Cargo.toml(修改)、Cargo.lock(自动更新)
前置依赖 无(可与阶段一并行)
预估工作量 XS< 15min
风险等级 低 — 标准依赖添加
验收条件 cargo build 成功,cargo test --no-run 成功,Cargo.lock 中生成 rusqlite 和 tempfile 条目

Task B2 — 实现 SqliteStore 核心

项目 内容
任务描述 创建 src/memory/store/sqlite_store.rs,实现以下 8 个子模块:
1SqliteStore 结构体 { conn: Arc<Mutex<Connection>> }
2SqliteStore::open(path) — 支持 ":memory:",内部调用 fs::create_dir_all 确保父目录存在
3lock_conn() — 内部辅助方法,.lock().unwrap_or_else(|e| e.into_inner()) 处理 Mutex 中毒
4migrate() — 按版本递增执行迁移:PRAGMA user_version 检查(初始版本号=1)→ 建表 memory_items + 索引 idx_memory_items_created_at + 设置 WAL 模式 + busy_timeout=5s + PRAGMA synchronous = NORMAL + PRAGMA wal_autocheckpoint=1000 + PRAGMA quick_check(检测数据库损坏)+ PRAGMA wal_checkpoint(TRUNCATE)
5MemoryStore trait 的 4 个方法实现(save/get/delete/list),全部使用 spawn_blocking 包裹;生命周期注意spawn_blocking 闭包前先 .conn.clone()Arc<Connection>,参数调 .clone() 取 owned 值,再传入 spawn_blocking(move |{ ... })
save: INSERT INTO memory_items (id, content, metadata, created_at) VALUES (?1, ?2, ?3, ?4) ON CONFLICT(id) DO UPDATE SET content=excluded.content, metadata=excluded.metadata, created_at=excluded.created_at(全字段覆盖 upsert,与 InMemoryStore 行为一致)
get: SELECT content, metadata, created_at FROM memory_items WHERE id = ?1 → Ok(None) 当无结果
delete: DELETE FROM memory_items WHERE id = ?1(幂等,不返回 NotFound
list: 根据 filter 字段(prefix/since/offset/limit)组合动态构造 WHERE 子句 + ORDER BY created_at ASC + LIMIT ? OFFSET ?,全部使用参数化查询防注入
6)序列化转换层:time::OffsetDateTime 存为 TEXT(ISO 8601),通过 .to_string() 绑定 String 参数;serde_json::Value 存为 TEXT,通过 serde_json::to_string() 绑定 String 参数;读取时通过 OffsetDateTime::parseserde_json::from_str 反序列化。在 SqliteStore 内部实现 to_sql_params() / from_sql_row() 辅助方法集中处理
(7)错误映射:不在 blanket impl From 中处理所有 rusqlite Error,而是在每个方法内部按数据完整性防御表的映射规则逐类处理:
— 数据库文件损坏 / IO 错误 → MemoryError::Storage
— created_at 解析失败 → MemoryError::Serialization(不 panic
— content/metadata 为 NULL → MemoryError::Storage
— serde_json 序列化/反序列化失败 → MemoryError::Serialization
— 约束冲突 → MemoryError::InvalidInput
(8)可观测性:在 4 个 trait 方法和 open() 上添加 #[tracing::instrument(skip(self))];正常完成记录 trace!,超过性能预算阈值记录 warn!,操作失败记录 error!
涉及文件 src/memory/store/sqlite_store.rs(新增)
前置依赖 Task B1rusqlite + tempfile 依赖)、Task A1store/ 目录存在)
预估工作量 M1-4h
风险等级 高 — 3 个技术点需留意:time::OffsetDateTimerusqlite::ToSql 实现,需显式处理 String 绑定;spawn_blocking + &self 生命周期需 clone 后才能传闭包;错误映射需精细匹配 rusqlite::Error 嵌套变体(SqliteFailure 内含 ErrorCode
验收条件 单元测试通过(见 Task B4)、cargo build 通过

Task B3 — 注册模块并重导出

项目 内容
任务描述 store.rs 添加 pub mod sqlite_store; + pub use sqlite_store::SqliteStore;;在 memory.rs 添加 pub use store::SqliteStore;
涉及文件 src/memory/store.rs(修改)、src/memory.rs(修改)
前置依赖 Task B2sqlite_store.rs 文件存在)
预估工作量 XS< 15min
风险等级
验收条件 cargo build 通过,SqliteStore 可从 agcore::memory::SqliteStore 路径访问

Task B4 — 编写测试

项目 内容
任务描述 sqlite_store.rs 中编写 #[cfg(test)] mod tests,覆盖:
1CRUD 基本操作(save → get → list → delete → get None
2Upsert 语义(同 id 重复 save 覆盖内容,created_at 保持调用方传入值)
3prefix 过滤(MemoryFilter.prefix
4)时间范围过滤(MemoryFilter.since
5offset/limit 分页
6)并发 10 个 writer × 10 次写入,验证无数据丢失
(7)持久化恢复(write → drop store → reopen 同一文件 → read
8)错误路径:open("/nonexistent_dir/ag.db") 返回 Storage 错误
辅助函数:make_item(id) 创建 MemoryItem,使用 tempfile::TempDir(或自定义 tmp 路径)隔离测试数据库文件
涉及文件 src/memory/store/sqlite_store.rs(修改追加 test mod
前置依赖 Task B2(实现完成)
预估工作量 M1-4h
风险等级 中 — 并发测试的时序控制、持久化恢复测试的 TempDir 管理
验收条件 全量测试通过,新增测试 ≥ 8 个

Task B5 — 验证阶段二

项目 内容
任务描述 运行全量测试链确认 SqliteStore 实现正确,不影响已有模块
涉及文件 全量
前置依赖 Task B3、Task B4
预估工作量 S< 1h
风险等级
验收条件 cargo test --all-targets 全绿(191 + 新增测试)、cargo clippy --all-targets -- -D warnings 0 警告、cargo build 通过

阶段三:验证收尾

Task C1 — 跨模块兼容性验证

项目 内容
任务描述 1)确认 ConversationMemory / KnowledgeStore / MemoryRetriever / AgentSession / SessionMemoryArc<dyn MemoryStore> 接受 SqliteStore 时编译通过且测试全绿
2)确认 SqliteStore 与 InMemoryStore 可互换——修改一个现有测试将后端从 InMemoryStore 换为 SqliteStore(使用 ":memory:"),测试全绿
(3)验证性能预算:在测试环境下测量 save(1KB)/get(1KB)/list(100行) 的单次延迟,确认 < 5ms / < 3ms / < 20ms
涉及文件 测试文件(memory/ 模块内各 test mod
前置依赖 Task B5
预估工作量 S< 1h
风险等级
验收条件 全量测试通过 + 互换测试通过 + 性能预算大致满足(未达标不阻塞发布)

依赖关系图

阶段一(目录重构)        阶段二(SqliteStore 实现)
┌──────────────┐         ┌──────────────┐
│ Task A1      │         │ Task B1      │ ← 无依赖,可与 A 并行
│ 创建目录+提取 │         │ Cargo.toml   │
└──────┬───────┘         └──────┬───────┘
       ↓                        ↓
┌──────────────┐         ┌──────────────┐
│ Task A2      │         │ Task B2      │
│ 修改store.rs │← A1 ───→│ 核心实现      │← B1 + A1
└──────┬───────┘         └──────┬───────┘
       ↓                        ↓
┌──────────────┐         ┌──────────────┐         ┌──────────────┐
│ Task A3      │         │ Task B3      │← B2 ──→│ Task B4      │
│ 验证阶段一    │         │ 注册+重导出   │         │ 编写测试     │
└──────────────┘         └──────┬───────┘         └──────┬───────┘
                               ↓                        ↓
                        ┌──────────────┐────────────────┘
                        │ Task B5      │← B3 + B4
                        │ 验证阶段二    │
                        └──────┬───────┘
                               ↓
                        阶段三(验证收尾)
                        ┌──────────────┐
                        │ Task C1      │
                        │ 跨模块兼容性   │
                        └──────────────┘

Commit 安排

顺序 Commit 类型 Scope 描述 包含 Task
1 refactor memory 将 store.rs 拆分为模块目录,仅结构搬移 A1 → A2 → A3
2 feat memory 实现 SqliteStore 持久化(含错误映射、测试、WAL 模式) B1 → B2 → B3 → B4 → B5 → C1

注意:Task B1 与阶段一无依赖,可以在 Commit 1 合并进行或在 Commit 2 开头。建议在 Commit 2 开头,因为 Cargo.toml 变更属于功能变更而非重构。

验证全链

实施完毕后整体认证链路:

  1. cargo test --all-targets — 全量测试通过
  2. cargo clippy --all-targets -- -D warnings — 0 警告
  3. cargo build --release — release 构建通过
  4. 确认 cargo doc --no-deps 无 warning(新增公开类型文档注释)
  5. 确认测试数量:191 + (8 个 new sqlite_store tests) = 199+ pass