LangChain 독학 가이드 9 - Agent와 Tools
지금까지는 우리가 정해준 순서대로 AI가 작업했어요. “이 프롬프트를 받으면 저 모델로 보내고, 결과는 이렇게 파싱해라” 이런 식으로요. 근데 진짜 똑똑한 AI는 스스로 판단해서 필요한 도구를 골라 쓸 수 있어야 하지 않을까요?
그게 바로 Agent예요. “오늘 날씨 알려줘”라고 하면 알아서 날씨 API를 호출하고, “123 곱하기 456은?”이라고 하면 계산기를 쓰는 거죠.
Agent가 뭔가요?
Agent는 LLM이 스스로 판단해서 도구를 선택하고 실행하는 시스템이에요. 기존 체인과의 차이점을 보면:
일반 체인: 프롬프트 → 모델 → 파서 (고정된 순서)
Agent: 질문 → AI가 판단 → 필요한 도구 실행 → 결과 확인 → 추가 작업 필요? → 반복…
Agent는 목표를 달성할 때까지 여러 도구를 조합해서 쓸 수 있어요.
첫 번째 Agent 만들기
간단한 Agent부터 만들어볼게요. 먼저 필요한 패키지 설치:
pip install langchain langchain-openai langgraph
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
load_dotenv()
# 간단한 도구 정의
@tool
def add(a: int, b: int) -> int:
"""두 숫자를 더합니다."""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""두 숫자를 곱합니다."""
return a * b
# 모델과 도구 준비
model = ChatOpenAI(model="gpt-4")
tools = [add, multiply]
# Agent 생성
agent = create_react_agent(model, tools)
# 실행
result = agent.invoke({
"messages": [{"role": "user", "content": "3과 5를 더한 다음, 그 결과에 2를 곱해줘"}]
})
# 마지막 메시지 출력
print(result["messages"][-1].content)
실행 결과:
Agent가 알아서 add(3, 5) → multiply(8, 2) 순서로 도구를 호출했어요!
@tool 데코레이터로 도구 만들기
커스텀 도구를 만들 때는 @tool 데코레이터를 사용해요. 중요한 건 docstring이에요. AI가 이걸 보고 어떤 도구를 쓸지 판단하거든요.
from langchain_core.tools import tool
from datetime import datetime
@tool
def get_current_time() -> str:
"""현재 시간을 반환합니다. 시간을 물어볼 때 사용하세요."""
return datetime.now().strftime("%Y년 %m월 %d일 %H시 %M분")
@tool
def get_word_length(word: str) -> int:
"""단어의 글자 수를 반환합니다."""
return len(word)
@tool
def reverse_string(text: str) -> str:
"""문자열을 뒤집습니다."""
return text[::-1]
실행 결과:
도구 작성 팁
- 명확한 docstring: AI가 도구 선택할 때 참고함
- 타입 힌트: 파라미터 타입 명시 필수
- 단일 책임: 하나의 도구는 하나의 기능만
실용적인 도구 예시
웹 검색 도구
from langchain_community.tools import DuckDuckGoSearchRun
search = DuckDuckGoSearchRun()
# Agent에 추가
tools = [search, add, multiply]
agent = create_react_agent(model, tools)
result = agent.invoke({
"messages": [{"role": "user", "content": "2024년 노벨 물리학상 수상자가 누구야?"}]
})
print(result["messages"][-1].content)
실행 결과:
날씨 조회 도구 (예시)
import requests
@tool
def get_weather(city: str) -> str:
"""특정 도시의 현재 날씨를 조회합니다. 도시 이름을 영어로 입력하세요."""
# 실제로는 날씨 API를 호출해야 함
api_key = "your-weather-api-key"
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
try:
response = requests.get(url)
data = response.json()
temp = data['main']['temp']
desc = data['weather'][0]['description']
return f"{city}의 현재 날씨: {temp}°C, {desc}"
except:
return f"{city}의 날씨 정보를 가져올 수 없습니다."
Agent의 사고 과정 보기
Agent가 어떻게 판단하는지 보고 싶다면:
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
@tool
def search_database(query: str) -> str:
"""데이터베이스에서 정보를 검색합니다."""
# 실제로는 DB 쿼리
return f"'{query}'에 대한 검색 결과: 관련 데이터 3건 발견"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""이메일을 전송합니다."""
return f"{to}에게 '{subject}' 제목의 이메일 전송 완료"
model = ChatOpenAI(model="gpt-4")
tools = [search_database, send_email]
agent = create_react_agent(model, tools)
# 실행하고 전체 과정 출력
result = agent.invoke({
"messages": [{"role": "user", "content": "김철수의 이메일 주소를 찾아서 회의 안내 메일을 보내줘"}]
})
# 모든 메시지 출력
for msg in result["messages"]:
print(f"[{msg.type}] {msg.content if hasattr(msg, 'content') else msg}")
print("-" * 50)
실행 결과:
여러 도구 조합 예시
실제 비즈니스 시나리오를 만들어볼게요:
from langchain_core.tools import tool
from datetime import datetime, timedelta
@tool
def check_inventory(product: str) -> str:
"""상품의 재고를 확인합니다."""
# 가상의 재고 데이터
inventory = {"노트북": 15, "키보드": 50, "마우스": 100}
count = inventory.get(product, 0)
return f"{product} 재고: {count}개"
@tool
def create_order(product: str, quantity: int) -> str:
"""주문을 생성합니다."""
order_id = f"ORD-{datetime.now().strftime('%Y%m%d%H%M%S')}"
return f"주문 생성 완료. 주문번호: {order_id}, 상품: {product}, 수량: {quantity}개"
@tool
def calculate_delivery_date(days: int) -> str:
"""배송 예정일을 계산합니다."""
delivery = datetime.now() + timedelta(days=days)
return f"배송 예정일: {delivery.strftime('%Y년 %m월 %d일')}"
# Agent 생성
tools = [check_inventory, create_order, calculate_delivery_date]
agent = create_react_agent(model, tools)
result = agent.invoke({
"messages": [{"role": "user", "content": "노트북 재고 확인하고, 3대 주문해줘. 배송은 며칠 걸려?"}]
})
print(result["messages"][-1].content)
실행 결과:
Agent가 세 가지 도구를 순서대로 호출해서 전체 작업을 완료했어요!
Agent 사용 시 주의사항
1. 비용 관리
Agent는 여러 번 LLM을 호출할 수 있어서 비용이 많이 나올 수 있어요.
# max_iterations으로 최대 반복 횟수 제한
agent = create_react_agent(
model,
tools,
# 설정은 langgraph 버전에 따라 다를 수 있음
)
2. 도구 설명 잘 쓰기
AI가 도구를 잘못 선택하면 엉뚱한 결과가 나와요. docstring을 명확하게 써주세요.
# 나쁜 예
@tool
def process(x):
"""처리합니다."""
pass
# 좋은 예
@tool
def calculate_tax(income: float, tax_rate: float) -> float:
"""소득과 세율을 입력받아 세금을 계산합니다.
Args:
income: 연소득 (원 단위)
tax_rate: 세율 (0~1 사이 소수)
Returns:
계산된 세금 금액
"""
return income * tax_rate
3. 에러 처리
도구 실행 중 에러가 나면 Agent가 멈출 수 있어요.
@tool
def risky_operation(data: str) -> str:
"""위험한 작업을 수행합니다."""
try:
# 실제 작업
result = some_risky_function(data)
return result
except Exception as e:
return f"오류 발생: {str(e)}"
정리
오늘 배운 Agent 핵심:
- Agent: AI가 스스로 도구를 선택하고 실행
- @tool: 커스텀 도구 만들기
- docstring: AI가 도구 선택 시 참고 (매우 중요!)
- create_react_agent: ReAct 방식 Agent 생성
- 도구 조합: 여러 도구를 연결해 복잡한 작업 수행
Agent는 LangChain의 꽃이라고 할 수 있어요. 다음 글에서는 지금까지 배운 모든 것을 조합해서 실전 프로젝트를 만들어볼 거예요!