---
title: Compaction en arrière-plan
url: https://platform.claude.com/docs/fr/build-with-claude/compaction-background
description: Demandez un résumé de compactage à la demande pendant que la conversation se poursuit sur son historique complet, puis insérez le bloc à sa place lorsqu'il arrive.
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
---

La « background compaction » (compaction en arrière-plan), souvent appelée « async compaction » (compaction asynchrone), modifie deux choses dans la [boucle de compaction](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#compact-in-a-loop) : la requête de compaction s'exécute pendant que la conversation se poursuit sur son historique complet, et le remplacement attend l'arrivée du bloc. Les sections [Poursuivre à partir du résumé](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#continue-from-the-summary) et [Gérer un résumé manquant ou une erreur](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#when-no-summary-comes-back) s'appliquent sans changement.

## Fonctionnement du remplacement pendant que le travail se poursuit

La requête de compaction et le bloc qu'elle renvoie sont les mêmes que dans la boucle. Votre historique s'allonge entre l'envoi de la requête et l'utilisation de son résultat, et le remplacement doit laisser ces ajouts en place.

1. Envoyez la [requête de compaction](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#request-a-summary) avec votre historique tel qu'il est, et notez le nombre de messages qu'il contenait.
2. Pendant que cette requête s'exécute, poursuivez la conversation sur l'historique complet. Ajoutez chaque nouveau tour, ne modifiez rien de ce qui se trouve déjà dans l'historique, et ne lancez pas d'autre requête de compaction tant que celle-ci n'a pas été intégrée ou n'a pas échoué.
3. Lorsque la réponse arrive avec le `stop_reason` `"compaction"`, retirez exactement les messages que vous avez envoyés du début de votre historique et placez le message renvoyé à leur place. Chaque tour ajouté depuis l'étape 1 reste après lui.
4. Envoyez l'historique ainsi remplacé dans la première requête qui suit l'arrivée du bloc, afin que la réflexion produite pendant la rédaction du résumé reste valide.

Par exemple, si la requête de compaction contenait les messages 1 à 5 et que la conversation a gagné les messages 6 à 8 pendant son exécution, après le remplacement votre historique se compose du bloc suivi des messages 6 à 8.

<Frame>
  ![Chronologie de la « background compaction » (compaction en arrière-plan) : la « compaction request » (requête de compaction) est envoyée avec les messages 1 à 5 pendant que la conversation se poursuit sur son historique complet et gagne les messages 6 à 8 ; lorsque le bloc arrive, il remplace les messages 1 à 5 au début de l'historique, et l'historique devient le bloc suivi des messages 6 à 8](https://platform.claude.com/docs/fr/images/compaction-background-timeline.svg)
</Frame>

Si la réponse a un autre `stop_reason`, aucun résumé n'a été produit, ce qui compte comme un échec à l'étape 2. Conservez l'historique complet ; la section [Gérer un résumé manquant ou une erreur](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#when-no-summary-comes-back) répertorie les causes et la marche à suivre pour chacune.

## Demander le résumé en arrière-plan

La requête de compaction est décomptée de vos « rate limits » (limites de débit) comme toute autre requête, et pendant son exécution votre application a deux requêtes ouvertes en même temps. La conversation continue de s'allonger sur son historique complet jusqu'au remplacement ; lancez donc la requête de compaction tant que la « context window » (fenêtre de contexte) a encore de la place pour les tours qui arrivent entre-temps.

Le programme suivant reprend la boucle de [Compacter dans une boucle](https://platform.claude.com/docs/fr/build-with-claude/compaction-on-demand#compact-in-a-loop), la requête de compaction étant retirée du chemin de la conversation. Les lignes mises en évidence indiquent où il diffère de la boucle, et la liste qui suit les présente dans l'ordre où le programme les exécute.

<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)

  # Réglez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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":
          # Remplacez exactement les messages contenus dans la requête de compaction.
          # Les tours suivants restent après le bloc.
          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})

      # La requête suivante envoie aussi cette réponse, comptez-la donc.
      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"},
          )

  # Insérez un résumé encore en cours de génération avant d'enregistrer
  # ou de poursuivre la conversation.
  if pending is not None:
      swap_in(history, pending.result(), sent)
  executor.shutdown()
  ```

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

  // Définissez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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`);
    // Remplacez exactement les messages contenus dans la requête de compaction. Les tours suivants restent après le bloc.
    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 });

    // La requête suivante envoie aussi cette réponse, comptez-la donc.
    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" }
      });
      // Marquez la requête comme réglée dans tous les cas. L'attendre renvoie alors le résumé ou lève une exception.
      const markSettled = () => {
        settled = true;
      };
      pending.then(markSettled, markSettled);
    }
  }

  // Substituez un résumé encore en cours d'arrivée avant d'enregistrer ou de poursuivre la conversation.
  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();

  // Définissez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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");
      // Remplacez exactement les messages contenus dans la requête de compaction. Les tours suivants restent après le bloc.
      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(),
      });

      // La requête suivante envoie aussi cette réponse, comptez-la donc.
      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"
          });
      }
  }

  // Substituez un résumé encore en cours d'arrivée avant d'enregistrer ou de poursuivre la conversation.
  if (pending is not null)
  {
      history = SwapIn(history, await pending, sent);
  }
  ```

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

  // Définissez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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)
  	// Remplacez exactement les messages contenus dans la requête de compaction. Les tours suivants restent après le bloc.
  	history = slices.Replace(history, 0, sent, summary.ToParam())
  }

  for i, question := range questions {
  	turn := i + 1
  	// La réception depuis un canal nil n'aboutit jamais, donc ceci est ignoré quand rien n'est en attente.
  	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())

  	// La requête suivante envoie aussi cette réponse, donc comptez-la.
  	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)
  	}
  }

  // Insérez un résumé encore en cours d'arrivée avant d'enregistrer ou de poursuivre la conversation.
  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;

  // Réglez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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");
      // Remplacez exactement les messages contenus dans la requête de compaction. Les tours suivants restent après le bloc.
      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());

          // La requête suivante envoie aussi cette réponse, comptez-la donc.
          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);
          }
      }

      // Substituez un résumé encore en cours de réception avant d'enregistrer ou de poursuivre la conversation.
      if (pending != null) {
          swapIn(history, pending.join(), sent);
      }
      client.close();
  }
  ```

  <CodeGroupItem language="PHP">
    Le SDK PHP envoie les requêtes de manière synchrone ; il ne peut donc pas exécuter la requête de compaction en arrière-plan pendant que la conversation se poursuit.
  </CodeGroupItem>

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

  # Réglez cette valeur près de votre budget d'entrée réel. Elle est basse ici pour qu'une courte conversation soit compactée.
  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"
    # Remplacez exactement les messages contenus dans la requête de compaction. Les tours suivants restent après le bloc.
    [{ 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 }

    # La requête suivante envoie aussi cette réponse, comptez-la donc.
    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

  # Insérez un résumé encore en cours d'arrivée avant d'enregistrer ou de poursuivre la conversation.
  history = swap_in(history, pending.value, sent) if pending
  ```
</CodeGroup>

* **Décider quand compacter :** la vérification de taille exige également qu'aucune requête de compaction ne soit en attente.
* **Lancer la requête :** là où la boucle attend la réponse de compaction, cette version note le nombre de messages que contient l'historique, lance la requête sur une copie de l'historique avec l'outil de concurrence propre à chaque langage, et passe au tour suivant sans attendre.
* **Vérifier le résultat :** au début de chaque tour, le programme vérifie si la requête en attente est terminée. Si c'est le cas, le programme effectue le remplacement avant d'envoyer la requête de ce tour.
* **Effectuer le remplacement :** là où la boucle remplace tout l'historique par le message renvoyé, la fonction de remplacement de cette version ne remplace que les messages que contenait la requête, comptés depuis le début, et conserve tout ce qui a été ajouté depuis.
* **Terminer la boucle :** si la requête de compaction est toujours en attente à la fin de la boucle, le programme l'attend et effectue le remplacement, afin qu'un résumé encore en cours d'acheminement ne soit pas perdu avant que vous n'enregistriez ou ne poursuiviez la conversation.

La vérification du `stop_reason` est identique à celle de la boucle : une réponse sans bloc laisse l'historique tel qu'il était. Comme plus rien n'est en attente, le programme peut alors lancer une nouvelle requête de compaction.

## Garder la réflexion valide pendant la construction du résumé

Les tours qui arrivent pendant la rédaction du résumé sont des tours conservés. Si vous renvoyez des « thinking blocks » (blocs de réflexion) sur un modèle avec [réflexion préservée](https://platform.claude.com/docs/fr/build-with-claude/preserved-thinking), la réflexion de ces tours ne reste valide que tant que les [conditions de validité de la réflexion conservée](https://platform.claude.com/docs/fr/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid) sont remplies.
