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
}
const maxBootstrapFileChars = 4096
func (cb *ContextBuilder) LoadBootstrapFiles() string {
bootstrapFiles := []string{
"AGENTS.md",
@ -345,7 +347,11 @@ func (cb *ContextBuilder) LoadBootstrapFiles() string {
for _, filename := range bootstrapFiles {
filePath := filepath.Join(cb.workspace, filename)
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)
// Save tool result message to session
agent.Sessions.AddFullMessage(opts.SessionKey, toolResultMsg)
// Save a truncated version of tool results to session history.
// 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)
threshold := agent.ContextWindow * 75 / 100
if len(newHistory) > 20 || tokenEstimate > threshold {
if len(newHistory) > 12 || tokenEstimate > threshold {
summarizeKey := agent.ID + ":" + sessionKey
if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading {
go func() {

View file

@ -9,6 +9,7 @@ import (
"regexp"
"strings"
"time"
"unicode/utf8"
"github.com/sipeed/picoclaw/pkg/fileutil"
)
@ -119,6 +120,26 @@ func (t *ReadFileTool) Parameters() map[string]any {
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 {
path, ok := args["path"].(string)
if !ok {
@ -130,6 +151,10 @@ func (t *ReadFileTool) Execute(ctx context.Context, args map[string]any) *ToolRe
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)
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))
@ -238,15 +263,25 @@ func (t *ListDirTool) Execute(ctx context.Context, args map[string]any) *ToolRes
return formatDirEntries(entries)
}
const maxDirEntries = 200
func formatDirEntries(entries []os.DirEntry) *ToolResult {
var result strings.Builder
for _, entry := range entries {
if entry.IsDir() {
result.WriteString("DIR: " + entry.Name() + "\n")
total := len(entries)
limit := total
if limit > maxDirEntries {
limit = maxDirEntries
}
for i := 0; i < limit; i++ {
if entries[i].IsDir() {
result.WriteString("DIR: " + entries[i].Name() + "\n")
} else {
result.WriteString("FILE: " + entry.Name() + "\n")
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())
}

View file

@ -359,13 +359,18 @@ func (t *SubagentTool) Execute(ctx context.Context, args map[string]any) *ToolRe
userContent = userContent[:maxUserLen] + "..."
}
// ForLLM: Full execution details
// ForLLM: Execution details (truncated to control token usage)
labelStr := label
if labelStr == "" {
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",
labelStr, loopResult.Iterations, loopResult.Content)
labelStr, loopResult.Iterations, resultContent)
return &ToolResult{
ForLLM: llmContent,

View file

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