-
Chain이 아니라 Graph — LangGraph로 AI 에이전트를 만드는 이유IT 2026. 4. 16. 21:00
LangChain이 있는데 왜 LangGraph가 따로 필요할까? LLM 에이전트의 핵심 흐름이 일직선(Chain)이 아니라 순환하는 그래프(Graph)이기 때문입니다. 실제 코드를 따라가며, 이 그래프가 어떤 모양이고 왜 이런 구조가 되는지 풀어봅니다.
Chain의 한계 — 직선은 판단을 못 한다
LangChain의 기본 모델은 이름 그대로 Chain입니다. A → B → C 순서로 단계를 밟는 파이프라인이죠.
# Chain 방식: 검색 → LLM → 출력 (순서 고정) chain = retriever | prompt | llm | output_parser이 구조는 "검색해서 답변해" 같은 단순 RAG에는 충분합니다. 하지만 이런 상황에서 막힙니다:
- 검색 결과가 불충분하면? → 다시 검색해야 하는데, 체인은 뒤로 못 돌아갑니다
- 웹 검색이 필요한지, RAG가 필요한지? → LLM이 판단해야 하는데, 체인은 분기를 못 합니다
- 도구를 3번 쓸지 1번 쓸지? → 실행 중에 결정해야 하는데, 체인은 고정된 순서만 따릅니다
에이전트에게 필요한 건 "다음에 뭘 할지 스스로 판단하고, 필요하면 되돌아가는" 구조입니다. 이것은 직선이 아니라 순환 그래프입니다.
ReAct Agent의 그래프 구조
LangGraph의
create_react_agent가 만드는 그래프는 아래와 같은 형태입니다. 핵심은 LLM 노드와 Tool 노드 사이의 순환입니다.그래프의 흐름을 정리하면:
- START → LLM: 사용자 메시지 + 시스템 프롬프트 + 메모리 컨텍스트를 받는다
- LLM → 조건 분기: LLM의 응답에
tool_calls가 있는지 검사한다 - YES → Tools: 지정된 도구를 실행하고, 결과를 메시지에 추가한다
- Tools → LLM: 도구 결과를 포함해서 다시 LLM에게 돌아간다 (이것이 핵심 루프)
- NO → END:
tool_calls가 없으면 최종 텍스트 응답이므로 출력한다
Chain이었다면 3번에서 끝나야 합니다. 하지만 Graph이기 때문에 4번이 가능합니다. LLM이 도구 결과를 보고 "아직 부족하다, 한 번 더 검색하자"고 판단할 수 있다는 뜻입니다.
코드로 보는 그래프 생성
LangGraph의
create_react_agent는 위 그래프를 단 몇 줄로 만들어 줍니다:# agent/graph.py from langgraph.prebuilt import create_react_agent from langchain_core.messages import SystemMessage graph = create_react_agent( model=llm, # ChatOpenAI (vLLM 엔드포인트) tools=tools, # @tool 함수 리스트 prompt=system_prompt, # 시스템 프롬프트 )내부적으로 이 함수는
StateGraph를 생성하고, 두 개의 노드(agent,tools)와 조건부 엣지를 연결합니다. 직접 만든다면 이런 형태입니다:# create_react_agent의 내부 동작 (개념) from langgraph.graph import StateGraph, END graph = StateGraph(AgentState) graph.add_node("agent", call_llm) # LLM 호출 graph.add_node("tools", execute_tools) # 도구 실행 graph.set_entry_point("agent") graph.add_conditional_edges( "agent", should_continue, # tool_calls 있으면 "tools", 없으면 END {"tools": "tools", "end": END}, ) graph.add_edge("tools", "agent") # ← 이것이 순환 엣지마지막 줄
add_edge("tools", "agent")가 Chain과 Graph의 결정적 차이입니다. 도구 실행 후 다시 LLM으로 돌아가는 이 엣지가 에이전트의 자율적 반복을 가능하게 합니다.도구(Tool) 등록 — @tool 데코레이터
도구 정의에는 LangChain의
@tool데코레이터를 사용합니다. LangGraph 자체에 별도의 도구 시스템이 있는 것이 아니라, LangChain이 제공하는 도구 추상화를 그대로 가져다 씁니다.@tool은 Python 함수의 docstring을 LLM에게 전달할 도구 설명으로, 타입 힌트를 파라미터 스키마로 자동 변환합니다.from langchain_core.tools import tool @tool def search_knowledge_vault( query: str, top_k: int = 3, category: Optional[str] = None, ) -> str: """개인 지식금고(Obsidian vault)에서 관련 정보를 검색합니다. 업무 기술 노트, 생활 기록, 경영학 수업 노트 등을 포함합니다.""" results = search(query=query, top_k=top_k, ...) return formatted_results이 함수 하나를 등록하면, LLM은 자동으로 아래와 같은 JSON 스키마를 전달받습니다:
{ "name": "search_knowledge_vault", "description": "개인 지식금고에서 관련 정보를 검색합니다...", "parameters": { "query": {"type": "string"}, "top_k": {"type": "integer", "default": 3}, "category": {"type": "string", "nullable": true} } }LLM은 이 스키마를 보고 언제, 어떤 파라미터로 호출할지를 스스로 결정합니다. 사람이 "RAG 성능 이슈 정리해줘"라고 하면, LLM이
search_knowledge_vault("RAG 성능 이슈")를 호출하겠다고 응답하는 식입니다.도구 실패 방어 — _safe_tool 래퍼
에이전트 루프에서 도구 하나가 예외를 던지면 전체 그래프가 멈춥니다. 이를 막기 위해 모든 도구를 래퍼로 감쌉니다:
def _safe_tool(tool_func): original_func = tool_func.func def wrapper(*args, **kwargs): try: return original_func(*args, **kwargs) except Exception as e: return f"[도구 오류] {tool_func.name}: {e}" tool_func.func = wrapper return tool_func # 그래프 생성 시 모든 도구에 적용 tools = [_safe_tool(t) for t in CORE_TOOLS]예외가 에러 텍스트로 변환되어 LLM에게 돌아갑니다. LLM은 이 에러 메시지를 보고 다른 도구를 시도하거나, 사용자에게 상황을 설명할 수 있습니다. 그래프의 순환 구조 덕분에 가능한 복구 패턴입니다.
실행 예시 — 그래프가 실제로 도는 모습
사용자가 "지난달 논의한 RAG 성능 이슈 정리해줘"라고 하면, 그래프는 아래처럼 순환합니다:
Chain이었다면 Iteration 1에서 검색 결과를 받고 바로 답변해야 합니다. 하지만 Graph 구조이기 때문에 LLM이 "결과가 부족하다"고 판단하면 추가 검색을 할 수 있습니다. 이 자율적 판단이 에이전트의 핵심 가치입니다.
스트리밍 — astream_events로 그래프 내부 엿보기
LangGraph는 그래프 실행 중 각 노드의 이벤트를 스트리밍으로 내보냅니다. 이를 활용하면 사용자에게 에이전트가 지금 무엇을 하고 있는지 실시간으로 보여줄 수 있습니다:
async for event in graph.astream_events( {"messages": langchain_messages}, version="v2" ): if event["event"] == "on_tool_start": # "검색 중..." 상태 표시 send_status(tool_name=event["name"], status="running") elif event["event"] == "on_chat_model_stream": # LLM 토큰을 실시간 스트리밍 send_token(event["data"]["chunk"])on_tool_start,on_tool_end,on_chat_model_stream같은 이벤트가 그래프의 각 노드 전환 시점에 발생합니다. 그래프의 구조가 명확하기 때문에, 이벤트 타이밍도 예측 가능합니다.왜 Graph인가 — 정리
LangGraph를 쓰는 이유를 한 문장으로 요약하면: 에이전트의 흐름에는 루프가 있기 때문입니다.
Chain (LangChain) Graph (LangGraph) 흐름 A → B → C (직선) A → B → A → B → C (순환) 도구 호출 고정된 순서, 1회 LLM이 판단, 0~N회 분기 사전 정의된 조건 LLM 출력 기반 동적 분기 상태 관리 입력 → 출력 파이프 메시지 리스트에 누적 적합한 용도 단순 RAG, 변환 파이프라인 에이전트, 멀티턴 도구 사용 Chain은 "정해진 절차를 따라 처리"하는 데 강합니다. Graph는 "상황을 보고 다음 행동을 결정"하는 데 강합니다. AI 에이전트는 후자입니다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
Microsoft 365 구독 해지와 AI 마이그레이션 — 556GB를 구출한 일주일 (0) 2026.04.19 ChatGPT 유료 구독 취소 후 달라진 3가지 (0) 2026.04.19 n8n으로 로컬 AI 챗봇 오케스트레이션 이전하기 — 하루 종일 삽질한 실전 기록 (0) 2026.04.18 AI 모델 리더보드, Chatbot Arena 완전 해부 (1) 2026.04.17 LangGraph 에이전트에 Langfuse 붙이기 — LLM 앱의 블랙박스를 유리상자로 (0) 2026.04.16 아카라이브 알파카 채널과 AI 모델 이름의 계보 (0) 2026.04.14 Gemma 4 로컬 AI 스택 완전 정복 — DGX Spark에서 돌려본 솔직한 후기 (1) 2026.04.13 GPU에서 LLM까지, 추론 스택 완전 해부 (0) 2026.04.13 GitHub 오픈소스 PR 리뷰, 놓치지 않는 자동화 시스템 만들기 (0) 2026.04.12 AI 코딩 에이전트의 자기 진화 학습 시스템 — 실수를 기억하고 성장하는 에이전트 만들기 (1) 2026.04.11