From d663d6c2932ce6592184f0c0fe2ccdab1826bda0 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Fri, 29 May 2026 18:40:48 +0800 Subject: [PATCH] fix: back off compaction overflow retries by token budget --- .changeset/compact-overflow-retry-budget.md | 6 +++++ .../src/agent/compaction/strategy.ts | 17 ++++++++++++-- .../agent-core/test/agent/compaction.test.ts | 22 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 .changeset/compact-overflow-retry-budget.md diff --git a/.changeset/compact-overflow-retry-budget.md b/.changeset/compact-overflow-retry-budget.md new file mode 100644 index 0000000000..72b47ce16e --- /dev/null +++ b/.changeset/compact-overflow-retry-budget.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Back off failed compaction retries by a fixed slice of the model context window. diff --git a/packages/agent-core/src/agent/compaction/strategy.ts b/packages/agent-core/src/agent/compaction/strategy.ts index 98b3fd801a..dacf94de4f 100644 --- a/packages/agent-core/src/agent/compaction/strategy.ts +++ b/packages/agent-core/src/agent/compaction/strategy.ts @@ -10,6 +10,7 @@ export interface CompactionConfig { maxRecentMessages: number; maxRecentUserMessages: number; maxRecentSizeRatio: number; + minOverflowReductionRatio: number; } export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = { @@ -20,6 +21,7 @@ export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = { maxRecentMessages: 4, maxRecentUserMessages: Infinity, maxRecentSizeRatio: 0.2, + minOverflowReductionRatio: 0.05, }; export interface CompactionStrategy { @@ -117,12 +119,23 @@ export class DefaultCompactionStrategy implements CompactionStrategy { } reduceCompactOnOverflow(messages: readonly Message[]): number { + const minReducedSize = Math.max( + 1, + Math.ceil(this.maxSize * this.config.minOverflowReductionRatio), + ); + let reducedSize = 0; + let bestN: number | undefined; + for (let i = messages.length - 2; i > 0; i--) { + reducedSize += estimateTokensForMessage(messages[i + 1]!); if (canSplitAfter(messages, i)) { - return i + 1; + bestN = i + 1; + if (reducedSize >= minReducedSize) { + return i + 1; + } } } - return messages.length; + return bestN ?? messages.length; } get checkAfterStep(): boolean { diff --git a/packages/agent-core/test/agent/compaction.test.ts b/packages/agent-core/test/agent/compaction.test.ts index cf02938c82..cbd9c43713 100644 --- a/packages/agent-core/test/agent/compaction.test.ts +++ b/packages/agent-core/test/agent/compaction.test.ts @@ -15,6 +15,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import type { AgentOptions } from '../../src/agent'; import { DefaultCompactionStrategy, type CompactionStrategy } from '../../src/agent/compaction'; import { HookEngine, type HookEngineTriggerArgs } from '../../src/session/hooks'; +import { estimateTokensForMessages } from '../../src/utils/tokens'; import { recordingTelemetry, type TelemetryRecord } from '../fixtures/telemetry'; import type { TestAgentContext, TestAgentOptions } from './harness/agent'; import { testAgent } from './harness/agent'; @@ -132,6 +133,24 @@ describe('Agent compaction', () => { expect(strategy.shouldBlock(210_000)).toBe(true); }); + it('backs off overflow compaction by at least five percent of the context window', () => { + const strategy = testCompactionStrategy(1_000); + const messages = [ + textMessage('user', 'old user'), + textMessage('assistant', 'old assistant'), + ...Array.from({ length: 20 }, () => [ + textMessage('user', 'continue'), + textMessage('assistant', ''), + ]).flat(), + ]; + + const reduced = strategy.reduceCompactOnOverflow(messages); + const removed = messages.slice(reduced); + + expect(reduced).toBeGreaterThan(0); + expect(estimateTokensForMessages(removed)).toBeGreaterThanOrEqual(50); + }); + it('ignores reserved context when the reserve is not smaller than the model window', () => { const strategy = new DefaultCompactionStrategy(() => 32_000, { triggerRatio: 0.85, @@ -141,6 +160,7 @@ describe('Agent compaction', () => { maxRecentMessages: 3, maxRecentUserMessages: Infinity, maxRecentSizeRatio: 0.2, + minOverflowReductionRatio: 0.05, }); expect(strategy.shouldCompact(1)).toBe(false); @@ -1601,6 +1621,7 @@ function testCompactionStrategy(maxSize: number = 1_000): DefaultCompactionStrat maxRecentMessages: 10, maxRecentUserMessages: Infinity, maxRecentSizeRatio: 0.2, + minOverflowReductionRatio: 0.05, }); } @@ -1613,6 +1634,7 @@ function overflowOnlyCompactionStrategy(maxSize: number = 14): DefaultCompaction maxRecentMessages: 3, maxRecentUserMessages: Infinity, maxRecentSizeRatio: 0.2, + minOverflowReductionRatio: 0.05, }); }