도구 러너는 에이전트 루프, 오류 래핑, 타입 안전성을 처리하므로 직접 처리할 필요가 없습니다. "human-in-the-loop"(인간 개입) 승인, 사용자 정의 로깅 또는 조건부 실행이 필요한 경우에는 대신 수동 루프를 사용하세요.
도구 호출, 도구 결과, 대화 관리를 수동으로 처리하는 대신, 도구 러너는 자동으로 다음을 수행합니다:
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",
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의 메시지를 산출하는 이터러블입니다. 각 반복에서 러너는 Claude가 도구 사용을 요청했는지 확인합니다. 요청했다면 도구를 실행하고 결과를 자동으로 Claude에게 다시 보낸 다음, 루프를 계속하기 위해 Claude의 다음 메시지를 산출합니다.
어떤 반복에서든 break 문으로 루프를 종료할 수 있습니다. 러너는 Claude가 도구 사용 없이 메시지를 반환할 때까지, 또는 max_iterations를 설정한 경우 해당 값에 도달할 때까지 루프를 반복합니다.
중간 메시지가 필요하지 않은 경우 최종 메시지를 직접 가져올 수 있습니다:
runner.until_done()을 사용하여 최종 메시지를 가져옵니다.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-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 호출 전에 러너의 상태를 수정할 수 있습니다. 각 반복은 다음 수명 주기를 따릅니다:
기본적으로 러너는 대화 상태를 관리합니다. 각 도구 호출 턴 후에 어시스턴트 메시지와 모든 도구 결과를 자체 메시지 기록에 추가합니다. 턴을 재시도하거나(응답을 버리고 다시 전송), 후속 메시지를 주입하거나, 도구 결과를 직접 구성하려는 경우 메시지 기록을 직접 관리합니다.
루프 본문 내부에서 러너의 메시지를 수정하여 직접 관리를 시작합니다. 정확한 방법은 SDK에 따라 다릅니다. 아래의 언어별 탭을 참조하세요.
한 반복에서 직접 관리를 시작하면 러너는 해당 턴의 어시스턴트 메시지나 도구 결과를 추가하지 않습니다. 대화를 유효하게 유지하는 책임은 사용자에게 있습니다. 어시스턴트 메시지와 도구 결과를 직접 추가하고(해당 턴을 반영하려는 경우), 도구 호출이 없을 때 루프가 여전히 종료될 수 있도록 상태를 조건부로 수정하고, 루프를 제한하기 위해 max_iterations를 전달하세요. 7개 SDK 모두 max_iterations를 지원합니다.
generate_tool_call_response()를 사용하여 도구 결과를 검사하거나 계산합니다. 루프 내부에서 append_messages()를 호출하면 러너에게 기록을 직접 관리하고 있음을 알리므로, 추가하는 내용에 어시스턴트 메시지와 도구 결과를 포함하세요.
runner = client.beta.messages.tool_runner(
model="claude-opus-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})장기 실행 에이전트 작업의 경우, Python, TypeScript, Ruby 도구 러너는 자동 압축(compaction)을 지원합니다. 이는 토큰 사용량이 임계값을 초과할 때 요약을 생성하여 대화가 컨텍스트 윈도우 제한을 넘어 계속될 수 있도록 합니다. 세 SDK 모두 이 클라이언트 측 옵션을 더 이상 사용하지 않으며(deprecated), 모든 SDK에서 사용할 수 있는 서버 측 컨텍스트 편집을 권장합니다. Go, Java, C#, PHP 도구 러너에는 클라이언트 측 압축이 포함되어 있지 않습니다.
도구가 예외를 발생시키면 도구 러너가 이를 포착하고 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=debugGo, 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",
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",
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는 딕셔너리입니다: {"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)스트리밍을 활성화하여 각 턴의 응답을 점진적으로 처리합니다. 각 반복은 이벤트를 반복할 수 있는 스트림 객체를 산출합니다.
stream=True를 설정하고 get_final_message()를 사용하여 누적된 메시지를 가져옵니다.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-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?