287 lines
10 KiB
Go
287 lines
10 KiB
Go
package intent_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/bizdev"
|
|
"chat-agent-system/internal/intent"
|
|
)
|
|
|
|
func TestIntentRecognition_QueryProgress(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_test_1",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "现在这个便携超声立项评估到哪一步了?有啥风险没?", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.TopIntent.Intent != intent.IntentQueryProgress {
|
|
t.Errorf("expected intent QUERY_PROGRESS, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.Confidence < 0.8 {
|
|
t.Errorf("expected confidence >= 0.8, got %v", res.TopIntent.Confidence)
|
|
}
|
|
if len(res.Candidates) == 0 {
|
|
t.Errorf("expected candidate intents list")
|
|
}
|
|
}
|
|
|
|
func TestIntentRecognition_SupplementInfo(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_test_2",
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "客户刚才补充说,目标市场除了国内还想考虑欧洲 CE 认证", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.TopIntent.Intent != intent.IntentSupplementInfo {
|
|
t.Errorf("expected intent SUPPLEMENT_INFO, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.FilledSlots[intent.SlotFactKey] != "target_market" {
|
|
t.Errorf("expected slot target_market, got %s", res.TopIntent.FilledSlots[intent.SlotFactKey])
|
|
}
|
|
}
|
|
|
|
func TestIntentRecognition_AdvanceStage(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_test_3",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "摸底表我看过了,没问题,可以进入方案论证阶段了", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.TopIntent.Intent != intent.IntentAdvanceStage {
|
|
t.Errorf("expected intent ADVANCE_STAGE, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
|
|
t.Errorf("expected target stage V0.5_feasibility, got %s", res.TopIntent.FilledSlots[intent.SlotTargetStage])
|
|
}
|
|
}
|
|
|
|
func TestIntentRecognition_TriggerTask(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_test_4",
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "先帮我出一版初步报价单看看", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.TopIntent.Intent != intent.IntentTriggerTask {
|
|
t.Errorf("expected intent TRIGGER_TASK, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.FilledSlots[intent.SlotTargetTask] != "cdo_quote" {
|
|
t.Errorf("expected target task cdo_quote, got %s", res.TopIntent.FilledSlots[intent.SlotTargetTask])
|
|
}
|
|
}
|
|
|
|
func TestIntentRecognition_CasualChat(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
res, err := rec.Recognize(context.Background(), "你好,请问你是谁?", nil)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.TopIntent.Intent != intent.IntentCasualChat {
|
|
t.Errorf("expected intent CASUAL_CHAT, got %s", res.TopIntent.Intent)
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// 三分支柔性消解核心测试集
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
func samplePendingExpectation() *intent.PendingExpectation {
|
|
optAdvance := intent.ExpectationOption{
|
|
OptionID: "1",
|
|
Title: "推进到 V0.5 方案成型与法规对齐",
|
|
Description: "小队将自动开展技术路线比选与分类判定",
|
|
Intent: intent.IntentAdvanceStage,
|
|
Slots: map[string]string{
|
|
intent.SlotTargetStage: string(bizdev.StageV05_Feasibility),
|
|
},
|
|
}
|
|
optRefresh := intent.ExpectationOption{
|
|
OptionID: "2",
|
|
Title: "局部刷新 V0.1 用户需求表",
|
|
Description: "根据新补充材料重新生成初筛画像",
|
|
Intent: intent.IntentTriggerTask,
|
|
Slots: map[string]string{
|
|
intent.SlotTargetTask: "intake",
|
|
},
|
|
}
|
|
|
|
return &intent.PendingExpectation{
|
|
ContextSnippet: "当前 V0.1 闪电摸底已就绪,建议推进到 V0.5",
|
|
DefaultOption: &optAdvance,
|
|
Options: []intent.ExpectationOption{optAdvance, optRefresh},
|
|
AllowAffirmative: true,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
// 用例 1(分支 1 极速兑现):"好", "行", "选1"
|
|
func TestThreeBranch_Branch1_DirectAffirmative(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_pending_1",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
PendingExpectation: samplePendingExpectation(),
|
|
}
|
|
|
|
testCases := []string{"好", "行", "可以", "没问题", "同意", "好的", "就按这个办", "选1", "1"}
|
|
for _, tc := range testCases {
|
|
res, err := rec.Recognize(context.Background(), tc, ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize '%s' failed: %v", tc, err)
|
|
}
|
|
if res.Branch != intent.BranchExpectationHit {
|
|
t.Errorf("input '%s': expected branch EXPECTATION_HIT, got %s", tc, res.Branch)
|
|
}
|
|
if res.TopIntent.Intent != intent.IntentAdvanceStage {
|
|
t.Errorf("input '%s': expected intent ADVANCE_STAGE, got %s", tc, res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
|
|
t.Errorf("input '%s': expected target stage V0.5, got %s", tc, res.TopIntent.FilledSlots[intent.SlotTargetStage])
|
|
}
|
|
if res.TopIntent.Confidence < 0.95 {
|
|
t.Errorf("input '%s': expected confidence >= 0.95, got %v", tc, res.TopIntent.Confidence)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 用例 2(分支 1 复合打补丁):"好,但目标市场改成欧洲CE"
|
|
func TestThreeBranch_Branch1_AffirmativeWithPatch(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_pending_2",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
PendingExpectation: samplePendingExpectation(),
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "好,但目标市场改成欧洲CE", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.Branch != intent.BranchExpectationHit {
|
|
t.Fatalf("expected branch EXPECTATION_HIT, got %s", res.Branch)
|
|
}
|
|
if res.TopIntent.Intent != intent.IntentAdvanceStage {
|
|
t.Errorf("expected intent ADVANCE_STAGE, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.FilledSlots[intent.SlotTargetStage] != string(bizdev.StageV05_Feasibility) {
|
|
t.Errorf("expected target stage V0.5, got %s", res.TopIntent.FilledSlots[intent.SlotTargetStage])
|
|
}
|
|
if res.TopIntent.FilledSlots["target_market"] != "欧洲CE" {
|
|
t.Errorf("expected patched target_market '欧洲CE', got '%s'", res.TopIntent.FilledSlots["target_market"])
|
|
}
|
|
if res.TopIntent.PatchSlots["target_market"] != "欧洲CE" {
|
|
t.Errorf("expected PatchSlots to have target_market '欧洲CE', got '%s'", res.TopIntent.PatchSlots["target_market"])
|
|
}
|
|
}
|
|
|
|
// 用例 3(分支 1 否定撤销):"先别推,等等"
|
|
func TestThreeBranch_Branch1_Decline(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_pending_3",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
PendingExpectation: samplePendingExpectation(),
|
|
}
|
|
|
|
testCases := []string{"先别推,等等", "算了吧", "先别", "先等等"}
|
|
for _, tc := range testCases {
|
|
res, err := rec.Recognize(context.Background(), tc, ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize '%s' failed: %v", tc, err)
|
|
}
|
|
if res.Branch != intent.BranchExpectationHit {
|
|
t.Errorf("input '%s': expected branch EXPECTATION_HIT, got %s", tc, res.Branch)
|
|
}
|
|
if res.TopIntent.Intent != intent.IntentDeclineExpectation {
|
|
t.Errorf("input '%s': expected intent DECLINE_EXPECTATION, got %s", tc, res.TopIntent.Intent)
|
|
}
|
|
if !res.TopIntent.IsDecline {
|
|
t.Errorf("input '%s': expected IsDecline to be true", tc)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 用例 4(分支 2 话题突发抢占):有挂起预期时,用户突然问新问题或补新事实
|
|
func TestThreeBranch_Branch2_NewIntentPreempt(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_pending_4",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
PendingExpectation: samplePendingExpectation(),
|
|
}
|
|
|
|
// 抢占场景 A: 突然提问技术问题
|
|
res1, err := rec.Recognize(context.Background(), "查一下探头功耗要怎么设计", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
if res1.Branch != intent.BranchNewIntentPreempt {
|
|
t.Errorf("expected branch NEW_INTENT_PREEMPT, got %s", res1.Branch)
|
|
}
|
|
if res1.TopIntent.Intent != intent.IntentQueryProgress {
|
|
t.Errorf("expected intent QUERY_PROGRESS, got %s", res1.TopIntent.Intent)
|
|
}
|
|
|
|
// 抢占场景 B: 突然补充事实
|
|
res2, err := rec.Recognize(context.Background(), "客户补充说预算大概15万", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
if res2.Branch != intent.BranchNewIntentPreempt {
|
|
t.Errorf("expected branch NEW_INTENT_PREEMPT, got %s", res2.Branch)
|
|
}
|
|
if res2.TopIntent.Intent != intent.IntentSupplementInfo {
|
|
t.Errorf("expected intent SUPPLEMENT_INFO, got %s", res2.TopIntent.Intent)
|
|
}
|
|
if res2.TopIntent.FilledSlots["budget"] != "15万" {
|
|
t.Errorf("expected budget '15万', got '%s'", res2.TopIntent.FilledSlots["budget"])
|
|
}
|
|
}
|
|
|
|
// 用例 5(分支 3 模糊分忧):"这个先放放,你觉得呢"
|
|
func TestThreeBranch_Branch3_AmbiguousConsult(t *testing.T) {
|
|
rec := intent.NewRecognizer()
|
|
ctx := &intent.ContextSnapshot{
|
|
SnapshotID: "ctx_pending_5",
|
|
CurrentStage: string(bizdev.StageV01_Triage),
|
|
PendingExpectation: samplePendingExpectation(),
|
|
}
|
|
|
|
res, err := rec.Recognize(context.Background(), "这个先放放,你觉得呢", ctx)
|
|
if err != nil {
|
|
t.Fatalf("recognize failed: %v", err)
|
|
}
|
|
|
|
if res.Branch != intent.BranchAmbiguousFallback {
|
|
t.Errorf("expected branch AMBIGUOUS_FALLBACK, got %s", res.Branch)
|
|
}
|
|
if res.TopIntent.Intent != intent.IntentCasualChat {
|
|
t.Errorf("expected intent CASUAL_CHAT, got %s", res.TopIntent.Intent)
|
|
}
|
|
if res.TopIntent.ClarificationQuestion == "" {
|
|
t.Errorf("expected non-empty clarification question for branch 3")
|
|
}
|
|
}
|