Claude API Compaction API実践ガイド — コンテキスト要約で「無限会話」エージェントを構築する
Claude APIで長時間動くエージェントを運用していると、必ずぶつかる壁がある。コンテキストウィンドウの上限だ。会話が長くなるほどトークンは膨らみ、コストは跳ね上がり、最悪の場合はリクエストが失敗する。Compaction APIは、この問題をサーバーサイドで自動解決する。実測で約58%のトークン削減——本記事では、閾値設計からストリーミング統合、課金の落とし穴まで、実装コード付きで徹底解説する。
Compaction APIとは何か
コンテキスト肥大化の問題
長時間稼働するAIエージェントには、コンテキストが際限なく膨張するという構造的な課題がある。典型的なシナリオを挙げよう。
- ツール呼び出しの繰り返し: ファイル読み取り→編集→ビルド→テスト→修正のループで、1サイクルごとに数千トークンが積み上がる
- マルチターン会話: カスタマーサポートBotで数十往復のやりとりが続くケース
- エージェントワークフロー: 調査→計画→実装→検証を自律的に回すパイプライン
従来の対策は、どれも一長一短だった。手動で要約すればプロンプトエンジニアリングの手間がかかり、会話リセットは文脈を完全に失う。スライディングウィンドウは古いメッセージを単純に切り捨てるため、重要な指示やコンテキストが消える。
Compactionが解決すること
Compaction APIは、入力トークン数が指定の閾値を超えた時点で、Claude自身が会話履歴を要約し、圧縮した状態で応答を継続する仕組みだ。開発者が要約ロジックを書く必要はない。APIパラメータを追加するだけで、サーバーサイドで自動的に処理される。
対応モデルはClaude Opus 4.6(claude-opus-4-6)とClaude Sonnet 4.6(claude-sonnet-4-6)。
基本実装 — 最小コードで動かす
ベータヘッダーとcontext_management設定
Compaction APIを利用するには、2つのポイントを押さえる必要がある。
- ベータヘッダー
anthropic-beta: compact-2026-01-12を指定する - 通常の
client.messages.create()ではなくclient.beta.messages.create()を使う
TypeScriptで動く最小サンプル
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
type Message = {
role: "user" | "assistant";
content: string;
};
const messages: Message[] = [];
async function chat(userMessage: string): Promise<string> {
messages.push({ role: "user", content: userMessage });
const response = await client.beta.messages.create(
{
model: "claude-sonnet-4-6",
max_tokens: 4096,
system: "あなたは親切なアシスタントです。",
messages,
context_management: {
edits: [
{
type: "compact_20260112",
trigger: { type: "input_tokens", value: 80000 },
// pause_after_compaction: false, // デフォルト
// デフォルトではcompactionは透過的に処理され、
// 通常のレスポンスと同じ形式で結果が返る
},
],
},
},
{
headers: { "anthropic-beta": "compact-2026-01-12" },
}
);
const assistantMessage =
response.content[0].type === "text" ? response.content[0].text : "";
messages.push({ role: "assistant", content: assistantMessage });
return assistantMessage;
}
trigger のデフォルト値は input_tokens: 150000、最小値は 50000 だ。閾値を下回る会話ではcompactionは発動せず、通常のMessages APIと同じ挙動になる。
閾値設計とpause_after_compactionの使い分け
ユースケース別の閾値ガイドライン
閾値はモデルのコンテキスト上限の50〜80%を目安にするとよい。上限ギリギリに設定すると、compaction処理自体にもトークンを消費するため、リクエスト失敗のリスクが残る。
| ユースケース | 推奨閾値 | 理由 |
|---|---|---|
| チャットBot | 50,000〜80,000 | コスト重視。頻繁に圧縮して安く運用 |
| コーディングエージェント | 120,000〜150,000 | 情報保持重視。ファイル内容や修正履歴を長く保持 |
| 調査・分析エージェント | 100,000〜130,000 | 中間。調査結果の要点が残ればよい |
pause_after_compaction: true/falseの判断基準
pause_after_compaction は、compaction発動後にAPIが即座にレスポンス生成を続けるか、一旦停止するかを制御する。
- false(デフォルト): 透過的に動かしたい場合。クライアント側の変更が最小限で済む
- true: compaction後にUI更新(「要約しました」の表示)、ログ記録、圧縮後メッセージの検証を挟みたい場合
const response = await client.beta.messages.create(
{
model: "claude-sonnet-4-6",
max_tokens: 4096,
messages,
context_management: {
edits: [
{
type: "compact_20260112",
trigger: { type: "input_tokens", value: 80000 },
pause_after_compaction: true,
// カスタム要約プロンプトで要約品質を制御
instructions:
"重要なタスク指示、ユーザーの好み、未完了の作業項目を優先して要約してください。",
},
],
},
},
{
headers: { "anthropic-beta": "compact-2026-01-12" },
}
);
if (response.stop_reason === "compaction") {
// 圧縮後のメッセージで会話履歴を置き換え
console.log("Compaction完了。圧縮後トークン数を記録...");
// 必要な処理を挟んでから、再度リクエストを送る
}
instructions を指定すれば、要約時にどの情報を優先的に保持するかをClaudeに指示できる。エージェントの種類に応じてカスタマイズしておくと、要約後の精度が安定する。
ストリーミングとツール呼び出しとの併用
ストリーミング時のcompactionイベント処理
ストリーミングモードでは、content_block_start / content_block_delta イベントの中にcompactionブロックが流れてくる。通常のテキストブロックと同様にイベントをハンドリングすればよい。
ツール呼び出しループでの注意点
エージェントの典型的なパターン——ツール呼び出しを含むループ処理でcompactionを使う場合、メッセージ配列の管理に注意が必要だ。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
type ConversationMessage = Anthropic.Beta.Messages.BetaMessageParam;
async function agentLoop(initialPrompt: string): Promise<void> {
let messages: ConversationMessage[] = [
{ role: "user", content: initialPrompt },
];
const tools: Anthropic.Beta.Messages.BetaTool[] = [
{
name: "read_file",
description: "ファイルを読み取る",
input_schema: {
type: "object" as const,
properties: { path: { type: "string" } },
required: ["path"],
},
},
];
while (true) {
const response = await client.beta.messages.create(
{
model: "claude-sonnet-4-6",
max_tokens: 4096,
messages,
tools,
context_management: {
edits: [
{
type: "compact_20260112",
trigger: { type: "input_tokens", value: 120000 },
},
],
},
},
{ headers: { "anthropic-beta": "compact-2026-01-12" } }
);
// compactionが発動した場合、デフォルト(pause_after_compaction: false)では
// 透過的に処理されるため、通常のstop_reasonが返る
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason === "end_turn") break;
// ツール呼び出し処理
if (response.stop_reason === "tool_use") {
const toolResults: Anthropic.Beta.Messages.BetaToolResultBlockParam[] =
[];
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await executeToolCall(block.name, block.input);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: result,
});
}
}
messages.push({ role: "user", content: toolResults });
}
}
}
async function executeToolCall(
name: string,
input: unknown
): Promise<string> {
// 実際のツール実行ロジック
return `${name} の実行結果`;
}
Adaptive Thinking(extended thinking)との併用については、thinkingブロックはcompactionの対象になりうる。thinking内の推論過程は要約時に圧縮される可能性があるため、最終的な判断結果がテキストブロック側にも出力される設計にしておくのが安全だ。
課金の落とし穴 — usage.iterationsを見逃すな
正直、ここが一番ハマりやすいポイントだと思う。
Compaction発動時のレスポンスでは、トップレベルの input_tokens / output_tokens にはcompactionフェーズ分が含まれない。正確なコストを把握するには、usage.iterations 配列を確認する必要がある。
function calculateTotalUsage(usage: {
input_tokens: number;
output_tokens: number;
iterations?: Array<{
type: string;
input_tokens: number;
output_tokens: number;
}>;
}): { totalInput: number; totalOutput: number } {
if (!usage.iterations || usage.iterations.length === 0) {
// compaction未発動: トップレベルの値がそのまま使える
return {
totalInput: usage.input_tokens,
totalOutput: usage.output_tokens,
};
}
// compaction発動時: iterations全体を合算
let totalInput = 0;
let totalOutput = 0;
for (const iteration of usage.iterations) {
totalInput += iteration.input_tokens;
totalOutput += iteration.output_tokens;
console.log(
` [${iteration.type}] in: ${iteration.input_tokens}, out: ${iteration.output_tokens}`
);
}
return { totalInput, totalOutput };
}
// 使用例
// const { totalInput, totalOutput } = calculateTotalUsage(response.usage);
// console.log(`実トークン消費: ${totalInput} input + ${totalOutput} output`);
Anthropic Cookbookの実測ベンチマークでは、208,838トークン → 86,446トークンと58.6%の削減が報告されている。ただし、compaction処理自体にもトークンが消費される点はトレードオフとして認識しておく必要がある。長い会話ほど圧縮の恩恵が大きく、短い会話で頻繁にcompactionを走らせると逆にコスト増になる場合もある。
よくあるハマりポイントと対処法
ベータヘッダーの付け忘れ: anthropic-beta: compact-2026-01-12 を指定せずにリクエストすると、context_management パラメータが無視されるか、分かりにくいエラーが返る。SDKの client.beta.messages.create() を使っていても、ヘッダーは別途指定が必要だ。
閾値を低くしすぎる: trigger を50,000(最小値)付近に設定すると、compactionが頻繁に走る。要約の要約が繰り返されることで情報の劣化が起き、エージェントが以前の指示を「忘れる」現象につながる。チャットBotでも80,000以上を推奨する。
システムプロンプトはcompaction対象外: これは安心材料だ。system パラメータに設定した内容はcompactionで要約されない。重要な指示や人格設定はシステムプロンプトに入れておけば、何度compactionが走っても失われない。
プロンプトキャッシュとの相互作用: Prompt Cachingを併用している場合、compaction後はキャッシュブレークポイントが無効になる可能性がある。圧縮後のメッセージは元のメッセージとは異なる内容になるため、キャッシュヒット率が下がる点は想定しておこう。
Compaction APIは、長時間稼働エージェントのコンテキスト管理という実務上の大きな課題を、APIパラメータの追加だけで解決できる強力な機能だ。閾値設計と pause_after_compaction の使い分けを押さえれば、トークンコストを半分以下に抑えつつ、会話の文脈を維持した「無限会話」エージェントを構築できる。まだベータ版のため仕様変更の可能性はあるが、長時間エージェントを運用しているなら今すぐ試す価値がある。
