Claude API の Context Compaction(beta)で長時間対話を継続する — 設定を間違えると会話が壊れる落とし穴

Claude API に Context Compaction ベータが追加されました。長時間の multi-turn 会話が 1M context を超えそうになる前に、サーバー側が履歴を自動要約して継続できるようにする機能です。beta header、設定方法、そして最大の footgun(response.content を text だけ拾ってしまう)を実装込みでまとめます。

Claude API に Context Compaction(beta、compact-2026-01-12)が追加されました。長時間の multi-turn 会話が 1M context に近づいた時点で、サーバー側が早期履歴を自動要約して、そのまま会話を続けられるようにする機能です。自作で tail-truncation や summarization を書く必要が(当面)無くなる、運用上は地味に重要な更新です。

本記事では、有効化の手順と、ここで外すと機能全体が静かに壊れる 1 行 を含めて整理します。

到達点

  • Context Compaction が効く対象モデルと beta header の正体を知る
  • context_management.edits の書き方
  • 最大の footgun: 応答の response.content を丸ごと次のターンに append する(text だけ拾うと compaction ブロックが失われる)
  • どれくらい長い会話で発動するか、コスト影響の見方

検証環境: 2026-04 時点、@anthropic-ai/sdk v0.90 系。

1. 対象モデルと beta header

Context Compaction は現時点で以下 3 モデルの beta サーフェスです。

  • Claude Opus 4.7 (claude-opus-4-7)
  • Claude Opus 4.6 (claude-opus-4-6)
  • Claude Sonnet 4.6 (claude-sonnet-4-6)

beta header は compact-2026-01-12。SDK からは client.beta.messages.create({ betas: ["compact-2026-01-12"], ... }) を呼び出すのが素直です。

2. 仕組み — trigger と edit

有効化には context_management.editscompact_20260112 エントリを入れます。デフォルトの trigger は 150,000 トークン(これを超えかけると自動要約が入る)。

const response = await client.beta.messages.create({
  betas: ["compact-2026-01-12"],
  model: "claude-opus-4-7",
  max_tokens: 16_000,
  thinking: { type: "adaptive" },
  context_management: {
    edits: [{ type: "compact_20260112" }],
  },
  messages,
});

要約処理は Claude 側で走ります。クライアントから「ここを要約して」と指示する必要はなく、context の消費量が閾値に近づいた時点で自動発火します。

3. ここで外すと全部壊れる — response.content を丸ごと append する

Compaction は レスポンスの content 配列に “compaction ブロック” を含めて返します。API はこのブロックを次のリクエストで読んで、すでに要約された履歴を上書きします。

そのため、応答を次のターンへ引き渡すときは、text だけを拾うのではなく、response.content を丸ごと messages に append しなければなりません。

// ❌ これは壊れる — compaction ブロックが失われ、次ターンで context が再膨張する
const text = response.content
  .filter((b) => b.type === "text")
  .map((b) => (b as { text: string }).text)
  .join("\n");
messages.push({ role: "assistant", content: text });
// ✅ これが正解 — content 配列をそのまま持ち回す
messages.push({ role: "assistant", content: response.content });

UI に text だけ表示するのと、API に content を戻すのは別の話です。UI 表示用に text を抽出するのは OK ですが、API 側には必ず response.content をそのまま送り直すという原則を守ります。

4. multi-turn の最小実装

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
const messages: Anthropic.Beta.BetaMessageParam[] = [];

async function chat(userMessage: string) {
  messages.push({ role: "user", content: userMessage });

  const response = await client.beta.messages.create({
    betas: ["compact-2026-01-12"],
    model: "claude-opus-4-7",
    max_tokens: 16_000,
    thinking: { type: "adaptive" },
    context_management: {
      edits: [{ type: "compact_20260112" }],
    },
    messages,
  });

  // ✅ content 配列を丸ごと保持 (compaction ブロックが含まれる可能性)
  messages.push({ role: "assistant", content: response.content });

  // UI 用には text だけ拾う
  const text = response.content
    .filter((b): b is Anthropic.Beta.BetaTextBlock => b.type === "text")
    .map((b) => b.text)
    .join("");

  return { text, usage: response.usage };
}

// 長尺の対話
console.log(await chat("Python web scraper を設計して"));
console.log(await chat("JavaScript レンダリングも対応させて"));
console.log(await chat("rate limit と error handling を追加"));
// …会話がどれだけ続いても 1M を超える前に自動 compaction

5. いつ使うか

  • 会話型エージェントで、ユーザーが 1 つのセッションで 100 ターン以上回す UI(コーディング伴走、調査アシスタント等)
  • 長時間の tool-use ループ で、入出力が累積して 150K トークンを超えそうな場合
  • Managed Agents ではない自作ループ(Managed Agents 側はコンテキスト管理が built-in)

短時間・短文の Q&A 主体なら compaction は発動しないので、beta header を付ける必要もありません。

6. usage から発動を見る

レスポンスの usage を毎ターン保存しておくと、どのターンで compaction が走ったかが粒度で見えます。特に input_tokens が急に下がるターンが、まさに要約が入って過去履歴が圧縮された瞬間です。

Prompt Caching 記事 (本ブログ既出) と同様、input_tokens + cache_creation_input_tokens + cache_read_input_tokens の合計で見るのが正解で、1 フィールド単体の推移を追うと誤解します。

7. 落とし穴

  • content を text 化して push する — compaction ブロックが失われ、次ターンで context が再膨張して効果ゼロ。本記事の最重要事項
  • beta header を忘れるbetas: ["compact-2026-01-12"] を指定していないと context_management が無視される
  • 対応モデルと組み合わせてない — Sonnet 4.5 以前 / Haiku 系では使えない。移行前にモデル ID を確認
  • trigger しきい値を低く期待しすぎる — デフォルトは 150K トークン。短いセッションでは発動しないので「効いていない」と誤認しやすい
  • streaming 時の取り扱い — streaming でも原則は同じで、最終メッセージ再構築時に response.content 全体を取り出す。ここを finalMessage() 等の SDK helper に寄せる

まとめ

Context Compaction は、自作ループで 1M 文脈を超えないための運用コードを肩代わりしてくれる beta です。使う上で重要なのは 1 点だけで、毎ターン response.content を丸ごと messages に戻す。ここさえ守れば beta 卒業まで特別な運用コードは要りません。

次回の release notes は Structured Outputs の新 output_config.formatclient.messages.parse() を取り上げる予定です。

参考

記事で登場したコードをひとつの最小リポジトリとしてまとめました。ファイルツリーから切り替えて、全体の構造をそのまま確認できます。

$cat./package.jsonjson
{
  "name": "claude-context-compaction-starter",
  "private": true,
  "type": "module",
  "scripts": {
    "basic": "bun src/basic.ts",
    "multi": "bun src/multi-turn.ts",
    "footgun": "bun src/common-footgun.ts",
    "usage": "bun src/inspect-usage.ts"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.90.0"
  },
  "devDependencies": {
    "@types/bun": "^1.3.0",
    "typescript": "^6.0.3"
  }
}
slugclaude-context-compactionfiles8click a file in the tree to switch