본문 바로가기
개발&프로그래밍

[Claude] Claude API Tool Use (Function Calling) 구현하기

by 재아군 2026. 2. 10.
반응형

 

Claude API의 가장 강력한 기능 중 하나는 Tool Use(툴 사용)입니다.

이를 통해 Claude는 외부 API 호출, 데이터베이스 조회, 계산 수행 등 사전 정의된 함수를 실행할 수 있습니다.

이번 가이드에서는 Tool Use의 개념부터 실전 구현까지, Claude를 진정한 AI 에이전트로 변신시키는 방법을 단계별로 알아보겠습니다.

Tool Use란?

Tool Use(또는 Function Calling)는 Claude가 필요에 따라 외부 함수나 API를 호출할 수 있도록 하는 기능입니다.

 

기본 개념

일반적인 Claude 사용:

사용자: "샌프란시스코 날씨 알려줘"
Claude: "죄송하지만 실시간 날씨 정보에 접근할 수 없습니다..."

Tool Use를 사용한 Claude:

사용자: "샌프란시스코 날씨 알려줘"
Claude: [get_weather 함수 호출] → API에서 날씨 정보 가져옴
Claude: "샌프란시스코는 현재 15도이며 맑은 날씨입니다."

 

Tool의 두 가지 유형

1. Client Tools (클라이언트 도구)

  • 개발자가 직접 구현한 함수
  • 사용자 시스템에서 실행
  • 예: 데이터베이스 조회, API 호출, 파일 읽기

2. Server Tools (서버 도구)

  • Anthropic 서버에서 자동 실행
  • 구현 불필요
  • 예: Web Search (웹 검색), Web Fetch (웹페이지 가져오기)

이 글에서는 Client Tools 구현에 집중합니다.

 

Tool Use 작동 원리

Tool Use는 4단계로 작동합니다:

1. [개발자] → Claude에게 사용 가능한 도구 목록 제공
2. [Claude]  → 사용자 질문 분석 후 필요한 도구 선택 및 파라미터 생성
3. [개발자] → 선택된 도구 실행 후 결과 반환
4. [Claude]  → 도구 결과를 바탕으로 최종 답변 생성

 

첫 번째 Tool 구현

1단계: 도구 정의

도구는 다음 3가지 요소로 정의됩니다:

tool = {
    "name": "get_weather",           # 도구 이름 (함수명처럼 작성)
    "description": "현재 날씨 조회", # Claude가 이해할 수 있는 설명
    "input_schema": {                # JSON Schema로 파라미터 정의
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "도시명, 예: Seoul, KR"
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "온도 단위"
            }
        },
        "required": ["location"]  # 필수 파라미터
    }
}

 

2단계: Claude에게 도구 전달

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[
        {
            "name": "get_weather",
            "description": "Get the current weather in a given location",
            "input_schema": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"]
            }
        }
    ],
    messages=[
        {"role": "user", "content": "What's the weather in Seoul?"}
    ]
)

print(response)

 

3단계: Claude의 응답 처리

Claude는 다음과 같이 응답합니다:

{
  "id": "msg_01Aq9w938a90dw8q",
  "model": "claude-opus-4-6",
  "stop_reason": "tool_use",  # 도구 사용을 원한다는 신호
  "content": [
    {
      "type": "text",
      "text": "I'll check the weather in Seoul for you."
    },
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835lq9",  # 도구 호출 ID
      "name": "get_weather",
      "input": {
        "location": "Seoul, KR",
        "unit": "celsius"
      }
    }
  ]
}

중요: stop_reason"tool_use"이면 Claude가 도구 실행을 원합니다!

 

4단계: 도구 실행 및 결과 반환

import requests

def get_weather(location, unit="celsius"):
    """실제 날씨 API 호출 함수"""
    # 여기서는 예시를 위해 하드코딩
    # 실제로는 OpenWeatherMap 등의 API 호출
    return f"15 degrees {unit}"

# Claude의 응답에서 tool_use 추출
tool_use = None
for block in response.content:
    if block.type == "tool_use":
        tool_use = block
        break

# 도구 실행
result = get_weather(
    location=tool_use.input["location"],
    unit=tool_use.input.get("unit", "celsius")
)

# Claude에게 결과 반환
final_response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[...],  # 동일한 도구 정의
    messages=[
        {"role": "user", "content": "What's the weather in Seoul?"},
        {
            "role": "assistant",
            "content": response.content  # Claude의 이전 응답
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_use.id,  # 매칭되는 ID
                    "content": result             # 도구 실행 결과
                }
            ]
        }
    ]
)

print(final_response.content[0].text)
# 출력: "The current weather in Seoul is 15 degrees Celsius."

 

실전 예제

예제 1: 계산기 도구

복잡한 수학 계산을 정확하게 수행:

import anthropic
import math

client = anthropic.Anthropic()

# 계산기 도구 정의
calculator_tool = {
    "name": "calculator",
    "description": "수학 계산 수행. 지원 연산: add, subtract, multiply, divide, sqrt, power",
    "input_schema": {
        "type": "object",
        "properties": {
            "operation": {
                "type": "string",
                "enum": ["add", "subtract", "multiply", "divide", "sqrt", "power"],
                "description": "수행할 연산"
            },
            "a": {
                "type": "number",
                "description": "첫 번째 숫자"
            },
            "b": {
                "type": "number",
                "description": "두 번째 숫자 (sqrt는 불필요)"
            }
        },
        "required": ["operation", "a"]
    }
}

def calculator(operation, a, b=None):
    """계산 실행 함수"""
    if operation == "add":
        return a + b
    elif operation == "subtract":
        return a - b
    elif operation == "multiply":
        return a * b
    elif operation == "divide":
        return a / b
    elif operation == "sqrt":
        return math.sqrt(a)
    elif operation == "power":
        return a ** b

def process_tool_call(user_message):
    """도구 호출 처리 전체 로직"""
    # 1. 초기 요청
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=[calculator_tool],
        messages=[{"role": "user", "content": user_message}]
    )

    # 2. Tool use 확인
    if response.stop_reason != "tool_use":
        return response.content[0].text

    # 3. 도구 실행
    tool_use = next(block for block in response.content if block.type == "tool_use")

    result = calculator(
        operation=tool_use.input["operation"],
        a=tool_use.input["a"],
        b=tool_use.input.get("b")
    )

    # 4. 결과 반환
    final_response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=[calculator_tool],
        messages=[
            {"role": "user", "content": user_message},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_use.id,
                        "content": str(result)
                    }
                ]
            }
        ]
    )

    return final_response.content[0].text

# 사용 예시
print(process_tool_call("123.45 곱하기 678.90은 얼마인가요?"))
# 출력: "123.45 곱하기 678.90은 83,810.055입니다."

 

예제 2: 데이터베이스 조회 도구

사용자 정보를 데이터베이스에서 조회:

import anthropic
import sqlite3

client = anthropic.Anthropic()

# 데이터베이스 초기화 (예시)
def init_db():
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS users
                 (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER)''')
    c.execute("INSERT OR IGNORE INTO users VALUES (1, 'Alice', 'alice@example.com', 30)")
    c.execute("INSERT OR IGNORE INTO users VALUES (2, 'Bob', 'bob@example.com', 25)")
    conn.commit()
    conn.close()

init_db()

# 데이터베이스 조회 도구
db_query_tool = {
    "name": "query_user",
    "description": "사용자 ID로 사용자 정보 조회",
    "input_schema": {
        "type": "object",
        "properties": {
            "user_id": {
                "type": "integer",
                "description": "조회할 사용자 ID"
            }
        },
        "required": ["user_id"]
    }
}

def query_user(user_id):
    """데이터베이스에서 사용자 조회"""
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute("SELECT * FROM users WHERE id = ?", (user_id,))
    result = c.fetchone()
    conn.close()

    if result:
        return f"ID: {result[0]}, 이름: {result[1]}, 이메일: {result[2]}, 나이: {result[3]}"
    else:
        return "사용자를 찾을 수 없습니다."

# 전체 대화 처리
conversation = []

def chat_with_db(user_message):
    conversation.append({"role": "user", "content": user_message})

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=[db_query_tool],
        messages=conversation
    )

    # Tool use 처리
    if response.stop_reason == "tool_use":
        conversation.append({"role": "assistant", "content": response.content})

        for block in response.content:
            if block.type == "tool_use":
                result = query_user(user_id=block.input["user_id"])

                conversation.append({
                    "role": "user",
                    "content": [{
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    }]
                })

        # 최종 응답 생성
        final_response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            tools=[db_query_tool],
            messages=conversation
        )

        return final_response.content[0].text

    return response.content[0].text

# 사용 예시
print(chat_with_db("사용자 ID 1번의 정보를 알려주세요."))
# 출력: "사용자 ID 1번은 Alice이며, 이메일은 alice@example.com, 나이는 30세입니다."

 

예제 3: 여러 도구 사용 (병렬 호출)

Claude는 독립적인 작업을 병렬로 처리할 수 있습니다:

import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_weather",
        "description": "특정 도시의 날씨 조회",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {"type": "string"}
            },
            "required": ["location"]
        }
    },
    {
        "name": "get_time",
        "description": "특정 시간대의 현재 시간 조회",
        "input_schema": {
            "type": "object",
            "properties": {
                "timezone": {"type": "string"}
            },
            "required": ["timezone"]
        }
    }
]

def get_weather(location):
    # 실제로는 API 호출
    return f"{location}의 날씨: 맑음, 15도"

def get_time(timezone):
    # 실제로는 시간 계산
    return f"{timezone} 시간: 오후 3:00"

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{
        "role": "user",
        "content": "뉴욕의 날씨와 현재 시간을 알려주세요."
    }]
)

# 모든 tool_use 추출
tool_uses = [block for block in response.content if block.type == "tool_use"]

# 모든 도구를 실행하고 결과 수집
tool_results = []
for tool_use in tool_uses:
    if tool_use.name == "get_weather":
        result = get_weather(tool_use.input["location"])
    elif tool_use.name == "get_time":
        result = get_time(tool_use.input["timezone"])

    tool_results.append({
        "type": "tool_result",
        "tool_use_id": tool_use.id,
        "content": result
    })

# 모든 결과를 한 번에 반환
final_response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[
        {"role": "user", "content": "뉴욕의 날씨와 현재 시간을 알려주세요."},
        {"role": "assistant", "content": response.content},
        {"role": "user", "content": tool_results}
    ]
)

print(final_response.content[0].text)
# 출력: "뉴욕은 현재 맑은 날씨에 15도이며, 현지 시간은 오후 3:00입니다."

 

고급 패턴

1. 순차적 도구 호출

하나의 도구 결과를 다음 도구의 입력으로 사용:

tools = [
    {
        "name": "get_user_location",
        "description": "사용자의 현재 위치 조회 (IP 기반)",
        "input_schema": {
            "type": "object",
            "properties": {}  # 파라미터 없음
        }
    },
    {
        "name": "get_weather",
        "description": "특정 위치의 날씨 조회",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {"type": "string"}
            },
            "required": ["location"]
        }
    }
]

# 사용자: "내 위치의 날씨는?"
# Claude: get_user_location 호출 → 결과: "Seoul, KR"
# Claude: get_weather(location="Seoul, KR") 호출 → 결과: "15도"
# Claude: "서울은 현재 15도입니다."

 

2. 에러 처리

도구 실행 실패 시 Claude에게 에러 전달:

try:
    result = get_weather(location=tool_use.input["location"])
except Exception as e:
    # 에러를 Claude에게 전달
    tool_results.append({
        "type": "tool_result",
        "tool_use_id": tool_use.id,
        "content": f"Error: {str(e)}",
        "is_error": True  # 에러 플래그
    })

Claude는 에러를 이해하고 사용자에게 적절히 설명합니다:

"죄송합니다. 날씨 정보를 가져오는 중 오류가 발생했습니다.
잠시 후 다시 시도해주세요."

 

3. Tool Choice 제어

Claude의 도구 사용을 제어:

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[...],
    tool_choice={
        "type": "tool",
        "name": "get_weather"  # 반드시 get_weather 사용
    },
    messages=[...]
)

Tool Choice 옵션:

  • {"type": "auto"} (기본값): Claude가 자동 판단
  • {"type": "any"}: 반드시 어떤 도구든 사용
  • {"type": "tool", "name": "도구명"}: 특정 도구만 사용

 

4. Strict Tool Use (스키마 검증 보장)

프로덕션 환경에서 도구 파라미터 검증 보장:

tools = [
    {
        "name": "create_user",
        "description": "신규 사용자 생성",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "age": {"type": "integer", "minimum": 0}
            },
            "required": ["name", "age"]
        },
        "strict": True  # 스키마 검증 강제
    }
]

strict: true를 사용하면 Claude가 반드시 스키마를 정확히 따릅니다.

 

 

Best Practics

 

1. 명확한 도구 설명 작성

# ❌ 나쁜 예
"description": "Get weather"

# ✅ 좋은 예
"description": "Get the current weather conditions including temperature, humidity, and precipitation for a specified location. Returns data in real-time from weather APIs."

2. 파라미터 설명 상세화

"properties": {
    "location": {
        "type": "string",
        "description": "The city and state, e.g. San Francisco, CA or Seoul, South Korea. Use the format 'City, Country' for international locations."
    }
}

3. 도구 개수 제한

  • 권장: 5-10개 이하
  • 최대: 20개 (성능 저하 가능)

너무 많은 도구는 Claude의 선택을 어렵게 만듭니다.

4. 도구 결과는 간결하게

# ❌ 너무 장황한 결과
result = """
날씨 정보:
==========
온도: 15도
습도: 60%
강수량: 0mm
풍속: 3m/s
...
(100줄 이상)
"""

# ✅ 간결한 결과
result = "Temperature: 15°C, Humidity: 60%, Clear sky"

5. 도구 실행 타임아웃 설정

import requests
from requests.exceptions import Timeout

def call_external_api(url):
    try:
        response = requests.get(url, timeout=5)  # 5초 타임아웃
        return response.json()
    except Timeout:
        return "API 응답 시간 초과"

 

자주 묻는 질문 (FAQ)

 

Q1. Tool Use는 어떤 모델에서 사용 가능한가요?

A: 모든 Claude 모델(Opus, Sonnet, Haiku)에서 지원됩니다. 단, 모델별 성능 차이가 있습니다:

  • Opus 4.6/4.5: 복잡한 도구 체인 및 추론에 최적
  • Sonnet 4.5: 대부분의 도구 사용에 충분, 가성비 최고
  • Haiku 4.5: 간단한 도구 호출에 적합, 가장 빠름

 

Q2. 도구 비용은 얼마나 추가되나요?

A: 도구 정의는 추가 입력 토큰으로 계산됩니다:

모델 tool_choice: auto tool_choice: any
Opus 4.6 +346 토큰 +313 토큰
Sonnet 4.5 +346 토큰 +313 토큰
Haiku 4.5 +346 토큰 +313 토큰

예시 (Sonnet 4.5):

  • 도구 정의: 346 토큰 ($0.001)
  • 사용자 질문: 20 토큰 ($0.00006)
  • 도구 응답: 50 토큰 출력 ($0.00075)
  • 총 비용: ~$0.002 (약 3원)

 

Q3. 여러 도구를 동시에 호출할 수 있나요?

A: 네! Claude는 독립적인 작업을 병렬로 처리할 수 있습니다 (Parallel Tool Use). 예를 들어 "뉴욕 날씨와 시간"을 물으면 두 도구를 동시에 호출합니다.

 

Q4. 도구가 실패하면 어떻게 되나요?

A: is_error: true 플래그와 에러 메시지를 Claude에게 전달하면, Claude가 사용자에게 적절히 설명합니다:

{
    "type": "tool_result",
    "tool_use_id": "toolu_123",
    "content": "API rate limit exceeded",
    "is_error": True
}

 

Q5. Tool Use와 Structured Outputs의 차이는?

A:

기능 Tool Use Structured Outputs
목적 외부 함수 실행 JSON 형식 응답 생성
실행 주체 개발자 시스템 Claude
사용 사례 API 호출, DB 조회 데이터 추출, 폼 작성

함께 사용 가능: Tool Use로 데이터를 가져오고, Structured Outputs로 정리된 JSON 반환!

 

Q6. Claude가 도구를 사용하지 않으면?

A: 다음을 확인하세요:

  1. 도구 설명이 명확한가? - 모호한 설명은 Claude가 이해 못함
  2. 파라미터가 적절한가? - required 파라미터 확인
  3. 질문이 도구와 관련있는가? - "안녕하세요"는 도구 불필요
  4. tool_choice 설정 확인 - {"type": "any"}로 강제 가능

디버깅 팁: 시스템 프롬프트에 추가

system="도구를 최대한 활용하여 정확한 답변을 제공하세요."

 

실전 활용 시나리오

시나리오 1: 고객 지원 봇

tools = [
    {"name": "query_order_status", "description": "주문 상태 조회"},
    {"name": "update_shipping_address", "description": "배송지 변경"},
    {"name": "request_refund", "description": "환불 요청"},
    {"name": "search_faq", "description": "FAQ 검색"}
]

시나리오 2: 데이터 분석 에이전트

tools = [
    {"name": "query_database", "description": "SQL 쿼리 실행"},
    {"name": "generate_chart", "description": "차트 생성"},
    {"name": "calculate_statistics", "description": "통계 계산"},
    {"name": "export_report", "description": "보고서 내보내기"}
]

시나리오 3: 스마트 홈 제어

tools = [
    {"name": "control_lights", "description": "조명 제어"},
    {"name": "set_temperature", "description": "온도 설정"},
    {"name": "check_security", "description": "보안 상태 확인"},
    {"name": "play_music", "description": "음악 재생"}
]

 

 

 

결론 - Tool Use로 Claude를 진정한 AI 에이전트로

Tool Use는 Claude를 단순한 대화형 AI에서 실제 작업을 수행하는 에이전트로 진화시킵니다.

핵심 요약:

  1.  도구 정의: name, description, input_schema
  2.  stop_reason 확인: "tool_use"면 도구 실행 필요
  3.  결과 반환: tool_result로 도구 실행 결과 전달
  4.  에러 처리: is_error 플래그 활용
  5.  병렬 처리: 여러 도구를 동시에 실행 가능

다음 단계:
이제 Part 1 (Claude API 기초)을 완료했습니다! Part 2에서는 Claude Code를 다룹니다:

  • 6편: Claude Code 설치 및 시작하기
  • 7편: 핵심 명령어 & 슬래시 커맨드
  • 8편: VS Code 연동

📌 관련 글:

🔗 참고 자료:


Sources:

반응형

댓글