📌 개념 설명
tools_condition은 LangGraph에서 에이전트 워크플로우의 라우팅을 자동으로 처리하는 사전 구축된 조건 함수입니다.
주요 역할:
- LLM이 도구(tool)를 호출해야 하는지 판단
- 도구 호출이 필요하면 → tools 노드로 라우팅
- 도구 호출이 없으면 → 종료(END)로 라우팅
- tool_calls가 있는지 자동으로 확인하여 분기 처리
🔧 기본 사용법
1. 간단한 예제
python
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import tools_condition, ToolNode
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
# 도구 정의
@tool
def get_weather(location: str) -> str:
"""특정 위치의 날씨를 가져옵니다."""
# 실제로는 API 호출
return f"{location}의 날씨는 맑습니다. 기온 22도입니다."
@tool
def search_web(query: str) -> str:
"""웹에서 정보를 검색합니다."""
return f"'{query}'에 대한 검색 결과입니다."
# LLM 설정 (도구 바인딩)
tools = [get_weather, search_web]
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
llm_with_tools = llm.bind_tools(tools)
# 그래프 구성
def chatbot(state: MessagesState):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
# 그래프 생성
graph_builder = StateGraph(MessagesState)
# 노드 추가
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools))
# 엣지 추가
graph_builder.add_edge(START, "chatbot")
# 🎯 tools_condition 사용 - 핵심!
graph_builder.add_conditional_edges(
"chatbot", # 출발 노드
tools_condition, # 조건 함수
# tools_condition이 반환하는 값에 따라 라우팅
# "tools" 반환 → tools 노드로
# "END" 반환 → 종료
)
graph_builder.add_edge("tools", "chatbot") # 도구 실행 후 다시 chatbot으로
# 컴파일
graph = graph_builder.compile()
💡 실전 예제
2. 전체 동작 예제
python
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import tools_condition, ToolNode
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from typing import Annotated
# === 도구 정의 ===
@tool
def calculate(expression: str) -> str:
"""수학 계산을 수행합니다. 예: '2 + 2' 또는 '10 * 5'"""
try:
result = eval(expression)
return f"계산 결과: {result}"
except Exception as e:
return f"계산 오류: {str(e)}"
@tool
def get_current_time() -> str:
"""현재 시간을 반환합니다."""
from datetime import datetime
return f"현재 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
@tool
def search_database(query: str) -> str:
"""데이터베이스에서 정보를 검색합니다."""
# 실제로는 DB 쿼리
db = {
"사용자": "현재 1,234명의 사용자가 등록되어 있습니다.",
"제품": "총 567개의 제품이 있습니다.",
}
return db.get(query, "검색 결과가 없습니다.")
# === 그래프 구성 ===
tools = [calculate, get_current_time, search_database]
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
llm_with_tools = llm.bind_tools(tools)
def assistant(state: MessagesState):
"""LLM을 호출하여 응답 생성"""
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
# 그래프 빌더
builder = StateGraph(MessagesState)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
# 라우팅 설정
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
"assistant",
tools_condition, # 🔑 자동으로 tool_calls 확인
)
builder.add_edge("tools", "assistant")
graph = builder.compile()
# === 실행 ===
def run_agent(user_input: str):
print(f"\n{'='*60}")
print(f"사용자: {user_input}")
print(f"{'='*60}")
events = graph.stream(
{"messages": [("user", user_input)]},
stream_mode="values"
)
for event in events:
event["messages"][-1].pretty_print()
# 테스트
if __name__ == "__main__":
# 도구 사용 필요 - tools_condition이 "tools"로 라우팅
run_agent("123 곱하기 456은 얼마야?")
# 도구 사용 필요 - tools_condition이 "tools"로 라우팅
run_agent("지금 몇 시야?")
# 도구 불필요 - tools_condition이 END로 라우팅
run_agent("안녕! 오늘 기분이 어때?")
# 여러 도구 연속 사용
run_agent("현재 시간을 알려주고, 100 나누기 4도 계산해줘")
🎯 tools_condition의 내부 동작
python
# tools_condition의 실제 로직 (단순화된 버전)
def tools_condition(state: MessagesState) -> str:
"""
마지막 메시지에 tool_calls가 있는지 확인
"""
last_message = state["messages"][-1]
# AIMessage에 tool_calls 속성이 있고, 비어있지 않으면
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools" # tools 노드로 라우팅
else:
return END # 종료
🔥 고급 패턴
3. 커스텀 조건과 결합
python
from typing import Literal
def should_continue(state: MessagesState) -> Literal["tools", "final_check", "__end__"]:
"""커스텀 라우팅 로직"""
last_message = state["messages"][-1]
# tool_calls가 있으면 도구 실행
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
# 특정 키워드가 있으면 추가 검증
if "검증" in last_message.content:
return "final_check"
# 그 외에는 종료
return "__end__"
# 그래프에 적용
builder.add_conditional_edges(
"assistant",
should_continue,
{
"tools": "tools",
"final_check": "verification_node",
"__end__": END
}
)
4. 반복 제한 추가
python
from typing import TypedDict
class AgentState(MessagesState):
iterations: int
def assistant_with_limit(state: AgentState):
response = llm_with_tools.invoke(state["messages"])
return {
"messages": [response],
"iterations": state.get("iterations", 0) + 1
}
def route_with_limit(state: AgentState) -> Literal["tools", "__end__"]:
# 10번 이상 반복하면 강제 종료
if state.get("iterations", 0) >= 10:
return "__end__"
# 그 외에는 tools_condition 로직 사용
last_message = state["messages"][-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
return "__end__"
builder = StateGraph(AgentState)
builder.add_node("assistant", assistant_with_limit)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", route_with_limit)
builder.add_edge("tools", "assistant")
📊 실행 흐름 다이어그램
사용자 입력
↓
[START] → [assistant 노드]
↓
(LLM 응답 생성)
↓
[tools_condition 체크]
/ \
tool_calls tool_calls
있음 없음
↓ ↓
[tools 노드] [END]
↓
(도구 실행)
↓
[assistant 노드] ← 다시 반복
✅ 핵심 포인트
- 자동 라우팅: tools_condition은 tool_calls 존재 여부를 자동으로 확인
- 간결한 코드: 조건 로직을 직접 작성할 필요 없음
- 표준 패턴: LangGraph의 권장 에이전트 패턴
- 확장 가능: 필요시 커스텀 조건으로 확장 가능
with Claude
'공부방 > Python & AI' 카테고리의 다른 글
| [AI] ChatPromptTemplate.from_messages (0) | 2025.10.13 |
|---|---|
| [Python] Sequence (0) | 2025.10.12 |
| [AI] add_messages (0) | 2025.10.12 |
| [Python] Annotated (0) | 2025.10.12 |
| [AI] MessagesPlaceholder (0) | 2025.10.12 |