package runtime import ( "context" "strings" "testing" core "agent-system-core" ) // 问题一回归(run_b520bf2e3037):事实核查修正轮触发时,现场那段正式总结必须 // 降级进过程时间线;修正轮的收尾才是最终答复——否则前端按「最后一个过程部件」 // 切分后,用户只看到修正流水账,看不到原总结。 func TestFactCheckDemotesPriorSummary(t *testing.T) { rc := &fakeRunCtx{ ctx: context.Background(), prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model", Lightweight: "lite-model"}}, verify: true, execArtifact: &core.Artifact{ID: "art_0123456789ab", Name: "报告.md"}, execArtifactContent: []byte("# 报告\n\n准确率 95%。"), } turns := padTurns( textDeltaTurn("", toolCall("make_report", `{"content":"准确率 95%"}`)), textDeltaTurn("已完成。## 产出物\n\n结构完整的用户总结。"), textDeltaTurn("已修正并重新登记。"), ) r := &LLMRunner{Stream: scriptStream(turns, nil), VerifyStream: passedStream()} if err := r.Execute(rc); err != nil { t.Fatalf("Execute: %v", err) } var summary *core.Part visible := []string{} sawFactCheck := false for i := range rc.assistant { p := rc.assistant[i] if p.Type == core.PartText && strings.Contains(p.Text, "结构完整的用户总结") { summary = &rc.assistant[i] } if p.Type == core.PartText && !p.Process { visible = append(visible, p.Text) } if p.Check != nil && !p.Check.Passed && strings.Contains(p.Check.Reason, "事实核查") { sawFactCheck = true } } if summary == nil { t.Fatal("prior summary text missing") } if !summary.Process { t.Fatalf("prior summary not demoted to process: %+v", *summary) } if !sawFactCheck { t.Fatal("fact check note missing") } if len(visible) != 1 || visible[0] != "已修正并重新登记。" { t.Fatalf("visible final texts = %v, want only the continuation closing", visible) } } // 同源跳过提示不再作为用户可见正文追加(原先经 StopDecision.Notice 顶掉正式总结), // 改记为通过态检查行留在过程时间线。 func TestVerifierSkipIsCheckNotFinalText(t *testing.T) { rc := &fakeRunCtx{ ctx: context.Background(), prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}}, // 轻量回落=主模型 → 同源跳过 verify: true, } turns := padTurns(textDeltaTurn("总结完成。")) r := &LLMRunner{Stream: scriptStream(turns, nil)} // 无 VerifyStream if err := r.Execute(rc); err != nil { t.Fatalf("Execute: %v", err) } skipCheck, noticeText := false, false visible := []string{} for _, p := range rc.assistant { if p.Check != nil && p.Check.Passed && strings.Contains(p.Check.Reason, "同源模型验证") { skipCheck = true } if p.Type == core.PartText { if strings.Contains(p.Text, verifierUnavailableNotice) { noticeText = true } if !p.Process { visible = append(visible, p.Text) } } } if !skipCheck { t.Fatal("same-source skip not recorded as a passing check") } if noticeText { t.Fatal("skip notice leaked into message text parts") } if len(visible) != 1 || visible[0] != "总结完成。" { t.Fatalf("visible final texts = %v", visible) } }