확장된 사고는 Claude에게 복잡한 작업을 위한 향상된 추론 능력을 제공하며, 최종 답변을 제공하기 전에 단계별 사고 과정에 대한 다양한 수준의 투명성을 제공합니다.

지원되는 모델

확장된 사고는 다음 모델에서 지원됩니다:
  • Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)
  • Claude Sonnet 4 (claude-sonnet-4-20250514)
  • Claude Sonnet 3.7 (claude-3-7-sonnet-20250219)
  • Claude Haiku 4.5 (claude-haiku-4-5-20251001)
  • Claude Opus 4.1 (claude-opus-4-1-20250805)
  • Claude Opus 4 (claude-opus-4-20250514)
API 동작은 Claude Sonnet 3.7과 Claude 4 모델 간에 다르지만, API 형태는 정확히 동일하게 유지됩니다.자세한 정보는 모델 버전 간 사고의 차이점을 참조하세요.

확장된 사고 작동 방식

확장된 사고가 켜져 있을 때, Claude는 내부 추론을 출력하는 thinking 콘텐츠 블록을 생성합니다. Claude는 최종 응답을 작성하기 전에 이 추론에서 얻은 통찰력을 통합합니다. API 응답에는 thinking 콘텐츠 블록이 포함되고, 그 다음에 text 콘텐츠 블록이 포함됩니다. 다음은 기본 응답 형식의 예시입니다:
{
  "content": [
    {
      "type": "thinking",
      "thinking": "Let me analyze this step by step...",
      "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL...."
    },
    {
      "type": "text",
      "text": "Based on my analysis..."
    }
  ]
}
확장된 사고의 응답 형식에 대한 자세한 정보는 Messages API 참조를 참조하세요.

확장된 사고 사용 방법

다음은 Messages API에서 확장된 사고를 사용하는 예시입니다:
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-sonnet-4-5",
    "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?"
        }
    ]
}'
확장된 사고를 켜려면, thinking 객체를 추가하고 type 매개변수를 enabled로 설정하고 budget_tokens를 확장된 사고를 위한 지정된 토큰 예산으로 설정합니다. budget_tokens 매개변수는 Claude가 내부 추론 과정에 사용할 수 있는 최대 토큰 수를 결정합니다. Claude 4 모델에서 이 제한은 전체 사고 토큰에 적용되며, 요약된 출력에는 적용되지 않습니다. 더 큰 예산은 복잡한 문제에 대한 더 철저한 분석을 가능하게 하여 응답 품질을 향상시킬 수 있지만, Claude는 특히 32k 이상의 범위에서 할당된 전체 예산을 사용하지 않을 수 있습니다. budget_tokensmax_tokens보다 작은 값으로 설정되어야 합니다. 그러나 도구와 함께 인터리브된 사고를 사용할 때는 토큰 제한이 전체 컨텍스트 창(200k 토큰)이 되므로 이 제한을 초과할 수 있습니다.

요약된 사고

확장된 사고가 활성화된 상태에서 Claude 4 모델의 Messages API는 Claude의 전체 사고 과정의 요약을 반환합니다. 요약된 사고는 오용을 방지하면서 확장된 사고의 완전한 지능적 이점을 제공합니다. 요약된 사고에 대한 몇 가지 중요한 고려사항은 다음과 같습니다:
  • 원래 요청에서 생성된 전체 사고 토큰에 대해 요금이 부과되며, 요약 토큰에 대해서는 부과되지 않습니다.
  • 청구된 출력 토큰 수는 응답에서 보는 토큰 수와 일치하지 않습니다.
  • 사고 출력의 처음 몇 줄은 더 자세하며, 프롬프트 엔지니어링 목적에 특히 도움이 되는 상세한 추론을 제공합니다.
  • Anthropic이 확장된 사고 기능을 개선하려고 노력함에 따라 요약 동작은 변경될 수 있습니다.
  • 요약은 최소한의 추가 지연 시간으로 Claude의 사고 과정의 핵심 아이디어를 보존하여 스트리밍 가능한 사용자 경험과 Claude Sonnet 3.7에서 Claude 4 모델로의 쉬운 마이그레이션을 가능하게 합니다.
  • 요약은 요청에서 대상으로 하는 모델과 다른 모델에 의해 처리됩니다. 사고 모델은 요약된 출력을 보지 않습니다.
Claude Sonnet 3.7은 계속해서 전체 사고 출력을 반환합니다.Claude 4 모델의 전체 사고 출력에 액세스해야 하는 드문 경우에는 영업팀에 문의하세요.

스트리밍 사고

서버 전송 이벤트(SSE)를 사용하여 확장된 사고 응답을 스트리밍할 수 있습니다. 확장된 사고에 대해 스트리밍이 활성화되면 thinking_delta 이벤트를 통해 사고 콘텐츠를 받습니다. Messages API를 통한 스트리밍에 대한 자세한 문서는 스트리밍 메시지를 참조하세요. 다음은 사고와 함께 스트리밍을 처리하는 방법입니다:
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-sonnet-4-5",
    "max_tokens": 16000,
    "stream": true,
    "thinking": {
        "type": "enabled",
        "budget_tokens": 10000
    },
    "messages": [
        {
            "role": "user",
            "content": "What is 27 * 453?"
        }
    ]
}'
스트리밍 출력 예시:
event: message_start
data: {"type": "message_start", "message": {"id": "msg_01...", "type": "message", "role": "assistant", "content": [], "model": "claude-sonnet-4-5", "stop_reason": null, "stop_sequence": null}}

event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "Let me solve this step by step:\n\n1. First break down 27 * 453"}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\n2. 453 = 400 + 50 + 3"}}

// Additional thinking deltas...

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "EqQBCgIYAhIM1gbcDa9GJwZA2b3hGgxBdjrkzLoky3dl1pkiMOYds..."}}

event: content_block_stop
data: {"type": "content_block_stop", "index": 0}

event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "text", "text": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "text_delta", "text": "27 * 453 = 12,231"}}

// Additional text deltas...

event: content_block_stop
data: {"type": "content_block_stop", "index": 1}

event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": null}}

event: message_stop
data: {"type": "message_stop"}
사고가 활성화된 스트리밍을 사용할 때 텍스트가 때때로 더 작은 토큰별 전달과 번갈아 가며 더 큰 청크로 도착하는 것을 알 수 있습니다. 이는 특히 사고 콘텐츠에서 예상되는 동작입니다.스트리밍 시스템은 최적의 성능을 위해 콘텐츠를 배치로 처리해야 하므로 스트리밍 이벤트 간에 지연이 발생할 수 있는 이러한 “청크” 전달 패턴이 나타날 수 있습니다. 우리는 이 경험을 지속적으로 개선하고 있으며, 향후 업데이트는 사고 콘텐츠가 더 부드럽게 스트리밍되도록 하는 데 중점을 둡니다.

도구 사용과 함께하는 확장된 사고

확장된 사고는 도구 사용과 함께 사용할 수 있어 Claude가 도구 선택 및 결과 처리를 통해 추론할 수 있습니다. 도구 사용과 함께 확장된 사고를 사용할 때 다음 제한사항을 알아두세요:
  1. 도구 선택 제한: 사고와 함께하는 도구 사용은 tool_choice: {"type": "auto"}(기본값) 또는 tool_choice: {"type": "none"}만 지원합니다. tool_choice: {"type": "any"} 또는 tool_choice: {"type": "tool", "name": "..."}를 사용하면 이러한 옵션이 도구 사용을 강제하므로 확장된 사고와 호환되지 않아 오류가 발생합니다.
  2. 사고 블록 보존: 도구 사용 중에는 마지막 어시스턴트 메시지에 대해 thinking 블록을 API에 다시 전달해야 합니다. 추론 연속성을 유지하기 위해 완전하고 수정되지 않은 블록을 API에 다시 포함시키세요.
다음은 도구 결과를 제공할 때 사고 블록을 보존하는 방법을 보여주는 실용적인 예시입니다:
weather_tool = {
    "name": "get_weather",
    "description": "Get current weather for a location",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
        "required": ["location"]
    }
}

# First request - Claude responds with thinking and tool request
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[weather_tool],
    messages=[
        {"role": "user", "content": "What's the weather in Paris?"}
    ]
)
API 응답에는 사고, 텍스트 및 tool_use 블록이 포함됩니다:
{
    "content": [
        {
            "type": "thinking",
            "thinking": "The user wants to know the current weather in Paris. I have access to a function `get_weather`...",
            "signature": "BDaL4VrbR2Oj0hO4XpJxT28J5TILnCrrUXoKiiNBZW9P+nr8XSj1zuZzAl4egiCCpQNvfyUuFFJP5CncdYZEQPPmLxYsNrcs...."
        },
        {
            "type": "text",
            "text": "I can help you get the current weather information for Paris. Let me check that for you"
        },
        {
            "type": "tool_use",
            "id": "toolu_01CswdEQBMshySk6Y9DFKrfq",
            "name": "get_weather",
            "input": {
                "location": "Paris"
            }
        }
    ]
}
이제 대화를 계속하고 도구를 사용해보겠습니다
# Extract thinking block and tool use block
thinking_block = next((block for block in response.content
                      if block.type == 'thinking'), None)
tool_use_block = next((block for block in response.content
                      if block.type == 'tool_use'), None)

# Call your actual weather API, here is where your actual API call would go
# let's pretend this is what we get back
weather_data = {"temperature": 88}

# Second request - Include thinking block and tool result
# No new thinking blocks will be generated in the response
continuation = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[weather_tool],
    messages=[
        {"role": "user", "content": "What's the weather in Paris?"},
        # notice that the thinking_block is passed in as well as the tool_use_block
        # if this is not passed in, an error is raised
        {"role": "assistant", "content": [thinking_block, tool_use_block]},
        {"role": "user", "content": [{
            "type": "tool_result",
            "tool_use_id": tool_use_block.id,
            "content": f"Current temperature: {weather_data['temperature']}°F"
        }]}
    ]
)
이제 API 응답에는 텍스트 포함됩니다
{
    "content": [
        {
            "type": "text",
            "text": "Currently in Paris, the temperature is 88°F (31°C)"
        }
    ]
}

사고 블록 보존

도구 사용 중에는 thinking 블록을 API에 다시 전달해야 하며, 완전하고 수정되지 않은 블록을 API에 다시 포함시켜야 합니다. 이는 모델의 추론 흐름과 대화 무결성을 유지하는 데 중요합니다.
이전 assistant 역할 턴에서 thinking 블록을 생략할 수 있지만, 다중 턴 대화에서는 항상 모든 사고 블록을 API에 다시 전달하는 것을 권장합니다. API는:
  • 제공된 사고 블록을 자동으로 필터링합니다
  • 모델의 추론을 보존하는 데 필요한 관련 사고 블록을 사용합니다
  • Claude에게 표시된 블록에 대한 입력 토큰에 대해서만 요금을 청구합니다
Claude가 도구를 호출할 때, 외부 정보를 기다리기 위해 응답 구성을 일시 중지합니다. 도구 결과가 반환되면 Claude는 기존 응답을 계속 구축합니다. 이는 몇 가지 이유로 도구 사용 중 사고 블록을 보존해야 합니다:
  1. 추론 연속성: 사고 블록은 도구 요청으로 이어진 Claude의 단계별 추론을 캡처합니다. 도구 결과를 게시할 때 원래 사고를 포함하면 Claude가 중단된 지점에서 추론을 계속할 수 있습니다.
  2. 컨텍스트 유지: 도구 결과가 API 구조에서 사용자 메시지로 나타나지만, 연속적인 추론 흐름의 일부입니다. 사고 블록을 보존하면 여러 API 호출에 걸쳐 이 개념적 흐름이 유지됩니다. 컨텍스트 관리에 대한 자세한 정보는 컨텍스트 창에 대한 가이드를 참조하세요.
중요: thinking 블록을 제공할 때, 연속된 thinking 블록의 전체 시퀀스는 원래 요청 중에 모델에서 생성된 출력과 일치해야 합니다. 이러한 블록의 시퀀스를 재배열하거나 수정할 수 없습니다.

인터리브된 사고

Claude 4 모델에서 도구 사용과 함께하는 확장된 사고는 인터리브된 사고를 지원하여 Claude가 도구 호출 간에 사고하고 도구 결과를 받은 후 더 정교한 추론을 할 수 있게 합니다. 인터리브된 사고를 통해 Claude는:
  • 다음에 무엇을 할지 결정하기 전에 도구 호출의 결과에 대해 추론할 수 있습니다
  • 중간에 추론 단계를 거쳐 여러 도구 호출을 연결할 수 있습니다
  • 중간 결과를 바탕으로 더 미묘한 결정을 내릴 수 있습니다
인터리브된 사고를 활성화하려면 API 요청에 베타 헤더 interleaved-thinking-2025-05-14를 추가하세요. 인터리브된 사고에 대한 몇 가지 중요한 고려사항은 다음과 같습니다:
  • 인터리브된 사고를 사용하면 budget_tokensmax_tokens 매개변수를 초과할 수 있습니다. 이는 하나의 어시스턴트 턴 내의 모든 사고 블록에 걸친 총 예산을 나타내기 때문입니다.
  • 인터리브된 사고는 Messages API를 통해 사용되는 도구에서만 지원됩니다.
  • 인터리브된 사고는 베타 헤더 interleaved-thinking-2025-05-14와 함께 Claude 4 모델에서만 지원됩니다.
  • Claude API에 대한 직접 호출을 통해 모든 모델에 대한 요청에서 interleaved-thinking-2025-05-14를 전달할 수 있으며, 효과는 없습니다.
  • 3rd-party 플랫폼(예: Amazon BedrockVertex AI)에서 Claude Opus 4.1, Opus 4 또는 Sonnet 4 이외의 모델에 interleaved-thinking-2025-05-14를 전달하면 요청이 실패합니다.
import anthropic

client = anthropic.Anthropic()

# Define tools
calculator_tool = {
    "name": "calculator",
    "description": "Perform mathematical calculations",
    "input_schema": {
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "Mathematical expression to evaluate"
            }
        },
        "required": ["expression"]
    }
}

database_tool = {
    "name": "database_query",
    "description": "Query product database",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "SQL query to execute"
            }
        },
        "required": ["query"]
    }
}

# First request - Claude thinks once before all tool calls
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    messages=[{
        "role": "user",
        "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
    }]
)

# Response includes thinking followed by tool uses
# Note: Claude thinks once at the beginning, then makes all tool decisions
print("First response:")
for block in response.content:
    if block.type == "thinking":
        print(f"Thinking (summarized): {block.thinking}")
    elif block.type == "tool_use":
        print(f"Tool use: {block.name} with input {block.input}")
    elif block.type == "text":
        print(f"Text: {block.text}")

# You would execute the tools and return results...
# After getting both tool results back, Claude directly responds without additional thinking
인터리브된 사고 없는 이 예시에서:
  1. Claude는 작업을 이해하기 위해 처음에 한 번 사고합니다
  2. 모든 도구 사용 결정을 미리 내립니다
  3. 도구 결과가 반환되면 Claude는 추가 사고 없이 즉시 응답을 제공합니다
import anthropic

client = anthropic.Anthropic()

# Same tool definitions as before
calculator_tool = {
    "name": "calculator",
    "description": "Perform mathematical calculations",
    "input_schema": {
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "Mathematical expression to evaluate"
            }
        },
        "required": ["expression"]
    }
}

database_tool = {
    "name": "database_query",
    "description": "Query product database",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "SQL query to execute"
            }
        },
        "required": ["query"]
    }
}

# First request with interleaved thinking enabled
response = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[{
        "role": "user",
        "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
    }]
)

print("Initial response:")
thinking_blocks = []
tool_use_blocks = []

for block in response.content:
    if block.type == "thinking":
        thinking_blocks.append(block)
        print(f"Thinking: {block.thinking}")
    elif block.type == "tool_use":
        tool_use_blocks.append(block)
        print(f"Tool use: {block.name} with input {block.input}")
    elif block.type == "text":
        print(f"Text: {block.text}")

# First tool result (calculator)
calculator_result = "7500"  # 150 * 50

# Continue with first tool result
response2 = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[
        {
            "role": "user",
            "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
        },
        {
            "role": "assistant",
            "content": [thinking_blocks[0], tool_use_blocks[0]]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[0].id,
                "content": calculator_result
            }]
        }
    ]
)

print("\nAfter calculator result:")
# With interleaved thinking, Claude can think about the calculator result
# before deciding to query the database
for block in response2.content:
    if block.type == "thinking":
        thinking_blocks.append(block)
        print(f"Interleaved thinking: {block.thinking}")
    elif block.type == "tool_use":
        tool_use_blocks.append(block)
        print(f"Tool use: {block.name} with input {block.input}")

# Second tool result (database)
database_result = "5200"  # Example average monthly revenue

# Continue with second tool result
response3 = client.beta.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    tools=[calculator_tool, database_tool],
    betas=["interleaved-thinking-2025-05-14"],
    messages=[
        {
            "role": "user",
            "content": "What's the total revenue if we sold 150 units of product A at $50 each, and how does this compare to our average monthly revenue from the database?"
        },
        {
            "role": "assistant",
            "content": [thinking_blocks[0], tool_use_blocks[0]]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[0].id,
                "content": calculator_result
            }]
        },
        {
            "role": "assistant",
            "content": thinking_blocks[1:] + tool_use_blocks[1:]
        },
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_blocks[1].id,
                "content": database_result
            }]
        }
    ]
)

print("\nAfter database result:")
# With interleaved thinking, Claude can think about both results
# before formulating the final response
for block in response3.content:
    if block.type == "thinking":
        print(f"Final thinking: {block.thinking}")
    elif block.type == "text":
        print(f"Final response: {block.text}")
인터리브된 사고와 함께하는 이 예시에서:
  1. Claude는 처음에 작업에 대해 사고합니다
  2. 계산기 결과를 받은 후, Claude는 그 결과가 무엇을 의미하는지에 대해 다시 사고할 수 있습니다
  3. 그런 다음 Claude는 첫 번째 결과를 바탕으로 데이터베이스를 쿼리하는 방법을 결정합니다
  4. 데이터베이스 결과를 받은 후, Claude는 최종 응답을 공식화하기 전에 두 결과에 대해 다시 한 번 사고합니다
  5. 사고 예산은 턴 내의 모든 사고 블록에 걸쳐 분배됩니다
이 패턴은 각 도구의 출력이 다음 결정에 정보를 제공하는 더 정교한 추론 체인을 가능하게 합니다.

프롬프트 캐싱과 함께하는 확장된 사고

사고와 함께하는 프롬프트 캐싱에는 몇 가지 중요한 고려사항이 있습니다:
확장된 사고 작업은 종종 완료하는 데 5분 이상 걸립니다. 더 긴 사고 세션과 다단계 워크플로우에서 캐시 히트를 유지하기 위해 1시간 캐시 지속 시간 사용을 고려하세요.
사고 블록 컨텍스트 제거
  • 이전 턴의 사고 블록은 컨텍스트에서 제거되어 캐시 중단점에 영향을 줄 수 있습니다
  • 도구 사용과 함께 대화를 계속할 때, 사고 블록은 캐시되고 캐시에서 읽을 때 입력 토큰으로 계산됩니다
  • 이는 트레이드오프를 만듭니다: 사고 블록이 시각적으로 컨텍스트 창 공간을 소비하지 않지만, 캐시될 때 여전히 입력 토큰 사용량에 계산됩니다
  • 사고가 비활성화되면, 현재 도구 사용 턴에서 사고 콘텐츠를 전달하는 경우 요청이 실패합니다. 다른 컨텍스트에서는 API에 전달된 사고 콘텐츠가 단순히 무시됩니다
캐시 무효화 패턴
  • 사고 매개변수(활성화/비활성화 또는 예산 할당) 변경은 메시지 캐시 중단점을 무효화합니다
  • 인터리브된 사고는 사고 블록이 여러 도구 호출 사이에 발생할 수 있어 캐시 무효화를 증폭시킵니다
  • 시스템 프롬프트와 도구는 사고 매개변수 변경이나 블록 제거에도 불구하고 캐시된 상태로 유지됩니다
사고 블록은 캐싱 및 컨텍스트 계산을 위해 제거되지만, 도구 사용, 특히 인터리브된 사고와 함께 대화를 계속할 때는 보존되어야 합니다.

사고 블록 캐싱 동작 이해

도구 사용과 함께 확장된 사고를 사용할 때, 사고 블록은 토큰 계산에 영향을 주는 특정 캐싱 동작을 보입니다: 작동 방식:
  1. 캐싱은 도구 결과를 포함하는 후속 요청을 할 때만 발생합니다
  2. 후속 요청이 이루어지면, 이전 대화 기록(사고 블록 포함)이 캐시될 수 있습니다
  3. 이러한 캐시된 사고 블록은 캐시에서 읽을 때 사용량 메트릭에서 입력 토큰으로 계산됩니다
  4. 도구 결과가 아닌 사용자 블록이 포함되면, 모든 이전 사고 블록이 무시되고 컨텍스트에서 제거됩니다
상세한 예시 흐름: 요청 1:
User: "What's the weather in Paris?"
응답 1:
[thinking_block_1] + [tool_use block 1]
요청 2:
User: ["What's the weather in Paris?"], 
Assistant: [thinking_block_1] + [tool_use block 1], 
User: [tool_result_1, cache=True]
응답 2:
[thinking_block_2] + [text block 2]
요청 2는 요청 콘텐츠(응답이 아님)의 캐시를 작성합니다. 캐시에는 원래 사용자 메시지, 첫 번째 사고 블록, 도구 사용 블록 및 도구 결과가 포함됩니다. 요청 3:
User: ["What's the weather in Paris?"], 
Assistant: [thinking_block_1] + [tool_use block 1], 
User: [tool_result_1, cache=True], 
Assistant: [thinking_block_2] + [text block 2], 
User: [Text response, cache=True]
도구 결과가 아닌 사용자 블록이 포함되었기 때문에 모든 이전 사고 블록이 무시됩니다. 이 요청은 다음과 동일하게 처리됩니다:
User: ["What's the weather in Paris?"], 
Assistant: [tool_use block 1], 
User: [tool_result_1, cache=True], 
Assistant: [text block 2], 
User: [Text response, cache=True]
핵심 포인트:
  • 이 캐싱 동작은 명시적인 cache_control 마커 없이도 자동으로 발생합니다
  • 이 동작은 일반 사고 또는 인터리브된 사고를 사용하든 일관됩니다
from anthropic import Anthropic
import requests
from bs4 import BeautifulSoup

client = Anthropic()

def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # Remove script and style elements
    for script in soup(["script", "style"]):
        script.decompose()

    # Get text
    text = soup.get_text()

    # Break into lines and remove leading and trailing space on each
    lines = (line.strip() for line in text.splitlines())
    # Break multi-headlines into a line each
    chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
    # Drop blank lines
    text = '\n'.join(chunk for chunk in chunks if chunk)

    return text

# Fetch the content of the article
book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
book_content = fetch_article_content(book_url)
# Use just enough text for caching (first few chapters)
LARGE_TEXT = book_content[:5000]

SYSTEM_PROMPT=[
    {
        "type": "text",
        "text": "You are an AI assistant that is tasked with literary analysis. Analyze the following text carefully.",
    },
    {
        "type": "text",
        "text": LARGE_TEXT,
        "cache_control": {"type": "ephemeral"}
    }
]

MESSAGES = [
    {
        "role": "user",
        "content": "Analyze the tone of this passage."
    }
]

# First request - establish cache
print("First request - establishing cache")
response1 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    system=SYSTEM_PROMPT,
    messages=MESSAGES
)

print(f"First response usage: {response1.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response1.content
})
MESSAGES.append({
    "role": "user",
    "content": "Analyze the characters in this passage."
})
# Second request - same thinking parameters (cache hit expected)
print("\nSecond request - same thinking parameters (cache hit expected)")
response2 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    system=SYSTEM_PROMPT,
    messages=MESSAGES
)

print(f"Second response usage: {response2.usage}")

# Third request - different thinking parameters (cache miss for messages)
print("\nThird request - different thinking parameters (cache miss for messages)")
response3 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 8000  # Changed thinking budget
    },
    system=SYSTEM_PROMPT,  # System prompt remains cached
    messages=MESSAGES  # Messages cache is invalidated
)

print(f"Third response usage: {response3.usage}")
from anthropic import Anthropic
import requests
from bs4 import BeautifulSoup

client = Anthropic()

def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # Remove script and style elements
    for script in soup(["script", "style"]):
        script.decompose()

    # Get text
    text = soup.get_text()

    # Break into lines and remove leading and trailing space on each
    lines = (line.strip() for line in text.splitlines())
    # Break multi-headlines into a line each
    chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
    # Drop blank lines
    text = '\n'.join(chunk for chunk in chunks if chunk)

    return text

# Fetch the content of the article
book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
book_content = fetch_article_content(book_url)
# Use just enough text for caching (first few chapters)
LARGE_TEXT = book_content[:5000]

# No system prompt - caching in messages instead
MESSAGES = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": LARGE_TEXT,
                "cache_control": {"type": "ephemeral"},
            },
            {
                "type": "text",
                "text": "Analyze the tone of this passage."
            }
        ]
    }
]

# First request - establish cache
print("First request - establishing cache")
response1 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000
    },
    messages=MESSAGES
)

print(f"First response usage: {response1.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response1.content
})
MESSAGES.append({
    "role": "user",
    "content": "Analyze the characters in this passage."
})
# Second request - same thinking parameters (cache hit expected)
print("\nSecond request - same thinking parameters (cache hit expected)")
response2 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 4000  # Same thinking budget
    },
    messages=MESSAGES
)

print(f"Second response usage: {response2.usage}")

MESSAGES.append({
    "role": "assistant",
    "content": response2.content
})
MESSAGES.append({
    "role": "user",
    "content": "Analyze the setting in this passage."
})

# Third request - different thinking budget (cache miss expected)
print("\nThird request - different thinking budget (cache miss expected)")
response3 = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=20000,
    thinking={
        "type": "enabled",
        "budget_tokens": 8000  # Different thinking budget breaks cache
    },
    messages=MESSAGES
)

print(f"Third response usage: {response3.usage}")
다음은 스크립트의 출력입니다 (약간 다른 숫자를 볼 수 있습니다)
First request - establishing cache
First response usage: { cache_creation_input_tokens: 1370, cache_read_input_tokens: 0, input_tokens: 17, output_tokens: 700 }

Second request - same thinking parameters (cache hit expected)

Second response usage: { cache_creation_input_tokens: 0, cache_read_input_tokens: 1370, input_tokens: 303, output_tokens: 874 }

Third request - different thinking budget (cache miss expected)
Third response usage: { cache_creation_input_tokens: 1370, cache_read_input_tokens: 0, input_tokens: 747, output_tokens: 619 }
이 예시는 메시지 배열에서 캐싱이 설정될 때, 사고 매개변수 변경(budget_tokens가 4000에서 8000으로 증가)이 캐시를 무효화함을 보여줍니다. 세 번째 요청은 cache_creation_input_tokens=1370cache_read_input_tokens=0으로 캐시 히트가 없음을 보여주며, 사고 매개변수가 변경될 때 메시지 기반 캐싱이 무효화됨을 증명합니다.

확장된 사고와 함께하는 최대 토큰 및 컨텍스트 창 크기

이전 Claude 모델(Claude Sonnet 3.7 이전)에서는 프롬프트 토큰과 max_tokens의 합이 모델의 컨텍스트 창을 초과하면 시스템이 자동으로 max_tokens를 컨텍스트 제한 내에 맞도록 조정했습니다. 이는 큰 max_tokens 값을 설정하면 시스템이 필요에 따라 자동으로 줄여주었다는 의미입니다. Claude 3.7 및 4 모델에서는 max_tokens(사고가 활성화된 경우 사고 예산 포함)가 엄격한 제한으로 적용됩니다. 이제 프롬프트 토큰 + max_tokens가 컨텍스트 창 크기를 초과하면 시스템이 검증 오류를 반환합니다.
더 철저한 심화 학습을 위해 컨텍스트 창에 대한 가이드를 읽어보실 수 있습니다.

확장된 사고와 함께하는 컨텍스트 창

사고가 활성화된 상태에서 컨텍스트 창 사용량을 계산할 때 알아두어야 할 몇 가지 고려사항이 있습니다:
  • 이전 턴의 사고 블록은 제거되어 컨텍스트 창에 계산되지 않습니다
  • 현재 턴 사고는 해당 턴의 max_tokens 제한에 계산됩니다
아래 다이어그램은 확장된 사고가 활성화될 때의 특수한 토큰 관리를 보여줍니다: 확장된 사고와 함께하는 컨텍스트 창 다이어그램 효과적인 컨텍스트 창은 다음과 같이 계산됩니다:
context window =
  (current input tokens - previous thinking tokens) +
  (thinking tokens + encrypted thinking tokens + text output tokens)
특히 사고를 포함하는 다중 턴 대화를 작업할 때 특정 사용 사례에 대한 정확한 토큰 수를 얻기 위해 토큰 계산 API를 사용하는 것을 권장합니다.

확장된 사고 및 도구 사용과 함께하는 컨텍스트 창

도구 사용과 함께 확장된 사고를 사용할 때, 사고 블록은 명시적으로 보존되어 도구 결과와 함께 반환되어야 합니다. 도구 사용과 함께하는 확장된 사고의 효과적인 컨텍스트 창 계산은 다음과 같습니다:
context window =
  (current input tokens + previous thinking tokens + tool use tokens) +
  (thinking tokens + encrypted thinking tokens + text output tokens)
아래 다이어그램은 도구 사용과 함께하는 확장된 사고의 토큰 관리를 보여줍니다: 도구 사용과 함께하는 확장된 사고 컨텍스트 창 다이어그램

확장된 사고와 함께 토큰 관리

확장된 사고 Claude 3.7 및 4 모델의 컨텍스트 창 및 max_tokens 동작을 고려할 때 다음이 필요할 수 있습니다:
  • 토큰 사용량을 더 적극적으로 모니터링하고 관리
  • 프롬프트 길이가 변경됨에 따라 max_tokens 값 조정
  • 토큰 계산 엔드포인트를 더 자주 사용
  • 이전 사고 블록이 컨텍스트 창에 누적되지 않는다는 점 인식
이 변경은 특히 최대 토큰 제한이 크게 증가함에 따라 더 예측 가능하고 투명한 동작을 제공하기 위해 이루어졌습니다.

사고 암호화

전체 사고 콘텐츠는 암호화되어 signature 필드에 반환됩니다. 이 필드는 API에 다시 전달될 때 사고 블록이 Claude에 의해 생성되었음을 확인하는 데 사용됩니다.
확장된 사고와 함께 도구를 사용할 때만 사고 블록을 다시 보내는 것이 엄격히 필요합니다. 그렇지 않으면 이전 턴의 사고 블록을 생략하거나 다시 전달하면 API가 자동으로 제거하도록 할 수 있습니다.사고 블록을 다시 보낼 때는 일관성을 위해 받은 모든 것을 다시 전달하고 잠재적인 문제를 피하는 것을 권장합니다.
사고 암호화에 대한 몇 가지 중요한 고려사항은 다음과 같습니다:
  • 응답을 스트리밍할 때, 서명은 content_block_stop 이벤트 직전에 content_block_delta 이벤트 내의 signature_delta를 통해 추가됩니다.
  • signature 값은 Claude 4 모델에서 이전 모델보다 상당히 깁니다.
  • signature 필드는 불투명한 필드이며 해석하거나 파싱해서는 안 됩니다. 검증 목적으로만 존재합니다.
  • signature 값은 플랫폼 간(Claude API, Amazon Bedrock, Vertex AI)에서 호환됩니다. 한 플랫폼에서 생성된 값은 다른 플랫폼과 호환됩니다.

사고 편집

때때로 Claude의 내부 추론이 우리의 안전 시스템에 의해 플래그될 수 있습니다. 이런 경우 thinking 블록의 일부 또는 전부를 암호화하여 redacted_thinking 블록으로 반환합니다. redacted_thinking 블록은 API에 다시 전달될 때 복호화되어 Claude가 컨텍스트를 잃지 않고 응답을 계속할 수 있게 합니다. 확장된 사고를 사용하는 고객 대면 애플리케이션을 구축할 때:
  • 편집된 사고 블록에는 사람이 읽을 수 없는 암호화된 콘텐츠가 포함되어 있다는 점을 인식하세요
  • “Claude의 내부 추론 중 일부가 안전상의 이유로 자동으로 암호화되었습니다. 이는 응답 품질에 영향을 주지 않습니다.”와 같은 간단한 설명을 제공하는 것을 고려하세요
  • 사용자에게 사고 블록을 보여줄 때 일반 사고 블록을 보존하면서 편집된 블록을 필터링할 수 있습니다
  • 확장된 사고 기능을 사용하면 때때로 일부 추론이 암호화될 수 있다는 점을 투명하게 알리세요
  • UI를 깨뜨리지 않고 편집된 사고를 우아하게 관리하기 위한 적절한 오류 처리를 구현하세요
다음은 일반 및 편집된 사고 블록을 모두 보여주는 예시입니다:
{
  "content": [
    {
      "type": "thinking",
      "thinking": "Let me analyze this step by step...",
      "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw3URve/op91XWHOEBLLqIOMfFG/UvLEczmEsUjavL...."
    },
    {
      "type": "redacted_thinking",
      "data": "EmwKAhgBEgy3va3pzix/LafPsn4aDFIT2Xlxh0L5L8rLVyIwxtE3rAFBa8cr3qpPkNRj2YfWXGmKDxH4mPnZ5sQ7vB9URj2pLmN3kF8/dW5hR7xJ0aP1oLs9yTcMnKVf2wRpEGjH9XZaBt4UvDcPrQ..."
    },
    {
      "type": "text",
      "text": "Based on my analysis..."
    }
  ]
}
출력에서 편집된 사고 블록을 보는 것은 예상되는 동작입니다. 모델은 안전 가드레일을 유지하면서 이 편집된 추론을 사용하여 응답에 정보를 제공할 수 있습니다.애플리케이션에서 편집된 사고 처리를 테스트해야 하는 경우, 프롬프트로 이 특별한 테스트 문자열을 사용할 수 있습니다: ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB
다중 턴 대화에서 thinkingredacted_thinking 블록을 API에 다시 전달할 때는 마지막 어시스턴트 턴에 대해 완전하고 수정되지 않은 블록을 API에 포함시켜야 합니다. 이는 모델의 추론 흐름을 유지하는 데 중요합니다. 모든 사고 블록을 API에 다시 전달하는 것을 권장합니다. 자세한 내용은 위의 사고 블록 보존 섹션을 참조하세요.
이 예시는 Claude의 내부 추론이 안전 시스템에 의해 플래그된 콘텐츠를 포함할 때 응답에 나타날 수 있는 redacted_thinking 블록을 처리하는 방법을 보여줍니다:
import anthropic

client = anthropic.Anthropic()

# Using a special prompt that triggers redacted thinking (for demonstration purposes only)
response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    messages=[{
        "role": "user",
        "content": "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB"
    }]
)

# Identify redacted thinking blocks
has_redacted_thinking = any(
    block.type == "redacted_thinking" for block in response.content
)

if has_redacted_thinking:
    print("Response contains redacted thinking blocks")
    # These blocks are still usable in subsequent requests

    # Extract all blocks (both redacted and non-redacted)
    all_thinking_blocks = [
        block for block in response.content
        if block.type in ["thinking", "redacted_thinking"]
    ]

    # When passing to subsequent requests, include all blocks without modification
    # This preserves the integrity of Claude's reasoning

    print(f"Found {len(all_thinking_blocks)} thinking blocks total")
    print(f"These blocks are still billable as output tokens")

모델 버전 간 사고의 차이점

Messages API는 Claude Sonnet 3.7과 Claude 4 모델 간에 주로 편집 및 요약 동작에서 사고를 다르게 처리합니다. 압축된 비교는 아래 표를 참조하세요:
기능Claude Sonnet 3.7Claude 4 모델
사고 출력전체 사고 출력 반환요약된 사고 반환
인터리브된 사고지원되지 않음interleaved-thinking-2025-05-14 베타 헤더와 함께 지원됨

가격

확장된 사고는 표준 토큰 가격 체계를 사용합니다:
모델기본 입력 토큰캐시 쓰기캐시 히트출력 토큰
Claude Opus 4.1$15 / MTok$18.75 / MTok$1.50 / MTok$75 / MTok
Claude Opus 4$15 / MTok$18.75 / MTok$1.50 / MTok$75 / MTok
Claude Sonnet 4.5$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
Claude Sonnet 4$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
Claude Sonnet 3.7$3 / MTok$3.75 / MTok$0.30 / MTok$15 / MTok
사고 과정에는 다음에 대한 요금이 부과됩니다:
  • 사고 중 사용된 토큰(출력 토큰)
  • 후속 요청에 포함된 마지막 어시스턴트 턴의 사고 블록(입력 토큰)
  • 표준 텍스트 출력 토큰
확장된 사고가 활성화되면 이 기능을 지원하기 위해 특수한 시스템 프롬프트가 자동으로 포함됩니다.
요약된 사고를 사용할 때:
  • 입력 토큰: 원래 요청의 토큰(이전 턴의 사고 토큰 제외)
  • 출력 토큰(청구됨): Claude가 내부적으로 생성한 원래 사고 토큰
  • 출력 토큰(표시됨): 응답에서 보는 요약된 사고 토큰
  • 무료: 요약 생성에 사용된 토큰
청구된 출력 토큰 수는 응답에서 보는 표시된 토큰 수와 일치하지 않습니다. 보는 요약이 아닌 전체 사고 과정에 대해 요금이 청구됩니다.

확장된 사고를 위한 모범 사례 및 고려사항

사고 예산 작업

  • 예산 최적화: 최소 예산은 1,024 토큰입니다. 최소값에서 시작하여 사용 사례에 최적의 범위를 찾기 위해 사고 예산을 점진적으로 늘리는 것을 권장합니다. 더 높은 토큰 수는 작업에 따라 수익 감소와 함께 더 포괄적인 추론을 가능하게 합니다. 예산을 늘리면 지연 시간 증가의 트레이드오프로 응답 품질이 향상될 수 있습니다. 중요한 작업의 경우 최적의 균형을 찾기 위해 다양한 설정을 테스트하세요. 사고 예산은 엄격한 제한이 아닌 목표라는 점에 유의하세요. 실제 토큰 사용량은 작업에 따라 달라질 수 있습니다.
  • 시작점: 복잡한 작업에는 더 큰 사고 예산(16k+ 토큰)으로 시작하고 필요에 따라 조정하세요.
  • 큰 예산: 32k 이상의 사고 예산의 경우 네트워킹 문제를 피하기 위해 배치 처리 사용을 권장합니다. 모델이 32k 토큰 이상 사고하도록 하는 요청은 시스템 타임아웃 및 열린 연결 제한에 부딪힐 수 있는 장시간 실행 요청을 야기합니다.
  • 토큰 사용량 추적: 비용과 성능을 최적화하기 위해 사고 토큰 사용량을 모니터링하세요.

성능 고려사항

  • 응답 시간: 추론 과정에 필요한 추가 처리로 인해 잠재적으로 더 긴 응답 시간에 대비하세요. 사고 블록 생성이 전체 응답 시간을 증가시킬 수 있다는 점을 고려하세요.
  • 스트리밍 요구사항: max_tokens가 21,333보다 클 때는 스트리밍이 필요합니다. 스트리밍할 때는 도착하는 사고 및 텍스트 콘텐츠 블록을 모두 처리할 준비를 하세요.

기능 호환성

  • 사고는 temperature 또는 top_k 수정 및 강제 도구 사용과 호환되지 않습니다.
  • 사고가 활성화되면 top_p를 1과 0.95 사이의 값으로 설정할 수 있습니다.
  • 사고가 활성화되면 응답을 미리 채울 수 없습니다.
  • 사고 예산 변경은 메시지를 포함하는 캐시된 프롬프트 접두사를 무효화합니다. 그러나 캐시된 시스템 프롬프트와 도구 정의는 사고 매개변수가 변경될 때도 계속 작동합니다.

사용 지침

  • 작업 선택: 수학, 코딩, 분석과 같은 단계별 추론의 이점을 받는 특히 복잡한 작업에 확장된 사고를 사용하세요.
  • 컨텍스트 처리: 이전 사고 블록을 직접 제거할 필요가 없습니다. Claude API는 이전 턴의 사고 블록을 자동으로 무시하며 컨텍스트 사용량을 계산할 때 포함되지 않습니다.
  • 프롬프트 엔지니어링: Claude의 사고 능력을 최대화하려면 확장된 사고 프롬프팅 팁을 검토하세요.

다음 단계