LLM 프롬프트 엔지니어링 고급 기법: 실전에서 바로 쓰는 프롬프트 패턴
Chain of Thought, Few-Shot Learning, Self-Consistency 등 실무에서 즉시 활용 가능한 고급 프롬프트 엔지니어링 기법을 코드 예제와 함께 소개합니다.
Function Calling은 LLM이 JSON 형식의 구조화된 데이터를 생성하고, 이를 통해 외부 함수를 호출할 수 있게 해주는 기능입니다. OpenAI GPT-3.5 Turbo와 GPT-4에서 지원합니다.
일반 LLM 응답:
user: "서울의 날씨를 알려줘"
LLM: "죄송하지만 실시간 날씨 정보를 제공할 수 없습니다..."
Function Calling 사용:
user: "서울의 날씨를 알려줘"
LLM: {
"function": "get_weather",
"arguments": {"location": "서울"}
}
# → 실제 날씨 API 호출
# → "서울의 현재 날씨는 맑음, 20도입니다"
AI에게 사용 가능한 함수를 알려줍니다.
from openai import OpenAI
client = OpenAI()
functions = [
{
"name": "get_weather",
"description": "특정 지역의 날씨 정보를 가져옵니다",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "도시 이름 (예: 서울, 부산)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "온도 단위"
}
},
"required": ["location"]
}
}
]
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "서울 날씨 알려줘"}],
functions=functions,
function_call="auto" # auto, none, {"name": "함수명"}
)
message = response.choices[0].message
import json
if message.function_call:
function_name = message.function_call.name
arguments = json.loads(message.function_call.arguments)
# 실제 함수 호출
if function_name == "get_weather":
result = get_weather(**arguments)
# 함수 실행 결과를 다시 LLM에 전달
messages.append(message) # LLM의 function_call 메시지
messages.append({
"role": "function",
"name": function_name,
"content": json.dumps(result)
})
# 최종 응답 생성
final_response = client.chat.completions.create(
model="gpt-4",
messages=messages
)
print(final_response.choices[0].message.content)
# "서울의 현재 날씨는 맑음, 기온은 섭씨 20도입니다."
import requests
from openai import OpenAI
client = OpenAI()
def get_weather(location, unit="celsius"):
"""실제 날씨 API 호출"""
api_key = "your-api-key"
url = f"http://api.weatherapi.com/v1/current.json"
params = {
"key": api_key,
"q": location
}
response = requests.get(url, params=params)
data = response.json()
temp = data['current']['temp_c'] if unit == "celsius" else data['current']['temp_f']
condition = data['current']['condition']['text']
return {
"location": location,
"temperature": temp,
"unit": unit,
"condition": condition
}
# 함수 정의
functions = [{
"name": "get_weather",
"description": "Get the current weather in a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The city name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}]
# 대화 처리
def chat(user_message):
messages = [{"role": "user", "content": user_message}]
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
functions=functions,
function_call="auto"
)
message = response.choices[0].message
# Function call이 있으면 실행
if message.function_call:
function_name = message.function_call.name
arguments = json.loads(message.function_call.arguments)
# 함수 실행
function_response = get_weather(**arguments)
# 결과를 메시지에 추가
messages.append(message)
messages.append({
"role": "function",
"name": function_name,
"content": json.dumps(function_response)
})
# 최종 응답 생성
second_response = client.chat.completions.create(
model="gpt-4",
messages=messages
)
return second_response.choices[0].message.content
return message.content
# 사용
print(chat("서울과 부산의 날씨를 화씨로 알려줘"))
import sqlite3
def query_database(query):
"""데이터베이스 쿼리 실행"""
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute(query)
results = cursor.fetchall()
conn.close()
return results
functions = [{
"name": "query_database",
"description": "SQL 쿼리를 실행하여 데이터베이스에서 정보를 조회합니다",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "실행할 SQL SELECT 쿼리"
}
},
"required": ["query"]
}
}]
# 사용 예
user_query = "2024년 10월 총 매출액은 얼마인가요?"
# LLM이 자동으로 SQL 생성:
# SELECT SUM(amount) FROM sales WHERE YEAR(date) = 2024 AND MONTH(date) = 10
import smtplib
from email.mime.text import MIMEText
def send_email(to, subject, body):
"""이메일 발송"""
msg = MIMEText(body)
msg['Subject'] = subject
msg['To'] = to
msg['From'] = 'your@email.com'
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login('your@email.com', 'password')
server.send_message(msg)
return {"status": "sent", "to": to}
functions = [{
"name": "send_email",
"description": "이메일을 발송합니다",
"parameters": {
"type": "object",
"properties": {
"to": {"type": "string", "description": "수신자 이메일"},
"subject": {"type": "string", "description": "메일 제목"},
"body": {"type": "string", "description": "메일 본문"}
},
"required": ["to", "subject", "body"]
}
}]
# 사용
chat("홍길동(hong@example.com)에게 내일 회의 일정 리마인더 메일 보내줘")
# LLM이 자동으로 적절한 제목과 본문 생성
# 여러 함수를 동시에 제공
functions = [
{
"name": "get_weather",
"description": "날씨 조회",
"parameters": {...}
},
{
"name": "search_flights",
"description": "항공편 검색",
"parameters": {...}
},
{
"name": "book_hotel",
"description": "호텔 예약",
"parameters": {...}
}
]
# LLM이 자동으로 적절한 함수 선택
user_query = "다음주 월요일 서울에서 제주도 가는 항공편 찾아주고, 제주 날씨도 알려줘"
# → search_flights와 get_weather 둘 다 호출
함수 결과를 바탕으로 또 다른 함수를 호출합니다.
def run_conversation(user_message):
messages = [{"role": "user", "content": user_message}]
# 최대 5번까지 함수 호출 가능
for _ in range(5):
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
functions=functions,
function_call="auto"
)
message = response.choices[0].message
if not message.function_call:
# 함수 호출이 없으면 최종 응답
return message.content
# 함수 실행
function_name = message.function_call.name
arguments = json.loads(message.function_call.arguments)
result = execute_function(function_name, arguments)
# 메시지에 추가하고 계속 진행
messages.append(message)
messages.append({
"role": "function",
"name": function_name,
"content": json.dumps(result)
})
return "최대 함수 호출 횟수 초과"
# 사용 예
chat("서울 날씨 확인하고, 비가 오면 우산 추천 상품 검색해줘")
# 1. get_weather(location="서울")
# 2. search_products(query="우산") - 날씨 결과에 따라
Function Calling을 JSON 파서로 활용합니다.
# 함수를 실제로 실행하지 않고 JSON만 생성
extract_schema = {
"name": "extract_contact_info",
"description": "텍스트에서 연락처 정보를 추출합니다",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"phone": {"type": "string"},
"company": {"type": "string"}
},
"required": ["name"]
}
}
text = """
안녕하세요, 저는 김철수입니다.
제 이메일은 kim@example.com이고,
전화번호는 010-1234-5678입니다.
ABC 회사에서 일하고 있습니다.
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": f"Extract contact info: {text}"}],
functions=[extract_schema],
function_call={"name": "extract_contact_info"} # 강제 호출
)
data = json.loads(response.choices[0].message.function_call.arguments)
print(data)
# {
# "name": "김철수",
# "email": "kim@example.com",
# "phone": "010-1234-5678",
# "company": "ABC"
# }
# GPT-4-turbo 이상에서 지원
response = client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[{
"role": "user",
"content": "서울, 부산, 제주의 날씨를 모두 알려줘"
}],
tools=[{ # functions 대신 tools 사용
"type": "function",
"function": weather_function_schema
}],
tool_choice="auto"
)
# 여러 함수 호출이 동시에 반환됨
for tool_call in response.choices[0].message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# 병렬 처리 가능
class AIAssistant:
def __init__(self):
self.client = OpenAI()
self.functions = self._register_functions()
def _register_functions(self):
return [
{
"name": "create_todo",
"description": "할 일 추가",
"parameters": {
"type": "object",
"properties": {
"task": {"type": "string"},
"due_date": {"type": "string"}
},
"required": ["task"]
}
},
{
"name": "search_web",
"description": "웹 검색",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
},
{
"name": "set_reminder",
"description": "리마인더 설정",
"parameters": {
"type": "object",
"properties": {
"message": {"type": "string"},
"time": {"type": "string"}
},
"required": ["message", "time"]
}
}
]
def chat(self, message):
# Function calling 로직...
pass
# 사용
assistant = AIAssistant()
assistant.chat("내일 오후 3시에 회의 리마인더 설정해줘")
assistant.chat("AI 최신 뉴스 검색해줘")
functions = [
{
"name": "get_order_status",
"description": "주문 상태 조회",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"}
}
}
},
{
"name": "initiate_refund",
"description": "환불 신청",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"reason": {"type": "string"}
}
}
},
{
"name": "track_shipment",
"description": "배송 추적",
"parameters": {
"type": "object",
"properties": {
"tracking_number": {"type": "string"}
}
}
}
]
# 고객: "주문번호 12345 환불하고 싶어요. 사이즈가 안 맞아서요"
# → initiate_refund(order_id="12345", reason="사이즈 불일치")
# ❌ 나쁜 예
{
"name": "func",
"description": "데이터 가져오기",
"parameters": {...}
}
# ✅ 좋은 예
{
"name": "get_user_profile",
"description": "사용자 ID를 입력받아 프로필 정보(이름, 이메일, 가입일)를 반환합니다. 사용자가 존재하지 않으면 null을 반환합니다.",
"parameters": {...}
}
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "날짜 (YYYY-MM-DD 형식)",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high"],
"description": "우선순위 (low: 낮음, medium: 보통, high: 높음)"
}
}
}
def execute_function(name, arguments):
try:
if name == "get_weather":
return get_weather(**arguments)
# ... 다른 함수들
except Exception as e:
return {
"error": str(e),
"message": "함수 실행 중 오류가 발생했습니다"
}
# 민감한 작업은 확인 절차 추가
def execute_function_safely(name, arguments):
# 위험한 작업
dangerous_functions = ["delete_data", "send_money", "update_password"]
if name in dangerous_functions:
# 사용자 확인 요청
if not confirm_with_user(name, arguments):
return {"error": "User cancelled the operation"}
return execute_function(name, arguments)
LLM이 존재하지 않는 함수를 호출하려 할 수 있습니다.
해결책:
valid_functions = {"get_weather", "search_web", "send_email"}
if message.function_call:
if message.function_call.name not in valid_functions:
return "요청하신 작업을 수행할 수 없습니다."
해결책:
from pydantic import BaseModel, ValidationError
class WeatherParams(BaseModel):
location: str
unit: str = "celsius"
try:
params = WeatherParams(**arguments)
result = get_weather(params.location, params.unit)
except ValidationError as e:
return {"error": "Invalid parameters", "details": str(e)}
Function calling은 일반 chat보다 토큰을 더 사용합니다.
최적화:
Function Calling은 LLM을 실세계와 연결하는 다리입니다. 이를 통해:
시작하기:
Function Calling을 마스터하면 단순한 챗봇을 넘어 진정한 AI 어시스턴트를 만들 수 있습니다!