第5讲:记忆系统集成与统一接口——Manager 重构与多记忆协同

一、统一记忆系统架构

复制代码
┌──────────────────────────────────────────────────────────────────────────────┐
│                         统一记忆系统架构                                        │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                                │
│  ┌────────────────────────────────────────────────────────────────────────┐   │
│  │                         Application Layer                               │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────┐  │   │
│  │  │ Chat Agent   │  │ Recommend    │  │ Personalize  │  │ ...      │  │   │
│  │  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └──────────┘  │   │
│  └─────────┼──────────────────┼──────────────────┼───────────────────────┘   │
│            │                  │                  │                             │
│  ┌─────────▼──────────────────▼──────────────────▼───────────────────────┐   │
│  │                     UnifiedMemoryManager                                 │   │
│  │                                                                          │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌────────────┐  │   │
│  │  │ Write Router │  │ Read Router  │  │ Sync Manager │  │ Monitor    │  │   │
│  │  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └────────────┘  │   │
│  └─────────┼──────────────────┼──────────────────┼─────────────────────────┘   │
│            │                  │                  │                             │
│  ┌─────────▼──────────────────▼──────────────────▼─────────────────────────┐   │
│  │                     Memory Layer Abstraction                              │   │
│  │                                                                          │   │
│  │  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐            │   │
│  │  │ WorkingMemory  │  │ EpisodicMemory │  │ SemanticMemory │            │   │
│  │  │ (Short-term)   │  │ (Long-term)    │  │ (Knowledge)    │            │   │
│  │  └───────┬────────┘  └───────┬────────┘  └───────┬────────┘            │   │
│  └──────────┼────────────────────┼────────────────────┼────────────────────┘   │
│             │                    │                    │                          │
│  ┌──────────▼────────────────────▼────────────────────▼────────────────────┐   │
│  │                     Storage Backends                                      │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  │   │
│  │  │ In-Memory│  │ Chroma   │  │ Neo4j    │  │ Redis    │  │ S3       │  │   │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘  └──────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                                                                │
│  ┌────────────────────────────────────────────────────────────────────────┐   │
│  │                     Cross-Cutting Services                               │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌────────────┐  │   │
│  │  │ Compressor   │  │ Forgetting   │  │ Embedding    │  │ Scoring    │  │   │
│  │  └──────────────┘  └──────────────┘  └──────────────┘  └────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                                                                │
└──────────────────────────────────────────────────────────────────────────────┘

二、统一 MemoryManager 重构

复制代码
// memory/manager.go
package memory

import (
    "context"
    "fmt"
    "log"
    "sync"
    "time"

    "memory/compress"
)

// MemoryManager 统一的记忆管理器
type MemoryManager struct {
    config      ManagerConfig
    layers      map[MemoryLayer]MemoryService
    compressor  *compress.CompressScheduler
    scorer      *compress.ImportanceScorer
    monitor     *Monitor
    router      *Router
    syncMgr     *SyncManager
    mu          sync.RWMutex
    started     bool
}

type ManagerConfig struct {
    DefaultWriteLayer MemoryLayer     `yaml:"default_write_layer"` // 默认写入层
    WriteMode         WriteMode       `yaml:"write_mode"`          // 写入模式
    SyncInterval      time.Duration   `yaml:"sync_interval"`       // 层间同步间隔
    EnableAutoSync    bool            `yaml:"enable_auto_sync"`    // 启用自动同步
    MaxConcurrentOps  int             `yaml:"max_concurrent_ops"`  // 最大并发操作数
    MetricsEnabled    bool            `yaml:"metrics_enabled"`     // 启用指标收集
}

type WriteMode string

const (
    WriteModeSync     WriteMode = "sync"      // 同步写入所有层
    WriteModeAsync    WriteMode = "async"     // 异步写入其他层
    WriteModePrimary  WriteMode = "primary"   // 只写主层
    WriteModeBatch    WriteMode = "batch"     // 批量写入
)

// MemoryService 记忆服务接口(各层实现)
type MemoryService interface {
    Save(ctx context.Context, entry *MemoryEntry) error
    Search(ctx context.Context, req *SearchRequest) ([]*SearchResult, error)
    Get(ctx context.Context, id string) (*MemoryEntry, error)
    Delete(ctx context.Context, id string) error
    Update(ctx context.Context, entry *MemoryEntry) error
    Count(ctx context.Context, userID string) (int, error)
    GetLayer() MemoryLayer
    Close() error
}

// NewMemoryManager 创建统一记忆管理器
func NewMemoryManager(config ManagerConfig) *MemoryManager {
    mm := &MemoryManager{
        config: config,
        layers: make(map[MemoryLayer]MemoryService),
        router: NewRouter(),
        syncMgr: NewSyncManager(config.SyncInterval),
        monitor: NewMonitor(config.MetricsEnabled),
    }

    return mm
}

// RegisterLayer 注册记忆层
func (mm *MemoryManager) RegisterLayer(service MemoryService) error {
    mm.mu.Lock()
    defer mm.mu.Unlock()

    layer := service.GetLayer()
    if _, exists := mm.layers[layer]; exists {
        return fmt.Errorf("layer %s already registered", layer)
    }

    mm.layers[layer] = service
    log.Printf("[MANAGER] Registered layer: %s", layer)

    return nil
}

// SetCompressor 设置压缩器
func (mm *MemoryManager) SetCompressor(c *compress.CompressScheduler) {
    mm.compressor = c
}

// SetScorer 设置评分器
func (mm *MemoryManager) SetScorer(s *compress.ImportanceScorer) {
    mm.scorer = s
}

// Start 启动管理器
func (mm *MemoryManager) Start() {
    mm.mu.Lock()
    defer mm.mu.Unlock()

    if mm.started {
        return
    }

    // 启动同步管理器
    if mm.config.EnableAutoSync {
        mm.syncMgr.Start(mm)
    }

    // 启动监控
    mm.monitor.Start()

    mm.started = true
    log.Printf("[MANAGER] Started with mode: %s", mm.config.WriteMode)
}

// Stop 停止管理器
func (mm *MemoryManager) Stop() {
    mm.mu.Lock()
    defer mm.mu.Unlock()

    if !mm.started {
        return
    }

    mm.syncMgr.Stop()
    mm.monitor.Stop()

    for _, service := range mm.layers {
        service.Close()
    }

    mm.started = false
    log.Printf("[MANAGER] Stopped")
}

// Save 保存记忆(统一入口)
func (mm *MemoryManager) Save(ctx context.Context, entry *MemoryEntry) error {
    start := time.Now()
    defer func() {
        mm.monitor.RecordLatency("save", time.Since(start))
    }()

    // 1. 确定目标层
    targetLayers := mm.resolveTargetLayers(entry)

    // 2. 根据写入模式执行
    switch mm.config.WriteMode {
    case WriteModeSync:
        return mm.saveSync(ctx, entry, targetLayers)
    case WriteModeAsync:
        return mm.saveAsync(ctx, entry, targetLayers)
    case WriteModePrimary:
        return mm.savePrimary(ctx, entry, targetLayers)
    case WriteModeBatch:
        return mm.saveBatch(ctx, entry, targetLayers)
    default:
        return mm.saveSync(ctx, entry, targetLayers)
    }
}

// resolveTargetLayers 解析目标层
func (mm *MemoryManager) resolveTargetLayers(entry *MemoryEntry) []MemoryLayer {
    mm.mu.RLock()
    defer mm.mu.RUnlock()

    // 如果指定了层,使用指定的
    if entry.Layer != "" {
        if _, exists := mm.layers[entry.Layer]; exists {
            return []MemoryLayer{entry.Layer}
        }
    }

    // 根据记忆类型决定
    switch entry.MemoryType {
    case MemoryTypeConversation:
        return []MemoryLayer{LayerWorking, LayerEpisodic}
    case MemoryTypePreference:
        return []MemoryLayer{LayerEpisodic, LayerSemantic}
    case MemoryTypeFact:
        return []MemoryLayer{LayerSemantic}
    case MemoryTypeContext:
        return []MemoryLayer{LayerWorking}
    default:
        return []MemoryLayer{mm.config.DefaultWriteLayer}
    }
}

// saveSync 同步写入所有层
func (mm *MemoryManager) saveSync(ctx context.Context, entry *MemoryEntry, layers []MemoryLayer) error {
    for _, layer := range layers {
        service, ok := mm.layers[layer]
        if !ok {
            continue
        }

        // 克隆条目并设置对应层
        layerEntry := entry.Clone()
        layerEntry.Layer = layer

        if err := service.Save(ctx, layerEntry); err != nil {
            mm.monitor.RecordError("save", layer)
            return fmt.Errorf("save to %s: %w", layer, err)
        }

        mm.monitor.RecordOperation("save", layer)
    }

    return nil
}

// saveAsync 异步写入其他层
func (mm *MemoryManager) saveAsync(ctx context.Context, entry *MemoryEntry, layers []MemoryLayer) error {
    if len(layers) == 0 {
        return nil
    }

    // 主层同步写入
    primaryLayer := layers[0]
    service, ok := mm.layers[primaryLayer]
    if !ok {
        return fmt.Errorf("primary layer %s not found", primaryLayer)
    }

    primaryEntry := entry.Clone()
    primaryEntry.Layer = primaryLayer

    if err := service.Save(ctx, primaryEntry); err != nil {
        return fmt.Errorf("save to primary %s: %w", primaryLayer, err)
    }

    // 其他层异步写入
    if len(layers) > 1 {
        go func() {
            asyncCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
            defer cancel()

            for _, layer := range layers[1:] {
                svc, ok := mm.layers[layer]
                if !ok {
                    continue
                }

                layerEntry := entry.Clone()
                layerEntry.Layer = layer

                if err := svc.Save(asyncCtx, layerEntry); err != nil {
                    log.Printf("[MANAGER] Async save to %s failed: %v", layer, err)
                    mm.monitor.RecordError("async_save", layer)
                    continue
                }

                mm.monitor.RecordOperation("async_save", layer)
            }
        }()
    }

    return nil
}

// savePrimary 只写主层
func (mm *MemoryManager) savePrimary(ctx context.Context, entry *MemoryEntry, layers []MemoryLayer) error {
    if len(layers) == 0 {
        return nil
    }

    primaryLayer := layers[0]
    service, ok := mm.layers[primaryLayer]
    if !ok {
        return fmt.Errorf("primary layer %s not found", primaryLayer)
    }

    primaryEntry := entry.Clone()
    primaryEntry.Layer = primaryLayer

    return service.Save(ctx, primaryEntry)
}

// saveBatch 批量写入
func (mm *MemoryManager) saveBatch(ctx context.Context, entry *MemoryEntry, layers []MemoryLayer) error {
    // 批量模式下,将条目加入缓冲区,定期刷入
    // 此处简化,直接同步写入
    return mm.saveSync(ctx, entry, layers)
}

// Search 统一搜索入口
func (mm *MemoryManager) Search(ctx context.Context, req *SearchRequest) ([]*SearchResult, error) {
    start := time.Now()
    defer func() {
        mm.monitor.RecordLatency("search", time.Since(start))
    }()

    // 1. 路由选择目标层
    targetLayers := mm.router.SelectLayers(req)

    // 2. 并行搜索
    type layerResult struct {
        layer   MemoryLayer
        results []*SearchResult
        err     error
    }

    resultCh := make(chan layerResult, len(targetLayers))
    var wg sync.WaitGroup

    for _, layer := range targetLayers {
        service, ok := mm.layers[layer]
        if !ok {
            continue
        }

        wg.Add(1)
        go func(svc MemoryService, l MemoryLayer) {
            defer wg.Done()

            layerReq := req.Clone()
            layerReq.Layer = l

            results, err := svc.Search(ctx, layerReq)
            resultCh <- layerResult{layer: l, results: results, err: err}
        }(service, layer)
    }

    // 3. 等待所有搜索完成
    go func() {
        wg.Wait()
        close(resultCh)
    }()

    // 4. 聚合结果
    var allResults []*SearchResult
    for result := range resultCh {
        if result.err != nil {
            log.Printf("[MANAGER] Search on %s failed: %v", result.layer, result.err)
            mm.monitor.RecordError("search", result.layer)
            continue
        }
        allResults = append(allResults, result.results...)
        mm.monitor.RecordOperation("search", result.layer)
    }

    // 5. 排序和去重
    allResults = mm.deduplicateAndSort(allResults)

    // 6. 截取上限
    if req.Limit > 0 && len(allResults) > req.Limit {
        allResults = allResults[:req.Limit]
    }

    return allResults, nil
}

// Get 获取单条记忆
func (mm *MemoryManager) Get(ctx context.Context, id string) (*MemoryEntry, error) {
    // 遍历所有层查找
    for _, service := range mm.layers {
        entry, err := service.Get(ctx, id)
        if err == nil && entry != nil {
            return entry, nil
        }
    }
    return nil, fmt.Errorf("entry %s not found in any layer", id)
}

// Delete 删除记忆(从所有层)
func (mm *MemoryManager) Delete(ctx context.Context, id string) error {
    var lastErr error
    for layer, service := range mm.layers {
        if err := service.Delete(ctx, id); err != nil {
            lastErr = err
            log.Printf("[MANAGER] Delete from %s failed: %v", layer, err)
            continue
        }
        mm.monitor.RecordOperation("delete", layer)
    }
    return lastErr
}

// deduplicateAndSort 去重并排序
func (mm *MemoryManager) deduplicateAndSort(results []*SearchResult) []*SearchResult {
    seen := make(map[string]bool)
    var unique []*SearchResult

    for _, r := range results {
        if seen[r.Entry.ID] {
            continue
        }
        seen[r.Entry.ID] = true
        unique = append(unique, r)
    }

    // 按相关性降序
    for i := 0; i < len(unique); i++ {
        for j := i + 1; j < len(unique); j++ {
            if unique[j].Score > unique[i].Score {
                unique[i], unique[j] = unique[j], unique[i]
            }
        }
    }

    return unique
}

// GetMetrics 获取监控指标
func (mm *MemoryManager) GetMetrics() *ManagerMetrics {
    return mm.monitor.GetMetrics()
}

三、路由模块

复制代码
// memory/router.go
package memory

import (
    "strings"
    "sync"
    "time"
)

// Router 记忆检索路由器
type Router struct {
    rules     []RoutingRule
    stats     map[MemoryLayer]*LayerStats
    mu        sync.RWMutex
}

type RoutingRule struct {
    Name        string          `yaml:"name"`
    Priority    int             `yaml:"priority"`
    Conditions  []RouteCondition `yaml:"conditions"`
    TargetLayer MemoryLayer     `yaml:"target_layer"`
    Weight      float64         `yaml:"weight"`
}

type RouteCondition struct {
    Field    string      `yaml:"field"`    // query_type / memory_type / importance / ...
    Operator string      `yaml:"operator"` // eq / neq / gt / lt / contains
    Value    interface{} `yaml:"value"`
}

type LayerStats struct {
    HitCount      int64
    MissCount     int64
    AvgLatency    time.Duration
    LastAccessAt  time.Time
}

func NewRouter() *Router {
    return &Router{
        rules: make([]RoutingRule, 0),
        stats: make(map[MemoryLayer]*LayerStats),
    }
}

// AddRule 添加路由规则
func (r *Router) AddRule(rule RoutingRule) {
    r.mu.Lock()
    defer r.mu.Unlock()
    r.rules = append(r.rules, rule)

    // 按优先级排序
    for i := 0; i < len(r.rules); i++ {
        for j := i + 1; j < len(r.rules); j++ {
            if r.rules[j].Priority > r.rules[i].Priority {
                r.rules[i], r.rules[j] = r.rules[j], r.rules[i]
            }
        }
    }
}

// SelectLayers 选择搜索目标层
func (r *Router) SelectLayers(req *SearchRequest) []MemoryLayer {
    r.mu.RLock()
    defer r.mu.RUnlock()

    layerScores := make(map[MemoryLayer]float64)

    // 应用规则
    for _, rule := range r.rules {
        if r.matchConditions(rule.Conditions, req) {
            layerScores[rule.TargetLayer] += rule.Weight
        }
    }

    // 如果没有匹配规则,使用默认策略
    if len(layerScores) == 0 {
        return r.defaultLayers(req)
    }

    // 按得分排序
    type scoredLayer struct {
        layer MemoryLayer
        score float64
    }

    var sorted []scoredLayer
    for layer, score := range layerScores {
        sorted = append(sorted, scoredLayer{layer, score})
    }

    for i := 0; i < len(sorted); i++ {
        for j := i + 1; j < len(sorted); j++ {
            if sorted[j].score > sorted[i].score {
                sorted[i], sorted[j] = sorted[j], sorted[i]
            }
        }
    }

    layers := make([]MemoryLayer, len(sorted))
    for i, sl := range sorted {
        layers[i] = sl.layer
    }

    return layers
}

// matchConditions 匹配条件
func (r *Router) matchConditions(conditions []RouteCondition, req *SearchRequest) bool {
    for _, cond := range conditions {
        if !r.matchCondition(cond, req) {
            return false
        }
    }
    return true
}

func (r *Router) matchCondition(cond RouteCondition, req *SearchRequest) bool {
    var actualValue interface{}

    switch cond.Field {
    case "query_type":
        actualValue = req.QueryType
    case "memory_type":
        actualValue = req.MemoryType
    case "layer":
        actualValue = req.Layer
    case "has_user_id":
        actualValue = req.UserID != ""
    case "query_length":
        actualValue = len(req.Query)
    default:
        return false
    }

    switch cond.Operator {
    case "eq":
        return actualValue == cond.Value
    case "neq":
        return actualValue != cond.Value
    case "gt":
        if v, ok := actualValue.(int); ok {
            if cv, ok := cond.Value.(int); ok {
                return v > cv
            }
        }
        return false
    case "lt":
        if v, ok := actualValue.(int); ok {
            if cv, ok := cond.Value.(int); ok {
                return v < cv
            }
        }
        return false
    case "contains":
        if s, ok := actualValue.(string); ok {
            if substr, ok := cond.Value.(string); ok {
                return strings.Contains(s, substr)
            }
        }
        return false
    default:
        return false
    }
}

// defaultLayers 默认层选择策略
func (r *Router) defaultLayers(req *SearchRequest) []MemoryLayer {
    // 根据查询类型选择
    switch req.QueryType {
    case QueryTypeRecent:
        return []MemoryLayer{LayerWorking, LayerEpisodic}
    case QueryTypeSemantic:
        return []MemoryLayer{LayerSemantic, LayerEpisodic}
    case QueryTypeExact:
        return []MemoryLayer{LayerWorking, LayerEpisodic, LayerSemantic}
    default:
        return []MemoryLayer{LayerEpisodic, LayerSemantic}
    }
}

// RecordHit 记录命中
func (r *Router) RecordHit(layer MemoryLayer, latency time.Duration) {
    r.mu.Lock()
    defer r.mu.Unlock()

    stats, exists := r.stats[layer]
    if !exists {
        stats = &LayerStats{}
        r.stats[layer] = stats
    }

    stats.HitCount++
    stats.AvgLatency = (stats.AvgLatency*time.Duration(stats.HitCount-1) + latency) / time.Duration(stats.HitCount)
    stats.LastAccessAt = time.Now()
}

// GetStats 获取路由统计
func (r *Router) GetStats() map[MemoryLayer]*LayerStats {
    r.mu.RLock()
    defer r.mu.RUnlock()

    result := make(map[MemoryLayer]*LayerStats)
    for k, v := range r.stats {
        result[k] = v
    }
    return result
}

四、同步管理器

复制代码
// memory/sync.go
package memory

import (
    "context"
    "log"
    "sync"
    "time"
)

// SyncManager 层间同步管理器
type SyncManager struct {
    interval    time.Duration
    running     bool
    mu          sync.Mutex
    stopCh      chan struct{}
    syncRules   []SyncRule
}

type SyncRule struct {
    Name         string        `yaml:"name"`
    SourceLayer  MemoryLayer   `yaml:"source_layer"`
    TargetLayer  MemoryLayer   `yaml:"target_layer"`
    Direction    SyncDirection `yaml:"direction"`
    Filter       SyncFilter    `yaml:"filter"`
    BatchSize    int           `yaml:"batch_size"`
    Interval     time.Duration `yaml:"interval"`
}

type SyncDirection string

const (
    SyncDirectionPush    SyncDirection = "push"    // 源->目标
    SyncDirectionPull    SyncDirection = "pull"    // 目标<-源
    SyncDirectionBoth    SyncDirection = "both"    // 双向
)

type SyncFilter struct {
    MinImportance float64 `yaml:"min_importance"`
    MaxAge        time.Duration `yaml:"max_age"`
    MemoryTypes   []MemoryType  `yaml:"memory_types"`
}

func NewSyncManager(interval time.Duration) *SyncManager {
    return &SyncManager{
        interval: interval,
        stopCh:   make(chan struct{}),
    }
}

// AddSyncRule 添加同步规则
func (sm *SyncManager) AddSyncRule(rule SyncRule) {
    sm.mu.Lock()
    defer sm.mu.Unlock()
    sm.syncRules = append(syncRules, rule)
}

// Start 启动同步循环
func (sm *SyncManager) Start(mm *MemoryManager) {
    sm.mu.Lock()
    if sm.running {
        sm.mu.Unlock()
        return
    }
    sm.running = true
    sm.mu.Unlock()

    go sm.syncLoop(mm)
}

// Stop 停止同步
func (sm *SyncManager) Stop() {
    sm.mu.Lock()
    defer sm.mu.Unlock()

    if !sm.running {
        return
    }

    close(sm.stopCh)
    sm.running = false
}

func (sm *SyncManager) syncLoop(mm *MemoryManager) {
    ticker := time.NewTicker(sm.interval)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
            sm.executeSync(mm)
        case <-sm.stopCh:
            return
        }
    }
}

func (sm *SyncManager) executeSync(mm *MemoryManager) {
    sm.mu.Lock()
    rules := make([]SyncRule, len(sm.syncRules))
    copy(rules, sm.syncRules)
    sm.mu.Unlock()

    for _, rule := range rules {
        sm.syncWithRule(mm, rule)
    }
}

func (sm *SyncManager) syncWithRule(mm *MemoryManager, rule SyncRule) {
    sourceSvc, ok := mm.layers[rule.SourceLayer]
    if !ok {
        return
    }

    targetSvc, ok := mm.layers[rule.TargetLayer]
    if !ok {
        return
    }

    ctx := context.Background()

    // 根据方向执行同步
    switch rule.Direction {
    case SyncDirectionPush:
        sm.pushSync(ctx, sourceSvc, targetSvc, rule)
    case SyncDirectionPull:
        sm.pullSync(ctx, sourceSvc, targetSvc, rule)
    case SyncDirectionBoth:
        sm.pushSync(ctx, sourceSvc, targetSvc, rule)
        sm.pullSync(ctx, sourceSvc, targetSvc, rule)
    }
}

func (sm *SyncManager) pushSync(ctx context.Context, source, target MemoryService, rule SyncRule) {
    // 从源层获取符合条件的条目
    req := &SearchRequest{
        Layer:      rule.SourceLayer,
        Limit:      rule.BatchSize,
        SortBy:     "updated_at",
        SortOrder:  "asc",
    }

    results, err := source.Search(ctx, req)
    if err != nil {
        log.Printf("[SYNC] Push search failed: %v", err)
        return
    }

    for _, result := range results {
        entry := result.Entry

        // 应用过滤器
        if !sm.passesFilter(entry, rule.Filter) {
            continue
        }

        // 写入目标层
        clone := entry.Clone()
        clone.Layer = rule.TargetLayer

        if err := target.Save(ctx, clone); err != nil {
            log.Printf("[SYNC] Push to %s failed: %v", rule.TargetLayer, err)
            continue
        }

        log.Printf("[SYNC] Pushed %s from %s to %s", entry.ID[:8], rule.SourceLayer, rule.TargetLayer)
    }
}

func (sm *SyncManager) pullSync(ctx context.Context, source, target MemoryService, rule SyncRule) {
    // 拉取逻辑与推送对称
    sm.pushSync(ctx, source, target, rule)
}

func (sm *SyncManager) passesFilter(entry *MemoryEntry, filter SyncFilter) bool {
    if filter.MinImportance > 0 && entry.Importance < filter.MinImportance {
        return false
    }
    if filter.MaxAge > 0 && time.Since(entry.UpdatedAt) > filter.MaxAge {
        return false
    }
    if len(filter.MemoryTypes) > 0 {
        found := false
        for _, mt := range filter.MemoryTypes {
            if entry.MemoryType == mt {
                found = true
                break
            }
        }
        if !found {
            return false
        }
    }
    return true
}

五、监控模块

复制代码
// memory/monitor.go
package memory

import (
    "log"
    "sync"
    "time"
)

// Monitor 记忆系统监控
type Monitor struct {
    enabled     bool
    metrics     *ManagerMetrics
    mu          sync.RWMutex
}

type ManagerMetrics struct {
    Operations    map[string]*OpMetrics   `json:"operations"`     // operation -> metrics
    Latencies     map[string]*LatencyMetrics `json:"latencies"`   // operation -> latency
    Errors        map[string]int64        `json:"errors"`         // operation -> count
    Layers        map[MemoryLayer]*LayerMetrics `json:"layers"`
    StartedAt     time.Time               `json:"started_at"`
    TotalOps      int64                   `json:"total_ops"`
    TotalErrors   int64                   `json:"total_errors"`
}

type OpMetrics struct {
    Count       int64     `json:"count"`
    LastAt      time.Time `json:"last_at"`
    SuccessRate float64   `json:"success_rate"`
}

type LatencyMetrics struct {
    P50     time.Duration `json:"p50"`
    P90     time.Duration `json:"p90"`
    P99     time.Duration `json:"p99"`
    Avg     time.Duration `json:"avg"`
    Samples []time.Duration `json:"-"` // 采样窗口
}

type LayerMetrics struct {
    EntryCount  int64     `json:"entry_count"`
    SizeBytes   int64     `json:"size_bytes"`
    LastAccess  time.Time `json:"last_access"`
    HitRate     float64   `json:"hit_rate"`
}

func NewMonitor(enabled bool) *Monitor {
    return &Monitor{
        enabled: enabled,
        metrics: &ManagerMetrics{
            Operations: make(map[string]*OpMetrics),
            Latencies:  make(map[string]*LatencyMetrics),
            Errors:     make(map[string]int64),
            Layers:     make(map[MemoryLayer]*LayerMetrics),
            StartedAt:  time.Now(),
        },
    }
}

func (m *Monitor) Start() {
    if !m.enabled {
        return
    }
    log.Printf("[MONITOR] Started")
}

func (m *Monitor) Stop() {
    if !m.enabled {
        return
    }
    m.report()
}

// RecordOperation 记录操作
func (m *Monitor) RecordOperation(op string, layer MemoryLayer) {
    if !m.enabled {
        return
    }

    m.mu.Lock()
    defer m.mu.Unlock()

    metrics, exists := m.metrics.Operations[op]
    if !exists {
        metrics = &OpMetrics{}
        m.metrics.Operations[op] = metrics
    }

    metrics.Count++
    metrics.LastAt = time.Now()
    m.metrics.TotalOps++

    // 更新层指标
    layerMetrics, exists := m.metrics.Layers[layer]
    if !exists {
        layerMetrics = &LayerMetrics{}
        m.metrics.Layers[layer] = layerMetrics
    }
    layerMetrics.LastAccess = time.Now()
}

// RecordError 记录错误
func (m *Monitor) RecordError(op string, layer MemoryLayer) {
    if !m.enabled {
        return
    }

    m.mu.Lock()
    defer m.mu.Unlock()

    m.metrics.Errors[op]++
    m.metrics.TotalErrors++

    // 更新成功率
    if opMetrics, exists := m.metrics.Operations[op]; exists {
        total := opMetrics.Count + m.metrics.Errors[op]
        if total > 0 {
            opMetrics.SuccessRate = float64(opMetrics.Count) / float64(total)
        }
    }
}

// RecordLatency 记录延迟
func (m *Monitor) RecordLatency(op string, d time.Duration) {
    if !m.enabled {
        return
    }

    m.mu.Lock()
    defer m.mu.Unlock()

    latMetrics, exists := m.metrics.Latencies[op]
    if !exists {
        latMetrics = &LatencyMetrics{
            Samples: make([]time.Duration, 0, 100),
        }
        m.metrics.Latencies[op] = latMetrics
    }

    // 滑动窗口采样
    if len(latMetrics.Samples) >= 100 {
        latMetrics.Samples = latMetrics.Samples[1:]
    }
    latMetrics.Samples = append(latMetrics.Samples, d)

    // 计算百分位
    m.calculatePercentiles(latMetrics)
}

func (m *Monitor) calculatePercentiles(lm *LatencyMetrics) {
    samples := make([]time.Duration, len(lm.Samples))
    copy(samples, lm.Samples)

    // 排序
    for i := 0; i < len(samples); i++ {
        for j := i + 1; j < len(samples); j++ {
            if samples[j] < samples[i] {
                samples[i], samples[j] = samples[j], samples[i]
            }
        }
    }

    n := len(samples)
    if n == 0 {
        return
    }

    lm.P50 = samples[n*50/100]
    lm.P90 = samples[n*90/100]
    lm.P99 = samples[n*99/100]

    sum := time.Duration(0)
    for _, s := range samples {
        sum += s
    }
    lm.Avg = sum / time.Duration(n)
}

// GetMetrics 获取指标快照
func (m *Monitor) GetMetrics() *ManagerMetrics {
    m.mu.RLock()
    defer m.mu.RUnlock()

    return m.metrics
}

func (m *Monitor) report() {
    metrics := m.GetMetrics()
    log.Printf("[MONITOR] === Memory System Report ===")
    log.Printf("[MONITOR] Total Ops: %d, Errors: %d", metrics.TotalOps, metrics.TotalErrors)
    log.Printf("[MONITOR] Uptime: %v", time.Since(metrics.StartedAt))

    for op, opMetrics := range metrics.Operations {
        log.Printf("[MONITOR] Op %s: count=%d, success_rate=%.2f%%",
            op, opMetrics.Count, opMetrics.SuccessRate*100)
    }

    for op, latMetrics := range metrics.Latencies {
        log.Printf("[MONITOR] Latency %s: avg=%v, p50=%v, p90=%v, p99=%v",
            op, latMetrics.Avg, latMetrics.P50, latMetrics.P90, latMetrics.P99)
    }
}

六、生产坑位

复制代码
┌─────────────────────────────────────────────────────────────┐
│                  第5讲 生产坑位汇总                           │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  坑位 1:层间数据不一致                                        │
│  • 现象:同一记忆在不同层内容不同                              │
│  • 后果:Agent 给出矛盾答案                                    │
│  • 解决:                                                  │
│    - 使用乐观锁(版本号控制)                                  │
│    - 定期层间一致性校验                                        │
│    - 主从层架构(主层写入,从层异步同步)                      │
│    - 冲突检测与自动修复                                        │
│                                                               │
│  坑位 2:异步写入丢失                                          │
│  • 现象:主层写入成功,但从层写入失败且无补偿                  │
│  • 后果:部分层缺失记忆                                        │
│  • 解决:                                                  │
│    - 引入重试队列(最多3次)                                   │
│    - 死信队列(重试失败后人工处理)                            │
│    - 写入确认回调                                              │
│    - 定期全量同步修复                                          │
│                                                               │
│  坑位 3:路由风暴                                              │
│  • 现象:复杂路由规则导致每次检索都扫描所有层                  │
│  • 后果:检索延迟飙升                                          │
│  • 解决:                                                  │
│    - 路由缓存(相同 query 缓存路由结果)                       │
│    - 路由规则编译(预编译成正则/表达式)                      │
│    - 层选择上限(最多查3层)                                   │
│    - 超时熔断(单层超过500ms跳过)                            │
│                                                               │
│  坑位 4:同步死循环                                            │
│  • 现象:双向同步规则导致 A->B->A 无限循环                    │
│  • 后果:CPU 打满,存储爆炸                                    │
│  • 解决:                                                  │
│    - 同步标记(写入时标记来源,避免回写)                      │
│    - 环路检测(有向图检测)                                    │
│    - 最大跳数限制(最多3跳)                                   │
│    - 幂等写入(相同内容不重复插入)                            │
│                                                               │
│  坑位 5:监控数据膨胀                                          │
│  • 现象:每秒记录所有操作指标,内存暴涨                        │
│  • 后果:OOM                                                    │
│  • 解决:                                                  │
│    - 滑动窗口采样(最多保留100个样本)                         │
│    - 聚合上报(每分钟聚合一次)                                │
│    - 分级监控(核心/非核心指标分开)                          │
│    - 外部存储(Prometheus + Grafana)                         │
│                                                               │
└─────────────────────────────────────────────────────────────┘

七、评测指标

复制代码
┌─────────────────────────────────────────────────────────────┐
│                  第5讲 评测指标体系                           │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  1. 写入性能                                                  │
│  ├─ 同步写入 P99:≤ 50ms                                      │
│  ├─ 异步写入 P99:≤ 10ms(主层)                              │
│  ├─ 批量写入吞吐量:≥ 10000 ops/s                             │
│  └─ 写入一致性:100%(同步模式)                              │
│                                                               │
│  2. 检索性能                                                  │
│  ├─ 单层检索 P99:≤ 100ms                                     │
│  ├─ 跨层检索 P99:≤ 300ms                                     │
│  ├─ 路由决策 P99:≤ 5ms                                       │
│  └─ 结果聚合 P99:≤ 10ms                                      │
│                                                               │
│  3. 同步效率                                                  │
│  ├─ 层间同步延迟:≤ 5s                                        │
│  ├─ 数据一致率:≥ 99.99%                                      │
│  ├─ 同步吞吐量:≥ 1000 entries/s                              │
│  └─ 冲突解决成功率:≥ 99%                                     │
│                                                               │
│  4. 系统稳定性                                                │
│  ├─ 可用性:≥ 99.9%                                           │
│  ├─ 平均故障恢复时间:≤ 5min                                  │
│  ├─ 监控开销:≤ 5% CPU                                        │
│  └─ 内存占用:≤ 500MB(空闲态)                               │
│                                                               │
└─────────────────────────────────────────────────────────────┘

八、使用示例

复制代码
// examples/integration_demo.go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "memory"
    "memory/episodic"
    "memory/semantic"
    "memory/compress"
)

func main() {
    ctx := context.Background()

    // 1. 创建统一管理器
    mgrConfig := memory.ManagerConfig{
        DefaultWriteLayer: memory.LayerEpisodic,
        WriteMode:         memory.WriteModeAsync,
        SyncInterval:      30 * time.Second,
        EnableAutoSync:    true,
        MaxConcurrentOps:  10,
        MetricsEnabled:    true,
    }
    mgr := memory.NewMemoryManager(mgrConfig)

    // 2. 注册各层
    // 工作记忆(内存)
    workingConfig := episodic.DefaultConfig()
    workingSvc := episodic.NewEpisodicMemory(workingConfig)
    mgr.RegisterLayer(workingSvc)

    // 情景记忆(Chroma)
    episodicConfig := episodic.Config{
        VectorDim:   768,
        DistanceFn:  "cosine",
        AutoEmbed:   true,
    }
    episodicSvc := episodic.NewEpisodicMemory(&episodicConfig)
    mgr.RegisterLayer(episodicSvc)

    // 语义记忆(Neo4j)
    semanticConfig := semantic.DefaultConfig()
    semanticSvc := semantic.NewSemanticMemory(semanticConfig)
    mgr.RegisterLayer(semanticSvc)

    // 3. 配置路由规则
    router := memory.NewRouter()
    router.AddRule(memory.RoutingRule{
        Name:     "recent_query_to_working",
        Priority: 100,
        Conditions: []memory.RouteCondition{
            {Field: "query_type", Operator: "eq", Value: memory.QueryTypeRecent},
        },
        TargetLayer: memory.LayerWorking,
        Weight:      10,
    })
    router.AddRule(memory.RoutingRule{
        Name:     "semantic_to_knowledge",
        Priority: 80,
        Conditions: []memory.RouteCondition{
            {Field: "query_type", Operator: "eq", Value: memory.QueryTypeSemantic},
        },
        TargetLayer: memory.LayerSemantic,
        Weight:      10,
    })
    router.AddRule(memory.RoutingRule{
        Name:     "long_query_to_all",
        Priority: 60,
        Conditions: []memory.RouteCondition{
            {Field: "query_length", Operator: "gt", Value: 50},
        },
        TargetLayer: memory.LayerEpisodic,
        Weight:      5,
    })

    // 注入路由(通过反射或公开方法,此处简化)
    // 实际项目中可通过构造函数注入

    // 4. 配置同步规则
    syncMgr := memory.NewSyncManager(30 * time.Second)
    syncMgr.AddSyncRule(memory.SyncRule{
        Name:        "working_to_episodic",
        SourceLayer: memory.LayerWorking,
        TargetLayer: memory.LayerEpisodic,
        Direction:   memory.SyncDirectionPush,
        Filter: memory.SyncFilter{
            MinImportance: 0.5,
            MaxAge:        1 * time.Hour,
        },
        BatchSize: 100,
    })
    syncMgr.AddSyncRule(memory.SyncRule{
        Name:        "episodic_to_semantic",
        SourceLayer: memory.LayerEpisodic,
        TargetLayer: memory.LayerSemantic,
        Direction:   memory.SyncDirectionPush,
        Filter: memory.SyncFilter{
            MinImportance: 0.7,
            MemoryTypes:  []memory.MemoryType{memory.MemoryTypeFact},
        },
        BatchSize: 50,
    })

    // 5. 启动管理器
    mgr.Start()
    defer mgr.Stop()

    // 6. 写入记忆
    fmt.Println("=== 写入记忆 ===")

// 写入工作记忆(短时上下文)
    workingEntry := memory.NewMemoryEntry(
        "user_001", "session_005",
        memory.MemoryTypeContext,
        memory.LayerWorking,
        "用户正在查看订单页面,当前订单状态为'已发货',物流单号 SF1234567890",
        memory.WithImportance(0.9),
        memory.WithTTL(30*time.Minute),
    )

    if err := mgr.Save(ctx, workingEntry); err != nil {
        log.Fatalf("save working memory failed: %v", err)
    }
    fmt.Printf("  ✓ 写入工作记忆: %s\n", workingEntry.ID[:8])

    // 写入情景记忆(长期对话记录)
    episodicEntry := memory.NewMemoryEntry(
        "user_001", "session_005",
        memory.MemoryTypeConversation,
        memory.LayerEpisodic,
        "用户询问了退货流程,客服解释了7天无理由退货政策。"+
            "用户表示理解,但担心运费问题。客服告知退货包邮。",
        memory.WithImportance(0.75),
        memory.WithTags([]string{"退货", "售后", "运费"}),
    )

    if err := mgr.Save(ctx, episodicEntry); err != nil {
        log.Fatalf("save episodic memory failed: %v", err)
    }
    fmt.Printf("  ✓ 写入情景记忆: %s\n", episodicEntry.ID[:8])

    // 写入语义记忆(知识事实)
    factEntry := memory.NewMemoryEntry(
        "", "",
        memory.MemoryTypeFact,
        memory.LayerSemantic,
        "7天无理由退货政策:自签收之日起7天内,商品完好不影响二次销售,可申请无理由退货。",
        memory.WithImportance(0.95),
        memory.WithTags([]string{"退货政策", "售后规则"}),
    )

    if err := mgr.Save(ctx, factEntry); err != nil {
        log.Fatalf("save semantic memory failed: %v", err)
    }
    fmt.Printf("  ✓ 写入语义记忆: %s\n", factEntry.ID[:8])

    // 7. 检索记忆
    fmt.Println("\n=== 检索记忆 ===")

    // 检索最近的上下文
    recentReq := &memory.SearchRequest{
        UserID:    "user_001",
        Query:     "订单状态",
        QueryType: memory.QueryTypeRecent,
        Limit:     5,
    }

    recentResults, err := mgr.Search(ctx, recentReq)
    if err != nil {
        log.Fatalf("search recent failed: %v", err)
    }
    fmt.Printf("最近上下文检索 (%d 条):\n", len(recentResults))
    for _, r := range recentResults {
        fmt.Printf("  [%s] score=%.3f: %s\n",
            r.Entry.Layer, r.Score, truncate(r.Entry.Content, 60))
    }

    // 语义检索
    semanticReq := &memory.SearchRequest{
        Query:     "退货政策和运费谁承担",
        QueryType: memory.QueryTypeSemantic,
        Limit:     3,
    }

    semanticResults, err := mgr.Search(ctx, semanticReq)
    if err != nil {
        log.Fatalf("search semantic failed: %v", err)
    }
    fmt.Printf("\n语义检索 (%d 条):\n", len(semanticResults))
    for _, r := range semanticResults {
        fmt.Printf("  [%s] score=%.3f: %s\n",
            r.Entry.Layer, r.Score, truncate(r.Entry.Content, 70))
    }

    // 8. 查看监控指标
    fmt.Println("\n=== 监控指标 ===")
    metrics := mgr.GetMetrics()
    fmt.Printf("总操作数: %d\n", metrics.TotalOps)
    fmt.Printf("总错误数: %d\n", metrics.TotalErrors)

    for op, opMetrics := range metrics.Operations {
        fmt.Printf("操作 %s: count=%d, 成功率=%.1f%%\n",
            op, opMetrics.Count, opMetrics.SuccessRate*100)
    }

    for op, latMetrics := range metrics.Latencies {
        fmt.Printf("延迟 %s: avg=%v, p50=%v, p90=%v\n",
            op, latMetrics.Avg, latMetrics.P50, latMetrics.P90)
    }

    // 9. 等待异步同步
    fmt.Println("\n=== 等待异步同步完成 ===")
    time.Sleep(2 * time.Second)
    fmt.Println("异步同步完成")

    // 10. 获取单条记忆
    fmt.Println("\n=== 获取单条记忆 ===")
    entry, err := mgr.Get(ctx, workingEntry.ID)
    if err != nil {
        log.Printf("get entry failed: %v", err)
    } else {
        fmt.Printf("找到记忆: [%s] %s\n", entry.Layer, truncate(entry.Content, 50))
    }

    fmt.Println("\n演示完成!")
}

func truncate(s string, n int) string {
    runes := []rune(s)
    if len(runes) <= n {
        return s
    }
    return string(runes[:n]) + "..."
}

九、完整配置示例

复制代码
# config/memory.yaml
manager:
  default_write_layer: episodic
  write_mode: async          # sync | async | primary | batch
  sync_interval: 30s
  enable_auto_sync: true
  max_concurrent_ops: 10
  metrics_enabled: true

layers:
  working:
    type: in_memory
    config:
      max_entries: 1000
      default_ttl: 30m
      cleanup_interval: 5m
  
  episodic:
    type: chroma
    config:
      host: localhost
      port: 8000
      collection: user_memories
      vector_dim: 768
      distance_fn: cosine
      auto_embed: true
      embed_model: text-embedding-ada-002
      batch_size: 100
  
  semantic:
    type: neo4j
    config:
      uri: bolt://localhost:7687
      username: neo4j
      password: ${NEO4J_PASSWORD}
      database: memory
      max_connection_pool: 20
      connection_timeout: 10s

routing_rules:
  - name: recent_to_working
    priority: 100
    conditions:
      - field: query_type
        operator: eq
        value: recent
    target_layer: working
    weight: 10

  - name: semantic_to_knowledge
    priority: 80
    conditions:
      - field: query_type
        operator: eq
        value: semantic
    target_layer: semantic
    weight: 10

  - name: long_query_to_all
    priority: 60
    conditions:
      - field: query_length
        operator: gt
        value: 50
    target_layer: episodic
    weight: 5

sync_rules:
  - name: working_to_episodic
    source_layer: working
    target_layer: episodic
    direction: push
    filter:
      min_importance: 0.5
      max_age: 1h
    batch_size: 100
    interval: 30s

  - name: episodic_to_semantic
    source_layer: episodic
    target_layer: semantic
    direction: push
    filter:
      min_importance: 0.7
      memory_types: ["fact", "preference"]
    batch_size: 50
    interval: 1m

compress:
  enabled: true
  strategies:
    - type: summarize
      schedule: "0 3 * * *"  # 每天凌晨3点
      min_compress_ratio: 0.5
      target_layers: ["episodic"]
    - type: merge
      schedule: "0 4 * * *"
      similarity_threshold: 0.85
      target_layers: ["episodic"]
    - type: forget
      schedule: "0 5 * * *"
      dry_run: true
      policies:
        - name: working_cleanup
          target_layer: working
          threshold: 0.2
          max_age: 1h
        - name: episodic_archive
          target_layer: episodic
          threshold: 0.3
          max_age: 30d
          archive_enabled: true

monitoring:
  prometheus:
    enabled: true
    port: 9090
  logging:
    level: info
    slow_query_threshold: 500ms

十、本讲总结

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    第5讲 核心收获                              │
│                                                               │
│  ✅ 重构了 MemoryManager 统一接口:                            │
│     - 支持多种写入模式(同步/异步/主从/批量)                 │
│     - 自动路由到最优记忆层                                    │
│     - 跨层检索与结果聚合                                      │
│     - 去重与排序                                              │
│                                                               │
│  ✅ 实现了智能路由模块:                                       │
│     - 基于规则的层选择                                        │
│     - 条件匹配引擎                                            │
│     - 默认回退策略                                            │
│     - 命中统计                                                │
│                                                               │
│  ✅ 实现了层间同步管理器:                                     │
│     - 推/拉/双向三种同步模式                                  │
│     - 过滤器(重要性/时效/类型)                              │
│     - 定时同步循环                                            │
│     - 防环检测                                                │
│                                                               │
│  ✅ 实现了监控模块:                                           │
│     - 操作计数与成功率                                        │
│     - 延迟百分位(P50/P90/P99)                              │
│     - 层级别指标                                              │
│     - 滑动窗口采样                                            │
│                                                               │
│  ✅ 完整的配置管理与 YAML 示例                                │
│                                                               │
└─────────────────────────────────────────────────────────────┘

十一、下一讲预告

第6讲:《记忆系统的高可用与容灾------多副本、故障转移与数据恢复》

你将学到:

  • 多副本写入策略(Leader/Follower 架构)
  • 故障检测与自动转移
  • 数据备份与恢复(全量 + 增量)
  • 读写分离与负载均衡
  • 跨区域部署与数据同步
  • 混沌工程与故障演练

🧰 开发之余的小工具推荐

在处理记忆系统的配置文件和日志时,经常需要对 YAML/JSON 进行格式校验、对比不同版本的配置文件差异、或者对日志中的 Base64 编码数据进行解码调试。zz365.top 提供在线 YAML/JSON 互转工具、文本对比工具和 Base64 编解码器,所有处理均在浏览器本地完成,无需担心敏感配置信息泄露。特别适合在调试记忆系统多环境配置时快速比对差异。

相关推荐
月与清酒1 小时前
2026年10月AI大模型全景对比:GPT-6、Claude 5.5、Gemini 4、DeepSeek V4.1到底差在哪
人工智能·gpt·ai
小豆庄3 小时前
处理含车牌/人脸的隐私视频,如何做到数据不离机?
opencv·ai·云计算·视频
俊哥V4 小时前
2026-10-21 AI 日报:开放权重性价比、Agent 治理立法、Physical AI 落地
人工智能·ai
会思想的苇草i5 小时前
OpenClaw 添加技能(Skills) & 常用命令
ai·大模型·skills·openclaw
TomEval5 小时前
【测AI】第16篇:模型公平性与可解释性测试 — AI 的偏见与黑箱
人工智能·python·功能测试·ai·测试用例
博谷5 小时前
2026年GEO监测服务商的信源证据链怎么建?BUGOOAI布谷GEO的T0-T3分级与追溯逻辑
ai
codigger5 小时前
AI学会写代码之后,程序员节为什么更重要了
ai·程序员·ai编程·#人工智能
硅基流动6 小时前
超级峰的 AI 开发实践:从做出功能到持续交付|开发者说
人工智能·科技·ai
玫瑰互动GEO7 小时前
把GeoReady九维当工程项做:储能管理官网的AI就绪度改造笔记
人工智能·ai·ai搜索·seo优化·豆包·deepseek·geo优化