603 lines
19 KiB
Markdown
603 lines
19 KiB
Markdown
# 记忆系统设计方案
|
||
|
||
> 设计日期:2026-06-07
|
||
> 状态:待实现
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
### 1.1 背景
|
||
|
||
AG Core 已完成 Phase 0(LLM 调用周期)、Phase 1(提示词工程)、Phase 2(工具系统)。Phase 3 的目标是构建记忆系统,为 Phase 4(Agent 运行时)提供记忆存储、管理与检索能力。
|
||
|
||
### 1.2 目标
|
||
|
||
提供一套可插拔的记忆抽象层,支持以下记忆形态:
|
||
|
||
- **对话记忆(ConversationMemory)** — 管理多轮对话消息历史,支持 sliding window / 全量策略
|
||
- **知识库(KnowledgeStore)** — 基于 LLM Wiki 模式的结构化知识管理,Agent 可自主编译和维护知识页面
|
||
- **检索器(MemoryRetriever)** — 单通道关键词检索,提供统一的记忆查找入口
|
||
|
||
### 1.3 设计原则
|
||
|
||
- **不引入 embedding 依赖** — 采用 Karpathy's LLM Wiki 模式的 index + keyword 检索,替代传统向量检索
|
||
- **trait + 轻量默认实现** — 存储抽象接口提供纯内存默认实现(InMemoryStore),满足原型和测试需求
|
||
- **模块间松耦合** — 记忆系统与 LlmCycle 的集成推迟到 Phase 4 Agent Runtime,Phase 3 只定义接口和数据操作
|
||
|
||
---
|
||
|
||
## 2. 需求分析
|
||
|
||
### 2.1 功能需求
|
||
|
||
| ID | 需求 | 优先级 | 说明 |
|
||
|----|------|--------|------|
|
||
| F1 | MemoryStore 通用键值存储 | P0 | save/get/delete/list |
|
||
| F2 | 对话消息管理 | P0 | 按 session 管理,支持 sliding window / full |
|
||
| F3 | 知识页面 CRUD | P1 | 创建/更新/删除/检索知识页面 |
|
||
| F4 | 知识页面关键词检索 | P1 | 基于标题/摘要/标签的关键词匹配 |
|
||
| F5 | 知识页面索引维护 | P1 | 维护可遍历的内容目录(index) |
|
||
| F6 | 可插拔后端 | P0 | MemoryStore 通过 trait 抽象,下游可实现自定义后端 |
|
||
| F7 | 记忆淘汰 | P1 | 支持 TTL 过期淘汰、容量上限淘汰 |
|
||
| F8 | 消息条目级淘汰 | P1 | ConversationMemory 达到上限后删除最旧消息 |
|
||
|
||
### 2.2 非功能需求
|
||
|
||
| ID | 需求 | 说明 |
|
||
|----|------|------|
|
||
| NF1 | 零 embedding 依赖 | 核心库不引入任何向量数据库或 embedding 模型依赖 |
|
||
| NF2 | 错误体系完善 | MemoryError 枚举,支持 is_recoverable() 分类 |
|
||
| NF3 | 线程安全 | 所有存储实现满足 Send + Sync |
|
||
| NF4 | 异步 API | 所有 IO 操作为 async |
|
||
| NF5 | 模块化 | 各组件独立可替换 |
|
||
|
||
---
|
||
|
||
## 3. 方案设计
|
||
|
||
### 3.1 总体架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph Retrieval["检索层"]
|
||
MR["MemoryRetriever"]
|
||
end
|
||
subgraph Logic["逻辑层"]
|
||
CM["ConversationMemory"]
|
||
KS["KnowledgeStore"]
|
||
end
|
||
subgraph Storage["存储层"]
|
||
MS["MemoryStore (trait)"]
|
||
IMS["InMemoryStore (默认)"]
|
||
end
|
||
|
||
MR --> KS
|
||
CM --> MS
|
||
KS --> MS
|
||
MS --> IMS
|
||
```
|
||
|
||
### 3.2 模块结构
|
||
|
||
```
|
||
src/
|
||
memory.rs # 模块根:pub mod + pub use 重导出
|
||
memory/
|
||
store.rs # MemoryStore trait + InMemoryStore
|
||
conversation.rs # ConversationMemory(对话管理)
|
||
knowledge.rs # KnowledgeStore(具体 struct)
|
||
retriever.rs # MemoryRetriever(单通道检索)
|
||
error.rs # MemoryError
|
||
types.rs # 核心数据类型
|
||
```
|
||
|
||
### 3.3 接口定义
|
||
|
||
#### MemoryStore — 底层存储抽象
|
||
|
||
```rust
|
||
#[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>;
|
||
}
|
||
```
|
||
|
||
#### InMemoryStore — 默认实现
|
||
|
||
```rust
|
||
pub struct InMemoryStore {
|
||
items: Mutex<HashMap<String, MemoryItem>>,
|
||
}
|
||
```
|
||
|
||
基于 `HashMap<String, MemoryItem>` + `Mutex`,纯内存,线程安全。
|
||
|
||
#### ConversationMemory — 对话记忆
|
||
|
||
```rust
|
||
pub struct ConversationMemory {
|
||
store: Arc<dyn MemoryStore>,
|
||
session_id: String,
|
||
config: ConversationMemoryConfig,
|
||
}
|
||
|
||
pub struct ConversationMemoryConfig {
|
||
pub strategy: MemoryStrategy, // sliding_window | full
|
||
pub max_turns: usize, // sliding window 的最大轮数
|
||
pub compact_config: Option<CompactConfig>, // 复用现有压缩配置
|
||
}
|
||
```
|
||
|
||
- 基于 `MemoryStore` 持久化消息
|
||
- `add_message()` 写入,`get_history()` 读取
|
||
- sliding window 模式:超出 `max_turns` 后,调用 `llm::compact::microcompact()` 裁剪
|
||
- 复用现有 `CompactConfig`(context_window, reserved_tokens, keep_recent)
|
||
|
||
#### KnowledgeStore — 具体 struct
|
||
|
||
```rust
|
||
pub struct KnowledgeStore {
|
||
store: Arc<dyn MemoryStore>,
|
||
index: Mutex<Vec<PageIndexEntry>>,
|
||
}
|
||
|
||
impl KnowledgeStore {
|
||
pub fn new(store: Arc<dyn MemoryStore>) -> Self { ... }
|
||
pub async fn add_page(&self, page: KnowledgePage) -> Result<(), MemoryError> { ... }
|
||
pub async fn get_page(&self, id: &str) -> Result<Option<KnowledgePage>, MemoryError> { ... }
|
||
pub async fn update_page(&self, page: KnowledgePage) -> Result<(), MemoryError> { ... }
|
||
pub async fn delete_page(&self, id: &str) -> Result<(), MemoryError> { ... }
|
||
pub async fn search(&self, query: &str) -> Result<Vec<KnowledgePage>, MemoryError> { ... }
|
||
pub async fn get_index(&self) -> Result<Vec<PageIndexEntry>, MemoryError> { ... }
|
||
}
|
||
```
|
||
|
||
- `search` 使用简单的字符串包含匹配标题/摘要/标签
|
||
- index 在 add/update/delete 时自动维护
|
||
|
||
#### MemoryRetriever — 简化版检索器
|
||
|
||
```rust
|
||
pub struct MemoryRetriever {
|
||
knowledge_store: KnowledgeStore,
|
||
config: RetrieverConfig,
|
||
}
|
||
|
||
pub struct RetrieverConfig {
|
||
pub max_results: usize, // 默认 20
|
||
pub min_score: f32, // 默认 0.1
|
||
}
|
||
|
||
pub struct RetrievalResult {
|
||
pub items: Vec<ScoredItem>,
|
||
pub query: String,
|
||
}
|
||
|
||
pub struct ScoredItem {
|
||
pub page: KnowledgePage,
|
||
pub score: f32, // TextOverlap 评分 [0.0, 1.0]
|
||
}
|
||
```
|
||
|
||
检索流程:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["输入: query"]
|
||
B["1. 关键词提取(split + 过滤停用词)"]
|
||
C["2. KnowledgeStore.search(keywords)"]
|
||
D["3. TextOverlap 评分"]
|
||
E["4. 过滤 score < min_score"]
|
||
F["5. 降序排序 → 截取 top-N"]
|
||
G["6. 返回 RetrievalResult"]
|
||
|
||
A --> B
|
||
B --> C
|
||
C --> D
|
||
D --> E
|
||
E --> F
|
||
F --> G
|
||
```
|
||
|
||
关键词提取在 MemoryRetriever 内部简单实现:按空格/标点分割 → 过滤单字符和停用词 → 返回关键词列表。TextOverlap 计算 query 与页面标题/摘要/内容的 n-gram 重叠度(基于 Dice 系数)。
|
||
|
||
### 3.4 核心数据类型
|
||
|
||
```rust
|
||
pub struct MemoryItem {
|
||
pub id: String,
|
||
pub content: String,
|
||
pub metadata: serde_json::Value,
|
||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||
}
|
||
|
||
pub struct MemoryFilter {
|
||
pub prefix: Option<String>,
|
||
pub since: Option<chrono::DateTime<chrono::Utc>>,
|
||
pub limit: Option<usize>,
|
||
}
|
||
|
||
pub struct KnowledgePage {
|
||
pub id: String,
|
||
pub title: String,
|
||
pub summary: String,
|
||
pub content: String,
|
||
pub tags: Vec<String>,
|
||
pub references: Vec<String>,
|
||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||
}
|
||
|
||
pub struct PageIndexEntry {
|
||
pub id: String,
|
||
pub title: String,
|
||
pub summary: String,
|
||
pub tags: Vec<String>,
|
||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||
}
|
||
|
||
pub enum MemoryStrategy {
|
||
SlidingWindow,
|
||
Full,
|
||
}
|
||
```
|
||
|
||
### 3.5 错误类型
|
||
|
||
```rust
|
||
#[derive(Debug, thiserror::Error)]
|
||
pub enum MemoryError {
|
||
#[error("Item not found: {0}")]
|
||
NotFound(String),
|
||
|
||
#[error("Item already exists: {0}")]
|
||
AlreadyExists(String),
|
||
|
||
#[error("Storage error: {0}")]
|
||
Storage(String),
|
||
|
||
#[error("Serialization error: {0}")]
|
||
Serialization(String),
|
||
|
||
#[error("Invalid input: {0}")]
|
||
InvalidInput(String),
|
||
|
||
#[error("Retrieval error: {0}")]
|
||
RetrievalError(String),
|
||
}
|
||
|
||
impl MemoryError {
|
||
pub fn is_recoverable(&self) -> bool {
|
||
matches!(self, Self::NotFound(_) | Self::RetrievalError(_))
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.6 ConversationMemory 与 compact 模块的集成
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph CM["ConversationMemory (src/memory/conversation.rs)"]
|
||
A["add_message()"]
|
||
A1["store.save(message) ← 写到底层存储"]
|
||
C{"sliding_window && over_limit?"}
|
||
D["messages = store.list(session)"]
|
||
E["compact_config.should_compact() ← 复用 CompactConfig"]
|
||
F["microcompact(&mut messages) ← 复用 microcompact()"]
|
||
G["store.save(pruned) ← 写回"]
|
||
H["return"]
|
||
I["get_history()"]
|
||
J["store.list(session)"]
|
||
|
||
A --> A1
|
||
A1 --> C
|
||
C -->|是| D
|
||
D --> E
|
||
E --> F
|
||
F --> G
|
||
G --> H
|
||
C -->|否| H
|
||
I --> J
|
||
end
|
||
K["imports: llm::compact::{CompactConfig, microcompact, should_compact}"]
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 物理存储策略
|
||
|
||
### 4.1 存储层次
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph RetrievalLayer["检索层"]
|
||
MR["MemoryRetriever (检索 + 评分,无状态)"]
|
||
end
|
||
subgraph AbstractionLayer["存储抽象层"]
|
||
ABS["MemoryStore\n(存储抽象接口,不感知存储介质)"]
|
||
end
|
||
subgraph ImplementationLayer["实现层"]
|
||
IMS["InMemoryStore (HashMap)\n进程内 volatile\n测试/原型适用"]
|
||
CUSTOM["下游自定义实现\nFileStore / SqliteStore / RedisStore / ...\n生产环境适用"]
|
||
end
|
||
|
||
MR --> ABS
|
||
ABS --> IMS
|
||
ABS --> CUSTOM
|
||
```
|
||
|
||
### 4.2 InMemoryStore 的物理存储
|
||
|
||
| 组件 | 数据结构 | 存储位置 | 持久化 | 生命周期 |
|
||
|------|---------|---------|--------|---------|
|
||
| `InMemoryStore` | `HashMap<String, MemoryItem>` | 进程堆内存 | ❌ | 随进程销毁 |
|
||
| `KnowledgeStore` | 基于 `InMemoryStore` + `Vec<PageIndexEntry>` | 进程堆内存 | ❌ | 随进程销毁 |
|
||
|
||
**适用场景:**
|
||
- 单元测试和集成测试
|
||
- 本地快速原型开发
|
||
- 单次会话的临时 Agent
|
||
|
||
**不适合场景:**
|
||
- 生产部署
|
||
- 需要跨进程/跨会话共享记忆
|
||
- 需要数据持久化和恢复
|
||
|
||
### 4.3 持久化存储方案(下游实现)
|
||
|
||
agcore 核心库**不内置**持久化实现,用户通过实现 `MemoryStore` trait 对接所需后端:
|
||
|
||
| 后端 | 实现建议 | 适用场景 | 复杂度 |
|
||
|------|---------|---------|--------|
|
||
| **JSON 文件** | MemoryStore trait → 序列化为单文件 JSON | 单机、轻量持久化 | 低 |
|
||
| **SQLite** | MemoryStore → 关系表 | 单机、中小规模 | 中 |
|
||
| **PostgreSQL** | MemoryStore → 关系表 | 多进程共享、中等规模 | 中 |
|
||
| **Redis** | MemoryStore → Hash/JSON 类型 | 高速缓存、会话共享 | 低 |
|
||
|
||
### 4.4 对下游实现的约束
|
||
|
||
`MemoryStore` trait 对持久化实现无特殊约束:
|
||
- 方法签名不涉及文件路径、连接字符串等存储细节
|
||
- 所有方法均为 `async`,持久化实现可自由选择同步(`spawn_blocking`)或异步 driver
|
||
- 初始化参数在具体实现的构造函数中注入
|
||
|
||
### 4.5 序列化
|
||
|
||
核心类型均实现 `Serialize` / `Deserialize`(通过 `#[derive(serde)]`),便于持久化实现直接复用:
|
||
|
||
```rust
|
||
#[derive(Serialize, Deserialize)]
|
||
pub struct MemoryItem { ... }
|
||
#[derive(Serialize, Deserialize)]
|
||
pub struct KnowledgePage { ... }
|
||
// ...
|
||
```
|
||
|
||
所有类型基于 `serde_json::Value` 作为 metadata 类型,不引入 protobuf/msgpack 等序列化框架。
|
||
|
||
---
|
||
|
||
## 5. 淘汰策略
|
||
|
||
### 5.1 问题
|
||
|
||
所有存储组件如果不设上限,会随运行时间无限增长。ConversationMemory 当前的 sliding window 只做 tool result 压缩,不删除消息条目。
|
||
|
||
### 5.2 淘汰策略
|
||
|
||
在 `MemoryStore` trait 层提供可选的淘汰配置,上层组件按需设置:
|
||
|
||
```rust
|
||
pub struct EvictionConfig {
|
||
pub policy: EvictionPolicy,
|
||
pub check_interval: usize, // 每写入 N 条后检查一次淘汰条件
|
||
}
|
||
|
||
pub enum EvictionPolicy {
|
||
None, // 不淘汰(默认)
|
||
Ttl { ttl_secs: u64 }, // 超过存活时间淘汰
|
||
Capacity { max_items: usize },// 超过容量淘汰最旧
|
||
}
|
||
```
|
||
|
||
`InMemoryStore` 在 `save()` 后检查淘汰条件:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["save(item)"]
|
||
B["items.insert(id, item)"]
|
||
C{"writes_since_last_check >= check_interval?"}
|
||
D{"policy 类型"}
|
||
E["Ttl → items.retain(created_at > cutoff)"]
|
||
F["Capacity → 按 created_at 升序排列,截断到 max_items"]
|
||
G["None → 不淘汰"]
|
||
|
||
A --> B
|
||
B --> C
|
||
C -->|是| D
|
||
C -->|否| G
|
||
D -->|Ttl| E
|
||
D -->|Capacity| F
|
||
D -->|None| G
|
||
```
|
||
|
||
### 5.3 各组件淘汰策略
|
||
|
||
| 组件 | 推荐策略 | 理由 |
|
||
|------|---------|------|
|
||
| **ConversationMemory** | `Capacity { max_items }` | 对话是流式的,旧消息价值递减,达到上限后淘汰最旧的消息条目 |
|
||
| **MemoryStore**(通用) | `Ttl { ttl_secs }` | 通用存储由调用方按场景决定 |
|
||
| **KnowledgeStore** | `None`(默认不淘汰) | 知识是累积的,新增不淘汰旧 |
|
||
|
||
### 5.4 ConversationMemory 淘汰行为
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["add_message(msg)"]
|
||
B["store.save(msg)"]
|
||
C{"eviction.policy == Capacity?"}
|
||
D["history = store.list(session)"]
|
||
E{"while history.len() > max_items"}
|
||
F["oldest = history.remove(0) ← 删除最旧消息"]
|
||
G["store.delete(oldest.id)"]
|
||
H["maybe_compact() ← 复用 microcompact() 做内容压缩"]
|
||
I["return"]
|
||
|
||
A --> B
|
||
B --> C
|
||
C -->|是| D
|
||
D --> E
|
||
E -->|是| F
|
||
F --> G
|
||
G --> E
|
||
E -->|否| H
|
||
C -->|否| H
|
||
H --> I
|
||
```
|
||
|
||
两种机制分层:
|
||
- **淘汰(eviction)**:删除整条消息,控制条目总数上限
|
||
- **压缩(compaction)**:压缩剩余消息的 tool result 内容,节省 token
|
||
|
||
### 5.5 InMemoryStore 的淘汰实现
|
||
|
||
```rust
|
||
impl InMemoryStore {
|
||
pub fn with_eviction(config: EvictionConfig) -> Self { ... }
|
||
}
|
||
|
||
// 在 save() 内部:
|
||
async fn save(&self, item: MemoryItem) -> Result<(), MemoryError> {
|
||
self.items.lock().insert(item.id.clone(), item);
|
||
self.maybe_evict().await;
|
||
}
|
||
|
||
async fn maybe_evict(&self) {
|
||
match &self.eviction.policy {
|
||
EvictionPolicy::Ttl { ttl_secs } => {
|
||
let cutoff = Utc::now() - Duration::seconds(*ttl_secs as i64);
|
||
self.items.lock().retain(|_, v| v.created_at > cutoff);
|
||
}
|
||
EvictionPolicy::Capacity { max_items } => {
|
||
let mut items = self.items.lock();
|
||
if items.len() > *max_items {
|
||
let mut vec: Vec<_> = items.drain().collect();
|
||
vec.select_nth_unstable_by(
|
||
*max_items,
|
||
|a, b| b.1.created_at.cmp(&a.1.created_at),
|
||
);
|
||
vec.truncate(*max_items);
|
||
*items = vec.into_iter().collect();
|
||
}
|
||
}
|
||
EvictionPolicy::None => {}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 实现计划
|
||
|
||
### Step 1:基础类型 + MemoryStore(含淘汰机制)
|
||
|
||
**文件**:`src/memory.rs`、`src/memory/types.rs`、`src/memory/error.rs`、`src/memory/store.rs`
|
||
|
||
- 创建 `memory.rs` + `memory/` 目录
|
||
- 定义 `MemoryItem`、`MemoryFilter`、`MemoryStrategy` 类型
|
||
- 定义 `MemoryError` 枚举
|
||
- 定义 `MemoryStore` trait 与 `InMemoryStore` 实现
|
||
- 定义 `EvictionConfig`、`EvictionPolicy`(None / Ttl / Capacity)
|
||
- `InMemoryStore.save()` 内部实现淘汰检查
|
||
- 单元测试:TTL 过期淘汰、容量上限淘汰、不淘汰(None)
|
||
- 验收:`cargo build` + `cargo test` 通过
|
||
|
||
**依赖**:chrono(日期时间)、serde(序列化)
|
||
|
||
### Step 2:ConversationMemory(含消息淘汰)
|
||
|
||
**文件**:`src/memory/conversation.rs`
|
||
|
||
- 定义 `ConversationMemoryConfig`、`MemoryStrategy`
|
||
- 实现 `ConversationMemory`
|
||
- `add_message()` → 写入 MemoryStore → 触发容量淘汰(删除最旧消息)
|
||
- 复用 `llm::compact` 的 `CompactConfig` 和 `microcompact()` 做内容压缩
|
||
- 单元测试:sliding window 消息淘汰、tool result 内容压缩、full 模式、空 session
|
||
- 验收:`cargo build` + `cargo test` 通过
|
||
|
||
**依赖**:MemoryStore(含 EvictionConfig)+ llm::compact
|
||
|
||
### Step 3:KnowledgeStore
|
||
|
||
**文件**:`src/memory/knowledge.rs`
|
||
|
||
- 定义 `KnowledgePage`、`PageIndexEntry` 类型
|
||
- 实现 `KnowledgeStore` 具体 struct(非 trait)
|
||
- 内部使用 `Arc<dyn MemoryStore>` 存储数据
|
||
- index 自动维护(add/update/delete 时同步)
|
||
- search 基于标题/摘要/标签的关键词匹配
|
||
- 单元测试:页面 CRUD、index 一致性、搜索
|
||
- 验收:`cargo build` + `cargo test` 通过
|
||
|
||
### Step 4:MemoryRetriever + 模块整合
|
||
|
||
**文件**:`src/memory/retriever.rs`、`src/memory.rs`
|
||
|
||
- 实现内存检索器 `MemoryRetriever`
|
||
- 内部关键词提取:split + 过滤停用词
|
||
- TextOverlap 评分:基于 Dice 系数计算 query 与页面的文本重叠度
|
||
- 阈值过滤 → 排序 → 截取 top-N
|
||
- 在 `memory.rs` 中用 `pub use` 重导出所有公共类型
|
||
- 在 `src/lib.rs` 中声明 `pub mod memory`
|
||
- 单元测试:关键词提取、TextOverlap 评分正确性、阈值过滤、排序正确性
|
||
- 集成测试:端到端检索流程
|
||
- 验收:`cargo build` + `cargo test` 通过
|
||
|
||
---
|
||
|
||
## 7. 风险评估
|
||
|
||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||
|------|------|------|---------|
|
||
| KnowledgeStore 的 keyword 检索在大规模下效率低 | 中 | 中 | MemoryStore 实现可替换——下游可使用 SQLite FTS 等更高效的后端 |
|
||
| ConversationMemory 与 compact 耦合引入循环依赖 | 低 | 高 | 仅引用 `CompactConfig`(纯数据结构)和 `microcompact()`(纯函数),不引用 cycle.rs |
|
||
| chrono 增加依赖体积 | 低 | 低 | chrono 已是 Rust 生态标准,且仅用于时间戳 |
|
||
| Phase 4 集成时发现 Memory 设计不合理 | 低 | 高 | 按最小可行接口设计,预留扩展空间 |
|
||
|
||
---
|
||
|
||
## 8. 验收标准
|
||
|
||
- [ ] `MemoryStore` trait + `InMemoryStore` 通过单元测试
|
||
- [ ] `EvictionConfig` 支持 None / Ttl / Capacity 三种策略
|
||
- [ ] `InMemoryStore` 在 save() 后正确执行 TTL 淘汰和容量淘汰
|
||
- [ ] `ConversationMemory` 支持 sliding window 和 full 两种策略
|
||
- [ ] `ConversationMemory` sliding window 模式下达到上限后删除最旧消息条目
|
||
- [ ] `ConversationMemory` 正确复用 `llm::compact` 的压缩逻辑
|
||
- [ ] `KnowledgeStore` 支持页面 CRUD 和 index 维护
|
||
- [ ] `MemoryRetriever` 支持基于 TextOverlap 的知识检索
|
||
- [ ] 无 embedding 相关依赖
|
||
- [ ] 模块结构:`memory.rs` + `memory/` 目录 + `pub use` 重导出
|
||
- [ ] `MemoryError` 枚举完善,支持 `is_recoverable()`
|
||
- [ ] 所有公开 API 有文档注释(`///`)
|
||
- [ ] `cargo build` 和 `cargo test` 通过
|
||
- [ ] 单个文件不超过 300 行
|
||
|
||
---
|
||
|
||
## 附录:与 Karpathy's LLM Wiki 的关系
|
||
|
||
本方案受 Karpathy's LLM Wiki 模式启发,但做了一些调整以适应 Agent 核心库的定位:
|
||
|
||
| Karpathy LLM Wiki | AG Core Memory System | 差异原因 |
|
||
|-------------------|----------------------|---------|
|
||
| 三层:Raw → Wiki → Schema | 三组件:MemoryStore → ConversationMemory + KnowledgeStore + MemoryRetriever | Agent 场景需要区分对话记忆和知识记忆 |
|
||
| index.md + log.md | PageIndexEntry(同 index.md)+ 无 log(Phase 4 Agent 负责) | 日志是工作流层职责,非存储层 |
|
||
| LLM Agent 全权维护 | KnowledgeStore 提供数据接口,Phase 4 Agent 编排工作流 | core 只提供存储能力,不编排 |
|
||
| 文件系统为后端 | MemoryStore trait 抽象后端 | 可插拔设计需要 trait 抽象 |
|
||
| 基于文件系统搜索 | index + keyword 检索 | 文件系统搜索不适合所有后端 |
|