---
title: 온디맨드 압축
url: https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand
description: 애플리케이션이 원하는 시점에 Claude에게 대화 요약을 요청한 다음, 그 요약에서 대화를 이어갑니다.
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
---

"On-demand compaction"(온디맨드 압축)을 사용하면 대화를 요약할 시점을 애플리케이션이 결정합니다. `compaction` 매개변수를 포함한 요청 하나를 보내면, Claude는 응답 대신 요약을 반환합니다.

## 온디맨드 압축의 작동 방식

"Compaction"(압축) 요청은 대화 턴과 별개입니다. 현재 상태의 대화를 `compaction` 매개변수와 함께 보내면, 응답에는 `compaction` 블록 하나만 포함됩니다. 이 블록에는 읽을 수 있는 텍스트 형태의 요약과 "signature"(서명)가 담겨 있습니다. 이후 요청에서는 이 블록을 받은 그대로 보내세요.

그때부터 블록은 자신이 요약한 메시지를 대신합니다. 블록은 `messages`의 맨 앞에 오고, 요약된 메시지는 제거되며, 다음 턴이 그 뒤에 이어집니다. Claude는 해당 메시지가 있던 자리에서 요약을 보게 됩니다.

<Frame>
  ![On-demand compaction(온디맨드 압축): 네 개의 메시지와 compaction 매개변수를 담은 요청은 응답 없이 하나의 compaction 블록을 반환하고, 다음 요청에서는 이 블록이 해당 네 개의 메시지를 대신하여 messages의 맨 앞에 오며 그 뒤에 다음 사용자 턴이 이어집니다](https://platform.claude.com/docs/ko/images/compaction-on-demand-swap.svg)
</Frame>

## 요약 요청하기

요약을 요청하는 요청과, 서명된 블록을 담은 이후의 모든 요청에 `compact-2026-09-04` "beta header"(베타 헤더)를 보내세요. 모델이 온디맨드 압축을 지원하는지 확인하려면 베타 헤더와 함께 [Models API](https://platform.claude.com/docs/ko/api/beta/models/list)를 호출하고 각 모델의 `capabilities.compaction`을 확인하세요. 하나의 요청에서 `compaction`과 `context_management`를 함께 사용할 수 없습니다.

현재 상태의 대화를 `"compaction": {"type": "summarize"}`와 함께 보내세요. API는 요청에 포함된 모든 메시지를 한 번 요약하고, 그 뒤에 응답을 생성하지 않으며, `stop_reason` `"compaction"`과 함께 블록만 반환합니다. 대화의 나머지 부분에서 사용하는 것과 동일한 `system` 프롬프트와 `tools`를 보내세요. "Summarizer"(요약기)가 이를 읽으며, ["preserved thinking"(보존된 사고)](https://platform.claude.com/docs/ko/build-with-claude/preserved-thinking)를 사용하는 모델에서 블록 뒤의 턴을 유지하는 경우 해당 턴의 사고는 `system`과 `tools`가 일치할 때만 유효하게 유지됩니다. 이 예제의 대화에는 `system` 프롬프트나 도구가 없으므로 요청에서 둘 다 보내지 않습니다:

<CodeGroup>
  ```bash cURL
  # max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 이상으로 여유 있게 설정하세요.
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: compact-2026-09-04" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 4096,
      "messages": [
        {"role": "user", "content": "I am building a recipe app. Help me name the main entities in the data model."},
        {"role": "assistant", "content": "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."},
        {"role": "user", "content": "Good. Now suggest field names for Recipe."}
      ],
      "compaction": {"type": "summarize"}
    }'
  ```

  ```bash CLI
  ant beta:messages create --beta compact-2026-09-04 <<'YAML'
  model: claude-opus-5-5
  # max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
  max_tokens: 4096
  messages:
    - role: user
      content: I am building a recipe app. Help me name the main entities in the data model.
    - role: assistant
      content: Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.
    - role: user
      content: Good. Now suggest field names for Recipe.
  compaction:
    type: summarize
  YAML
  ```

  ```python Python
  from anthropic.types.beta import BetaMessageParam

  client = anthropic.Anthropic()

  history: list[BetaMessageParam] = [
      {
          "role": "user",
          "content": "I am building a recipe app. Help me name the main entities in the data model.",
      },
      {
          "role": "assistant",
          "content": "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.",
      },
      {"role": "user", "content": "Good. Now suggest field names for Recipe."},
  ]

  response = client.beta.messages.create(
      model="claude-opus-5-5",
      # max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
      max_tokens=4096,
      betas=["compact-2026-09-04"],
      messages=history,
      compaction={"type": "summarize"},
  )
  print(f"Stop reason: {response.stop_reason}")
  ```

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

  const history: Anthropic.Beta.Messages.BetaMessageParam[] = [
    {
      role: "user",
      content: "I am building a recipe app. Help me name the main entities in the data model."
    },
    {
      role: "assistant",
      content:
        "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."
    },
    { role: "user", content: "Good. Now suggest field names for Recipe." }
  ];

  const response = await client.beta.messages.create({
    model: "claude-opus-5-5",
    // max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
    max_tokens: 4096,
    betas: ["compact-2026-09-04"],
    messages: history,
    compaction: { type: "summarize" }
  });
  console.log(`Stop reason: ${response.stop_reason}`);
  ```

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

  AnthropicClient client = new();

  List<BetaMessageParam> history =
  [
      new()
      {
          Role = Role.User,
          Content = "I am building a recipe app. Help me name the main entities in the data model.",
      },
      new()
      {
          Role = Role.Assistant,
          Content = "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.",
      },
      new() { Role = Role.User, Content = "Good. Now suggest field names for Recipe." },
  ];

  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      // max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 이상을 허용하세요.
      MaxTokens = 4096,
      Betas = [AnthropicBeta.Compact2026_09_04],
      Messages = history,
      Compaction = new BetaCompactionConfig(), // type defaults to "summarize"
  });

  Console.WriteLine($"Stop reason: {response.StopReason?.Raw()}");
  ```

  ```go Go
  client := anthropic.NewClient()

  history := []anthropic.BetaMessageParam{
  	anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("I am building a recipe app. Help me name the main entities in the data model.")),
  	{
  		Role:    anthropic.BetaMessageParamRoleAssistant,
  		Content: []anthropic.BetaContentBlockParamUnion{anthropic.NewBetaTextBlock("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")},
  	},
  	anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Good. Now suggest field names for Recipe.")),
  }

  response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  	Model: anthropic.ModelClaudeOpus5_5,
  	// max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 이상으로 여유 있게 설정하세요.
  	MaxTokens: 4096,
  	Betas:     []anthropic.AnthropicBeta{anthropic.AnthropicBetaCompact2026_09_04},
  	Messages:  history,
  	Compaction: anthropic.BetaCompactionConfigUnionParam{
  		OfSummarize: &anthropic.BetaSummarizeCompactionParam{},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println("Stop reason:", response.StopReason)
  ```

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

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

      var params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          // max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
          .maxTokens(4096)
          .addBeta(AnthropicBeta.COMPACT_2026_09_04)
          .addUserMessage("I am building a recipe app. Help me name the main entities in the data model.")
          .addAssistantMessage("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")
          .addUserMessage("Good. Now suggest field names for Recipe.")
          .compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
          .build();

      var response = client.beta().messages().create(params);
      response.stopReason().ifPresent(reason -> IO.println("Stop reason: " + reason));
  }
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  use Anthropic\Beta\Messages\BetaCompactionConfig;
  use Anthropic\Beta\Messages\BetaMessageParam;
  use Anthropic\Beta\Messages\BetaMessageParam\Role;

  $client = new Client();

  $history = [
      BetaMessageParam::with(
          role: Role::USER,
          content: 'I am building a recipe app. Help me name the main entities in the data model.',
      ),
      BetaMessageParam::with(
          role: Role::ASSISTANT,
          content: 'Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.',
      ),
      BetaMessageParam::with(role: Role::USER, content: 'Good. Now suggest field names for Recipe.'),
  ];

  $response = $client->beta->messages->create(
      model: Model::CLAUDE_OPUS_5_5,
      // max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 이상으로 여유 있게 설정하세요.
      maxTokens: 4096,
      betas: [AnthropicBeta::COMPACT_2026_09_04],
      messages: $history,
      compaction: BetaCompactionConfig::with(), // type defaults to 'summarize'
  );

  echo "Stop reason: {$response->stopReason}", PHP_EOL;
  ```

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

  history = [
    {
      role: "user",
      content: "I am building a recipe app. Help me name the main entities in the data model."
    },
    {
      role: "assistant",
      content: "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."
    },
    { role: "user", content: "Good. Now suggest field names for Recipe." }
  ]

  response = client.beta.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    # max_tokens는 사고 과정을 포함한 호출 전체의 상한이므로 수천 토큰 정도의 여유를 두세요.
    max_tokens: 4096,
    betas: [Anthropic::AnthropicBeta::COMPACT_2026_09_04],
    messages: history,
    compaction: { type: "summarize" }
  )
  puts "Stop reason: #{response.stop_reason}"
  ```
</CodeGroup>

```json Response
{
  "id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",
  "type": "message",
  "role": "assistant",
  "model": "claude-opus-5-5",
  "content": [
    {
      "type": "compaction",
      "content": "Summary of the conversation: the user is designing the data model for a recipe app. The entities agreed so far are Recipe, Ingredient, Step, and RecipeIngredient, which holds the quantity and unit. The user then asked for field names for Recipe.",
      "signature": "EuYBCkQY..."
    }
  ],
  "stop_reason": "compaction",
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "iterations": [{ "type": "compaction", "input_tokens": 144, "output_tokens": 276 }]
  }
}
```

요약 호출은 요청의 모델, `system`, `tools`, 사고 설정, `max_tokens`를 사용합니다. 요약기는 도구 정의를 읽지만 도구를 실행하지는 않으며, 응답에는 사고가 포함되지 않습니다. `max_tokens`는 모델이 요약을 작성하기 전에 수행하는 사고를 포함하여 호출 전체의 상한이 되므로, 수천 개의 토큰을 허용하세요. 호출이 청구되는 방식은 [압축 사용량 계산하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#understanding-usage)에서 확인할 수 있습니다.

마지막 `assistant` 턴이 아직 결과가 없는 도구 호출로 끝나면 API는 요청을 거부합니다. 해당 턴의 도구 결과를 먼저 보내세요. 또한 `stop_sequences`, 구조화된 출력의 `output_config.format`, 그리고 유형이 `any` 또는 `tool`인 `tool_choice`는 제외하세요. 이들은 요약 호출에서 아무런 효과가 없으며, API가 거부합니다. 대화는 여전히 모델의 컨텍스트 윈도우에 들어가야 하므로, 한도를 넘은 후가 아니라 넘기 전에 압축하세요.

응답을 스트리밍하면 블록은 한 번에 전체가 도착합니다. 완전한 블록을 담은 `content_block_start` 이벤트 하나와 그 뒤의 `content_block_stop`을 받으며, `content_block_delta` 이벤트는 없습니다. `ping` 이벤트는 이들 앞이나 사이에 도착할 수 있습니다.

## 요약에서 이어서 진행하기

기록에서 보낸 메시지를 반환된 어시스턴트 메시지로 교체하세요. `compaction` 블록은 `signature`를 포함하여 API가 반환한 그대로 유지하세요. 압축 요청을 보낸 후에 진행된 턴은 변경 없이 블록 뒤에 이어지며, [백그라운드 압축](https://platform.claude.com/docs/ko/build-with-claude/compaction-background)은 바로 이 점을 기반으로 합니다. 이후의 모든 요청에서 베타 헤더와 함께 블록을 맨 앞에 보내세요:

```json
{
  "model": "claude-opus-5-5",
  "max_tokens": 2048,
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "compaction",
          "content": "Summary of the conversation: the user is designing the data model for a recipe app. The entities agreed so far are Recipe, Ingredient, Step, and RecipeIngredient, which holds the quantity and unit. The user then asked for field names for Recipe.",
          "signature": "EuYBCkQY..."
        }
      ]
    },
    {
      "role": "assistant",
      "content": "For Recipe, use title, description, servings, prep_minutes, and cook_minutes. Add created_at and updated_at timestamps."
    },
    { "role": "user", "content": "Now do the same for Ingredient." }
  ]
}
```

이 예제는 `user` 턴으로 끝난 요청 샘플에서 이어집니다. 다이어그램은 요약이 작성되는 동안 진행된 턴이 없는 더 단순한 경우를 보여줍니다. 여기서 두 번째 `assistant` 메시지는 마지막으로 요약된 `user` 턴에 대한 응답입니다. 이 메시지는 요약이 작성되는 동안 도착했으므로 요약된 메시지에 포함되지 않았습니다. 블록이 여전히 맨 앞에 오기 때문에 여기서는 `assistant` 메시지가 두 개 연속으로 와도 괜찮습니다.

API는 블록이 있는 자리에 요약을 넣고, 이후의 모든 메시지를 변경 없이 Claude에게 전달합니다. 다음 규칙을 따르세요:

* 블록을 `messages`의 맨 앞에 두세요. 독립된 `assistant` 메시지로 두거나, 첫 번째 메시지의 첫 번째 콘텐츠 블록으로 둘 수 있으며, 이때 첫 번째 메시지는 `user` 메시지든 `assistant` 메시지든 상관없습니다.
* 요약된 메시지를 제거하세요. 블록 앞에 남아 있는 메시지가 있으면 요청은 400 오류(`compaction_block_misplaced`)를 반환합니다.
* 이후의 모든 요청에서 요청당 정확히 하나의 `compaction` 블록을 보내세요.

<Warning>
  교체 과정에서 발생하는 두 가지 실수는 오류를 일으키지 않습니다. 요약된 메시지가 블록 뒤에 남아 있으면 API는 이를 Claude에게 다시 보냅니다. 이후 요청에서 블록을 빠뜨리면 Claude는 요약을 받지 못합니다.
</Warning>

"Threshold compaction"(임계값 압축)은 반대 방식으로 작동합니다. 임계값 압축의 블록은 요약한 메시지 뒤에 오며, API가 해당 메시지를 대신 제거합니다. [압축 블록 다시 전달하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-threshold#passing-compaction-blocks-back)를 참조하세요.

Python에서는 이 페이지의 샘플처럼 `client.beta.messages`를 사용하세요. `client.messages`를 호출하고 블록을 직접 직렬화하는 경우 `to_dict()` 또는 `model_dump(exclude_none=True)`를 사용하세요. 일반 `model_dump()`는 블록에 `citations: null`과 `text: null`을 추가하며, API는 이를 거부합니다.

블록 뒤의 턴을 유지하고 해당 턴의 사고 블록을 다시 보내는 경우, 그 사고를 유효하게 유지하는 조건은 [압축과 보존된 사고](https://platform.claude.com/docs/ko/build-with-claude/compaction-thinking-blocks)에 설명되어 있습니다.

### 다시 압축하기

이미 블록으로 시작하는 대화를 압축하려면 `compaction`을 다시 보내세요. 새 블록은 이전 요약과 그 이후의 모든 내용을 요약합니다. 그때부터는 가장 최신 블록만 보내세요.

## 루프에서 압축하기

다음 요청에는 응답도 함께 포함되므로, 루프는 각 턴이 끝난 후 마지막 응답의 입력 토큰과 출력 토큰을 더합니다. 그 합계가 한도를 넘고 아직 진행할 턴이 남아 있으면, 루프는 동일한 모델과 `system` 프롬프트로 압축 요청을 보내고, `stop_reason`을 확인하고, 기록을 반환된 메시지로 교체한 다음, 압축 직전의 턴을 출력합니다. 샘플의 한도인 2,500 토큰은 짧은 대화도 압축되도록 의도적으로 낮게 설정한 값입니다. 실제 입력 예산에 가깝게 설정하세요.

<CodeGroup exclude="shell">
  ```python Python
  from anthropic.types.beta import BetaMessageParam

  client = anthropic.Anthropic()

  # 실제 입력 예산에 가깝게 설정하세요. 여기서는 짧은 대화도 압축되도록 낮게 설정했습니다.
  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?",
  ]

  history: list[BetaMessageParam] = []
  for turn, question in enumerate(QUESTIONS, start=1):
      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):
          summary = client.beta.messages.create(
              model="claude-opus-5-5",
              max_tokens=4096,
              system=SYSTEM,
              betas=["compact-2026-09-04"],
              messages=history,
              compaction={"type": "summarize"},
          )
          if summary.stop_reason == "compaction":
              history = [{"role": "assistant", "content": summary.content}]
              print(f"Compacted before turn {turn + 1}")
  ```

  ```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?"
  ];

  let history: Anthropic.Beta.Messages.BetaMessageParam[] = [];
  for (const [index, question] of questions.entries()) {
    const turn = index + 1;
    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) {
      const summary = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        system: systemPrompt,
        betas: ["compact-2026-09-04"],
        messages: history,
        compaction: { type: "summarize" }
      });
      if (summary.stop_reason === "compaction") {
        history = [{ role: "assistant", content: summary.content }];
        console.log(`Compacted before turn ${turn + 1}`);
      }
    }
  }
  ```

  ```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?",
  ];

  List<BetaMessageParam> history = [];
  foreach (var (index, question) in questions.Index())
  {
      var turn = index + 1;
      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)
      {
          var summary = await 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 (summary.StopReason == BetaStopReason.Compaction)
          {
              history =
              [
                  new()
                  {
                      Role = Role.Assistant,
                      Content = summary.Content.Select(block => new BetaContentBlockParam(block.Json)).ToList(),
                  },
              ];
              Console.WriteLine($"Compacted before turn {turn + 1}");
          }
      }
  }
  ```

  ```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
  for i, question := range questions {
  	turn := i + 1
  	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) {
  		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:  history,
  			Compaction: anthropic.BetaCompactionConfigUnionParam{
  				OfSummarize: &anthropic.BetaSummarizeCompactionParam{},
  			},
  		})
  		if err != nil {
  			log.Fatal(err)
  		}
  		if summary.StopReason == anthropic.BetaStopReasonCompaction {
  			history = []anthropic.BetaMessageParam{summary.ToParam()}
  			fmt.Printf("Compacted before turn %d\n", turn+1)
  		}
  	}
  }
  ```

  ```java Java
  import com.anthropic.models.beta.AnthropicBeta;
  import com.anthropic.models.beta.messages.BetaCompactionConfig;
  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 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>();
      for (int turn = 1; turn <= questions.size(); turn++) {
          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()) {
              var summaryParams = MessageCreateParams.builder()
                  .model(Model.CLAUDE_OPUS_5_5)
                  .maxTokens(4096)
                  .system(SYSTEM)
                  .addBeta(AnthropicBeta.COMPACT_2026_09_04)
                  .messages(history)
                  .compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
                  .build();
              var summary = client.beta().messages().create(summaryParams);
              if (summary.stopReason().map(BetaStopReason.COMPACTION::equals).orElse(false)) {
                  history.clear();
                  history.add(summary.toParam());
                  IO.println("Compacted before turn " + (turn + 1));
              }
          }
      }
  }
  ```

  ```php PHP
  use Anthropic\Beta\AnthropicBeta;
  use Anthropic\Beta\Messages\BetaCompactionConfig;
  use Anthropic\Beta\Messages\BetaMessageParam;
  use Anthropic\Beta\Messages\BetaMessageParam\Role;
  use Anthropic\Beta\Messages\BetaStopReason;

  $client = new Client();

  // 실제 입력 예산에 가깝게 설정하세요. 여기서는 짧은 대화에서도 압축이 일어나도록 낮게 설정했습니다.
  const COMPACT_AT_TOKENS = 2500;
  const 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?',
  ];

  $history = [];
  foreach ($questions as $index => $question) {
      $turn = $index + 1;
      $history[] = BetaMessageParam::with(role: Role::USER, content: $question);
      $response = $client->beta->messages->create(
          model: Model::CLAUDE_OPUS_5_5,
          maxTokens: 8192,
          system: SYSTEM,
          betas: [AnthropicBeta::COMPACT_2026_09_04],
          messages: $history,
      );
      $history[] = BetaMessageParam::with(role: Role::ASSISTANT, content: $response->content);

      // 다음 요청에서 이 응답도 함께 전송되므로 이를 포함해 계산하세요.
      $conversationTokens = $response->usage->inputTokens + $response->usage->outputTokens;
      if ($conversationTokens > COMPACT_AT_TOKENS && $turn < count($questions)) {
          $summary = $client->beta->messages->create(
              model: Model::CLAUDE_OPUS_5_5,
              maxTokens: 4096,
              system: SYSTEM,
              betas: [AnthropicBeta::COMPACT_2026_09_04],
              messages: $history,
              compaction: new BetaCompactionConfig(), // type defaults to 'summarize'
          );
          if ($summary->stopReason === BetaStopReason::COMPACTION->value) {
              $history = [BetaMessageParam::with(role: Role::ASSISTANT, content: $summary->content)];
              printf("Compacted before turn %d\n", $turn + 1);
          }
      }
  }
  ```

  ```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?"
  ]

  history = []
  questions.each.with_index(1) do |question, turn|
    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
      summary = client.beta.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 4096,
        system_: SYSTEM,
        betas: [Anthropic::AnthropicBeta::COMPACT_2026_09_04],
        messages: history,
        compaction: { type: "summarize" }
      )
      if summary.stop_reason == :compaction
        history = [{ role: "assistant", content: summary.content }]
        puts "Compacted before turn #{turn + 1}"
      end
    end
  end
  ```
</CodeGroup>

코드는 블록을 찾기 전에 `stop_reason`을 먼저 확인합니다. 그 이유는 [요약 누락 또는 오류 처리하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#when-no-summary-comes-back)에서 설명합니다. 기록은 추가되는 것이 아니라 교체됩니다. 반환된 메시지는 [요약에서 이어서 진행하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#continue-from-the-summary)의 규칙에 따라 요청에 포함된 모든 메시지를 대체합니다. 요약이 반환되지 않으면 루프는 기록을 그대로 유지하고 다음 턴 이후에 다시 요청합니다.

Python, TypeScript, C#, Go, Java, PHP, Ruby의 SDK ["tool runner"(도구 러너)](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-runner)가 압축 요청을 대신 보낼 수 있습니다. 압축하기로 결정하면 러너에서 `compact_before_next_turn()` (typescript, java, php: `compactBeforeNextTurn()`; csharp, go: `CompactBeforeNextTurn()`)를 호출하세요. 현재 턴과 그 도구 호출이 끝나면 러너는 압축 요청을 보내고 기록을 반환된 메시지로 교체합니다. 러너는 `compact-2026-09-04` 베타를 추가하지 않으므로, 러너를 생성할 때 이 베타를 지정하세요.

러너는 자체 매개변수로 압축 요청을 구성하며 `context_management`는 제외합니다. 또한 API가 압축 요청에서 거부하는 `stop_sequences`, 유형이 `any` 또는 `tool`인 `tool_choice`, 구조화된 출력의 `output_config.format`도 제외합니다. 그 이유는 [요약 요청하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#request-a-summary)에서 설명합니다. 러너는 이후 요청에서 이들을 다시 보냅니다. Python 1.8.0, TypeScript 0.128.0, C# 12.50.0, Go 1.75.0, Java 2.65.0 이전의 SDK 버전은 압축 요청에도 이들을 보냅니다. 해당 버전에서 이러한 매개변수 중 하나라도 설정한 러너는 400 오류를 받습니다. 러너는 ["task budget"(작업 예산)](https://platform.claude.com/docs/ko/build-with-claude/task-budgets)을 변경 없이 보냅니다. `output_config.task_budget`에 `remaining`이 설정되어 있으면 압축 요청이 400 오류를 반환하므로, [제한 사항 및 다른 기능과의 상호작용](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#how-it-fits-with-the-rest-of-the-api)에서 설명하는 것처럼 `remaining`을 설정하지 마세요. 러너의 `context_management`에 압축 편집이 있으면 러너는 압축을 거부하므로, 하나의 러너에서는 한 종류의 압축만 사용하세요.

### 압축 시점

완료된 턴 이후라면 언제든 압축 요청을 보낼 수 있으므로, 시점은 코드에서 결정합니다.

다음 요청의 크기를 추정하려면 루프에서처럼 마지막 응답의 `usage`에서 `input_tokens`와 `output_tokens`를 더하세요. [프롬프트 캐싱](https://platform.claude.com/docs/ko/build-with-claude/prompt-caching#tracking-cache-performance)을 사용하는 경우 `input_tokens`는 마지막 캐시 중단점 이후의 토큰만 계산하므로 `cache_read_input_tokens`와 `cache_creation_input_tokens`도 더하세요. 동일한 메시지를 [토큰 카운팅](https://platform.claude.com/docs/ko/build-with-claude/token-counting) 엔드포인트로 보낼 수도 있습니다.

그 숫자를 모델의 [컨텍스트 윈도우](https://platform.claude.com/docs/ko/build-with-claude/context-windows)보다 낮게 직접 정한 한도와 비교하세요.

## 자체 요약 프롬프트 작성하기

`instructions`가 없으면 API는 자체 요약 프롬프트를 사용합니다. 비어 있지 않은 `instructions` 문자열(최대 16,384자)은 해당 프롬프트를 완전히 대체합니다. 예를 들면 다음과 같습니다:

```json
{
  "compaction": {
    "type": "summarize",
    "instructions": "Summarize this recipe app design conversation. Preserve every entity and field name agreed so far, and the user's latest open request. Do not call tools; respond with the summary text only."
  }
}
```

요약기는 `instructions` 유무와 관계없이 이전 사고를 포함한 전체 대화를 읽습니다. `instructions`에는 요약에 반드시 남겨야 할 내용을 명시하고, 모델에게 도구를 호출하지 말라고 지시하세요. 요약 호출은 다른 모든 요청과 동일한 안전장치 하에서 실행됩니다.

## 요약 누락 또는 오류 처리하기

요약은 요약 호출이 도구 호출 없이 텍스트와 함께 정상적으로 종료될 때만 생성됩니다. 그렇지 않은 경우에도 응답은 빈 `content`와 함께 200으로 반환되므로, 블록을 찾기 전에 `stop_reason`을 확인하세요. 이 호출도 청구되며 `usage.iterations`에 보고되고, 호출 자체를 수행할 수 없었던 경우에는 사용량이 0으로 보고됩니다. `stop_reason`은 요약 호출이 종료된 사유입니다. 어떤 경우든 요약 없이 계속 진행하고 나중에 압축할 수 있습니다.

| `stop_reason`                     | 원인                          | 조치 방법                                              |
| --------------------------------- | --------------------------- | -------------------------------------------------- |
| `"max_tokens"`                    | 요약이 중간에 잘렸습니다.              | 더 큰 `max_tokens`로 다시 보내세요.                         |
| `"model_context_window_exceeded"` | 요약 프롬프트를 넣을 공간이 없었습니다.      | 더 짧은 `instructions` 또는 더 적은 메시지로 다시 보내세요.          |
| `"tool_use"`                      | 모델이 요약을 작성하는 대신 도구를 호출했습니다. | 모델에게 도구를 호출하지 말라고 지시하는 `instructions`와 함께 다시 보내세요. |
| `"refusal"`                       | 요청이 거절되었습니다.                | 요약 없이 계속 진행하세요.                                    |
| `"end_turn"`                      | 호출이 텍스트를 반환하지 않았습니다.        | 요약 없이 계속 진행하세요.                                    |

요약 호출에는 다른 요청과 동일한 안전장치가 적용됩니다. `"refusal"` 이후에는 [`stop_details`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#refusal)에서 거절의 원인이 된 정책 범주를 확인할 수 있습니다.

### 오류

압축 요청이나 블록을 담은 요청이 아예 실패할 수도 있습니다. 대부분의 400 오류에는 무엇을 제거하거나 다시 보내야 하는지 알려주는 메시지가 포함됩니다. 일부 오류에는 `compaction_`으로 시작하는 `error.details.error_code`도 포함됩니다. `compaction`과 함께 사용할 수 없는 필드와 같은 매개변수 오류에는 메시지만 포함됩니다.

| 오류                                                                                              | 원인                                                            | 조치 방법                                                                                                                                           |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| 529 `overloaded_error`, `error.details.error_code` `compaction_unavailable`                     | 블록을 생성하는 중이거나 다시 보낸 블록을 읽는 중에 일시적인 서버 문제가 발생했습니다.             | 요청을 재시도하세요.                                                                                                                                     |
| 400 `compaction_block_misplaced`                                                                | 요약된 메시지가 블록 앞에 남아 있습니다.                                       | 해당 메시지를 제거하여 블록이 `messages`의 맨 앞에 오도록 하세요.                                                                                                      |
| 400 `compaction_signature_invalid` 또는 `compaction_content_mismatch`                             | API가 반환한 후 블록의 `signature` 또는 `content`가 변경되었습니다.             | `signature`를 포함하여 블록을 반환된 그대로 보내세요.                                                                                                             |
| 400                                                                                             | 요청에 `compaction` 블록이 두 개 이상 포함되어 있습니다.                        | 가장 최신 블록 하나만 보내세요.                                                                                                                              |
| 400                                                                                             | 마지막 `assistant` 턴이 아직 결과가 없는 도구 호출로 끝납니다.                     | 해당 턴의 도구 결과를 보낸 다음 압축하세요.                                                                                                                       |
| 400 `compaction_nothing_to_summarize`                                                           | `messages`에 `user` 또는 `assistant` 콘텐츠가 없습니다(예: 빈 목록).         | `user` 또는 `assistant` 메시지를 하나 이상 보내세요.                                                                                                          |
| 압축 요청에서 발생하는 400으로, `compaction` 매개변수가 `requires anthropic-beta: compact-2026-09-04`라는 메시지가 포함됨 | 압축 요청에 베타 헤더가 누락되었습니다.                                        | 베타 헤더를 추가하세요. [요약 요청하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#request-a-summary)를 참조하세요.                   |
| 블록을 담은 이후 요청에서 발생하는 400: `compaction`이 예상되는 콘텐츠 블록 유형이 아니라는 유효성 검사 오류. 메시지에 헤더에 대한 언급은 없음       | 해당 요청에 베타 헤더가 누락되었습니다.                                        | 블록을 담은 모든 요청에 베타 헤더를 추가하세요. [요약 요청하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#request-a-summary)를 참조하세요.     |
| `messages.0.content.0.compaction.citations: Extra inputs are not permitted`와 같은 400 유효성 검사 오류   | API가 반환하지 않은 필드(예: `citations: null`)가 포함된 채로 블록이 다시 전송되었습니다. | 블록을 반환된 그대로 보내세요. [요약에서 이어서 진행하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#continue-from-the-summary)를 참조하세요. |

## 압축 사용량 계산하기

요약 호출은 다른 요청과 마찬가지로 청구되고 속도 제한이 적용되며, `usage.iterations`에서 `compaction` 항목으로 보고됩니다. 응답이 생성되지 않았으므로 최상위 `input_tokens`와 `output_tokens`는 0입니다. 대화에서 소비한 양을 계산하려면 최상위 필드가 아니라 `usage.iterations` 전체를 합산하세요. 이후 요청에서 블록을 다시 보내는 데에는 추가 압축 비용이 발생하지 않습니다.

이제 대화를 압축하고 요약 누락을 처리하는 작동하는 루프가 완성되었습니다. 다음 두 페이지는 루프의 실행 방식을 바꾸며, 둘을 함께 사용할 수도 있습니다. [최근 턴을 유지하는 압축](https://platform.claude.com/docs/ko/build-with-claude/compaction-keep-recent-turns)은 마지막 턴들을 원문 그대로 유지하고, [백그라운드 압축](https://platform.claude.com/docs/ko/build-with-claude/compaction-background)은 요약이 작성되는 동안에도 대화를 계속 진행할 수 있게 합니다. 사고 블록을 다시 보내면서 이 둘 중 하나를 사용하는 경우에는 [압축과 보존된 사고](https://platform.claude.com/docs/ko/build-with-claude/compaction-thinking-blocks)가 적용됩니다.

## 제한 사항 및 다른 기능과의 상호작용

* **임계값 압축 및 컨텍스트 편집.** 동일한 요청에서 `compaction`과 `context_management`를 함께 보낼 수 없습니다. 임계값 압축(`compact_20260112`)은 서명된 블록을 담은 요청에서는 실행할 수 없습니다.
* **프롬프트 캐싱.** 블록에 `cache_control`을 지정하면 요약 뒤에 중단점이 설정됩니다.
* **대화 중간의 시스템 메시지 및 도구 변경.** 요약 범위 안에 있는 `role: "system"` 메시지도 함께 요약되므로, 블록이 이를 대체하면 해당 텍스트 지시는 더 이상 적용되지 않습니다. 이 메시지들이 설정한 [메시지별 effort](https://platform.claude.com/docs/ko/build-with-claude/effort#change-effort-mid-conversation-beta) 수준도 이어지지 않습니다. 이후 메시지가 수준을 설정할 때까지 턴은 요청의 최상위 `output_config.effort`로 실행되며, 이를 설정하지 않은 경우 모델의 기본값으로 실행됩니다. 지시나 effort 수준이 여전히 중요하다면 `role: "system"` 메시지에서 다시 명시하세요. 그 메시지는 다음 새 `user` 턴 바로 뒤에 보내고, 그 이후로는 기록에 남겨 두세요. [도구 변경](https://platform.claude.com/docs/ko/build-with-claude/mid-conversation-system-messages#mid-conversation-tool-changes)과, 블록 뒤에 턴을 유지할 때 해당 메시지를 어디에 두어야 하는지는 [시스템 프롬프트 또는 도구 변경](https://platform.claude.com/docs/ko/build-with-claude/compaction-thinking-blocks#change-the-system-prompt-or-tools)을 참조하세요.
* **작업 예산.** ["task budget"(작업 예산)](https://platform.claude.com/docs/ko/build-with-claude/task-budgets)의 `remaining` 값(`output_config.task_budget.remaining`)을 `compaction`과 함께 보내거나 블록을 담은 요청에서 보내지 마세요. 그렇게 하면 400 오류가 반환됩니다.
* **토큰 카운팅.** [토큰 카운팅](https://platform.claude.com/docs/ko/build-with-claude/token-counting) 엔드포인트는 `compaction` 매개변수를 무시합니다.
* **요약에 담을 수 없는 콘텐츠.** 요약된 메시지 안의 이미지, 문서, `container_upload` 블록, 가져온 URL은 블록이 해당 메시지를 대체하면 사라집니다. 이후 턴에서 여전히 필요한 내용은 다시 명시하거나 다시 업로드하세요.
