Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Strategies for managing LLM context windows including summarization, trimming, routing, and avoiding context rot
.claude/skills/sickn33-context-window-management/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 24% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 88% | 0% |
Strategies for managing LLM context windows including summarization, trimming, routing, and avoiding context rot
Different strategies based on context size
When to use: Building any multi-turn conversation system
typescriptinterface ContextTier { maxTokens: number; strategy: 'full' | 'summarize' | 'rag'; model: string; } const TIERS: ContextTier[] = [ { maxTokens: 8000, strategy: 'full', model: 'claude-3-haiku' }, { maxTokens: 32000, strategy: 'full', model: 'claude-3-5-sonnet' }, { maxTokens: 100000, strategy: 'summarize', model: 'claude-3-5-sonnet' }, { maxTokens: Infinity, strategy: 'rag', model: 'claude-3-5-sonnet' } ]; async function selectStrategy(messages: Message[]): ContextTier { const tokens = await countTokens(messages); for (const tier of TIERS) { if (tokens <= tier.maxTokens) { return tier; } } return TIERS[TIERS.length - 1]; } async function prepareContext(messages: Message[]): PreparedContext { const tier = await selectStrategy(messages); switch (tier.strategy) { case 'full': return { messages, model: tier.model }; case 'summarize': const summary = await summarizeOldMessages(messages); return { messages: [summary, ...recentMessages(messages)], model: tier.model }; case 'rag': const relevant = await retrieveRelevant(messages); return { messages: [...relevant, ...recentMessages(messages)], model: tier.model }; } }
Place important content at start and end
When to use: Constructing prompts with significant context
typescript// LLMs weight beginning and end more heavily // Structure prompts to leverage this function buildOptimalPrompt(components: { systemPrompt: string; criticalContext: string; conversationHistory: Message[]; currentQuery: string; }): string { // START: System instructions (always first) const parts = [components.systemPrompt]; // CRITICAL CONTEXT: Right after system (high primacy) if (components.criticalContext) { parts.push(`## Key Context\n${components.criticalContext}`); } // MIDDLE: Conversation history (lower weight) // Summarize if long, keep recent messages full const history = components.conversationHistory; if (history.length > 10) { const oldSummary = summarize(history.slice(0, -5)); const recent = history.slice(-5); parts.push(`## Earlier Conversation (Summary)\n${oldSummary}`); parts.push(`## Recent Messages\n${formatMessages(recent)}`); } else { parts.push(`## Conversation\n${formatMessages(history)}`); } // END: Current query (high recency) // Restate critical requirements here parts.push(`## Current Request\n${components.currentQuery}`); // FINAL: Reminder of key constraints parts.push(`Remember: ${extractKeyConstraints(components.systemPrompt)}`); return parts.join('\n\n'); }
Summarize by importance, not just recency
When to use: Context exceeds optimal size
typescriptinterface MessageWithMetadata extends Message { importance: number; // 0-1 score hasCriticalInfo: boolean; // User preferences, decisions referenced: boolean; // Was this referenced later? } async function smartSummarize( messages: MessageWithMetadata[], targetTokens: number ): Message[] { // Sort by importance, preserve order for tied scores const sorted = [...messages].sort((a, b) => (b.importance + (b.hasCriticalInfo ? 0.5 : 0) + (b.referenced ? 0.3 : 0)) - (a.importance + (a.hasCriticalInfo ? 0.5 : 0) + (a.referenced ? 0.3 : 0)) ); const keep: Message[] = []; const summarizePool: Message[] = []; let currentTokens = 0; for (const msg of sorted) { const msgTokens = await countTokens([msg]); if (currentTokens + msgTokens < targetTokens * 0.7) { keep.push(msg); currentTokens += msgTokens; } else { summarizePool.push(msg); } } // Summarize the low-importance messages if (summarizePool.length > 0) { const summary = await llm.complete(` Summarize these messages, preserving: - Any user preferences or decisions - Key facts that might be referenced later - The overall flow of conversation Messages: ${formatMessages(summarizePool)} `); keep.unshift({ role: 'system', content: `[Earlier context: ${summary}]` }); } // Restore original order return keep.sort((a, b) => a.timestamp - b.timestamp); }
Allocate token budget across context components
When to use: Need predictable context management
typescriptinterface TokenBudget { system: number; // System prompt criticalContext: number; // User prefs, key info history: number; // Conversation history query: number; // Current query response: number; // Reserved for response } function allocateBudget(totalTokens: number): TokenBudget { return { system: Math.floor(totalTokens * 0.10), // 10% criticalContext: Math.floor(totalTokens * 0.15), // 15% history: Math.floor(totalTokens * 0.40), // 40% query: Math.floor(totalTokens * 0.10), // 10% response: Math.floor(totalTokens * 0.25), // 25% }; } async function buildWithBudget( components: ContextComponents, modelMaxTokens: number ): PreparedContext { const budget = allocateBudget(modelMaxTokens); // Truncate/summarize each component to fit budget const prepared = { system: truncateToTokens(components.system, budget.system), criticalContext: truncateToTokens( components.criticalContext, budget.criticalContext ), history: await summarizeToTokens(components.history, budget.history), query: truncateToTokens(components.query, budget.query), }; // Reallocate unused budget const used = await countTokens(Object.values(prepared).join('\n')); const remaining = modelMaxTokens - used - budget.response; if (remaining > 0) { // Give extra to history (most valuable for conversation) prepared.history = await summarizeToTokens( components.history, budget.history + remaining ); } return prepared; }
Severity: WARNING
Message: Building context without token counting. May exceed model limits.
Fix action: Count tokens before sending, implement budget allocation
Severity: WARNING
Message: Truncating messages without summarization. Critical context may be lost.
Fix action: Summarize old messages instead of simply removing them
Severity: INFO
Message: Hardcoded token limit. Consider making configurable per model.
Fix action: Use model-specific limits from configuration
Severity: WARNING
Message: LLM calls without context management strategy.
Fix action: Implement context management: budgets, summarization, or RAG
Skills: context-window-management, rag-implementation, conversation-memory, prompt-caching
Workflow:
1. Design context strategy
2. Implement RAG for large corpuses
3. Set up memory persistence
4. Add caching for performanceWorks well with: rag-implementation, conversation-memory, prompt-caching, llm-npc-dialogue
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | fail→pass | 33,950 | 7,621 | -78% | 1 | 1 | 0% | 3,160 | 3,924 | +24% | 0 | 0 | — |
case-01 | fail→pass | 17,677 | 17,187 | -3% | 1 | 1 | 0% | 3,627 | 5,414 | +49% | 0 | 0 | — |
case-02 | fail→fail | 33,274 | 13,030 | -61% | 1 | 1 | 0% | 1,749 | 4,734 | +171% | 0 | 0 | — |
case-03 | fail→pass | 23,076 | 21,564 | -7% | 1 | 1 | 0% | 4,281 | 6,785 | +58% | 0 | 0 | — |
case-04 | fail→pass | 19,319 | 12,181 | -37% | 1 | 1 | 0% | 3,518 | 4,522 | +29% | 0 | 0 | — |
case-05 | pass→pass | 20,472 | 13,874 | -32% | 1 | 1 | 0% | 3,635 | 4,743 | +30% | 0 | 0 | — |
case-06 | pass→pass | 16,796 | 12,030 | -28% | 1 | 1 | 0% | 2,456 | 4,403 | +79% | 0 | 0 | — |
case-07 | pass→pass | 10,449 | 10,565 | +1% | 1 | 1 | 0% | 1,881 | 4,195 | +123% | 0 | 0 | — |
case-08 | pass→pass | 13,403 | 12,526 | -7% | 1 | 1 | 0% | 2,348 | 4,398 | +87% | 0 | 0 | — |
case-09 | pass→pass | 10,198 | 10,994 | +8% | 1 | 1 | 0% | 1,788 | 4,087 | +129% | 0 | 0 | — |
case-10 | pass→pass | 15,278 | 12,551 | -18% | 1 | 1 | 0% | 2,551 | 4,581 | +80% | 0 | 0 | — |
case-11 | fail→pass | 8,247 | 2,583 | -69% | 1 | 1 | 0% | 1,408 | 2,645 | +88% | 0 | 0 | — |
case-12 | fail→pass | 9,172 | 3,065 | -67% | 1 | 1 | 0% | 1,457 | 2,791 | +92% | 0 | 0 | — |
case-13 | fail→pass | 5,637 | 4,815 | -15% | 1 | 1 | 0% | 1,003 | 3,087 | +208% | 0 | 0 | — |
case-15 | fail→pass | 9,622 | 2,335 | -76% | 1 | 1 | 0% | 1,517 | 2,767 | +82% | 0 | 0 | — |
case-16 | pass→pass | 12,069 | 10,107 | -16% | 1 | 1 | 0% | 1,930 | 3,882 | +101% | 0 | 0 | — |
case-17 | fail→pass | 10,667 | 2,565 | -76% | 1 | 1 | 0% | 1,769 | 2,800 | +58% | 0 | 0 | — |
case-18 | pass→pass | 13,923 | 9,505 | -32% | 1 | 1 | 0% | 2,474 | 4,023 | +63% | 0 | 0 | — |
case-19 | fail→pass | 31,370 | 3,082 | -90% | 1 | 1 | 0% | 2,515 | 2,855 | +14% | 0 | 0 | — |
case-20 | pass→pass | 14,907 | 11,541 | -23% | 1 | 1 | 0% | 2,694 | 4,447 | +65% | 0 | 0 | — |
case-21 | pass→pass | 11,613 | 10,556 | -9% | 1 | 1 | 0% | 2,047 | 4,112 | +101% | 0 | 0 | — |
case-22 | pass→pass | 9,441 | 7,599 | -20% | 1 | 1 | 0% | 1,585 | 3,473 | +119% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +45 percentage points is the difference between those two pass rates over the 20 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.