원문: https://langchain-ai.github.io/langgraph/agents/multi-agent/

개요

단일 에이전트는 여러 도메인을 전문화하거나 많은 도구를 관리해야 할 때 어려움을 겪을 수 있습니다. 이를 해결하기 위해 에이전트를 더 작고 독립적인 에이전트로 분리하여 멀티 에이전트 시스템으로 구성할 수 있습니다.

멀티 에이전트 시스템에서 에이전트는 서로 통신해야 합니다. 이들은 핸드오프(Handoff) 를 통해 통신합니다. 핸드오프는 제어권을 넘겨줄 에이전트와 해당 에이전트에게 전송할 페이로드를 설명하는 프리미티브입니다.

가장 인기 있는 두 가지 멀티 에이전트 아키텍처는 다음과 같습니다:

슈퍼바이저

슈퍼바이저

langgraph-supervisor 라이브러리를 사용하여 슈퍼바이저 멀티 에이전트 시스템을 만들 수 있습니다:

pip install langgraph-supervisor
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph_supervisor import create_supervisor
 
def book_hotel(hotel_name: str):
    """호텔 예약"""
    return f"Successfully booked a stay at {hotel_name}."
 
def book_flight(from_airport: str, to_airport: str):
    """항공편 예약"""
    return f"Successfully booked a flight from {from_airport} to {to_airport}."
 
flight_assistant = create_react_agent(
    model="openai:gpt-4o",
    tools=[book_flight],
    prompt="You are a flight booking assistant",
    name="flight_assistant"
)
 
hotel_assistant = create_react_agent(
    model="openai:gpt-4o",
    tools=[book_hotel],
    prompt="You are a hotel booking assistant",
    name="hotel_assistant"
)
 
supervisor = create_supervisor(
    agents=[flight_assistant, hotel_assistant],
    model=ChatOpenAI(model="gpt-4o"),
    prompt=(
        "You manage a hotel booking assistant and a"
        "flight booking assistant. Assign work to them."
    )
).compile()
 
for chunk in supervisor.stream(
    {
        "messages": [
            {
                "role": "user",
                "content": "book a flight from BOS to JFK and a stay at McKittrick Hotel"
            }
        ]
    }
):
    print(chunk)
    print("\n")

스웜(Swarm)

스웜

langgraph-swarm 라이브러리를 사용하여 스웜 멀티 에이전트 시스템을 만들 수 있습니다:

pip install langgraph-swarm
from langgraph.prebuilt import create_react_agent
from langgraph_swarm import create_swarm, create_handoff_tool
 
transfer_to_hotel_assistant = create_handoff_tool(
    agent_name="hotel_assistant",
    description="Transfer user to the hotel-booking assistant.",
)
 
transfer_to_flight_assistant = create_handoff_tool(
    agent_name="flight_assistant",
    description="Transfer user to the flight-booking assistant.",
)
 
flight_assistant = create_react_agent(
    model="anthropic:claude-3-5-sonnet-latest",
    tools=[book_flight, transfer_to_hotel_assistant],
    prompt="You are a flight booking assistant",
    name="flight_assistant"
)
 
hotel_assistant = create_react_agent(
    model="anthropic:claude-3-5-sonnet-latest",
    tools=[book_hotel, transfer_to_flight_assistant],
    prompt="You are a hotel booking assistant",
    name="hotel_assistant"
)
 
swarm = create_swarm(
    agents=[flight_assistant, hotel_assistant],
    default_active_agent="flight_assistant"
).compile()
 
for chunk in swarm.stream(
    {
        "messages": [
            {
                "role": "user",
                "content": "book a flight from BOS to JFK and a stay at McKittrick Hotel"
            }
        ]
    }
):
    print(chunk)
    print("\n")

핸드오프

멀티 에이전트 상호작용의 일반적인 패턴은 핸드오프로, 한 에이전트가 제어권을 다른 에이전트에게 넘깁니다. 핸드오프를 통해 다음을 지정할 수 있습니다:

  • 목적지: 이동할 대상 에이전트
  • 페이로드: 해당 에이전트에게 전달할 정보

이것은 langgraph-supervisor(슈퍼바이저가 개별 에이전트에게 핸드오프)와 langgraph-swarm(개별 에이전트가 다른 에이전트에게 핸드오프) 모두에서 사용됩니다.

create_react_agent로 핸드오프를 구현하려면 다음을 수행해야 합니다:

1. 제어권을 다른 에이전트로 전송할 수 있는 특수 도구 생성

def transfer_to_bob():
    """bob에게 전송"""
    return Command(
        # 이동할 에이전트(노드)의 이름
        goto="bob",
        # 에이전트에게 전송할 데이터
        update={"messages": [...]},
        # LangGraph에게 부모 그래프의 에이전트 노드로
        # 이동해야 함을 나타냄
        graph=Command.PARENT,
    )

2. 핸드오프 도구에 접근할 수 있는 개별 에이전트 생성

flight_assistant = create_react_agent(
    ..., tools=[book_flight, transfer_to_hotel_assistant]
)
hotel_assistant = create_react_agent(
    ..., tools=[book_hotel, transfer_to_flight_assistant]
)

3. 개별 에이전트를 노드로 포함하는 부모 그래프 정의

from langgraph.graph import StateGraph, MessagesState
 
multi_agent_graph = (
    StateGraph(MessagesState)
    .add_node(flight_assistant)
    .add_node(hotel_assistant)
    ...
)

완전한 구현 예제

이를 종합하여 항공편 예약 어시스턴트와 호텔 예약 어시스턴트라는 두 에이전트가 있는 간단한 멀티 에이전트 시스템을 구현하는 방법은 다음과 같습니다:

from typing import Annotated
from langchain_core.tools import tool, InjectedToolCallId
from langgraph.prebuilt import create_react_agent, InjectedState
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.types import Command
 
def create_handoff_tool(*, agent_name: str, description: str | None = None):
    name = f"transfer_to_{agent_name}"
    description = description or f"Transfer to {agent_name}"
 
    @tool(name, description=description)
    def handoff_tool(
        state: Annotated[MessagesState, InjectedState],  # (1) 런타임에 상태 주입
        tool_call_id: Annotated[str, InjectedToolCallId],
    ) -> Command:
        tool_message = {
            "role": "tool",
            "content": f"Successfully transferred to {agent_name}",
            "name": name,
            "tool_call_id": tool_call_id,
        }
        return Command(  # (2) Command 객체 반환
            goto=agent_name,  # (3) 이동할 에이전트
            update={"messages": state["messages"] + [tool_message]},  # (4) 상태 업데이트
            graph=Command.PARENT,  # (5) 부모 그래프로 이동
        )
 
    return handoff_tool
 
# 핸드오프 도구 생성
transfer_to_hotel_assistant = create_handoff_tool(
    agent_name="hotel_assistant",
    description="Transfer user to the hotel-booking assistant.",
)
 
transfer_to_flight_assistant = create_handoff_tool(
    agent_name="flight_assistant",
    description="Transfer user to the flight-booking assistant.",
)
 
# 간단한 에이전트 도구
def book_hotel(hotel_name: str):
    """호텔 예약"""
    return f"Successfully booked a stay at {hotel_name}."
 
def book_flight(from_airport: str, to_airport: str):
    """항공편 예약"""
    return f"Successfully booked a flight from {from_airport} to {to_airport}."
 
# 에이전트 정의
flight_assistant = create_react_agent(
    model="anthropic:claude-3-5-sonnet-latest",
    tools=[book_flight, transfer_to_hotel_assistant],
    prompt="You are a flight booking assistant",
    name="flight_assistant"
)
 
hotel_assistant = create_react_agent(
    model="anthropic:claude-3-5-sonnet-latest",
    tools=[book_hotel, transfer_to_flight_assistant],
    prompt="You are a hotel booking assistant",
    name="hotel_assistant"
)
 
# 멀티 에이전트 그래프 정의
multi_agent_graph = (
    StateGraph(MessagesState)
    .add_node(flight_assistant)
    .add_node(hotel_assistant)
    .add_edge(START, "flight_assistant")
    .compile()
)
 
# 멀티 에이전트 그래프 실행
for chunk in multi_agent_graph.stream(
    {
        "messages": [
            {
                "role": "user",
                "content": "book a flight from BOS to JFK and a stay at McKittrick Hotel"
            }
        ]
    }
):
    print(chunk)
    print("\n")

참고

이 핸드오프 구현은 다음을 가정합니다:

  • 각 에이전트가 MessagesState를 사용합니다
  • 핸드오프는 부모 그래프의 노드 간에 발생합니다
  • 도구 메시지가 상태의 메시지 목록에 추가됩니다

LangGraph 슈퍼바이저스웜 문서를 확인하여 핸드오프를 사용자 정의하는 방법을 알아보세요.

주요 개념

Command 객체

Command 객체는 제어 흐름과 상태 업데이트를 동시에 처리할 수 있게 해줍니다:

  • goto: 이동할 대상 노드(에이전트)
  • update: 상태에 적용할 업데이트
  • graph: 이동할 그래프 지정(Command.PARENT는 부모 그래프를 의미)

InjectedState

InjectedState 어노테이션을 사용하면 런타임에 도구 함수에 현재 그래프 상태를 주입할 수 있습니다. 이를 통해 도구가 현재 상태에 접근하여 의사결정을 내리거나 상태를 업데이트할 수 있습니다.

에이전트 간 통신

멀티 에이전트 시스템에서 에이전트는 다음과 같은 방식으로 통신합니다:

  1. 직접 핸드오프: 에이전트가 핸드오프 도구를 호출하여 다른 에이전트로 직접 제어권을 넘김
  2. 슈퍼바이저를 통한 라우팅: 슈퍼바이저가 다음에 실행할 에이전트를 결정
  3. 메시지 공유: 모든 에이전트가 공유 메시지 목록을 통해 컨텍스트를 공유

참고 자료