프로그래매틱 도구 호출 (Programmatic Tool Calling)¶
프로그래매틱 도구 호출을 쓰면 Claude가 여러분의 도구를 코드로 직접 호출할 수 있어요. 매번 도구를 부를 때마다 모델을 왕복(round trip)시키는 대신, 코드 실행(code execution) 컨테이너 안에서 프로그램적으로 호출하는 거죠. 이렇게 하면 여러 도구를 쓰는 워크플로에서 지연 시간이 줄고, 데이터가 모델의 컨텍스트 창에 들어오기 전에 Claude가 직접 필터링하거나 가공해서 토큰 소비도 줄어요.
에이전트 검색 벤치마크(BrowseComp, DeepSearchQA) 같은 것들은 다단계 웹 조사와 복잡한 정보 검색을 시험하는데, 기본 검색 도구 위에 프로그래매틱 도구 호출을 얹으면 평균 11% 성능이 오르고 입력 토큰은 24% 적게 썼어요(동적 필터링을 통한 웹 검색 개선 참고).
한번 생각해 볼게요. 직원 20명의 예산 준수 여부를 검사한다고 해요. 전통적인 방식으로는 20번을 따로따로 모델 왕복을 해야 하고, 그 과정에서 비용 항목 수천 줄을 컨텍스트에 끌어와야 해요. 그런데 프로그래매틱 도구 호출을 쓰면 스크립트 하나가 20건의 조회를 전부 실행하고, 결과를 필터링해서 한도를 초과한 직원만 반환해요. 그 결과 Claude가 추론해야 할 대상이 수백 킬로바이트에서 몇 줄로 줄어드는 거죠.
프로그래매틱 도구 호출은 도구 버전 code_execution_20260120 이상의 코드 실행 도구를 필요로 해요.
빠른 시작 (Quick start)¶
Claude가 데이터베이스를 여러 번 질의하고 결과를 집계하는 예시를 볼게요. 도구 정의에 allowed_callers: ["code_execution_20260120"]를 추가하는 것, 바로 그 한 줄이 그 도구를 코드 실행 안에서 호출 가능하게 만들어요(allowed_callers 필드 참고).
cURL / CLI / Python / TypeScript / C# / Go / Java / PHP / Ruby
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=[
{
"role": "user",
"content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue",
}
],
tools=[
{"type": "code_execution_20260120", "name": "code_execution"},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL query to execute"}
},
"required": ["sql"],
},
"allowed_callers": ["code_execution_20260120"],
},
],
)
print(response)
응답은 stop_reason: "tool_use"로 멈추고, container ID와 함께 query_database용 tool_use 블록이 나와요. 이 블록의 caller 필드가 어떤 코드 실행이 그 호출을 했는지를 알려줘요. 예제 워크플로 3단계처럼 결과를 돌려주면 코드가 끝까지 실행될 수 있어요.
프로그래매틱 도구 호출이 동작하는 방식¶
도구를 코드 실행에서 호출 가능하도록 설정했고, Claude가 그 도구가 필요하다고 판단하면 이런 일이 일어나요.
- Claude가 그 도구를 함수로 호출하는 Python 코드를 작성해요. 여러 도구 호출과 전/후처리 로직이 포함될 수 있어요.
- Claude는 이 코드를 코드 실행을 통해 샌드박스 컨테이너에서 실행해요.
- 도구 함수가 호출되면 코드 실행이 잠시 멈추고, API는
tool_use블록을 반환해요. - 여러분이 도구 결과를 제공하면 코드 실행이 계속돼요. 중간 결과는 Claude의 컨텍스트 창에 로드되지 않아요.
- 코드 실행이 전부 끝나면 Claude가 최종 출력을 받아 작업을 계속해요.
이 방식이 특히 유용한 경우를 볼게요.
- 대용량 데이터 처리: 도구 결과가 Claude의 컨텍스트에 들어오기 전에 필터링하거나 집계해요.
- 다단계 워크플로: 도구 호출 사이마다 Claude를 샘플링하지 않고 직렬로 또는 반복문으로 호출해서 토큰과 지연 시간을 아껴요.
- 조건부 로직: 중간 도구 결과를 바탕으로 결정을 내려요.
핵심 개념¶
allowed_callers 필드¶
allowed_callers 필드는 어떤 컨텍스트에서 도구를 호출할 수 있는지를 지정해요.
{
"name": "query_database",
"description": "Execute a SQL query against the database",
"input_schema": {
// ...
},
"allowed_callers": ["code_execution_20260120"]
}
가능한 값:
["direct"]— Claude가 이 도구를 직접 호출하도록 안내돼요(생략하면 기본값).["code_execution_20260120"]— Claude가 이 도구를 코드 실행 안에서만 호출하도록 안내돼요.["direct", "code_execution_20260120"]— Claude가 이 도구를 직접 또는 코드 실행 안에서 호출할 수 있어요.
allowed_callers에는 "code_execution_20260120"과 "code_execution_20260521" 둘 다 쓸 수 있고, 서로 바꿔 쓸 수 있어요. 어떤 쪽 코드 실행 도구 버전으로 요청하든 두 caller를 나열한 도구를 충족시켜요. 다만 응답 블록은 요청이 어떤 버전을 선언했든 항상 caller를 code_execution_20260120으로 표시해요.
응답의 caller 필드¶
모든 도구 사용 블록에는 호출 방식을 알려주는 caller 필드가 들어가요.
직접 호출(전통적인 도구 사용):
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "query_database",
"input": { "sql": "<sql>" },
"caller": { "type": "direct" }
}
프로그래매틱 호출:
{
"type": "tool_use",
"id": "toolu_xyz789",
"name": "query_database",
"input": { "sql": "<sql>" },
"caller": {
"type": "code_execution_20260120",
"tool_id": "srvtoolu_abc123"
}
}
여기서 tool_id는 호출을 만든 코드 실행 server_tool_use 블록의 id예요. 그래서 각 프로그래매틱 tool_use를 그것을 만들어낸 코드 실행과 짝지을 수 있어요.
컨테이너 수명 주기¶
프로그래매틱 도구 호출은 코드 실행과 같은 컨테이너를 사용해요.
- 컨테이너 생성: 기존 컨테이너를 재사용하지 않는 한, 요청마다 새 컨테이너가 생성돼요.
- 컨테이너 ID: 응답의
container필드에expires_at타임스탬프와 함께 반환돼요. - 재사용: 다음 요청에 컨테이너 ID를 다시 넘기면 상태를 유지해요. 프로그래매틱 도구 호출이 여러분의 결과를 기다리는 동안에는 이 요청에서 컨테이너 ID가 선택이 아니라 필수예요. 없으면 API가 그 요청을 거부해요.
- 만료:
expires_at이 컨테이너에 남은 시간을 알려줘요. 유휴 컨테이너는 현재 약 5분 후 회수되고, 생성 후 30일이 지나면 어떤 컨테이너도 재사용할 수 없어요.
예제 워크플로¶
완전한 프로그래매틱 도구 호출 흐름이 어떻게 흘러가는지 단계별로 볼게요.
1단계: 초기 요청¶
코드 실행과 프로그래매틱 호출을 허용하는 도구로 요청을 보내요. 프로그래매틱 호출을 활성화하려면 도구 정의에 allowed_callers 필드를 추가하면 돼요.
요청 형태는 빠른 시작 예시와 동일해요. tools 목록에 code_execution을 포함하고, 코드에서 호출하고 싶은 도구에 allowed_callers: ["code_execution_20260120"]를 추가한 다음 사용자 메시지를 보내면 돼요. 이 워크플로의 나머지 단계에서는 사용자 메시지 "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"를 쓸게요.
2단계: 도구 호출이 포함된 API 응답¶
Claude가 여러분의 도구를 호출하는 코드를 작성해요. API는 멈추고 다음을 반환해요.
Output
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll query the purchase history and analyze the results."
},
{
"type": "server_tool_use",
"id": "srvtoolu_abc123",
"name": "code_execution",
"input": {
"code": "import json\n\nrows = json.loads(await query_database({'sql': '<sql>'}))\ntop_customers = sorted(rows, key=lambda x: x['revenue'], reverse=True)[:5]\nprint(f'Top 5 customers: {top_customers}')"
}
},
{
"type": "tool_use",
"id": "toolu_def456",
"name": "query_database",
"input": { "sql": "<sql>" },
"caller": {
"type": "code_execution_20260120",
"tool_id": "srvtoolu_abc123"
}
}
],
"container": {
"id": "container_xyz789",
"expires_at": "2026-01-20T14:30:00Z"
},
"stop_reason": "tool_use"
}
3단계: 도구 결과 제공¶
전체 대화 기록에 도구 결과를 더해서 보내요. 이 요청에서 중요한 세 가지가 있어요.
- 결과를 담는 사용자 메시지는
tool_result블록만 담을 수 있어요. 메시지 포맷 제약 참고. - 멈춘 응답의
containerID를 넘겨요. 처리 대기 중인 프로그래매틱 도구 호출이 있는데 컨테이너 ID가 없으면 API가 그 연속 요청을 거부해요. - 원래 요청과 같은
tools배열을 보내요. 멈춘 코드를 재개하려면 코드 실행 도구가 여전히 있어야 해요. 그리고 이 요청에서 보내는 도구 정의는 남은 턴 동안 Claude와 실행 중인 코드가 사용할 수 있는 정의가 돼요.
cURL / CLI / Python / TypeScript / C# / Go / Java / PHP / Ruby
response = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
container="container_xyz789", # Reuse the container
messages=[
{
"role": "user",
"content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue",
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll query the purchase history and analyze the results.",
},
{
"type": "server_tool_use",
"id": "srvtoolu_abc123",
"name": "code_execution",
"input": {"code": "..."},
},
{
"type": "tool_use",
"id": "toolu_def456",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {
"type": "code_execution_20260120",
"tool_id": "srvtoolu_abc123",
},
},
],
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_def456",
"content": '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]',
}
],
},
],
# Same tools array as the original request
tools=[
{"type": "code_execution_20260120", "name": "code_execution"},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL query to execute"}
},
"required": ["sql"],
},
"allowed_callers": ["code_execution_20260120"],
},
],
)
4단계: 다음 도구 호출 또는 완료¶
코드는 멈춘 지점에서 이어서 여러분의 결과를 처리해요. 각 연속 응답은 더 많은 프로그래매틱 tool_use 블록과 함께 다시 멈추거나, 코드 실행을 완료하고 Claude가 턴을 이어가게 해요(5단계). 둘을 구분하려면 stop_reason과 각 tool_use 블록의 caller를 확인하면 돼요. 여러분을 위해 멈춘 응답은 stop_reason: "tool_use"이고, caller가 코드 실행 버전을 가리키는 tool_use 블록을 가져요. 그럴 때는 처리 대기 중인 모든 프로그래매틱 호출에 대해 사용자 메시지 하나에 tool_result를 넣어 3단계를 반복하면 돼요.
5단계: 최종 응답¶
코드 실행이 완료되면 Claude가 최종 응답을 제공해요.
Output
{
"content": [
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc123",
"content": {
"type": "code_execution_result",
"stdout": "Top 5 customers: [{'customer_id': 'C1', 'revenue': 45000}, {'customer_id': 'C2', 'revenue': 38000}, {'customer_id': 'C5', 'revenue': 32000}, {'customer_id': 'C8', 'revenue': 28500}, {'customer_id': 'C3', 'revenue': 24000}]",
"stderr": "",
"return_code": 0,
"content": []
}
},
{
"type": "text",
"text": "I've analyzed the purchase history from last quarter. Your top 5 customers generated $167,500 in total revenue, with Customer C1 leading at $45,000."
}
],
"stop_reason": "end_turn"
}
고급 패턴¶
반복문을 이용한 배치 처리¶
Claude는 여러 항목을 효율적으로 처리하는 코드를 작성할 수 있어요.
regions = ["West", "East", "Central", "North", "South"]
results = {}
for region in regions:
rows = json.loads(await query_database({"sql": f"<sql for {region}>"}))
results[region] = sum(row["revenue"] for row in rows)
# Process results programmatically
top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")
이 패턴이 주는 이점이에요.
- 모델 왕복 횟수를 N번(지역마다 한 번)에서 1번으로 줄여요.
- 큰 결과 집합을 Claude로 돌려보내기 전에 프로그램적으로 처리해요.
- 원시 데이터 대신 집계된 결론만 반환해서 토큰을 아껴요.
조기 종료¶
Claude는 성공 기준이 충족되는 즉시 처리를 멈출 수 있어요.
endpoints = ["us-east", "eu-west", "apac"]
for endpoint in endpoints:
status = await check_health({"endpoint": endpoint})
if status == "healthy":
print(f"Found healthy endpoint: {endpoint}")
break # Stop early, don't check remaining
조건부 도구 선택¶
path = "/tmp/example.txt"
file_info = json.loads(await get_file_info({"path": path}))
if file_info["size"] < 10000:
content = await read_full_file({"path": path})
else:
content = await read_file_summary({"path": path})
print(content)
데이터 필터링¶
server_id = "srv-01"
log_text = await fetch_logs({"server_id": server_id})
errors = [line for line in log_text.splitlines() if "ERROR" in line]
print(f"Found {len(errors)} errors")
for error in errors[-10:]: # Only return last 10 errors
print(error)
응답 형식¶
프로그래매틱 도구 호출¶
코드 실행이 도구를 호출할 때:
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "query_database",
"input": { "sql": "<sql>" },
"caller": {
"type": "code_execution_20260120",
"tool_id": "srvtoolu_xyz789"
}
}
도구 결과 처리¶
여러분의 도구 결과는 실행 중인 코드로 돌아가요.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000, \"orders\": 23}, {\"customer_id\": \"C2\", \"revenue\": 38000, \"orders\": 18}, ...]"
}
]
}
코드 실행 완료¶
모든 도구 호출이 충족되고 코드가 완료되면:
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_xyz789",
"content": {
"type": "code_execution_result",
"stdout": "Analysis complete. Top 5 customers identified from 847 total records.",
"stderr": "",
"return_code": 0,
"content": []
}
}
오류 처리¶
흔한 오류¶
| 오류 | 나타나는 위치 | 설명 | 해결책 |
|---|---|---|---|
invalid_tool_input |
응답의 code_execution_tool_result 오류 블록에 있는 error_code |
코드 실행 도구에 잘못된 매개변수가 전달됨 | 코드 실행 도구 오류 참고 |
invalid_request_error (tool_choice에 대해) |
HTTP 400 오류 응답 | tool_choice가 allowed_callers에 "direct"가 포함되지 않은 도구를 지목함 |
그 도구의 allowed_callers에 "direct"를 추가하거나, 그 도구를 tool_choice에서 빼고 Claude가 코드에서 호출하게 둠 |
도구 호출 중 컨테이너 만료¶
도구 결과가 약 4분 안에 도착하지 않으면, 처리 대기 중인 호출이 Claude의 실행 코드 안에서 TimeoutError를 일으켜요. Claude는 stderr에서 그 오류를 보고 보통 호출을 다시 시도해요.
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc123",
"content": {
"type": "code_execution_result",
"stdout": "",
"stderr": "TimeoutError: Calling tool ['query_database'] timed out (no response after 270s).",
"return_code": 0,
"content": []
}
}
타임아웃을 막으려면:
- 응답의
expires_at필드를 주시해요. - 도구 실행에 타임아웃을 구현해요.
- 긴 작업을 더 작은 조각으로 나누는 것도 고려해요.
도구 실행 오류¶
도구가 오류를 반환하면:
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"content": "Error: Query timeout - table lock exceeded 30 seconds"
}
Claude의 코드는 이 오류를 받아서 적절히 처리할 수 있어요.
제약 사항과 한계¶
기능 비호환성¶
- 구조화된 출력(Structured outputs):
strict: true인 도구는 프로그래매틱 호출에서 지원되지 않아요. - 도구 선택(Tool choice):
tool_choice로 특정 도구의 프로그래매틱 호출을 강제할 수 없어요. - 병렬 도구 사용(Parallel tool use):
disable_parallel_tool_use: true는 프로그래매틱 호출에서 지원되지 않아요.
입력 스키마 한계¶
input_schema에 재귀적 $ref(자기 자신을 참조하는 참조 순환 등)가 들어 있는 커스텀 도구는 프로그래매틱 호출을 활성화할 수 없어요. 그런 도구의 allowed_callers에 코드 실행 도구 버전을 포함하면 Circular $ref detected 메시지가 담긴 400 invalid_request_error로 요청이 실패해요. 같은 스키마라도 직접 도구 호출에는 허용돼요.
이 문제를 우회하려면 다음 중 하나를 해요.
allowed_callers를 생략하거나["direct"]로 설정해서 그 도구를 직접 전용(direct-only)으로 둬요. 같은 요청의 다른 도구들은 여전히 프로그래매틱 호출을 쓸 수 있어요.- 스키마에서 순환을 없애요. 예를 들어 재귀를 고정 깊이로 풀고, 더 깊은 중첩을 가장 안쪽 레벨의
description에 설명하거나, 재귀적 속성을 기대하는 형태를 설명하는description이 달린 평범한{"type": "object"}로 바꿔요.
도구 제약¶
다음 도구들은 프로그래매틱으로 호출할 수 없어요.
- MCP 커넥터가 제공하는 도구
- 컴퓨터 사용(computer use)과 브라우저 사용(browser use) 도구셋(
computer_toolset_20260801과browser_toolset_20260801) — 이들의allowed_callers필드는"direct"만 받아요.
메시지 포맷 제약¶
프로그래매틱 도구 호출에 응답할 때는 엄격한 포맷 요구 사항이 있어요.
도구 결과 전용 응답: 처리 대기 중인 프로그래매틱 도구 호출이 결과를 기다리고 있다면, 응답 메시지는 tool_result 블록만 담아야 해요. 도구 결과 뒤에라도 어떤 텍스트 콘텐츠도 넣을 수 없어요.
잘못된 예 — 프로그래매틱 도구 호출에 응답할 때 텍스트를 넣으면 안 됨:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"
},
{ "type": "text", "text": "What should I do next?" }
]
}
유효한 예 — 프로그래매틱 도구 호출에 응답할 때 도구 결과만:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"
}
]
}
이 제약은 프로그래매틱(코드 실행) 도구 호출에 응답할 때만 적용돼요. 일반적인 클라이언트 측 도구 호출에서는 도구 결과 뒤에 텍스트 콘텐츠를 넣을 수 있어요.
텍스트 전용 도구 결과: 프로그래매틱 호출에 답하는 각 tool_result의 content는 문자열이거나 text 블록이어야 해요. 이미지, 문서, 기타 콘텐츠 블록 유형은 거부돼요.
속도 제한¶
프로그래매틱 도구 호출은 일반 도구 호출과 같은 속도 제한을 받아요. 코드 실행의 각 도구 호출은 별개의 호출로 집계돼요.
사용 전에 도구 결과 검증하기¶
프로그래매틱으로 호출될 사용자 정의 도구를 구현할 때:
- 도구 결과는 문자열로 반환돼요. 실행 환경이 처리할 수도 있는 코드 조각이나 실행 가능한 명령을 포함해 어떤 콘텐츠든 담을 수 있어요.
- 외부 도구 결과를 검증해요. 도구가 외부 소스의 데이터를 반환하거나 사용자 입력을 받는다면, 출력이 코드로 해석되거나 실행될 때 코드 주입 위험을 인지해야 해요.
토큰 효율성¶
프로그래매틱 도구 호출은 세 가지 방식으로 토큰 소비를 줄여요.
- 프로그래매틱 호출의 도구 결과는 Claude의 컨텍스트에 추가되지 않아요. 최종 코드 출력만 추가돼요.
- 중간 처리는 코드 안에서 일어나요. 필터링, 집계, 그 밖의 변환은 모델 토큰을 소비하지 않아요.
- 한 번의 코드 실행에서 여러 도구 호출. 별도의 모델 턴에 비해 오버헤드가 줄어요.
예를 들어 10개의 도구를 직접 호출하면, 프로그램적으로 호출하고 요약을 반환할 때보다 토큰이 약 10배 많이 들어요.
Anthropic의 내부 평가(프로덕션 Claude 모델 기준)에서:
- 75개 도구 프로젝트 관리 에이전트 벤치마크에서 프로그래매틱 도구 호출을 켜면 청구 입력 토큰이 약 38% 줄었고 작업 정확도는 변함이 없었어요.
- τ²-bench(항공, 소매, 통신 도메인)에서는 각 턴이 도구 호출을 한두 번 순차적으로 하는데, 프로그래매틱 도구 호출이 점수는 그대로 두고 비용은 약 8% 더 들었어요. 순차적 단일 호출 워크플로는 이점이 없어요.
- 프로덕션 API 트래픽 전체에서
tools배열에 도구 정의가 10~49개 있는 요청은 프로그래매틱 도구 호출을 켜면 보통 20%~40%의 토큰 절감을 봐요.
실제 절감 폭은 워크로드 형태에 따라 달라져요. 언제 프로그래매틱 호출을 쓸까를 보세요.
사용량과 요금¶
프로그래매틱 도구 호출은 코드 실행과 같은 요금을 사용해요. 자세한 내용은 코드 실행 요금을 보세요.
모범 사례¶
도구 설계¶
- 상세한 출력 설명을 제공해요. Claude는 도구 결과를 코드에서 역직렬화하므로, 형식(JSON 구조와 필드 타입)을 문서화해요.
- 구조화된 데이터를 반환해요. JSON이나 그 밖의 기계가 읽을 수 있는 형식이 프로그래매틱 처리에 가장 잘 맞아요.
- 응답을 간결하게 유지해요. 처리 오버헤드를 최소화하기 위해 필요한 데이터만 반환해요.
언제 프로그래매틱 호출을 쓸까¶
프로그래매틱 도구 호출은 작은 고정 오버헤드(컨테이너 시작, 스크립트 생성)를 들여서 도구 결과 토큰과 모델 왕복에서 큰 절감을 얻는 거예요. 그 거래가 성사되는지는 워크로드 형태에 달려 있어요.
강하게 어울리는 경우:
- 많은 항목에 걸친 팬아웃이나 병렬 연산(예: 50개 엔드포인트 점검, 20개 레코드 조회).
- Claude의 컨텍스트에 들어오기 전에 필터링·집계·요약할 수 있는 커다란 도구 결과.
- 에이전틱 검색과 검색(iterative querying과 결과 필터링이 워크플로를 지배하는 경우).
약하게 어울리는 경우:
- 각 호출이 이전 결과에 대한 Claude의 추론에 의존하는 엄격한 순차 워크플로 — 그 경우 스크립트가 모델 왕복을 건너뛸 수 없기 때문이에요.
- 작은 응답을 가진 도구 호출이 몇 개 안 되는 경우, 특히 대화의 첫 턴이면 컨테이너와 스크립트 오버헤드가 절감을 넘어설 수 있어요.
- 호출 사이에 즉각적인 사용자 피드백이 필요한 도구.
확신이 안 서면, 트래픽의 대표 표본에서 allowed_callers 유무에 따른 청구 입력 토큰을 측정해 보고 광범위하게 켜기 전에 판단해요.
성능 최적화¶
- 여러 관련 요청을 할 때 컨테이너를 재사용해서 상태를 유지해요.
- 가능하면 유사한 연산을 한 번의 코드 실행으로 배치해요.
문제 해결¶
흔한 문제¶
tool_choice를 설정할 때 invalid_request_error
tool_choice는allowed_callers에"direct"가 없는 도구를 지목할 수 없어요. 그 도구의allowed_callers에"direct"를 추가하거나, 그 도구를tool_choice에서 빼고 Claude가 코드에서 호출하게 두면 돼요.
컨테이너 만료
- 멈춘 응답의
expires_at타임스탬프보다 훨씬 전에 각 프로그래매틱 도구 호출에 응답해요. Claude의 코드는 약 4분 후 결과를 기다리는 걸 멈추고, 유휴 컨테이너는 현재 약 5분 후 회수돼요. - 더 빠른 도구 실행을 구현해 보는 것도 좋아요.
도구 결과가 제대로 파싱되지 않음
- 도구가 Claude가 역직렬화할 수 있는 문자열 데이터를 반환하도록 해요.
- 도구 설명에 명확한 출력 형식 문서를 제공해요.
디버깅 팁¶
- 흐름을 추적하려고 모든 도구 호출과 결과를 로그로 남겨요.
- 프로그래매틱 호출임을 확인하려고
caller필드를 점검해요. - 제대로 재사용되는지 컨테이너 ID를 모니터링해요.
- 프로그래매틱 호출을 켜기 전에 도구를 독립적으로 테스트해요.
프로그래매틱 도구 호출이 잘 동작하는 이유¶
Claude는 방대한 양의 코드로 학습됐어요. 그래서 도구를 호출 가능한 Python 함수로 제시하면 그 강점을 활용할 수 있어요.
- 도구 구성: 연결된 호출, 반복문, 조건문이 모델 왕복의 연속 대신 평범한 Python 제어 흐름이 돼요.
- 결과 처리: Claude의 코드가 커다란 도구 출력을 필터링하고 집계하거나 파일로 써요. 컨텍스트 창에는 최종 출력만 들어와요.
- 지연 시간: 한 번의 코드 실행 안에서 도구 호출 사이에 모델을 다시 샘플링하지 않아요.
대체 구현¶
프로그래매틱 도구 호출은 일반화 가능한 패턴이라 여러분의 자체 인프라에서도 구현할 수 있어요. 접근 방식들을 비교해 볼게요.
클라이언트 측 직접 실행¶
Claude에 코드 실행 도구를 제공하고, 그 환경에서 어떤 함수를 쓸 수 있는지 설명해요. Claude가 코드와 함께 도구를 호출하면, 여러분의 애플리케이션이 그 함수가 정의된 곳에서 로컬로 실행해요.
장점:
- 애플리케이션 재구성이 거의 필요 없어요.
- 환경과 지침을 완전히 제어할 수 있어요.
단점:
- 샌드박스 밖에서 신뢰할 수 없는 코드를 실행해요.
- 도구 호출이 코드 주입의 벡터가 될 수 있어요.
이런 경우에 써요: 애플리케이션이 임의 코드를 안전하게 실행할 수 있고, 최소한의 구현을 원하며, Anthropic의 관리형 제공이 여러분의 필요에 맞지 않을 때.
자체 관리 샌드박스 실행¶
Claude의 관점에서는 같은 접근 방식이지만, 코드는 보안 제약(예: 네트워크 송신 불가)이 있는 샌드박스 컨테이너에서 실행돼요. 도구가 외부 리소스를 필요로 한다면, 샌드박스 밖에서 도구 호출을 실행하는 프로토콜이 필요해요.
장점:
- 여러분의 자체 인프라에서 안전한 프로그래매틱 도구 호출.
- 실행 환경을 완전히 제어.
단점:
- 구축과 유지가 복잡해요.
- 인프라와 프로세스 간 통신을 모두 관리해야 해요.
이런 경우에 써요: 보안이 결정적이고 Anthropic의 관리형 솔루션이 요구 사항에 맞지 않을 때.
Anthropic 관리형 실행¶
Anthropic의 프로그래매틱 도구 호출은 샌드박스 실행의 관리형 버전으로, Claude에 맞게 조정된 주관적인 Python 환경을 써요. Anthropic이 컨테이너 관리, 코드 실행, 안전한 도구 호출 통신을 처리해요.
장점:
- 기본적으로 안전하고 보안이 확보돼요.
- 도구 정의 하나로 활성화되고, 실행할 인프라가 없어요.
- 환경과 지침이 Claude에 최적화돼 있어요.
Claude API, Claude Platform on AWS, 또는 Microsoft Foundry를 쓰고 있다면 Anthropic의 관리형 솔루션을 고려해 보세요. Microsoft Foundry에서는 Azure에 호스팅된 경우 지원되지 않는 추가 기능으로 프로그래매틱 도구 호출이 Hosted on Anthropic 배포를 필요로 해요.
데이터 보존¶
프로그래매틱 도구 호출은 코드 실행 인프라 위에 구축되고, 같은 샌드박스 컨테이너를 사용해요. 실행 산출물과 출력을 포함한 컨테이너 데이터는 최대 30일 동안 보존돼요.
모든 기능의 ZDR 자격에 대해서는 API 및 데이터 보존을 보세요.
다음 단계¶
세밀한 도구 스트리밍 (Fine-grained tool streaming)
서버 측 JSON 버퍼링 없이 도구 입력을 스트리밍해서 지연 시간에 민감한 애플리케이션에 적합해요.
코드 실행 도구 (Code execution tool)
샌드박스 컨테이너에서 Python과 bash 코드를 실행해 데이터를 분석하고, 파일을 만들고, 솔루션을 반복 개선해요.
Claude와 함께하는 도구 사용 (Tool use with Claude)
Claude를 외부 도구와 API에 연결해요. 도구가 어디서 실행되는지, Claude가 언제 호출하는지, 어떤 도구가 작업에 맞는지 살펴봐요.
도구 스키마를 지정하고, 효과적인 설명을 쓰고, Claude가 도구를 호출하는 시점을 제어해요.
호환성¶
| 지원 모델 | - Fable 5 and 5.1 - Mythos 5 and 5.1 - Opus 4.5, 4.6, 4.7, 4.8, and 5 - Sonnet 4.5, 4.6, and 5 |
|---|---|
| 지원 플랫폼 | - Claude API - Claude Platform on AWS - Microsoft Foundry1 |
-
Microsoft Foundry에서는 프로그래매틱 도구 호출이 Hosted on Anthropic 배포를 필요로 해요. ↩
-
프로그래매틱 도구 호출은
code_execution_20260120이상 도구 버전의 코드 실행 도구를 필요로 해요. - Claude Haiku 4.5는
code_execution_20260120이상 도구 버전을 받지만 프로그래매틱 도구 호출은 지원하지 않아요.