返回 DeepSeek-Reasonix
goal_display.go
根目录 / internal / agent / goal_display.go
1 package agent
2
3 import (
4 "fmt"
5 "strings"
6 )
7
8 // StripGoalMarkers removes goal status markers like [goal:complete],
9 // [goal:continue], and [goal:blocked:...] from display text so users see
10 // natural language instead of protocol markers. Exported for use by frontends.
11 // The markers are still kept in the session history for controller parsing.
12 func StripGoalMarkers(text string) string {
13 text = strings.TrimSpace(text)
14 lines := strings.Split(text, "\n")
15 cleaned := make([]string, 0, len(lines))
16 for _, line := range lines {
17 trimmed := strings.TrimSpace(line)
18 if trimmed == "[goal:complete]" || trimmed == "[goal:continue]" {
19 continue
20 }
21 // Planner conclusion markers are coordinator protocol, not prose: the
22 // relay path trusts them (isNoOpPlan / plan approval) but users should
23 // see the planner's words, not the contract.
24 if lower := strings.ToLower(trimmed); lower == "[no_changes]" || lower == "[planner_requires_approval]" {
25 continue
26 }
27 if strings.HasPrefix(trimmed, "[goal:blocked:") && strings.HasSuffix(trimmed, "]") {
28 reason := strings.TrimPrefix(trimmed, "[goal:blocked:")
29 reason = strings.TrimSuffix(reason, "]")
30 if reason != "" {
31 cleaned = append(cleaned, fmt.Sprintf("\u26a0\ufe0f Blocked: %s", reason))
32 }
33 continue
34 }
35 cleaned = append(cleaned, line)
36 }
37 return strings.TrimSpace(strings.Join(cleaned, "\n"))
38 }
39
40 const (
41 autoResearchEvidenceOpen = "<autoresearch-evidence>"
42 autoResearchEvidenceClose = "</autoresearch-evidence>"
43 )
44
45 // StripAutoResearchEvidenceBlocks removes <autoresearch-evidence> protocol
46 // blocks the model emits for the controller's evidence recorder. Like goal
47 // markers, they stay in the session history for parsing (#6665).
48 func StripAutoResearchEvidenceBlocks(text string) string {
49 var b strings.Builder
50 rest := text
51 for {
52 start := strings.Index(rest, autoResearchEvidenceOpen)
53 if start < 0 {
54 b.WriteString(rest)
55 return strings.TrimSpace(b.String())
56 }
57 b.WriteString(rest[:start])
58 afterOpen := rest[start+len(autoResearchEvidenceOpen):]
59 end := strings.Index(afterOpen, autoResearchEvidenceClose)
60 if end < 0 {
61 return strings.TrimSpace(b.String())
62 }
63 rest = afterOpen[end+len(autoResearchEvidenceClose):]
64 }
65 }
66
67 // DisplayAssistantText is the single display filter for assistant answer text:
68 // it removes every protocol artifact ([goal:*] markers, autoresearch evidence
69 // blocks) before the text reaches a sink. Session history keeps the raw text —
70 // apply this at emission or render boundaries only.
71 func DisplayAssistantText(text string) string {
72 return StripGoalMarkers(StripAutoResearchEvidenceBlocks(text))
73 }
74
74 lines GO