// 最终输出契约测试:内联 折离 + 终轮正文干净(llm.go splitThinkBlock)。 package runtime import ( "context" "strings" "testing" agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm" core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git" ) // TestSplitThinkBlock 单测:首部 … 剥离;未闭合/非首部/空块按原文。 func TestSplitThinkBlock(t *testing.T) { cases := []struct { in string body, think string }{ {"推理答案", "答案", "推理"}, {" 推理\n答案", "答案", "推理"}, {"推理", "", "推理"}, {"未闭合", "未闭合", ""}, {"正文 混入", "正文 混入", ""}, {"没有标签的正文", "没有标签的正文", ""}, {"答案", "答案", ""}, } for _, c := range cases { b, th := splitThinkBlock(c.in) if b != c.body || th != c.think { t.Errorf("splitThinkBlock(%q) = (%q, %q), want (%q, %q)", c.in, b, th, c.body, c.think) } } } // TestLLMRunnerThinkStrippedFromReply 契约:模型把推理以 内联进最终回复时, // 落库拆为「思考(过程)+ 干净正文」——最终输出即对用户的回复。 func TestLLMRunnerThinkStrippedFromReply(t *testing.T) { turns := [][]agentllm.Event{ { agentllm.Event{Kind: agentllm.KindTextDelta, Text: "用户要纪要,我分三步……\n已生成会议纪要,要点三项。"}, agentllm.Event{Kind: agentllm.KindMessageEnd}, }, } r := &LLMRunner{Stream: scriptStream(turns, nil)} rc := &fakeRunCtx{ctx: context.Background(), prof: core.AgentProfile{Model: core.ModelRoute{Chat: "m"}}} if err := r.Execute(rc); err != nil { t.Fatalf("Execute: %v", err) } var think, text *core.Part for i := range rc.assistant { p := rc.assistant[i] if p.Type == core.PartThinking { think = &rc.assistant[i] } if p.Type == core.PartText { text = &rc.assistant[i] } } if think == nil || !strings.Contains(think.Text, "分三步") || !think.Process { t.Errorf("内联推理未折离为思考部件: %+v", rc.assistant) } if text == nil || strings.Contains(text.Text, "") || !strings.Contains(text.Text, "要点三项") { t.Errorf("最终正文不干净: %+v", text) } if text.Process { t.Errorf("终轮正文不得标记 Process: %+v", text) } }