클로드 API 툴 사용(Function Calling) 입문: 재고 조회 예제로 이해하기

클로드 API 툴 사용(Function Calling) 입문: 재고 조회 예제로 이해하기 대표 이미지

클로드는 똑똑하지만 우리 회사 재고가 몇 개인지는 모릅니다. 실시간 데이터나 사내 시스템의 정보는 클로드의 지식 밖에 있죠. 이 간극을 메우는 것이 툴 사용(tool use, function calling)입니다. 클로드가 "이 질문에 답하려면 재고 조회 함수를 이 인수로 호출해야 해"라고 판단하면, 내 코드가 그 함수를 실행해 결과를 돌려주는 방식입니다.

전체 흐름

Claude API 툴 사용 전체 흐름

1. 나 → 클로드: 질문 + 사용할 수 있는 툴 목록
2. 클로드 → 나: "check_stock 툴을 sku='GB-CUP-01'로 호출해 줘" (stop_reason: tool_use)
3. 나: 실제로 함수 실행 → 결과: 재고 37개
4. 나 → 클로드: 툴 실행 결과 전달 (tool_result)
5. 클로드 → 나: "현재 GB-CUP-01 재고는 37개입니다." (최종 답변)

클로드는 결정만 하고, 실행은 내 코드가 합니다. 이 점이 보안상으로도 중요합니다. 어떤 함수를 실제로 실행할지는 끝까지 내가 통제합니다.

1단계. 툴 정의하기

툴은 이름, 설명, 입력 형식(JSON Schema)으로 정의합니다.

tools = [
    {
        "name": "check_stock",
        "description": "상품 코드(SKU)로 현재 창고 재고 수량을 조회한다. "
                       "고객이 재고, 품절 여부, 구매 가능 여부를 물을 때 사용한다.",
        "input_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string", "description": "상품 코드, 예: GB-CUP-01"}
            },
            "required": ["sku"],
        },
    }
]

설명(description)이 가장 중요합니다. 클로드는 이 설명을 읽고 언제 이 툴을 쓸지 판단합니다. "무엇을 하는지"와 함께 "언제 써야 하는지"를 적어 주세요.

2단계. 직접 루프 작성하기

툴 사용 흐름을 이해하기 위해 먼저 직접 작성해 봅니다.

import anthropic

client = anthropic.Anthropic()

STOCK = {"GB-CUP-01": 37, "GB-BAG-02": 0}

def check_stock(sku: str) -> str:
    if sku not in STOCK:
        return f"{sku}: 존재하지 않는 상품 코드"
    return f"{sku}: 재고 {STOCK[sku]}개"

messages = [{"role": "user", "content": "GB-BAG-02 지금 살 수 있나요?"}]

while True:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )
    messages.append({"role": "assistant", "content": response.content})

    if response.stop_reason != "tool_use":
        break

    results = []
    for block in response.content:
        if block.type == "tool_use" and block.name == "check_stock":
            output = check_stock(**block.input)
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": output,
            })
    messages.append({"role": "user", "content": results})

print("".join(b.text for b in response.content if b.type == "text"))

결과로 "GB-BAG-02는 현재 재고가 없어 구매가 어렵습니다. 재입고 알림을 신청해 보세요." 같은 답이 나옵니다.

코드의 핵심 포인트

  • stop_reason == "tool_use"이면 클로드가 툴 호출을 원하는 것입니다.
  • 클로드의 응답 전체(response.content)를 대화에 그대로 추가해야 합니다.
  • 툴 결과는 tool_use_id를 맞춰 user 메시지로 돌려줍니다.
  • 툴 호출이 여러 개면 결과도 한 번의 user 메시지에 모두 담아 보냅니다.

3단계. SDK 툴 러너로 간단히

파이썬 SDK에는 이 루프를 대신 돌려 주는 툴 러너(tool runner) 헬퍼가 있습니다. 함수에 데코레이터를 붙이면 툴 정의와 실행 루프를 SDK가 처리합니다. 구체적인 사용법은 SDK 버전에 따라 다를 수 있으니 공식 문서의 예제를 확인하세요. 흐름을 이해한 뒤에 쓰면 디버깅이 훨씬 쉽습니다.

실무에서 주의할 점

툴 사용 실무 주의점

  1. 입력값 검증: 클로드가 넘긴 인수를 그대로 DB 쿼리에 넣지 마세요. 형식과 범위를 검증합니다.
  2. 오류는 숨기지 말고 돌려주기: 실패하면 is_error: true와 함께 오류 내용을 돌려주면 클로드가 상황을 설명하거나 다른 방법을 시도합니다.
  3. 위험한 툴은 확인 단계: 결제, 삭제, 메일 발송처럼 되돌릴 수 없는 툴은 실행 전에 사람의 확인을 받도록 설계합니다.
  4. 툴 결과의 내용도 검증: 툴이 외부 웹페이지나 문서를 가져온다면, 그 안에 AI를 속이려는 지시문이 있을 수 있음을 염두에 두세요.
  5. 반복 횟수 제한: 루프가 끝나지 않는 상황에 대비해 최대 반복 횟수를 둡니다.

기억할 것 세 가지

  1. 툴 사용은 클로드가 호출할 함수와 인수를 결정하고, 실행은 내 코드가 하는 구조입니다.
  2. 툴 설명에 "무엇을, 언제" 쓰는지 명확히 적고, 결과는 tool_use_id를 맞춰 돌려줍니다.
  3. 입력 검증, 오류 반환, 위험 작업 확인 단계, 반복 제한을 갖추세요.

AI에게 손발을 달아 줄 때

툴 사용을 처음 이해했을 때 든 생각은 "AI에게 손발을 달아 주는 것"이었습니다. 동시에 그 손발이 할 수 있는 일을 내가 정확히 정해야 한다는 책임도 느꼈습니다. 처음에는 읽기 전용 툴(조회)만 만들어 보고, 쓰기 툴(수정·발송)은 확인 단계를 충분히 갖춘 뒤에 추가하는 것을 권합니다.

출처·참고 링크

🤔 자주 묻는 질문 (FAQ)

Q. 클로드가 내 함수를 직접 실행하나요?

아닙니다. 클로드는 어떤 툴을 어떤 인수로 호출할지 결정해 알려 줄 뿐이고, 실제 실행은 내 코드가 합니다. 실행 결과를 다시 클로드에게 보내면 그 결과를 바탕으로 답변을 완성합니다.

Q. 툴은 몇 개까지 줄 수 있나요?

여러 개를 줄 수 있으며, 클로드가 상황에 맞는 툴을 고릅니다. 다만 툴이 많아질수록 설명이 길어져 토큰을 더 쓰고 선택이 헷갈릴 수 있으므로, 이름과 설명을 명확히 쓰는 것이 중요합니다.

Q. 툴 입력값이 잘못 오면 어떻게 하나요?

실행 전에 입력값을 검증하고, 오류가 나면 is_error를 표시한 tool_result로 오류 내용을 돌려주세요. 클로드가 오류를 보고 다시 시도하거나 사용자에게 설명합니다.
#툴사용 #FunctionCalling #ClaudeAPI #AI에이전트 #파이썬