Skip to content

[AI优化] 预测性预加载 - 基于用户行为的智能缓存预热 #102

@hrygo

Description

@hrygo

问题描述

用户首次查询时需要完整的检索流程,延迟较高:

  • 日程查询需访问数据库
  • 笔记搜索需计算 embedding
  • 空闲时间查找需遍历日程

如果能预测用户的常见查询,提前加载到缓存,可以显著降低延迟。

建议方案

用户行为模式分析

// ai/prefetch/patterns.go
type UserPattern struct {
    UserID              int32
    MorningScheduleQuery bool           // 是否常在早上查日程
    EveningReview       bool           // 是否常在晚上回顾
    FrequentMemoTopics  []string       // 常搜索的笔记主题
    ActiveHours         []int          // 活跃时段 (0-23)
    WeekdayPatterns     map[int][]int  // 周几的活跃时段
    LastUpdated         time.Time
}

type PatternAnalyzer struct {
    store   PatternStore
    window  time.Duration // 分析窗口,如 30 天
}

func (a *PatternAnalyzer) Analyze(userID int32) (*UserPattern, error) {
    // 从历史日志分析用户行为
    logs, err := a.store.GetRecentLogs(userID, a.window)
    if err != nil {
        return nil, err
    }
    
    pattern := &UserPattern{UserID: userID}
    
    // 分析查询时间分布
    hourCounts := make(map[int]int)
    for _, log := range logs {
        hour := log.Timestamp.Hour()
        hourCounts[hour]++
    }
    
    // 判断早上查日程的习惯
    morningQueries := hourCounts[7] + hourCounts[8] + hourCounts[9]
    scheduleQueries := countScheduleQueries(logs)
    pattern.MorningScheduleQuery = morningQueries > 3 && scheduleQueries > 5
    
    // 分析常搜索的主题
    pattern.FrequentMemoTopics = extractFrequentTopics(logs)
    
    return pattern, nil
}

预加载器

// ai/prefetch/loader.go
type PredictiveLoader struct {
    analyzer    *PatternAnalyzer
    scheduler   *cron.Cron
    toolCache   *ToolResultCache
    embedding   embedding.Service
    maxParallel int
}

func (l *PredictiveLoader) Start() {
    // 每天凌晨分析所有活跃用户的模式
    l.scheduler.AddFunc("0 3 * * *", l.analyzeAllUsers)
    
    // 每小时检查并预加载
    l.scheduler.AddFunc("0 * * * *", l.prefetchForActiveUsers)
    
    l.scheduler.Start()
}

func (l *PredictiveLoader) prefetchForActiveUsers() {
    hour := time.Now().Hour()
    
    // 获取这个小时可能活跃的用户
    users := l.getUsersActiveAtHour(hour)
    
    sem := make(chan struct{}, l.maxParallel)
    for _, userID := range users {
        sem <- struct{}{}
        go func(uid int32) {
            defer func() { <-sem }()
            l.prefetchForUser(uid)
        }(userID)
    }
}

func (l *PredictiveLoader) prefetchForUser(userID int32) {
    pattern := l.analyzer.GetPattern(userID)
    if pattern == nil {
        return
    }
    
    ctx := context.Background()
    
    // 预加载今日日程
    if pattern.MorningScheduleQuery && isMorning() {
        l.warmupScheduleCache(ctx, userID, "today")
    }
    
    // 预加载常用笔记主题的 embedding
    for _, topic := range pattern.FrequentMemoTopics {
        l.warmupMemoEmbedding(ctx, userID, topic)
    }
}

func (l *PredictiveLoader) warmupScheduleCache(ctx context.Context, userID int32, timeRange string) {
    // 执行查询并缓存结果
    result, err := l.scheduleQueryTool.Execute(ctx, map[string]any{
        "user_id":    userID,
        "time_range": timeRange,
    })
    if err != nil {
        slog.Warn("prefetch schedule failed", "user_id", userID, "error", err)
        return
    }
    
    key := CacheKey{
        ToolName: "schedule_query",
        UserID:   userID,
        InputHash: hashInput(timeRange),
    }
    l.toolCache.Set(key, result, 10*time.Minute)
}

资源限制

type PrefetchConfig struct {
    Enabled       bool
    MaxParallel   int           // 最大并发预加载数
    MaxCPUPercent float64       // CPU 使用上限
    MaxMemoryMB   int           // 内存使用上限
    QuietHours    []int         // 静默时段 (不预加载)
}

func (l *PredictiveLoader) shouldPrefetch() bool {
    if !l.config.Enabled {
        return false
    }
    
    hour := time.Now().Hour()
    for _, quiet := range l.config.QuietHours {
        if hour == quiet {
            return false
        }
    }
    
    // 系统负载检查
    if getCPUUsage() > l.config.MaxCPUPercent {
        return false
    }
    
    return true
}

文件变更

  • ai/prefetch/patterns.go - 用户模式分析
  • ai/prefetch/loader.go - 预加载器
  • ai/prefetch/config.go - 配置
  • ai/prefetch/storage.go - 模式持久化
  • server/cron/prefetch_job.go - 定时任务注册

验收标准

  • 基于历史行为分析用户模式
  • 后台定时预加载不影响前台请求
  • 资源使用可控(CPU/内存限制)
  • 预加载仅对活跃用户生效
  • 可配置启用/禁用
  • 效果监控(命中率提升)

预估工时

5-7 天

技术风险

  • 需要足够的历史数据
  • 行为模式可能变化
  • 预加载可能浪费资源

相关 Issue

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions