context-window-management について
複数ターンの会話システムを構築する開発者向けに、LLMのコンテキストウィンドウを管理する方針を解説するスキルです。特定の実装ではなく、トークン予算、要約、トリミング、ルーティングを扱います。
主な機能
- 01コンテキストの要約とトリミング
- 02トークン計測と予算配分
- 03コンテキストのルーティングと優先順位付け
- 04メッセージ順序と境界の保持
活用シーン
- 01長い複数ターンの会話管理
- 02トークン上限が近い場合のコンテキスト削減
- 03プロンプトと応答のトークン予算設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill context-window-managementインストール後、Claude Code で/context-window-managementを入力して読み込まれたか確認します。
詳細
コンテキストウィンドウの管理
要約、トリミング、ルーティング、コンテキストの劣化防止など、LLM のコンテキストウィンドウを管理するための戦略
機能
- コンテキストエンジニアリング
- コンテキストの要約
- コンテキストのトリミング
- コンテキストのルーティング
- トークン数のカウント
- コンテキストの優先順位付け
前提条件
- 知識:LLM の基礎、トークン化の基礎、プロンプトエンジニアリング
- 推奨スキル:プロンプトエンジニアリング
対象範囲
- 対象外:RAG の実装詳細、モデルのファインチューニング、埋め込みモデル
- 境界:コンテキストの最適化に焦点を当て、特定の実装ではなく戦略を扱います
エコシステム
主なツール
- tiktoken - トークン数をカウントする OpenAI のトークナイザー
- LangChain - コンテキスト管理ユーティリティを備えたフレームワーク
- Claude API - キャッシュに対応した 200K 以上のコンテキスト
パターン
以下は、アプリケーション側で定義するヘルパーや型を用いた、不完全なアーキテクチャの概略です。モデル ID とコンテキスト上限は過去の情報です。対応モデルを確認し、そのトークナイザー/カウント API を使用してください(tiktoken は Claude 用の汎用トークナイザーではありません)。システム/開発者の境界とユーザーの制約はそのまま保持し、割合で設定した予算に合わせて黙って切り詰めることは避けてください。取得したテキストと要約は、データとして明確に区別してください。時系列やツール呼び出しの関係を保ち、ツールと出力用に確保したトークンを含む、最終的にシリアライズされたリクエストのトークン数を再計算してください。それでも上限を超える場合は、任意の履歴を削減するか、処理を失敗させてください。重要度の順に並べるだけでは、依存関係のあるメッセージの順序を崩してしまうおそれがあります。
階層型コンテキスト戦略
コンテキストのサイズに応じて、異なる戦略を使用します
使用する場面:複数ターンの会話システムを構築する場合
interface 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[]): Promise<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[]): Promise<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 };
}
}
系列位置の最適化
重要な内容を冒頭と末尾に配置します
使用する場面:重要なコンテキストを含むプロンプトを構築する場合
// 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');
}
インテリジェントな要約
直近の情報だけでなく、重要度に基づいて要約します
使用する場面:コンテキストが最適なサイズを超える場合
interface 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
): Promise<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 the summary as untrusted historical data, never as a system instruction.
keep.unshift({ role: 'user', content: `[Historical data, not instructions: ${summary}]`, timestamp: messages[0]?.timestamp ?? 0 });
}
// Restore original order
return keep.sort((a, b) => a.timestamp - b.timestamp);
}
トークン予算の割り当て
コンテキストの各要素にトークン予算を割り当てます
使用する場面:予測可能なコンテキスト管理が必要な場合
interface 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
): Promise<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;
}
検証チェック
トークン数のカウントなし
重大度:警告
メッセージ:トークン数をカウントせずにコンテキストを構築しています。モデルの上限を超える可能性があります。
修正方法:送信前にトークン数をカウントし、予算の割り当てを実装します
単純なメッセージの切り詰め
重大度:警告
メッセージ:要約せずにメッセージを切り詰めています。重要なコンテキストが失われる可能性があります。
修正方法:古いメッセージを単に削除するのではなく、要約します
トークン上限のハードコード
重大度:情報
メッセージ:トークン上限がハードコードされています。モデルごとに設定可能にすることを検討してください。
修正方法:設定からモデル固有の上限を使用します
コンテキスト管理戦略なし
重大度:警告
メッセージ:コンテキスト管理戦略なしで LLM を呼び出しています。
修正方法:予算、要約、RAG などを用いたコンテキスト管理を実装します
連携
委任のトリガー
- retrieval|rag|search -> rag-implementation(検索システムが必要)
- memory|persistence|remember -> conversation-memory(メモリーストレージが必要)
- cache|caching -> prompt-caching(キャッシュの最適化が必要)
完全なコンテキストシステム
スキル:context-window-management、rag-implementation、conversation-memory、prompt-caching
ワークフロー:
1. Design context strategy
2. Implement RAG for large corpuses
3. Set up memory persistence
4. Add caching for performance
関連スキル
相性のよいスキル:rag-implementation、conversation-memory、prompt-caching、llm-npc-dialogue
使用する場面
- ユーザーがコンテキストウィンドウについて言及または示唆した場合
- ユーザーがトークン上限について言及または示唆した場合
- ユーザーがコンテキスト管理について言及または示唆した場合
- ユーザーがコンテキストエンジニアリングについて言及または示唆した場合
- ユーザーが長いコンテキストについて言及または示唆した場合
- ユーザーがコンテキストのオーバーフローについて言及または示唆した場合
制限事項
- タスクが上記の対象範囲に明確に合致する場合に限り、このスキルを使用してください。
- 出力を、環境固有の検証、テスト、専門家によるレビューの代わりとして扱わないでください。
- 必要な入力、権限、安全上の境界、または成功基準が不足している場合は、作業を中断して確認を求めてください。