一、统一记忆系统架构
┌──────────────────────────────────────────────────────────────────────────────┐
│ 统一记忆系统架构 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ 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 编解码器,所有处理均在浏览器本地完成,无需担心敏感配置信息泄露。特别适合在调试记忆系统多环境配置时快速比对差异。