package chat_test import ( "context" "os" "path/filepath" "strings" "testing" "chat-agent-system/internal/bizdev" "chat-agent-system/internal/chat" "chat-agent-system/internal/coordinator" "chat-agent-system/internal/intent" ) // TestRealE2EWorkspaceChatSimulation 基于真实 Agent 生成的业务工作区开展端到端参谋对话与三分支消解实机验证 func TestRealE2EWorkspaceChatSimulation(t *testing.T) { if testing.Short() { t.Skip("skipping workspace-bound E2E in short mode") } wsDir := "data/dev/workspaces/bizdev/intake_1788792827436879000" if _, err := os.Stat(wsDir); os.IsNotExist(err) { wsDir = filepath.Join("..", "..", wsDir) } if _, err := os.Stat(wsDir); os.IsNotExist(err) { t.Skipf("workspace not found: %s", wsDir) } u := coordinator.UserInfo{ ID: "u_test_user", Name: "张工", Role: "研发负责人", ProjectRole: "composer", } assembler := coordinator.NewContextAssembler() userCtx, err := assembler.AssembleFromWorkspace(wsDir, u) if err != nil { t.Fatalf("failed to assemble context from real workspace: %v", err) } if userCtx.Project.Name != "研发流程辅助智能体系统" { t.Errorf("expected project name '研发流程辅助智能体系统', got %s", userCtx.Project.Name) } if userCtx.Project.CustomerName != "北京水木东方科技有限公司" { t.Errorf("expected customer name '北京水木东方科技有限公司', got %s", userCtx.Project.CustomerName) } rec := intent.NewRecognizer() engine := coordinator.NewCoordinatorEngine(nil) // 参谋交互层验证协调决策与上下文更新 session := chat.NewSession("real_e2e_session", userCtx, rec, engine) // Turn 1: 真实工作区状态与背景查询 (分支 2: 新意图抢占 QUERY_PROGRESS) t.Log("=== Turn 1: 询问真实工作区当前状态与风险 ===") resp1, err := session.SendMessage(context.Background(), "帮我看看当前项目的整体进展如何,材料到底够不够?") if err != nil { t.Fatalf("Turn 1 failed: %v", err) } if resp1.Branch != intent.BranchNewIntentPreempt { t.Errorf("Turn 1 expected NEW_INTENT_PREEMPT, got %s", resp1.Branch) } if resp1.IntentResult.TopIntent.Intent != intent.IntentQueryProgress { t.Errorf("Turn 1 expected QUERY_PROGRESS, got %s", resp1.IntentResult.TopIntent.Intent) } if !strings.Contains(resp1.ReplyText, "研发流程辅助智能体系统") { t.Errorf("Turn 1 reply should mention project name, got: %s", resp1.ReplyText) } t.Logf("Turn 1 响应:\n%s\n", resp1.ReplyText) // Turn 2: 补充事实黑板 (分支 2: 补充信息 SUPPLEMENT_INFO) t.Log("=== Turn 2: 补充事实设定至真实项目黑板 ===") resp2, err := session.SendMessage(context.Background(), "客户确定目标市场不仅是国内,还要扩展到欧洲CE") if err != nil { t.Fatalf("Turn 2 failed: %v", err) } if resp2.Branch != intent.BranchNewIntentPreempt { t.Errorf("Turn 2 expected NEW_INTENT_PREEMPT, got %s", resp2.Branch) } if resp2.IntentResult.TopIntent.Intent != intent.IntentSupplementInfo { t.Errorf("Turn 2 expected SUPPLEMENT_INFO, got %s", resp2.IntentResult.TopIntent.Intent) } if !strings.Contains(session.UserCtx.BlackboardFacts["target_market"], "欧洲CE") { t.Errorf("target_market not properly updated in blackboard: %v", session.UserCtx.BlackboardFacts) } t.Logf("Turn 2 响应:\n%s\n", resp2.ReplyText) // Turn 3: 模糊分忧/咨询 (分支 3: 模糊降级 AMBIGUOUS_FALLBACK) t.Log("=== Turn 3: 用户犹豫不决,参谋给出客观建议与选项 ===") resp3, err := session.SendMessage(context.Background(), "这个先放放,你觉得呢") if err != nil { t.Fatalf("Turn 3 failed: %v", err) } if resp3.Branch != intent.BranchAmbiguousFallback { t.Errorf("Turn 3 expected AMBIGUOUS_FALLBACK, got %s", resp3.Branch) } if !strings.Contains(resp3.ReplyText, "完全理解") && !strings.Contains(resp3.ReplyText, "建议") { t.Errorf("Turn 3 expected advisor consultation style, got: %s", resp3.ReplyText) } t.Logf("Turn 3 响应:\n%s\n", resp3.ReplyText) // Turn 4: 极速肯定兑现推进 (分支 1: 挂起预期命中 EXPECTATION_HIT) t.Log("=== Turn 4: 用户极速回复“好”,直接兑现挂起预期推进至 V0.5 ===") resp4, err := session.SendMessage(context.Background(), "好") if err != nil { t.Fatalf("Turn 4 failed: %v", err) } if resp4.Branch != intent.BranchExpectationHit { t.Errorf("Turn 4 expected EXPECTATION_HIT, got %s", resp4.Branch) } if resp4.IntentResult.TopIntent.Intent != intent.IntentAdvanceStage { t.Errorf("Turn 4 expected ADVANCE_STAGE, got %s", resp4.IntentResult.TopIntent.Intent) } if session.UserCtx.Project.CurrentStage != bizdev.StageV05_Feasibility { t.Errorf("expected project advanced to V0.5, got %s", session.UserCtx.Project.CurrentStage) } t.Logf("Turn 4 响应:\n%s\n", resp4.ReplyText) // Turn 5: 否定/暂缓挂起预期 (分支 1: 显式拒绝 DECLINE_EXPECTATION) t.Log("=== Turn 5: 用户对新阶段的下一步建议表示暂缓 ===") resp5, err := session.SendMessage(context.Background(), "先别推,等等") if err != nil { t.Fatalf("Turn 5 failed: %v", err) } if resp5.Branch != intent.BranchExpectationHit { t.Errorf("Turn 5 expected EXPECTATION_HIT, got %s", resp5.Branch) } if resp5.IntentResult.TopIntent.Intent != intent.IntentDeclineExpectation { t.Errorf("Turn 5 expected DECLINE_EXPECTATION, got %s", resp5.IntentResult.TopIntent.Intent) } if !strings.Contains(resp5.ReplyText, "听您的") { t.Errorf("Turn 5 expected polite reply, got: %s", resp5.ReplyText) } if session.UserCtx.PendingExpectation != nil { t.Errorf("expected PendingExpectation to be cleared after decline") } t.Logf("Turn 5 响应:\n%s\n", resp5.ReplyText) }