테마 전환

AI Agent 도구 호출 실전: 외부 API와 서비스 연동하기

Easton editorial illustration: tool-socket control board

AI에게 날씨를 확인하거나 파일을 읽고 API를 호출해 달라고 했는데, 돌아오는 답은 “외부 데이터에 접근할 수 없습니다”뿐이었던 적이 있나요?

꽤 답답합니다.

AI가 충분히 똑똑하지 않아서가 아닙니다. 핵심 능력 하나, 바로 도구 호출이 없기 때문입니다. AI를 ‘대화만 하는 존재’에서 ‘실제로 작업하는 존재’로 바꿔 주는 이 기술을 살펴보겠습니다.


AI 도구 호출이란?

쉽게 말해, 도구 호출은 AI에 손을 달아 주는 일입니다.

기존 대규모 언어 모델은 학습 데이터만을 바탕으로 질문에 답할 수 있습니다. “오늘 베이징 날씨는 어때?”라고 물으면 “실시간 데이터를 가져올 수 없습니다”라고 답할 뿐입니다. 하지만 도구 호출을 사용하면 AI가 날씨 API 같은 특정 함수의 실행을 직접 요청한 다음 그 결과를 받아 답변할 수 있습니다.

이 능력이 얼마나 중요할까요? 전술만 말하던 참모가 직접 병력을 지휘하는 장군이 되는 것과 비슷한 변화입니다.

세 가지 주요 방식

현재 널리 쓰이는 도구 호출 방식은 크게 세 가지입니다.

방식대표 제품적합한 용도
Function CallingOpenAI GPT구조화된 출력, 간단한 API 호출
Tool UseClaude복잡한 도구 체인, 여러 단계의 작업
MCPClaude Code표준화된 도구 생태계

무엇을 선택할지는 요구 사항에 달려 있습니다. 간단한 상황에는 OpenAI Function Calling이면 충분합니다. 복잡한 Agent 시스템에는 Claude Tool Use가 더 적합하고, 도구 생태계를 만들고 싶다면 MCP가 유력한 방향입니다.


OpenAI Function Calling: 기초부터 실제 사용까지

먼저 OpenAI의 방식을 살펴보겠습니다. Function Calling의 설계는 간결하며 핵심은 세 단계입니다.

  1. 도구를 정의합니다(AI에 사용할 수 있는 도구를 알려 줍니다).
  2. AI가 호출할 도구를 결정합니다(함수 이름과 매개변수를 반환합니다).
  3. 도구를 실행하고 결과를 다시 AI에 전달합니다.

전체 예제

날씨 조회 도구를 만든다고 가정해 보겠습니다. 먼저 도구 Schema를 정의합니다.

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "지정한 도시의 현재 날씨 정보를 가져옵니다",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "도시 이름(예: '北京', '上海')"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "온도 단위이며 기본값은 섭씨입니다"
                }
            },
            "required": ["city"]
        }
    }
}]

여기서 description 필드를 대충 작성하면 안 됩니다. AI는 이 설명을 보고 언제 도구를 호출해야 하는지 판단합니다. 누군가 이 필드에 “날씨 가져오기”라고만 적은 사례를 본 적이 있는데, AI가 도구를 언제 써야 할지 제대로 판단하지 못했습니다. “지정한 도시의 현재 날씨 정보를 가져옵니다”로 바꾸자 호출 정확도가 60%에서 95%로 올랐습니다.

그다음 요청을 보냅니다.

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "오늘 베이징은 더워?"}
    ],
    tools=tools
)

AI는 다음과 같은 도구 호출 요청을 반환합니다.

tool_call = response.choices[0].message.tool_calls[0]
# tool_call.function.name = "get_weather"
# tool_call.function.arguments = '{"city": "北京"}'

이제 실제 함수를 호출하고 결과를 다시 전달합니다.

# 실제 날씨 API 호출 실행
weather_result = get_weather_from_api("北京")

# 결과를 다시 전달
final_response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "오늘 베이징은 더워?"},
        response.choices[0].message,  # AI의 도구 호출 요청
        {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(weather_result)
        }
    ]
)

AI는 날씨 데이터를 바탕으로 “오늘 베이징은 28도로 꽤 덥습니다. 외출할 때 자외선 차단제를 챙기세요.”처럼 자연스럽게 답할 수 있습니다.

Strict Mode: 안정적인 출력 보장

OpenAI가 2024년에 도입한 Strict Mode는 JSON Schema가 불안정하게 일치하던 문제를 해결합니다. 활성화 방법은 간단합니다.

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "strict": True,  # 이 줄 추가
        # ... 기타 필드
    }
}]

활성화하면 AI가 반환하는 매개변수가 정의한 Schema와 100% 일치합니다. 켜지 않으면 다양한 파싱 오류가 생길 수 있으므로 프로덕션 환경에서는 활성화를 적극 권장합니다.


Claude Tool Use: 더 강력한 도구 체인

Claude의 Tool Use는 설계 관점에서 OpenAI와 몇 가지 중요한 차이가 있습니다.

병렬 호출

Claude는 한 번에 여러 도구 호출을 반환할 수 있습니다. 예를 들어 사용자가 “베이징과 상하이의 날씨를 비교해 줘”라고 물으면 Claude는 get_weather를 두 번 호출하라는 요청을 한 번에 보냅니다.

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "베이징과 상하이의 날씨를 비교해 줘"}],
    tools=tools
)

# response.content에는 두 개의 tool_use block이 포함될 수 있음
for block in response.content:
    if block.type == "tool_use":
        print(f"호출: {block.name}, 매개변수: {block.input}")

복잡한 작업을 처리할 때 특히 유용합니다. OpenAI도 병렬 호출을 지원하지만 Claude의 구현은 더 자연스럽습니다. 어떤 호출을 병렬로 실행할 수 있고 어떤 호출을 순차적으로 실행해야 하는지 지능적으로 판단합니다.

Tool Choice 전략

Claude는 도구 선택을 더 세밀하게 제어할 수 있습니다.

# 자동 선택(기본값)
tool_choice = {"type": "auto"}

# 도구 사용 강제(도구를 사용하지 않으면 답하지 않음)
tool_choice = {"type": "any"}

# 특정 도구 사용 지정
tool_choice = {"type": "tool", "name": "get_weather"}

any는 언제 사용할까요? 사용자의 질문에 답하려면 반드시 도구가 필요하다고 확신할 때입니다. 주문 상태 조회처럼 데이터베이스를 확인하지 않고는 답할 수 없는 경우 AI가 도구를 반드시 사용하도록 강제합니다.

오류 처리

도구 호출 실패는 흔한 일입니다. API 타임아웃, 네트워크 불안정, 잘못된 매개변수 등 원인은 다양합니다. Claude는 이를 자연스럽게 처리하는 메커니즘을 제공합니다.

tool_result = {
    "type": "tool_result",
    "tool_use_id": tool_use.id,
    "content": "API 호출 실패: 연결 시간 초과",  # AI에 실패 원인을 직접 전달
    "is_error": True  # 오류로 표시
}

AI는 오류를 확인한 뒤 다른 방법을 시도하거나 사용자에게 이해하기 쉬운 안내를 제공합니다. 예외를 그대로 던지는 것보다 훨씬 낫습니다. 사용자는 기술 오류 메시지 대신 “죄송합니다. 현재 날씨 서비스를 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.”라는 안내를 보게 됩니다.


MCP: 도구 표준화의 미래

도구 호출을 이야기할 때 MCP(Model Context Protocol)를 빼놓을 수 없습니다.

MCP가 필요한 이유

현재의 도구 생태계는 지나치게 파편화되어 있습니다. Claude에 GitHub 도구를 연결하고 ChatGPT에 Slack 도구를 연결하려면 각각 따로 개발해야 합니다. MCP의 목표는 통합 표준을 만드는 것입니다. 도구를 한 번 작성하면 어디서나 사용할 수 있게 하는 것입니다.

구조는 간단합니다.

MCP Client(Claude Code/Claude Desktop)

    MCP Server(도구 제공자)

   External Tool/API

Claude Code의 MCP 활용

Claude Code는 현재 MCP 지원이 가장 뛰어난 제품입니다. /mcp 명령으로 도구를 설정할 수 있습니다.

# 원격 MCP 서버 추가
claude mcp add my-server --transport sse --url https://api.example.com/mcp

# 로컬 도구 추가
claude mcp add local-tool --command node ./my-tool.js

설정이 끝나면 Claude Code가 서버에서 제공하는 도구를 자동으로 찾습니다. 대화에서 관련 내용을 언급하면 해당 도구를 자동으로 호출합니다.

예를 들어 get-github-issues 도구를 설정하고 Claude Code에 “이 프로젝트에는 어떤 open issue가 있어?”라고 물으면 도구를 직접 호출해 결과를 정리해 줍니다.

전체 과정에서 도구가 있다는 사실을 거의 의식하지 않게 됩니다. 이것이 도구 호출의 이상적인 모습입니다.


프로덕션 환경에서 주의할 점

도구 호출은 간단해 보이지만 실제 프로덕션 환경에는 주의해야 할 점이 적지 않습니다.

보안: AI가 제멋대로 행동하지 않게 하기

AI가 호출하면 안 되는 API를 호출할 수 있습니다. 다음과 같이 대응합니다.

  1. 도구 권한 분류: AI에는 필요한 도구만 제공하고 민감한 작업에는 사람의 확인을 요구합니다.
  2. 입력 검증: AI가 생성한 매개변수를 다시 검증하고 API에 그대로 전달하지 않습니다.
  3. 호출 감사: 모든 도구 호출의 매개변수와 결과를 기록해 나중에 추적할 수 있게 합니다.

AI가 사용자의 자유 입력을 데이터베이스 쿼리 인터페이스에 그대로 전달했다가 SQL 인젝션으로 이어진 사례를 본 적이 있습니다. AI가 직접 주입한 것은 아니지만 악성 입력을 그대로 넘긴 셈입니다. 따라서 AI가 생성한 매개변수를 절대 그대로 신뢰해서는 안 됩니다.

타임아웃과 재시도

도구 호출은 실패할 수 있습니다. 적절한 타임아웃을 설정하고 재시도 메커니즘을 함께 사용합니다.

async def call_tool_with_retry(tool_func, args, max_retries=3):
    for attempt in range(max_retries):
        try:
            return await asyncio.wait_for(
                tool_func(**args),
                timeout=10.0  # 10초 타임아웃
            )
        except asyncio.TimeoutError:
            if attempt == max_retries - 1:
                return {"error": "도구 호출 시간 초과"}
            await asyncio.sleep(1)  # 1초 기다린 뒤 재시도

Token 비용

도구 정의와 반환 결과는 모두 Token을 소비합니다. 특히 많은 데이터를 반환하면 비용이 커질 수 있습니다. 몇 가지 방법을 권장합니다.

  1. 도구 설명 간소화: description은 가능한 한 짧게 작성하되 필요한 내용은 분명히 전달합니다.
  2. 반환 데이터 제한: API가 반환한 데이터를 먼저 필터링해 필요한 필드만 남깁니다.
  3. 캐시 사용: 같은 쿼리 결과를 몇 분간 캐시할 수 있습니다.

마무리

도구 호출은 AI Agent의 핵심 능력입니다. 이 기능이 없으면 AI는 말로만 전략을 제시할 뿐이지만, 도구 호출이 있으면 실제 작업을 수행할 수 있습니다.

OpenAI의 Function Calling은 간결하고 사용하기 쉬워 입문과 단순한 상황에 적합합니다. Claude의 Tool Use는 더 강력해 복잡한 Agent 시스템에 어울리고, MCP는 도구 표준화의 흐름이므로 장기적으로 주목할 가치가 있습니다.

무엇을 선택할지는 요구 사항에 달려 있습니다. 어느 방식을 선택하든 보안, 오류 처리, 성능 최적화라는 세 가지 문제는 반드시 미리 고려해야 합니다.


참고 자료

FAQ

OpenAI Function Calling과 Claude Tool Use는 무엇이 다른가요?
주요 차이는 병렬 호출과 오류 처리입니다. Claude는 한 번에 여러 도구 호출을 반환하고 어떤 호출을 병렬로 실행할지 판단할 수 있습니다. 오류 처리에서는 `is_error` 플래그를 제공해 AI가 자연스럽게 대체 방안을 선택하도록 합니다. OpenAI는 더 간결하고 배우기 쉬워 단순한 상황에 적합합니다.
직접 Function Calling을 구현하는 대신 언제 MCP를 사용해야 하나요?
도구 생태계를 구축하거나 같은 도구 모음을 여러 AI 플랫폼에서 사용해야 할 때 MCP를 선택합니다. MCP는 표준화된 도구 프로토콜을 제공하므로 도구를 한 번 작성해 Claude와 ChatGPT 같은 여러 클라이언트에서 사용하고 중복 개발을 줄일 수 있습니다.
도구 호출이 실패하면 어떻게 처리해야 하나요?
세 가지를 권장합니다. 1) 타임아웃과 재시도 메커니즘을 설정합니다(예: 10초 타임아웃, 최대 3회 재시도). 2) Claude의 `is_error: true` 플래그로 AI에 실패 원인을 알립니다. 3) 캐시 데이터나 이해하기 쉬운 안내를 반환하는 대체 방안을 준비합니다.
AI가 민감한 API를 호출하지 못하게 하려면 어떻게 해야 하나요?
권한을 단계별로 나누는 것이 핵심입니다. AI에는 필요한 도구만 제공하고 결제나 삭제 같은 민감한 작업에는 사람의 확인을 요구합니다. 또한 AI가 생성한 매개변수를 그대로 신뢰하지 말고, API에 전달하기 전에 반드시 다시 검증해야 합니다.
Strict Mode란 무엇이며 활성화해야 하나요?
Strict Mode는 OpenAI가 2024년에 도입한 기능으로, AI가 반환하는 매개변수가 정의한 JSON Schema와 100% 일치하도록 보장합니다. 다양한 파싱 오류를 피할 수 있으므로 프로덕션 환경에서는 활성화를 적극 권장합니다. function 정의에 `"strict": true`를 추가하면 됩니다.

2분 읽기 · 게시일: 2026년 3월 21일 · 수정일: 2026년 9월 4일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog