// Package context - composite.go 实现多策略叠加的 CompositePolicy. // // 宪法第 8 条「叠加而非替换」的核心实现. // 现实中一个 Agent 可能同时处理编程和仓储任务, // 编程策略保留"文件路径,函数名",仓储策略保留"订单号,SKU", // CompositePolicy 合并两者,确保跨场景切换时不丢关键信息. // // 使用示例: // // codePolicy := &DefaultCodePolicy{} // warehousePolicy := &WarehousePolicy{} // combined := NewCompositePolicy(codePolicy, warehousePolicy) // compressor.SetPolicy(combined) // // 压缩时同时保留文件路径和订单号 // // 叠加规则: // - PreserveKeywords: 合并所有策略的关键词,去重 // - ScoreMessageImportance: 取所有策略的最高分(宁可多保留不可多丢弃) // - MaxRecentRoundsToKeep: 取最大值(保留更多上下文) // - PreprocessForCompaction: 依次应用所有策略的预处理 // // 升华改进(ELEVATED): 多策略叠加而非单策略替换. // 替代方案:Config 中只传一个 Policy(原始设计,锁定单场景). package context import ( "strings" "sync" ) // CompositePolicy 将多个 CompactionPolicy 叠加为一个. // // 使用示例: // // code := &DefaultCodePolicy{} // warehouse := &WarehousePolicy{} // 自定义实现 // composite := NewCompositePolicy(code, warehouse) // // // 关键词合并: // // composite.PreserveKeywords() → // // "file paths, function names, ..., order numbers, SKU codes" // // // 重要性评分取最高: // // code 给 0.3, warehouse 给 0.9 → 最终 0.9 // // // 上下文轮数取最大: // // code 返回 5, warehouse 返回 8 → 最终 8 // // // 预处理依次执行: // // code 先移除图像 → warehouse 再移除过期订单 type CompositePolicy struct { mu sync.RWMutex policies []CompactionPolicy fallback CompactionPolicy // 零策略时的兜底(避免每个方法重复 new) } // NewCompositePolicy 创建叠加策略. // 至少传一个策略,否则兜底使用 DefaultCodePolicy. // // 使用示例: // // // 单策略退化--行为与直接使用该策略一致 // cp := NewCompositePolicy(&DefaultCodePolicy{}) // // // 多策略叠加--编程 + 仓储 // cp := NewCompositePolicy(&DefaultCodePolicy{}, &WarehousePolicy{}) // // // 零策略兜底--自动使用 DefaultCodePolicy // cp := NewCompositePolicy() func NewCompositePolicy(policies ...CompactionPolicy) *CompositePolicy { cp := &CompositePolicy{ policies: make([]CompactionPolicy, 0, len(policies)), fallback: &DefaultCodePolicy{}, } for _, p := range policies { if p != nil { cp.policies = append(cp.policies, p) } } return cp } // Name 返回叠加策略的名称,由所有子策略名称组合而成. func (cp *CompositePolicy) Name() string { cp.mu.RLock() defer cp.mu.RUnlock() if len(cp.policies) == 0 { return "composite(empty)" } names := make([]string, len(cp.policies)) for i, p := range cp.policies { names[i] = p.Name() } return "composite(" + strings.Join(names, "+") + ")" } // PreserveKeywords 合并所有策略的关键词,用 ", " 连接,去重. // // 编程策略: "file paths, function names, error messages" // 仓储策略: "order numbers, SKU codes, error messages" // 叠加结果: "file paths, function names, error messages, order numbers, SKU codes" func (cp *CompositePolicy) PreserveKeywords() string { cp.mu.RLock() defer cp.mu.RUnlock() if len(cp.policies) == 0 { return cp.fallback.PreserveKeywords() } seen := make(map[string]bool) var parts []string for _, p := range cp.policies { kw := p.PreserveKeywords() for _, segment := range strings.Split(kw, ",") { trimmed := strings.TrimSpace(segment) if trimmed == "" { continue } lower := strings.ToLower(trimmed) if !seen[lower] { seen[lower] = true parts = append(parts, trimmed) } } } return strings.Join(parts, ", ") } // ScoreMessageImportance 取所有策略的最高分. // // 精妙之处(CLEVER): 取最高分而非平均分. // 如果编程策略给 0.3(普通文本),但仓储策略给 0.9(含订单号), // 最终 0.9 -- 这条消息对仓储场景很重要,不应该被压缩掉. // 替代方案:加权平均(更"公平",但会稀释某个场景的高重要性消息). func (cp *CompositePolicy) ScoreMessageImportance(role string, content string) float64 { cp.mu.RLock() defer cp.mu.RUnlock() if len(cp.policies) == 0 { return cp.fallback.ScoreMessageImportance(role, content) } maxScore := 0.0 for _, p := range cp.policies { score := p.ScoreMessageImportance(role, content) if score > maxScore { maxScore = score } } return maxScore } // MaxRecentRoundsToKeep 取所有策略的最大值. // 不同场景需要不同的上下文深度,取最大确保不遗漏. func (cp *CompositePolicy) MaxRecentRoundsToKeep() int { cp.mu.RLock() defer cp.mu.RUnlock() if len(cp.policies) == 0 { return cp.fallback.MaxRecentRoundsToKeep() } maxRounds := 0 for _, p := range cp.policies { rounds := p.MaxRecentRoundsToKeep() if rounds > maxRounds { maxRounds = rounds } } return maxRounds } // PreprocessForCompaction 依次应用所有策略的预处理. // // 精妙之处(CLEVER): 顺序执行而非并行,因为前一个策略的裁剪 // 可能影响后一个策略的判断(例如裁掉了图像后文本变短). func (cp *CompositePolicy) PreprocessForCompaction(msgs []CompactMessage) []CompactMessage { cp.mu.RLock() defer cp.mu.RUnlock() if len(cp.policies) == 0 { return cp.fallback.PreprocessForCompaction(msgs) } result := msgs for _, p := range cp.policies { result = p.PreprocessForCompaction(result) } return result } // Add 动态添加策略(运行时场景切换). // // 使用示例: // // composite := NewCompositePolicy(&DefaultCodePolicy{}) // // 用户开始处理仓储任务 // composite.Add(&WarehousePolicy{}) // // 后续压缩将同时保留编程和仓储关键词 // // Add 动态添加策略(运行时场景切换). // 如果已有同名策略,替换(upsert 语义).两个同名策略是 bug 不是 feature. func (cp *CompositePolicy) Add(p CompactionPolicy) { if p == nil { return } cp.mu.Lock() defer cp.mu.Unlock() // upsert:同名替换 for i, existing := range cp.policies { if existing.Name() == p.Name() { cp.policies[i] = p return } } cp.policies = append(cp.policies, p) } // Remove 按名称移除策略(场景卸载时). // 返回是否成功移除. // // 使用示例: // // composite.Remove("warehouse") // // 后续压缩只保留编程关键词 func (cp *CompositePolicy) Remove(name string) bool { cp.mu.Lock() defer cp.mu.Unlock() for i, p := range cp.policies { if p.Name() == name { cp.policies = append(cp.policies[:i], cp.policies[i+1:]...) return true } } return false } // Len 返回叠加策略的数量. func (cp *CompositePolicy) Len() int { cp.mu.RLock() defer cp.mu.RUnlock() return len(cp.policies) }