본문 바로가기
공부방/Python & AI

[AI] LangGraph의 tools_condition

by 래채 2025. 10. 13.

📌 개념 설명

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 노드] ← 다시 반복

✅ 핵심 포인트

  1. 자동 라우팅: tools_condition은 tool_calls 존재 여부를 자동으로 확인
  2. 간결한 코드: 조건 로직을 직접 작성할 필요 없음
  3. 표준 패턴: LangGraph의 권장 에이전트 패턴
  4. 확장 가능: 필요시 커스텀 조건으로 확장 가능

 

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