---
title: 최근 턴을 유지하는 압축
url: https://platform.claude.com/docs/ko/build-with-claude/compaction-keep-recent-turns
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
---

"Keep-tail compaction"(최근 턴 유지 압축)은 대화의 마지막 몇 턴을 요약 뒤에 원문 그대로 유지합니다. 이 방식은 [압축 루프](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#compact-in-a-loop)에서 두 가지를 변경합니다. 하나는 압축 요청에 어떤 메시지가 들어가는지이고, 다른 하나는 블록 뒤에 무엇을 전송하는지입니다. [요약에서 이어서 진행하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#continue-from-the-summary)의 모든 내용은 변경 없이 그대로 적용됩니다.

## 유지할 턴 선택하기

어떤 턴을 유지할지 설정하는 매개변수는 없습니다. 기록에서 "cut point"(분할 지점)를 직접 선택합니다. 그 이전의 메시지는 압축 요청에 들어가고, 그 지점부터의 메시지는 유지됩니다.

유지된 턴은 전체 길이 그대로 Claude에게 다시 전달되므로, 더 많이 유지할수록 압축으로 확보되는 공간은 줄어듭니다.

열린 채로 남는 도구 호출이 없도록, 각 도구 호출과 그 결과가 같은 쪽에 오는 위치에서 분할하세요. 전송하는 메시지가 아직 결과가 없는 도구 호출을 포함한 `assistant` 턴으로 끝나면, API는 압축 요청을 거부합니다.

## 이전 턴을 압축하고 나머지는 블록 뒤에 전송하기

최근 턴의 꼬리를 원문 그대로 유지하려면 해당 턴을 압축 요청에서 제외하세요. API는 전송된 모든 메시지를 요약하므로, 이전 턴만 전송한 다음 유지한 턴 앞에 블록을 배치하세요.

유지한 턴은 "thinking block"(사고 블록)을 포함하여 기록에 있는 그대로 정확히 전송하세요. [요약 요청하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#request-a-summary)에서와 같이 두 요청 모두 베타 헤더를 포함합니다.

다음 예시에서 기록에는 두 개의 턴이 있으며, 분할 지점은 두 번째 턴을 유지합니다. 압축 요청에는 첫 번째 턴이 포함됩니다:

```json
{
  "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."
    }
  ],
  "compaction": { "type": "summarize" }
}
```

다음 요청은 반환된 블록을 먼저 전송하고, 그다음 유지한 턴을 원래 그대로 전송한 뒤, 새 `user` 메시지를 전송합니다. [요약에서 이어서 진행하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#continue-from-the-summary)에서 블록으로 시작하는 요청을 확인할 수 있습니다.

다음 프로그램은 [루프에서 압축하기](https://platform.claude.com/docs/ko/build-with-claude/compaction-on-demand#compact-in-a-loop)의 루프를 마지막 두 턴을 유지하도록 변경한 것입니다. 강조 표시된 줄은 기존 루프와 다른 부분을 보여줍니다.

<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."
  KEEP_TURNS = 2

  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 KEEP_TURNS < turn < len(QUESTIONS):
          # 하나의 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로,
          # 유지되는 턴은 사용자 메시지로 시작합니다.
          split = -2 * KEEP_TURNS
          older, recent = history[:split], history[split:]
          summary = client.beta.messages.create(
              model="claude-opus-5-5",
              max_tokens=4096,
              system=SYSTEM,
              betas=["compact-2026-09-04"],
              messages=older,
              compaction={"type": "summarize"},
          )
          if summary.stop_reason == "compaction":
              history = [{"role": "assistant", "content": summary.content}, *recent]
              print(f"Kept {len(recent) // 2} turns after the block")
  ```

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

  // 실제 입력 예산에 가깝게 설정하세요. 여기서는 짧은 대화에서도 압축이 일어나도록 낮게 설정했습니다.
  const compactAtTokens = 2500;
  const systemPrompt = "You help design a recipe app's data model. Keep answers short.";
  const keepTurns = 2;

  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 > keepTurns && turn < questions.length) {
      // 하나의 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
      const older = history.slice(0, -2 * keepTurns);
      const recent = history.slice(-2 * keepTurns);
      const summary = await client.beta.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        system: systemPrompt,
        betas: ["compact-2026-09-04"],
        messages: older,
        compaction: { type: "summarize" }
      });
      if (summary.stop_reason === "compaction") {
        history = [{ role: "assistant", content: summary.content }, ...recent];
        console.log(`Kept ${recent.length / 2} turns after the block`);
      }
    }
  }
  ```

  ```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.";
  const int KeepTurns = 2;

  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 > KeepTurns && turn < questions.Length)
      {
          // 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
          var older = history[..^(2 * KeepTurns)];
          var recent = history[^(2 * KeepTurns)..];
          var summary = await client.Beta.Messages.Create(new MessageCreateParams
          {
              Model = Model.ClaudeOpus5_5,
              MaxTokens = 4096,
              System = SystemPrompt,
              Betas = [AnthropicBeta.Compact2026_09_04],
              Messages = older,
              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(),
                  },
                  .. recent,
              ];
              Console.WriteLine($"Kept {recent.Count / 2} turns after the block");
          }
      }
  }
  ```

  ```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."}}
  const keepTurns = 2

  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 > keepTurns && turn < len(questions) {
  		// 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
  		split := len(history) - 2*keepTurns
  		older, recent := history[:split], history[split:]
  		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:  older,
  			Compaction: anthropic.BetaCompactionConfigUnionParam{
  				OfSummarize: &anthropic.BetaSummarizeCompactionParam{},
  			},
  		})
  		if err != nil {
  			log.Fatal(err)
  		}
  		if summary.StopReason == anthropic.BetaStopReasonCompaction {
  			history = slices.Replace(history, 0, split, summary.ToParam())
  			fmt.Printf("Kept %d turns after the block\n", len(recent)/2)
  		}
  	}
  }
  ```

  ```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.";
  static final int KEEP_TURNS = 2;

  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 > KEEP_TURNS && turn < questions.size()) {
              // 하나의 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
              var older = history.subList(0, history.size() - 2 * KEEP_TURNS);
              var summaryParams = MessageCreateParams.builder()
                  .model(Model.CLAUDE_OPUS_5_5)
                  .maxTokens(4096)
                  .system(SYSTEM)
                  .addBeta(AnthropicBeta.COMPACT_2026_09_04)
                  .messages(older)
                  .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)) {
                  older.clear();
                  history.addFirst(summary.toParam());
                  IO.println("Kept " + (history.size() - 1) / 2 + " turns after the block");
              }
          }
      }
  }
  ```

  ```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.";
  const KEEP_TURNS = 2;

  $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 > KEEP_TURNS && $turn < count($questions)) {
          // 한 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
          $older = array_slice($history, 0, -2 * KEEP_TURNS);
          $recent = array_slice($history, -2 * KEEP_TURNS);
          $summary = $client->beta->messages->create(
              model: Model::CLAUDE_OPUS_5_5,
              maxTokens: 4096,
              system: SYSTEM,
              betas: [AnthropicBeta::COMPACT_2026_09_04],
              messages: $older,
              compaction: new BetaCompactionConfig(), // type defaults to 'summarize'
          );
          if ($summary->stopReason === BetaStopReason::COMPACTION->value) {
              $history = [BetaMessageParam::with(role: Role::ASSISTANT, content: $summary->content), ...$recent];
              printf("Kept %d turns after the block\n", intdiv(count($recent), 2));
          }
      }
  }
  ```

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

  # 실제 입력 예산에 가깝게 설정하세요. 여기서는 짧은 대화에서도 압축이 일어나도록 낮게 설정했습니다.
  COMPACT_AT_TOKENS = 2500
  SYSTEM = "You help design a recipe app's data model. Keep answers short."
  KEEP_TURNS = 2

  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 > KEEP_TURNS && turn < questions.length
      # 하나의 턴은 사용자 메시지 하나와 어시스턴트 응답 하나로 구성되므로, 유지되는 턴은 사용자 메시지로 시작합니다.
      older, recent = history[...-2 * KEEP_TURNS], history.last(2 * KEEP_TURNS)
      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: older,
        compaction: { type: "summarize" }
      )
      if summary.stop_reason == :compaction
        history = [{ role: "assistant", content: summary.content }, *recent]
        puts "Kept #{recent.length / 2} turns after the block"
      end
    end
  end
  ```
</CodeGroup>

* **분할 지점 선택:** 프로그램은 마지막 두 턴을 유지하며, 여기서 턴은 하나의 `user` 메시지와 그에 대한 응답을 의미합니다. 기록을 끝에서 네 번째 메시지 위치에서 분할하므로, 유지된 턴은 `user` 메시지로 시작합니다.
* **압축 시점 결정:** 크기 검사는 대화의 턴 수가 프로그램이 유지하는 턴 수보다 많을 것도 요구하므로, 이전 부분이 비어 있는 경우는 없습니다.
* **압축 요청:** 기존 루프는 전체 기록을 전송하지만, 이 버전은 이전 메시지만 전송합니다.
* **교체:** 기존 루프는 전체 기록을 반환된 메시지로 교체하지만, 이 버전의 새 기록은 반환된 메시지 뒤에 유지된 턴이 이어지는 형태입니다.

`stop_reason` 검사와 교체 이후의 모든 요청은 기존 루프와 동일합니다.

## 유지된 턴에서 사고 블록을 유효하게 유지하기

[보존된 사고](https://platform.claude.com/docs/ko/build-with-claude/preserved-thinking)를 지원하는 모델에서 사고 블록을 다시 전송하는 경우, 유지된 턴의 사고 내용은 [유지된 사고가 유효하기 위한 조건](https://platform.claude.com/docs/ko/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid)이 충족되는 동안에만 유효하며, 그중 하나의 조건은 분할 지점이 위치할 수 있는 곳을 제한합니다.

응답과 다음 `user` 메시지 사이에 있는 프로그램의 분할 지점은 이 조건을 충족합니다. 이미 보낸 요청의 끝에서 분할하는 경우도 마찬가지입니다. 해당 요청의 `messages`를 정확히 그대로 압축하고, 그 이후 기록에 추가된 모든 내용은 유지하세요.
