- Authors
- Name
- 왜 멀티에이전트인가
- LangGraph 핵심 개념 정리
- 멀티에이전트 설계 패턴 3가지
- 프레임워크 비교: LangGraph vs CrewAI vs AutoGen
- 상태 관리와 체크포인트 설계
- 도구 통합과 에이전트별 도구 격리
- 가드레일: 멀티에이전트의 안전장치
- Human-in-the-Loop 구현
- 에러 처리와 장애 복구
- 평가 체계: 멀티에이전트 챗봇의 품질 보장
- 프로덕션 배포 아키텍처
- 운영 경고 사항
- 프로덕션 체크리스트
- 트러블슈팅 가이드
- 마무리
- References

왜 멀티에이전트인가
단일 LLM 에이전트로 고객 문의, 주문 조회, 기술 지원, 환불 처리를 모두 처리하려고 하면 프롬프트가 비대해지고 도구 충돌이 발생한다. 프롬프트가 길어질수록 모델의 instruction following 정확도는 떨어지며, 도구 수가 30개를 넘어가면 tool selection 오류율이 급격히 상승한다.
멀티에이전트 아키텍처는 이 문제를 "분업"으로 해결한다. 각 에이전트는 하나의 전문 역할만 담당하며, 자신의 도메인에 특화된 프롬프트와 도구 세트를 가진다. 에이전트 간 협업은 프레임워크 수준에서 관리되므로, 개별 에이전트의 복잡도는 낮게 유지된다.
LangGraph는 이러한 멀티에이전트 시스템을 방향 그래프(Directed Graph) 구조로 설계할 수 있게 해주는 프레임워크다. 2025년 10월 1.0 정식 릴리스 이후 프로덕션 환경에서 가장 널리 채택되는 에이전트 오케스트레이션 도구로 자리잡았다.
LangGraph 핵심 개념 정리
LangGraph의 멀티에이전트 시스템은 세 가지 핵심 요소로 구성된다.
State: 그래프 전체가 공유하는 데이터 구조
State는 그래프를 흐르는 모든 데이터의 스냅샷이다. TypedDict 또는 Pydantic BaseModel로 정의하며, 모든 노드가 이 State를 읽고 업데이트한다.
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
current_agent: str
user_intent: str
tool_results: dict
escalation_needed: bool
add_messages는 LangGraph가 제공하는 reducer 함수로, 메시지 리스트를 덮어쓰지 않고 append 방식으로 누적한다. 이 패턴 덕분에 여러 에이전트가 순차적으로 메시지를 추가하더라도 대화 이력이 유실되지 않는다.
Node: 실제 작업을 수행하는 함수
각 노드는 State를 입력받아 처리한 뒤, 업데이트된 State를 반환하는 Python 함수다. 멀티에이전트 시스템에서 노드는 개별 에이전트 또는 도구 실행기에 해당한다.
Edge: 노드 간 전이를 제어하는 라우팅 규칙
일반 Edge는 고정된 노드 간 연결이고, Conditional Edge는 State 값에 따라 동적으로 다음 노드를 결정한다. 멀티에이전트 라우팅의 핵심이 바로 이 Conditional Edge다.
멀티에이전트 설계 패턴 3가지
LangGraph에서 자주 사용되는 멀티에이전트 협업 패턴은 크게 세 가지다.
패턴 1: Supervisor (중앙 관리자)
하나의 Supervisor 에이전트가 모든 전문 에이전트를 관리한다. 사용자 입력이 들어오면 Supervisor가 의도를 파악하고, 적절한 전문 에이전트에게 작업을 위임한다.
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
# 전문 에이전트 정의
order_agent = create_react_agent(
model=model,
tools=[search_order, cancel_order, track_shipping],
name="order_expert",
prompt="주문 관련 문의만 처리합니다. 주문번호가 없으면 반드시 먼저 확인하세요."
)
tech_agent = create_react_agent(
model=model,
tools=[search_docs, run_diagnostic, create_ticket],
name="tech_expert",
prompt="기술 지원 문의를 처리합니다. 문제 재현 단계를 먼저 파악하세요."
)
refund_agent = create_react_agent(
model=model,
tools=[check_refund_policy, process_refund, send_confirmation],
name="refund_expert",
prompt="환불/반품 처리 전담. 반드시 환불 정책을 먼저 확인하세요."
)
def supervisor_router(state: AgentState) -> str:
"""Supervisor가 의도를 분류하여 적절한 에이전트로 라우팅"""
intent = state.get("user_intent", "")
if "주문" in intent or "배송" in intent:
return "order_expert"
elif "오류" in intent or "버그" in intent or "설정" in intent:
return "tech_expert"
elif "환불" in intent or "반품" in intent:
return "refund_expert"
return "fallback"
# 그래프 구성
graph = StateGraph(AgentState)
graph.add_node("supervisor", supervisor_node)
graph.add_node("order_expert", order_agent)
graph.add_node("tech_expert", tech_agent)
graph.add_node("refund_expert", refund_agent)
graph.add_node("fallback", fallback_node)
graph.add_edge(START, "supervisor")
graph.add_conditional_edges("supervisor", supervisor_router)
graph.add_edge("order_expert", END)
graph.add_edge("tech_expert", END)
graph.add_edge("refund_expert", END)
graph.add_edge("fallback", END)
app = graph.compile()
Supervisor 패턴의 장점은 라우팅 로직이 한곳에 집중되어 디버깅이 쉽다는 것이다. 단점은 Supervisor가 단일 장애 지점(SPOF)이 될 수 있다는 점이다.
패턴 2: Shared Scratchpad (공유 작업 공간)
모든 에이전트가 동일한 메시지 리스트(scratchpad)를 공유한다. 각 에이전트가 수행한 작업과 중간 결과가 다른 에이전트에게 자연스럽게 전달된다. 복잡한 분석 작업에서 에이전트들이 서로의 작업 결과를 참고해야 할 때 유용하다.
패턴 3: Hierarchical Teams (계층적 팀)
에이전트 노드 자체가 또 다른 LangGraph 서브그래프가 되는 구조다. 예를 들어 "고객지원팀" 서브그래프 안에 주문, 배송, 환불 에이전트가 있고, "기술지원팀" 서브그래프 안에 진단, 문서검색, 티켓 에이전트가 있는 형태다. 대규모 조직의 업무 분장을 그대로 코드에 반영할 수 있다.
프레임워크 비교: LangGraph vs CrewAI vs AutoGen
멀티에이전트 프레임워크 선택 시 아래 비교표를 참고한다.
| 항목 | LangGraph | CrewAI | AutoGen |
|---|---|---|---|
| 설계 철학 | 그래프 기반 워크플로우 | 역할 기반 팀 구성 | 대화 기반 협업 |
| State 관리 | TypedDict/Pydantic 기반 명시적 State | 내장 상태 관리 (자동) | SharedContext 딕셔너리 |
| 제어 수준 | 노드/엣지 단위 세밀한 제어 | 역할/태스크 단위 추상화 | 대화 흐름 기반 암묵적 제어 |
| Human-in-the-Loop | 네이티브 API (interrupt/approve) | 콜백 기반 | ConversableAgent 인터럽트 |
| Checkpoint/복구 | 내장 (PostgresSaver, MemorySaver) | 외부 구현 필요 | 제한적 |
| 프로덕션 성숙도 | 높음 (1.0 GA, LangGraph Platform) | 중간 (빠른 성장 중) | 중간 (연구 중심) |
| 학습 곡선 | 높음 (그래프 개념 이해 필요) | 낮음 (YAML/직관적 API) | 중간 |
| MCP 통합 | 공식 지원 | 커뮤니티 | 미지원 |
| Observability | LangSmith 네이티브 통합 | LangSmith/Langfuse 연동 | 제한적 |
| 추천 유즈케이스 | 복잡한 조건부 워크플로우, 장기 실행 작업 | 빠른 프로토타이핑, 역할 기반 팀 | 연구/실험, 대화형 에이전트 |
결론: 프로덕션 멀티에이전트 챗봇에는 LangGraph가 가장 적합하다. 세밀한 제어, 체크포인트 복구, Human-in-the-Loop 네이티브 지원이 운영 안정성의 핵심이기 때문이다. CrewAI는 빠른 MVP 구축에, AutoGen은 연구 목적에 적합하다.
상태 관리와 체크포인트 설계
프로덕션 멀티에이전트 챗봇에서 상태 관리는 단순히 메시지 이력을 저장하는 것을 넘어선다. 에이전트 간 핸드오프 컨텍스트, 도구 실행 결과, 사용자 인증 정보, 에스컬레이션 상태까지 포함해야 한다.
체크포인트로 장애 복구 보장
LangGraph의 체크포인터는 각 superstep(노드 실행 단위)마다 자동으로 상태를 저장한다. 서버가 중간에 재시작되더라도 마지막 체크포인트에서 그래프를 재개할 수 있다.
from langgraph.checkpoint.postgres import PostgresSaver
# PostgreSQL 기반 체크포인터 설정
DB_URI = "postgresql://user:password@localhost:5432/langgraph_checkpoints"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup() # 테이블 자동 생성
app = graph.compile(checkpointer=checkpointer)
# thread_id로 대화 세션 구분
config = {"configurable": {"thread_id": "user-session-abc123"}}
# 실행: 체크포인트가 각 노드 실행 후 자동 저장
result = app.invoke(
{"messages": [{"role": "user", "content": "주문 12345 배송 상태 알려줘"}]},
config=config
)
# 장애 후 복구: 동일 thread_id로 자동 재개
resumed = app.invoke(
{"messages": [{"role": "user", "content": "환불도 가능해?"}]},
config=config # 이전 대화 컨텍스트 자동 로드
)
프로덕션 체크포인트 운영 규칙
- PostgresSaver를 기본으로 사용:
MemorySaver는 개발/테스트 전용이다. 프로덕션에서는 반드시PostgresSaver또는 커스텀 영속 체크포인터를 사용한다. - State 스키마 버전 관리: State에
schema_version필드를 추가하고, 스키마 변경 시 마이그레이션 로직을 구현한다. - 체크포인트 TTL 설정: 완료된 대화의 체크포인트는 일정 기간 후 삭제하여 스토리지 비용을 관리한다.
- 민감 정보 제외: 체크포인트에 저장되는 State에서 API 키, 인증 토큰 등 민감 정보를 반드시 제외한다.
도구 통합과 에이전트별 도구 격리
멀티에이전트 시스템에서 도구 관리의 핵심 원칙은 최소 권한(Least Privilege) 이다. 각 에이전트는 자신의 역할에 필요한 도구만 접근할 수 있어야 한다.
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class OrderSearchInput(BaseModel):
order_id: str = Field(description="주문번호 (예: ORD-2026-12345)")
@tool(args_schema=OrderSearchInput)
def search_order(order_id: str) -> dict:
"""주문 정보를 조회합니다. 주문번호 형식: ORD-YYYY-NNNNN"""
if not order_id.startswith("ORD-"):
return {"error": "유효하지 않은 주문번호 형식입니다"}
# DB 조회 로직
return db.orders.find_one({"order_id": order_id})
@tool
def process_refund(order_id: str, reason: str, amount: float) -> dict:
"""환불을 처리합니다. 환불 정책 확인 후에만 호출하세요."""
if amount > 1_000_000:
return {"error": "100만원 초과 환불은 수동 승인이 필요합니다", "escalate": True}
# 환불 처리 로직
return payment_gateway.refund(order_id, amount, reason)
# 에이전트별 도구 할당 (격리)
ORDER_TOOLS = [search_order, track_shipping, cancel_order]
REFUND_TOOLS = [check_refund_policy, process_refund, send_confirmation]
TECH_TOOLS = [search_docs, run_diagnostic, create_ticket]
# 주문 에이전트는 환불 도구에 접근 불가, 환불 에이전트는 주문 취소 도구에 접근 불가
MCP(Model Context Protocol) 통합
LangGraph 1.0부터 MCP 프로토콜을 공식 지원한다. 외부 시스템의 도구를 MCP 서버로 노출하면, LangGraph 에이전트가 표준화된 인터페이스로 호출할 수 있다. 도구마다 별도 어댑터를 작성할 필요가 없어 통합 비용이 크게 줄어든다.
가드레일: 멀티에이전트의 안전장치
멀티에이전트 시스템은 단일 에이전트보다 공격 표면이 넓다. 에이전트 간 메시지 전달, 도구 호출, 상태 변경 각 단계에서 가드레일이 필요하다.
입력 가드레일
사용자 입력이 그래프에 진입하기 전에 검증한다.
from langchain_core.messages import HumanMessage
def input_guardrail_node(state: AgentState) -> AgentState:
"""입력 가드레일: prompt injection, PII, 부적절한 콘텐츠 탐지"""
last_message = state["messages"][-1]
# 1. 입력 길이 제한
if len(last_message.content) > 4000:
return {
**state,
"messages": state["messages"] + [
{"role": "assistant", "content": "메시지가 너무 깁니다. 4000자 이내로 줄여주세요."}
],
"escalation_needed": False
}
# 2. Prompt injection 탐지
injection_patterns = [
"시스템 프롬프트를 무시",
"ignore previous instructions",
"you are now",
"new instructions:",
]
content_lower = last_message.content.lower()
for pattern in injection_patterns:
if pattern.lower() in content_lower:
return {
**state,
"messages": state["messages"] + [
{"role": "assistant", "content": "요청을 처리할 수 없습니다."}
],
"escalation_needed": True
}
# 3. PII 마스킹 (주민등록번호, 카드번호 등)
import re
masked_content = re.sub(
r'\d{6}-\d{7}', '[주민번호 마스킹]', last_message.content
)
masked_content = re.sub(
r'\d{4}-\d{4}-\d{4}-\d{4}', '[카드번호 마스킹]', masked_content
)
if masked_content != last_message.content:
state["messages"][-1] = HumanMessage(content=masked_content)
return state
# 그래프에 가드레일 노드 추가
graph.add_node("input_guardrail", input_guardrail_node)
graph.add_edge(START, "input_guardrail")
graph.add_conditional_edges("input_guardrail", supervisor_router)
도구 호출 가드레일
에이전트가 도구를 호출할 때 인자를 검증하고, 위험도가 높은 도구는 확인 단계를 추가한다.
- Rate limiting: 동일 도구의 반복 호출을 제한 (예:
process_refund는 세션당 최대 3회) - 금액 상한: 환불, 결제 등 금융 도구는 금액 상한을 설정
- Human-in-the-Loop: 고위험 도구(데이터 삭제, 대량 발송 등)는 사람 승인 후 실행
출력 가드레일
에이전트 응답이 사용자에게 전달되기 전에 검증한다. 내부 시스템 정보 노출, 부적절한 표현, 할루시네이션 의심 응답을 필터링한다.
Human-in-the-Loop 구현
LangGraph는 그래프 실행을 특정 노드에서 중단하고, 사람의 승인을 받은 후 재개하는 기능을 네이티브로 지원한다.
from langgraph.types import interrupt
def refund_approval_node(state: AgentState) -> AgentState:
"""환불 금액이 50만원 이상이면 관리자 승인을 요청"""
refund_amount = state["tool_results"].get("refund_amount", 0)
if refund_amount >= 500_000:
# 그래프 실행을 여기서 중단 - 사람의 승인을 기다림
approval = interrupt({
"type": "refund_approval",
"amount": refund_amount,
"order_id": state["tool_results"]["order_id"],
"message": f"환불 금액 {refund_amount:,}원. 승인하시겠습니까?"
})
if approval.get("approved"):
return {**state, "escalation_needed": False}
else:
return {
**state,
"messages": state["messages"] + [
{"role": "assistant",
"content": "환불 요청이 관리자에 의해 거절되었습니다. 고객센터로 문의해주세요."}
]
}
return state
이 패턴은 고위험 작업에서 사람의 판단을 개입시켜 사고를 방지한다. 승인 대기 중에도 체크포인트가 유지되므로 서버 재시작에 영향을 받지 않는다.
에러 처리와 장애 복구
멀티에이전트 시스템에서 흔히 발생하는 장애 시나리오와 대응 방법을 정리한다.
장애 시나리오별 대응
| 장애 유형 | 증상 | 대응 방법 |
|---|---|---|
| LLM API 타임아웃 | 특정 노드에서 30초 이상 응답 없음 | 노드별 타임아웃 설정 + 재시도 로직 |
| 무한 루프 | 에이전트 간 순환 핸드오프 반복 | 최대 반복 횟수(recursion_limit) 설정 |
| 도구 실행 실패 | 외부 API 장애로 도구가 에러 반환 | fallback 도구 + 사용자에게 상태 안내 |
| 체크포인트 DB 장애 | 상태 저장/로드 실패 | Circuit breaker + 인메모리 폴백 |
| 에이전트 라우팅 오류 | 잘못된 에이전트로 전달 | Supervisor 재분류 + 최대 재라우팅 횟수 제한 |
recursion_limit 설정
LangGraph 그래프에서 무한 루프를 방지하는 가장 기본적인 안전장치다.
# 최대 25번의 노드 실행 후 강제 종료
app = graph.compile(checkpointer=checkpointer)
result = app.invoke(
{"messages": [{"role": "user", "content": "문의 내용"}]},
config={"configurable": {"thread_id": "session-1"}, "recursion_limit": 25}
)
recursion_limit에 도달하면 GraphRecursionError가 발생한다. 이 에러를 catch하여 사용자에게 "처리 시간이 초과되었습니다. 고객센터로 연결해 드리겠습니다" 같은 안내를 제공해야 한다.
재시도 전략
외부 API 호출이 포함된 도구 노드에는 반드시 재시도 로직을 구현한다.
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import httpx
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type((httpx.TimeoutException, httpx.HTTPStatusError))
)
def call_external_api(endpoint: str, payload: dict) -> dict:
"""외부 API 호출 with exponential backoff"""
response = httpx.post(
endpoint,
json=payload,
timeout=10.0
)
response.raise_for_status()
return response.json()
평가 체계: 멀티에이전트 챗봇의 품질 보장
멀티에이전트 시스템의 평가는 단일 에이전트보다 복잡하다. 개별 에이전트의 응답 품질뿐 아니라 라우팅 정확도, 에이전트 간 핸드오프 효율, 전체 워크플로우의 완결성까지 측정해야 한다.
평가 항목
- 라우팅 정확도: Supervisor가 사용자 의도를 올바른 에이전트로 전달하는 비율. 목표 95% 이상.
- 도구 호출 정확도: 에이전트가 올바른 도구를 올바른 인자로 호출하는 비율. 목표 90% 이상.
- 응답 관련성: 최종 응답이 사용자 질문에 적절한지 LLM-as-a-Judge로 평가. 목표 4.0/5.0 이상.
- 핸드오프 효율: 에이전트 전환 횟수가 최소인지 확인. 불필요한 핸드오프는 지연과 비용을 증가시킨다.
- 장애 복구율: 도구 실패 시 graceful degradation이 작동하는 비율.
LangSmith 통합 모니터링
LangGraph는 LangSmith와 네이티브로 통합되어, 그래프의 모든 노드 실행을 trace로 기록한다. 각 에이전트의 프롬프트, LLM 응답, 도구 호출, 소요 시간, 토큰 사용량을 한눈에 확인할 수 있다. 프로덕션 환경에서는 아래 지표를 대시보드에 반드시 포함한다.
- P99 응답 시간: 사용자 메시지 입력부터 최종 응답까지의 지연
- 에이전트별 호출 빈도: 특정 에이전트에 트래픽이 과도하게 집중되는지 확인
- 도구 호출 실패율: 외부 서비스 장애를 조기에 탐지
- 토큰 사용량/비용: 에이전트별, 세션별 비용 추적
프로덕션 배포 아키텍처
LangGraph Platform은 2025년 GA(General Availability)로 출시되었으며, 세 가지 배포 옵션을 제공한다.
배포 옵션
- Self-Hosted (오픈소스): LangGraph 라이브러리를 직접 서버에 배포. FastAPI 또는 Flask와 결합하여 REST API로 노출한다. 인프라 관리 부담이 있지만 완전한 제어가 가능하다.
- LangGraph Cloud (SaaS): LangChain이 관리하는 클라우드 인프라. Plus/Enterprise 플랜에서 사용 가능. 인프라 관리 부담 없이 빠른 배포가 가능하다.
- Hybrid: SaaS 컨트롤 플레인 + 자체 호스팅 데이터 플레인. 민감 데이터가 자사 인프라를 벗어나지 않으면서 관리 편의성을 확보한다.
Self-Hosted 배포 예시: FastAPI 통합
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langgraph.checkpoint.postgres import PostgresSaver
import uvicorn
app_api = FastAPI(title="Multi-Agent Chatbot API")
# 그래프 컴파일 (앱 시작 시 1회)
DB_URI = "postgresql://user:password@db:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)
checkpointer.setup()
chatbot_graph = build_multi_agent_graph() # 위에서 정의한 그래프
compiled_app = chatbot_graph.compile(checkpointer=checkpointer)
class ChatRequest(BaseModel):
session_id: str
message: str
class ChatResponse(BaseModel):
session_id: str
reply: str
agent_used: str
@app_api.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
try:
config = {
"configurable": {"thread_id": request.session_id},
"recursion_limit": 25
}
result = compiled_app.invoke(
{"messages": [{"role": "user", "content": request.message}]},
config=config
)
last_ai_message = result["messages"][-1]
return ChatResponse(
session_id=request.session_id,
reply=last_ai_message.content,
agent_used=result.get("current_agent", "unknown")
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app_api, host="0.0.0.0", port=8000)
스케일링 고려사항
NVIDIA 엔지니어링 블로그에서 제안하는 LangGraph 프로덕션 스케일링 3단계를 참고한다.
- 단일 사용자 프로파일링: 단일 세션의 평균 노드 실행 시간, 메모리 사용량, DB 쿼리 수를 측정한다.
- 부하 테스트: Locust 또는 k6로 동시 사용자 수를 점진적으로 늘려 병목 지점을 파악한다. 주요 병목은 보통 LLM API 호출과 체크포인트 DB 쓰기에서 발생한다.
- 단계별 롤아웃 + 모니터링: OpenTelemetry + Datadog 조합으로 실시간 지표를 수집하며, 카나리 배포로 점진적으로 트래픽을 전환한다.
운영 경고 사항
프로덕션 멀티에이전트 챗봇 운영 시 반드시 주의해야 할 사항들이다.
비용 폭증 방지
- 멀티에이전트 시스템은 에이전트 수 x LLM 호출 횟수만큼 토큰을 소비한다. Supervisor가 3개의 에이전트를 순차적으로 호출하면 단일 에이전트 대비 3-4배의 비용이 발생할 수 있다.
- 대응: 세션별 최대 토큰 예산을 설정하고, 예산 초과 시 간소화된 응답 경로(lightweight fallback)로 전환한다.
응답 지연 관리
- 에이전트 체인이 길어지면 응답 시간이 수십 초에 달할 수 있다. 사용자 체감 지연을 줄이기 위해 스트리밍 응답을 구현한다.
- LangGraph의
astream_eventsAPI를 사용하면 각 노드의 실행 이벤트를 실시간으로 스트리밍할 수 있다.
에이전트 간 정보 누출
- 주문 에이전트가 처리한 결제 정보가 기술 지원 에이전트에게 불필요하게 노출되는 경우가 있다. State에 민감 정보를 저장할 때는 에이전트별 접근 범위를 명확히 분리해야 한다.
프롬프트 버전 관리
- 에이전트별 프롬프트는 반드시 버전 관리한다. 프롬프트 변경이 라우팅 정확도에 미치는 영향을 A/B 테스트로 검증한 후 배포한다.
프로덕션 체크리스트
멀티에이전트 챗봇을 프로덕션에 배포하기 전 아래 항목을 반드시 확인한다.
아키텍처
- 에이전트별 역할과 도구가 명확히 분리되어 있는가
- Supervisor 라우팅 로직에 fallback 경로가 있는가
- State 스키마에 버전 필드가 포함되어 있는가
- 순환 참조(에이전트 A가 B를 호출하고 B가 다시 A를 호출)가 없는가
안전성
- 입력 가드레일 (길이 제한, injection 탐지, PII 마스킹)이 적용되었는가
- 도구 호출 가드레일 (인자 검증, rate limit, 금액 상한)이 적용되었는가
- 출력 가드레일 (내부 정보 노출 방지)이 적용되었는가
- recursion_limit이 설정되어 있는가
- 고위험 도구에 Human-in-the-Loop이 적용되었는가
복구
- PostgresSaver 기반 체크포인트가 설정되어 있는가
- 외부 API 호출에 재시도 로직(exponential backoff)이 있는가
- 도구 실패 시 fallback 응답이 준비되어 있는가
- GraphRecursionError 핸들링이 되어 있는가
모니터링
- LangSmith 또는 동등한 tracing이 활성화되어 있는가
- P99 응답 시간 알림이 설정되어 있는가
- 에이전트별 토큰 사용량 추적이 가능한가
- 도구 호출 실패율 알림이 설정되어 있는가
배포
- SSL/TLS 암호화가 적용되어 있는가
- API Gateway 뒤에 배포되어 있는가
- 부하 테스트를 통해 최대 동시 사용자 수를 확인했는가
- 카나리 배포 또는 블루/그린 배포 전략이 수립되어 있는가
- 롤백 절차가 문서화되어 있는가
트러블슈팅 가이드
증상: Supervisor가 동일 에이전트를 반복 호출
원인: Supervisor 프롬프트가 모호하거나, 에이전트의 응답이 "작업 완료"를 명확히 표시하지 않는 경우 발생한다.
해결: 각 에이전트의 응답 포맷에 status: "completed" 또는 status: "needs_more_info" 필드를 추가하고, Supervisor가 이 필드를 기반으로 라우팅을 결정하도록 프롬프트를 수정한다.
증상: 체크포인트 로드 시 State 역직렬화 실패
원인: State 스키마를 변경한 후 이전 체크포인트와 호환되지 않는 경우 발생한다.
해결: State에 schema_version 필드를 도입하고, 체크포인트 로드 시 버전을 확인하여 마이그레이션 함수를 실행한다. 호환 불가능한 변경인 경우 해당 세션을 새로 시작하도록 안내한다.
증상: 특정 에이전트의 응답이 비정상적으로 느림
원인: 해당 에이전트에 할당된 도구가 외부 API를 호출하고 있고, 해당 API의 응답 시간이 느린 경우다.
해결: 노드별 타임아웃을 설정하고, 타임아웃 시 캐시된 결과 또는 기본 응답을 반환하는 fallback 로직을 추가한다. LangSmith trace에서 노드별 소요 시간을 확인하여 병목을 정확히 파악한다.
증상: 에이전트 핸드오프 시 컨텍스트 유실
원인: State에 충분한 컨텍스트가 전달되지 않거나, 메시지 히스토리가 잘려 있는 경우다.
해결: 핸드오프 시 Supervisor가 요약 메시지를 State에 추가하도록 구현한다. 예를 들어 "고객 김영주님이 주문번호 ORD-2026-12345의 환불을 요청. 주문 금액 150,000원, 배송 완료 상태" 같은 구조화된 요약을 삽입한다.
마무리
LangGraph 멀티에이전트 챗봇은 단일 에이전트의 한계를 분업과 그래프 구조로 극복한다. 그러나 아키텍처 복잡도가 높은 만큼, 설계 단계에서 상태 관리, 가드레일, 에러 처리, 평가 체계를 철저히 준비해야 한다.
핵심을 정리하면 다음과 같다.
- 에이전트 역할을 명확히 분리하고, 각 에이전트에 최소한의 도구만 할당한다.
- Supervisor 패턴으로 시작하고, 규모가 커지면 Hierarchical Teams으로 확장한다.
- PostgresSaver 체크포인트로 장애 복구를 보장하고, State 스키마를 버전 관리한다.
- 3중 가드레일(입력/도구호출/출력)을 반드시 적용한다.
- LangSmith 기반 모니터링으로 라우팅 정확도, 응답 시간, 비용을 지속적으로 추적한다.
이 가이드의 체크리스트와 코드 예제를 기반으로 프로덕션 배포를 진행하면, 안정적이고 확장 가능한 멀티에이전트 챗봇 시스템을 구축할 수 있다.