package capacity import ( "context" "testing" ) // uRecall builds an Input over messages with the given recall (L4 feed), mirroring // the in() helper but adding the recall hook. func inRecall(messages []string, recall []RecallItem) Input { in := in(messages...) in.Recall = recall return in } // taskRecord is a canonical intent record written back by RecordIntent, carrying a // learned task expression for the closed loop. func taskRecord(expr string, conf float64) string { return formatIntentRecord(Judgment{IsTask: true, TaskKind: taskKindOps, Confidence: conf, Text: expr}) } func TestDecide_L4_WakesMissedTask_CanonicalRecord(t *testing.T) { d := New(activeConfig()) // "部署" is a strong verb but "搞一下那个部署" carries no imperative marker or // object noun, so L1 is undecided and L2 (single turn) is undecided too. A learned // habit (canonical record) that "搞一下这个部署" is a task must wake it (L4 fills // the gap 设计 §2.2). recall := []RecallItem{{ Content: taskRecord("搞一下这个部署", 0.85), Scope: "user", Score: 0.8, }} dec := d.Decide(context.Background(), inRecall([]string{"搞一下那个部署"}, recall)) if dec.State != StateAwake { t.Fatalf("state = %s, want awake (L4 habit learned 搞一下=办事)", dec.State) } if !contains(dec.AllowedTools, "terminal") { t.Fatalf("awake allowed should include terminal (ops habit), got %v", dec.AllowedTools) } if dec.Confidence < 0.6 { t.Fatalf("confidence = %v, want >= 0.6", dec.Confidence) } if !dec.Judgment.IsTask { t.Fatalf("judgment.IsTask = false, want true") } } func TestDecide_L4_WakesMissedTask_HabitMemory(t *testing.T) { d := New(activeConfig()) // A habit-style memory (not written by RecordIntent) that leans 办事 and shares the // phrase must also wake, without L1/L2 support. recall := []RecallItem{{ Content: "用户习惯:他习惯说搞一下就是要我办事", Scope: "user", Score: 0.5, }} dec := d.Decide(context.Background(), inRecall([]string{"搞一下那个脚本"}, recall)) if dec.State != StateAwake { t.Fatalf("state = %s, want awake (habit memory leans 办事)", dec.State) } } func TestDecide_L4_BoostsConfidenceOnAwake(t *testing.T) { d := New(activeConfig()) // L2 already wakes this follow-up at 0.66; a strong learned habit corroborating // the same task phrasing must raise the confidence (设计: 越用越准). without := d.Decide(context.Background(), in("帮我查一下资料", "还有吗")) if without.State != StateAwake || without.Confidence < 0.6 { t.Fatalf("baseline = state %s conf %v, want awake >= 0.6", without.State, without.Confidence) } recall := []RecallItem{{ Content: taskRecord("还有吗", 0.9), Scope: "user", }} with := d.Decide(context.Background(), inRecall([]string{"帮我查一下资料", "还有吗"}, recall)) if with.State != StateAwake { t.Fatalf("with-recall state = %s, want awake", with.State) } if with.Confidence <= without.Confidence { t.Fatalf("with-recall confidence = %v, want > baseline %v", with.Confidence, without.Confidence) } } func TestDecide_L4_MissingRecall_FallsBackToL1L2(t *testing.T) { d := New(activeConfig()) // No recall (nil or empty) keeps L1/L2 behavior identical. base := d.Decide(context.Background(), in("帮我查一下资料")) nilRecall := d.Decide(context.Background(), inRecall([]string{"帮我查一下资料"}, nil)) emptyRecall := d.Decide(context.Background(), inRecall([]string{"帮我查一下资料"}, []RecallItem{})) for _, got := range []Decision{nilRecall, emptyRecall} { if got.State != base.State || got.Confidence != base.Confidence { t.Fatalf("no-recall = state %s conf %v, want baseline %s %v", got.State, got.Confidence, base.State, base.Confidence) } } } func TestDecide_L4_IgnoresNonAllowedScope(t *testing.T) { d := New(activeConfig()) // A recalled item outside the allowed scopes (stranger / secret) must not feed L4 // even when it leans task and shares the phrase — 只读用户授权范围记忆. for _, scope := range []string{"stranger", "secret", "group"} { recall := []RecallItem{{ Content: "用户习惯:他说搞一下就是要我办事", Scope: scope, Score: 0.9, }} dec := d.Decide(context.Background(), inRecall([]string{"搞一下那个部署"}, recall)) if dec.State != StateQuiet { t.Fatalf("scope %q leaked into L4: state = %s, want quiet", scope, dec.State) } } } func TestDecide_L4_NonTaskHabit_StaysConservative(t *testing.T) { d := New(activeConfig()) // A recalled preference that is not a task habit (coffee) must not wake a neutral // turn — 默认保守 still holds. recall := []RecallItem{{ Content: "用户习惯:他喜欢喝冰美式", Scope: "user", Score: 0.9, }} dec := d.Decide(context.Background(), inRecall([]string{"今天喝冰美式了吗"}, recall)) if dec.State != StateQuiet { t.Fatalf("non-task habit woke to %s, want quiet", dec.State) } } func TestDecide_L4_UndecidedNoHabit_ConservativeQuiet(t *testing.T) { d := New(activeConfig()) dec := d.Decide(context.Background(), inRecall([]string{"嗯", "这个", "然后"}, nil)) if dec.State != StateQuiet { t.Fatalf("undecided state = %s, want quiet (conservative)", dec.State) } } func TestRecordIntent_TaskFormatsParses(t *testing.T) { d := New(activeConfig()) j := Judgment{IsTask: true, TaskKind: taskKindQuery, Confidence: 0.9, Text: "帮我查一下今天的天气;再跑个测试"} rec := d.RecordIntent(j) if rec == "" { t.Fatal("RecordIntent of a task judgment returned empty") } got, ok := parseIntentRecord(rec) if !ok { t.Fatalf("parseIntentRecord failed on %q", rec) } if !got.isTask || got.taskKind != taskKindQuery || got.text != j.Text { t.Fatalf("round-trip mismatch: %+v, want isTask=true kind=query text=%q", got, j.Text) } if got.confidence != j.Confidence { t.Fatalf("confidence = %v, want %v", got.confidence, j.Confidence) } } func TestRecordIntent_NonTaskReturnsEmpty(t *testing.T) { d := New(activeConfig()) if rec := d.RecordIntent(Judgment{IsTask: false, Text: "你好"}); rec != "" { t.Fatalf("non-task judgment persisted %q, want empty (nothing worth learning)", rec) } } func TestDecide_JudgmentPopulated(t *testing.T) { d := New(activeConfig()) task := d.Decide(context.Background(), in("帮我查一下资料")) if !task.Judgment.IsTask || task.Judgment.TaskKind != taskKindQuery { t.Fatalf("task judgment = %+v, want IsTask=true kind=query", task.Judgment) } if task.Judgment.Text != "帮我查一下资料" { t.Fatalf("judgment text = %q, want the current turn", task.Judgment.Text) } quiet := d.Decide(context.Background(), in("你好")) if quiet.Judgment.IsTask || quiet.Judgment.TaskKind != "" { t.Fatalf("quiet judgment = %+v, want IsTask=false kind empty", quiet.Judgment) } }