perf: 7 additional token optimizations to minimize API costs

1. Truncate tool results in session history (500 chars) - prevents
   re-sending full 10K-20K tool outputs on every subsequent LLM call
2. Limit list_dir to 200 entries - prevents huge directory listings
3. Truncate subagent ForLLM to 3000 chars - caps unbounded output
4. Detect binary files in read_file - refuse to read images/binaries
   that would waste tokens on garbage data
5. web_fetch: compact text format instead of JSON MarshalIndent
6. Lower summarization threshold from 20 to 12 messages
7. Limit bootstrap files to 4K chars each in system prompt

Made-with: Cursor
This commit is contained in:
A Magno 2026-03-02 12:42:01 +01:00
parent a99cf50f27
commit 7359fb51a4
5 changed files with 74 additions and 21 deletions

View file

@ -333,6 +333,8 @@ func skillFilesModifiedSince(skillsDir string, t time.Time) bool {
return changed return changed
} }
const maxBootstrapFileChars = 4096
func (cb *ContextBuilder) LoadBootstrapFiles() string { func (cb *ContextBuilder) LoadBootstrapFiles() string {
bootstrapFiles := []string{ bootstrapFiles := []string{
"AGENTS.md", "AGENTS.md",
@ -345,7 +347,11 @@ func (cb *ContextBuilder) LoadBootstrapFiles() string {
for _, filename := range bootstrapFiles { for _, filename := range bootstrapFiles {
filePath := filepath.Join(cb.workspace, filename) filePath := filepath.Join(cb.workspace, filename)
if data, err := os.ReadFile(filePath); err == nil { if data, err := os.ReadFile(filePath); err == nil {
fmt.Fprintf(&sb, "## %s\n\n%s\n\n", filename, data) content := string(data)
if len(content) > maxBootstrapFileChars {
content = content[:maxBootstrapFileChars] + "\n... (truncated)"
}
fmt.Fprintf(&sb, "## %s\n\n%s\n\n", filename, content)
} }
} }

View file

@ -969,8 +969,19 @@ func (al *AgentLoop) runLLMIteration(
} }
messages = append(messages, toolResultMsg) messages = append(messages, toolResultMsg)
// Save tool result message to session // Save a truncated version of tool results to session history.
agent.Sessions.AddFullMessage(opts.SessionKey, toolResultMsg) // Full results are already in `messages` for the current iteration;
// persisting truncated versions prevents context bloat on reload.
truncatedContent := contentForLLM
const maxToolResultHistory = 500
if len(truncatedContent) > maxToolResultHistory {
truncatedContent = truncatedContent[:maxToolResultHistory] + fmt.Sprintf("... (truncated, %d chars total)", len(contentForLLM))
}
agent.Sessions.AddFullMessage(opts.SessionKey, providers.Message{
Role: "tool",
Content: truncatedContent,
ToolCallID: tc.ID,
})
} }
} }
@ -1014,7 +1025,7 @@ func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, c
tokenEstimate := al.estimateTokens(newHistory) tokenEstimate := al.estimateTokens(newHistory)
threshold := agent.ContextWindow * 75 / 100 threshold := agent.ContextWindow * 75 / 100
if len(newHistory) > 20 || tokenEstimate > threshold { if len(newHistory) > 12 || tokenEstimate > threshold {
summarizeKey := agent.ID + ":" + sessionKey summarizeKey := agent.ID + ":" + sessionKey
if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading { if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading {
go func() { go func() {

View file

@ -9,6 +9,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"time" "time"
"unicode/utf8"
"github.com/sipeed/picoclaw/pkg/fileutil" "github.com/sipeed/picoclaw/pkg/fileutil"
) )
@ -119,6 +120,26 @@ func (t *ReadFileTool) Parameters() map[string]any {
const maxReadFileChars = 10000 const maxReadFileChars = 10000
func isBinaryContent(data []byte) bool {
if len(data) == 0 {
return false
}
sample := data
if len(sample) > 512 {
sample = sample[:512]
}
if !utf8.Valid(sample) {
return true
}
nullCount := 0
for _, b := range sample {
if b == 0 {
nullCount++
}
}
return nullCount > 0
}
func (t *ReadFileTool) Execute(ctx context.Context, args map[string]any) *ToolResult { func (t *ReadFileTool) Execute(ctx context.Context, args map[string]any) *ToolResult {
path, ok := args["path"].(string) path, ok := args["path"].(string)
if !ok { if !ok {
@ -130,6 +151,10 @@ func (t *ReadFileTool) Execute(ctx context.Context, args map[string]any) *ToolRe
return ErrorResult(err.Error()) return ErrorResult(err.Error())
} }
if isBinaryContent(content) {
return ErrorResult(fmt.Sprintf("binary file detected (%d bytes). Use exec to inspect binary files (e.g. file, xxd, strings)", len(content)))
}
text := string(content) text := string(content)
if len(text) > maxReadFileChars { if len(text) > maxReadFileChars {
text = text[:maxReadFileChars] + fmt.Sprintf("\n\n... (truncated, showing %d of %d chars. Use exec with head/tail/sed for specific sections)", maxReadFileChars, len(content)) text = text[:maxReadFileChars] + fmt.Sprintf("\n\n... (truncated, showing %d of %d chars. Use exec with head/tail/sed for specific sections)", maxReadFileChars, len(content))
@ -238,14 +263,24 @@ func (t *ListDirTool) Execute(ctx context.Context, args map[string]any) *ToolRes
return formatDirEntries(entries) return formatDirEntries(entries)
} }
const maxDirEntries = 200
func formatDirEntries(entries []os.DirEntry) *ToolResult { func formatDirEntries(entries []os.DirEntry) *ToolResult {
var result strings.Builder var result strings.Builder
for _, entry := range entries { total := len(entries)
if entry.IsDir() { limit := total
result.WriteString("DIR: " + entry.Name() + "\n") if limit > maxDirEntries {
} else { limit = maxDirEntries
result.WriteString("FILE: " + entry.Name() + "\n")
} }
for i := 0; i < limit; i++ {
if entries[i].IsDir() {
result.WriteString("DIR: " + entries[i].Name() + "\n")
} else {
result.WriteString("FILE: " + entries[i].Name() + "\n")
}
}
if total > maxDirEntries {
fmt.Fprintf(&result, "\n... (%d more entries not shown, total: %d)\n", total-maxDirEntries, total)
} }
return NewToolResult(result.String()) return NewToolResult(result.String())
} }

View file

@ -359,13 +359,18 @@ func (t *SubagentTool) Execute(ctx context.Context, args map[string]any) *ToolRe
userContent = userContent[:maxUserLen] + "..." userContent = userContent[:maxUserLen] + "..."
} }
// ForLLM: Full execution details // ForLLM: Execution details (truncated to control token usage)
labelStr := label labelStr := label
if labelStr == "" { if labelStr == "" {
labelStr = "(unnamed)" labelStr = "(unnamed)"
} }
resultContent := loopResult.Content
const maxSubagentLLM = 3000
if len(resultContent) > maxSubagentLLM {
resultContent = resultContent[:maxSubagentLLM] + fmt.Sprintf("... (truncated, %d chars total)", len(loopResult.Content))
}
llmContent := fmt.Sprintf("Subagent task completed:\nLabel: %s\nIterations: %d\nResult: %s", llmContent := fmt.Sprintf("Subagent task completed:\nLabel: %s\nIterations: %d\nResult: %s",
labelStr, loopResult.Iterations, loopResult.Content) labelStr, loopResult.Iterations, resultContent)
return &ToolResult{ return &ToolResult{
ForLLM: llmContent, ForLLM: llmContent,

View file

@ -661,21 +661,17 @@ func (t *WebFetchTool) Execute(ctx context.Context, args map[string]any) *ToolRe
text = text[:maxChars] text = text[:maxChars]
} }
result := map[string]any{ var llmContent string
"url": urlStr, if truncated {
"status": resp.StatusCode, llmContent = fmt.Sprintf("[%s %d truncated=%v]\n%s", urlStr, resp.StatusCode, truncated, text)
"extractor": extractor, } else {
"truncated": truncated, llmContent = fmt.Sprintf("[%s %d]\n%s", urlStr, resp.StatusCode, text)
"length": len(text),
"text": text,
} }
resultJSON, _ := json.MarshalIndent(result, "", " ")
return &ToolResult{ return &ToolResult{
ForLLM: string(resultJSON), ForLLM: llmContent,
ForUser: fmt.Sprintf( ForUser: fmt.Sprintf(
"Fetched %d bytes from %s (extractor: %s, truncated: %v)", "Fetched %d chars from %s (extractor: %s, truncated: %v)",
len(text), len(text),
urlStr, urlStr,
extractor, extractor,