Tool
: LLM에게 제공하는 명확한 목적을 수행하는 함수. Agent가 외부 세계와 상호작용하기 위한 인터페이스
Agent의 행동은 도구를 통해 이루어진다.
MCP 규약이 생기면서 도구 활용을 설명하는 것도 효과적이게 되었으며, 사용이 용이해짐
사용 과정
- 도구 정의
- 프롬프트 설계: 모델이 도구를 적절히 사용할 수 있도록 프롬프트 설계
- 모델 추론: 언어 모델이 주어진 태스크를 분석하고 필요한 도구 결정
- 도구 호출: 모델이 선택한 도구를 호출하고 결과를 받음
- 결과 통합: 도구 호출 결과를 모델 응답에 통합
정의 필수 요소
도구 정의 스키마 작성을 위한 필수 요소
- 이름(name)
- 역할(description)
- 입력 파라미터(parameter)
실습: 도구 3개를 제작해 AI Agent 만들기
도구 정의
import openai
import json
import requests
from datetime import datetime
print("✅ 라이브러리 임포트 완료")
OPENAI_API_KEY = "여기에 API 키 입력"
TAVILY_API_KEY = "여기에 API 키 입력" # https://tavily.com
client = openai.OpenAI(api_key=OPENAI_API_KEY)
print("✅ API 키 설정 완료")
print(f"OpenAI 클라이언트: {client}")
tools = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "인터넷에서 실시간 정보를 검색합니다.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "검색할 질문이나 키워드. 쿼리에 대한 설명입니다."
}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "수학 계산을 수행합니다.",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "결과를 계산해낼 수학 표현식입니다."
}
},
"required": ["expression"]
}
}
},
{
"type": "function", #이 도구는 꼭 입력받아야 할 것이 없기 때문에 "required": 가 없음
"function": {
"name": "get_current_time",
"description": "현재 날짜와 시간을 가져옵니다.",
"parameters": {
"type": "object",
"properties": {}
}
}
}
]
print("✅ 도구 정의 완료")
print(f"총 {len(tools)}개의 도구가 정의되었습니다:")
for tool in tools:
print(f" - {tool['function']['name']}: {tool['function']['description']}")
위 셀의 실행결과
✅ 도구 정의 완료 총 3개의 도구가 정의되었습니다:
- web_search: 인터넷에서 실시간 정보를 검색합니다.
- calculate: 수학 계산을 수행합니다.
- get_current_time: 현재 날짜와 시간을 가져옵니다.
검색 API 확인
추천: Tavily, Duckduckgo
#Tavily의 API 테스트 해보기
url = "https://api.tavily.com/search"
payload = {
"api_key": TAVILY_API_KEY,
"query": '최근 베네수엘라 이슈에 대해 알려줘',
"search_depth": "basic",
"max_results": 5
}
response = requests.post(url, json = payload)
response.raise_for_status()
data = response.json()
data
위 셀의 실행결과 (아래 results 안에 같은 구조로 5개의 뉴스가 나옴)
- 본 Tavliy API의 호출 구조 파악하기
{'query': '최근 베네수엘라 이슈에 대해 알려줘',
'follow_up_questions': None,
'answer': None,
'images': [],
'results': [{'url': 'https://www.youtube.com/watch?v=b697Kg9rCi8',
'title': '베네수엘라 수도 7차례 폭발음…"미국이 공격! 비상사태!" (자막뉴스 ...', 'content': '베네수엘라 수도 7차례 폭발음…"미국이 공격! 비상사태!" (자막뉴스) / SBS\nSBS 뉴스\n5120000 subscribers\n193 likes\n12013 views\n3 Jan 2026\n밤하늘에 굉음과 함께 섬광이 수차례 번쩍입니다.\n\n도시 곳곳에선 시커먼 연기와 함께 폭발이 이어집니다.\n\n우리 시간으로 오늘 오후, 베네수엘라의 수도 카라카스에서 여러 차례 폭발음이 들렸다고 외신들이 일제히 보도했습니다.\n\n[CNN 생방송 : 이들은 이번 공격이 카라카스뿐만 아니라 미란다, \n\n앞서 트럼프 미국 행정부는 마두로 대통령의 퇴진을 압박하기 위해 베네수엘라에 대한 지상작전 가능성을 언급했습니다.\n\n최근에는 미 중앙정보국 CIA가 베네수엘라 해안의 외딴 항만 부두를 타격했다는 미국 언론 보도가 나오기도 했습니다.\n\n\n☞더 자세한 정보\nhttps://news.sbs.co.kr/y/?id=N1008390906\n\n☞[뉴스영상] 기사 모아보기 \nhttps://news.sbs.co.kr/y/t/?id=10000000121\n \n\n#베네수엘라 #미국 #뉴스영상 \n\n▶SBS 뉴스 채널 구독하기 : https://n.sbs.co.kr/youtube \n\n♨지금 뜨거운 이슈, 함께 토론하기(스프 구독) : https://premium.sbs.co.kr\n\n▶SBS 뉴스 라이브 : https://n.sbs.co.kr/youtubeLive , https://n.sbs.co.kr/live \n\n▶SBS 뉴스 제보하기\n홈페이지: https://n.sbs.co.kr/inform \n애플리케이션: \'SBS뉴스\' 앱 설치하고 제보 - https://n.sbs.co.kr/App \n카카오톡: \'SBS뉴스\'와 친구 맺고 채팅 - https://pf.kakao.com/_ewsdq/chat \n페이스북: \'SBS뉴스\' 메시지 전송 - https://www.facebook.com/sbs8news \n이메일: sbs8news@sbs.co.kr \n문자 # 누르고 6000 \n전화: 02-2113-6000 \n\n홈페이지: https://news.sbs.co.kr/ \n페이스북: https://www.facebook.com/sbs8news \nX: https://x.com/sbs8news\n카카오톡: https://pf.kakao.com/_ewsdq\n인스타그램: https://www.instagram.com/sbsnews\nThread: https://www.threads.com/@sbsnews\n\nCopyright Ⓒ SBS. All rights reserved. 무단 전재, 재배포 및 AI학습 이용 금지\n149 comments\n',
'score': 0.70823324,
'raw_content': None},
'score': 0.70823324, #유사도 점수
웹 서치 함수
def web_search(query):
"""실제 웹 검색을 수행하는 함수"""
print(f"🔍 웹 검색 실행: '{query}'")
try:
url = "https://api.tavily.com/search"
payload = {
"api_key": TAVILY_API_KEY,
"query": query,
"search_depth": "basic",
"max_results": 3
}
response = requests.post(url, json = payload)
response.raise_for_status()
data = response.json()
results = []
for result in data.get("results", []): #데이터 파싱해오기
results.append({
'title' : result.get('title'),
'url' : result.get('url'),
'content': result.get('content')
})
return json.dumps({ #json 형식으로 LLM이 잘 이해하도록 구성
"query": query,
"results": results,
"answer": data.get("answer", "")
}, ensure_ascii=False)
except Exception as e: #예외처리로 결과 체크하기
return json.dumps({"error": f"검색 실패: {str(e)}"}, ensure_ascii=False)
web_search('베네수엘라 이슈')
위 테스트 실행 결과
🔍 웹 검색 실행: '베네수엘라 이슈'
{"query": "베네수엘라 이슈", "results": [{"title": "오늘의 5가지 이슈: 美 베네수엘라 급습, 테슬라굴욕", "url": "https://www.bloomberg.co.kr/blog/five-venezuela-tesla/", "content": "오늘의 5가지 이슈: 美 베네수엘라 급습, 테슬라굴욕 · 달러-원, 1445원 부근으로 상승…위안화는 연일 강세 · 트럼프, 베네수엘라 마두로 축출…석유 관심."}, {"title": "\\"상황 악화되면 여기로\\" 교민이 전한 공포의 베네수엘라 [뉴스 ...", "url": "https://www.youtube.com/watch?v=ivaBhEaDHyo", "content": "00:00 \\"극도의 긴장 상태‥불안감 속 생필품 사재기까지\\" (2026.01.05/뉴스데스크/MBC) 01:58 친 마두로 세력 건재한 베네수엘라‥미국 통치 가능할까?"}, {"title": "“베네수엘라 위기, 경유가격 상승 자극 가능성…관심 필요”", "url": "https://www.busan.com/view/busan/view.php?code=2026010614542352156", "content": "도널드 트럼프 행정부가 지난 3일 니콜라스 마두로 베네수엘라 대통령을 전격 체포한 가운데 베네수엘라 사태가 국제 경유가격 상승을 자극할 가능성"}], "answer": null}
계산 함수와 현재 시간 함수
def calculate(expression):
"""수학 계산 함수"""
print(f"🧮 계산 실행: '{expression}'")
try:
# 안전한 계산
allowed_names = {'abs': abs ,'round':round,'pow':pow}
result = eval(expression,{'__builtins__' :{}},allowed_names)
return json.dumps({
"expression": expression,
"result": result
}, ensure_ascii=False)
except Exception as e:
return json.dumps({"error": f"계산 실패: {str(e)}"}, ensure_ascii=False)
def get_current_time(): #이 함수에서는 매개변수 받지 않음
"""현재 시간 함수"""
print(f"⏰ 현재 시간 조회")
now = datetime.now()
return json.dumps({
'datetime' : now.strftime('%Y-%m-D %H:%M:%S'), #스트링으로 포맷팅 변경
'date':now.strftime('%Y-%m-%d'),
'time':now.strftime('%H:%M:%S'),
'day_of_week':now.strftime('%A') #요일
}, ensure_ascii=False)
# 함수 매핑 딕셔너리
available_functions = {
'web_search' : web_search,
'calculate' : calculate,
'get_current_time': get_current_time
}
print("✅ 도구 함수 구현 완료")
datetime.now().strftime("%y-%m-%d")
함수 내에서 사용한 datetime의 테스트 실행 결과
26-01-06
대화 중 도구 사용 판단 체크하기
print("=" * 60)
print("📝 테스트: 간단한 계산")
print("=" * 60)
# 사용자 메시지
user_message = '오늘 기분 좋네' #질문 변경하며 테스트 해보기
messages = [{'role' : 'user', 'content': user_message}]
print(f"\n질문: {user_message}")
# 1단계: OpenAI API 호출
response = client.chat.completions.create(
model = 'gpt-4.1-nano',
messages=messages,
tools=tools,
tool_choice='auto' #도구 사용 판단 자동화
)
response_message = response.choices[0].message
print(f"\n📩 GPT 응답: {response_message}")
위 코드의 실행 결과 (GPT에서 도구를 사용하지 않는 것으로 판단해, 도구가 활동되지 않음)
============================================================ 📝 테스트: 간단한 계산 ============================================================
질문: 오늘 기분 좋네
📩 GPT 응답: ChatCompletionMessage(content='기분이 좋다니 정말 다행이네요! 오늘 특별한 일이 있었나요? 아니면 그저 좋은 날씨거나 좋은 일이 있었나요?', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None)
아래처럼 메시지 변경해봤을 때 실행 결과 (도구 2개 활용됨)
user_message = '어제 광화문에서 있었던 이슈'
============================================================ 📝 테스트: 간단한 계산 ============================================================
질문: 어제 광화문에서 있었던 이슈
📩 GPT 응답: ChatCompletionMessage(content=None, refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=[ChatCompletionMessageFunctionToolCall(id='call_RAk97aqn05zvaPi7wrGgIXs4', function=Function(arguments='{}', name='get_current_time'), type='function'), ChatCompletionMessageFunctionToolCall(id='call_T9cYarqF1oOBpfBhqkCy9i2G', function=Function(arguments='{"query": "어제 광화문에서 있었던 이슈"}', name='web_search'), type='function')])
아래처럼 메시지 변경해봤을 때 실행 결과 (도구 1개 활용됨)
============================================================ 📝 테스트: 간단한 계산 ============================================================ 질문: 3 더하기 10은 뭐지?
📩 GPT 응답: ChatCompletionMessage(content=None, refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=[ChatCompletionMessageFunctionToolCall(id='call_8HAl3DGWx8nIqU35BGMTGsgd', function=Function(arguments='{"expression":"3+10"}', name='calculate'), type='function')])
도구 호출 정보 체크하기
if response_message.tool_calls:
print(f"\n✅ GPT가 도구 호출을 요청했습니다!")
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"\n🔧 호출할 함수: {function_name}")
print(f"📝 전달할 인자: {function_args}")
# 실제 함수 실행
function_result = available_functions[function_name](**function_args)
print(f"✅ 함수 실행 결과: {function_result}")
else:
print(f"\n💬 직접 답변: {response_message.content}")
위 코드 실행 결과 (어제 광화문에서 있었던 이슈를 쿼리에 넣었을 때 2개 도구가 호출된 것 확인 가능)
✅ GPT가 도구 호출을 요청했습니다!
🔧 호출할 함수: web_search
📝 전달할 인자: {'query': '어제 광화문에서 있었던 이슈'}
🔍 웹 검색 실행: '어제 광화문에서 있었던 이슈'
✅ 함수 실행 결과: {"query": "어제 광화문에서 있었던 이슈", "results": [{"title": "이슈 | 오늘, 광화문", "url": "
https://www.khan.co.kr/issue/articles/as217?page=9
", "content": "문 닫힌 광화문 앞을 관광객들이 지나가고 있습니다.광화문광장에 만장이 서 있습니다. 고 오종렬 한국진보연대 총회 의장 영결식이 10일 서울 광화문광장에서 열렸습니다."}, {"title": "[뉴스퀘어 2PM] 국회, '쿠팡 청문회' 이틀째...적반하장 쿠팡?", "url": "
https://www.ytn.co.kr/_ln/0101_202512311407447311
", "content": "하지만 그렇다 하더라도 국회가 어제 있었던 여러 가지 상황들을 볼 때 ... 서울 광화문 일대의 광화문스퀘어에서도카운트다운 미디어아트가 펼쳐집니다."}, {"title": "광화문 집회 참가자 경찰버스에 압사?…경찰 “사실 아냐”", "url": "
https://news.kbs.co.kr/news/view.do?ncd=4518200
", "content": "서울 종로경찰서는 어제(15일) 있었던 서울 광화문 집회에서 경찰 버스가 움직여 50대 남성 A 씨가 20분 넘게 끼어있다가 압사했다는 내용의 유튜브"}], "answer": null}
🔧 호출할 함수: get_current_time
📝 전달할 인자: {}
⏰ 현재 시간 조회
✅ 함수 실행 결과: {"datetime": "2026-01-D 07:47:05", "date": "2026-01-06", "time": "07:47:05", "day_of_week": "Tuesday"}
그런데 어제 날짜에 맞추어 기사가 검색되기를 바랬으나 그렇게 되지는 않고,
'어제 광화문' 이라는 키워드가 들어간 기사가 검색됨. 그리고 오늘 날짜가 검색되긴 했으나 어떻게 활용되었는지 유추하기 어려움
반복해서 도구 호출하기
print("=" * 60)
print("📝 전체 플로우 테스트")
print("=" * 60)
user_message = "베네수엘라 이슈에 대해 알려줘"
messages = [{"role": "user", "content": user_message}]
print(f"\n질문: {user_message}")
# 1단계: 초기 API 호출
print("\n[1단계] OpenAI API 호출...")
response = client.chat.completions.create(
model = 'gpt-4o',
messages=messages,
tools=tools,
tool_choice='auto'
)
response_message = response.choices[0].message
# 2단계: 도구 호출 처리
if response_message.tool_calls:
print("[2단계] 도구 호출 발견!")
# 응답 메시지를 대화에 추가
messages.append(response_message)
# 각 도구 호출 실행
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"\n 🔧 함수: {function_name}")
print(f" 📝 인자: {function_args}")
# 함수 실행
function_result = available_functions[function_name](**function_args)
print(f" ✅ 결과: {function_result}")
# 함수 결과를 대화에 추가
messages.append({
'tool_call_id' : tool_call.id,
'role' : 'tool',
'name' : function_name,
'content' : function_result
})
# 3단계: 함수 결과를 포함하여 다시 API 호출
print("\n[3단계] 함수 결과를 포함하여 다시 API 호출...")
second_response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
final_answer = second_response.choices[0].message.content
print(f"\n💬 최종 답변: {final_answer}")
else:
print(f"\n💬 직접 답변: {response_message.content}")
위 코드 실행 결과
============================================================ 📝 전체 플로우 테스트 ============================================================
질문: 베네수엘라 이슈에 대해 알려줘
[1단계] OpenAI API 호출...
[2단계] 도구 호출 발견!
🔧 함수: web_search
📝 인자: {'query': '베네수엘라 이슈 2023'}
🔍 웹 검색 실행: '베네수엘라 이슈 2023'
✅ 결과: {"query": "베네수엘라 이슈 2023", "results": [{"title": "[PDF] VENEZUELA 2023 HUMAN RIGHTS REPORT", "url": "
https://www.state.gov/wp-content/uploads/2024/02/528267_VENEZUELA-2023-HUMAN-RIGHTS-REPORT.pdf
", "content": "Significant human rights issues included credible reports of: unlawful or arbitrary killings, including extrajudicial killings; enforced disappearance;."}, {"title": "World Report 2023: Venezuela | Human Rights Watch", "url": "
https://www.hrw.org/world-report/2023/country-chapters/venezuela
", "content": "The UN Office of the High Commissioner for Human Rights (OHCHR), which has a presence in Venezuela, lost access in 2022 to detention centers where political prisoners are held. Venezuelan authorities harass and persecute human rights defenders and civil society organizations addressing the human rights and humanitarian emergencies."}, {"title": "베네수엘라, 여러 대책에도 여전히 어려운 경제 베네수엘라 EMERiCs", "url": "
https://m.cafe.daum.net/dragon333y/hrKk/24511?listURI=%2Fdragon333y%2FhrKk
", "content": "2023년 8월 4일 베네수엘라 중앙은행(Banco Central de Venezuela)은 상반기 금 보유고가 8톤 줄었다고 밝혔다. 지난 2022년 12월 69톤이었던"}], "answer": null}
[3단계] 함수 결과를 포함하여 다시 API 호출... 💬 최종 답변: 2023년에 베네수엘라는 다양한 인권 문제와 경제적 어려움을 겪고 있습니다. 1. **인권 문제**: - 베네수엘라 정부는 불법적이거나 자의적인 살인, 강제 실종 등의 인권 유린에 대한 보고를 받고 있습니다. - 정보 기관이 고위 관리들의 명령에 따라 범죄를 저지른 것으로 보고되었으며, 이를 통해 정부 반대파를 억압하는 정책이 시행되고 있다는 주장이 있습니다. - 불법 금 채굴 활동으로 인한 인권 유린도 심각하게 보고되고 있습니다. 2. **경제 문제**: - 베네수엘라의 경제 상태는 여전히 어렵습니다. 2023년 상반기 동안 베네수엘라 중앙은행의 금 보유고는 감소했습니다. - 금 보유고가 지속적으로 감소하고 있는 것은 베네수엘라의 경제적 불안정을 나타내는 부분입니다.
자율적으로 도구를 사용하는 에이전트 (반복해서 활용 가능하도록)
class SimpleAgent:
"""자율적으로 도구를 사용하는 간단한 에이전트"""
def __init__(self, max_iterations=5):
self.max_iterations = max_iterations
self.messages = []
def run(self, user_message):
"""에이전트 실행"""
self.messages = [{"role": "user", "content": user_message}]
print(f"\n{'='*60}")
print(f"🤖 에이전트 시작")
print(f"{'='*60}")
print(f"📝 질문: {user_message}\n")
for iteration in range(1, self.max_iterations + 1):
print(f"\n--- 반복 {iteration} ---")
# API 호출
response = client.chat.completions.create(
model="gpt-4o",
messages=self.messages,
tools=tools,
tool_choice="auto"
)
response_message = response.choices[0].message
# 도구 호출이 없으면 종료
if not response_message.tool_calls:
print('작업 완료!')
return response_message.content
# 응답 메시지 추가
self.messages.append(response_message)
# 각 도구 호출 처리
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"🔧 도구: {function_name}({function_args})")
# 함수 실행
function_result = available_functions[function_name](**function_args)
# 결과 추가
self.messages.append({
'tool_call_id' : tool_call.id,
'role' : 'tool',
'name' : function_name,
'content' : function_result
})
return "⚠️ 최대 반복 횟수 도달"
print("✅ 에이전트 클래스 정의 완료")
agent = SimpleAgent(max_iterations=5)
result = agent.run(
'오늘 날짜를 확인하고, 2024년이 몇 일 전인지 계산하고, 그 결과를 알려줘')
print(f"\n💬 최종 답변:\n{result}")
위 코드 실행 결과
============================================================ 🤖 에이전트 시작 ============================================================
📝 질문: 오늘 날짜를 확인하고, 2024년이 몇 일 전인지 계산하고, 그 결과를 알려줘
--- 반복 1 ---
🔧 도구: get_current_time({})
⏰ 현재 시간 조회
--- 반복 2 ---
🔧 도구: calculate({'expression': '(2026-01-06) - (2024-01-01)'})
🧮 계산 실행: '(2026-01-06) - (2024-01-01)'
--- 반복 3 ---
🔧 도구: calculate({'expression': '(2026*365 + 6) - (2024*365 + 1)'})
🧮 계산 실행: '(2026*365 + 6) - (2024*365 + 1)'
--- 반복 4 --- 작업 완료!
💬 최종 답변: 오늘은 2026년 1월 6일이고, 2024년 1월 1일로부터 735일이 지났습니다.
'컴퓨터공학 + HCI > AI' 카테고리의 다른 글
| AI Agent - LangGraph로 멀티 에이전트 구현하기 (1) | 2026.01.06 |
|---|---|
| AI Agent - LangGraph로 에이전트 구현하기 (0) | 2026.01.06 |
| AI Agent 개요 및 구조 (0) | 2026.01.06 |
| 논문 리뷰 - Graph Retrieval-Augmented Generation: A Survey (2024) (0) | 2026.01.06 |
| LangChain: RAG 구현에 사용되는 프레임워크 (3) | 2026.01.05 |