Python boto3를 통해서 AWS Bedrock 예제 중, functino calling에 대해서 작성한다.
import boto3
import json
from datetime import datetime, timedelta
import pytz
# === 기본 설정 ===REGION = "us-west-2"
MODEL_ID = "anthropic.claude-3-5-sonnet-20240620-v1:0"
ENDPOINT_URL = "https://vpce-019e60d12e6b2b4d7-h5tebb8a.bedrock-runtime.us-west-2.vpce.amazonaws.com"
def get_current_datetime_kst():
"""한국 시간(KST) 기준 현재 날짜/시간 반환"""
kst = pytz.timezone('Asia/Seoul')
return datetime.now(kst)
def main():
client = boto3.client("bedrock-runtime", region_name=REGION, endpoint_url=ENDPOINT_URL)
now = get_current_datetime_kst()
current_date = now.strftime("%Y-%m-%d")
current_time = now.strftime("%H:%M")
tomorrow = (now + timedelta(days=1)).strftime("%Y-%m-%d")
day_after_tomorrow = (now + timedelta(days=2)).strftime("%Y-%m-%d")
weekday_korean = {
"Monday": "월요일", "Tuesday": "화요일", "Wednesday": "수요일",
"Thursday": "목요일", "Friday": "금요일", "Saturday": "토요일", "Sunday": "일요일"
}
day_of_week = weekday_korean[now.strftime("%A")]
# System prompt - 더 강력하게
system_prompt = f"""현재 시간: {current_date} {current_time} ({day_of_week})
당신은 날짜/시간 파싱 전문가입니다. Tool을 호출할 때 반드시 다음 규칙을 따르세요:
**필수 규칙**:
1. parse_date의 date 파라미터는 YYYY-MM-DD 형식의 실제 날짜만 입력 가능
2. "내일", "모레" 같은 상대적 표현은 절대 입력 불가
3. 상대적 표현을 받으면 현재 날짜({current_date})를 기준으로 계산 필수
**계산 예시**:
- 오늘 → {current_date}
- 내일 → {tomorrow}
- 모레 → {day_after_tomorrow}
Tool 호출 시 반드시 계산된 실제 날짜를 입력하세요."""
body = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 2048,
"system": system_prompt,
"tools": [
{
"name": "parse_date",
"description": f"""날짜를 YYYY-MM-DD 형식으로 반환합니다.
현재: {current_date}
이 tool은 YYYY-MM-DD 형식의 실제 날짜만 받습니다.
"내일", "모레" 같은 상대적 표현은 입력 불가능합니다.
올바른 입력 예시:
- "{current_date}" (오늘)
- "{tomorrow}" (내일)
- "{day_after_tomorrow}" (모레)
잘못된 입력 예시:
- "내일" ❌
- "3일 후" ❌
- "다음 주" ❌""",
"input_schema": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": f"YYYY-MM-DD 형식의 날짜만 가능. 현재가 {current_date}이고 '내일'을 입력하려면 '{tomorrow}'를 입력해야 합니다."
}
},
"required": ["date"]
}
},
{
"name": "parse_time",
"description": "시간을 24시간 형식으로 파싱합니다.",
"input_schema": {
"type": "object",
"properties": {
"hour": {
"type": "string",
"description": "00-23 사이의 두 자리 문자열"
},
"minute": {
"type": "string",
"description": "00-59 사이의 두 자리 문자열"
}
},
"required": ["hour", "minute"]
}
}
],
"messages": [
# Few-shot 예시 1: 오늘
{
"role": "user",
"content": "오늘 오후 2시에 약속이 있어"
},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_example1_date",
"name": "parse_date",
"input": {
"date": current_date # 계산된 실제 날짜
}
},
{
"type": "tool_use",
"id": "toolu_example1_time",
"name": "parse_time",
"input": {
"hour": "14",
"minute": "00"
}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_example1_date",
"content": json.dumps({"date": current_date})
},
{
"type": "tool_result",
"tool_use_id": "toolu_example1_time",
"content": json.dumps({"hour": "14", "minute": "00"})
}
]
},
{
"role": "assistant",
"content": f"네, {current_date} 14:00에 약속이 있으시군요."
},
# 실제 질문
{
"role": "user",
"content": "내일 오후 3시 이후의 회의실 예약해줘"
}
],
}
print("=== Request Body ===")
print(json.dumps(body, indent=2, ensure_ascii=False))
response = client.invoke_model(
modelId=MODEL_ID,
body=json.dumps(body)
)
result = json.loads(response["body"].read().decode("utf-8"))
print("\n=== 1차 모델 응답 ===")
print(json.dumps(result, indent=2, ensure_ascii=False))
# 결과 확인
print("\n=== Tool 호출 결과 ===")
for content in result.get("content", []):
if content.get("type") == "tool_use":
tool_name = content.get("name")
tool_input = content.get("input")
if tool_name == "parse_date":
date_value = tool_input.get("date")
if date_value == tomorrow:
print(f"✅ 날짜 파싱 성공: {date_value}")
else:
print(f"❌ 날짜 파싱 실패: {date_value} (예상: {tomorrow})")
elif tool_name == "parse_time":
hour = tool_input.get("hour")
minute = tool_input.get("minute")
print(f"✅ 시간 파싱: {hour}:{minute}")
if __name__ == "__main__":
main()
system_prompt = """당신은 날짜 파싱 전문가입니다.
<예시>
입력: "내일 오후 3시"
출력: {"date": "2025-10-29", "hour": "15", "minute": "00"}
입력: "오늘 저녁 7시"
출력: {"date": "2025-10-28", "hour": "19", "minute": "00"}
</예시>
위 예시처럼 상대적 표현을 실제 날짜로 변환하세요."""
messages = [
{"role": "user", "content": "내일 오후 3시에 약속"}
]
특징:
system_prompt = """당신은 날짜 파싱 전문가입니다."""
messages = [
# 실제 대화처럼 예시 보여주기
{"role": "user", "content": "오늘 오후 2시"},
{"role": "assistant", "content": [
{"type": "tool_use", "name": "parse_date",
"input": {"date": "2025-10-28"}}
]},
{"role": "user", "content": [tool_result]},
{"role": "assistant", "content": "확인했습니다"},
# 실제 질문
{"role": "user", "content": "내일 오후 3시"}
]
특징:
# System prompt 예시는 이렇게 쓰지만...
system: """
<예시>
입력: "내일"
tool: parse_date({"date": "2025-10-29"})
</예시>
"""
# Claude는 이걸 "텍스트 설명"으로만 읽음
# 실제 tool_use JSON 구조를 생성하는 데는 도움이 덜 됨
# Messages 예시는 실제 JSON 구조를 보여줌
{
"role": "assistant",
"content": [
{
"type": "tool_use", # ← 이 정확한 구조를 학습!
"id": "toolu_xxx",
"name": "parse_date",
"input": {"date": "2025-10-29"}
}
]
}
| 상황 | 권장 방식 | 이유 |
|---|---|---|
| 일반 텍스트 생성 | System Prompt 내 <예시> | 간단하고 효과적 |
| Tool 호출 | Messages에 대화 예시 | Tool JSON 구조 학습 필요 |
| 간단한 Tool | System Prompt도 가능 | 단순한 경우 둘 다 OK |
| 복잡한 Tool 흐름 | Messages 필수 | 여러 tool 조합, 조건부 호출 등 |
가장 효과적인 방법은 둘 다 사용하는 것입니다:
# System Prompt: 전반적인 규칙 설명
system_prompt = f"""현재 날짜: {current_date}
Tool 호출 규칙:
- parse_date는 YYYY-MM-DD 형식만 받습니다
- "내일", "모레" 같은 표현은 변환 필수
<예시 변환>
- "내일" → "{tomorrow}"
- "모레" → "{day_after_tomorrow}"
</예시 변환>
"""
# Messages: 실제 동작 예시
messages = [
# Few-shot 예시
{"role": "user", "content": "오늘 2시"},
{"role": "assistant", "content": [
{"type": "tool_use", "name": "parse_date",
"input": {"date": current_date}} # 실제 구조!
]},
# ... tool_result ...
# 실제 질문
{"role": "user", "content": "내일 3시"}
]
System Prompt의 <예시>: 개념 설명 Messages의 대화 예시: 실제 동작 학습
Tool 호출에서는 Messages 방식이 더 효과적입니다. Claude가 실제 JSON 구조를 보고 그대로 따라 할 수 있기 때문입니다.
하지만 실제 대화 히스토리로 오해받지 않도록 주의가 필요합니다. 실제 사용자 히스토리와 구분하기 위해: