AI

태태코딩 - python 툴 설명

태태코 2026. 9. 28. 14:29
반응형

도구가 왜 필요할까

LLM은 학습한 내용 안에서만 답할 수 있다. 그래서 이런 질문에는 답하지 못한다.

  • 지금 몇 시야? → 현재 시각을 모름
  • 이 웹페이지에 뭐 있어? → 인터넷에 접속 못 함
  • 우리 DB에 이번 달 데이터 몇 건이야? → 내 시스템에 접근 못 함

이 한계를 넘게 해주는 게 도구(Tool)다. 내가 만든 파이썬 함수를 LLM이 필요할 때 불러 쓸 수 있게 해주는 기능이고, OpenAI에서는 Function Calling이라고 부른다.

가장 중요한 사실: LLM은 도구를 직접 실행하지 않는다

LLM은 OpenAI 서버에서 돌고, 내 함수는 내 컴퓨터에 있다. LLM은 내 코드를 볼 수도, 실행할 수도 없다.

LLM이 하는 일은 "이 도구를 이 값으로 실행해줘"라는 요청서를 돌려주는 것까지다. 실제 실행은 내 파이썬 코드가 한다. 그래서 API를 두 번 호출하게 된다.

[1차 호출] 나 → LLM : "지금 몇 시야?" + 도구 목록
           LLM → 나 : "get_current_time 실행해줘" (답변은 비어 있음)

[실행]     내 파이썬 코드가 get_current_time() 실행 → "2026-09-28 14:30"

[2차 호출] 나 → LLM : 지금까지 대화 + 도구 실행 결과
           LLM → 나 : "지금은 오후 2시 30분입니다."

왜 응답을 확인하고 다시 요청할까

1. 1차 응답에서 tool_calls를 확인하는 이유
LLM이 도구가 필요 없다고 판단하면 바로 답을 준다. 필요하다고 판단하면 답 대신 요청서(tool_calls)를 준다. 어느 쪽인지 확인해야 다음 행동을 정할 수 있다. 도구를 쓸지 말지는 LLM이 스스로 판단한다. "수도가 어디야?"는 바로 답하고, "지금 몇 시야?"는 도구를 요청한다.

2. 실행 결과를 들고 다시 요청하는 이유
LLM은 기억력이 없다. 매 호출이 독립적이라서, 도구 결과를 알려주려면 지금까지의 대화 + 도구 결과를 통째로 다시 보내야 한다. 그래야 LLM이 결과를 읽고 사람이 읽을 수 있는 최종 답을 만든다.

도구 등록에 필요한 3가지

① 실제 함수

평범한 파이썬 함수다.

from datetime import datetime

def get_current_time():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

② 도구 설명서 (JSON 스키마)

LLM은 코드를 볼 수 없으니, 이 함수가 뭔지 글로 설명해줘야 한다.

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "현재 날짜와 시각을 알려준다.",
            "parameters": {
                "type": "object",
                "properties": {}
            }
        }
    }
]
항목 역할
name LLM이 요청서에 적을 도구 이름
description LLM이 도구를 고르는 유일한 근거. 대충 쓰면 엉뚱할 때 쓰거나 안 씀
parameters 도구에 넘길 값의 이름·타입·설명. LLM이 여기에 맞춰 값을 채워 보냄

③ 이름 → 함수 연결표

LLM은 "get_current_time"이라는 문자열로 요청한다. 이 문자열을 실제 함수와 연결해줘야 실행할 수 있다.

available_functions = {
    "get_current_time": get_current_time,
}

전체 흐름 코드

import json
from openai import OpenAI

client = OpenAI()
messages = [{"role": "user", "content": "지금 몇 시야?"}]

# 1차 호출: 도구 목록과 함께 질문
response = client.chat.completions.create(
    model="gpt-4o", messages=messages, tools=tools
)
msg = response.choices[0].message

# 도구 요청이 있으면 실행
if msg.tool_calls:
    messages.append(msg)  # LLM의 요청서도 대화 기록에 남김
    for call in msg.tool_calls:
        func = available_functions[call.function.name]
        args = json.loads(call.function.arguments)
        result = func(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,   # 어느 요청에 대한 결과인지 짝 맞추기
            "content": str(result)
        })

    # 2차 호출: 결과를 보고 최종 답변
    response = client.chat.completions.create(
        model="gpt-4o", messages=messages, tools=tools
    )
    msg = response.choices[0].message

print(msg.content)

자주 하는 실수

  • 1차 응답만 출력하고 끝냄: 도구를 요청한 응답은 content가 None이다. 실행하고 다시 호출해야 답이 나온다.
  • LLM의 요청서(msg)를 기록에 안 넣음: tool 메시지만 넣으면 짝이 안 맞아 에러가 난다.
  • tool_call_id 누락: 어떤 요청의 결과인지 알 수 없어 에러가 난다.
  • description을 대충 씀: LLM이 도구를 제대로 고르지 못한다.

정리

  • 도구 = LLM이 요청하고, 내 코드가 실행하는 함수
  • 흐름 = 1차 호출(요청서 받기) → 실행 → 2차 호출(결과 주고 답 받기)
  • 등록 = 함수 + 설명서 + 연결표

이 한 바퀴가 "AI 에이전트"의 가장 기본 단위다. 여기서 도구를 늘리고, 이 과정을 여러 번 반복하게 만들면 에이전트가 된다.

반응형