问题描述
用户首次查询时需要完整的检索流程,延迟较高:
- 日程查询需访问数据库
- 笔记搜索需计算 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 - 定时任务注册
验收标准
预估工时
5-7 天
技术风险
- 需要足够的历史数据
- 行为模式可能变化
- 预加载可能浪费资源
相关 Issue
问题描述
用户首次查询时需要完整的检索流程,延迟较高:
如果能预测用户的常见查询,提前加载到缓存,可以显著降低延迟。
建议方案
用户行为模式分析
预加载器
资源限制
文件变更
ai/prefetch/patterns.go- 用户模式分析ai/prefetch/loader.go- 预加载器ai/prefetch/config.go- 配置ai/prefetch/storage.go- 模式持久化server/cron/prefetch_job.go- 定时任务注册验收标准
预估工时
5-7 天
技术风险
相关 Issue