대화형 인공지능은 “오늘 날씨가 어때?” 라는 질문에 대답할 수 있을까요?
대형 언어 모델(Large Language Model, LLM) 단독으로는 불가능합니다.

대화형 인공지능은 정보 검색, 콘텐츠 생성, 고객 서비스 등 다양한 작업에서 사람을 돕고 있습니다. 그러나 이러한 기술의 근간이 되는 대형 언어 모델(Large Language Model, LLM)에는 분명한 한계가 존재합니다.

영화 <아이언맨>의 인공지능 비서 ‘자비스(J.A.R.V.I.S.)’는 외부 시스템에 접근하여 오늘의 날씨 검색부터 메일을 대신 작성해주거나 심지어 필요한 상품을 주문해주기도 합니다. 하지만 LLM은 가장 단순한 오늘의 날씨조차 직접적으로 답변할 수 없습니다.

이러한 한계를 극복하기 위해 Tool calling이 개발되었습니다.


ReAct

ReAct: Synergizing Reasoning and Acting in Language Models (Shunyu Yao, et al. 2022) 논문은 추론(Reasoning) 과정에 행동(Acting, Tool calling)을 결합하여, 내장 함수를 실행하거나 외부 API를 호출함으로써 기존 LLM의 한계를 뛰어넘는 새로운 가능성을 제시했습니다. Tool calling 개념은 ReAct 이전에도 존재했으나, ReAct는 이를 LLM에 효과적으로 접목시켜 유의미한 성과를 보여주었고, 이는 Model Context Protocol(MCP)의 등장을 가속화하는 계기가 되었습니다.

그림 1. Comparison of prompting methods
Shunyu Yao, et al. (2022)

ReAct는 'Thought -> Action -> Observation -> Thought -> Action -> Observation'의 순환 구조를 활용하여 외부 세계에서 얻은 정보로 추론을 보완하고, 환각(hallucination)을 줄이며, 더 신뢰할 수 있는 결정을 내릴 수 있게 했습니다.

이후 2023년에는 ChatGPT에 Function calling이 공식적으로 도입되었고, 2024년 11월에는 Anthropic이 Model Context Protocol(MCP)을 공개했습니다. Anthropic은 MCP 공개와 함께 Tool calling에 대한 지시 학습을 거친 Claude 3.7 Sonnet 모델을 선보였는데, 이는 Cursor AI와 같은 Coding Agent의 등장을 이끌었으며 전 세계적으로 LLM에 MCP를 접목하여 새로운 가능성을 탐구하는 계기가 되었습니다.


Tool calling

Tool calling이란 LLM이 함수, API, 외부 도구 등을 호출하고 상호작용할 수 있도록 돕는 기능입니다. 이는 LLM의 능력을 확장시켜 단순히 텍스트 생성과 이해를 넘어, 실제 세상의 정보를 검색하거나, 작업을 수행하거나, 특정 기능을 사용하는 등 훨씬 더 복잡하고 유용한 작업을 수행할 수 있도록 만듭니다.

Tool calling을 이용하여 외부 API를 호출하는 함수를 구현하기 위해서는 아래의 과정을 따릅니다.


1. Function

import requests

def get_weather(latitude, longitude):
    response = requests.get(f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m")
    data = response.json()
    return data['current']['temperature_2m']

Tool calling 기능 활용에 사용할 함수를 개발해야 합니다. 위의 코드는 위도와 경도를 외부 API에 전달하여 해당 위치의 날씨 정보를 얻는 함수입니다.


2. Function Description

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "Get current temperature for provided coordinates in celsius.",
    "parameters": {
      "type": "object",
      "properties": {
        "latitude": {"type": "number"},
        "longitude": {"type": "number"}
      },
      "required": ["latitude", "longitude"],
      "additionalProperties": false
    },
    "strict": true
  }
}

함수 개발 후에는 해당 함수에 대한 설명서를 작성해야 합니다. 이는 LLM이 호출 가능한 여러 함수 중 어떤 함수를 선택할지 결정하는 중요한 지표 역할을 하므로, 어떤 매개변수(arguments)를 입력으로 받고 어떤 출력을 하는지 명확하게 기술해야 합니다. 이러한 함수 설명서의 형태는 MCP의 등장 이후 어느 정도 정형화된 포맷으로 통일되었습니다.


3. Prompt (Qwen3)

<|im_start|>system
# Tools

You may call one or more functions to assist with the user query.

You are provided with function signatures within <tools></tools> XML tags:
<tools>
{"type": "function", "function": {"name": "get_weather", "description": "Get current temperature for provided coordinates in celsius.", "parameters": {"type": "object", "properties": {"latitude": {"type": "number"}, "longitude": {"type": "number"}}, "required": ["latitude", "longitude"]}}}
</tools>

For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call><|im_end|>

Tool Calling의 초기에는 ReAct, ReWOO(Binfeng Xu, et al.)와 같은 다양한 전략이 존재했지만, 최근에는 tokenizer_config.jsonchat_template 옵션에 Tool Calling을 위한 시스템 프롬프트가 모델별로 정의되어 있습니다. 이러한 프롬프트는 충분한 지시 학습(Instruction tuning)과 강화 학습(Reinforcement Learning from Human Feedback)을 거쳤기 때문에 별도로 입출력을 제어할 필요가 없습니다.

Qwen3의 경우, <tools></tools> 태그 안에 Function Description을 삽입한 후 시스템 프롬프트로 사용하면 됩니다.


4. Tool Calling

Input
<|im_start|>user
서울 날씨가 어때?<|im_end|>
Output
<|im_start|>assistant
<tool_call>
{"name": "get_weather", "arguments": {"latitude": 37.5665, "longitude": 126.978}}
</tool_call><|im_end|>
{
  "name": "get_weather",
  "arguments": {
    "latitude": 37.5665,
    "longitude": 126.978
  }
}

마찬가지로 Qwen3의 경우, “서울 날씨가 어때?”가 입력으로 들어오면 위의 Input txt와 같이 변환되어 LLM 입력으로 사용되고, 출력은 Ouput txt와 같은 형태의 문자열을 나타납니다.


5. Response Generation

Response
{
  "time": "2025-07-02T05:45",
  "interval": 900,
  "temperature_2m": 27.9,
  "wind_speed_10m": 5.3,
  "weather_code": 51
}
Generation
<|im_start|>user
<tool_response>
{"time": "2025-07-02T05:45", "interval": 900, "temperature_2m": 27.9, "wind_speed_10m": 5.3, "weather_code": 51}
</tool_response><|im_end|>
<|im_start|>assistant
현재 서울은 가랑비가 내리고 있으며, 기온은 27.9°C입니다.
<|im_end|>

현재 프로세스에서 마지막으로 받은 LLM 응답은 어떤 함수를 호출해야 할지에 대한 JSON 형태의 문자열입니다. 우리는 이 문자열을 참고하여 함수를 호출했고 그 결과는 아마도 JSON 형태의 데이터일 것입니다.

이 응답 결과를 LLM에 다시 전달하면 프로세스는 두 가지 분기로 나뉩니다. 첫 번째는 Tool Calling을 다시 실행하는 경우입니다. LLM이 최종 결과물을 생성하기에 지식이 부족하다고 판단하면, <tools></tools> 태그 내부의 함수들을 참고하여 Tool Calling을 다시 실행합니다.

두 번째는 자연어 형태의 최종 결과를 출력하는 것입니다. LLM이 추가적인 지식이 필요 없다고 판단하면, 사용자 질의(User query)와 Tool response를 참고하여 최종 출력 컨텍스트를 생성합니다.


Reference

[1] Yao, Shunyu (2022) | ReAct: Synergizing Reasoning and Acting in Language Models

[2] OpenAI | Function calling

[3] Qwen3 | Chat Template