Skip to main content

멀티 에이전트 (Multi-Agent)

복잡한 시스템에서는 단일 에이전트 대신 여러 전문 에이전트를 조합합니다. 각 에이전트가 특정 역할을 담당하고, 협력하여 작업을 완수합니다.

Supervisor 패턴

중앙 관리자(Supervisor)가 작업을 분배하고, 전문 에이전트의 결과를 취합합니다.
from typing import TypedDict, Annotated, Sequence, Literal
from langchain.chat_models import init_chat_model
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.types import Command

llm = init_chat_model("gpt-4o-mini", temperature=0)

class State(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]

# Supervisor: 다음에 누가 작업할지 결정
def supervisor(state: State) -> Command[Literal["researcher", "writer", "__end__"]]:
    response = llm.invoke([
        SystemMessage(content="""당신은 팀 관리자입니다.
질문을 분석하여 다음 작업자를 선택하세요:
- researcher: 정보 수집이 필요할 때
- writer: 최종 답변을 작성할 때
- __end__: 작업이 완료되었을 때
'NEXT: 작업자이름' 형식으로 응답하세요."""),
    ] + state["messages"])

    # 다음 에이전트 결정
    content = response.content
    if "researcher" in content.lower():
        goto = "researcher"
    elif "writer" in content.lower():
        goto = "writer"
    else:
        goto = "__end__"

    return Command(goto=goto, update={"messages": [response]})

# 전문 에이전트들
def researcher(state: State) -> Command[Literal["supervisor"]]:
    response = llm.invoke([
        SystemMessage(content="당신은 연구 전문가입니다. 질문에 대한 정보를 수집하세요."),
    ] + state["messages"])
    return Command(goto="supervisor", update={"messages": [response]})

def writer(state: State) -> Command[Literal["supervisor"]]:
    response = llm.invoke([
        SystemMessage(content="당신은 글쓰기 전문가입니다. 수집된 정보를 바탕으로 답변을 작성하세요."),
    ] + state["messages"])
    return Command(goto="supervisor", update={"messages": [response]})

# 그래프 구성
graph = StateGraph(State)
graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher)
graph.add_node("writer", writer)
graph.add_edge(START, "supervisor")

app = graph.compile()

Command 기반 에이전트 전환

Command(goto=...) 패턴은 LangGraph의 핵심 멀티 에이전트 메커니즘입니다.
필드설명예시
goto다음 노드 이름"researcher", "__end__"
update상태 업데이트{"messages": [response]}
resumeHuman-in-the-Loop 재개Command(resume=value)

Swarm 패턴

중앙 관리자 없이 에이전트들이 서로 직접 작업을 넘기는 패턴입니다.
# Swarm: 각 에이전트가 다음 에이전트를 직접 지정
def agent_a(state: State) -> Command[Literal["agent_b", "__end__"]]:
    response = llm.invoke([
        SystemMessage(content="접수 담당입니다. 복잡하면 agent_b에게 넘기세요."),
    ] + state["messages"])

    if "복잡" in state["messages"][-1].content:
        return Command(goto="agent_b", update={"messages": [response]})
    return Command(goto="__end__", update={"messages": [response]})

def agent_b(state: State) -> Command[Literal["agent_c", "__end__"]]:
    response = llm.invoke([
        SystemMessage(content="분석 담당입니다. 작성이 필요하면 agent_c에게 넘기세요."),
    ] + state["messages"])
    return Command(goto="agent_c", update={"messages": [response]})

Supervisor vs Swarm

항목SupervisorSwarm
제어 방식중앙 집중분산
유연성보통높음
디버깅쉬움어려움
확장성보통높음
적합한 경우명확한 역할 분담동적 협업

서브그래프 활용

복잡한 에이전트를 서브그래프로 분리하여 모듈화합니다.
# 서브그래프: Research Agent 내부 워크플로우
research_graph = StateGraph(State)
research_graph.add_node("search", search_node)
research_graph.add_node("summarize", summarize_node)
research_graph.add_edge(START, "search")
research_graph.add_edge("search", "summarize")
research_graph.add_edge("summarize", END)
research_subgraph = research_graph.compile()

# 메인 그래프에서 서브그래프를 노드로 사용
main_graph = StateGraph(State)
main_graph.add_node("supervisor", supervisor)
main_graph.add_node("research", research_subgraph)  # 서브그래프를 노드로 추가
main_graph.add_node("writer", writer)

main_graph.add_edge(START, "supervisor")
# ... 엣지 연결
시작 추천: Supervisor 패턴으로 시작하세요. 중앙 관리자가 흐름을 제어하므로 디버깅이 쉽습니다. 에이전트 간 동적 협업이 필요해지면 Swarm 패턴으로 전환하세요.