diff --git a/pkg/agent/context.go b/pkg/agent/context.go
index 8df221ad2..715f3bc64 100644
--- a/pkg/agent/context.go
+++ b/pkg/agent/context.go
@@ -218,14 +218,16 @@ func (cb *ContextBuilder) BuildMessages(
) []providers.Message {
messages := []providers.Message{}
- systemPrompt := cb.BuildSystemPrompt()
+ var sysBuilder strings.Builder
+ sysBuilder.WriteString(cb.BuildSystemPrompt())
// Add Current Session info if provided
if channel != "" && chatID != "" {
- systemPrompt += fmt.Sprintf("\n\n## Current Session\nChannel: %s\nChat ID: %s", channel, chatID)
+ fmt.Fprintf(&sysBuilder, "\n\n## Current Session\nChannel: %s\nChat ID: %s", channel, chatID)
}
// Log system prompt summary for debugging (debug mode only)
+ systemPrompt := sysBuilder.String()
logger.DebugCF("agent", "System prompt built",
map[string]any{
"total_chars": len(systemPrompt),
@@ -244,7 +246,9 @@ func (cb *ContextBuilder) BuildMessages(
})
if summary != "" {
- systemPrompt += "\n\n## Summary of Previous Conversation\n\n" + summary
+ sysBuilder.WriteString("\n\n## Summary of Previous Conversation\n\n")
+ sysBuilder.WriteString(summary)
+ systemPrompt = sysBuilder.String()
}
history = sanitizeHistoryForProvider(history)
diff --git a/pkg/channels/slack.go b/pkg/channels/slack.go
index f7359cd6d..4dde2eb29 100644
--- a/pkg/channels/slack.go
+++ b/pkg/channels/slack.go
@@ -228,8 +228,8 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
Timestamp: messageTS,
})
- content := ev.Text
- content = c.stripBotMention(content)
+ var contentBuf strings.Builder
+ contentBuf.WriteString(c.stripBotMention(ev.Text))
var mediaPaths []string
localFiles := []string{} // 跟踪需要清理的本地文件
@@ -262,16 +262,17 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
if err != nil {
logger.ErrorCF("slack", "Voice transcription failed", map[string]any{"error": err.Error()})
- content += fmt.Sprintf("\n[audio: %s (transcription failed)]", file.Name)
+ fmt.Fprintf(&contentBuf, "\n[audio: %s (transcription failed)]", file.Name)
} else {
- content += fmt.Sprintf("\n[voice transcription: %s]", result.Text)
+ fmt.Fprintf(&contentBuf, "\n[voice transcription: %s]", result.Text)
}
} else {
- content += fmt.Sprintf("\n[file: %s]", file.Name)
+ fmt.Fprintf(&contentBuf, "\n[file: %s]", file.Name)
}
}
}
+ content := contentBuf.String()
if strings.TrimSpace(content) == "" {
return
}
diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go
index 3c63a88d4..cb10e3a64 100644
--- a/pkg/channels/telegram.go
+++ b/pkg/channels/telegram.go
@@ -7,6 +7,7 @@ import (
"net/url"
"os"
"regexp"
+ "strconv"
"strings"
"sync"
"time"
@@ -797,7 +798,7 @@ func extractCodeBlocks(text string) codeBlockMatch {
i := 0
text = re.ReplaceAllStringFunc(text, func(m string) string {
- placeholder := fmt.Sprintf("\x00CB%d\x00", i)
+ placeholder := "\x00CB" + strconv.Itoa(i) + "\x00"
i++
return placeholder
})
@@ -821,7 +822,7 @@ func extractInlineCodes(text string) inlineCodeMatch {
i := 0
text = re.ReplaceAllStringFunc(text, func(m string) string {
- placeholder := fmt.Sprintf("\x00IC%d\x00", i)
+ placeholder := "\x00IC" + strconv.Itoa(i) + "\x00"
i++
return placeholder
})
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index 97b6b62b1..953eb494b 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -239,11 +239,18 @@ func formatComponent(component string) string {
}
func formatFields(fields map[string]any) string {
- var parts []string
+ var sb strings.Builder
+ sb.WriteByte('{')
+ first := true
for k, v := range fields {
- parts = append(parts, fmt.Sprintf("%s=%v", k, v))
+ if !first {
+ sb.WriteString(", ")
+ }
+ fmt.Fprintf(&sb, "%s=%v", k, v)
+ first = false
}
- return fmt.Sprintf("{%s}", strings.Join(parts, ", "))
+ sb.WriteByte('}')
+ return sb.String()
}
func Debug(message string) {
diff --git a/pkg/skills/loader.go b/pkg/skills/loader.go
index eb0d5f322..e71ae5201 100644
--- a/pkg/skills/loader.go
+++ b/pkg/skills/loader.go
@@ -214,15 +214,20 @@ func (sl *SkillsLoader) LoadSkillsForContext(skillNames []string) string {
return ""
}
- var parts []string
+ var sb strings.Builder
+ first := true
for _, name := range skillNames {
content, ok := sl.LoadSkill(name)
if ok {
- parts = append(parts, fmt.Sprintf("### Skill: %s\n\n%s", name, content))
+ if !first {
+ sb.WriteString("\n\n---\n\n")
+ }
+ fmt.Fprintf(&sb, "### Skill: %s\n\n%s", name, content)
+ first = false
}
}
- return strings.Join(parts, "\n\n---\n\n")
+ return sb.String()
}
func (sl *SkillsLoader) BuildSkillsSummary() string {
@@ -231,23 +236,23 @@ func (sl *SkillsLoader) BuildSkillsSummary() string {
return ""
}
- var lines []string
- lines = append(lines, "")
+ var sb strings.Builder
+ sb.WriteString("")
for _, s := range allSkills {
escapedName := escapeXML(s.Name)
escapedDesc := escapeXML(s.Description)
escapedPath := escapeXML(s.Path)
- lines = append(lines, fmt.Sprintf(" "))
- lines = append(lines, fmt.Sprintf(" %s", escapedName))
- lines = append(lines, fmt.Sprintf(" %s", escapedDesc))
- lines = append(lines, fmt.Sprintf(" %s", escapedPath))
- lines = append(lines, fmt.Sprintf(" %s", s.Source))
- lines = append(lines, " ")
+ sb.WriteString("\n ")
+ fmt.Fprintf(&sb, "\n %s", escapedName)
+ fmt.Fprintf(&sb, "\n %s", escapedDesc)
+ fmt.Fprintf(&sb, "\n %s", escapedPath)
+ fmt.Fprintf(&sb, "\n %s", s.Source)
+ sb.WriteString("\n ")
}
- lines = append(lines, "")
+ sb.WriteString("\n")
- return strings.Join(lines, "\n")
+ return sb.String()
}
func (sl *SkillsLoader) getSkillMetadata(skillPath string) *SkillMetadata {
diff --git a/pkg/tools/web.go b/pkg/tools/web.go
index 452e95e0f..6e177ec8d 100644
--- a/pkg/tools/web.go
+++ b/pkg/tools/web.go
@@ -70,19 +70,19 @@ func (p *BraveSearchProvider) Search(ctx context.Context, query string, count in
return fmt.Sprintf("No results for: %s", query), nil
}
- var lines []string
- lines = append(lines, fmt.Sprintf("Results for: %s", query))
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "Results for: %s", query)
for i, item := range results {
if i >= count {
break
}
- lines = append(lines, fmt.Sprintf("%d. %s\n %s", i+1, item.Title, item.URL))
+ fmt.Fprintf(&sb, "\n%d. %s\n %s", i+1, item.Title, item.URL)
if item.Description != "" {
- lines = append(lines, fmt.Sprintf(" %s", item.Description))
+ fmt.Fprintf(&sb, "\n %s", item.Description)
}
}
- return strings.Join(lines, "\n"), nil
+ return sb.String(), nil
}
type TavilySearchProvider struct {
@@ -152,19 +152,19 @@ func (p *TavilySearchProvider) Search(ctx context.Context, query string, count i
return fmt.Sprintf("No results for: %s", query), nil
}
- var lines []string
- lines = append(lines, fmt.Sprintf("Results for: %s (via Tavily)", query))
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "Results for: %s (via Tavily)", query)
for i, item := range results {
if i >= count {
break
}
- lines = append(lines, fmt.Sprintf("%d. %s\n %s", i+1, item.Title, item.URL))
+ fmt.Fprintf(&sb, "\n%d. %s\n %s", i+1, item.Title, item.URL)
if item.Content != "" {
- lines = append(lines, fmt.Sprintf(" %s", item.Content))
+ fmt.Fprintf(&sb, "\n %s", item.Content)
}
}
- return strings.Join(lines, "\n"), nil
+ return sb.String(), nil
}
type DuckDuckGoSearchProvider struct{}
@@ -208,17 +208,9 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query
return fmt.Sprintf("No results found or extraction failed. Query: %s", query), nil
}
- var lines []string
- lines = append(lines, fmt.Sprintf("Results for: %s (via DuckDuckGo)", query))
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "Results for: %s (via DuckDuckGo)", query)
- // Pre-compile snippet regex to run inside the loop
- // We'll search for snippets relative to the link position or just globally if needed
- // But simple global search for snippets might mismatch order.
- // Since we only have the raw HTML string, let's just extract snippets globally and assume order matches (risky but simple for regex)
- // Or better: Let's assume the snippet follows the link in the HTML
-
- // A better regex approach: iterate through text and find matches in order
- // But for now, let's grab all snippets too
reSnippet := regexp.MustCompile(`([\s\S]*?)`)
snippetMatches := reSnippet.FindAllStringSubmatch(html, count+5)
@@ -239,19 +231,19 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query
}
}
- lines = append(lines, fmt.Sprintf("%d. %s\n %s", i+1, title, urlStr))
+ fmt.Fprintf(&sb, "\n%d. %s\n %s", i+1, title, urlStr)
// Attempt to attach snippet if available and index aligns
if i < len(snippetMatches) {
snippet := stripTags(snippetMatches[i][1])
snippet = strings.TrimSpace(snippet)
if snippet != "" {
- lines = append(lines, fmt.Sprintf(" %s", snippet))
+ fmt.Fprintf(&sb, "\n %s", snippet)
}
}
}
- return strings.Join(lines, "\n"), nil
+ return sb.String(), nil
}
func stripTags(content string) string {
@@ -605,13 +597,16 @@ func (t *WebFetchTool) extractText(htmlContent string) string {
result = re.ReplaceAllString(result, "\n\n")
lines := strings.Split(result, "\n")
- var cleanLines []string
+ var sb strings.Builder
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" {
- cleanLines = append(cleanLines, line)
+ if sb.Len() > 0 {
+ sb.WriteByte('\n')
+ }
+ sb.WriteString(line)
}
}
- return strings.Join(cleanLines, "\n")
+ return sb.String()
}