LLM - Bedrock function calling

JunMyung Lee·2025년 10월 28일

LLM

목록 보기
10/10

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()

왜 few-shot을 message에다가 담았을까?

Few-shot Learning 방식 비교

두 가지 Few-shot 방식

1️⃣ System Prompt 내부에 예시 (일반적인 방법)
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시에 약속"}
]

특징:

  • ✅ 단순하고 깔끔
  • ✅ 일반 텍스트 생성에 효과적
  • Tool 호출 방식에는 효과가 떨어질 수 있음
  • ❌ 예시가 "설명"으로만 읽힘

2️⃣ Messages에 대화 형태로 예시 (Tool 사용시 권장)
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시"}
]

특징:

  • Tool 호출 동작을 정확히 보여줌
  • ✅ Claude가 "아, 이렇게 tool을 써야 하는구나" 명확히 이해
  • ✅ 복잡한 상호작용(tool 호출 + 결과 처리)에 효과적
  • ⚠️ 실제 대화 히스토리처럼 보일 수 있음 (혼동 가능)

왜 Tool에서는 Messages 방식이 더 좋을까?

Tool 호출은 "특별한 JSON 구조"이기 때문
# 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 구조 학습 필요
간단한 ToolSystem 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 구조를 보고 그대로 따라 할 수 있기 때문입니다.

하지만 실제 대화 히스토리로 오해받지 않도록 주의가 필요합니다. 실제 사용자 히스토리와 구분하기 위해:

  • Few-shot 예시는 앞부분에 배치
  • 명확한 구분자 사용 (예: 특정 형식의 ID)
  • 또는 별도 파라미터로 전달 (Anthropic API의 경우)
profile
11년차 검색개발자 입니다. 여러 지식과 함께 실제 서비스를 운영 하면서 발생한 이슈에 대해서 정리하고 공유하고자 합니다.

0개의 댓글