package agent import ( "strings" "testing" ) // TestFallbackSummary 验证 LLM 压缩失败时的兜底摘要: // 纯文本、保留最近消息要点、无孤立 tool 消息。 func TestFallbackSummary(t *testing.T) { history := []Message{ {Role: "user", Content: stringPointer("第一轮任务:分析日志")}, {Role: "assistant", Content: stringPointer("已确认方案,开始执行")}, {Role: "assistant", ToolCalls: []ToolCall{{ID: "c1", Type: "function", Function: ToolFunctionCall{Name: "run_bash", Arguments: `{"command":"ls"}`}}}}, {Role: "tool", ToolCallID: "c1", Content: stringPointer("file.txt")}, {Role: "user", Content: stringPointer("继续,报告结果")}, } summary := fallbackSummary(history) if summary == "" { t.Fatal("fallbackSummary 不应返回空") } if strings.Contains(summary, "tool_calls") || strings.Contains(summary, "\"id\"") { t.Fatalf("fallbackSummary 必须为纯文本,不得包含结构化 tool_calls: %s", summary) } if !strings.Contains(summary, "用户") || !strings.Contains(summary, "助手") || !strings.Contains(summary, "工具结果") { t.Fatalf("fallbackSummary 应包含用户/助手/工具结果要点: %s", summary) } if !strings.Contains(summary, "调用了 run_bash") { t.Fatalf("fallbackSummary 应记录工具调用名: %s", summary) } } // TestCompactionInputBudget 验证单次压缩输入预算随窗口放大而放大, // 且始终留出输出与安全余量。 func TestCompactionInputBudget(t *testing.T) { a := &Agent{cfg: Config{MaxContextTokens: 96000, CompactionTokens: 4096, StreamOutputTokens: 4096}} budget := a.compactionInputBudget() if budget <= 0 || budget >= 96000 { t.Fatalf("预算应介于 (0, 96000),got %d", budget) } // 窗口放大后预算也应放大 a2 := &Agent{cfg: Config{MaxContextTokens: 48000, CompactionTokens: 4096, StreamOutputTokens: 4096}} if a2.compactionInputBudget() >= budget { t.Fatalf("窗口更大的预算应更大,got %d vs %d", a2.compactionInputBudget(), budget) } // 极端小窗口时兜底下限 4096 a3 := &Agent{cfg: Config{MaxContextTokens: 2000, CompactionTokens: 512, StreamOutputTokens: 512}} if got := a3.compactionInputBudget(); got < 4096 { t.Fatalf("小窗口预算应有下限,got %d", got) } } // TestEstimateMessageTokens 验证单条消息估算包含 content、tool_calls 与开销。 func TestEstimateMessageTokens(t *testing.T) { m := Message{ Role: "assistant", Content: stringPointer("你好"), ToolCalls: []ToolCall{{ ID: "c1", Type: "function", Function: ToolFunctionCall{Name: "read_file", Arguments: `{"path":"a.txt"}`}, }}, } n := estimateMessageTokens(m) if n <= 0 { t.Fatalf("估算应大于 0,got %d", n) } plain := estimateMessageTokens(Message{Role: "user", Content: stringPointer("你好")}) if n <= plain { t.Fatalf("含 tool_calls 的消息估算应大于纯文本,got %d vs %d", n, plain) } }