Claude Platform Docs
Messages도구

도구 러너 (SDK)

SDK의 도구 러너를 사용하여 에이전트 루프, 오류 래핑, 타입 안전성을 자동으로 처리하세요.

"Tool runner"(도구 러너)는 에이전트 루프, 오류 래핑, 타입 안전성을 대신 처리해 주므로 직접 처리할 필요가 없습니다. 사람의 승인(human-in-the-loop), 사용자 정의 로깅 또는 조건부 실행이 필요한 경우에는 대신 수동 루프를 사용하세요.

도구 호출, 도구 결과, 대화 관리를 수동으로 처리하는 대신, 도구 러너는 자동으로 다음을 수행합니다:

  • Claude가 도구를 호출하면 도구를 실행합니다
  • 요청/응답 사이클을 처리합니다
  • 대화 상태를 관리합니다
  • 타입 안전성과 유효성 검사를 제공합니다

기본 사용법

SDK 헬퍼를 사용하여 도구를 정의한 다음, 도구 러너를 사용하여 실행하세요.

SDK의 도구 시그니처에 따라 도구는 결과를 문자열 또는 콘텐츠 블록(텍스트, 이미지 또는 문서 블록)으로 반환하므로, 도구는 멀티모달 결과를 반환할 수 있습니다. 반환된 문자열은 단일 텍스트 콘텐츠 블록이 됩니다. JSON 객체나 숫자와 같은 구조화된 데이터를 반환하려면 먼저 문자열로 인코딩하세요.

@beta_tool 데코레이터를 사용하여 타입 힌트와 독스트링으로 도구를 정의하세요.

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()


@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
    """Get the current weather in a given location.

    Args:
        location: The city and state, e.g. San Francisco, CA
        unit: Temperature unit, either 'celsius' or 'fahrenheit'
    """
    return json.dumps({"temperature": "20°C", "condition": "Sunny"})


@beta_tool
def calculate_sum(a: int, b: int) -> str:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number
    """
    return str(a + b)


runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[get_weather, calculate_sum],
    messages=[
        {
            "role": "user",
            "content": "What's the weather like in Paris? Also, what's 15 + 27?",
        }
    ],
)
for message in runner:
    print(message)

@beta_tool 데코레이터는 함수 인수와 독스트링을 검사하여 JSON 스키마를 자동으로 도출합니다.

도구 러너 반복하기

도구 러너는 Claude의 메시지를 산출(yield)하는 이터러블입니다. 각 반복에서 러너는 Claude가 도구 사용을 요청했는지 확인합니다. 요청했다면 도구를 실행하고 결과를 자동으로 Claude에 다시 보낸 다음, 루프를 계속할 수 있도록 Claude의 다음 메시지를 산출합니다.

break 문을 사용하여 어느 반복에서든 루프를 종료할 수 있습니다. 러너는 Claude가 도구 사용이 없는 메시지를 반환할 때까지, 또는 설정한 경우 max_iterations에 도달할 때까지 반복합니다.

중간 메시지가 필요하지 않다면 최종 메시지를 직접 가져올 수 있습니다:

runner.until_done()을 사용하여 최종 메시지를 가져오세요.

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[get_weather, calculate_sum],
    messages=[
        {
            "role": "user",
            "content": "What's the weather like in Paris? Also, what's 15 + 27?",
        }
    ],
)
final_message = runner.until_done()
for block in final_message.content:
    if block.type == "text":
        print(block.text)

고급 사용법

루프 내에서 각 응답 메시지를 읽고 다음 API 호출 전에 러너의 상태를 수정할 수 있습니다. 각 반복은 다음 수명 주기를 따릅니다:

  1. 러너가 현재 상태로 Messages API에 요청을 보냅니다.
  2. 러너가 응답 메시지를 루프 본문에 산출합니다.
  3. 루프 본문이 실행됩니다. 메시지를 읽고 선택적으로 러너의 상태를 수정할 수 있습니다.
  4. 루프 본문이 반환되면 러너는 메시지 기록을 수정했는지 확인합니다.
    • 메시지 기록을 수정하지 않은 경우: 메시지에 도구 호출이 포함되어 있으면 러너는 어시스턴트 메시지와 도구 결과를 추가한 다음 계속합니다. 도구 호출이 없으면 루프가 종료됩니다.
    • 메시지 기록을 수정한 경우: 러너는 자동 추가를 건너뛰고 사용자의 상태를 변경 없이 사용합니다. 메시지 기록 직접 관리하기를 참조하세요.

메시지 기록 직접 관리하기

기본적으로 러너는 대화 상태를 대신 관리합니다. 각 도구 호출 턴 이후 어시스턴트 메시지와 도구 결과를 자체 메시지 기록에 추가합니다. 턴을 재시도(응답을 버리고 다시 전송)하거나, 후속 메시지를 삽입하거나, 도구 결과를 직접 구성하려는 경우 메시지 기록을 직접 관리하게 됩니다.

루프 본문 내부에서 러너의 메시지를 수정하면 직접 관리하게 됩니다. 정확한 방법은 SDK에 따라 다릅니다. 이어지는 언어별 탭을 참조하세요.

특정 반복에서 직접 관리하는 경우, 러너는 해당 턴의 어시스턴트 메시지나 도구 결과를 추가하지 않습니다. 대화를 유효하게 유지하는 책임은 사용자에게 있습니다. 어시스턴트 메시지와 도구 결과를 직접 추가하고(해당 턴을 반영하려는 경우), 도구 호출이 없을 때 루프가 여전히 종료될 수 있도록 상태를 조건부로 수정하며, 루프를 제한하기 위해 max_iterations를 전달하세요. 7개 SDK 모두 max_iterations를 지원합니다.

generate_tool_call_response()를 사용하여 도구 결과를 검사하거나 계산하세요. 루프 내부에서 append_messages()를 호출하면 기록을 직접 관리하고 있음을 러너에 알리게 되므로, 추가하는 내용에 어시스턴트 메시지와 도구 결과를 포함하세요.

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    max_iterations=10,
    tools=[get_weather],
    messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()
    if tool_response is not None:
        # append_messages()는 상태를 수정됨으로 표시하므로 러너는 이번 반복에서
        # 자동 추가를 건너뜁니다. 어시스턴트 메시지와 tool result를 직접 추가하고
        # 필요한 후속 메시지도 함께 추가하세요.
        runner.append_messages(
            message,
            tool_response,
            {"role": "user", "content": "Please be concise."},
        )
    # 도구 호출이 없으면 상태를 그대로 두어 루프가 종료되도록 합니다.

메시지 기록을 직접 관리하지 않고 max_tokens와 같은 요청 매개변수를 변경하려면 set_messages_params()를 사용하세요. 러너는 여전히 어시스턴트 메시지와 도구 결과를 자동으로 추가합니다.

for message in runner:
    runner.set_messages_params(lambda params: {**params, "max_tokens": 2048})

자동 컨텍스트 관리

장시간 실행되는 에이전트 작업의 경우, TypeScript 및 Ruby 도구 러너는 자동 "compaction"(압축)을 지원합니다. 이 기능은 토큰 사용량이 임계값을 초과하면 요약을 생성하여 대화가 컨텍스트 윈도우 한도를 넘어 계속될 수 있도록 합니다. 두 SDK 모두 이 클라이언트 측 옵션을 지원 중단하고 서버 측 압축을 권장하며, 서버 측 압축은 context_management 요청 매개변수를 통해 모든 SDK의 도구 러너에서 작동합니다. Python SDK(v1.0 이상)와 Go, Java, C#, PHP 도구 러너에는 클라이언트 측 압축이 포함되어 있지 않습니다. Python, TypeScript, C#, Go, Java, PHP, Ruby 도구 러너에는 온디맨드 압축을 위한 compact_before_next_turn() 헬퍼가 있습니다. 루프에서 압축하기를 참조하세요. 러너에서는 이 헬퍼와 context_management 압축 편집 중 하나만 사용하고, 둘 다 사용하지 마세요.

도구 실행 디버깅

도구가 예외를 던지면 도구 러너는 이를 잡아 is_error: true인 도구 결과로 Claude에 오류를 반환합니다. 도구 결과에는 전체 스택 트레이스가 아닌 예외의 메시지(Python에서는 타입과 메시지)가 담깁니다.

SDK가 로깅하는 내용은 언어별로 다릅니다. Python SDK는 도구가 처리되지 않은 예외를 발생시킬 때마다 표준 logging 모듈을 통해 스택 트레이스를 포함한 전체 예외를 로깅합니다. Python, TypeScript, Java SDK는 ANTHROPIC_LOG 환경 변수를 읽어 요청 및 응답 세부 정보를 포함하는 SDK 로깅을 활성화합니다:

# info 수준으로 로그 기록
export ANTHROPIC_LOG=info

# 더 자세한 출력을 위해 debug 수준으로 로그 기록
export ANTHROPIC_LOG=debug

Go, Ruby, C#, PHP SDK는 ANTHROPIC_LOG를 읽지 않습니다. Python 외에는 어떤 SDK도 실패한 도구를 로깅하지 않습니다. 도구가 실패한 이유를 확인하려면 반환하거나 다시 던지기 전에 도구 함수 내부에서 예외를 잡아 로깅하세요.

도구 오류 가로채기

기본적으로 도구 오류는 Claude에 다시 전달되며, Claude는 이에 적절히 응답할 수 있습니다. 그러나 오류를 감지하여 다르게 처리하고 싶을 수 있습니다. 예를 들어 실행을 조기에 중단하거나 사용자 정의 오류 처리를 구현하는 경우입니다.

Python 및 TypeScript SDK에서는 도구 응답 메서드(Python의 generate_tool_call_response(), TypeScript의 generateToolResponse())를 사용하여 도구 결과를 가로채고 Claude에 전송되기 전에 오류를 확인하세요. 다른 SDK는 해당 훅을 노출하지 않습니다. 각 탭에서 가장 가까운 대안을 설명합니다:

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[my_tool],
    messages=[{"role": "user", "content": "Run my_tool with the query 'hello'."}],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()

    if tool_response is not None:
        # tool_response는 dict입니다: {"role": "user", "content": [...]}
        # 도구 결과 중 오류가 있는지 확인합니다
        for block in tool_response["content"]:
            if block.get("is_error"):
                # 옵션 1: 예외를 발생시켜 루프를 중단합니다
                raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")

                # 옵션 2: 로그를 남기고 계속 진행합니다(Claude가 처리하도록 함)
                # logger.error(f"Tool error: {json.dumps(block['content'])}")

    # 메시지를 정상적으로 처리합니다
    print(message.content)

도구 결과 수정하기

도구 결과가 Claude에 다시 전송되기 전에 수정할 수 있습니다. 이는 도구 결과에 프롬프트 캐싱을 활성화하기 위해 cache_control과 같은 메타데이터를 추가하거나, 도구 출력을 변환하는 데 유용합니다.

Python 및 TypeScript SDK에서는 도구 응답 메서드를 사용하여 도구 결과를 가져온 다음, 러너가 진행하기 전에 수정하세요. 수정된 결과를 명시적으로 추가할지 제자리에서 변경할지는 SDK에 따라 다릅니다. 각 탭의 코드 주석을 참조하세요.

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[search_documents],
    messages=[
        {
            "role": "user",
            "content": "Search for information about the climate of San Francisco",
        }
    ],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()

    if tool_response is not None:
        # tool_response는 dict입니다: {"role": "user", "content": [...]}
        # 캐시 제어를 추가하도록 도구 결과를 수정합니다
        for block in tool_response["content"]:
            if block["type"] == "tool_result":
                # 이 도구 결과를 캐시하도록 cache_control을 추가합니다
                block["cache_control"] = {"type": "ephemeral"}

        # 수정된 응답을 추가합니다(원본이 자동으로 추가되지 않도록 합니다)
        runner.append_messages(message, tool_response)

    print(message.content)

스트리밍

"Streaming"(스트리밍)을 활성화하여 각 턴의 응답을 점진적으로 처리하세요. 각 반복은 이벤트를 반복할 수 있는 스트림 객체를 산출합니다.

stream=True를 설정하고 get_final_message()를 사용하여 누적된 메시지를 가져오세요.

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[calculate_sum],
    messages=[{"role": "user", "content": "What is 15 + 27?"}],
    stream=True,
)

# 스트리밍 시 runner는 BetaMessageStream을 반환합니다
for message_stream in runner:
    for event in message_stream:
        print("event:", event)
    print("message:", message_stream.get_final_message())

print(runner.until_done())

다음 단계

문법 제약 샘플링으로 Claude의 도구 입력에 JSON Schema 준수를 강제하세요.

tool_use 블록을 파싱하고, tool_result 응답을 포맷하며, is_error로 오류를 처리하세요.

메시지 기록 가이드 및 문제 해결과 함께 병렬 도구 호출을 활성화, 포맷, 비활성화하세요.

도구 스키마를 지정하고, 효과적인 설명을 작성하며, Claude가 도구를 호출하는 시점을 제어하세요.

Was this page helpful?