From bca92433ba209e1866c86eaa342f708f29ba882e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 20 Feb 2026 16:06:33 +0900 Subject: [PATCH] Use strings.Builder instead of += concatenation in loops --- pkg/agent/context.go | 6 ++--- pkg/agent/loop.go | 54 +++++++++++++++++++++------------------ pkg/agent/memory.go | 61 +++++++++++++++++++------------------------- 3 files changed, 58 insertions(+), 63 deletions(-) diff --git a/pkg/agent/context.go b/pkg/agent/context.go index 27e3ef9dc..78f5f1ffa 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -146,15 +146,15 @@ func (cb *ContextBuilder) LoadBootstrapFiles() string { "IDENTITY.md", } - var result string + var sb strings.Builder for _, filename := range bootstrapFiles { filePath := filepath.Join(cb.workspace, filename) if data, err := os.ReadFile(filePath); err == nil { - result += fmt.Sprintf("## %s\n\n%s\n\n", filename, string(data)) + fmt.Fprintf(&sb, "## %s\n\n%s\n\n", filename, data) } } - return result + return sb.String() } func (cb *ContextBuilder) BuildMessages(history []providers.Message, summary string, currentMessage string, media []string, channel, chatID string) []providers.Message { diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index e7b48d47a..bec44325e 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -818,49 +818,49 @@ func formatMessagesForLog(messages []providers.Message) string { return "[]" } - var result string - result += "[\n" + var sb strings.Builder + sb.WriteString("[\n") for i, msg := range messages { - result += fmt.Sprintf(" [%d] Role: %s\n", i, msg.Role) + fmt.Fprintf(&sb, " [%d] Role: %s\n", i, msg.Role) if len(msg.ToolCalls) > 0 { - result += " ToolCalls:\n" + sb.WriteString(" ToolCalls:\n") for _, tc := range msg.ToolCalls { - result += fmt.Sprintf(" - ID: %s, Type: %s, Name: %s\n", tc.ID, tc.Type, tc.Name) + fmt.Fprintf(&sb, " - ID: %s, Type: %s, Name: %s\n", tc.ID, tc.Type, tc.Name) if tc.Function != nil { - result += fmt.Sprintf(" Arguments: %s\n", utils.Truncate(tc.Function.Arguments, 200)) + fmt.Fprintf(&sb, " Arguments: %s\n", utils.Truncate(tc.Function.Arguments, 200)) } } } if msg.Content != "" { content := utils.Truncate(msg.Content, 200) - result += fmt.Sprintf(" Content: %s\n", content) + fmt.Fprintf(&sb, " Content: %s\n", content) } if msg.ToolCallID != "" { - result += fmt.Sprintf(" ToolCallID: %s\n", msg.ToolCallID) + fmt.Fprintf(&sb, " ToolCallID: %s\n", msg.ToolCallID) } - result += "\n" + sb.WriteString("\n") } - result += "]" - return result + sb.WriteString("]") + return sb.String() } // formatToolsForLog formats tool definitions for logging -func formatToolsForLog(tools []providers.ToolDefinition) string { - if len(tools) == 0 { +func formatToolsForLog(toolDefs []providers.ToolDefinition) string { + if len(toolDefs) == 0 { return "[]" } - var result string - result += "[\n" - for i, tool := range tools { - result += fmt.Sprintf(" [%d] Type: %s, Name: %s\n", i, tool.Type, tool.Function.Name) - result += fmt.Sprintf(" Description: %s\n", tool.Function.Description) + var sb strings.Builder + sb.WriteString("[\n") + for i, tool := range toolDefs { + fmt.Fprintf(&sb, " [%d] Type: %s, Name: %s\n", i, tool.Type, tool.Function.Name) + fmt.Fprintf(&sb, " Description: %s\n", tool.Function.Description) if len(tool.Function.Parameters) > 0 { - result += fmt.Sprintf(" Parameters: %s\n", utils.Truncate(fmt.Sprintf("%v", tool.Function.Parameters), 200)) + fmt.Fprintf(&sb, " Parameters: %s\n", utils.Truncate(fmt.Sprintf("%v", tool.Function.Parameters), 200)) } } - result += "]" - return result + sb.WriteString("]") + return sb.String() } // summarizeSession summarizes the conversation history for a session. @@ -936,14 +936,18 @@ func (al *AgentLoop) summarizeSession(agent *AgentInstance, sessionKey string) { // summarizeBatch summarizes a batch of messages. func (al *AgentLoop) summarizeBatch(ctx context.Context, agent *AgentInstance, batch []providers.Message, existingSummary string) (string, error) { - prompt := "Provide a concise summary of this conversation segment, preserving core context and key points.\n" + var sb strings.Builder + sb.WriteString("Provide a concise summary of this conversation segment, preserving core context and key points.\n") if existingSummary != "" { - prompt += "Existing context: " + existingSummary + "\n" + sb.WriteString("Existing context: ") + sb.WriteString(existingSummary) + sb.WriteString("\n") } - prompt += "\nCONVERSATION:\n" + sb.WriteString("\nCONVERSATION:\n") for _, m := range batch { - prompt += fmt.Sprintf("%s: %s\n", m.Role, m.Content) + fmt.Fprintf(&sb, "%s: %s\n", m.Role, m.Content) } + prompt := sb.String() response, err := agent.Provider.Chat(ctx, []providers.Message{{Role: "user", Content: prompt}}, nil, agent.Model, map[string]interface{}{ "max_tokens": 1024, diff --git a/pkg/agent/memory.go b/pkg/agent/memory.go index 3f6896f91..6e5d0ba40 100644 --- a/pkg/agent/memory.go +++ b/pkg/agent/memory.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "time" ) @@ -100,7 +101,8 @@ func (ms *MemoryStore) AppendToday(content string) error { // GetRecentDailyNotes returns daily notes from the last N days. // Contents are joined with "---" separator. func (ms *MemoryStore) GetRecentDailyNotes(days int) string { - var notes []string + var sb strings.Builder + first := true for i := 0; i < days; i++ { date := time.Now().AddDate(0, 0, -i) @@ -109,53 +111,42 @@ func (ms *MemoryStore) GetRecentDailyNotes(days int) string { filePath := filepath.Join(ms.memoryDir, monthDir, dateStr+".md") if data, err := os.ReadFile(filePath); err == nil { - notes = append(notes, string(data)) + if !first { + sb.WriteString("\n\n---\n\n") + } + sb.Write(data) + first = false } } - if len(notes) == 0 { - return "" - } - - // Join with separator - var result string - for i, note := range notes { - if i > 0 { - result += "\n\n---\n\n" - } - result += note - } - return result + return sb.String() } // GetMemoryContext returns formatted memory context for the agent prompt. // Includes long-term memory and recent daily notes. func (ms *MemoryStore) GetMemoryContext() string { - var parts []string - - // Long-term memory longTerm := ms.ReadLongTerm() - if longTerm != "" { - parts = append(parts, "## Long-term Memory\n\n"+longTerm) - } - - // Recent daily notes (last 3 days) recentNotes := ms.GetRecentDailyNotes(3) - if recentNotes != "" { - parts = append(parts, "## Recent Daily Notes\n\n"+recentNotes) - } - if len(parts) == 0 { + if longTerm == "" && recentNotes == "" { return "" } - // Join parts with separator - var result string - for i, part := range parts { - if i > 0 { - result += "\n\n---\n\n" - } - result += part + var sb strings.Builder + sb.WriteString("# Memory\n\n") + + if longTerm != "" { + sb.WriteString("## Long-term Memory\n\n") + sb.WriteString(longTerm) } - return fmt.Sprintf("# Memory\n\n%s", result) + + if recentNotes != "" { + if longTerm != "" { + sb.WriteString("\n\n---\n\n") + } + sb.WriteString("## Recent Daily Notes\n\n") + sb.WriteString(recentNotes) + } + + return sb.String() }