---
title: Сжатие в фоновом режиме
url: https://platform.claude.com/docs/ru/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/ru/build-with-claude/compaction-on-demand#compact-in-a-loop): запрос на сжатие выполняется, пока разговор продолжается с полной историей, а замена откладывается до получения блока. Разделы [Продолжение со сводки](https://platform.claude.com/docs/ru/build-with-claude/compaction-on-demand#continue-from-the-summary) и [Обработка отсутствующей сводки или ошибки](https://platform.claude.com/docs/ru/build-with-claude/compaction-on-demand#when-no-summary-comes-back) применяются без изменений.

## Как работает замена, пока работа продолжается

Запрос на сжатие и возвращаемый им блок такие же, как в цикле. Ваша история растёт в промежутке между отправкой запроса и использованием его результата, и замена должна сохранить этот прирост.

1. Отправьте [запрос на сжатие](https://platform.claude.com/docs/ru/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/ru/images/compaction-background-timeline.svg)
</Frame>

Если в ответе указан любой другой `stop_reason`, сводка не была создана, и это считается неудачей в смысле шага 2. Сохраните полную историю; в разделе [Обработка отсутствующей сводки или ошибки](https://platform.claude.com/docs/ru/build-with-claude/compaction-on-demand#when-no-summary-comes-back) перечислены причины и действия для каждой из них.

## Запрос сводки в фоновом режиме

Запрос на сжатие учитывается в ваших «rate limits» (ограничениях скорости), как и любой другой запрос, и пока он выполняется, у вашего приложения одновременно открыты два запроса. Разговор продолжается с полной историей и растёт вплоть до замены, поэтому запускайте запрос на сжатие, пока в «context window» (контекстном окне) ещё есть место для ходов, которые поступят за это время.

Следующая программа — это цикл из раздела [Сжатие в цикле](https://platform.claude.com/docs/ru/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();

  // Задайте значение, близкое к реальному бюджету входных токенов. Здесь оно занижено, чтобы сжимался даже короткий диалог.
  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`);
    // Замените ровно те сообщения, которые содержал запрос на сжатие. Последующие ходы остаются после блока.
    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" }
      });
      // В любом случае пометьте запрос как завершённый. Тогда ожидание вернёт сводку или выбросит исключение.
      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` не отличается от цикла: ответ без блока оставляет историю в прежнем виде. Поскольку ожидающих запросов больше нет, программа может затем запустить новый запрос на сжатие.

## Сохранение действительности размышлений во время создания сводки

Ходы, поступающие во время написания сводки, являются сохранёнными ходами. Если вы отправляете блоки размышлений обратно модели с [сохранёнными размышлениями](https://platform.claude.com/docs/ru/build-with-claude/preserved-thinking), размышления в этих ходах остаются действительными только до тех пор, пока выполняются [условия, при которых сохранённые размышления остаются действительными](https://platform.claude.com/docs/ru/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid).
