From eedda67aad8735a33caaab8e2bb5db6af439b0c6 Mon Sep 17 00:00:00 2001 From: WeiCHEN Date: Mon, 7 Sep 2026 22:11:54 +0800 Subject: [PATCH] feat(cli): add three-branch interaction feedback and comprehensive verification test cases Co-authored-by: Cursor --- cmd/bizdev-cli/main.go | 14 ++--- cmd/bizdev-cli/main_test.go | 101 ++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/cmd/bizdev-cli/main.go b/cmd/bizdev-cli/main.go index 5e7c2c28..017ad9f8 100644 --- a/cmd/bizdev-cli/main.go +++ b/cmd/bizdev-cli/main.go @@ -411,7 +411,7 @@ func handleChatMode( os.Exit(1) } top := resp.IntentResult.TopIntent - fmt.Printf("[意图识别: %s (置信度: %.0f%%)] - %s\n", top.Intent, top.Confidence*100, top.Reasoning) + fmt.Printf("[意图识别: %s (置信度: %.0f%%) | 消解分支: %s] - %s\n", top.Intent, top.Confidence*100, resp.Branch, top.Reasoning) fmt.Printf("\n%s\n", resp.ReplyText) if resp.AdvisorBrief != nil && (top.Intent == intent.IntentAdvanceStage || top.Intent == intent.IntentQueryProgress) { printAdvisorBrief(resp.AdvisorBrief) @@ -427,10 +427,12 @@ func handleChatMode( fmt.Printf("当前阶段: %s | 用户身份: %s (%s)\n", userCtx.Project.CurrentStage, userCtx.User.Name, userCtx.User.Role) fmt.Printf("可用动作: %v\n", userCtx.AvailableActions) fmt.Println("------------------------------------------------------------------") - fmt.Println("💡 提示:您可以直接输入自然语言,例如:") - fmt.Println(" • \"现在这个便携超声立项评估到哪一步了?有啥风险没?\"") - fmt.Println(" • \"客户补充说目标市场除了国内还想考虑欧洲 CE 认证\"") - fmt.Println(" • \"我看摸底结果可以,进入方案论证阶段吧\"") + fmt.Println("💡 参谋互动小贴士(三分支柔性消解):") + fmt.Println(" • 极速兑现(分支 1):当参谋给出下一步建议时,回复“好”、“行”或“1”直接推进") + fmt.Println(" • 复合补丁(分支 1):可以肯定并同时附带微调,如:“好,但目标市场改成欧洲CE”") + fmt.Println(" • 暂缓撤销(分支 1):如果不想推进,直接说:“先别推,等等”") + fmt.Println(" • 突发抢占(分支 2):随时发起新问题,如:“查一下探头功耗要怎么设计”") + fmt.Println(" • 模糊分忧(分支 3):拿不定主意时,可以说:“这个先放放,你觉得呢”") fmt.Println(" • 输入 'exit' 或 'quit' 退出") fmt.Println("==================================================================") @@ -456,7 +458,7 @@ func handleChatMode( } top := resp.IntentResult.TopIntent - fmt.Printf("\n[智能参谋] (识别意图: %s | 置信度: %.0f%%)\n", top.Intent, top.Confidence*100) + fmt.Printf("\n[智能参谋] (消解分支: %s | 核心意图: %s | 置信度: %.0f%%)\n", resp.Branch, top.Intent, top.Confidence*100) fmt.Println(resp.ReplyText) if resp.AdvisorBrief != nil && (top.Intent == intent.IntentAdvanceStage || top.Intent == intent.IntentQueryProgress) { diff --git a/cmd/bizdev-cli/main_test.go b/cmd/bizdev-cli/main_test.go index 913217bf..f115c657 100644 --- a/cmd/bizdev-cli/main_test.go +++ b/cmd/bizdev-cli/main_test.go @@ -235,3 +235,104 @@ func TestBizdevCLIChatQueries(t *testing.T) { t.Errorf("expected stage advance success, got: %s", string(out4)) } } + +func TestBizdevCLIThreeBranchQueries(t *testing.T) { + tmpDir := t.TempDir() + envContent := "ENV=dev\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\n" + envPath := filepath.Join(tmpDir, ".env") + _ = os.WriteFile(envPath, []byte(envContent), 0o644) + + // 用例 1: 极速兑现 (好) + cmd1 := exec.Command("go", "run", "./cmd/bizdev-cli", + "-env", envPath, + "-backend", "stub", + "-chat", + "-query", "好", + "-project", "掌上便携超声", + ) + cmd1.Dir = moduleRoot(t) + out1, err := cmd1.CombinedOutput() + if err != nil { + t.Fatalf("query 1 failed: %v, out: %s", err, string(out1)) + } + if !strings.Contains(string(out1), "消解分支: EXPECTATION_HIT") { + t.Errorf("expected EXPECTATION_HIT, got: %s", string(out1)) + } + + // 用例 2: 复合打补丁 (好,但目标市场改成欧洲CE) + cmd2 := exec.Command("go", "run", "./cmd/bizdev-cli", + "-env", envPath, + "-backend", "stub", + "-chat", + "-query", "好,但目标市场改成欧洲CE", + "-project", "掌上便携超声", + ) + cmd2.Dir = moduleRoot(t) + out2, err := cmd2.CombinedOutput() + if err != nil { + t.Fatalf("query 2 failed: %v, out: %s", err, string(out2)) + } + if !strings.Contains(string(out2), "消解分支: EXPECTATION_HIT") || !strings.Contains(string(out2), "target_market") { + t.Errorf("expected EXPECTATION_HIT with target_market patch, got: %s", string(out2)) + } + + // 用例 3: 否定暂缓 (先别推,等等) + cmd3 := exec.Command("go", "run", "./cmd/bizdev-cli", + "-env", envPath, + "-backend", "stub", + "-chat", + "-query", "先别推,等等", + "-project", "掌上便携超声", + ) + cmd3.Dir = moduleRoot(t) + out3, err := cmd3.CombinedOutput() + if err != nil { + t.Fatalf("query 3 failed: %v, out: %s", err, string(out3)) + } + if !strings.Contains(string(out3), "消解分支: EXPECTATION_HIT") || !strings.Contains(string(out3), "DECLINE_EXPECTATION") { + t.Errorf("expected DECLINE_EXPECTATION, got: %s", string(out3)) + } + if !strings.Contains(string(out3), "听您的") { + t.Errorf("expected polite advisor reply: %s", string(out3)) + } + + // 用例 4: 话题突发抢占 (查一下探头功耗要怎么设计) + cmd4 := exec.Command("go", "run", "./cmd/bizdev-cli", + "-env", envPath, + "-backend", "stub", + "-chat", + "-query", "查一下探头功耗要怎么设计", + "-project", "掌上便携超声", + ) + cmd4.Dir = moduleRoot(t) + out4, err := cmd4.CombinedOutput() + if err != nil { + t.Fatalf("query 4 failed: %v, out: %s", err, string(out4)) + } + if !strings.Contains(string(out4), "消解分支: NEW_INTENT_PREEMPT") { + t.Errorf("expected NEW_INTENT_PREEMPT, got: %s", string(out4)) + } + if !strings.Contains(string(out4), "功耗") { + t.Errorf("expected technical answer in reply: %s", string(out4)) + } + + // 用例 5: 模糊分忧 (这个先放放,你觉得呢) + cmd5 := exec.Command("go", "run", "./cmd/bizdev-cli", + "-env", envPath, + "-backend", "stub", + "-chat", + "-query", "这个先放放,你觉得呢", + "-project", "掌上便携超声", + ) + cmd5.Dir = moduleRoot(t) + out5, err := cmd5.CombinedOutput() + if err != nil { + t.Fatalf("query 5 failed: %v, out: %s", err, string(out5)) + } + if !strings.Contains(string(out5), "消解分支: AMBIGUOUS_FALLBACK") { + t.Errorf("expected AMBIGUOUS_FALLBACK, got: %s", string(out5)) + } + if !strings.Contains(string(out5), "审慎考量") && !strings.Contains(string(out5), "参谋建议") { + t.Errorf("expected consultative advisor answer in reply: %s", string(out5)) + } +}