본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 3장: 함수 호출(Function Calling)과 도구 사용
2026년 3월 24일·AI / ML·

3장: 함수 호출(Function Calling)과 도구 사용

Function Calling의 원리를 이해하고, OpenAI/Anthropic/Google의 도구 호출 인터페이스로 구조화된 출력을 생성하는 방법을 학습합니다.

15분856자9개 섹션
structured-outputaidata-engineeringllm
공유
structured-output3 / 10
12345678910
이전2장: JSON Schema 기반 LLM 출력 제어다음4장: Pydantic과 타입 안전 출력

학습 목표

  • Function Calling의 동작 원리와 메시지 흐름을 이해합니다
  • OpenAI, Anthropic, Google의 도구 호출 인터페이스를 비교합니다
  • 병렬 도구 호출과 강제 도구 선택 패턴을 학습합니다
  • 도구 결과 처리와 에이전트 루프 통합 방법을 익힙니다

Function Calling의 원리

**Function Calling(함수 호출)**은 LLM이 외부 도구나 API를 호출할 수 있게 하는 기능입니다. 원래는 날씨 조회, 데이터베이스 검색 같은 외부 작업을 위해 설계되었지만, 구조화된 출력을 생성하는 강력한 수단으로도 활용됩니다.

동작 흐름

Function Calling의 전체 흐름은 다음과 같습니다.

구조화된 출력 용도로 사용할 때는 실제 도구를 실행하지 않고, LLM이 생성한 도구 호출 인자를 바로 구조화된 데이터로 사용합니다.

도구 정의 구조

도구 정의는 함수의 이름, 설명, 매개변수 스키마로 구성됩니다. 매개변수 스키마가 곧 원하는 출력의 구조를 결정합니다.

tool_definition.py
python
tool = {
    "type": "function",
    "function": {
        "name": "extract_entities",
        "description": "텍스트에서 개체명(Named Entity)을 추출합니다.",
        "parameters": {
            "type": "object",
            "properties": {
                "persons": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "description": "인물의 전체 이름"
                            },
                            "role": {
                                "type": "string",
                                "description": "텍스트에서 파악된 역할이나 직함"
                            }
                        },
                        "required": ["name", "role"]
                    },
                    "description": "텍스트에 언급된 인물 목록"
                },
                "organizations": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "텍스트에 언급된 조직/기관 목록"
                },
                "locations": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "텍스트에 언급된 장소 목록"
                }
            },
            "required": ["persons", "organizations", "locations"]
        }
    }
}

프로바이더별 도구 호출 인터페이스

OpenAI

OpenAI의 도구 호출은 tools 매개변수로 도구를 정의하고, 응답의 tool_calls에서 결과를 추출합니다.

openai_tool_calling.py
python
from openai import OpenAI
import json
 
client = OpenAI()
 
response = client.chat.completions.create(
    model="gpt-4o-2026-02",
    messages=[
        {
            "role": "user",
            "content": "삼성전자 이재용 회장이 서울 서초구 본사에서 기자회견을 열었습니다."
        }
    ],
    tools=[tool],  # 위에서 정의한 도구
    tool_choice={"type": "function", "function": {"name": "extract_entities"}}
)
 
# 도구 호출 결과 추출
tool_call = response.choices[0].message.tool_calls[0]
entities = json.loads(tool_call.function.arguments)
print(json.dumps(entities, ensure_ascii=False, indent=2))
output.json
json
{
  "persons": [
    {"name": "이재용", "role": "삼성전자 회장"}
  ],
  "organizations": ["삼성전자"],
  "locations": ["서울 서초구"]
}

Anthropic

Anthropic Claude의 도구 호출은 tools와 tool_choice를 사용합니다. 응답 구조가 OpenAI와 다르므로 추출 방식이 달라집니다.

anthropic_tool_calling.py
python
import anthropic
import json
 
client = anthropic.Anthropic()
 
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[
        {
            "name": "extract_entities",
            "description": "텍스트에서 개체명을 추출합니다.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "persons": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string"},
                                "role": {"type": "string"}
                            },
                            "required": ["name", "role"]
                        }
                    },
                    "organizations": {
                        "type": "array",
                        "items": {"type": "string"}
                    },
                    "locations": {
                        "type": "array",
                        "items": {"type": "string"}
                    }
                },
                "required": ["persons", "organizations", "locations"]
            }
        }
    ],
    tool_choice={"type": "tool", "name": "extract_entities"},
    messages=[
        {
            "role": "user",
            "content": "삼성전자 이재용 회장이 서울 서초구 본사에서 기자회견을 열었습니다."
        }
    ]
)
 
# Anthropic은 content 블록에서 tool_use 타입을 찾아야 합니다
for block in response.content:
    if block.type == "tool_use":
        entities = block.input
        print(json.dumps(entities, ensure_ascii=False, indent=2))

Google Gemini

Google Gemini도 Function Calling을 지원하며, Python SDK에서는 함수 선언 방식이 약간 다릅니다.

gemini_tool_calling.py
python
import google.generativeai as genai
 
genai.configure(api_key="YOUR_API_KEY")
 
extract_entities = genai.protos.FunctionDeclaration(
    name="extract_entities",
    description="텍스트에서 개체명을 추출합니다.",
    parameters=genai.protos.Schema(
        type=genai.protos.Type.OBJECT,
        properties={
            "persons": genai.protos.Schema(
                type=genai.protos.Type.ARRAY,
                items=genai.protos.Schema(
                    type=genai.protos.Type.OBJECT,
                    properties={
                        "name": genai.protos.Schema(type=genai.protos.Type.STRING),
                        "role": genai.protos.Schema(type=genai.protos.Type.STRING),
                    },
                    required=["name", "role"]
                )
            ),
            "organizations": genai.protos.Schema(
                type=genai.protos.Type.ARRAY,
                items=genai.protos.Schema(type=genai.protos.Type.STRING)
            ),
            "locations": genai.protos.Schema(
                type=genai.protos.Type.ARRAY,
                items=genai.protos.Schema(type=genai.protos.Type.STRING)
            )
        },
        required=["persons", "organizations", "locations"]
    )
)
 
tool = genai.protos.Tool(function_declarations=[extract_entities])
model = genai.GenerativeModel("gemini-2.0-flash", tools=[tool])
 
response = model.generate_content(
    "삼성전자 이재용 회장이 서울 서초구 본사에서 기자회견을 열었습니다.",
    tool_config={"function_calling_config": {"mode": "ANY"}}
)
 
# 함수 호출 결과 추출
fc = response.candidates[0].content.parts[0].function_call
print(type(fc).to_dict(fc))

병렬 도구 호출

LLM이 한 번의 응답에서 여러 도구를 동시에 호출하는 **Parallel Tool Calling(병렬 도구 호출)**은 복잡한 작업을 효율적으로 처리하는 데 유용합니다.

parallel_tool_calling.py
python
from openai import OpenAI
import json
 
client = OpenAI()
 
# 여러 도구를 한번에 정의
tools = [
    {
        "type": "function",
        "function": {
            "name": "extract_sentiment",
            "description": "텍스트의 감성을 분석합니다.",
            "parameters": {
                "type": "object",
                "properties": {
                    "sentiment": {
                        "type": "string",
                        "enum": ["positive", "negative", "neutral"]
                    },
                    "score": {"type": "number"}
                },
                "required": ["sentiment", "score"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "extract_topics",
            "description": "텍스트의 주제를 추출합니다.",
            "parameters": {
                "type": "object",
                "properties": {
                    "topics": {
                        "type": "array",
                        "items": {"type": "string"}
                    },
                    "primary_topic": {"type": "string"}
                },
                "required": ["topics", "primary_topic"]
            }
        }
    }
]
 
response = client.chat.completions.create(
    model="gpt-4o-2026-02",
    messages=[
        {
            "role": "system",
            "content": "텍스트를 분석하여 감성과 주제를 모두 추출하세요."
        },
        {
            "role": "user",
            "content": "최신 AI 기술이 의료 분야에 혁신을 가져오고 있어 매우 기대됩니다."
        }
    ],
    tools=tools,
    parallel_tool_calls=True
)
 
# 여러 도구 호출 결과를 각각 처리
for tool_call in response.choices[0].message.tool_calls:
    name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)
    print(f"[{name}] {json.dumps(args, ensure_ascii=False)}")
Info

병렬 도구 호출은 OpenAI에서 기본적으로 활성화되어 있습니다. 구조화된 출력 용도로 사용할 때는 parallel_tool_calls=False로 설정하여 하나의 도구만 호출하도록 강제하는 것이 일반적입니다.


강제 도구 선택

구조화된 출력을 보장하려면 LLM이 반드시 특정 도구를 호출하도록 강제해야 합니다. 각 프로바이더의 강제 도구 선택 방식을 비교합니다.

프로바이더설정설명
OpenAItool_choice={"type": "function", "function": {"name": "도구명"}}특정 도구 강제 호출
OpenAItool_choice="required"아무 도구든 반드시 호출
OpenAItool_choice="auto"LLM이 호출 여부 결정 (기본값)
Anthropictool_choice={"type": "tool", "name": "도구명"}특정 도구 강제 호출
Anthropictool_choice={"type": "any"}아무 도구든 반드시 호출
Googletool_config={"function_calling_config": {"mode": "ANY"}}도구 호출 강제

구조화된 출력을 얻으려면 특정 도구를 강제로 지정하는 것이 가장 안전합니다. auto 모드에서는 LLM이 도구를 호출하지 않고 일반 텍스트로 응답할 수 있기 때문입니다.


도구 결과 처리와 에이전트 루프

실제 도구를 실행하는 완전한 에이전트 루프에서는 도구 호출 결과를 다시 LLM에 전달하여 후속 판단을 이끌어냅니다.

agent_loop.py
python
from openai import OpenAI
import json
 
client = OpenAI()
 
def run_agent(user_message: str, tools: list, available_functions: dict) -> str:
    """도구 호출을 포함하는 에이전트 루프"""
    messages = [
        {"role": "system", "content": "사용자 요청을 처리하기 위해 필요한 도구를 호출하세요."},
        {"role": "user", "content": user_message}
    ]
 
    max_iterations = 10
    for _ in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-4o-2026-02",
            messages=messages,
            tools=tools,
        )
 
        message = response.choices[0].message
        messages.append(message)
 
        # 도구 호출이 없으면 최종 응답으로 판단
        if not message.tool_calls:
            return message.content
 
        # 각 도구 호출을 실행하고 결과를 메시지에 추가
        for tool_call in message.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)
 
            # 실제 함수 실행
            func = available_functions[func_name]
            result = func(**func_args)
 
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result, ensure_ascii=False)
            })
 
    return "최대 반복 횟수에 도달했습니다."

구조화된 출력 전용 패턴

에이전트 루프와 달리, 구조화된 출력만 필요한 경우에는 도구 호출 인자를 바로 사용하고 루프를 종료합니다.

structured_output_pattern.py
python
from openai import OpenAI
import json
from typing import Any
 
client = OpenAI()
 
def get_structured_output(
    prompt: str,
    schema: dict,
    schema_name: str = "output",
    model: str = "gpt-4o-2026-02"
) -> dict[str, Any]:
    """도구 호출을 활용한 구조화된 출력 추출 유틸리티"""
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        tools=[
            {
                "type": "function",
                "function": {
                    "name": schema_name,
                    "description": "구조화된 출력을 생성합니다.",
                    "parameters": schema
                }
            }
        ],
        tool_choice={"type": "function", "function": {"name": schema_name}},
        parallel_tool_calls=False
    )
 
    tool_call = response.choices[0].message.tool_calls[0]
    return json.loads(tool_call.function.arguments)
 
 
# 사용 예시
result = get_structured_output(
    prompt="Python의 장단점을 분석하세요.",
    schema={
        "type": "object",
        "properties": {
            "pros": {
                "type": "array",
                "items": {"type": "string"},
                "description": "장점 목록"
            },
            "cons": {
                "type": "array",
                "items": {"type": "string"},
                "description": "단점 목록"
            },
            "overall_rating": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10,
                "description": "종합 평점 (1-10)"
            }
        },
        "required": ["pros", "cons", "overall_rating"]
    },
    schema_name="analyze_language"
)
Tip

도구 호출 기반 구조화된 출력은 네이티브 JSON Schema 방식을 지원하지 않는 모델에서도 사용할 수 있는 범용적인 패턴입니다. Anthropic Claude처럼 네이티브 JSON 모드가 없는 모델에서 특히 유용합니다.


Function Calling vs Structured Outputs 비교

두 접근 방식의 적합한 사용 시나리오를 정리합니다.

시나리오권장 방식이유
단순 데이터 추출네이티브 Structured Output100% 스키마 보장, 간단한 구현
외부 API 연동Function Calling실제 도구 실행이 필요
멀티 프로바이더 지원Function Calling모든 주요 프로바이더가 지원
에이전트 시스템Function Calling루프와 도구 실행이 핵심
복합 작업 (추출 + 실행)둘 다추출은 Structured Output, 실행은 Function Calling

정리

이번 장에서는 Function Calling의 동작 원리와 프로바이더별 구현 방식을 살펴보았습니다.

핵심 내용을 정리하면 다음과 같습니다.

  • Function Calling은 도구 호출 인자를 구조화된 출력으로 활용하는 패턴입니다
  • OpenAI, Anthropic, Google 모두 도구 호출을 지원하지만, API 인터페이스가 다릅니다
  • tool_choice로 특정 도구를 강제 지정하면 구조화된 출력을 보장할 수 있습니다
  • 병렬 도구 호출은 복합 분석에 유용하지만, 구조화된 출력 용도에서는 단일 호출이 일반적입니다
  • 에이전트 루프는 도구 결과를 다시 LLM에 전달하여 반복적인 추론을 수행합니다

다음 장 미리보기

4장에서는 Pydantic을 활용한 타입 안전 출력을 다룹니다. Pydantic v2 모델 정의, JSON Schema 자동 생성, Instructor 라이브러리를 통한 자동 재시도와 스트리밍, 그리고 TypeScript의 Zod까지 포괄적으로 학습합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#structured-output#ai#data-engineering#llm

관련 글

AI / ML

4장: Pydantic과 타입 안전 출력

Pydantic v2로 LLM 출력 스키마를 정의하고, Instructor 라이브러리로 자동 재시도와 스트리밍 구조화 출력을 구현합니다.

2026년 3월 26일·15분
AI / ML

2장: JSON Schema 기반 LLM 출력 제어

JSON Schema 기초 문법을 학습하고, OpenAI, Anthropic, Google 주요 프로바이더의 구조화된 출력 API를 실습합니다.

2026년 3월 22일·13분
AI / ML

5장: 비정형 데이터에서 구조화된 정보 추출

PDF, 이미지, 웹페이지 등 비정형 데이터에서 LLM을 활용하여 구조화된 정보를 추출하는 실전 기법을 학습합니다.

2026년 3월 28일·18분
이전 글2장: JSON Schema 기반 LLM 출력 제어
다음 글4장: Pydantic과 타입 안전 출력

댓글

목차

약 15분 남음
  • 학습 목표
  • Function Calling의 원리
    • 동작 흐름
    • 도구 정의 구조
  • 프로바이더별 도구 호출 인터페이스
    • OpenAI
    • Anthropic
    • Google Gemini
  • 병렬 도구 호출
  • 강제 도구 선택
  • 도구 결과 처리와 에이전트 루프
    • 구조화된 출력 전용 패턴
  • Function Calling vs Structured Outputs 비교
  • 정리
  • 다음 장 미리보기