// Package context - policy.go 定义压缩策略接口和编程场景的默认实现. // // 升华改进(ELEVATED): 策略可插拔.编程场景 PreserveKeywords 返回 // "file paths, function names",仓储场景返回 "order numbers, SKU codes". // 不同场景不改引擎代码,只换策略. // 替代方案:硬编码编程关键词(原始设计,简单但锁死在编程场景). package context import ( "encoding/json" "strings" ) // CompactionPolicy 定义特定场景的压缩策略. // 编程场景提供默认实现,其他场景(仓储,法律等)注册自己的实现. // // Shape: synchronous callback. Engine calls the policy's methods during // compression decisions; consumers inject per-scenario implementations via // engine.Config.CompactionPolicies (stackable, multiple policies coexist). // // 形态: 同步回调. 引擎在压缩决策时调策略方法; 消费者经 // engine.Config.CompactionPolicies 注入场景专用实现 (可叠加, 多策略并存). type CompactionPolicy interface { // Name 返回策略的唯一标识名称(用于日志和动态移除). // 升华改进(ELEVATED): 新增方法,支持 CompositePolicy 按名称移除策略. // 替代方案:用指针比较来识别策略(不直观且不可序列化). Name() string // PreserveKeywords 压缩摘要中必须保留的关键信息描述 PreserveKeywords() string // ScoreMessageImportance 评估单条消息的重要性(0.0-1.0) // 低分消息优先被摘要化,高分消息尽量保留原文 ScoreMessageImportance(role string, content string) float64 // MaxRecentRoundsToKeep 保留最近多少轮完整对话 MaxRecentRoundsToKeep() int // PreprocessForCompaction 压缩前预处理(裁剪大响应,移除重复等) PreprocessForCompaction(msgs []CompactMessage) []CompactMessage } // DefaultCodePolicy 编程场景的默认压缩策略. type DefaultCodePolicy struct{} // Name 返回策略名称. func (p *DefaultCodePolicy) Name() string { return "code" } // PreserveKeywords 编程场景需要保留的关键信息. func (p *DefaultCodePolicy) PreserveKeywords() string { return "file paths, function names, class names, error messages, " + "git commands, test results, key decisions and their rationale" } // ScoreMessageImportance 评估编程场景中单条消息的重要性. // 含错误信息,文件路径,决策语言的消息更重要. func (p *DefaultCodePolicy) ScoreMessageImportance(role string, content string) float64 { score := 0.3 // 含错误信息 → 重要 if containsErrorPattern(content) { score += 0.3 } // 含文件路径 → 中等重要 if containsFilePath(content) { score += 0.1 } // 含决策语言 → 重要 if containsDecisionLanguage(content) { score += 0.2 } // 工具结果中的长输出 → 低重要 if len(content) > 10000 { score -= 0.2 } return clamp(score, 0.0, 1.0) } // MaxRecentRoundsToKeep 编程场景保留最近 5 轮对话. func (p *DefaultCodePolicy) MaxRecentRoundsToKeep() int { return 5 } // PreprocessForCompaction 编程场景的压缩预处理. // 1. 移除图像块(图像对摘要无帮助但占大量 token) // 2. 截断超长工具结果(>8K 只保留头尾) func (p *DefaultCodePolicy) PreprocessForCompaction(msgs []CompactMessage) []CompactMessage { result := make([]CompactMessage, len(msgs)) for i, msg := range msgs { result[i] = msg // 尝试解析为 content block 数组 var blocks []map[string]any if err := json.Unmarshal(msg.Content, &blocks); err != nil { continue } modified := false filtered := make([]map[string]any, 0, len(blocks)) for _, block := range blocks { blockType, _ := block["type"].(string) // 移除图像块 if blockType == "image" { modified = true continue } // 截断超长工具结果 if blockType == "tool_result" { if content, ok := block["content"].(string); ok && len(content) > 8000 { block["content"] = content[:4000] + "\n\n... [preprocessed: truncated] ...\n\n" + content[len(content)-4000:] modified = true } } filtered = append(filtered, block) } if modified { if newContent, err := json.Marshal(filtered); err == nil { result[i].Content = newContent } } } return result } // --- 辅助函数 --- // containsErrorPattern 检测内容中是否包含错误模式. func containsErrorPattern(content string) bool { lower := strings.ToLower(content) patterns := []string{ "error", "err:", "failed", "failure", "panic", "exception", "traceback", "fatal", "cannot", "unable to", } for _, p := range patterns { if strings.Contains(lower, p) { return true } } return false } // containsFilePath 检测内容中是否包含文件路径. func containsFilePath(content string) bool { // 检查绝对路径 if strings.Contains(content, "/") { // 常见代码文件扩展名 exts := []string{".go", ".ts", ".tsx", ".js", ".py", ".rs", ".java", ".yaml", ".json"} for _, ext := range exts { if strings.Contains(content, ext) { return true } } } return false } // containsDecisionLanguage 检测内容中是否包含决策相关语言. func containsDecisionLanguage(content string) bool { lower := strings.ToLower(content) patterns := []string{ "decided", "decision", "chose", "chosen", "approach", "instead of", "rather than", "because", "trade-off", "alternative", "strategy", "design", } for _, p := range patterns { if strings.Contains(lower, p) { return true } } return false } // clamp 将值限制在 [min, max] 范围内. func clamp(v, min, max float64) float64 { if v < min { return min } if v > max { return max } return v }