[LLM] GPT 100% 활용하기: Function Calling 가이드


Function Calling 가이드

서론

대규모 언어 모델(LLM)이 등장한 이후, 이러한 모델들은 자연스러운 대화와 추론에서 뛰어난 성능을 보이고 있다. 그러나 LLM 단독으로는 최신 정보 검색이나 정확한 계산, 외부 시스템과의 상호작용 등 실시간 액션 수행에 한계를 지닌다. 예를 들어, 사용자가 “파리의 내일 날씨가 어떻습니까?”라고 물으면, 모델은 학습 데이터에 없는 최신 기상 정보를 알 수 없어 정확한 답변을 할 수 없다. 이러한 한계를 해결하기 위해 Function Calling(함수 호출) 기능이 도입되었다. Function Calling이란 LLM이 외부 함수나 API를 호출하여 필요한 데이터를 가져오거나 작업을 수행할 수 있도록 하는 기능으로, 모델의 지식 범위를 확장하고 실시간으로 정보를 가져올 수 있도록 한다.

Function Calling 개념은 2023년 OpenAI의 GPT-3.5/4 API에 처음 도입되었고, Anthropic의 Claude 등 다른 최신 모델들에도 유사한 기능이 추가되고 있다. 이 기능을 통해 AI 모델은 개발자가 정의한 함수 목록 중 적절한 함수를 골라 호출함으로써, 예컨대 실시간 정보 조회(날씨, 환율 등), 데이터베이스 질의, 계산 수행 같은 작업을 자연어 지시로 실행할 수 있다. 


이번 글에서는 Function Calling의 일반 개념과 아키텍처를 살펴보고, OpenAI의 Function Calling을 중심으로 실전 활용 방법을 자세히 다뤄보고자 한다. 아울러 기능의 필요성, 등장 배경, 주요 구조와 흐름(함수 정의, 호출, 인자 검증 등)을 논하고, 여러 모델 제공 업체(OpenAI, Anthropic 등)의 Function Calling 구현 비교 및 공통 패턴을 분석하고자 한다. 또한, AI 에이전트가 외부 지식 베이스나 API로부터 배경 정보를 fetch하여 답변에 활용하는 실제 코드 예시(GPT-4.1 + RAG 기반의 함수 호출)를 제시할 것이다. 마지막으로 관련 연구를 인용하며, 이 기능의 확장 방향(예: 툴 활용, 멀티에이전트 협력, 자율 에이전트 프레임워크 등)을 살펴보자.


Function Calling의 개념 및 아키텍처

Function Calling은 LLM이 외부 도구(함수, API 등)를 호출하여 모델 자체의 한계를 넘어서는 정보를 얻거나 작업을 수행하도록 하는 능력을 뜻한다. 요컨대, LLM이 자연어 요청을 해석하여 사전에 정의된 함수와 인자를 산출하고, 해당 함수를 실행함으로써 응답을 보강하는 구조다. 이를 통해 LLM은 자신의 트레이닝 지식만으로는 해결하기 어려운 문제도 외부 기능을 활용해 해결할 수 있게 된다. 예를 들어 날씨 질문이 들어오면 모델이 get_weather(city, date)와 같은 함수를 호출하여 실시간 정보를 얻고, 최종적으로 “내일 파리의 날씨는 18°C에 약간 흐릴 것으로 예상됩니다.”와 같이 대답할 수 있다.

Function Calling이 등장하게 된 배경에는, LLM의 고질적인 한계인 사실 환각(hallucination) 문제와 지식 시한(expired knowledge) 문제가 있다. 초거대 언어 모델은 방대한 텍스트 코퍼스(corpus)를 학습하지만, 정확한 계산 수행이나 최신 정보 접근 면에서 취약할 수 있다. 이를 개선하기 위해 연구자들은 모델에 도구 사용 능력을 부여하려는 시도를 해왔다. 예를 들어 Meta AI의 Toolformer 연구는 LM 스스로 API 호출을 학습하여 계산기, 검색엔진 등의 도구를 적절히 활용하도록 한 사례다. 해당 모델은 어떤 API를 언제, 어떤 인자로 호출할지 결정하고, 그 결과를 응답 생성에 반영하도록 훈련되었다. 이렇게 도구 활용 능력을 내재화하는 접근은 LLM의 작업 범위를 크게 넓혔고, OpenAI의 Function Calling은 이러한 맥락에서 보다 일반적이고 개발자 친화적인 인터페이스로 도입되었다. OpenAI는 ChatGPT 플러그인과 Function Calling 등을 통해 외부 도구 연계를 공식 지원하기 시작했고, 이를 JSON 스키마 기반의 표준화된 방법으로 구현했다.


일반적인 동작 흐름

Function Calling의 처리 흐름은 다음과 같이 요약할 수 있다:

LLM이 사용자 요청을 받고 함수 목록을 참고하여 호출을 결정, 함수 실행 결과를 활용해 최종 응답을 생성한다.

Function Calling을 통한 LLM과 외부 함수의 상호작용 과정

  1. 함수 정의 제공: 개발자는 미리 사용할 수 있는 함수들의 목록과 각 함수의 이름(name), 설명(description), 매개변수 스키마(parameters) 등을 JSON 형식으로 정의하여 LLM에게 제공해야 한다. 이 때 매개변수는 JSON Schema 형태로 타입(문자열, 숫자 등), 필수 여부(required), 제약 조건(format, enum 등)을 명시해야 한다. 예컨대 check_availability라는 호텔 예약 함수에 대해 도착일(arrival_date), 퇴실일(checkout_date), 인원(people) 등의 매개변수를 날짜 형식 및 정수로 요구하도록 정의할 수 있다. 이렇게 정의된 함수 목록은 OpenAI API의 경우 functions 파라미터로 전달되고, Anthropic Claude의 경우 tools 형태로 전달된다.

            "arrival_date": {
              "type": "string",
              "format": "date",
              "description": "The arrival date in yyyy-mm-dd format (e.g. 2023-02-13)"
            },
            "checkout_date": {
              "type": "string",
              "format": "date",
              "description": "The checkout date in yyyy-mm-dd format (e.g. 2023-02-18)"
            },
            "people": {
              "type": "integer",
              "description": "The number of people"
            }
    
  2. 모델 호출 및 함수 결정: 사용자의 프롬프트와 함께 위의 함수 정의 목록을 LLM에 전달하면, LLM은 사용자의 요청을 분석하여 필요한 경우 알맞은 함수를 선택 및 호출 결정을 내린다. 이때 OpenAI의 모델들은 함수 호출이 필요한 시점을 자동으로 인식하도록 미세조정되어 있어서, “사용자 요청에 어떤 함수가 도움이 될지” 판단한 뒤 함수 이름과 인자를 JSON 형태로 응답한다. 예를 들어 사용자의 질문이 “다음 주 화요일 밤에 방이 있나요?”라면, 모델은 사전 제공된 함수 목록 중 check_availability 함수를 골라 {"arrival_date": "2023-07-04", "checkout_date": "2023-07-05", "people": 1}라는 인자와 함께 함수 호출을 제안한다. OpenAI API 응답에서는 choices[0].finish_reason"function_call"로 표시되고, message.function_call 필드에 함수 이름과 인자가 구조화되어 반환된다. Anthropic Claude의 경우도 유사하게 stop_reason으로 tool_use를 반환하여 함수 호출 의도를 나타낸다.

  3. 함수 실행 및 결과 획득: 모델이 함수 호출을 결정하면, 실제 함수 실행은 개발자가 함수 내부를 어떻게 구현했는지에 달려있다. 모델이 응답 메시지에 포함한 함수명과 인자를 파싱하여, 해당 함수(예: get_weather)를 실제 외부 코드로 실행한다. 이 때 함수의 인자는 JSON 스키마에 맞춰 모델이 생성했지만, 여전히 유효성 검증이 필요하다. 모델이 스키마를 어기거나 불완전한 인자를 보낼 가능성을 고려하여, 개발자는 인자를 검증하고 오류가 있다면 예외 처리 또는 재호출을 해야 한다. 예컨대 필수 인자가 누락되었거나 타입이 잘못된 경우, 해당 함수를 바로 실행하는 대신 모델에게 추가 정보를 요청하거나 디폴트 값을 사용할 수 있다. OpenAI 포럼의 권고에 따르면, 함수 호출 시 매번 인자를 검증하고 필요한 경우 모델에게 수정된 프롬프트로 재시도하는 방식을 사용할 수 있다. 일단 함수 실행에 성공하면, 그 결과 값(예: 날씨 정보, DB 질의 결과 등)을 얻을 수 있다.

  4. 모델에 결과 전달 및 최종 응답 생성: 함수 실행 결과를 다시 LLM에게 전달하여 최종 답변을 완성하는 단계다. OpenAI API에서는 함수 결과를 새로운 메시지로 추가하는데, 이 메시지의 role"function"으로, name을 해당 함수 이름으로, content를 함수 실행 결과(텍스트 형태)로 설정한다. 그런 다음 이 대화 내역을 계속 모델에 보내 후속 답변을 요청하면, LLM은 함수 호출로 얻은 실제 데이터를 참고하여 사용자에게 줄 최종 자연어 응답을 생성한다. Claude의 경우에도 함수 실행 결과를 tool_result 블록으로 담아 새 사용자 메시지로 제공하면, 모델이 그것을 보고 이어서 응답을 완성한다. 최종적으로 LLM은 “콜럼버스(Columbus, OH)의 현재 날씨는 화창하고 25°C입니다”처럼 외부 정보가 반영된 답변을 사용자에게 제공한다.


이상의 과정에서 주목할 점은, LLM 자체는 함수 실행을 직접 하지 않고 함수 호출에 필요한 지시만 반환한다는 것이다. OpenAI의 모델들은 함수 호출을 출력 형태로 기술할 뿐 실제 코드를 실행하지 않기 때문에, 개발자가 함수 로직을 구현하고 모델이 내놓은 JSON 인자를 받아 실행하는 구조다. 이는 보안과 신뢰성 측면에서도 중요한데, 모델이 곧바로 외부 시스템에 영향을 주는 것보다 한 단계 검증을 거칠 수 있기 때문이다. 또한 이러한 접근은 LLM을 플러그인처럼 활용하던 기존 방법(예: LangChain 에이전트에서 복잡한 프롬프트를 작성해 모델에게 도구 사용을 유도하던 방식)을 간소화한다. 요컨대 Function Calling 기능은 모델이 언제 어떤 함수를 써야 할지 파악하는 의도 인식필요한 인자를 구조화해 내놓는 출력 형식화를 모델에게 맡김으로써, 개발자는 보다 안정적인 인터페이스로 모델과 외부기능을 연계할 수 있게 된다.


OpenAI Function Calling의 필요성과 특징

OpenAI의 함수 호출 기능은 위와 같은 맥락에서 출시 당시 개발자들에게 큰 주목을 받았다. 이 접근은 프롬프트 엔지니어링에 의존하여 모델에게 툴 사용을 암시적으로 유도해야 했던 문제를 줄이고, 명시적이고 구조적인 프로토콜을 제공했기 때문이다. OpenAI는 해당 기능을 발표하며 이를 “LLM을 외부 데이터와 시스템에 연결”하는 방법으로 소개했고, JSON 스키마를 통한 엄격한 형식 지정토큰 낭비 감소 등의 이점을 강조했다. 실제로 JSON 형태의 응답은 파싱이 용이하기 때문에, 이전처럼 모델이 자연어로 설명한 내용을 정규식 등으로 후처리할 필요가 거의 없다. 또한 Function Calling을 사용하면 모델이 중간 단계 결과를 구조화하여 제공하므로, 응답의 일관성과 신뢰성이 높아진다. Datacamp의 튜토리얼에서도 “이제 불규칙한 출력 형식을 처리하기 위해 별도의 파싱 로직이나 정규식을 작성할 필요 없이, 모델이 곧바로 JSON으로 결과를 줄 수 있다”고 설명한다.

Function Calling의 필요성은 RAG(Retrieval-Augmented Generation) 등 기존 기법과도 관련이 있다. 기존의 RAG에서는 벡터 검색 등을 통해 외부 지식을 찾아 프롬프트에 삽입함으로써 답변의 근거를 마련했는데, 이 과정에서 모델 입력 길이가 길어지거나 모델이 검색 쿼리를 스스로 생성하지 못하는 한계가 있었다. Function Calling 도입 후에는 모델이 적절한 검색 쿼리를 함수 인자로 생성하여 개발자가 검색 함수를 호출하게 할 수 있으므로, 보다 능동적인 검색 전략이 가능해졌다. 이는 사용자의 질문을 분석해 필요한 경우 모델 스스로 “검색이 필요함”을 인지하고, 정교한 쿼리를 구성해 search() 함수를 호출하는 식의 상호작용으로 구현될 수 있다. 이후 결과가 없으면 기본 답변으로 fallback하고, 특정 패턴의 질문에만 함수를 호출하는 등의 유연한 흐름 제어도 가능해졌다.

물론 OpenAI의 Function Calling에도 제약과 한계는 존재합니다. 앞서 언급한 대로 JSON Schema로 매개변수 형식을 지정하지만, 현재 모델은 복잡한 제약(예: 특정 형식의 문자열)까지 완벽히 준수하지 못할 수 있다. 예를 들어 enum으로 몇 가지 값만 허용한 경우라도, 모델이 다른 값을 넣는 오류가 발생할 수 있다. 또한 2023년 도입 당시에는 배열 타입 매개변수 미지원과 같은 제한도 있었으며,  model snapshot 관리 이슈(특정 날짜의 함수호출 지원 버전이 업데이트 없이 고정) 등 실험적인 측면이 있었습니다. 그럼에도 불구하고 OpenAI 측은 함수 호출 기능을 통해 개발자의 작업량을 줄이고, 지능형 워크플로우에서의 의도 분류를 모델이 맡아줄 수 있다고 강조한 바 있다. 실제로 “자연어 기반 워크플로우와 코파일럿 시나리오에서 의도 인식을 크게 단순화하며, 서드파티 도구(예: LangChain 에이전트)의 의존성을 줄이고 지연 시간을 낮춰준다”는 평가가 있다 (Getting started with OpenAI function calling | by Chas Sweeting | Medium).


OpenAI Function Calling의 구현과 실전 적용

이 절에서는 OpenAI API를 사용하여 Function Calling을 구현하는 방법을 상세히 설명하고자 한다. OpenAI의 GPT-3.5-turbo (2023-06) 및 GPT-4 (2023-06) 모델부터 함수 호출 기능이 공식 지원되었고, ChatCompletion API 호출 시 몇 가지 인자를 추가로 지정하여 사용할 수 있다. 기본 흐름은 이전 절의 개요와 동일하나, 실제 코드 레벨에서 함수 정의호출 처리가 어떻게 이루어지는지 살펴보겠다.

함수 정의 및 호출 설정

OpenAI API를 통해 모델에 함수를 제공하려면, ChatCompletion.create 호출 시 functions 파라미터에 함수 목록을 전달한다. 각 함수는 파이썬 딕셔너리로 기술되며, 주요 키는 "name", "description", "parameters"다. 아래 코드는 두 개의 함수를 정의한 예시다:

functions = [
    {
        "name": "check_availability",
        "description": "특정 날짜 범위에 방이 있는지 확인합니다",
        "parameters": {
            "type": "object",
            "properties": {
                "arrival_date": {
                    "type": "string",
                    "format": "date",
                    "description": "YYYY-MM-DD 형식의 체크인 날짜"
                },
                "checkout_date": {
                    "type": "string",
                    "format": "date",
                    "description": "YYYY-MM-DD 형식의 체크아웃 날짜"
                },
                "people": {
                    "type": "integer",
                    "description": "숙박 인원 수"
                }
            },
            "required": ["arrival_date", "checkout_date", "people"]
        }
    },
    {
        "name": "send_receipt",
        "description": "입력된 이메일 주소로 최근 영수증 사본을 전송합니다",
        "parameters": {
            "type": "object",
            "properties": {
                "email": {
                    "type": "string",
                    "format": "email",
                    "description": "수신인 이메일 주소"
                }
            }
            # required 생략 (선택 사항)
        }
    }
]

위 정의에서 check_availability 함수는 세 개의 필드를 필요로 하고 (required로 명시), send_receipt 함수는 이메일만 입력받으면 되며 required 필드는 없다. 이처럼 JSON Schema로 인자를 기술하면 모델이 함수를 호출할 때 이 스키마를 참고하여 가장 적합한 JSON 인자를 만들어낸다. 함수 정의 시 가장 좋은 방법으로는, 각 필드에 description을 달아주어 모델이 값을 어떻게 채워야 할지 힌트를 주는 것이 권장된다.

그 다음, ChatCompletion 요청을 할 때 functions=functions 인자를 포함하고, function_call="auto"로 설정하면 모델이 자동으로 함수 호출 여부를 결정한다. 기본값 "auto"는 모델이 판단하여 필요한 경우 함수를 호출하며, 특정 함수를 강제로 호출하게 하고 싶다면 function_call={"name": "<함수명>"}처럼 지정할 수도 있다. 아래는 사용자의 문의를 보내는 예시다:

import openai

messages = [
  {"role": "user", "content": "지난주에 숙박했는데 영수증을 이메일로 받을 수 있을까요?"}
]

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-0613",
    messages=messages,
    functions=functions,
    function_call="auto"
)

위 시나리오에서 사용자는 지난 숙박에 대한 영수증 이메일 전송을 문의하고 있다. 모델은 제공된 함수 목록을 검토한 뒤, send_receipt 함수를 사용하는 것이 적절하다고 판단할 것이다. 실제 response 객체의 내용을 출력해보면, 함수 호출 형태로 응답한 경우 다음과 같은 구조를 확인할 수 있다:

{
  "choices": [
    {
      "finish_reason": "function_call",
      "message": {
        "role": "assistant",
        "content": null,
        "function_call": {
          "name": "send_receipt",
          "arguments": "{\n  \"email\": \"user@example.com\"\n}"
        }
      }
    }
  ],
  ...
}

위 결과에서 finish_reason"function_call"로 나타나 있으며, message.function_call 필드에 모델이 선택한 함수 이름과 인자가 들어있다. 이 예에서는 모델이 send_receipt 함수를 호출하고자 하며, 인자로 {"email": "user@example.com"}을 제시하였다. contentnull인데, 이는 함수 호출을 선택한 단계에서는 아직 최종 답변 컨텐츠를 주지 않음을 의미한다.


함수 호출 처리 및 응답 완성

모델의 응답이 함수 호출일 경우, 이제 개발자 측 코드에서 해당 함수를 실제로 실행해야 한다. 예를 들어 위 send_receipt 호출을 받았다면, 다음과 같이 처리한다:

if response.choices[0].finish_reason == "function_call":
    func_call = response.choices[0].message.function_call
    func_name = func_call["name"]
    args = json.loads(func_call["arguments"])
    
    if func_name == "send_receipt":
        result = send_receipt(**args)  # 예: send_receipt(email="user@example.com")
        # 함수 실행 결과(예: "이메일로 영수증을 전송했습니다.")를 모델에 전달
        messages.append({"role": "function", "name": func_name, "content": result})
        
        final_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=messages
        )
        answer = final_response.choices[0].message.content
        print("ChatGPT:", answer)

위 코드에서는 function_call로부터 함수 이름과 인자(JSON 파싱 후 딕셔너리로)를 추출한 뒤, 매칭되는 실제 파이썬 함수를 호출하고 그 결과를 문자열로 받아온다. 그런 다음 대화 messages에 새로운 메시지를 추가하는데, role"function"으로, name을 함수명으로 하여 결과를 담았다. 이 형식은 OpenAI가 정한 것으로, 대화 히스토리에 함수 실행 결과를 마치 또 하나의 메시지처럼 추가해주는 역할을 한다. 마지막으로 동일한 model에 이 갱신된 messages를 보내면, 모델은 방금 전달된 함수 결과를 참고하여 최종 답변을 생성하게 된다.

실제로 send_receipt 함수가 "user@example.com"으로 메일을 보냈다면, 그 성공 메시지를 받아 모델에 제공했을 것이다. 모델은 이를 보고 최종적으로 사용자에게 자연어 응답을 만든다. 예를 들면: "요청하신 이메일 주소(user@example.com)로 영수증을 보내드렸습니다. 12시간 내에 도착하지 않으면 알려주세요."와 같은 답변을 생성할 수 있습니다. 이처럼 함수 호출 과정을 거치면서, 모델은 단독으로 답할 때보다 훨씬 정확하고 실행력 있는 응답을 제공할 수 있다.

OpenAI의 함수 호출 기능은 다중 함수 지원도 하기 때문에, 한 번의 API 요청에 여러 함수를 정의해 두면 모델이 상황에 따라 적절한 것을 골라 호출한다. 예를 들어 날씨 확인 함수와 뉴스 검색 함수를 함께 넣으면, "뉴욕의 내일 날씨와 주요 뉴스 알려줘" 같은 복합 질문에 대해 모델이 두 함수를 연달아 호출하여 처리할 수도 있다. 다만 동일 응답에서 여러 함수를 동시에 호출하는 것은 현재는 한 번에 하나씩 순차 수행하도록 권장된다. 즉 첫 함수 결과를 받고 나서 대화 내 재호출하는 형태로 다단계 도구 사용을 구현할 수 있다. Anthropic Claude의 MCP 방식처럼 한 번에 여러 함수 호출 결과를 묶어 처리하는 고도화된 시나리오는 OpenAI에서는 대화 컨텍스트를 통한 반복 호출로 커버할 수 있다.

마지막으로, OpenAI 함수 호출 기능의 보안 측면을 언급하면, 모델이 반환한 함수명과 인자를 반드시 화이트리스트 검증하는 것이 좋다. 개발자는 functions 목록 이외의 함수 호출은 무시하거나 에러 처리해야 하며, 인자 JSON이 스키마에 맞지 않는 경우도 대비해야 한다. 이를 통해 악의적인 프롬프트나 예기치 않은 모델 출력이 시스템에 문제를 일으키지 않도록 할 수 있다. 예컨대 함수명 문자열을 그대로 eval()하는 식의 구현은 절대 피해야 하며, 안전하게 분기 처리를 하는 것이 원칙이다.


여러 Function Calling 방식: OpenAI vs Anthropic 등

현재 OpenAI 외에도 Anthropic, Google, Meta 등의 여러 AI 기업들이 LLM의 툴 사용 기능을 지원하거나 유사한 인터페이스를 연구 중이다. 대표적으로 Anthropic사의 Claude 모델은 OpenAI와는 약간 다른 접근으로 함수 호출을 지원하며, 이를 Tool Use 혹은 Model Context Protocol (MCP)라고 부른다. 이 섹션에서는 OpenAI와 Anthropic의 방식을 비교하고, 타사들의 일반화된 패턴을 논해보겠다.

OpenAIAnthropic Claude의 접근 비교

OpenAI의 Function Calling과 Anthropic Claude의 MCP는 목표는 유사하지만 구현과 사용성 측면에서 차이점이 존재한다. 주요 비교 사항을 표로 정리하면 다음과 같다:

  • 함수 정의 방식: OpenAI는 functions 매개변수로 JSON Schema에 기반한 함수 목록을 제공하도록 한다. 개발자가 스키마를 직접 작성해야 하지만, 덕분에 모델 출력이 구조적으로 제한되고 비교적 deterministic하다.

    OpenAI - Function call

    반면 Anthropic은 대화 메세지 내에 툴에 대한 설명을 자연스럽게 포함시키는 MCP 방식을 취한다. 함수(툴)를 JSON으로 정의하기보다는, Claude API에 tools로 함수명과 설명, 인자 타입을 넘기면 시스템 프롬프트에 이들이 삽입되어 모델이 이해하도록 한다. 모델은 대화 흐름 속에서 <<tool>>과 같은 특별 토큰으로 함수 호출을 표현하며, 보다 대화에 녹아든 형태로 기능 사용을 결정한다.
    Anthropic - Tools

    요약하면 OpenAI는 명시적 스키마 기반, Anthropic은 대화 컨텍스트 기반으로 함수 정의를 활용한다.

  • 응답 형식: OpenAI 모델은 함수 호출 시 JSON 객체(function_call 필드)로 응답하며, 개발자는 이를 파싱한다. Anthropic Claude는 응답 중간에 <tool_name> 블록을 생성하고 stop_reason: "tool_use"로 응답을 종료한다. 이때 Claude의 응답에는 함수명과 인자가 포함된 프롬프트 조각이 담겨 있으며, OpenAI처럼 구조화된 JSON은 아닐 수 있다. 대신 Anthropic은 API 레벨에서 이러한 툴 호출을 감지하여 response.tool_calls 리스트로 파싱된 결과를 제공하기도 한다. OpenAI 쪽이 응답 파싱이 직관적(정해진 필드 확인)인 반면, Anthropic 쪽은 유연하지만 프롬프트에 더 의존하는 차이가 있다.

  • 다중 도구 및 체이닝: Anthropic MCP의 강점 중 하나는 하나의 대화 내에서 여러 도구를 자연스럽게 연쇄적으로 활용하는 데 있다. 예를 들어 Claude는 첫 번째 툴 호출로 얻은 정보를 활용해 곧바로 두 번째 툴을 호출하는 식으로 대화 맥락 안에서 연속적인 사고와 액션을 수행할 수 있다. OpenAI 모델도 여러 함수 정의는 가능하지만, 한 번에 하나씩 호출하고 응답을 이어받아 다시 함수 호출을 하는 반복 API 호출이 필요하다. 따라서 Anthropic은 멀티툴 워크플로우의 원활함에서 장점을 보이고, OpenAI는 명시적 제어와 검증 용이성에서 장점이 있다고 요약할 수 있다.

  • 보안 및 통제: OpenAI의 방식은 개발자가 허용한 함수와 스키마 밖으로 모델이 벗어나기 어려운 환경을 제공한다. JSON 스키마에 없으면 함수 호출 자체를 모델이 시도하지 않으므로, 보다 통제가 용이하다. Anthropic은 보다 유연하고 사람 같은 도구 사용을 지향하지만, 반대로 말하면 모델이 도구 사용 지시를 프롬프트 기반으로 생성하기에 예기치 않은 형태의 호출이 섞일 수 있는 여지가 있다. Anthropic Claude 팀은 이를 해결하기 위해 MCP를 표준 프로토콜화하여 다양한 모델에서도 동일한 방식으로 도구를 사용할 수 있도록 했다. MCP는 “AI 모델을 다양한 데이터 소스와 도구에 연결하는 표준화된 방법”으로 소개되며, 일종의 개방형 표준으로 제안되고 있습니다. 이는 OpenAI가 자사 API 내에서만 통용되는 JSON 규격을 쓰는 것과 대비된다.
    (참고로 OpenAI도 MCP 방식을 지원하기로 결정했다!)

이러한 차이로 인해 어떤 점을 중요하게 여기느냐에 따라 API 선택이 달라진다. 
OpenAI = 견고한 틀 제공, Anthropic = 유연한 컨텍스트 제공, 니즈에 맞춰 선택하자.


기타 LLM 서비스 업체의 기능 및 일반 패턴

이 외에도 Azure OpenAI는 OpenAI와 동일한 메커니즘을 Azure 플랫폼에서 제공하며, Google Vertex AI의 PaLM 2 또한 2023년 말부터 함수 호출(Function Calling) 기능을 도입했다. Google은 자사 버텍스 API에서 함수 호출을 쉽게 활용할 수 있도록 함수 스키마 자동 생성 가이드 등을 제공하고 있다. 예컨대 Semantic Kernel 프레임워크에서는 C# 메서드에 attribute를 달면 자동으로 JSON 스키마를 모델에게 넘겨주고, 응답의 함수 호출을 자동 처리해 주는 등 개발 편의성을 높이고 있습니다.

오픈소스 LLM 쪽에서는 Meta의 Llama 2 등이 기본적으로 이러한 기능을 지원하지는 않지만, LangChain이나 LlamaIndex 같은 에이전트 프레임워크가 OpenAI 방식의 function call을 흉내내어 프롬프트 수준에서 구현하고 있다. 예를 들어 LangChain은 ReAct 방식으로 LLM이 <Action><Action Input>을 출력하도록 유도해 외부 도구를 쓰게 했는데, OpenAI Function Calling 등장 이후에는 해당 출력을 JSON 함수 호출로 대체하는 파서가 추가되었다. 요컨대, 일반적인 패턴은 (1) 개발자가 사용 가능한 도구 목록과 사용법을 모델에 제공하고, (2) 모델은 자연어 질문을 도구 호출 형태로 변환하며, (3) 외부 시스템이 그 호출을 실행 후 결과를 다시 모델에 전달하고, (4) 모델이 최종 응답을 생성하는 흐름으로 수렴하다. 각 제공자는 이 흐름을 실현하기 위해 입출력 형식을 조금씩 달리할 뿐, 궁극적인 목표는 동일하다.


함수 호출을 활용한 지식 검색 예시 (GPT-4 + RAG 시나리오)

이번 섹션에서는 Function Calling과 RAG(Retrieval-Augmented Generation) 기법을 결합하여, AI 에이전트가 외부 지식 베이스로부터 필요한 정보를 가져와 답변하도록 하는 예제를 살펴보겠다. 예를 들어, GPT-4 기반의 챗봇이 자사 제품 매뉴얼 데이터베이스에서 정보를 찾아와 사용자 질문에 근거 있는 답을 하는 시나리오를 가정해보겠다. 이 경우, search_docs라는 함수를 정의해 두면 모델이 적절한 질의로 해당 함수를 호출하여 문서를 검색하고, 그 결과를 토대로 답변하게 할 수 있다.


예시 시나리오: 지식베이스 Q&A

  • 상황: 한 소프트웨어 회사의 챗봇이 사용자의 기술 문의에 답변하고자 한다. 챗봇은 회사의 내부 FAQ/매뉴얼 DB에서 정보를 가져올 수 있어야 한다.

  • 접근: OpenAI GPT-4에게 search_docs라는 함수를 제공하고, 사용자가 질문하면 모델이 이 함수를 통해 관련 문서를 검색한 후 답변을 구성하도록 한다.

먼저 검색 함수를 정의한다. 실제로는 벡터 DB나 검색 엔진을 호출하겠지만, 여기서는 단순화를 위해 키워드에 따라 미리 정해둔 텍스트 조각을 반환하는 함수로 가정하자:

# 예시 지식 베이스 (키: 검색어, 값: 답변 조각)
knowledge_base = {
    "비밀번호 재설정": "비밀번호를 재설정하려면 설정 메뉴에서 '비밀번호 변경'을 선택한 후 이메일로 전송된 코드를 입력하십시오.",
    "계정 잠금": "계정이 잠긴 경우 30분 후에 자동으로 잠금이 해제됩니다. 즉시 해제를 원하면 관리자에게 문의해야 합니다."
}

def search_docs(query: str) -> str:
    """내부 문서를 검색하여 관련 내용 반환"""
    # 간단한 구현: query 키워드가 포함된 항목 찾기
    for keyword, doc in knowledge_base.items():
        if keyword in query:
            return f"[{keyword}] {doc}"
    return "관련 문서를 찾지 못했습니다."

이제 OpenAI API에 함수를 알려주기 위해 functions 목록에 search_docs를 추가하자:

functions = [
    {
        "name": "search_docs",
        "description": "제품 매뉴얼 DB에서 질의어에 해당하는 문서를 검색합니다",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "검색할 질문이나 키워드"
                }
            },
            "required": ["query"]
        }
    }
]

그런 다음, 사용자의 질문을 받아 모델에 전달한다. 예를 들어 사용자가 “계정이 잠겼는데 어떻게 해야 하나요?”라고 질문했다고 하자:

user_question = "계정이 잠겼는데 어떻게 해야 하나요?"
messages = [{"role": "user", "content": user_question}]

response = openai.ChatCompletion.create(
    model="gpt-4-0613",
    messages=messages,
    functions=functions,
    function_call="auto"
)

GPT-4는 주어진 질문을 이해한 뒤, 내부적으로 “이 질문에 답하려면 search_docs 함수를 써서 '계정 잠금' 관련 정보를 찾아야겠다”는 결론을 내릴 것이다. 그 결과 response에는 함수 호출이 담겨 나오게 됩니다. 예시로 예상되는 response.choices[0].message.function_call은 다음과 같을 수 있다:

{
  "name": "search_docs",
  "arguments": "{\n  \"query\": \"계정 잠금 해제\"\n}"
}

모델이 입력 질의를 약간 변형하여 "계정 잠금 해제"라는 키워드로 검색하려고 결정한 모습이다. 이제 이 정보를 이용해 실제 함수를 호출하자:

func_call = response.choices[0].message.function_call
func_name = func_call["name"]
args = json.loads(func_call["arguments"])

if func_name == "search_docs":
    result = search_docs(**args)  # 예: search_docs(query="계정 잠금 해제")
    messages.append({"role": "function", "name": func_name, "content": result})
    final_response = openai.ChatCompletion.create(model="gpt-4-0613", messages=messages)
    answer = final_response.choices[0].message.content
    print(answer)

search_docs 함수는 "계정 잠금 해제"를 인자로 받아 지식 베이스에서 관련 내용을 찾아 반환한다. 위의 간단한 구현에서는 "계정 잠금" 키를 가진 항목을 찾아 [계정 잠금] ... 내용의 문자열을 돌려줄 것이다. 이 결과를 다시 GPT-4에 전달하여 최종 답변을 얻는다. GPT-4는 함수 결과 ("[계정 잠금] ... 30분 후 자동 해제 ... 관리자 문의" 등의 내용)를 참고하여 사용자에게 완결된 답을 작성할 것이다. 예를 들면 최종 출력은 다음과 같이 나올 수 있다:

챗봇: 고객님의 계정이 잠긴 경우, 30분 후에 자동으로 잠금이 해제됩니다. 즉각 해제를 원하신다면, 내부 규정상 관리자에게 요청하여 해제해야 합니다. (참고: 계정 보안을 위해 일정 시간 후 자동 해제되도록 설정되어 있습니다.)

이 답변은 실제 매뉴얼의 내용을 근거로 작성된 것이므로, 정확성과 신뢰도가 높다. 또한 사용자에게 필요한 조치를 명확히 안내하고 있음을 볼 수 있다.

위 과정에서 보듯이, Function Calling을 사용하면 모델이 질문에 따라 주도적으로 지식 베이스 조회를 수행한다. 개발자는 단지 검색 함수만 제공하면 되고, 언제 검색할지, 어떤 키워드로 검색할지 등을 일일이 규칙으로 코딩하지 않아도 된다. 모델이 언어 이해를 바탕으로 적절한 쿼리를 구성해주기 때문이다. Microsoft의 예시에서도, 사용자의 질문이 특정 문서나 작성자에 관한 구조화 질의일 경우 함수 호출을 통해 더 나은 검색 쿼리를 만들고, 그렇지 않은 경우에는 일반 질의로 처리하도록 설계한 사례가 있다. 이렇듯 RAG와 함수 호출의 결합은 자연어 질의→구조적 검색→응답 생성의 파이프라인을 한층 지능적으로 만들어 준다.

또한 이 접근은 응답의 근거 제시에도 도움을 준다. 위 시나리오에서 챗봇이 “[계정 잠금] ...”과 같이 결과를 내부적으로 받았지만, 필요하다면 이를 인용하거나 사용자에게 근거로 제시할 수도 있다. 실제 비즈니스 챗봇에서는 함수 결과와 모델 응답을 조합하여, "출처: 사내 매뉴얼 3.2장" 같은 근거를 덧붙이는 것도 가능하다. 이는 궁극적으로 LLM의 사실적 신뢰성을 높이고 사용자에게 투명한 정보 제공을 실현하는 방향이다.

마지막으로, GPT-4 등의 강력한 LLM은 함수 호출을 연속적으로 활용해 복잡한 작업도 수행할 수 있습니다. 예를 들어 사용자가 “OO에 관한 최신 뉴스 헤드라인을 요약해줘”라고 하면, 모델은 search_news 함수로 뉴스를 검색하고, 이어서 summarize 함수를 불러 요약하는 식으로 두 단계 호출을 할 수 있다. 이러한 멀티스텝 함수 호출은 어느 정도 개발자의 흐름 설계와 반복 호출이 필요하지만, 모델의 추론 능력과 결합하면 사람의 고차원 개입 없이도 상당히 자율적인 정보 수집 및 처리를 구현할 수 있다. 즉, Function Calling은 단순 질의응답을 넘어 작업 수행형 에이전트를 만드는 토대가 될 수 있다!


관련 연구 및 기능 확장 방향

Function Calling은 LLM의 도구 활용을 표준화한 기능으로 볼 수 있으며, 이 주제와 관련하여 다양한 연구와 발전이 이뤄지고 있다. 마지막으로, Function Calling과 연계된 주요 연구 흐름과 향후 확장 가능성을 알아보고자 한다. 특히, 툴 유틸라이제이션(도구 활용) 연구, 멀티에이전트 협력, 자율 에이전트 프레임워크와의 연관성을 중심으로 살펴보겠다.


LLM의 도구 활용과 Function Calling

LLM이 외부 도구를 사용하는 아이디어는 Function Calling 이전부터 학계에서 활발히 탐구되었다. 앞서 언급한 Toolformer는 LLM이 자체적으로 API 사용을 학습시킨 예이며, ReAct(Reasoning Acting) 방식은 모델에게 체계적인 추론과 액션 시퀀스를 생성하게 하여 문제를 해결하도록 한 기법이다. ReAct(2022) 논문에서는 LLM이 “생각(Thought): …”“행동(Action): …”을 교대로 출력하며, 예컨대 Action 단계에서 Search[...]와 같은 함수를 호출하도록 프롬프트를 설계했다. 이러한 프롬프트 엔지니어링 기반 접근들은 이후 LangChain 등의 구현으로 이어지면서, Function Calling이 나오기 전까지 표준적인 솔루션으로 쓰였다. 그러나 프롬프트 기반 도구 사용은 모델 출력이 구조화되어 있지 않기 때문에 파싱과 오류 처리가 어려웠고, 모델이 의도대로 행동하지 않을 위험이 있었다. OpenAI Function Calling의 등장은 이러한 문제를 크게 완화하여, “LLM에게 도구 사용을 가르치되, 그 출력은 일정한 JSON으로 받자”라는 현실적인 해법을 제시한 것으로 평가된다. Lilian Weng(OpenAI 연구원)의 정리에서도, "플러그인(Plugins)이나 함수 호출(Function Calling)과 같은 기능은 실제 LLM의 도구 사용을 가능케 하는 실용적 예시"라고 언급된다. 다시 말해, Function Calling은 툴 활용 연구의 상용화 버전이며, 이를 통해 연구 아이디어들이 폭넓게 응용되고 있다.

또한 Function Calling 확장으로, 모델이 사용할 수 있는 툴의 범위를 넓히는 시도가 이어지고 있다. 예컨대, OpenAI 플러그인 생태계에서는 웹 브라우저, 코드 실행기, 데이터베이스 질의기 등 다양한 기능을 플러그인으로 제공하였고, GPT-4는 이들을 호출하여 복잡한 작업을 수행할 수 있음을 보여주었다. 앞으로는 Function Calling을 통해 멀티모달 도구 (이미지 생성, 음성 합성 등)까지 호출하거나, 사용자 정의 툴을 실행하면서 중간결과를 체계적으로 공유하는 기능도 추가될 전망이다. 실제 OpenAI의 GPT-4osms Vision 기능과 결합하여 이미지 분석 후 함수 호출을 하는 등, 멀티모달 통합도 가시화되고 있다.


멀티 에이전트 협력과 자율 에이전트

Function Calling은 단일 LLM과 외부 시스템 간 인터페이스를 정했지만, 이를 다수의 LLM이 협력하는 시나리오로 확장할 수도 있다. 예를 들어 한 LLM이 다른 LLM을 함수처럼 호출하는 멀티에이전트 환경을 생각해볼 수 있다. 실제 연구 중에는 개별 에이전트를 서로의 도구로 등록하여 한 에이전트가 필요할 때 다른 에이전트를 호출하는 프레임워크도 등장했다. 이런 시스템에서는 조율자 역할의 LLM이 있고, 개별 전문성을 지닌 LLM들이 함수로 노출되어 있다. 오픈소스 프로젝트 Langroid는 OpenAI 함수 호출과 유사한 메커니즘으로 임의의 LLM들 간 메시지를 주고받게 하여, 예컨대 요약 담당 에이전트번역 담당 에이전트가 협력해 질의에 답하도록 구현하기도 했다.

더 나아가, HuggingGPT와 같은 체계는 LLM(ChatGPT)가 여러 AI 모델들을 연계하는 콘트롤러 에이전트로 활용된 사례다. HuggingGPT (Shen et al, 2023)은 LLM을 태스크 플래너로 사용하여, HuggingFace에 공개된 다양한 전문가 AI 모델들을 연결함으로써 복잡한 AI 작업을 수행한 것으로, 예컨대 복잡한 멀티모달 입력이 주어지면 먼저 작업을 분할하고 각 부분을 처리할 최적의 모델(이미지 분류기, 음성 인식기 등)을 선택하여 실행한 뒤, 그 결과를 모아 최종 응답을 생성한다. 이때 LLM은 본질적으로 각 모델을 호출하는 함수를 사용한 것과 다름없으며, 모델들의 기능 설명을 입력받아 마치 Function Calling을 하듯 작동했다. 이러한 접근은 대형 언어 모델 + 도구/에이전트 생태계의 파워를 보여주며, 향후 Function Calling을 이용하여 LLM 간 토론이나 에이전트 협력까지 구현할 수 있음을 시사했다.
(현재(2025.05)도 LLM에게 마피아 게임을 시키는 등 관련 프로젝트가 진행 중이다.)

자율 에이전트 프레임워크 측면에서, 2023년에 큰 화제가 된 AutoGPTBabyAGI 같은 시스템을 들 수 있다. AutoGPT는 GPT-4를 기반으로, 사용자로부터 고수준의 목표를 받으면 스스로 하위 작업을 생성하고, 인터넷 검색, 코드 실행 등의 도구를 연쇄적으로 사용하면서 목표를 달성하려 시도하는 완전 자율 에이전트다. 초기 AutoGPT 구현은 OpenAI의 공식 툴 사용 API가 나오기 전에 만들어졌기 때문에, 모델에게 JSON 형식의 계획과 명령을 출력시키고 이를 파싱하는 등 상당히 복잡한 파이프라인을 구성했었다. Lilian Weng는 “AutoGPT가 주목받으며 LLM을 메인 컨트롤러로 한 자율 에이전트 가능성에 관심이 쏠렸지만, 자연어 인터페이스로 인한 신뢰성 이슈가 있었다”고 평했다. 다행이도 OpenAI가 정식으로 Function Calling을 지원하면서 AutoGPT 프로젝트도 나중에는 이를 지원하여, 모델이 browse_website(url: ...) 같은 함수를 직접 호출하게 업데이트되었다. 이는 자율 에이전트의 루프 구조[계획 수립 → 실행 → 결과 평가 → 다음 계획]에서, 함수 호출이 액션 실행 단계를 깔끔하게 담당할 수 있음을 보여준다. AutoGPT류의 에이전트들은 더욱 세련된 함수 호출 체계를 받아들여 신뢰성과 효율성을 향상시킬 것이다.

또 다른 맥락에서, Multi-Modal Agent로봇 제어 분야에서도 Function Calling 개념이 유용하게 쓰일 수 있다. 예를 들어 가정용 로봇에게 “부엌을 청소하고 설거지를 시작해”라고 지시하면, 언어 모델 에이전트가 move_to(location)activate_appliance(device) 같은 로봇 제어 API를 순차적으로 호출해 업무를 수행하는 시나리오가 가능하다. 이는 이미 간단한 형태로는 구현이 시작되었으며, OpenAI API와 로봇 제어 인터페이스를 연결하는 GPT-실세계 프로젝트들이 등장하고 있다. 이러한 맥락에서도 Function Calling은 자연어→프로그래밍 액션의 다리로 기능하며, 보다 복잡한 멀티스텝 작업을 안전하게 자동화하는 핵심 요소로 자리매김할 것이다.


결론

요약하면, Function Calling은 대형 언어 모델의 능력을 한 단계 확대하여 부 데이터 획득과 행동 수행을 가능케 하는 핵심 기술이다. 이 글에서는 Function Calling의 개념적 아키텍처와 OpenAI를 중심으로 한 구현 방법, 그리고 Anthropic 등 다양한 접근을 비교 분석하였다. Function Calling의 도입 배경에는 LLM의 한계를 보완하려는 요구가 있었으며, JSON 스키마 기반의 구조화된 함수 호출은 모델과 외부 시스템의 인터페이스를 명확히 정의함으로써 신뢰성 높은 상호작용을 이끌어냈다. 실제 코드 예시를 통해 살펴본 바와 같이, 모델이 능동적으로 함수 호출을 결정하게 함으로써 개발자는 보다 간결한 로직으로 복잡한 작업(예: 지식 검색, 데이터 처리)을 처리할 수 있다.

Function Calling은 현재도 진화 중인 기술로서, 연구 및 산업계의 다양한 응용과 접목되고 있다. LLM의 tool use에 관한 선행 연구들은 Function Calling의 개념적 기반을 제공하였고, 이러한 기능은 멀티에이전트 시스템이나 자율적인 AI 에이전트 개발에도 중요한 역할을 하고 있다. AI 에이전트가 스스로 계획을 세우고 외부 API를 호출하며 작업을 수행하는 미래를 그려볼 때, Function Calling은 그러한 자율지능 시스템의 표준 인터페이스로 작동할 것으로 기대된다. 이미 초기 자율 에이전트 예시에서 Function Calling을 도입함으로써 효율성과 안정성이 개선되는 사례들이 나타나고 있다.

마지막으로, Function Calling의 발전 방향과 관련하여 안전성확장성 측면의 과제가 남아 있다. 모델이 호출할 수 있는 함수의 범위를 어떻게 안전하게 제한할 것인지, 함수 호출에 따른 오류나 예외를 어떻게 처리할 것인지, 그리고 더욱 복잡한 다중 함수 시나리오에서 어떻게 원활한 대화 흐름을 유지할 것인지 등이 연구 과제로 지적됩니다. 또한, 업계 표준의 관점에서 25년 초 빠르게 성장한 Anthropic의 MCP처럼 공개 표준 프로토콜이 자리잡을지, 아니면 각 플랫폼별 API가 독자 발전할지도 향후 지켜봐야 할 부분이다. 그럼에도 분명한 것은, Function Calling은 LLM 시대의 새로운 패러다임으로서, 정적 지식에 머물던 언어 모델들을 Active한 지능형 에이전트로 변모시키고 있다는 사실이다. 앞으로도 이 분야의 기술 및 연구가 지속됨에 따라, AI 모델이 인간과 협업하여 현실 세계의 문제를 해결하는 능력은 더욱 정교하고 유용해질 것으로 기대된다!


추천글:

[LLM] GPT 100% 활용하기: Prompt Engineering 가이드


hyeon_B

안녕하세요! AI 기술을 이용해 더 나은 세상을 만들어 나가고 싶은 과기원생 Hyeon이라고 합니다. 저는 앞으로 인공지능 시대에는 지식을 '활용'하는 능력이 중요해질 것이라고 생각합니다. 대부분의 일들은 인공지능이 뛰어난 모습을 보이지만, 인공지능은 데이터로 부터 연관관계를 학습하기 때문에 지식들을 새로 통합해서 활용하는 능력이 부족합니다. 인공지능이 뉴턴 전에 만들어졌다면 사과가 떨어지는 이유에 대답하지 못했을 것이고, 아인슈타인 전에 만들어졌다면 중력이 어떻게 생기는지 설명하지 못했을 것입니다. 따라서 앞으로 우리는 '본질'을 탐구하고 그 본질로부터 다른 곳에 적용하며 인공지능을 현명하게 활용해야 할 것입니다. 함께 인공지능 시대를 준비합시다!

댓글 쓰기

다음 이전

POST ADS1

POST ADS 2