Claude Sonnet 5로 마이그레이션하기

Claude Sonnet 5로 마이그레이션하기 (Migrating to Claude Sonnet 5)

이 가이드는 이전 Claude 모델에서 Claude Sonnet 5로 옮길 때 필요한 모델 ID 변경, 호환성 파괴 변경, 그리고 마이그레이션 체크리스트를 정리한 문서예요. 특히 Claude Sonnet 4.6과 그 이전 Sonnet 모델에서 이동하는 경우에 초점을 맞춰 단계별로 안내해 드려요.

출처: 문서

본문

이 가이드는 [Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) 코드 마이그레이션을 다뤄요. [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview)를 사용한다면 모델 이름을 업데이트하는 것 외에 필요한 변경은 없어요. **Claude API 스킬로 마이그레이션을 자동화하세요.** Claude Code에서 `/claude-api migrate`를 실행해 번들된 [Claude API 스킬](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill#migrating-to-a-newer-claude-model)을 호출하세요. 현재 Claude 모델을 대상으로 동작해요:
/claude-api migrate this project to claude-sonnet-5

이 스킬은 코드베이스 전체에 걸쳐 대상 모델에 맞는 모델 ID 교체와, 필요시 호환성 파괴 파라미터 변경, 프리필 대체, effort 보정을 적용한 다음, 수동으로 확인할 항목의 체크리스트를 생성해요. 파일을 편집하기 전에 마이그레이션 범위(전체 작업 디렉터리, 하위 디렉터리, 또는 특정 파일 목록)를 확인하라고 요청해요. 이 스킬은 Amazon Bedrock과 AWS 위의 Claude Platform 클라이언트도 감지해 해당 플랫폼의 모델 ID 형식과 기능 변경을 조정해요.

Claude Sonnet 5는 Claude 모델 제품군에서 속도와 지능의 최상의 조합을 제공해요. Claude Sonnet 4.6을 기반으로 만들어졌어요.

Claude Sonnet 5는 입력/출력 백만 토큰당 $2/$10 USD로 책정된 Claude Sonnet 4.6의 드롭인 업그레이드예요. 자세한 내용은 가격을 참고하세요. Claude Sonnet 4.6에서 이미 실행 중인 코드에는 두 가지 호환성 파괴 API 변경이 있어요. 첫째, adaptive thinking이 기본값으로 켜져 있고 수동 extended thinking(thinking: {type: "enabled", budget_tokens: N})은 400 오류를 반환해요. 그래서 thinking 없이 실행되던 요청이 이제 첫 text 블록 전에 thinking 블록을 반환할 수 있고, 위치로 콘텐츠를 읽는 코드는 type으로 콘텐츠 블록을 선택해야 해요. 둘째, 샘플링 파라미터(temperature, top_p, top_k)를 비기본값으로 설정하면 400 오류를 반환해요. thinking 깊이를 제어하려면 effort 파라미터와 함께 adaptive thinking을 사용하세요. Claude Sonnet 5는 1M 토큰 컨텍스트 창, adaptive thinking, 프롬프트 캐싱, 배치 처리, Files API, PDF 지원, 비전, 그리고 서버 측 및 클라이언트 측 도구 전체 등 Claude Sonnet 4.6과 동일한 기능 세트를 지원해요. Claude API와 Google Cloud에서 Claude Sonnet 5는 안정 버전 computer_toolset_20260801 툴셋으로 컴퓨터 사용브라우저 사용 도구도 지원해요.

Claude Sonnet 4.6에서 Claude Sonnet 5로 마이그레이션

코드가 Claude Sonnet 4.5 이하에 있다면 [Claude Sonnet 4.5 및 이전 Sonnet 모델에서 Claude Sonnet 5로 마이그레이션](https://platform.claude.com/docs/en/models/sonnet-5/migration-guide#migrating-from-sonnet-45)도 적용하세요. 그 단계에는 이 섹션만으로는 다루지 않는 호환성 파괴 변경(assistant 메시지 프리필 거부, 도구 파라미터 JSON 이스케이핑 차이)이 포함돼 있어요.

모델 이름 업데이트

# Sonnet migration
model = "claude-sonnet-4-6"  # Before
model = "claude-sonnet-5"  # After

변경된 내용

다음 목록의 4번과 5번은 호환성 파괴 변경이에요. max_tokens은 총 출력(thinking + 응답 텍스트)의 하드 한도이므로, Claude Sonnet 4.6에서 thinking 없이 실행되던 워크로드는 이를 다시 검토하세요.

  1. 새 토크나이저: Claude Sonnet 5는 새 토크나이저를 사용해요. 같은 입력 텍스트는 Claude Sonnet 4.6보다 약 30% 더 많은 토큰을 생성해요. 정확한 증가량은 콘텐츠에 따라 달라져요. 요청, 응답, 스트리밍 이벤트는 동일한 형태를 유지하며 코드 변경이 필요하지 않지만, 토큰으로 측정하거나 예산을 세우는 모든 것이 바뀌어요: 같은 텍스트의 usage 필드와 토큰 계산 결과가 더 높고, 1M 토큰 컨텍스트 창이 더 적은 텍스트를 담으며, Claude Sonnet 4.6에 맞춰 조정된 max_tokens 한도는 동등한 출력을 잘라낼 수 있어요. 토큰당 가격은 더 낮지만(Claude Sonnet 4.6의 입력/출력 백만 토큰당 $3/$15 USD 대비 $2/$10 USD), 동등한 요청의 비용은 직접 비례해 내려가지 않아요. 이전 모델로 측정한 수치를 재사용하지 말고 Claude Sonnet 5 기준으로 토큰 계산을 다시 실행하세요.

  2. 128k 최대 출력 토큰 (변경 없음): Claude Sonnet 5는 Claude Sonnet 4.6과 동일하게 최대 128k 출력 토큰을 지원해요. 기존 max_tokens 값은 계속 유효해요. 크기를 정할 때 새 토크나이저를 고려하세요.

  3. Assistant 메시지 프리필 (변경 없음): Claude Sonnet 5에서 assistant 메시지 프리필은 Claude Sonnet 4.6과 동일하게 400 오류를 반환해요. Claude Sonnet 4.6으로 마이그레이션할 때 프리필을 제거했다면 추가 변경은 필요 없어요. 대신 구조화된 출력, 시스템 프롬프트 지시, 또는 output_config.format을 사용하세요.

  4. Adaptive thinking 기본값 켜짐: Claude Sonnet 4.6에서는 thinking 필드가 없는 요청이 thinking 없이 실행되고, Claude Sonnet 5에서는 같은 요청이 adaptive thinking으로 실행돼요. thinking을 끄려면 thinking: {type: "disabled"}를 전달하세요. 수동 extended thinking(thinking: {type: "enabled", budget_tokens: N})은 지원되지 않으며 400 오류를 반환해요. thinking 깊이를 제어하려면 effort 파라미터(기본 high)를 사용하세요.

    thinking이 켜지면 응답이 첫 text 블록 전에 하나 이상의 thinking 블록으로 시작할 수 있으며, 기본 display: "omitted"에서 빈 thinking 필드로 반환돼요. content[0].text처럼 위치로 답변을 읽는 코드나 첫 콘텐츠 블록을 텍스트로 취급하는 스트림 핸들러는 대신 type 필드로 콘텐츠 블록을 선택해야 하고, 도구 사용 루프는 thinking 블록을 도구 결과와 함께 완전하고 수정 없이 다시 전달해야 해요(thinking 블록 보존 참고). thinking 토큰은 thinking 텍스트가 반환되지 않아도 출력 토큰으로 과금돼요. Claude Sonnet 4.6에서 thinking을 사용하고 반환된 thinking 텍스트를 표시한다면, thinking.display가 거기서 "summarized"로 기본설정되고 Claude Sonnet 5에서는 "omitted"로 기본설정된다는 점에 주의하세요. 아래 예시처럼 읽을 수 있는 요약을 계속 받으려면 display: "summarized"를 설정하세요(thinking 표시 제어 참고).

    Adaptive thinking은 Claude Sonnet 5에서 기본값으로 켜져 있어요. 여기서 `thinking` 필드는 `display: "summarized"`를 설정하기 위해 명시적으로 표시한 것뿐이에요. `thinking`을 생략하면 Claude Sonnet 5는 기본적으로 응답에서 thinking 콘텐츠를 생략해요. 모델별 기본값은 [각 모델이 거부하는 구성](https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting#rejected-configurations)을 참고하세요.
    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "x-api-key: $ANTHR...KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "content-type: application/json" \
        -d '{
          "model": "claude-sonnet-5",
          "max_tokens": 16000,
          "thinking": {
            "type": "adaptive",
            "display": "summarized"
          },
          "output_config": {
            "effort": "high"
          },
          "messages": [
            {
              "role": "user",
              "content": "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
            }
          ]
        }'
      ```
    
      ```bash CLI
      ant messages create --transform content --format yaml <<'YAML'
      model: claude-sonnet-5
      max_tokens: 16000
      thinking:
        type: adaptive
        display: summarized
      output_config:
        effort: high
      messages:
        - role: user
          content: Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.
      YAML
      ```
    
      ```python Python
      client = anthropic.Anthropic()
    
      response = client.messages.create(
          model="claude-sonnet-5",
          max_tokens=16000,
          thinking={"type": "adaptive", "display": "summarized"},
          output_config={"effort": "high"},
          messages=[
              {
                  "role": "user",
                  "content": "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.",
              }
          ],
      )
    
      # The response contains summarized thinking blocks and text blocks
      for block in response.content:
          match block.type:
              case "thinking":
                  print(f"\nThinking summary: {block.thinking}")
              case "text":
                  print(f"\nResponse: {block.text}")
      ```
    
      ```typescript TypeScript
      const client = new Anthropic();
    
      const response = await client.messages.create({
        model: "claude-sonnet-5",
        max_tokens: 16000,
        thinking: {
          type: "adaptive",
          display: "summarized"
        },
        output_config: {
          effort: "high"
        },
        messages: [
          {
            role: "user",
            content: "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
          }
        ]
      });
    
      // The response contains summarized thinking blocks and text blocks
      for (const block of response.content) {
        switch (block.type) {
          case "thinking":
            console.log(`\nThinking summary: ${block.thinking}`);
            break;
          case "text":
            console.log(`\nResponse: ${block.text}`);
            break;
        }
      }
      ```
    
      ```csharp C#
      AnthropicClient client = new();
    
      var response = await client.Messages.Create(new()
      {
          Model = Model.ClaudeSonnet5,
          MaxTokens = 16000,
          Thinking = new ThinkingConfigAdaptive { Display = Display.Summarized },
          OutputConfig = new OutputConfig { Effort = Effort.High },
          Messages =
          [
              new()
              {
                  Role = Role.User,
                  Content = "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.",
              },
          ],
      });
    
      // The response contains summarized thinking blocks and text blocks
      foreach (var block in response.Content)
      {
          if (block.TryPickThinking(out var thinking))
          {
              Console.WriteLine($"\nThinking summary: {thinking.Thinking}");
          }
          else if (block.TryPickText(out var text))
          {
              Console.WriteLine($"\nResponse: {text.Text}");
          }
      }
      ```
    
      ```go Go
      client := anthropic.NewClient()
    
      response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
      \tModel:     anthropic.ModelClaudeSonnet5,
      \tMaxTokens: 16000,
      \tThinking: anthropic.ThinkingConfigParamUnion{
      \t\tOfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{
      \t\t\tDisplay: anthropic.ThinkingConfigAdaptiveDisplaySummarized,
      \t\t},
      \t},
      \tOutputConfig: anthropic.OutputConfigParam{
      \t\tEffort: anthropic.OutputConfigEffortHigh,
      \t},
      \tMessages: []anthropic.MessageParam{
      \t\tanthropic.NewUserMessage(anthropic.NewTextBlock("Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.")),
      \t},
      })
      if err != nil {
      \tlog.Fatal(err)
      }
    
      // The response contains summarized thinking blocks and text blocks
      for _, block := range response.Content {
      \tswitch block := block.AsAny().(type) {
      \tcase anthropic.ThinkingBlock:
      \t\tfmt.Printf("\nThinking summary: %s", block.Thinking)
      \tcase anthropic.TextBlock:
      \t\tfmt.Printf("\nResponse: %s", block.Text)
      \t}
      }
      ```
    
      ```java Java
      import com.anthropic.client.okhttp.AnthropicOkHttpClient;
      import com.anthropic.models.messages.MessageCreateParams;
      import com.anthropic.models.messages.Model;
      import com.anthropic.models.messages.OutputConfig;
      import com.anthropic.models.messages.ThinkingConfigAdaptive;
    
      void main() {
          var client = AnthropicOkHttpClient.fromEnv();
    
          var params = MessageCreateParams.builder()
              .model(Model.CLAUDE_SONNET_5)
              .maxTokens(16_000)
              .thinking(ThinkingConfigAdaptive.builder()
                  .display(ThinkingConfigAdaptive.Display.SUMMARIZED)
                  .build())
              .outputConfig(OutputConfig.builder()
                  .effort(OutputConfig.Effort.HIGH)
                  .build())
              .addUserMessage("Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.")
              .build();
    
          var response = client.messages().create(params);
    
          // The response contains summarized thinking blocks and text blocks
          for (var block : response.content()) {
              block.thinking().ifPresent(thinkingBlock ->
                  IO.println("\nThinking summary: " + thinkingBlock.thinking())
              );
              block.text().ifPresent(textBlock ->
                  IO.println("\nResponse: " + textBlock.text())
              );
          }
      }
      ```
    
      ```php PHP
      use Anthropic\Messages\TextBlock;
      use Anthropic\Messages\ThinkingBlock;
    
      $client = new Client();
    
      $response = $client->messages->create(
          model: 'claude-sonnet-5',
          maxTokens: 16000,
          thinking: ['type' => 'adaptive', 'display' => 'summarized'],
          outputConfig: ['effort' => 'high'],
          messages: [
              [
                  'role' => 'user',
                  'content' => 'Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.',
              ],
          ],
      );
    
      // The response contains summarized thinking blocks and text blocks
      foreach ($response->content as $block) {
          echo match (true) {
              $block instanceof ThinkingBlock => "\nThinking summary: {$block->thinking}",
              $block instanceof TextBlock => "\nResponse: {$block->text}",
              default => '',
          };
      }
      ```
    
      ```ruby Ruby
      client = Anthropic::Client.new
    
      response = client.messages.create(
        model: "claude-sonnet-5",
        max_tokens: 16_000,
        thinking: {type: :adaptive, display: :summarized},
        output_config: {effort: :high},
        messages: [
          {
            role: :user,
            content: "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
          }
        ]
      )
    
      # The response contains summarized thinking blocks and text blocks
      response.content.each do |block|
        case block
        when Anthropic::Models::ThinkingBlock
          puts "\nThinking summary: #{block.thinking}"
        when Anthropic::Models::TextBlock
          puts "\nResponse: #{block.text}"
        end
      end
      ```
    </CodeGroup>
    
    ```bash cURL curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHR...KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 16000, "thinking": { "type": "enabled", "budget_tokens": 10000 }, "messages": [ { "role": "user", "content": "Are there an infinite number of prime numbers such that n mod 4 == 3?" } ] }' ```
      ```bash CLI
      ant messages create \
        --format yaml <<'YAML'
      model: claude-sonnet-4-6
      max_tokens: 16000
      thinking:
        type: enabled
        budget_tokens: 10000
      messages:
        - role: user
          content: Are there an infinite number of prime numbers such that n mod 4 == 3?
      YAML
      ```
    
      ```python Python
      client = anthropic.Anthropic()
    
      response = client.messages.create(
          model="claude-sonnet-4-6",
          max_tokens=16000,
          thinking={"type": "enabled", "budget_tokens": 10000},
          messages=[
              {
                  "role": "user",
                  "content": "Are there an infinite number of prime numbers such that n mod 4 == 3?",
              }
          ],
      )
    
      # The response contains summarized thinking blocks and text blocks
      for block in response.content:
          match block.type:
              case "thinking":
                  print(f"\nThinking summary: {block.thinking}")
              case "text":
                  print(f"\nResponse: {block.text}")
      ```
    
      ```typescript TypeScript
      const client = new Anthropic();
    
      const response = await client.messages.create({
        model: "claude-sonnet-4-6",
        max_tokens: 16000,
        thinking: {
          type: "enabled",
          budget_tokens: 10000,
        },
        messages: [
          {
            role: "user",
            content: "Are there an infinite number of prime numbers such that n mod 4 == 3?",
          },
        ],
      });
    
      // The response contains summarized thinking blocks and text blocks
      for (const block of response.content) {
        switch (block.type) {
          case "thinking":
            console.log(`\nThinking summary: ${block.thinking}`);
            break;
          case "text":
            console.log(`\nResponse: ${block.text}`);
            break;
        }
      }
      ```
    
      ```csharp C#
      AnthropicClient client = new();
    
      var response = await client.Messages.Create(new()
      {
          Model = Model.ClaudeSonnet4_6,
          MaxTokens = 16000,
          Thinking = new ThinkingConfigEnabled(budgetTokens: 10000),
          Messages =
          [
              new()
              {
                  Role = Role.User,
                  Content = "Are there an infinite number of prime numbers such that n mod 4 == 3?",
              },
          ],
      });
    
      // The response contains summarized thinking blocks and text blocks
      foreach (var block in response.Content)
      {
          if (block.TryPickThinking(out var thinking))
          {
              Console.WriteLine($"\nThinking summary: {thinking.Thinking}");
          }
          else if (block.TryPickText(out var text))
          {
              Console.WriteLine($"\nResponse: {text.Text}");
          }
      }
      ```
    
      ```go Go
      client := anthropic.NewClient()
    
      response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
      \tModel:     anthropic.ModelClaudeSonnet4_6,
      \tMaxTokens: 16000,
      \tThinking:  anthropic.ThinkingConfigParamOfEnabled(10000),
      \tMessages: []anthropic.MessageParam{
      \t\tanthropic.NewUserMessage(anthropic.NewTextBlock("Are there an infinite number of prime numbers such that n mod 4 == 3?")),
      \t},
      })
      if err != nil {
      \tlog.Fatal(err)
      }
    
      // The response contains summarized thinking blocks and text blocks
      for _, block := range response.Content {
      \tswitch block := block.AsAny().(type) {
      \tcase anthropic.ThinkingBlock:
      \t\tfmt.Printf("\nThinking summary: %s", block.Thinking)
      \tcase anthropic.TextBlock:
      \t\tfmt.Printf("\nResponse: %s", block.Text)
      \t}
      }
      ```
    
      ```java Java
      import com.anthropic.client.okhttp.AnthropicOkHttpClient;
      import com.anthropic.models.messages.MessageCreateParams;
      import com.anthropic.models.messages.Model;
    
      void main() {
          var client = AnthropicOkHttpClient.fromEnv();
    
          var params = MessageCreateParams.builder()
              .model(Model.CLAUDE_SONNET_4_6)
              .maxTokens(16_000)
              .enabledThinking(10_000)
              .addUserMessage("Are there an infinite number of prime numbers such that n mod 4 == 3?")
              .build();
    
          var response = client.messages().create(params);
    
          // The response contains summarized thinking blocks and text blocks
          for (var block : response.content()) {
              block.thinking().ifPresent(thinkingBlock ->
                  IO.println("\nThinking summary: " + thinkingBlock.thinking())
              );
              block.text().ifPresent(textBlock ->
                  IO.println("\nResponse: " + textBlock.text())
              );
          }
      }
      ```
    
      ```php PHP
      $client = new Client();
    
      $response = $client->messages->create(
          model: 'claude-sonnet-4-6',
          maxTokens: 16000,
          thinking: ['type' => 'enabled', 'budget_tokens' => 10000],
          messages: [
              [
                  'role' => 'user',
                  'content' => 'Are there an infinite number of prime numbers such that n mod 4 == 3?',
              ],
          ],
      );
    
      // The response contains summarized thinking blocks and text blocks
      foreach ($response->content as $block) {
          echo match (true) {
              $block instanceof \Anthropic\Messages\ThinkingBlock => "\nThinking summary: {$block->thinking}",
              $block instanceof \Anthropic\Messages\TextBlock => "\nResponse: {$block->text}",
              default => '',
          };
      }
      ```
    
      ```ruby Ruby
      client = Anthropic::Client.new
    
      response = client.messages.create(
        model: "claude-sonnet-4-6",
        max_tokens: 16_000,
        thinking: {
          type: :enabled,
          budget_tokens: 10_000
        },
        messages: [
          {
            role: :user,
            content: "Are there an infinite number of prime numbers such that n mod 4 == 3?"
          }
        ]
      )
    
      # The response contains summarized thinking blocks and text blocks
      response.content.each do |block|
        case block
        when Anthropic::Models::ThinkingBlock
          puts "\nThinking summary: #{block.thinking}"
        when Anthropic::Models::TextBlock
          puts "\nResponse: #{block.text}"
        end
      end
      ```
    </CodeGroup>
    
  5. 샘플링 파라미터 제거됨: 샘플링 파라미터(temperature, top_p, top_k)를 비기본값으로 설정하는 것은 허용되지 않으며 400 오류를 반환해요.

  6. 사이버 보안 안전장치: Claude Sonnet 5는 실시간 사이버 보안 안전장치를 갖춘 첫 Sonnet 등급 모델이에요. 금지되거나 고위험 사이버 보안 주제가 포함된 요청은 거절될 수 있어요. 거절은 오류가 아닌 stop_reason: "refusal"을 가진 성공적인 HTTP 200 응답으로 반환돼요. 안전장치가 차단하는 내용과 합법적인 보안 작업이 Cyber Verification Program에 어떻게 적용될 수 있는지는 Claude Opus 및 Sonnet의 실시간 사이버 안전장치를 참고하세요.

마이그레이션 체크리스트

  • 모델 이름을 claude-sonnet-4-6에서 claude-sonnet-5로 업데이트하세요.
  • Claude Sonnet 5 기준으로 토큰 계산을 다시 실행하세요. 새 토크나이저는 같은 텍스트에 대해 약 30% 더 많은 토큰을 생성하므로, 토큰당 가격이 낮음에도 요청당 비용이 바뀔 수 있어요. 정확한 증가량은 콘텐츠와 워크로드 형태에 따라 달라져요.
  • 예상 출력 길이에 가깝게 조정된 max_tokens 한도를 다시 검토하고, 유용한 경우 128k 최대값(Claude Sonnet 4.6과 동일)까지 올리세요.
  • thinking: {type: "enabled", budget_tokens: N} 구성을 제거하세요(400 오류 반환). Adaptive thinking이 기본값으로 켜져 있어요. 끄려면 {type: "disabled"}를 전달하고, 깊이를 제어하려면 effort 파라미터를 사용하세요.
  • content[0].text처럼 위치로 콘텐츠를 읽는 응답 파싱을 업데이트하세요: thinking이 켜지면 thinking 블록이 text 블록보다 먼저 도착해요. 대신 type으로 콘텐츠 블록을 선택하고, 도구 사용 루프에서 thinking 블록을 수정 없이 다시 전달하세요. 수정된 블록은 400 오류를 반환해요.
  • thinking 필드를 파싱하는 코드가 그것을 표시 텍스트로만 취급하는지 확인하세요. thinking.display는 Claude Sonnet 5에서 "omitted"로 기본설정되고(Claude Sonnet 4.6에서는 "summarized"였음), 그래서 thinking 블록이 빈 thinking 필드로 도착해요. 읽을 수 있는 요약을 받으려면 display: "summarized"를 설정하세요. thinking 표시 제어를 참고하세요.
  • 비기본값으로 설정된 temperature, top_p, top_k 파라미터를 제거하세요 (Claude Sonnet 5에서 400 오류 반환).
  • 워크로드가 사이버 보안 주제와 관련될 수 있다면 stop_reason: "refusal" 처리 로직을 추가하세요.
  • 프로덕션 배포 전에 일반적인 워크로드에서 비용을 다시 기준선으로 잡으세요.
  • 이전에 thinking 없이 실행되던 워크로드의 max_tokens을 다시 검토하세요.

Claude Sonnet 4.5 및 이전 Sonnet 모델에서 Claude Sonnet 5로 마이그레이션

Claude Sonnet 4.5 또는 이전 Sonnet 모델에서 Claude Sonnet 5로 직접 마이그레이션한다면, Claude Sonnet 4.6에서 Claude Sonnet 5로 마이그레이션 변경 사항과 이 섹션의 변경 사항을 모두 적용하세요.

Claude Sonnet 5는 effort 파라미터가 없던 Sonnet 4.5와 달리 기본 effort 수준이 `high`예요. 마이그레이션하면서 [effort 파라미터](https://platform.claude.com/docs/en/build-with-claude/effort)를 조정하는 것을 고려하세요. 명시적으로 설정하지 않으면 기본 effort 수준으로 더 높은 지연 시간을 경험할 수 있어요.

호환성 파괴 변경

Sonnet 4.5에서 마이그레이션할 때

  1. Assistant 메시지 프리필 더 이상 지원되지 않음

    Sonnet 4.5 이하에서 마이그레이션할 때의 호환성 파괴 변경이에요.

    assistant 메시지 프리필은 Claude Sonnet 4.6 이상 모델(Claude Sonnet 5 포함)에서 400 오류를 반환해요. 대신 구조화된 출력, 시스템 프롬프트 지시, 또는 output_config.format을 사용하세요.

    일반적인 프리필 사용 사례와 마이그레이션:

    • 출력 포맷 제어(JSON/YAML 출력 강제): 구조화된 출력 또는 분류 작업에 enum 필드가 있는 도구를 사용하세요.

    • 서두 제거("Here is..." 같은 문구 제거): 시스템 프롬프트에 직접 지시를 추가하세요: "서두 없이 직접 응답해라. 'Here is...', 'Based on...' 같은 문구로 시작하지 말 것."

    • 잘못된 거절 피하기: Claude는 이제 적절한 거절을 훨씬 잘해요. 프리필 없이 사용자 메시지에서 명확하게 프롬프팅하는 것으로 충분해요.

    • 연속(이어 쓰기)(중단된 응답 재개): 연속 부분을 사용자 메시지로 옮기세요: "이전 응답이 중단되어 [previous_response]로 끝났습니다. 중단된 지점부터 이어서 작성해 주세요."

    • 컨텍스트 보강 / 역할 일관성(긴 대화에서 컨텍스트 새로고침): 이전에 프리필된 assistant 알림이었던 것을 사용자 턴에 주입하세요.

  2. 도구 파라미터 JSON 이스케이핑이 다를 수 있음

    Sonnet 4.5 이하에서 마이그레이션할 때의 호환성 파괴 변경이에요.

    도구 파라미터의 JSON 문자열 이스케이핑이 이전 모델과 다를 수 있어요. 표준 JSON 파서는 자동으로 처리하지만, 사용자 정의 문자열 기반 파싱은 업데이트가 필요할 수 있어요.

Extended thinking 변경: Claude Sonnet 4.5의 budget_tokens 구성(thinking: {type: "enabled", budget_tokens: N})은 Claude Sonnet 5에서 지원되지 않으며 400 오류를 반환해요. Adaptive thinking이 기본값으로 켜져 있어 대부분의 워크로드는 thinking 구성이 전혀 필요하지 않아요. thinking 깊이를 제어하려면 effort 파라미터를 사용하세요. Claude Sonnet 4.5를 extended thinking 없이 실행했다면 thinking: {type: "disabled"}를 전달해 그 동작을 유지하세요.

Claude 3.x에서 마이그레이션할 때

  1. 샘플링 파라미터 제거

    Claude 3.x 모델에서 마이그레이션할 때의 호환성 파괴 변경이에요.

    샘플링 파라미터(temperature, top_p, top_k)를 비기본값으로 설정하면 Claude Sonnet 5에서 400 오류를 반환해요. 요청에서 제거하고, 프롬프팅으로 모델 동작을 안내하세요.

  2. 도구 버전 업데이트

    Claude 3.x 모델에서 마이그레이션할 때의 호환성 파괴 변경이에요.

    최신 도구 버전(text_editor_20250728, code_execution_20260521)으로 업데이트하세요. undo_edit 명령을 사용하는 코드는 제거하세요.

  3. refusal stop reason 처리

    거절 stop reason 처리를 위해 애플리케이션을 업데이트하세요.

  4. 동작 변경에 맞게 프롬프트 업데이트

    Claude 4 모델은 더 간결하고 직접적인 커뮤니케이션 스타일을 가져요. 최적화 지침은 프롬프팅 모범 사례를 검토하세요.

Claude Haiku 4.5에서 Claude Sonnet 5로 마이그레이션

Claude Haiku 4.5와 Claude Sonnet 5는 같은 등급 내 인접 모델보다 API 수준에서 더 많이 달라요. Claude Haiku 4.5는 수동 extended thinking(기본 꺼짐), 200k 토큰 컨텍스트 창, 최대 64k 출력 토큰을 사용하는 반면, Claude Sonnet 5는 adaptive thinking이 기본값으로 켜진 채 실행되고, 기본값으로 1M 토큰 컨텍스트 창을 제공하며, 최대 128k 출력 토큰을 지원해요.

모델 이름 업데이트

model = "claude-haiku-4-5-20251001"  # Before
model = "claude-sonnet-5"  # After

변경된 내용

  1. Thinking 구성: Claude Haiku 4.5는 수동 extended thinking(thinking: {type: "enabled", budget_tokens: N})을 지원하고 thinking: {type: "adaptive"}를 거부해요. Claude Sonnet 5에서는 지원이 반대예요: adaptive thinking이 기본값으로 켜져 있고, 수동 extended thinking은 400 오류를 반환해요. thinking: {type: "enabled", budget_tokens: N} 구성을 제거하고 기본값에 맡기거나, thinking을 끄려면 thinking: {type: "disabled"}를 전달하세요. budget_tokens의 직접적인 대체재는 없어요. thinking 깊이를 제어하려면 effort 파라미터를 사용하세요. Effort는 Claude Haiku 4.5에서 제공되지 않으며 Claude Sonnet 5에서 high로 기본설정돼요.

    두 종류의 Claude Haiku 4.5 요청 모두 응답 형태가 바뀌어요. extended thinking 없이 실행되던 요청은 이제 첫 text 블록 전에 하나 이상의 thinking 블록을 반환할 수 있어요. 그래서 content[0].text처럼 위치로 답변을 읽는 코드는 대신 type 필드로 콘텐츠 블록을 선택해야 하고, 도구 사용 루프는 thinking 블록을 도구 결과와 함께 완전하고 수정 없이 다시 전달해야 해요(thinking 블록 보존 참고). extended thinking을 사용하던 요청은 계속 thinking 블록을 받지만, Claude Sonnet 5에서 thinking.display"summarized"가 아닌 "omitted"로 기본설정되므로 그 블록들이 빈 thinking 필드로 도착해요. 읽을 수 있는 요약을 계속 받으려면 display: "summarized"를 설정하세요(thinking 표시 제어 참고). thinking 토큰은 thinking 텍스트가 반환되지 않아도 출력 토큰으로 과금돼요.

  2. 샘플링 파라미터 제거됨: temperaturetop_p는 Claude Haiku 4.5에서 동작해요(둘 다가 아니라 한 번에 하나). Claude Sonnet 5에서는 temperature, top_p, top_k를 비기본값으로 설정하면 400 오류를 반환해요. 이 파라미터를 제거하고 프롬프팅으로 모델 동작을 안내하세요.

  3. Assistant 프리필 제거됨: assistant 메시지 프리필은 Claude Haiku 4.5에서 동작하지만 Claude Sonnet 5에서는 400 오류를 반환해요. 대신 구조화된 출력, 시스템 프롬프트 지시, 또는 output_config.format을 사용하세요.

  4. 더 큰 컨텍스트 창과 출력: Claude Sonnet 5는 기본값으로 1M 토큰 컨텍스트 창을 제공하며(Claude Haiku 4.5의 200k에서 증가), 최대 128k 출력 토큰을 지원해요(64k에서 증가). Claude Sonnet 5는 다른 토크나이저도 사용하므로, Claude Haiku 4.5로 측정한 수치를 재사용하지 말고 토큰 계산을 다시 실행하세요.

  5. 가격: Claude Haiku 4.5는 입력/출력 백만 토큰당 $1/$5 USD로 책정돼요. Claude Sonnet 5는 입력/출력 백만 토큰당 $2/$10 USD로 책정돼요. Claude 가격을 참고하세요.

  6. 사이버 보안 안전장치: Claude Sonnet 5는 실시간 사이버 보안 안전장치가 있어요. 금지되거나 고위험 사이버 보안 주제가 포함된 요청은 거절될 수 있으며, stop_reason: "refusal"을 가진 성공적인 HTTP 200 응답으로 반환돼요. 안전장치가 차단하는 내용과 합법적인 보안 작업이 Cyber Verification Program에 어떻게 적용될 수 있는지는 Claude Opus 및 Sonnet의 실시간 사이버 안전장치를 참고하세요.

마이그레이션 체크리스트

  • 모델 이름을 claude-haiku-4-5-20251001(또는 claude-haiku-4-5 별칭)에서 claude-sonnet-5로 업데이트하세요.
  • thinking: {type: "enabled", budget_tokens: N} 구성을 제거하세요(400 오류 반환). Adaptive thinking이 기본값으로 켜져 있어요. no-thinking 동작을 유지하려면 thinking: {type: "disabled"}를 전달하고, thinking 없이 실행되던 워크로드의 max_tokens을 다시 검토하세요.
  • content[0].text처럼 위치로 콘텐츠를 읽는 응답 파싱을 업데이트하세요: thinking이 켜지면 thinking 블록이 text 블록보다 먼저 도착해요. 대신 type으로 콘텐츠 블록을 선택하고, 도구 사용 루프에서 thinking 블록을 수정 없이 다시 전달하세요. 수정된 블록은 400 오류를 반환해요.
  • UI가 thinking 콘텐츠를 표시한다면 display: "summarized"를 설정하세요. thinking.display는 Claude Sonnet 5에서 "omitted"로 기본설정되므로, 그렇지 않으면 thinking 블록이 빈 thinking 필드로 도착해요. thinking 표시 제어를 참고하세요.
  • thinking 깊이와 토큰 소비를 제어하려면 effort 파라미터(기본 high)를 사용하세요. Claude Haiku 4.5에는 제공되지 않으므로 기존 설정이 이어지지 않아요.
  • temperaturetop_p 설정을 제거하세요(비기본값은 Claude Sonnet 5에서 400 오류 반환).
  • assistant 메시지 프리필을 모두 제거하세요(Claude Sonnet 5에서 400 오류 반환).
  • Claude Sonnet 5 기준으로 토큰 계산을 다시 실행하고, 최대 128k까지 올릴 수 있는 max_tokens 한도를 다시 검토하세요.
  • 워크로드가 사이버 보안 주제와 관련될 수 있다면 stop_reason: "refusal" 처리 로직을 추가하세요.
  • 프로덕션 배포 전에 일반적인 워크로드에서 비용을 다시 기준선으로 잡으세요. 토큰당 가격이 달라요.

더 알아보기 (Learn more)