perf: replace []string+Join with strings.Builder in hot paths

Covers web search providers (Brave/Tavily/DuckDuckGo/extractText),
skills loader, logger formatFields, system prompt builder, and
Slack message handler. Eliminates intermediate slice allocations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dj-oyu 2026-02-24 15:42:46 +09:00
parent ec7b98708d
commit b9a0677572
6 changed files with 65 additions and 52 deletions

View file

@ -218,14 +218,16 @@ func (cb *ContextBuilder) BuildMessages(
) []providers.Message { ) []providers.Message {
messages := []providers.Message{} messages := []providers.Message{}
systemPrompt := cb.BuildSystemPrompt() var sysBuilder strings.Builder
sysBuilder.WriteString(cb.BuildSystemPrompt())
// Add Current Session info if provided // Add Current Session info if provided
if channel != "" && chatID != "" { 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) // Log system prompt summary for debugging (debug mode only)
systemPrompt := sysBuilder.String()
logger.DebugCF("agent", "System prompt built", logger.DebugCF("agent", "System prompt built",
map[string]any{ map[string]any{
"total_chars": len(systemPrompt), "total_chars": len(systemPrompt),
@ -244,7 +246,9 @@ func (cb *ContextBuilder) BuildMessages(
}) })
if summary != "" { 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) history = sanitizeHistoryForProvider(history)

View file

@ -228,8 +228,8 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
Timestamp: messageTS, Timestamp: messageTS,
}) })
content := ev.Text var contentBuf strings.Builder
content = c.stripBotMention(content) contentBuf.WriteString(c.stripBotMention(ev.Text))
var mediaPaths []string var mediaPaths []string
localFiles := []string{} // 跟踪需要清理的本地文件 localFiles := []string{} // 跟踪需要清理的本地文件
@ -262,16 +262,17 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
if err != nil { if err != nil {
logger.ErrorCF("slack", "Voice transcription failed", map[string]any{"error": err.Error()}) 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 { } else {
content += fmt.Sprintf("\n[voice transcription: %s]", result.Text) fmt.Fprintf(&contentBuf, "\n[voice transcription: %s]", result.Text)
} }
} else { } else {
content += fmt.Sprintf("\n[file: %s]", file.Name) fmt.Fprintf(&contentBuf, "\n[file: %s]", file.Name)
} }
} }
} }
content := contentBuf.String()
if strings.TrimSpace(content) == "" { if strings.TrimSpace(content) == "" {
return return
} }

View file

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -797,7 +798,7 @@ func extractCodeBlocks(text string) codeBlockMatch {
i := 0 i := 0
text = re.ReplaceAllStringFunc(text, func(m string) string { text = re.ReplaceAllStringFunc(text, func(m string) string {
placeholder := fmt.Sprintf("\x00CB%d\x00", i) placeholder := "\x00CB" + strconv.Itoa(i) + "\x00"
i++ i++
return placeholder return placeholder
}) })
@ -821,7 +822,7 @@ func extractInlineCodes(text string) inlineCodeMatch {
i := 0 i := 0
text = re.ReplaceAllStringFunc(text, func(m string) string { text = re.ReplaceAllStringFunc(text, func(m string) string {
placeholder := fmt.Sprintf("\x00IC%d\x00", i) placeholder := "\x00IC" + strconv.Itoa(i) + "\x00"
i++ i++
return placeholder return placeholder
}) })

View file

@ -239,11 +239,18 @@ func formatComponent(component string) string {
} }
func formatFields(fields map[string]any) string { func formatFields(fields map[string]any) string {
var parts []string var sb strings.Builder
sb.WriteByte('{')
first := true
for k, v := range fields { for k, v := range fields {
parts = append(parts, fmt.Sprintf("%s=%v", k, v)) if !first {
sb.WriteString(", ")
} }
return fmt.Sprintf("{%s}", strings.Join(parts, ", ")) fmt.Fprintf(&sb, "%s=%v", k, v)
first = false
}
sb.WriteByte('}')
return sb.String()
} }
func Debug(message string) { func Debug(message string) {

View file

@ -214,15 +214,20 @@ func (sl *SkillsLoader) LoadSkillsForContext(skillNames []string) string {
return "" return ""
} }
var parts []string var sb strings.Builder
first := true
for _, name := range skillNames { for _, name := range skillNames {
content, ok := sl.LoadSkill(name) content, ok := sl.LoadSkill(name)
if ok { 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 { func (sl *SkillsLoader) BuildSkillsSummary() string {
@ -231,23 +236,23 @@ func (sl *SkillsLoader) BuildSkillsSummary() string {
return "" return ""
} }
var lines []string var sb strings.Builder
lines = append(lines, "<skills>") sb.WriteString("<skills>")
for _, s := range allSkills { for _, s := range allSkills {
escapedName := escapeXML(s.Name) escapedName := escapeXML(s.Name)
escapedDesc := escapeXML(s.Description) escapedDesc := escapeXML(s.Description)
escapedPath := escapeXML(s.Path) escapedPath := escapeXML(s.Path)
lines = append(lines, fmt.Sprintf(" <skill>")) sb.WriteString("\n <skill>")
lines = append(lines, fmt.Sprintf(" <name>%s</name>", escapedName)) fmt.Fprintf(&sb, "\n <name>%s</name>", escapedName)
lines = append(lines, fmt.Sprintf(" <description>%s</description>", escapedDesc)) fmt.Fprintf(&sb, "\n <description>%s</description>", escapedDesc)
lines = append(lines, fmt.Sprintf(" <location>%s</location>", escapedPath)) fmt.Fprintf(&sb, "\n <location>%s</location>", escapedPath)
lines = append(lines, fmt.Sprintf(" <source>%s</source>", s.Source)) fmt.Fprintf(&sb, "\n <source>%s</source>", s.Source)
lines = append(lines, " </skill>") sb.WriteString("\n </skill>")
} }
lines = append(lines, "</skills>") sb.WriteString("\n</skills>")
return strings.Join(lines, "\n") return sb.String()
} }
func (sl *SkillsLoader) getSkillMetadata(skillPath string) *SkillMetadata { func (sl *SkillsLoader) getSkillMetadata(skillPath string) *SkillMetadata {

View file

@ -70,19 +70,19 @@ func (p *BraveSearchProvider) Search(ctx context.Context, query string, count in
return fmt.Sprintf("No results for: %s", query), nil return fmt.Sprintf("No results for: %s", query), nil
} }
var lines []string var sb strings.Builder
lines = append(lines, fmt.Sprintf("Results for: %s", query)) fmt.Fprintf(&sb, "Results for: %s", query)
for i, item := range results { for i, item := range results {
if i >= count { if i >= count {
break 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 != "" { 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 { 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 return fmt.Sprintf("No results for: %s", query), nil
} }
var lines []string var sb strings.Builder
lines = append(lines, fmt.Sprintf("Results for: %s (via Tavily)", query)) fmt.Fprintf(&sb, "Results for: %s (via Tavily)", query)
for i, item := range results { for i, item := range results {
if i >= count { if i >= count {
break 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 != "" { 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{} 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 return fmt.Sprintf("No results found or extraction failed. Query: %s", query), nil
} }
var lines []string var sb strings.Builder
lines = append(lines, fmt.Sprintf("Results for: %s (via DuckDuckGo)", query)) 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(`<a class="result__snippet[^"]*".*?>([\s\S]*?)</a>`) reSnippet := regexp.MustCompile(`<a class="result__snippet[^"]*".*?>([\s\S]*?)</a>`)
snippetMatches := reSnippet.FindAllStringSubmatch(html, count+5) 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 // Attempt to attach snippet if available and index aligns
if i < len(snippetMatches) { if i < len(snippetMatches) {
snippet := stripTags(snippetMatches[i][1]) snippet := stripTags(snippetMatches[i][1])
snippet = strings.TrimSpace(snippet) snippet = strings.TrimSpace(snippet)
if 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 { func stripTags(content string) string {
@ -605,13 +597,16 @@ func (t *WebFetchTool) extractText(htmlContent string) string {
result = re.ReplaceAllString(result, "\n\n") result = re.ReplaceAllString(result, "\n\n")
lines := strings.Split(result, "\n") lines := strings.Split(result, "\n")
var cleanLines []string var sb strings.Builder
for _, line := range lines { for _, line := range lines {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if 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()
} }