02. Tool Calling & 구조화된 출력
개요
이번 세션에서는 정규식 기반 파싱에서 Pydantic과 OpenAI Function Calling API를 사용한 구조화된 도구 호출로 발전합니다. 이를 통해 타입 안전성, 유효성 검사, 신뢰성을 얻을 수 있습니다.
왜 Function Calling인가?
| 정규식 파싱 | Function Calling |
|---|---|
| ❌ 취약함 | ✅ 견고함 |
| ❌ 타입 체크 없음 | ✅ 완전한 타입 안전성 |
| ❌ 일관성 없음 | ✅ 형식 보장 |
| ❌ 유지보수 어려움 | ✅ 확장 용이 |
핵심 개념
도구 스키마를 위한 Pydantic
완전한 타입 안전성으로 도구 파라미터를 정의합니다:
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
"""계산기 도구의 입력 스키마"""
expression: str = Field(
description="계산할 수학 표현식"
)OpenAI 형식으로 변환
OpenAI는 특정 JSON 스키마 형식을 기대합니다:
def pydantic_to_openai_function(model, name, description):
return {
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": model.model_json_schema()
}
}Function Calling 흐름
실습
노트북에서 다음을 수행합니다:
- Pydantic 스키마 정의 - Calculator, Search, Weather 도구
- OpenAI 형식으로 변환 - tools 배열 구축
- 도구 핸들러 구현 - 실제 실행 로직
- FunctionCallingAgent 구축 - 완전한 에이전트 클래스
- Tavily 통합 - 실제 웹 검색 기능
Tavily 통합
Tavily API 키가 있으면 실제 웹 검색이 가능합니다:
from tavily import TavilyClient
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
results = client.search(query, max_results=5)비교: 이전 vs 이후
이전 (정규식 파싱)
# 취약함 - 쉽게 깨짐
pattern = r"Action:\s*(\w+)\[(.+?)\]"
match = re.search(pattern, response)이후 (Function Calling)
# 견고함 - 구조 보장
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto"
)
for tool_call in response.message.tool_calls:
# 타입 안전, 검증된 인자
args = json.loads(tool_call.function.arguments)모범 사례
⚠️
팁: Function Calling을 사용하더라도 항상 도구 인자를 검증하세요. LLM도 실수할 수 있습니다.
- 설명적인 스키마 - 더 좋은 설명 = 더 좋은 도구 선택
- 에러 처리 - 도구 실행을 try/except로 감싸기
- 로깅 - 디버깅을 위해 모든 도구 호출 기록
- 타임아웃 - 외부 API는 멈출 수 있음
다음 단계
실제로 무언가를 만들 준비가 되셨나요? 주말 프로젝트에서 개인 리서치 어시스턴트를 만들어보세요!