diff --git a/docs/6-memory-system.md b/docs/6-memory-system.md index 297f924..6e26d0d 100644 --- a/docs/6-memory-system.md +++ b/docs/6-memory-system.md @@ -17,13 +17,12 @@ AG Core 已完成 Phase 0(LLM 调用周期)、Phase 1(提示词工程) - **对话记忆(ConversationMemory)** — 管理多轮对话消息历史,支持 sliding window / 全量策略 - **知识库(KnowledgeStore)** — 基于 LLM Wiki 模式的结构化知识管理,Agent 可自主编译和维护知识页面 -- **知识图谱(KnowledgeGraph)** — 实体-关系图存储,支持关联检索与图遍历 -- **检索器(MemoryRetriever)** — 组合多种检索策略,提供统一的记忆查找入口 +- **检索器(MemoryRetriever)** — 单通道关键词检索,提供统一的记忆查找入口 ### 1.3 设计原则 - **不引入 embedding 依赖** — 采用 Karpathy's LLM Wiki 模式的 index + keyword 检索,替代传统向量检索 -- **trait + 轻量默认实现** — 所有抽象接口均提供纯内存默认实现(InMemory),满足原型和测试需求 +- **trait + 轻量默认实现** — 存储抽象接口提供纯内存默认实现(InMemoryStore),满足原型和测试需求 - **模块间松耦合** — 记忆系统与 LlmCycle 的集成推迟到 Phase 4 Agent Runtime,Phase 3 只定义接口和数据操作 --- @@ -34,19 +33,14 @@ AG Core 已完成 Phase 0(LLM 调用周期)、Phase 1(提示词工程) | ID | 需求 | 优先级 | 说明 | |----|------|--------|------| -| F1 | 存储任意键值对 | P0 | MemoryStore 提供通用的 save/get/delete/list | -| F2 | 对话消息管理 | P0 | 按 session 管理多轮对话历史,支持滑动窗口裁剪 | -| F3 | 知识页面管理 | P1 | 创建/更新/删除/检索知识页面(标题+摘要+内容+标签+交叉引用) | -| F4 | 知识页面索引 | P1 | 维护可遍历的内容目录(index) | -| F5 | 知识页面关键词检索 | P1 | 基于标题/摘要/标签的关键词匹配 | -| F6 | 实体-关系图存储 | P1 | 实体节点增删查 + 关系边管理 + 图遍历 | -| F7 | 组合检索 | P2 | 综合 KnowledgeStore + KnowledgeGraph 的多策略检索 | -| F8 | 可插拔后端 | P0 | 所有存储抽象提供 trait,下游可实现自定义后端(文件/SQLite/图数据库等) | -| F9 | 记忆淘汰 | P1 | 支持 TTL 过期淘汰、容量上限淘汰、Hybrid 组合策略 | -| F10 | 消息条目级淘汰 | P1 | ConversationMemory 达到上限后删除最旧消息,而非仅压缩内容 | -| F11 | 基于召回价值的淘汰 | P2 | 根据召回频率、匹配评分、时效性综合计算"记忆价值",淘汰价值最低的记忆 | -| F12 | 召回统计记录 | P2 | MemoryStore 记录每次召回事件(recall_count + score),供淘汰策略使用 | -| F13 | 标签复用查询 | P1 | KnowledgeGraph 提供 find_tags() 接口,供调用方在创建实体时复用已有标签,避免同义标签膨胀 | +| 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 非功能需求 @@ -56,7 +50,7 @@ AG Core 已完成 Phase 0(LLM 调用周期)、Phase 1(提示词工程) | NF2 | 错误体系完善 | MemoryError 枚举,支持 is_recoverable() 分类 | | NF3 | 线程安全 | 所有存储实现满足 Send + Sync | | NF4 | 异步 API | 所有 IO 操作为 async | -| NF5 | 模块化 | 各组件独立可替换(通过 trait) | +| NF5 | 模块化 | 各组件独立可替换 | --- @@ -64,18 +58,24 @@ AG Core 已完成 Phase 0(LLM 调用周期)、Phase 1(提示词工程) ### 3.1 总体架构 -``` -┌─────────────────────────────────────────────────────────────┐ -│ MemoryRetriever │ -│ (组合检索:index + keyword + graph) │ -├──────────────────┬──────────────────┬───────────────────────┤ -│ ConversationMemory│ KnowledgeStore │ KnowledgeGraph │ -│ (对话消息管理) │ (知识页面管理) │ (实体-关系图) │ -├──────────────────┴──────────────────┴───────────────────────┤ -│ MemoryStore │ -│ (底层存储抽象:save/get/delete/list) │ -│ InMemoryStore (默认) │ -└─────────────────────────────────────────────────────────────┘ +```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 模块结构 @@ -86,9 +86,8 @@ src/ memory/ store.rs # MemoryStore trait + InMemoryStore conversation.rs # ConversationMemory(对话管理) - knowledge.rs # KnowledgeStore trait + InMemoryKnowledgeStore - graph.rs # KnowledgeGraph trait + InMemoryGraph - retriever.rs # MemoryRetriever(组合检索器) + knowledge.rs # KnowledgeStore(具体 struct) + retriever.rs # MemoryRetriever(单通道检索) error.rs # MemoryError types.rs # 核心数据类型 ``` @@ -138,383 +137,73 @@ pub struct ConversationMemoryConfig { - sliding window 模式:超出 `max_turns` 后,调用 `llm::compact::microcompact()` 裁剪 - 复用现有 `CompactConfig`(context_window, reserved_tokens, keep_recent) -#### KnowledgeStore — 知识库 +#### KnowledgeStore — 具体 struct ```rust -#[async_trait] -pub trait KnowledgeStore: Send + Sync { - async fn add_page(&self, page: KnowledgePage) -> Result<(), MemoryError>; - async fn get_page(&self, id: &str) -> Result, MemoryError>; - async fn update_page(&self, page: KnowledgePage) -> Result<(), MemoryError>; - async fn delete_page(&self, id: &str) -> Result<(), MemoryError>; - async fn search(&self, query: &str) -> Result, MemoryError>; - async fn get_index(&self) -> Result, MemoryError>; -} -``` - -- `add_page` 自动更新 index -- `search` 基于页面标题/摘要/标签的关键词匹配(不依赖 embedding) -- `get_index` 返回所有页面的摘要目录(类似 LLM Wiki 的 index.md) - -#### InMemoryKnowledgeStore — 知识库默认实现 - -```rust -pub struct InMemoryKnowledgeStore { - pages: Mutex>, +pub struct KnowledgeStore { + store: Arc, index: Mutex>, } -``` -- `search()` 使用简单的字符串包含匹配(可替换为更复杂的检索算法) - -#### KnowledgeGraph — 知识图谱 - -图谱检索的核心流程:**输入 → 关键词提取 → 实体标签匹配 → 图遍历** - -```rust -#[async_trait] -pub trait KnowledgeGraph: Send + Sync { - // 实体管理 - async fn add_entity(&self, entity: GraphEntity) -> Result<(), MemoryError>; - async fn get_entity(&self, id: &str) -> Result, MemoryError>; - async fn remove_entity(&self, id: &str) -> Result<(), MemoryError>; - - // 关系管理 - async fn add_relation(&self, relation: GraphRelation) -> Result<(), MemoryError>; - async fn remove_relation(&self, source_id: &str, target_id: &str, relation_type: &str) -> Result<(), MemoryError>; - async fn get_related(&self, entity_id: &str, depth: usize) -> Result, MemoryError>; - - // 检索 - async fn find_by_keywords(&self, keywords: &[String]) -> Result, MemoryError>; - - // 标签管理 - async fn find_tags(&self, prefix: &str) -> Result, MemoryError>; - async fn entity_count_by_tag(&self, tag: &str) -> Result; - async fn set_entity_tags(&self, entity_id: &str, tags: Vec) -> Result<(), MemoryError>; - fn tag_constraints(&self) -> TagConstraints; +impl KnowledgeStore { + pub fn new(store: Arc) -> Self { ... } + pub async fn add_page(&self, page: KnowledgePage) -> Result<(), MemoryError> { ... } + pub async fn get_page(&self, id: &str) -> Result, 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, MemoryError> { ... } + pub async fn get_index(&self) -> Result, MemoryError> { ... } } ``` -- `find_by_keywords()` — 用关键词匹配实体的 `name`(前缀匹配)和 `tags`(完全匹配),不模糊搜索全字段 -- `get_related()` — 找到匹配实体后,按 `depth` 遍历关联实体,同时返回关联关系 -- `find_tags()` — 前缀匹配已有标签,供 Phase 4 Agent 做标签复用决策 -- `entity_count_by_tag()` — 查看某标签的实体关联数,判断标签的通用程度 -- `set_entity_tags()` — 替换实体的全部标签,调用方按关联度降序传入,内部自动截断到 `max_tags_per_entity` +- `search` 使用简单的字符串包含匹配标题/摘要/标签 +- index 在 add/update/delete 时自动维护 -#### 标签规范与生成策略 - -标签是连接 `KnowledgeStore`(内容)和 `KnowledgeGraph`(实体)的检索桥梁。两条检索通道共享同一组关键词,通过标签匹配实现交叉发现。 - -##### 标签复用原则 - -标签不应随意增长。**优先复用已有标签,确无合适标签时才创建新标签**。这保证: - -- 同一概念永远使用同一标签(`"state-graph"` 不会出现 `"state-graph"` + `"state-graph-framework"` 并存) -- 共享标签的实体自然形成概念关联,增强图检索的连通性 -- 标签空间收敛,检索效率不随实体数量增长而退化 - -##### 标签注册与复用流程 - -KnowledgeGraph 提供标签查询接口供调用方(Phase 4 Agent)做复用决策: - -```rust -#[async_trait] -pub trait KnowledgeGraph: Send + Sync { - // ... 实体/关系管理方法 ... - - /// 查询已有标签(前缀匹配),用于标签复用决策 - async fn find_tags(&self, prefix: &str) -> Result, MemoryError>; - - /// 查询某标签关联的实体数量 - async fn entity_count_by_tag(&self, tag: &str) -> Result; -} -``` - -Phase 4 Agent 在 Ingest 时遵从这个流程创建实体标签: - -``` -LLM 提取候选标签 → ["LangGraph", "StateGraph", "state-machine", "graph-framework"] - │ - ┌─────────────────────┼──────────────────────┐ - │ for each candidate: │ │ - │ │ │ - │ graph.find_tags(candidate.lowercase()) │ - │ │ │ - │ ├─ 命中已有标签 → 复用(用已有标签字符串) │ - │ │ "state-machine" → 已有 "state-machine" │ - │ │ 用 "state-machine" 而非创建新标签 │ - │ │ │ - │ └─ 无匹配 → 注册新标签 │ - │ "graph-framework" → 全新标签,注册 │ - │ │ - └─────────────────────────────────────────────┘ - -最终 entity.tags = ["langgraph", "state-graph", "state-machine", "graph-framework"] - ↑ 复用了已有标签 -``` - -##### 标签容量与精炼 - -每个实体的标签数量有上限,不能无限增长。超出上限时,保留关联度最高的标签。 - -```rust -pub struct GraphEntity { - pub id: String, - pub name: String, - pub entity_type: String, - pub description: String, - pub tags: Vec, // 检索标签,按关联度降序排列(索引越小越关键) -} - -pub struct TagConstraints { - pub max_tags_per_entity: usize, // 每个实体最多标签数,默认 8 -} -``` - -KnowledgeGraph 提供标签集替换接口,由调用方(Phase 4 Agent)按关联度排序后写入,内部自动截断: - -```rust -#[async_trait] -pub trait KnowledgeGraph: Send + Sync { - // ... 其他方法 ... - - /// 替换实体的全部标签 - /// 调用方确保 tags 已按关联度降序排列 - /// 内部自动截断到 max_tags_per_entity - async fn set_entity_tags(&self, entity_id: &str, tags: Vec) -> Result<(), MemoryError>; - - /// 获取标签容量约束 - fn tag_constraints(&self) -> TagConstraints; -} -``` - -标签精炼流程(Phase 4 Agent 负责编排): - -``` -LLM 从内容中提取候选标签(可能有 15-20 个) - │ - ├─ 1. 评估每个标签与实体的关联度 - │ → "langgraph": 0.95 | "graph-framework": 0.3 | "computer": 0.1 - │ - ├─ 2. 按关联度降序排列 - │ → ["langgraph", "state-graph", "state-machine", "graph", ...] - │ - ├─ 3. 复用已有标签(find_tags) - │ → 将同义候选替换为已有标签 - │ - ├─ 4. 保留 top-8(受 max_tags_per_entity 约束) - │ → ["langgraph", "state-graph", "state-machine", "graph", - │ "agent", "workflow", "llm", "orchestration"] - │ - └─ 5. set_entity_tags(entity_id, top_8) - → KnowledgeGraph 内部截断到 max_tags_per_entity -``` - -##### 标签规范 - -| 规则 | 说明 | 示例 | -|------|------|------| -| 原子性 | 每个标签是一个独立的关键词,不包含空格或标点 | `"state-graph"` ✅ / `"State Graph framework"` ❌ | -| 小写化 | 统一小写存储,匹配时大小写不敏感 | `"langgraph"` | -| 单数优先 | 优先使用单数形式 | `"agent"` 而非 `"agents"` | -| 领域受限 | 3-8 个标签/实体,聚焦可检索性而非描述性 | `["langgraph", "state-graph", "graph", "state-machine"]` | - -**匹配行为(find_by_keywords):** - -| 字段 | 匹配方式 | 说明 | -|------|---------|------| -| `name` | **前缀匹配** | `"lang"` 匹配 `"LangGraph"`、`"LangChain"`,支持大小写不敏感 | -| `tags` | **完全匹配** | `"state-graph"` 只匹配 `"state-graph"`,不匹配 `"state"` | -| `entity_type` | 完全匹配(精确值) | `"framework"` 匹配 `"framework"` | - -**标签生成方式(Phase 4 Agent 层面):** - -``` -Ingest 新内容 - │ - ├─ LLM 提取标签(主要方式) - │ → 分析内容 → 提取 3-8 个关键概念作为标签 - │ → 同时写入 KnowledgePage.tags 和 GraphEntity.tags - │ - ├─ 规则提取(辅助方式) - │ → 从 entity.name 中按 CamelCase / kebab-case 拆分 - │ → "StateGraph" → ["state", "graph", "state-graph"] - │ - └─ 标签继承(关联同步) - → KnowledgePage 的标签自动同步到关联的 GraphEntity - → 页面标签变更时,同步更新关联实体的标签 -``` - -**标签在检索中的作用:** - -``` -检索关键词: ["langgraph", "state-graph"] - │ - ├─ KnowledgeStore 匹配: 页面 title(前缀) / tags(完全) / summary(包含) - │ → 命中 KnowledgePage { tags: ["langgraph", "graph", "state-machine"] } ← tags 完全匹配 - │ → 命中 KnowledgePage { title: "LangGraph StateGraph Guide" } ← title 前缀匹配 - │ - └─ KnowledgeGraph 匹配: 实体 name(前缀) / tags(完全) - → 命中 GraphEntity { name: "LangGraph", tags: ["langgraph", "agent-framework"] } ← 都匹配 - → 命中 GraphEntity { name: "StateGraph", tags: ["langgraph", "state-graph"] } ← tags 完全匹配 - → get_related("StateGraph") → 遍历关联实体 "StateMachine", "Graph" 等 -``` - -`tags` 定位为**精准的检索锚点**,`name` 作为**松散的检索入口**,两者配合确保召回既有广度又有精度。 - -#### InMemoryGraph — 知识图谱默认实现 - -```rust -pub struct InMemoryGraph { - entities: Mutex>, - relations: Mutex>, -} -``` - -- 图遍历使用 BFS/DFS,`depth` 控制搜索深度 -- 适合原型和小规模图 - -#### MemoryRetriever — 组合检索器(含关联度评分) - -两种检索通道会产出大量结果,因此需要**关联度评分机制**来排序和过滤。 - -##### ScoringStrategy — 评分策略 - -不依赖 embedding,基于可计算的文本和图结构特征: - -| 策略 | 基准 | 计算方式 | 适用场景 | -|------|------|---------|---------| -| `TextOverlap` | **原始 query** | query 与召回结果的文本重叠度(n-gram Jaccard / Dice 系数),标题 > 摘要 > 内容 | 默认推荐,衡量内容契合度 | -| `GraphDistance` | 实体关系 | 实体间最短路径长度的倒数 → `1 / (1 + distance)` | 图遍历结果排序 | -| `TemporalWeight` | 时间 | 时间衰减:`1.0 / (1 + days_since_update)` | 时间敏感的知识(如新闻) | -| `ReferenceCount` | 页面权威性 | 被其他页面引用的次数归一化(简化版 PageRank) | 权威页面自然排在前面 | -| `Hybrid` | 综合 | 以上加权组合:`w1*overlap + w2*graph + w3*temporal + w4*reference` | 需要多维度权衡的场景 | - -##### KeywordExtractor — 关键词提取 - -检索前先从输入中提取关键词,作为两个通道共享的检索入口: - -```rust -pub trait KeywordExtractor: Send + Sync { - fn extract(&self, query: &str) -> Vec; -} - -// 默认实现:按非字母数字字符分割,过滤停用词和单字符词 -pub struct SimpleKeywordExtractor { - stop_words: HashSet, -} -``` - -##### RetrievalResult — 统一召回结果 +#### MemoryRetriever — 简化版检索器 ```rust pub struct MemoryRetriever { - knowledge_store: Arc, - knowledge_graph: Arc, - keyword_extractor: Arc, + knowledge_store: KnowledgeStore, config: RetrieverConfig, } pub struct RetrieverConfig { - pub scoring: ScoringStrategy, - pub max_results: usize, // 最大返回条数,默认 20 - pub min_score: f32, // 最低分数阈值 [0.0, 1.0],默认 0.1 - pub graph_depth: usize, // 图遍历深度,默认 2 - pub weights: ScoreWeights, // Hybrid 策略的权重分配 -} - -pub struct ScoreWeights { - pub overlap: f32, // 默认 0.5 — 文本重叠度,以原始 query 为基准 - pub graph: f32, // 默认 0.2 — 图距离 - pub temporal: f32, // 默认 0.1 — 时间衰减 - pub reference: f32, // 默认 0.2 — 引用计数 -} - -pub enum ScoringStrategy { - TextOverlap, // 以原始 query 为准绳的文本重叠度(默认) - GraphDistance, - TemporalWeight, - ReferenceCount, - Hybrid(ScoreWeights), + pub max_results: usize, // 默认 20 + pub min_score: f32, // 默认 0.1 } pub struct RetrievalResult { pub items: Vec, - pub total_found: usize, pub query: String, } pub struct ScoredItem { - pub source: RetrievalSource, // KnowledgeStore | KnowledgeGraph - pub score: f32, // [0.0, 1.0] 最终评分 - pub breakdown: ScoreBreakdown, // 各维度评分明细(可选) -} - -pub enum RetrievalSource { - KnowledgePage(KnowledgePage), - GraphEntity(GraphEntity), -} - -pub struct ScoreBreakdown { - pub overlap_score: f32, // 与原始 query 的文本重叠度 - pub graph_score: f32, // 图距离 - pub temporal_score: f32, // 时间衰减 - pub reference_score: f32, // 引用计数 -} - -pub enum RetrievalStrategy { - Hybrid, // 结合所有通道 + 评分排序(默认) - KnowledgeOnly, // 仅 KnowledgeStore - GraphOnly, // 仅 KnowledgeGraph + pub page: KnowledgePage, + pub score: f32, // TextOverlap 评分 [0.0, 1.0] } ``` 检索流程: -``` -输入: "LangGraph 的 StateGraph 状态管理原理?" - │ - ├─ 1. 关键词提取 (KeywordExtractor) - │ → ["LangGraph", "StateGraph", "状态管理"] - │ - ├─ 2. 并行召回 ──────────────────────────────────────┐ - │ │ │ - │ ├─ KnowledgeStore.find_by_keywords(keywords) │ - │ │ → 匹配页面标题/摘要/标签 │ - │ │ → 返回 KnowledgePage 列表 │ - │ │ │ - │ └─ KnowledgeGraph.find_by_keywords(keywords) ──┐ │ - │ → 匹配实体 name / tags │ │ - │ → 命中: "LangGraph" (framework), │ │ - │ "StateGraph" (concept) │ │ - │ → get_related("LangGraph", depth=2) │ │ - │ → 返回匹配实体 + 关联实体列表 │ │ - │ │ │ - ├─ 3. 逐条评分 (ScoringStrategy) ←───────────────────┘ │ - │ // 基准:原始 query,而非中间关键词 │ │ - │ → overlap_score: 结果内容与原始 query 的文本重叠度 │ │ - │ (n-gram Jaccard 系数,标题权重大于正文) │ │ - │ → graph_score: 实体在图中的距离(如有) │ │ - │ → temporal_score: 时间衰减 │ │ - │ → reference_score: 引用次数 │ │ - │ → score = Σ(weight_i * score_i) │ │ - │ │ - ├─ 4. 过滤: score < min_score → 丢弃 │ - ├─ 5. 排序: score 降序 → 截取 max_results 条 │ - └─ 6. 返回: RetrievalResult │ +```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 ``` -流程说明: -1. **关键词提取** — 先用 `KeywordExtractor` 从输入中提取候选关键词,两个通道共享 -2. **KnowledgeStore 检索** — 用关键词匹配页面的 `title`(精准/前缀)、`tags`(完全)、`summary`(包含) -3. **KnowledgeGraph 检索** — 用关键词匹配实体 `name` 和 `tags`,命中后按 `depth` 图遍历获取关联实体 -4. **评分** — 对每条召回结果计算 `score ∈ [0.0, 1.0]` -5. **过滤排序** — 丢弃低分、按分排序、截取 top N - -这样设计的好处: -- 关键词生成在两个通道间共享,避免重复计算 -- 图谱检索从"模糊搜索"变为"标签精确匹配 → 图遍历",更精准 -- 每个步骤职责单一,可独立替换实现 +关键词提取在 MemoryRetriever 内部简单实现:按空格/标点分割 → 过滤单字符和停用词 → 返回关键词列表。TextOverlap 计算 query 与页面标题/摘要/内容的 n-gram 重叠度(基于 Dice 系数)。 ### 3.4 核心数据类型 @@ -538,7 +227,7 @@ pub struct KnowledgePage { pub summary: String, pub content: String, pub tags: Vec, - pub references: Vec, // 交叉引用的页面 ID 列表 + pub references: Vec, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } @@ -551,78 +240,10 @@ pub struct PageIndexEntry { pub updated_at: chrono::DateTime, } -pub struct GraphEntity { - pub id: String, - pub name: String, - pub entity_type: String, // "person" | "concept" | "project" | ... - pub description: String, - pub tags: Vec, // 检索标签(全小写,原子词,按关联度降序排列) -} - -pub struct GraphRelation { - pub source_id: String, - pub target_id: String, - pub relation_type: String, // "works_on" | "part_of" | "related_to" | ... - pub weight: f32, // 关系强度 [0.0, 1.0] -} - pub enum MemoryStrategy { SlidingWindow, Full, } - -pub enum ScoringStrategy { - KeywordDensity, - GraphDistance, - TemporalWeight, - ReferenceCount, - Hybrid(ScoreWeights), -} - -pub struct ScoreWeights { - pub keyword: f32, // 默认 0.4 - pub graph: f32, // 默认 0.3 - pub temporal: f32, // 默认 0.1 - pub reference: f32, // 默认 0.2 -} - -pub struct RetrieverConfig { - pub scoring: ScoringStrategy, - pub max_results: usize, - pub min_score: f32, - pub graph_depth: usize, - pub weights: ScoreWeights, -} - -pub struct RetrievalResult { - pub items: Vec, - pub total_found: usize, - pub query: String, -} - -pub struct ScoredItem { - pub source: RetrievalSource, - pub score: f32, - pub breakdown: ScoreBreakdown, -} - -pub struct ScoreBreakdown { - pub keyword_score: f32, - pub graph_score: f32, - pub temporal_score: f32, - pub reference_score: f32, -} - -pub enum RetrievalSource { - KnowledgePage(KnowledgePage), - GraphEntity(GraphEntity), -} - -pub enum RetrievalStrategy { - Hybrid, - KnowledgeOnly, - GraphOnly, -} ``` ### 3.5 错误类型 @@ -645,9 +266,6 @@ pub enum MemoryError { #[error("Invalid input: {0}")] InvalidInput(String), - #[error("Graph error: {0}")] - GraphError(String), - #[error("Retrieval error: {0}")] RetrievalError(String), } @@ -661,25 +279,32 @@ impl MemoryError { ### 3.6 ConversationMemory 与 compact 模块的集成 -``` -ConversationMemory (src/memory/conversation.rs) - │ - │ add_message() - │ ├─ store.save(message) ← 写到底层存储 - │ ├─ if sliding_window && over_limit: - │ │ messages = store.list(session) - │ │ compact_config.should_compact() ← 复用 CompactConfig - │ │ microcompact(&mut messages) ← 复用 microcompact() - │ │ store.save(pruned) ← 写回 - │ └─ return - │ - │ get_history() - │ └─ store.list(session) - │ - └─ imports: llm::compact::{CompactConfig, microcompact, should_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}"] +``` --- @@ -687,37 +312,35 @@ ConversationMemory (src/memory/conversation.rs) ### 4.1 存储层次 -``` -┌─────────────────────────────────────────────────┐ -│ MemoryRetriever │ -│ (检索 + 评分,无状态) │ -├─────────────────────────────────────────────────┤ -│ MemoryStore / KnowledgeStore / KnowledgeGraph │ -│ (存储抽象接口,不感知存储介质) │ -├──────────────────┬──────────────────────────────┤ -│ InMemoryStore │ 下游自定义实现 │ -│ (HashMap) │ (FileStore / SqliteStore │ -│ 进程内 volatile│ / RedisStore / ... ) │ -│ 测试/原型适用 │ 生产环境适用 │ -└──────────────────┴──────────────────────────────┘ +```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` | 进程堆内存 | ❌ | 随进程销毁 | -| `InMemoryKnowledgeStore` | `HashMap` + `Vec` | 进程堆内存 | ❌ | 随进程销毁 | -| `InMemoryGraph` | `HashMap` + `Vec` | 进程堆内存 | ❌ | 随进程销毁 | -| `InMemoryStore.recall_stats` | `HashMap` | 进程堆内存 | ❌ | 随进程销毁 | +| `KnowledgeStore` | 基于 `InMemoryStore` + `Vec` | 进程堆内存 | ❌ | 随进程销毁 | **适用场景:** - 单元测试和集成测试 - 本地快速原型开发 -- 单次会话的临时 Agent(不期望跨会话持久化) -- Phase 4 开发阶段的验证 +- 单次会话的临时 Agent **不适合场景:** - 生产部署 @@ -726,32 +349,21 @@ ConversationMemory (src/memory/conversation.rs) ### 4.3 持久化存储方案(下游实现) -agcore 核心库**不内置**持久化实现,用户通过实现 `MemoryStore` / `KnowledgeStore` / `KnowledgeGraph` trait 对接所需后端: +agcore 核心库**不内置**持久化实现,用户通过实现 `MemoryStore` trait 对接所需后端: | 后端 | 实现建议 | 适用场景 | 复杂度 | |------|---------|---------|--------| | **JSON 文件** | MemoryStore trait → 序列化为单文件 JSON | 单机、轻量持久化 | 低 | -| **SQLite** | MemoryStore + KnowledgeStore → 关系表 | 单机、中小规模 | 中 | -| **PostgreSQL** | MemoryStore + KnowledgeStore → 关系表 | 多进程共享、中等规模 | 中 | +| **SQLite** | MemoryStore → 关系表 | 单机、中小规模 | 中 | +| **PostgreSQL** | MemoryStore → 关系表 | 多进程共享、中等规模 | 中 | | **Redis** | MemoryStore → Hash/JSON 类型 | 高速缓存、会话共享 | 低 | -| **Neo4j** | KnowledgeGraph → 图数据库 | 大规模图谱检索 | 高 | - -**示例:FileStore 结构示意(由用户自行实现)** - -``` -~/.agcore/memory/ - store.json # MemoryStore 数据 - knowledge.json # KnowledgeStore 数据 - graph_entities.json # KnowledgeGraph 实体 - graph_relations.json # KnowledgeGraph 关系 -``` ### 4.4 对下游实现的约束 -MemoryStore / KnowledgeStore / KnowledgeGraph 三个 trait 对持久化实现没有任何特殊约束: +`MemoryStore` trait 对持久化实现无特殊约束: - 方法签名不涉及文件路径、连接字符串等存储细节 -- 所有方法均为 `async`,持久化实现可以自由选择同步(`spawn_blocking`)或异步 driver -- 初始化参数在具体实现的构造函数中注入(如 `SqliteStore::new("path/to/db").await`) +- 所有方法均为 `async`,持久化实现可自由选择同步(`spawn_blocking`)或异步 driver +- 初始化参数在具体实现的构造函数中注入 ### 4.5 序列化 @@ -769,11 +381,13 @@ pub struct KnowledgePage { ... } --- -### 4.1 问题 +## 5. 淘汰策略 -所有存储组件(MemoryStore、KnowledgeStore、KnowledgeGraph)如果不设上限,会随运行时间无限增长。ConversationMemory 当前的 sliding window 只做 tool result 压缩,不删除消息条目。 +### 5.1 问题 -### 4.2 淘汰策略 +所有存储组件如果不设上限,会随运行时间无限增长。ConversationMemory 当前的 sliding window 只做 tool result 压缩,不删除消息条目。 + +### 5.2 淘汰策略 在 `MemoryStore` trait 层提供可选的淘汰配置,上层组件按需设置: @@ -784,160 +398,72 @@ pub struct EvictionConfig { } pub enum EvictionPolicy { - None, // 不淘汰(默认,KnowledgeStore/KnowledgeGraph 适用) - Ttl { ttl_secs: u64 }, // 超过存活时间淘汰(MemoryStore 通用) - Capacity { max_items: usize }, // 超过容量淘汰最旧(ConversationMemory 适用) - RecallBased { - max_items: usize, - recall_weight: f32, // 召回频率权重,默认 0.3 - score_weight: f32, // 平均匹配评分权重,默认 0.5 - recency_weight: f32, // 最近召回时间权重,默认 0.2 - }, - Hybrid { - ttl_secs: Option, - max_items: Option, - recall: Option, - }, + None, // 不淘汰(默认) + Ttl { ttl_secs: u64 }, // 超过存活时间淘汰 + Capacity { max_items: usize },// 超过容量淘汰最旧 } ``` -### 4.3 RecallBased 淘汰策略 +`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 → 不淘汰"] -#### 记忆价值评分 - -```rust -pub struct RecallStats { - pub recall_count: u64, // 累计被召回的次数 - pub total_score: f64, // 累计评分(平均分 = total_score / recall_count) - pub last_recall_at: i64, // 最后一次被召回的时间戳(秒) -} - -// 记忆价值 = recall_freq * recall_weight + avg_score * score_weight + recency * recency_weight -impl RecallStats { - pub fn value(&self, now: i64, config: &RecallBasedConfig) -> f64 { - let recency = 1.0 / (1.0 + (now - self.last_recall_at) as f64 / 86400.0); - let freq = (self.recall_count as f64).ln_1p(); - let avg = if self.recall_count > 0 { - self.total_score / self.recall_count as f64 - } else { - 0.0 - }; - freq * config.recall_weight as f64 - + avg * config.score_weight as f64 - + recency * config.recency_weight as f64 - } -} + A --> B + B --> C + C -->|是| D + C -->|否| G + D -->|Ttl| E + D -->|Capacity| F + D -->|None| G ``` -淘汰时按 `value()` 升序排列,淘汰价值最低的记忆。 - -#### 召回统计的更新时机 - -在 `MemoryStore` trait 中增加可选方法 `record_recall()`: - -```rust -#[async_trait] -pub trait MemoryStore: Send + Sync { - async fn save(&self, item: MemoryItem) -> Result<(), MemoryError>; - async fn get(&self, id: &str) -> Result, MemoryError>; - async fn delete(&self, id: &str) -> Result<(), MemoryError>; - async fn list(&self, filter: &MemoryFilter) -> Result, MemoryError>; - - /// 记录一次召回事件(可选,仅 RecallBased 淘汰策略需要) - async fn record_recall(&self, id: &str, score: f32) -> Result<(), MemoryError> { - // 默认空实现,不强制实现 - Ok(()) - } -} -``` - -#### 召回统计的存储 - -`InMemoryStore` 内部额外维护一个 `HashMap`: - -```rust -pub struct InMemoryStore { - items: Mutex>, - recall_stats: Mutex>, // 召回统计 - eviction: EvictionConfig, - created_at: Mutex>, // 写入时间(用于 TTL) -} -``` - -淘汰时的完整流程: - -``` -save(item) - │ - ├─ items.insert(id, item) - ├─ created_at.insert(id, now) - │ - └─ if writes_since_last_check >= check_interval: - switch policy: - Ttl → items.retain(created_at > cutoff) - Capacity → items: 按 created_at 升序排列,截断到 max_items - recall_stats: 同步清理 - RecallBased → 每条计算 value(now) - 按 value 升序排列,截断到 max_items - recall_stats: 同步清理 - Hybrid → 先 TTL → 再 Capacity 或 RecallBased -``` - -### 4.4 MemoryRetriever 对召回统计的联动 - -`MemoryRetriever` 每次成功检索后,对被选中的结果调用 `store.record_recall(id, score)`: - -``` -retrieve(query) - │ - ├─ 关键词提取 → 并行召回 - ├─ 评分 → 过滤 → 排序 - ├─ return results - │ - └─ 后台(非阻塞): - for item in results: - store.record_recall(item.id, item.score).await; - graph.record_recall(entity_id, item.score).await; -``` - -这样,被频繁召回且评分高的记忆会获得高价值分,自然保留;长期不被使用或评分低的会被优先淘汰。 - -### 4.3 各组件淘汰策略 +### 5.3 各组件淘汰策略 | 组件 | 推荐策略 | 理由 | |------|---------|------| | **ConversationMemory** | `Capacity { max_items }` | 对话是流式的,旧消息价值递减,达到上限后淘汰最旧的消息条目 | -| **MemoryStore**(通用) | `Ttl { ttl_secs }` 或 `Hybrid` | 通用存储由调用方按场景决定 | -| **KnowledgeStore** | `None`(默认不淘汰) | 知识是累积的,新增不淘汰旧;如需要可按 `Hybrid` 配置 | -| **KnowledgeGraph** | `None`(默认不淘汰) | 实体关系是长期资产,不应自动淘汰 | +| **MemoryStore**(通用) | `Ttl { ttl_secs }` | 通用存储由调用方按场景决定 | +| **KnowledgeStore** | `None`(默认不淘汰) | 知识是累积的,新增不淘汰旧 | -### 4.4 ConversationMemory 淘汰行为 +### 5.4 ConversationMemory 淘汰行为 -当前方案的 sliding window 仅压缩 tool result 内容,但不删除消息。升级后: +```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"] -``` -add_message(msg) - │ - ├─ store.save(msg) - │ - ├─ if eviction.policy == Capacity: - │ history = store.list(session) - │ while history.len() > max_items: - │ oldest = history.remove(0) ← 删除最旧消息 - │ store.delete(oldest.id) - │ - ├─ maybe_compact() ← 复用 microcompact() 做内容压缩 - │ - └─ 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 -### 4.5 InMemoryStore 的淘汰实现 +### 5.5 InMemoryStore 的淘汰实现 ```rust impl InMemoryStore { @@ -947,7 +473,7 @@ impl InMemoryStore { // 在 save() 内部: async fn save(&self, item: MemoryItem) -> Result<(), MemoryError> { self.items.lock().insert(item.id.clone(), item); - self.maybe_evict().await; // 写入后检查淘汰条件 + self.maybe_evict().await; } async fn maybe_evict(&self) { @@ -960,14 +486,14 @@ async fn maybe_evict(&self) { let mut items = self.items.lock(); if items.len() > *max_items { let mut vec: Vec<_> = items.drain().collect(); - vec.sort_by_key(|(_, v)| v.created_at); + 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::Hybrid { ttl_secs, max_items } => { - // 先按 TTL 淘汰,再按容量淘汰 - } EvictionPolicy::None => {} } } @@ -975,6 +501,8 @@ async fn maybe_evict(&self) { --- +## 6. 实现计划 + ### Step 1:基础类型 + MemoryStore(含淘汰机制) **文件**:`src/memory.rs`、`src/memory/types.rs`、`src/memory/error.rs`、`src/memory/store.rs` @@ -983,9 +511,9 @@ async fn maybe_evict(&self) { - 定义 `MemoryItem`、`MemoryFilter`、`MemoryStrategy` 类型 - 定义 `MemoryError` 枚举 - 定义 `MemoryStore` trait 与 `InMemoryStore` 实现 -- 定义 `EvictionConfig`、`EvictionPolicy`(None / Ttl / Capacity / Hybrid) +- 定义 `EvictionConfig`、`EvictionPolicy`(None / Ttl / Capacity) - `InMemoryStore.save()` 内部实现淘汰检查 -- 单元测试:TTL 过期淘汰、容量上限淘汰、不淘汰(None)、Hybrid 组合 +- 单元测试:TTL 过期淘汰、容量上限淘汰、不淘汰(None) - 验收:`cargo build` + `cargo test` 通过 **依赖**:chrono(日期时间)、serde(序列化) @@ -1008,88 +536,56 @@ async fn maybe_evict(&self) { **文件**:`src/memory/knowledge.rs` - 定义 `KnowledgePage`、`PageIndexEntry` 类型 -- 定义 `KnowledgeStore` trait -- 实现 `InMemoryKnowledgeStore` +- 实现 `KnowledgeStore` 具体 struct(非 trait) + - 内部使用 `Arc` 存储数据 - index 自动维护(add/update/delete 时同步) - search 基于标题/摘要/标签的关键词匹配 - 单元测试:页面 CRUD、index 一致性、搜索 - 验收:`cargo build` + `cargo test` 通过 -### Step 4:KnowledgeGraph(含标签匹配) - -**文件**:`src/memory/graph.rs` - -- 定义 `GraphEntity`(含 `tags: Vec`)、`GraphRelation` 类型 -- 定义 `KnowledgeGraph` trait -- 实现 `InMemoryGraph` - - BFS/DFS 图遍历 - - `find_by_keywords()`:关键词前缀匹配 `name`,完全匹配 `tags`,大小写不敏感 - - `find_tags(prefix)`:标签复用查询,前缀匹配已有标签 - - `set_entity_tags()`:替换实体的全部标签,按传入顺序保留前 `max_tags_per_entity` 个 - - `tag_constraints()`:返回 `TagConstraints { max_tags_per_entity: 8 }` - - 内部维护 `tag_index: HashMap>` 实现标签到实体的快速检索 -- 标签规范在源码中以常量或文档注释形式明确(小写化、原子性、优先复用已有、每实体 ≤8 个) -- 单元测试:实体 CRUD、关系添加/删除、标签匹配(大小写不敏感、前缀/完全区分)、标签复用查询、标签容量截断、图遍历 -- 验收:`cargo build` + `cargo test` 通过 - -### Step 5:MemoryRetriever(关键词提取 + 评分机制)+ 模块整合 +### Step 4:MemoryRetriever + 模块整合 **文件**:`src/memory/retriever.rs`、`src/memory.rs` -- 定义 `KeywordExtractor` trait + `SimpleKeywordExtractor` 默认实现(分割 + 停用词过滤) -- 定义 `ScoringStrategy` 枚举(TextOverlap / GraphDistance / TemporalWeight / ReferenceCount / Hybrid) -- 定义 `ScoreWeights`、`ScoredItem`、`ScoreBreakdown`、`RetrieverConfig` -- 实现评分函数: - - `TextOverlap`: 以原始 query 为基准,计算召回内容与 query 的 n-gram Jaccard/Dice 系数 - - 图距离、时间衰减、引用计数 -- 实现 `MemoryRetriever`: - - `retrieve(query)`: 关键词提取 → 并行召回 → 逐条评分(以原始 query 为基准)→ 阈值过滤 → 排序截取 - - 支持 `RetrievalStrategy` 切换检索通道 - - 可选:启用 `ScoreBreakdown` 明细(默认 off,减少计算开销) +- 实现内存检索器 `MemoryRetriever` + - 内部关键词提取:split + 过滤停用词 + - TextOverlap 评分:基于 Dice 系数计算 query 与页面的文本重叠度 + - 阈值过滤 → 排序 → 截取 top-N - 在 `memory.rs` 中用 `pub use` 重导出所有公共类型 - 在 `src/lib.rs` 中声明 `pub mod memory` -- 单元测试:关键词提取、各评分策略的正确性、阈值过滤、排序正确性 +- 单元测试:关键词提取、TextOverlap 评分正确性、阈值过滤、排序正确性 - 集成测试:端到端检索流程 - 验收:`cargo build` + `cargo test` 通过 --- -## 5. 风险评估 +## 7. 风险评估 | 风险 | 概率 | 影响 | 缓解措施 | |------|------|------|---------| -| KnowledgeStore 的 keyword 检索在大规模下效率低 | 中 | 中 | search 算法可替换——下游通过实现 trait 使用 BM25/TF-IDF | -| InMemoryGraph 不支持复杂图查询 | 中 | 中 | `depth` 参数限制遍历深度;生产场景替换为专用图数据库 | +| KnowledgeStore 的 keyword 检索在大规模下效率低 | 中 | 中 | MemoryStore 实现可替换——下游可使用 SQLite FTS 等更高效的后端 | | ConversationMemory 与 compact 耦合引入循环依赖 | 低 | 高 | 仅引用 `CompactConfig`(纯数据结构)和 `microcompact()`(纯函数),不引用 cycle.rs | | chrono 增加依赖体积 | 低 | 低 | chrono 已是 Rust 生态标准,且仅用于时间戳 | -| Phase 4 集成时发现 Memory trait 设计不合理 | 低 | 高 | 按最小可行接口设计,预留扩展空间 | +| Phase 4 集成时发现 Memory 设计不合理 | 低 | 高 | 按最小可行接口设计,预留扩展空间 | --- -## 6. 验收标准 +## 8. 验收标准 -- [ ] `MemoryStore` trait 定义清晰的 4 个方法,`InMemoryStore` 通过单元测试 -- [ ] `EvictionConfig` 支持 None / Ttl / Capacity / RecallBased / Hybrid 五种策略 -- [ ] `EvictionPolicy::RecallBased` 按记忆价值公式正确计算并淘汰低价值记忆 -- [ ] `InMemoryStore` 在 save() 后正确执行 TTL 淘汰、容量淘汰、基于召回价值淘汰 -- [ ] `InMemoryStore::record_recall()` 记录召回次数和评分 -- [ ] `MemoryRetriever` 每次检索后调用 `record_recall()` 更新召回统计 +- [ ] `MemoryStore` trait + `InMemoryStore` 通过单元测试 +- [ ] `EvictionConfig` 支持 None / Ttl / Capacity 三种策略 +- [ ] `InMemoryStore` 在 save() 后正确执行 TTL 淘汰和容量淘汰 - [ ] `ConversationMemory` 支持 sliding window 和 full 两种策略 - [ ] `ConversationMemory` sliding window 模式下达到上限后删除最旧消息条目 - [ ] `ConversationMemory` 正确复用 `llm::compact` 的压缩逻辑 -- [ ] `KnowledgeStore` trait + `InMemoryKnowledgeStore` 支持页面 CRUD 和 index 维护 -- [ ] `KnowledgeGraph` trait + `InMemoryGraph` 支持实体 CRUD、关系管理和图遍历 -- [ ] `KnowledgeGraph::find_tags(prefix)` 支持前缀匹配查询已有标签 -- [ ] `KnowledgeGraph::set_entity_tags()` 替换全部标签,截断到 `max_tags_per_entity` -- [ ] 每个实体的标签数不超过 `TagConstraints::max_tags_per_entity`(默认 8) -- [ ] 标签复用原则在方案文档和源码规范中明确,下游实现可据此遵守 -- [ ] `MemoryRetriever` 组合 KnowledgeStore + KnowledgeGraph 返回统一检索结果 -- [ ] 无 embedding 相关依赖(不引入 fastembed、pgvector、qdrant 等 crate) -- [ ] 模块结构符合项目惯例:`memory.rs` + `memory/` 目录 + `pub use` 重导出 -- [ ] `MemoryError` 枚举定义完善,支持 `is_recoverable()` +- [ ] `KnowledgeStore` 支持页面 CRUD 和 index 维护 +- [ ] `MemoryRetriever` 支持基于 TextOverlap 的知识检索 +- [ ] 无 embedding 相关依赖 +- [ ] 模块结构:`memory.rs` + `memory/` 目录 + `pub use` 重导出 +- [ ] `MemoryError` 枚举完善,支持 `is_recoverable()` - [ ] 所有公开 API 有文档注释(`///`) - [ ] `cargo build` 和 `cargo test` 通过 -- [ ] 单个文件不超过 300 行(遵循现有代码库风格) +- [ ] 单个文件不超过 300 行 --- @@ -1099,8 +595,8 @@ async fn maybe_evict(&self) { | Karpathy LLM Wiki | AG Core Memory System | 差异原因 | |-------------------|----------------------|---------| -| 三层:Raw → Wiki → Schema | 四组件:MemoryStore → ConversationMemory + KnowledgeStore + KnowledgeGraph + MemoryRetriever | Agent 场景需要区分对话记忆和知识记忆 | +| 三层: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 + graph 混合检索 | 文件系统搜索不适合所有后端 | +| 基于文件系统搜索 | index + keyword 检索 | 文件系统搜索不适合所有后端 |