第1讲:记忆系统总体架构与核心接口设计

发布时间:2026/10/20 19:56:37
第1讲:记忆系统总体架构与核心接口设计
一、整体架构┌─────────────────────────────────────────────────────────────────────┐ │ Agent 记忆系统总体架构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 用户请求 │ │ │ └────────────────────────┬────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ MemoryManager (入口) │ │ │ │ • 统一读写接口 Save/Search/Forget │ │ │ │ • 自动路由到对应层级 │ │ │ │ • 读写策略同步/异步/批量 │ │ │ └──────┬──────────────┬──────────────┬───────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Working │ │ Episodic │ │ Semantic │ │ │ │ Memory │ │ Memory │ │ Memory │ │ │ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 内存 │ │ 向量数据库 │ │ 知识图谱 │ │ │ │ RingBuf │ │ Chroma/Pine │ │ Neo4j/Nebula │ │ │ └──────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 辅助组件 │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │Embedding │ │ 压缩 │ │ 遗忘 │ │ 重要性 │ │ │ │ │ │ 服务 │ │ Compress │ │ Forget │ │ Scorer │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘二、核心数据结构2.1 统一记忆条目// memory/types.go package memory import ( time encoding/json ) // MemoryEntry 是所有记忆层的统一数据单元 type MemoryEntry struct { ID string json:id // 唯一标识 UUID UserID string json:user_id // 所属用户 SessionID string json:session_id // 所属会话 Type MemoryType json:type // 记忆类型 Layer MemoryLayer json:layer // 所属层级 Content string json:content // 记忆内容文本 Metadata map[string]interface{} json:metadata // 元数据 Importance float64 json:importance // 重要性 0-1 Embedding []float64 json:- // 向量不序列化到 JSON CreatedAt time.Time json:created_at AccessedAt time.Time json:accessed_at // 最后访问时间 ExpiresAt *time.Time json:expires_at // 过期时间nil 表示永不过期 Version int json:version // 版本号用于冲突处理 } // MemoryType 记忆类型枚举 type MemoryType string const ( MemoryTypeConversation MemoryType conversation // 对话记录 MemoryTypePreference MemoryType preference // 用户偏好 MemoryTypeFact MemoryType fact // 事实性知识 MemoryTypeDecision MemoryType decision // 决策记录 MemoryTypeFeedback MemoryType feedback // 反馈结果 MemoryTypeTask MemoryType task // 任务状态 MemoryTypeSummary MemoryType summary // 摘要 ) // MemoryLayer 记忆层级 type MemoryLayer string const ( LayerWorking MemoryLayer working LayerEpisodic MemoryLayer episodic LayerSemantic MemoryLayer semantic ) // MemoryEntryOption 创建条目的可选参数 type MemoryEntryOption func(*MemoryEntry) func WithImportance(score float64) MemoryEntryOption { return func(e *MemoryEntry) { e.Importance score } } func WithMetadata(key string, value interface{}) MemoryEntryOption { return func(e *MemoryEntry) { if e.Metadata nil { e.Metadata make(map[string]interface{}) } e.Metadata[key] value } } func WithTTL(d time.Duration) MemoryEntryOption { return func(e *MemoryEntry) { t : time.Now().Add(d) e.ExpiresAt t } } func NewMemoryEntry(userID, sessionID string, mtype MemoryType, layer MemoryLayer, content string, opts ...MemoryEntryOption) *MemoryEntry { entry : MemoryEntry{ ID: generateUUID(), UserID: userID, SessionID: sessionID, Type: mtype, Layer: layer, Content: content, Metadata: make(map[string]interface{}), Importance: 0.5, // 默认中等重要性 CreatedAt: time.Now(), AccessedAt: time.Now(), Version: 1, } for _, opt : range opts { opt(entry) } return entry }2.2 检索请求与响应// memory/types_search.go package memory import time // SearchRequest 统一的检索请求 type SearchRequest struct { Query string // 查询文本 UserID string // 限定用户空不限定 SessionID string // 限定会话空不限定 Types []MemoryType // 限定记忆类型空不限 Layers []MemoryLayer // 限定层级空全部 TopK int // 返回条数上限 MinScore float64 // 最低相似度阈值 TimeRange *TimeRange // 时间范围过滤 Importance *float64 // 最低重要性阈值 IncludeMeta bool // 是否返回元数据 } type TimeRange struct { Start time.Time End time.Time } // SearchResult 检索结果 type SearchResult struct { Entry *MemoryEntry Score float64 // 综合得分相似度 重要性 时间衰减 Similarity float64 // 语义相似度 Relevancy float64 // 相关性得分 } // SearchResponse 检索响应 type SearchResponse struct { Results []*SearchResult TotalCount int QueryTime time.Duration SourceLayers map[MemoryLayer]int // 各层级命中的数量 }三、核心接口设计3.1 MemoryStore 接口底层存储抽象// memory/store.go package memory import ( context time ) // MemoryStore 底层存储接口 // 不同的记忆层级可以实现各自的 Store type MemoryStore interface { // 写入 Save(ctx context.Context, entry *MemoryEntry) error BatchSave(ctx context.Context, entries []*MemoryEntry) error // 读取 Get(ctx context.Context, id string) (*MemoryEntry, error) GetByUser(ctx context.Context, userID string, limit, offset int) ([]*MemoryEntry, error) // 检索 Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error) // 更新 Update(ctx context.Context, entry *MemoryEntry) error // 删除 Delete(ctx context.Context, id string) error DeleteByUser(ctx context.Context, userID string) error DeleteExpired(ctx context.Context) (int, error) // 返回删除条数 // 维护 Count(ctx context.Context, userID string) (int, error) Close() error } // MemoryStoreFactory 工厂方法 type MemoryStoreFactory func(config map[string]interface{}) (MemoryStore, error)3.2 MemoryManager 接口统一入口// memory/manager.go package memory import ( context fmt sync time log ) // MemoryManager 记忆管理器——对外统一入口 type MemoryManager struct { working *WorkingMemory episodic *EpisodicMemory semantic *SemanticMemory embedding EmbeddingService scorer ImportanceScorer compressor MemoryCompressor forgetter ForgetStrategy config ManagerConfig stats ManagerStats mu sync.RWMutex } type ManagerConfig struct { EnableWorking bool yaml:enable_working EnableEpisodic bool yaml:enable_episodic EnableSemantic bool yaml:enable_semantic // 工作记忆配置 WorkingMemorySize int yaml:working_memory_size // 保留最近 N 轮 // 写入策略 WriteMode WriteMode yaml:write_mode // sync/async/batch AsyncBufferSize int yaml:async_buffer_size FlushInterval time.Duration yaml:flush_interval // 检索策略 DefaultTopK int yaml:default_top_k MinSimilarity float64 yaml:min_similarity TimeDecayFactor float64 yaml:time_decay_factor // 时间衰减系数 } type WriteMode string const ( WriteModeSync WriteMode sync WriteModeAsync WriteMode async WriteModeBatch WriteMode batch ) type ManagerStats struct { TotalSaves int64 TotalSearches int64 TotalForgets int64 AvgSaveLatency time.Duration AvgSearchLatency time.Duration CacheHitRate float64 } // NewMemoryManager 创建记忆管理器 func NewMemoryManager( working *WorkingMemory, episodic *EpisodicMemory, semantic *SemanticMemory, embedding EmbeddingService, scorer ImportanceScorer, compressor MemoryCompressor, forgetter ForgetStrategy, config ManagerConfig, ) *MemoryManager { mm : MemoryManager{ working: working, episodic: episodic, semantic: semantic, embedding: embedding, scorer: scorer, compressor: compressor, forgetter: forgetter, config: config, } // 如果启用异步写入启动后台协程 if config.WriteMode WriteModeAsync || config.WriteMode WriteModeBatch { go mm.asyncFlushLoop() } // 定期清理过期记忆 go mm.expirationCleanupLoop() return mm } // Save 保存一条记忆自动路由到对应层级 func (mm *MemoryManager) Save(ctx context.Context, entry *MemoryEntry) error { start : time.Now() defer func() { mm.mu.Lock() mm.stats.TotalSaves mm.stats.AvgSaveLatency (mm.stats.AvgSaveLatency time.Since(start)) / 2 mm.mu.Unlock() }() // 1. 计算重要性 importance, err : mm.scorer.Score(ctx, entry) if err ! nil { log.Printf([MEMORY] Importance scoring failed: %v, err) importance 0.5 // fallback } entry.Importance importance // 2. 根据重要性和配置决定写入哪些层级 switch { case importance 0.8: // 高重要性 → 写入所有层级 return mm.saveToAllLayers(ctx, entry) case importance 0.4: // 中等重要性 → 写入情景记忆 return mm.saveToEpisodic(ctx, entry) default: // 低重要性 → 仅工作记忆 return mm.saveToWorking(ctx, entry) } } // Search 检索记忆多层级融合 func (mm *MemoryManager) Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error) { start : time.Now() defer func() { mm.mu.Lock() mm.stats.TotalSearches mm.stats.AvgSearchLatency (mm.stats.AvgSearchLatency time.Since(start)) / 2 mm.mu.Unlock() }() response : SearchResponse{ Results: make([]*SearchResult, 0), SourceLayers: make(map[MemoryLayer]int), } // 1. 并行检索各层级 type layerResult struct { layer MemoryLayer results []*SearchResult err error } resultCh : make(chan layerResult, 3) var wg sync.WaitGroup // 检查是否需要检索工作记忆 if mm.config.EnableWorking containsLayer(req.Layers, LayerWorking) { wg.Add(1) go func() { defer wg.Done() results, err : mm.working.Search(ctx, req) resultCh - layerResult{LayerWorking, results, err} }() } if mm.config.EnableEpisodic containsLayer(req.Layers, LayerEpisodic) { wg.Add(1) go func() { defer wg.Done() results, err : mm.episodic.Search(ctx, req) resultCh - layerResult{LayerEpisodic, results, err} }() } if mm.config.EnableSemantic containsLayer(req.Layers, LayerSemantic) { wg.Add(1) go func() { defer wg.Done() results, err : mm.semantic.Search(ctx, req) resultCh - layerResult{LayerSemantic, results, err} }() } // 等待所有检索完成 go func() { wg.Wait() close(resultCh) }() // 2. 收集并融合结果 seen : make(map[string]bool) // 去重 for res : range resultCh { if res.err ! nil { log.Printf([MEMORY] Search error in %s: %v, res.layer, res.err) continue } for _, r : range res.results { if r.Entry nil || seen[r.Entry.ID] { continue } seen[r.Entry.ID] true // 应用时间衰减 r.Score mm.applyTimeDecay(r) response.Results append(response.Results, r) response.SourceLayers[res.layer] } } // 3. 按综合得分排序 sortSearchResults(response.Results) // 4. 截断到 TopK if len(response.Results) req.TopK { response.Results response.Results[:req.TopK] } response.TotalCount len(response.Results) response.QueryTime time.Since(start) return response, nil } // Remember 便捷方法保存一条记忆 func (mm *MemoryManager) Remember(ctx context.Context, userID, sessionID string, content string, mtype MemoryType, opts ...MemoryEntryOption) error { entry : NewMemoryEntry(userID, sessionID, mtype, , content, opts...) return mm.Save(ctx, entry) } // Recall 便捷方法检索记忆 func (mm *MemoryManager) Recall(ctx context.Context, query string, userID string, topK int) ([]*MemoryEntry, error) { req : SearchRequest{ Query: query, UserID: userID, TopK: topK, Layers: []MemoryLayer{LayerWorking, LayerEpisodic, LayerSemantic}, } resp, err : mm.Search(ctx, req) if err ! nil { return nil, err } entries : make([]*MemoryEntry, len(resp.Results)) for i, r : range resp.Results { entries[i] r.Entry } return entries, nil } // Forget 遗忘记忆 func (mm *MemoryManager) Forget(ctx context.Context, id string) error { mm.mu.Lock() mm.stats.TotalForgets mm.mu.Unlock() var lastErr error if mm.config.EnableWorking { if err : mm.working.Delete(ctx, id); err ! nil { lastErr err } } if mm.config.EnableEpisodic { if err : mm.episodic.Delete(ctx, id); err ! nil { lastErr err } } if mm.config.EnableSemantic { if err : mm.semantic.Delete(ctx, id); err ! nil { lastErr err } } return lastErr } // Stats 获取统计信息 func (mm *MemoryManager) Stats() ManagerStats { mm.mu.RLock() defer mm.mu.RUnlock() return mm.stats } // 内部方法 func (mm *MemoryManager) saveToAllLayers(ctx context.Context, entry *MemoryEntry) error { // 先计算 embedding emb, err : mm.embedding.Embed(ctx, entry.Content) if err ! nil { return fmt.Errorf(embedding: %w, err) } entry.Embedding emb // 并行写入各层级 var wg sync.WaitGroup errCh : make(chan error, 3) if mm.config.EnableWorking { wg.Add(1) go func() { defer wg.Done() if err : mm.working.Save(ctx, entry); err ! nil { errCh - fmt.Errorf(working: %w, err) } }() } if mm.config.EnableEpisodic { wg.Add(1) go func() { defer wg.Done() if err : mm.episodic.Save(ctx, entry); err ! nil { errCh - fmt.Errorf(episodic: %w, err) } }() } if mm.config.EnableSemantic { wg.Add(1) go func() { defer wg.Done() if err : mm.semantic.Save(ctx, entry); err ! nil { errCh - fmt.Errorf(semantic: %w, err) } }() } wg.Wait() close(errCh) // 收集第一个错误 for err : range errCh { return err // 返回第一个错误 } return nil } func (mm *MemoryManager) saveToEpisodic(ctx context.Context, entry *MemoryEntry) error { emb, err : mm.embedding.Embed(ctx, entry.Content) if err ! nil { return fmt.Errorf(embedding: %w, err) } entry.Embedding emb return mm.episodic.Save(ctx, entry) } func (mm *MemoryManager) saveToWorking(ctx context.Context, entry *MemoryEntry) error { return mm.working.Save(ctx, entry) } func (mm *MemoryManager) applyTimeDecay(result *SearchResult) float64 { if mm.config.TimeDecayFactor 0 { return result.Score } hoursSinceAccess : time.Since(result.Entry.AccessedAt).Hours() decay : 1.0 / (1.0 mm.config.TimeDecayFactor*hoursSinceAccess) return result.Score * decay } func (mm *MemoryManager) asyncFlushLoop() { // 异步/批量写入的后台循环 ticker : time.NewTicker(mm.config.FlushInterval) defer ticker.Stop() buffer : make([]*MemoryEntry, 0, mm.config.AsyncBufferSize) for range ticker.C { if len(buffer) 0 { continue } // 批量写入情景记忆 ctx, cancel : context.WithTimeout(context.Background(), 10*time.Second) if err : mm.episodic.BatchSave(ctx, buffer); err ! nil { log.Printf([MEMORY] Async flush failed: %v, err) } cancel() buffer buffer[:0] } } func (mm *MemoryManager) expirationCleanupLoop() { ticker : time.NewTicker(1 * time.Hour) defer ticker.Stop() for range ticker.C { ctx : context.Background() if mm.config.EnableEpisodic { deleted, err : mm.episodic.DeleteExpired(ctx) if err ! nil { log.Printf([MEMORY] Episodic cleanup: %v, err) } else if deleted 0 { log.Printf([MEMORY] Cleaned %d expired episodic memories, deleted) } } if mm.config.EnableSemantic { deleted, err : mm.semantic.DeleteExpired(ctx) if err ! nil { log.Printf([MEMORY] Semantic cleanup: %v, err) } else if deleted 0 { log.Printf([MEMORY] Cleaned %d expired semantic memories, deleted) } } } } // 辅助函数 func containsLayer(layers []MemoryLayer, target MemoryLayer) bool { if len(layers) 0 { return true // 空列表表示全部 } for _, l : range layers { if l target { return true } } return false } func sortSearchResults(results []*SearchResult) { // 按 Score 降序排列 for i : 0; i len(results); i { for j : i 1; j len(results); j { if results[j].Score results[i].Score { results[i], results[j] results[j], results[i] } } } }四、辅助服务接口// memory/services.go package memory import context // EmbeddingService 向量化服务 type EmbeddingService interface { Embed(ctx context.Context, text string) ([]float64, error) EmbedBatch(ctx context.Context, texts []string) ([][]float64, error) Dimensions() int } // ImportanceScorer 重要性评分器 type ImportanceScorer interface { Score(ctx context.Context, entry *MemoryEntry) (float64, error) } // MemoryCompressor 记忆压缩器 type MemoryCompressor interface { // Compress 压缩多条记忆为一条摘要 Compress(ctx context.Context, entries []*MemoryEntry) (*MemoryEntry, error) // ShouldCompress 判断是否需要压缩 ShouldCompress(entries []*MemoryEntry) bool } // ForgetStrategy 遗忘策略 type ForgetStrategy interface { // SelectCandidates 选择需要遗忘的记忆 SelectCandidates(ctx context.Context, store MemoryStore, count int) ([]*MemoryEntry, error) // Forget 执行遗忘 Forget(ctx context.Context, store MemoryStore, entries []*MemoryEntry) error }五、工作记忆实现示例// memory/working.go package memory import ( container/ring context sync ) // WorkingMemory 工作记忆——基于环形缓冲区的短期记忆 type WorkingMemory struct { ring *ring.Ring // 环形缓冲区 index map[string]*ring.Ring // ID 到节点的快速索引 mu sync.RWMutex config WorkingConfig } type WorkingConfig struct { Size int yaml:size // 最大条目数 } func NewWorkingMemory(config WorkingConfig) *WorkingMemory { return WorkingMemory{ ring: ring.New(config.Size), index: make(map[string]*ring.Ring), config: config, } } func (wm *WorkingMemory) Save(ctx context.Context, entry *MemoryEntry) error { wm.mu.Lock() defer wm.mu.Unlock() // 如果存在旧条目覆盖 if node, exists : wm.index[entry.ID]; exists { node.Value entry return nil } // 如果当前节点已有值从索引中移除旧值 if old, ok : wm.ring.Value.(*MemoryEntry); ok { delete(wm.index, old.ID) } // 写入新值 wm.ring.Value entry wm.index[entry.ID] wm.ring wm.ring wm.ring.Next() return nil } func (wm *WorkingMemory) Search(ctx context.Context, req *SearchRequest) ([]*SearchResult, error) { wm.mu.RLock() defer wm.mu.RUnlock() var results []*SearchResult wm.ring.Do(func(v interface{}) { if v nil { return } entry, ok : v.(*MemoryEntry) if !ok { return } // 过滤条件 if req.UserID ! entry.UserID ! req.UserID { return } if req.SessionID ! entry.SessionID ! req.SessionID { return } if len(req.Types) 0 !containsType(req.Types, entry.Type) { return } // 简单的关键词匹配工作记忆不需要语义检索 score : simpleMatchScore(req.Query, entry.Content) if score req.MinScore { return } results append(results, SearchResult{ Entry: entry, Score: score, }) }) return results, nil } func (wm *WorkingMemory) Get(ctx context.Context, id string) (*MemoryEntry, error) { wm.mu.RLock() defer wm.mu.RUnlock() if node, exists : wm.index[id]; exists { if entry, ok : node.Value.(*MemoryEntry); ok { return entry, nil } } return nil, nil } func (wm *WorkingMemory) Delete(ctx context.Context, id string) error { wm.mu.Lock() defer wm.mu.Unlock() if node, exists : wm.index[id]; exists { node.Value nil delete(wm.index, id) } return nil } func (wm *WorkingMemory) DeleteExpired(ctx context.Context) (int, error) { // 工作记忆不过期由环形缓冲区自动覆盖 return 0, nil } func (wm *WorkingMemory) Count(ctx context.Context, userID string) (int, error) { wm.mu.RLock() defer wm.mu.RUnlock() return len(wm.index), nil } func (wm *WorkingMemory) Close() error { wm.mu.Lock() defer wm.mu.Unlock() wm.index nil wm.ring nil return nil } // 辅助函数 func containsType(types []MemoryType, target MemoryType) bool { for _, t : range types { if t target { return true } } return false } func simpleMatchScore(query, content string) float64 { if query { return 0.5 // 无查询时返回中等分数 } // 简单的子串匹配 // 生产环境中应使用 TF-IDF 或 BM25 if len(query) len(content) { return 0 } // 计算重叠字符比例 matchCount : 0 for _, c : range query { for _, d : range content { if c d { matchCount break } } } return float64(matchCount) / float64(len(query)) }六、生产坑位┌─────────────────────────────────────────────────────────────┐ │ 第1讲 生产坑位汇总 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 坑位 1写入放大问题 │ │ • 现象一条记忆写入触发多次 embedding 多层存储 │ │ • 后果高并发下 embedding 服务成为瓶颈 │ │ • 解决 │ │ - embedding 结果本地缓存相同内容不重复计算 │ │ - 写入采用批量 异步模式 │ │ - 重要性 0.4 的记忆跳过 embedding │ │ │ │ 坑位 2检索结果过多导致 Prompt 超长 │ │ • 现象一次检索返回 50 条记忆全部塞入 Prompt │ │ • 后果Token 消耗暴增LLM 注意力分散 │ │ • 解决 │ │ - 严格控制 TopK建议 3-5 条 │ │ - 结果重排序只保留最相关的 │ │ - 设置单条内容最大长度如 500 chars │ │ │ │ 坑位 3记忆冲突与一致性 │ │ • 现象用户在不同时间给出矛盾信息 │ │ • 后果Agent 产生混乱的回答 │ │ • 解决 │ │ - 版本号递增保留历史版本 │ │ - 冲突检测同类型同主题的记忆超过 2 条标记为冲突 │ │ - 引入置信度字段新信息的置信度初始较低 │ │ │ │ 坑位 4冷启动问题 │ │ • 现象新用户没有任何历史记忆 │ │ • 后果检索为空记忆系统形同虚设 │ │ • 解决 │ │ - 预设通用知识库作为种子记忆 │ │ - 前几轮对话强制记录快速积累 │ │ - 利用用户画像地区/行业/角色生成初始偏好 │ │ │ │ 坑位 5遗忘策略过于激进 │ │ • 现象用户几天没来记忆被清理 │ │ • 后果用户回来发现 Agent 不认识自己了 │ │ • 解决 │ │ - 区分短期遗忘和长期遗忘 │ │ - 高重要性0.8的记忆永不自动删除 │ │ - 用户活跃度影响遗忘阈值 │ │ │ └─────────────────────────────────────────────────────────────┘七、评测指标┌─────────────────────────────────────────────────────────────┐ │ 第1讲 评测指标体系 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. 功能正确性 │ │ ├─ Save/Search/Forget 基本流程通过率≥ 99% │ │ ├─ 多层级检索结果融合正确率≥ 95% │ │ └─ 重要性路由准确率≥ 90% │ │ │ │ 2. 性能指标 │ │ ├─ Save P99 延迟≤ 50ms同步/ ≤ 200ms含 embedding │ │ ├─ Search P99 延迟≤ 100ms │ │ ├─ 吞吐量≥ 1000 ops/s单节点 │ │ └─ 内存占用工作记忆 10K 条目 ≤ 50MB │ │ │ │ 3. 检索质量 │ │ ├─ 命中率用户期望的记忆能被找到≥ 85% │ │ ├─ 精确率检索结果的相关性≥ 80% │ │ ├─ 召回率相关记忆被找到的比例≥ 75% │ │ └─ MRRMean Reciprocal Rank≥ 0.7 │ │ │ │ 4. 稳定性 │ │ ├─ 连续运行 72h 无内存泄漏 │ │ ├─ 并发 100 请求下无死锁 │ │ ├─ 异常输入空内容/超长文本不崩溃 │ │ └─ embedding 服务中断时有降级策略 │ │ │ └─────────────────────────────────────────────────────────────┘八、目录结构memory/ ├── types.go # 核心数据类型MemoryEntry, SearchRequest 等 ├── types_search.go # 检索相关类型 ├── manager.go # MemoryManager 统一入口 ├── store.go # MemoryStore 接口定义 ├── services.go # 辅助服务接口Embedding, Scorer 等 ├── working.go # 工作记忆实现 ├── episodic.go # 情景记忆接口具体实现在后续讲 ├── semantic.go # 语义记忆接口具体实现在后续讲 ├── errors.go # 错误定义 ├── utils.go # 工具函数UUID, 排序等 │ ├── impl/ │ ├── memory/ │ │ ├── chroma.go # Chroma 向量数据库适配 │ │ ├── pinecone.go # Pinecone 适配 │ │ └── memory.go # 内存版测试用 │ ├── embedding/ │ │ ├── openai.go # OpenAI Embedding │ │ ├── local.go # 本地模型BGE │ │ └── cache.go # 嵌入缓存 │ ├── scorer/ │ │ ├── basic.go # 基于规则的评分 │ │ └── ml.go # 基于模型的评分 │ └── compress/ │ └── summarizer.go # LLM 摘要压缩 │ ├── config/ │ └── defaults.go # 默认配置 │ └── examples/ └── basic_test.go # 基本使用示例九、本讲总结┌─────────────────────────────────────────────────────────────┐ │ 第1讲 核心收获 │ │ │ │ ✅ 设计了记忆系统三层架构 │ │ - 工作记忆短期、内存、自动淘汰 │ │ - 情景记忆中期、向量数据库、语义检索 │ │ - 语义记忆长期、知识图谱、关系推理 │ │ │ │ ✅ 定义了统一的数据模型 │ │ - MemoryEntry 作为所有层级的通用数据单元 │ │ - 包含 ID/用户/内容/重要性/向量/时间戳等字段 │ │ - 支持扩展元数据和版本控制 │ │ │ │ ✅ 实现了 MemoryManager 统一入口 │ │ - Save 自动根据重要性路由到合适层级 │ │ - Search 并行检索各层级并融合结果 │ │ - 支持同步/异步/批量三种写入模式 │ │ - 内置时间衰减和过期清理 │ │ │ │ ✅ 实现了工作记忆WorkingMemory │ │ - 基于环形缓冲区O(1) 写入 │ │ - 自动淘汰旧条目 │ │ - 支持快速 ID 索引 │ │ │ │ ✅ 识别了 5 个生产坑位及解决方案 │ │ │ └─────────────────────────────────────────────────────────────┘十、下一讲预告第2讲《情景记忆——向量数据库集成与语义检索》你将学到Chroma/Pinecone 向量数据库适配文本向量化与索引构建多路召回向量 关键词 时间结果重排序与融合缓存策略降低延迟 开发之余的小工具推荐在调试记忆系统时经常需要查看向量数据库的内容、测试 embedding 效果或可视化记忆之间的关系。zz365.top 提供在线 JSON 格式化工具、Base64 编解码器和文本对比工具方便你在开发过程中快速处理数据格式问题。所有处理都在浏览器本地完成无需担心数据泄露。