OpenAI의 ChatCompletion API에서 Function Calling(함수 호출) 기능을 통해 모델이 스스로 적절한 함수를 선택하고, 필요한 인자를 구성하여 호출할 수 있게 되었습니다. 기존에는 모델의 답변을 파싱하여 사용자가 직접 “필요한 정보를 추출”하곤 했지만, 이제는 모델이 명시적으로 “특정 함수”를 호출하고, 그 결과를 받아 다음 문맥에 활용할 수 있습니다.
이 글에서는 a부터 z까지 총 26개의 함수가 서로 의존성을 갖는 예시를 통해, Function Calling을 어떻게 구성할 수 있는지 살펴보겠습니다. 각 함수는 이전 함수가 만들어낸 “토큰”을 입력받아 새로운 “토큰”을 생성하고, 마지막에 a_func
가 최종 결과를 만들어내는 구조입니다.
1. 시나리오: a_func를 호출하기 위해 z_func부터 차례대로 호출
예시 상황은 다음과 같습니다.
- z_func: 의존성이 전혀 없는 함수. 내부적으로
"z"
라는 토큰을 만듦 - y_func:
z_token
을 받아서"y" + z_token
을 리턴 - x_func:
y_token
→"x" + y_token
- …
- b_func:
c_token
→"b" + c_token
- a_func:
b_token
→"a" + b_token
(최종 결과)
사용자는 “A 함수를 호출해”라고만 말해도, 모델은 z_func → y_func → x_func → ... → b_func → a_func
순으로 호출해야 한다는 사실을 인식하고, 중간 단계 함수들을 모두 호출할 수 있습니다.
2. 전체 예시 코드
아래 코드에서는:
z_func ~ a_func
: 파이썬 상의 실제 함수들(문자열을 이어붙이는 단순 예시)tools
: OpenAI에 노출할 함수의 정의(각 함수 이름, 설명, 파라미터 스키마)toolkits
: 함수 이름과 실제 파이썬 함수의 매핑openai_llm(...)
: ChatCompletion API를 호출하고, 모델이 함수 호출을 요청하면 실제로 파이썬 함수를 실행하여 결과를 다시 모델에게 전달(while 루프로 재시도)
코드 전문
# -*- coding: utf-8 -*-
"""
function_calling_demo.py
OpenAI의 Function Calling 기능을 활용해,
a_func부터 z_func까지 26개의 함수가 서로 의존성을 가지도록 구성한 예시 코드.
'도구(tools)' 정의를 통해 모델이 필요할 때 각 함수를 호출하고,
함수 결과를 다시 모델에 전달하는 과정을 반복하여 최종 값을 얻는다.
"""
from openai import OpenAI
import requests
import json
import yfinance as yf
########################################
# 1. OpenAI 클라이언트 초기화
########################################
client = OpenAI(
api_key="sk-p..."
)
########################################
# 2. a~z 함수 정의
# - z_func: 의존성 없는 가장 기초
# - y_func: z_token 필요
# - ...
# - a_func: b_token 필요(최종)
#
# 실제 로직(토큰 생성 등)은 단순히 문자열을 이어붙이는 것으로 예시 구성
########################################
def z_func():
"""z_func: 의존성 없음, 결과로 'z'를 리턴"""
return "z"
def y_func(z_token):
"""y_func: z_token -> 'y' + z_token"""
return "y" + z_token
def x_func(y_token):
"""x_func: y_token -> 'x' + y_token"""
return "x" + y_token
def w_func(x_token):
"""w_func: x_token -> 'w' + x_token"""
return "w" + x_token
def v_func(w_token):
"""v_func: w_token -> 'v' + w_token"""
return "v" + w_token
def u_func(v_token):
"""u_func: v_token -> 'u' + v_token"""
return "u" + v_token
def t_func(u_token):
"""t_func: u_token -> 't' + u_token"""
return "t" + u_token
def s_func(t_token):
"""s_func: t_token -> 's' + t_token"""
return "s" + t_token
def r_func(s_token):
"""r_func: s_token -> 'r' + s_token"""
return "r" + s_token
def q_func(r_token):
"""q_func: r_token -> 'q' + r_token"""
return "q" + r_token
def p_func(q_token):
"""p_func: q_token -> 'p' + q_token"""
return "p" + q_token
def o_func(p_token):
"""o_func: p_token -> 'o' + p_token"""
return "o" + p_token
def n_func(o_token):
"""n_func: o_token -> 'n' + o_token"""
return "n" + o_token
def m_func(n_token):
"""m_func: n_token -> 'm' + n_token"""
return "m" + n_token
def l_func(m_token):
"""l_func: m_token -> 'l' + m_token"""
return "l" + m_token
def k_func(l_token):
"""k_func: l_token -> 'k' + l_token"""
return "k" + l_token
def j_func(k_token):
"""j_func: k_token -> 'j' + k_token"""
return "j" + k_token
def i_func(j_token):
"""i_func: j_token -> 'i' + j_token"""
return "i" + j_token
def h_func(i_token):
"""h_func: i_token -> 'h' + i_token"""
return "h" + i_token
def g_func(h_token):
"""g_func: h_token -> 'g' + h_token"""
return "g" + h_token
def f_func(g_token):
"""f_func: g_token -> 'f' + g_token"""
return "f" + g_token
def e_func(f_token):
"""e_func: f_token -> 'e' + f_token"""
return "e" + f_token
def d_func(e_token):
"""d_func: e_token -> 'd' + e_token"""
return "d" + e_token
def c_func(d_token):
"""c_func: d_token -> 'c' + d_token"""
return "c" + d_token
def b_func(c_token):
"""b_func: c_token -> 'b' + c_token"""
return "b" + c_token
def a_func(b_token):
"""a_func: b_token -> 'a' + b_token (최종)"""
return "a" + b_token
##############################################
# 3. tools 정의 (Function Calling용 스키마)
# - a~z 모든 함수를 등록하고, 각 함수에 필요한 인자와 설명을 JSON Schema로 표시
##############################################
tools = [
{
"type": "function",
"function": {
"name": "z_func",
"description": (
"Z 함수: 의존성이 없는 가장 기초 함수. 결과값으로 z_token을 "
"생성한다고 가정. 다른 많은 함수들이 z_token을 필요로 할 수 있음."
),
"parameters": {
"type": "object",
"properties": {
# z_func는 인자가 없음
},
"required": []
},
},
},
{
"type": "function",
"function": {
"name": "y_func",
"description": (
"Y 함수: z_token이 필요. z_func의 결과로부터 z_token을 받아서 "
"y_token을 생성한다고 가정."
),
"parameters": {
"type": "object",
"properties": {
"z_token": {
"type": "string",
"description": "z_func 호출 결과로 얻은 토큰"
}
},
"required": ["z_token"]
},
},
},
{
"type": "function",
"function": {
"name": "x_func",
"description": (
"X 함수: y_token이 필요. y_func의 결과로부터 y_token을 받아서 "
"x_token을 생성한다고 가정."
),
"parameters": {
"type": "object",
"properties": {
"y_token": {
"type": "string",
"description": "y_func 호출 결과로 얻은 토큰"
}
},
"required": ["y_token"]
},
},
},
{
"type": "function",
"function": {
"name": "w_func",
"description": "W 함수: x_token 필요, 결과로 w_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"x_token": {
"type": "string",
"description": "x_func 호출 결과로 얻은 토큰"
}
},
"required": ["x_token"]
},
},
},
{
"type": "function",
"function": {
"name": "v_func",
"description": "V 함수: w_token 필요, 결과로 v_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"w_token": {
"type": "string",
"description": "w_func 호출 결과로 얻은 토큰"
}
},
"required": ["w_token"]
},
},
},
{
"type": "function",
"function": {
"name": "u_func",
"description": "U 함수: v_token 필요, 결과로 u_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"v_token": {
"type": "string",
"description": "v_func 호출 결과로 얻은 토큰"
}
},
"required": ["v_token"]
},
},
},
{
"type": "function",
"function": {
"name": "t_func",
"description": "T 함수: u_token 필요, 결과로 t_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"u_token": {
"type": "string",
"description": "u_func 호출 결과로 얻은 토큰"
}
},
"required": ["u_token"]
},
},
},
{
"type": "function",
"function": {
"name": "s_func",
"description": "S 함수: t_token 필요, 결과로 s_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"t_token": {
"type": "string",
"description": "t_func 호출 결과로 얻은 토큰"
}
},
"required": ["t_token"]
},
},
},
{
"type": "function",
"function": {
"name": "r_func",
"description": "R 함수: s_token 필요, 결과로 r_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"s_token": {
"type": "string",
"description": "s_func 호출 결과로 얻은 토큰"
}
},
"required": ["s_token"]
},
},
},
{
"type": "function",
"function": {
"name": "q_func",
"description": "Q 함수: r_token 필요, 결과로 q_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"r_token": {
"type": "string",
"description": "r_func 호출 결과로 얻은 토큰"
}
},
"required": ["r_token"]
},
},
},
{
"type": "function",
"function": {
"name": "p_func",
"description": "P 함수: q_token 필요, 결과로 p_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"q_token": {
"type": "string",
"description": "q_func 호출 결과로 얻은 토큰"
}
},
"required": ["q_token"]
},
},
},
{
"type": "function",
"function": {
"name": "o_func",
"description": "O 함수: p_token 필요, 결과로 o_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"p_token": {
"type": "string",
"description": "p_func 호출 결과로 얻은 토큰"
}
},
"required": ["p_token"]
},
},
},
{
"type": "function",
"function": {
"name": "n_func",
"description": "N 함수: o_token 필요, 결과로 n_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"o_token": {
"type": "string",
"description": "o_func 호출 결과로 얻은 토큰"
}
},
"required": ["o_token"]
},
},
},
{
"type": "function",
"function": {
"name": "m_func",
"description": "M 함수: n_token 필요, 결과로 m_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"n_token": {
"type": "string",
"description": "n_func 호출 결과로 얻은 토큰"
}
},
"required": ["n_token"]
},
},
},
{
"type": "function",
"function": {
"name": "l_func",
"description": "L 함수: m_token 필요, 결과로 l_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"m_token": {
"type": "string",
"description": "m_func 호출 결과로 얻은 토큰"
}
},
"required": ["m_token"]
},
},
},
{
"type": "function",
"function": {
"name": "k_func",
"description": "K 함수: l_token 필요, 결과로 k_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"l_token": {
"type": "string",
"description": "l_func 호출 결과로 얻은 토큰"
}
},
"required": ["l_token"]
},
},
},
{
"type": "function",
"function": {
"name": "j_func",
"description": "J 함수: k_token 필요, 결과로 j_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"k_token": {
"type": "string",
"description": "k_func 호출 결과로 얻은 토큰"
}
},
"required": ["k_token"]
},
},
},
{
"type": "function",
"function": {
"name": "i_func",
"description": "I 함수: j_token 필요, 결과로 i_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"j_token": {
"type": "string",
"description": "j_func 호출 결과로 얻은 토큰"
}
},
"required": ["j_token"]
},
},
},
{
"type": "function",
"function": {
"name": "h_func",
"description": "H 함수: i_token 필요, 결과로 h_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"i_token": {
"type": "string",
"description": "i_func 호출 결과로 얻은 토큰"
}
},
"required": ["i_token"]
},
},
},
{
"type": "function",
"function": {
"name": "g_func",
"description": "G 함수: h_token 필요, 결과로 g_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"h_token": {
"type": "string",
"description": "h_func 호출 결과로 얻은 토큰"
}
},
"required": ["h_token"]
},
},
},
{
"type": "function",
"function": {
"name": "f_func",
"description": "F 함수: g_token 필요, 결과로 f_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"g_token": {
"type": "string",
"description": "g_func 호출 결과로 얻은 토큰"
}
},
"required": ["g_token"]
},
},
},
{
"type": "function",
"function": {
"name": "e_func",
"description": "E 함수: f_token 필요, 결과로 e_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"f_token": {
"type": "string",
"description": "f_func 호출 결과로 얻은 토큰"
}
},
"required": ["f_token"]
},
},
},
{
"type": "function",
"function": {
"name": "d_func",
"description": "D 함수: e_token 필요, 결과로 d_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"e_token": {
"type": "string",
"description": "e_func 호출 결과로 얻은 토큰"
}
},
"required": ["e_token"]
},
},
},
{
"type": "function",
"function": {
"name": "c_func",
"description": "C 함수: d_token 필요, 결과로 c_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"d_token": {
"type": "string",
"description": "d_func 호출 결과로 얻은 토큰"
}
},
"required": ["d_token"]
},
},
},
{
"type": "function",
"function": {
"name": "b_func",
"description": "B 함수: c_token 필요, 결과로 b_token을 만든다고 가정.",
"parameters": {
"type": "object",
"properties": {
"c_token": {
"type": "string",
"description": "c_func 호출 결과로 얻은 토큰"
}
},
"required": ["c_token"]
},
},
},
{
"type": "function",
"function": {
"name": "a_func",
"description": (
"A 함수: b_token이 필요. b_func 호출 결과로 b_token을 받고, "
"최종 결과를 생성한다고 가정."
),
"parameters": {
"type": "object",
"properties": {
"b_token": {
"type": "string",
"description": "b_func 호출 결과로 얻은 토큰"
}
},
"required": ["b_token"]
},
},
}
]
##############################################
# 4. 실제 Python 함수(코드)와 tool name 매핑
##############################################
toolkits = {
"z_func": z_func,
"y_func": y_func,
"x_func": x_func,
"w_func": w_func,
"v_func": v_func,
"u_func": u_func,
"t_func": t_func,
"s_func": s_func,
"r_func": r_func,
"q_func": q_func,
"p_func": p_func,
"o_func": o_func,
"n_func": n_func,
"m_func": m_func,
"l_func": l_func,
"k_func": k_func,
"j_func": j_func,
"i_func": i_func,
"h_func": h_func,
"g_func": g_func,
"f_func": f_func,
"e_func": e_func,
"d_func": d_func,
"c_func": c_func,
"b_func": b_func,
"a_func": a_func,
}
########################################
# 5. 시스템 메시지
# - 모델에게 "알아서 필요한 함수는 호출하라" 고 지시
########################################
system_prompt = """
You are a helpful assistant. Write in Korean.
You have access to the following tools. If the user asks for something that requires calling a function, call it with the correct arguments.
Otherwise, respond directly in natural language.
"""
########################################
# 6. 대화(챗) + 함수 호출 로직
########################################
def openai_llm(user_input, chat_history):
"""
- chat_history에 대화 내용(system, user, assistant, tool 등)을 순차적으로 기록
- 모델에게 질의 후, 만약 tool_calls가 있으면 해당 함수를 실제로 호출하고,
그 결과를 tool 메시지로 추가
- 다시 모델에게 재요청을 반복하여, 함수 호출이 더 이상 없을 때 최종 답변을 반환
"""
# 최초 대화에 system 메시지를 넣음
if len(chat_history) == 0:
chat_history.append({"role": "system", "content": system_prompt})
# 사용자 메시지를 추가
chat_history.append({"role": "user", "content": user_input})
# 함수 호출이 없을 때까지 반복
while True:
# 1) OpenAI ChatCompletion 요청
completion = client.chat.completions.create(
model="gpt-4o", # 예시 모델
messages=chat_history,
tools=tools
)
# 모델이 생성한 assistant 메시지를 획득
assistant_msg = completion.choices[0].message
# assistant_msg를 대화 기록에 추가
chat_history.append(assistant_msg)
# 2) 모델이 함수 호출을 했는지 (tool_calls) 확인
if not hasattr(assistant_msg, "tool_calls") or not assistant_msg.tool_calls:
# 함수 호출이 없으면 일반 답변으로 간주, while 종료
break
# 3) 함수 호출이 있을 경우 -> 실제 파이썬 함수를 호출
for tool_call in assistant_msg.tool_calls:
fn_name = tool_call.function.name
fn_args_str = tool_call.function.arguments
# 함수 인자를 JSON 디코딩
fn_args = json.loads(fn_args_str) if fn_args_str else {}
print(fn_name, fn_args_str) # 디버그 출력 (어떤 함수가 어떻게 호출되는지)
# 실제 함수 실행
result = toolkits[fn_name](**fn_args)
# 실행 결과를 role="tool" 메시지로 추가
tool_message = {
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
}
chat_history.append(tool_message)
# 4) tool 메시지를 추가한 뒤, while 루프를 반복하여
# 모델이 다시 한 번(또는 여러 번) 함수 호출할지 확인
# 없으면 break
# 함수 호출이 없을 때(while 탈출 시), 최종적으로 assistant_msg.content 리턴
return assistant_msg.content
########################################
# 7. 실행 예시
########################################
if __name__ == "__main__":
# 첫 테스트: "A 함수를 호출해"라고 요청해보기
# 모델이 z부터 b까지 차례로 호출 후 최종적으로 a_func를 호출하는지 확인
answer = openai_llm("A 함수를 호출해", [])
print("A I >", answer)
# 대화 루프 예시
# - 사용자가 원하는 요청을 입력하면, 모델이
# 필요한 함수를 순차적으로 호출할 수 있음
chat_history = []
while True:
user_input = input("\nUSER> ")
if user_input.lower() == "quit":
break
ai_response = openai_llm(user_input, chat_history)
print(f"A I> {ai_response}")
3. 동작 확인
예시로, “A 함수를 호출해”라고 입력하면, 콘솔에 함수 호출 과정이 순서대로 찍히고, 최종적으로 “a_func”가 호출된 결과(문자열 "abcdefghijklmnopqrstuvwxyz"
등)가 출력됩니다.
실제로는 모델이 단계마다 z_func
를 호출하여 "z"
토큰을 만들고, y_func(z_token="z")
를 호출해 "yz"
를 만든 뒤, x_func(y_token="yz")
를 호출해 "xyz"
… 이런 식으로 중간 함수 결과를 축적합니다.
콘솔 로그:

4. 정리 및 활용 아이디어
- Function Calling을 통해, 단순 “모델 출력 파싱” 과정을 자동화
- 복잡한 함수 간 의존이 있어도, 모델이 “먼저 필요한 정보를 얻기 위한 함수 → 후속 함수” 순으로 여러 번 호출 가능
- 실제로는 사내 API, DB, 외부 API 등을 호출하여 다양한 작업을 수행할 수 있음
- 함수 개수가 많을수록(10~20개 이상) 모델이 올바른 함수를 찾기 어려울 수 있으므로, 함수명을 직관적으로 지어주고, 시스템 메시지나 함수
description
에서 활용 맥락을 자세히 안내해주면 좋습니다.
이로써, 서로 의존관계를 가지는 다수의 함수를 OpenAI Function Calling으로 유연하게 구성하는 방법을 살펴봤습니다.
Reference