From 0c6cd02285a2460d975e9c9b948b5dec8c15a285 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sun, 29 Mar 2026 10:14:52 +0700 Subject: [PATCH] refactor(compaction): remove minMessages guard, use token-only threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message count-based compaction trigger (default 200) was a legacy safety net from when token estimation was unreliable. With calibrated overhead estimation using actual LLM PromptTokens, the token threshold alone is sufficient — matching industry standard (Claude Code, Anthropic API, LangChain all use token-only). Also fixes EstimateTokens vs EstimateHistoryTokens inconsistency in end-of-run overhead calculation (loop_history.go), and updates stale CLAUDE.md docs from 75% to 85%. --- CLAUDE.md | 2 +- internal/agent/loop_history.go | 11 ++++------- internal/config/config.go | 3 +-- ui/desktop/frontend/src/i18n/locales/en/agents.json | 2 -- ui/desktop/frontend/src/i18n/locales/vi/agents.json | 2 -- ui/desktop/frontend/src/i18n/locales/zh/agents.json | 2 -- ui/desktop/frontend/src/types/agent.ts | 1 - ui/web/src/i18n/locales/en/agents.json | 2 -- ui/web/src/i18n/locales/vi/agents.json | 2 -- ui/web/src/i18n/locales/zh/agents.json | 2 -- .../config-sections/compaction-section.tsx | 13 +------------ ui/web/src/types/agent.ts | 1 - 12 files changed, 7 insertions(+), 36 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2ace0a3b..fefeff7f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ ui/web/ React SPA (pnpm, Vite, Tailwind, Radix UI) - **Agent types:** `open` (per-user context, 7 files) vs `predefined` (shared context + USER.md per-user) - **Context files:** `agent_context_files` (agent-level) + `user_context_files` (per-user), routed via `ContextFileInterceptor` - **Providers:** Anthropic (native HTTP+SSE), OpenAI-compat (HTTP+SSE), DashScope (Alibaba Qwen), Claude CLI (stdio+MCP bridge), ACP (Anthropic Console Proxy), Codex (OpenAI). All use `RetryDo()` for retries. Loads from `llm_providers` table with encrypted API keys -- **Agent loop:** `RunRequest` → think→act→observe → `RunResult`. Events: `run.started`, `run.completed`, `chunk`, `tool.call`, `tool.result`. Auto-summarization at >75% context +- **Agent loop:** `RunRequest` → think→act→observe → `RunResult`. Events: `run.started`, `run.completed`, `chunk`, `tool.call`, `tool.result`. Auto-summarization at >85% context (token-based only) - **Context propagation:** `store.WithAgentType(ctx)`, `store.WithUserID(ctx)`, `store.WithAgentID(ctx)`, `store.WithLocale(ctx)` - **WebSocket protocol (v3):** Frame types `req`/`res`/`event`. First request must be `connect` - **Config:** JSON5 at `GOCLAW_CONFIG` env. Secrets in `.env.local` or env vars, never in config.json diff --git a/internal/agent/loop_history.go b/internal/agent/loop_history.go index aa61e2c6..59585d5a 100644 --- a/internal/agent/loop_history.go +++ b/internal/agent/loop_history.go @@ -553,18 +553,15 @@ func (l *Loop) maybeSummarize(ctx context.Context, sessionKey string) { } tokenEstimate := EstimateTokensWithCalibration(history, adjustedLastPT, lastMC) - // Resolve compaction thresholds from config with sensible defaults. + // Resolve compaction threshold from config: token-only (no message count guard). + // Industry standard — Claude Code, Anthropic API, LangChain all use token-based thresholds. historyShare := config.DefaultHistoryShare if l.compactionCfg != nil && l.compactionCfg.MaxHistoryShare > 0 { historyShare = l.compactionCfg.MaxHistoryShare } - minMessages := 200 - if l.compactionCfg != nil && l.compactionCfg.MinMessages > 0 { - minMessages = l.compactionCfg.MinMessages - } threshold := int(float64(l.contextWindow) * historyShare) - if len(history) <= minMessages && tokenEstimate <= threshold { + if tokenEstimate <= threshold { return } @@ -681,7 +678,7 @@ func (l *Loop) estimateOverhead(history []providers.Message, lastPromptTokens, l if count > len(history) { count = len(history) } - historyEstAtCalibration := EstimateTokens(history[:count]) + historyEstAtCalibration := EstimateHistoryTokens(history[:count]) overhead := lastPromptTokens - historyEstAtCalibration if overhead < 0 { overhead = 0 diff --git a/internal/config/config.go b/internal/config/config.go index 989c73c5..e7411103 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -133,8 +133,7 @@ type AgentDefaults struct { // Matching TS agents.defaults.compaction. type CompactionConfig struct { ReserveTokensFloor int `json:"reserveTokensFloor,omitempty"` // min reserve tokens (default 20000) - MaxHistoryShare float64 `json:"maxHistoryShare,omitempty"` // max share of context for history (default 0.75) - MinMessages int `json:"minMessages,omitempty"` // min messages before compaction triggers (default 200) + MaxHistoryShare float64 `json:"maxHistoryShare,omitempty"` // max share of context for history (default 0.85) KeepLastMessages int `json:"keepLastMessages,omitempty"` // messages to keep after compaction (default 4) MemoryFlush *MemoryFlushConfig `json:"memoryFlush,omitempty"` // pre-compaction flush } diff --git a/ui/desktop/frontend/src/i18n/locales/en/agents.json b/ui/desktop/frontend/src/i18n/locales/en/agents.json index fc2bf89b..1df10228 100644 --- a/ui/desktop/frontend/src/i18n/locales/en/agents.json +++ b/ui/desktop/frontend/src/i18n/locales/en/agents.json @@ -344,8 +344,6 @@ "description": "Context window compaction and memory flush settings", "maxHistoryShare": "Max History Share (0-1)", "maxHistoryShareTip": "Maximum fraction of context window for conversation history (e.g. 0.75 = 75%). Compaction triggers when exceeded.", - "minMessages": "Min Messages", - "minMessagesTip": "Minimum messages before compaction can trigger, regardless of token count.", "keepLastMessages": "Keep Last Messages", "keepLastMessagesTip": "Recent messages kept after compaction. Older messages are replaced by a summary.", "memoryFlush": "Memory Flush", diff --git a/ui/desktop/frontend/src/i18n/locales/vi/agents.json b/ui/desktop/frontend/src/i18n/locales/vi/agents.json index 99016629..0821a520 100644 --- a/ui/desktop/frontend/src/i18n/locales/vi/agents.json +++ b/ui/desktop/frontend/src/i18n/locales/vi/agents.json @@ -344,8 +344,6 @@ "description": "Cài đặt nén cửa sổ ngữ cảnh và ghi nhớ trước nén", "maxHistoryShare": "Phần lịch sử tối đa (0-1)", "maxHistoryShareTip": "Tỷ lệ tối đa của cửa sổ ngữ cảnh dành cho lịch sử hội thoại (vd: 0.75 = 75%). Nén kích hoạt khi vượt quá.", - "minMessages": "Tin nhắn tối thiểu", - "minMessagesTip": "Số tin nhắn tối thiểu trước khi nén có thể kích hoạt, bất kể số token.", "keepLastMessages": "Giữ tin nhắn cuối", "keepLastMessagesTip": "Số tin nhắn gần nhất giữ lại sau khi nén. Các tin nhắn cũ hơn được thay thế bằng bản tóm tắt.", "memoryFlush": "Ghi nhớ trước nén", diff --git a/ui/desktop/frontend/src/i18n/locales/zh/agents.json b/ui/desktop/frontend/src/i18n/locales/zh/agents.json index 656c8785..ce0edc62 100644 --- a/ui/desktop/frontend/src/i18n/locales/zh/agents.json +++ b/ui/desktop/frontend/src/i18n/locales/zh/agents.json @@ -344,8 +344,6 @@ "description": "上下文窗口压缩和压缩前记忆设置", "maxHistoryShare": "最大历史份额 (0-1)", "maxHistoryShareTip": "上下文窗口用于对话历史的最大比例(如0.75 = 75%)。超过时触发压缩。", - "minMessages": "最小消息数", - "minMessagesTip": "压缩触发前所需的最少消息数,与令牌数无关。", "keepLastMessages": "保留最后消息数", "keepLastMessagesTip": "压缩后保留的最近消息数。旧消息被摘要替换。", "memoryFlush": "压缩前记忆", diff --git a/ui/desktop/frontend/src/types/agent.ts b/ui/desktop/frontend/src/types/agent.ts index 75e12469..6628f362 100644 --- a/ui/desktop/frontend/src/types/agent.ts +++ b/ui/desktop/frontend/src/types/agent.ts @@ -17,7 +17,6 @@ export interface MemoryConfig { export interface CompactionConfig { reserveTokensFloor?: number maxHistoryShare?: number - minMessages?: number keepLastMessages?: number memoryFlush?: { enabled?: boolean diff --git a/ui/web/src/i18n/locales/en/agents.json b/ui/web/src/i18n/locales/en/agents.json index 7ca70acd..3882bc13 100644 --- a/ui/web/src/i18n/locales/en/agents.json +++ b/ui/web/src/i18n/locales/en/agents.json @@ -591,8 +591,6 @@ "description": "Context window compaction and memory flush settings", "maxHistoryShare": "Max History Share (0-1)", "maxHistoryShareTip": "Maximum fraction of context window for conversation history (e.g. 0.75 = 75%). Compaction triggers when exceeded.", - "minMessages": "Min Messages", - "minMessagesTip": "Minimum messages before compaction can trigger, regardless of token count.", "keepLastMessages": "Keep Last Messages", "keepLastMessagesTip": "Recent messages kept after compaction. Older messages are replaced by a summary.", "memoryFlush": "Memory Flush", diff --git a/ui/web/src/i18n/locales/vi/agents.json b/ui/web/src/i18n/locales/vi/agents.json index 4b862d48..31e67a3c 100644 --- a/ui/web/src/i18n/locales/vi/agents.json +++ b/ui/web/src/i18n/locales/vi/agents.json @@ -591,8 +591,6 @@ "description": "Cài đặt nén cửa sổ ngữ cảnh và ghi nhớ trước nén", "maxHistoryShare": "Phần lịch sử tối đa (0-1)", "maxHistoryShareTip": "Tỷ lệ tối đa của cửa sổ ngữ cảnh dành cho lịch sử hội thoại (vd: 0.75 = 75%). Nén kích hoạt khi vượt quá.", - "minMessages": "Tin nhắn tối thiểu", - "minMessagesTip": "Số tin nhắn tối thiểu trước khi nén có thể kích hoạt, bất kể số token.", "keepLastMessages": "Giữ tin nhắn cuối", "keepLastMessagesTip": "Số tin nhắn gần nhất giữ lại sau khi nén. Các tin nhắn cũ hơn được thay thế bằng bản tóm tắt.", "memoryFlush": "Ghi nhớ trước nén", diff --git a/ui/web/src/i18n/locales/zh/agents.json b/ui/web/src/i18n/locales/zh/agents.json index ca7b44e5..73e2aa5c 100644 --- a/ui/web/src/i18n/locales/zh/agents.json +++ b/ui/web/src/i18n/locales/zh/agents.json @@ -591,8 +591,6 @@ "description": "上下文窗口压缩和压缩前记忆设置", "maxHistoryShare": "最大历史份额 (0-1)", "maxHistoryShareTip": "上下文窗口用于对话历史的最大比例(如0.75 = 75%)。超过时触发压缩。", - "minMessages": "最小消息数", - "minMessagesTip": "压缩触发前所需的最少消息数,与令牌数无关。", "keepLastMessages": "保留最后消息数", "keepLastMessagesTip": "压缩后保留的最近消息数。旧消息被摘要替换。", "memoryFlush": "压缩前记忆", diff --git a/ui/web/src/pages/agents/agent-detail/config-sections/compaction-section.tsx b/ui/web/src/pages/agents/agent-detail/config-sections/compaction-section.tsx index f9aee6a3..fd138ee7 100644 --- a/ui/web/src/pages/agents/agent-detail/config-sections/compaction-section.tsx +++ b/ui/web/src/pages/agents/agent-detail/config-sections/compaction-section.tsx @@ -25,22 +25,11 @@ export function CompactionSection({ value, onChange }: CompactionSectionProps) { onChange({ ...value, maxHistoryShare: numOrUndef(e.target.value) })} /> -
- {t(`${s}.minMessages`)} - onChange({ ...value, minMessages: numOrUndef(e.target.value) })} - /> -
- -
{t(`${s}.keepLastMessages`)}