---
title: バックグラウンドでのコンパクション
url: https://platform.claude.com/docs/ja/build-with-claude/compaction-background
description: 会話が完全な履歴で継続している間にオンデマンドのコンパクション要約をリクエストし、到着したらブロックを差し替えます。
featureMetadata:
  status: beta
  betaHeader: compact-2026-09-04
  supportedModels:
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-sonnet-4-6
    - claude-haiku-5-5
  supportedPlatforms:
    Claude API: beta
    Claude Platform on AWS: beta
    Google Cloud: beta
    Microsoft Foundry: beta
---

「background compaction」（バックグラウンドコンパクション）は、しばしば「async compaction」（非同期コンパクション）とも呼ばれ、[コンパクションループ](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#compact-in-a-loop)における2つの点を変更します。コンパクションリクエストは会話が完全な履歴で継続している間に実行され、差し替えはブロックが到着するまで待機します。[要約から続ける](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#continue-from-the-summary)と[要約が返されない場合やエラーを処理する](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#when-no-summary-comes-back)は、変更なしでそのまま適用されます。

## 作業を継続しながら差し替えを行う仕組み

コンパクションリクエストと、それが返すブロックは、ループの場合と同じです。リクエストを送信してからその結果を使用するまでの間に履歴は増えていくため、差し替えではその増加分をそのまま残す必要があります。

1. 現時点の履歴で[コンパクションリクエスト](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#request-a-summary)を送信し、そこに含まれていたメッセージの数を記録します。
2. そのリクエストの実行中も、完全な履歴で会話を継続します。新しいターンはそれぞれ追加し、履歴にすでにあるものは編集せず、このリクエストが差し替えられるか失敗するまでは別のコンパクションリクエストを開始しないでください。
3. `stop_reason` が `"compaction"` のレスポンスが到着したら、送信したメッセージとまったく同じ分だけを履歴の先頭から削除し、その位置に返されたメッセージを配置します。ステップ1以降に追加されたすべてのターンは、その後ろに残ります。
4. ブロックが到着した後の最初のリクエストで差し替え済みの履歴を送信し、要約の作成中に生成された思考が有効なままになるようにします。

たとえば、コンパクションリクエストにメッセージ1〜5が含まれており、その実行中に会話にメッセージ6〜8が追加された場合、差し替え後の履歴はブロックの後にメッセージ6〜8が続く形になります。

<Frame>
  ![「background compaction」（バックグラウンドコンパクション）のタイムライン：コンパクションリクエストはメッセージ1〜5とともに送信され、その間も会話は完全な履歴のまま続行してメッセージ6〜8が追加されます。ブロックが届くと、履歴の先頭にあるメッセージ1〜5を置き換え、履歴はブロックの後にメッセージ6〜8が続く形になります](https://platform.claude.com/docs/ja/images/compaction-background-timeline.svg)
</Frame>

レスポンスの `stop_reason` がそれ以外の値である場合、要約は生成されておらず、これはステップ2における失敗として扱われます。完全な履歴を保持してください。[要約が返されない場合やエラーを処理する](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#when-no-summary-comes-back)に、その原因とそれぞれの対処方法が記載されています。

## バックグラウンドで要約をリクエストする

コンパクションリクエストは、他のリクエストと同様に「rate limits」（レート制限）にカウントされ、その実行中はアプリケーションで2つのリクエストが同時に開いた状態になります。会話は差し替えまで完全な履歴で増え続けるため、その間に到着するターンを収める余裕が「context window」（コンテキストウィンドウ）にまだあるうちにコンパクションリクエストを開始してください。

次のプログラムは、[ループでコンパクションする](https://platform.claude.com/docs/ja/build-with-claude/compaction-on-demand#compact-in-a-loop)のループから、コンパクションリクエストを会話の経路から外したものです。ハイライトされた行はループとの違いを示しており、以下のリストではプログラムが実行する順にそれらを説明します。

<CodeGroup exclude="shell, php">
  ```python Python
  from concurrent.futures import Future, ThreadPoolExecutor

  import anthropic
  from anthropic.types.beta import BetaMessage, BetaMessageParam

  client = anthropic.Anthropic()
  executor = ThreadPoolExecutor(max_workers=1)

  # 実際の入力予算に近い値を設定してください。ここでは短い会話でもコンパクションされるよう低くしています。
  COMPACT_AT_TOKENS = 2500
  SYSTEM = "You help design a recipe app's data model. Keep answers short."

  QUESTIONS = [
      "What are the main entities in the data model?",
      "Which fields should Recipe have?",
      "Which fields should Ingredient have?",
      "Which fields should RecipeIngredient have?",
      "Which fields should Step have?",
      "Which indexes should these tables have?",
      "Which fields should be required?",
      "Which fields should have default values?",
  ]


  def swap_in(history: list[BetaMessageParam], summary: BetaMessage, sent: int) -> None:
      if summary.stop_reason == "compaction":
          # コンパクションリクエストが保持していたメッセージだけを正確に置き換えます。
          # 以降のターンはブロックの後ろに残ります。
          history[:sent] = [{"role": "assistant", "content": summary.content}]
          print(f"Swapped {sent} messages")


  history: list[BetaMessageParam] = []
  pending: Future[BetaMessage] | None = None
  sent = 0
  for turn, question in enumerate(QUESTIONS, start=1):
      if pending is not None and pending.done():
          swap_in(history, pending.result(), sent)
          pending = None

      history.append({"role": "user", "content": question})
      response = client.beta.messages.create(
          model="claude-opus-5-5",
          max_tokens=8192,
          system=SYSTEM,
          betas=["compact-2026-09-04"],
          messages=history,
      )
      history.append({"role": "assistant", "content": response.content})

      # 次のリクエストではこの応答も送信されるため、カウントに含めます。
      conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
      if (
          conversation_tokens > COMPACT_AT_TOKENS
          and turn < len(QUESTIONS)
          and pending is None
      ):
          sent = len(history)
          pending = executor.submit(
              client.beta.messages.create,
              model="claude-opus-5-5",
              max_tokens=4096,
              system=SYSTEM,
              betas=["compact-2026-09-04"],
              messages=history.copy(),
              compaction={"type": "summarize"},
          )

  # 保存する前に、まだ生成中の要約に差し替えます
  # または会話を続行します。
  if pending is not None:
      swap_in(history, pending.result(), sent)
  executor.shutdown()
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // 実際の入力予算に近い値を設定してください。ここでは短い会話でも compaction（圧縮）されるよう低くしています。
  const compactAtTokens = 2500;
  const systemPrompt = "You help design a recipe app's data model. Keep answers short.";

  const questions = [
    "What are the main entities in the data model?",
    "Which fields should Recipe have?",
    "Which fields should Ingredient have?",
    "Which fields should RecipeIngredient have?",
    "Which fields should Step have?",
    "Which indexes should these tables have?",
    "Which fields should be required?",
    "Which fields should have default values?"
  ];

  function swapIn(
    history: Anthropic.Beta.Messages.BetaMessageParam[],
    summary: Anthropic.Beta.Messages.BetaMessage,
    sent: number
  ): Anthropic.Beta.Messages.BetaMessageParam[] {
    if (summary.stop_reason !== "compaction") {
      return history;
    }
    console.log(`Swapped ${sent} messages`);
    // compaction リクエストが保持していたメッセージだけを置き換えます。後続のターンはブロックの後に残ります。
    return [{ role: "assistant", content: summary.content }, ...history.slice(sent)];
  }

  let history: Anthropic.Beta.Messages.BetaMessageParam[] = [];
  let pending: Promise<Anthropic.Beta.Messages.BetaMessage> | undefined;
  let settled = false;
  let sent = 0;
  for (const [index, question] of questions.entries()) {
    const turn = index + 1;
    if (pending && settled) {
      history = swapIn(history, await pending, sent);
      pending = undefined;
      settled = false;
    }

    history.push({ role: "user", content: question });
    const response = await client.beta.messages.create({
      model: "claude-opus-5-5",
      max_tokens: 8192,
      system: systemPrompt,
      betas: ["compact-2026-09-04"],
      messages: history
    });
    history.push({ role: "assistant", content: response.content });

    // 次のリクエストではこの応答も送信されるため、カウントに含めてください。
    const conversationTokens = response.usage.input_tokens + response.usage.output_tokens;
    if (conversationTokens > compactAtTokens && turn < questions.length && !pending) {
      sent = history.length;
      pending = client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        system: systemPrompt,
        betas: ["compact-2026-09-04"],
        messages: [...history],
        compaction: { type: "summarize" }
      });
      // いずれの場合もリクエストを完了済みとしてマークします。その後 await すると要約が返るか、例外がスローされます。
      const markSettled = () => {
        settled = true;
      };
      pending.then(markSettled, markSettled);
    }
  }

  // 会話を保存または続行する前に、まだ生成中の要約に差し替えてください。
  if (pending) {
    history = swapIn(history, await pending, sent);
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta;
  using Anthropic.Models.Beta.Messages;
  using Model = Anthropic.Models.Messages.Model;

  AnthropicClient client = new();

  // 実際の入力予算に近い値を設定してください。ここでは短い会話でもコンパクションが発生するよう低く設定しています。
  const int CompactAtTokens = 2500;
  const string SystemPrompt = "You help design a recipe app's data model. Keep answers short.";

  string[] questions =
  [
      "What are the main entities in the data model?",
      "Which fields should Recipe have?",
      "Which fields should Ingredient have?",
      "Which fields should RecipeIngredient have?",
      "Which fields should Step have?",
      "Which indexes should these tables have?",
      "Which fields should be required?",
      "Which fields should have default values?",
  ];

  static List<BetaMessageParam> SwapIn(List<BetaMessageParam> history, BetaMessage summary, int sent)
  {
      if (summary.StopReason != BetaStopReason.Compaction)
      {
          return history;
      }
      Console.WriteLine($"Swapped {sent} messages");
      // コンパクションリクエストが保持していたメッセージだけを正確に置き換えます。以降のターンはブロックの後に残ります。
      return
      [
          new()
          {
              Role = Role.Assistant,
              Content = summary.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
          },
          .. history[sent..],
      ];
  }

  List<BetaMessageParam> history = [];
  Task<BetaMessage>? pending = null;
  var sent = 0;
  foreach (var (index, question) in questions.Index())
  {
      var turn = index + 1;
      if (pending is { IsCompleted: true })
      {
          history = SwapIn(history, await pending, sent);
          pending = null;
      }

      history.Add(new() { Role = Role.User, Content = question });
      var response = await client.Beta.Messages.Create(new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 8192,
          System = SystemPrompt,
          Betas = [AnthropicBeta.Compact2026_09_04],
          Messages = history,
      });
      history.Add(new()
      {
          Role = Role.Assistant,
          Content = response.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
      });

      // 次のリクエストではこの応答も送信されるため、これもカウントしてください。
      var conversationTokens = response.Usage.InputTokens + response.Usage.OutputTokens;
      if (conversationTokens > CompactAtTokens && turn < questions.Length && pending is null)
      {
          sent = history.Count;
          pending = client.Beta.Messages.Create(new MessageCreateParams
          {
              Model = Model.ClaudeOpus5_5,
              MaxTokens = 4096,
              System = SystemPrompt,
              Betas = [AnthropicBeta.Compact2026_09_04],
              Messages = [.. history],
              Compaction = new BetaCompactionConfig(), // type defaults to "summarize"
          });
      }
  }

  // 会話を保存または続行する前に、まだ生成中の要約を差し替えてください。
  if (pending is not null)
  {
      history = SwapIn(history, await pending, sent);
  }
  ```

  ```go Go
  ctx := context.Background()
  client := anthropic.NewClient()

  // 実際の入力予算に近い値を設定してください。ここでは短い会話でもコンパクションされるよう低くしています。
  const compactAtTokens = 2500
  system := []anthropic.BetaTextBlockParam{{Text: "You help design a recipe app's data model. Keep answers short."}}

  questions := []string{
  	"What are the main entities in the data model?",
  	"Which fields should Recipe have?",
  	"Which fields should Ingredient have?",
  	"Which fields should RecipeIngredient have?",
  	"Which fields should Step have?",
  	"Which indexes should these tables have?",
  	"Which fields should be required?",
  	"Which fields should have default values?",
  }

  var history []anthropic.BetaMessageParam
  var pending chan *anthropic.BetaMessage
  var sent int
  swapIn := func(summary *anthropic.BetaMessage) {
  	if summary.StopReason != anthropic.BetaStopReasonCompaction {
  		return
  	}
  	fmt.Printf("Swapped %d messages\n", sent)
  	// コンパクションリクエストが保持していたメッセージだけを置き換えます。以降のターンはブロックの後に残ります。
  	history = slices.Replace(history, 0, sent, summary.ToParam())
  }

  for i, question := range questions {
  	turn := i + 1
  	// nil チャネルからの受信は決して成功しないため、保留中のものがなければここはスキップされます。
  	select {
  	case summary := <-pending:
  		swapIn(summary)
  		pending = nil
  	default:
  	}

  	history = append(history, anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(question)))
  	response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 8192,
  		System:    system,
  		Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaCompact2026_09_04},
  		Messages:  history,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	history = append(history, response.ToParam())

  	// 次のリクエストではこの応答も送信されるため、カウントに含めます。
  	conversationTokens := response.Usage.InputTokens + response.Usage.OutputTokens
  	if conversationTokens > compactAtTokens && turn < len(questions) && pending == nil {
  		sent = len(history)
  		pending = make(chan *anthropic.BetaMessage, 1)
  		go func(messages []anthropic.BetaMessageParam, result chan<- *anthropic.BetaMessage) {
  			summary, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
  				Model:     anthropic.ModelClaudeOpus5_5,
  				MaxTokens: 4096,
  				System:    system,
  				Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaCompact2026_09_04},
  				Messages:  messages,
  				Compaction: anthropic.BetaCompactionConfigUnionParam{
  					OfSummarize: &anthropic.BetaSummarizeCompactionParam{},
  				},
  			})
  			if err != nil {
  				log.Fatal(err)
  			}
  			result <- summary
  		}(slices.Clone(history), pending)
  	}
  }

  // 会話を保存または続行する前に、まだ生成中の要約を差し替えてください。
  if pending != nil {
  	swapIn(<-pending)
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaCompactionConfig;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaMessageParam;
  import com.anthropic.models.beta.messages.BetaStopReason;
  import com.anthropic.models.beta.messages.MessageCreateParams;

  // 実際の入力予算に近い値を設定してください。ここでは短い会話でもコンパクションされるよう低くしています。
  static final long COMPACT_AT_TOKENS = 2500;
  static final String SYSTEM = "You help design a recipe app's data model. Keep answers short.";

  void swapIn(List<BetaMessageParam> history, BetaMessage summary, int sent) {
      if (!summary.stopReason().map(BetaStopReason.COMPACTION::equals).orElse(false)) {
          return;
      }
      IO.println("Swapped " + sent + " messages");
      // コンパクションリクエストが保持していたメッセージだけを置き換えます。以降のターンはブロックの後に残ります。
      history.subList(0, sent).clear();
      history.addFirst(summary.toParam());
  }

  void main() {
      var client = AnthropicOkHttpClient.fromEnv();

      var questions = List.of(
          "What are the main entities in the data model?",
          "Which fields should Recipe have?",
          "Which fields should Ingredient have?",
          "Which fields should RecipeIngredient have?",
          "Which fields should Step have?",
          "Which indexes should these tables have?",
          "Which fields should be required?",
          "Which fields should have default values?"
      );

      var history = new ArrayList<BetaMessageParam>();
      CompletableFuture<BetaMessage> pending = null;
      int sent = 0;
      for (int turn = 1; turn <= questions.size(); turn++) {
          if (pending != null && pending.isDone()) {
              swapIn(history, pending.join(), sent);
              pending = null;
          }

          history.add(BetaMessageParam.builder()
              .role(BetaMessageParam.Role.USER)
              .content(questions.get(turn - 1))
              .build());
          var params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(8192)
              .system(SYSTEM)
              .addBeta(AnthropicBeta.COMPACT_2026_09_04)
              .messages(history)
              .build();
          var response = client.beta().messages().create(params);
          history.add(response.toParam());

          // 次のリクエストではこの応答も送信されるため、カウントに含めてください。
          long conversationTokens = response.usage().inputTokens() + response.usage().outputTokens();
          if (conversationTokens > COMPACT_AT_TOKENS && turn < questions.size() && pending == null) {
              sent = history.size();
              var summaryParams = MessageCreateParams.builder()
                  .model(Model.CLAUDE_OPUS_5_5)
                  .maxTokens(4096)
                  .system(SYSTEM)
                  .addBeta(AnthropicBeta.COMPACT_2026_09_04)
                  .messages(List.copyOf(history))
                  .compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
                  .build();
              pending = client.async().beta().messages().create(summaryParams);
          }
      }

      // 会話を保存または続行する前に、まだ生成中の要約を差し替えてください。
      if (pending != null) {
          swapIn(history, pending.join(), sent);
      }
      client.close();
  }
  ```

  <CodeGroupItem language="PHP">
    PHP SDKはリクエストを同期的に送信するため、会話を続けながらバックグラウンドでコンパクションリクエストを実行することはできません。
  </CodeGroupItem>

  ```ruby Ruby
  client = Anthropic::Client.new

  # 実際の入力予算に近い値を設定してください。ここでは短い会話でも圧縮されるよう低くしています。
  COMPACT_AT_TOKENS = 2500
  SYSTEM = "You help design a recipe app's data model. Keep answers short."

  questions = [
    "What are the main entities in the data model?",
    "Which fields should Recipe have?",
    "Which fields should Ingredient have?",
    "Which fields should RecipeIngredient have?",
    "Which fields should Step have?",
    "Which indexes should these tables have?",
    "Which fields should be required?",
    "Which fields should have default values?"
  ]

  def swap_in(history, summary, sent)
    return history unless summary.stop_reason == :compaction

    puts "Swapped #{sent} messages"
    # 圧縮リクエストに含まれていたメッセージだけを正確に置き換えます。以降のターンはブロックの後に残ります。
    [{ role: "assistant", content: summary.content }, *history[sent..]]
  end

  history = []
  pending = nil
  sent = 0
  questions.each.with_index(1) do |question, turn|
    if pending && !pending.alive?
      history = swap_in(history, pending.value, sent)
      pending = nil
    end

    history << { role: "user", content: question }
    response = client.beta.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 8192,
      system_: SYSTEM,
      betas: [Anthropic::AnthropicBeta::COMPACT_2026_09_04],
      messages: history
    )
    history << { role: "assistant", content: response.content }

    # 次のリクエストではこの応答も送信されるため、カウントに含めてください。
    conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
    if conversation_tokens > COMPACT_AT_TOKENS && turn < questions.length && pending.nil?
      sent = history.length
      pending = Thread.new(history.dup) do |snapshot|
        client.beta.messages.create(
          model: Anthropic::Model::CLAUDE_OPUS_5_5,
          max_tokens: 4096,
          system_: SYSTEM,
          betas: [Anthropic::AnthropicBeta::COMPACT_2026_09_04],
          messages: snapshot,
          compaction: { type: "summarize" }
        )
      end
    end
  end

  # 会話を保存または続行する前に、まだ生成途中の要約に差し替えてください。
  history = swap_in(history, pending.value, sent) if pending
  ```
</CodeGroup>

* **コンパクションのタイミングの判断：** サイズのチェックでは、保留中のコンパクションリクエストがないことも条件になります。
* **リクエストの開始：** ループではコンパクションのレスポンスを待機していた箇所で、このバージョンでは履歴に含まれるメッセージの数を記録し、各言語固有の並行処理ツールを使って履歴のコピーでリクエストを開始し、待機せずに次のターンへ進みます。
* **結果の確認：** 各ターンの冒頭で、プログラムは保留中のリクエストが完了したかどうかを確認します。完了している場合、プログラムはそのターンのリクエストを送信する前に差し替えを行います。
* **差し替えの実行：** ループでは履歴全体を返されたメッセージで置き換えていた箇所で、このバージョンの差し替え関数は、リクエストに含まれていたメッセージだけを先頭から数えて置き換え、それ以降に追加されたものはすべて保持します。
* **ループの終了：** ループの終了時にコンパクションリクエストがまだ保留中の場合、プログラムはその完了を待って差し替えを行います。これにより、会話を保存または継続する前に、まだ到着途中の要約が失われることはありません。

`stop_reason` のチェックはループから変更されていません。ブロックを含まないレスポンスの場合、履歴は元のまま残ります。保留中のものがなくなるため、プログラムはその後、新しいコンパクションリクエストを開始できます。

## 要約の作成中に思考を有効に保つ

要約の作成中に到着したターンは、残されるターンです。[「preserved thinking」（保持された思考）](https://platform.claude.com/docs/ja/build-with-claude/preserved-thinking)に対応したモデルで思考ブロックを送り返す場合、それらのターンの思考は、[残した思考が有効なままとなる条件](https://platform.claude.com/docs/ja/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid)が満たされている間のみ有効です。
