Agentic AI 구축/LangChain & LangGraph

LangChain의 한계와 이를 보완하기 위한 LangGraph의 구조

gksyb4235 2025. 11. 19. 11:32

LangChain의 구조


 

 

LangChain의 구조를 정리하면 위와 같은 식으로 동작한다.

LangChain은 아래의 "REACT: Synergizing Reasoning and Acting In Language Models"라는 논문의 아이디어를 기반으로 탄생한 Framework이다.

 

 

 

LangChain Agent는 내부적으로 다음의 Loop를 반복해서 돈다.

 

 

  • Tool 목록 + 설명을 LLM에 보여줌
    • MCP Tool이든, LangChain Tool이든, 결국은
      • 이름, 무슨 일을 하는지 설명, 입력 포맷(스키마)
        위와 같은 정보를 프롬프트(또는 OpenAI tool spec)로 LLM에게 넘겨줌.
  • Decide(): “지금 무엇을 할까?”
    • LLM에게 현재 대화 상태 / 작업 목표 / 사용 가능한 Tool 목록을 주고 다음과 같이 묻는다.
    • “지금 바로 답을 출력할래? 아니면 어떤 Tool을 쓸래? 쓴다면 어떤 입력으로?”
  • Action(): Tool 호출
    • LLM이 “Tool A를 이런 입력으로 호출하겠다”라고 텍스트로 결정하면,
    • 프레임워크(LangChain)가 실제 MCP Tool API를 호출해서 실행한다.
  • Observation: Tool 실행 결과를 다시 LLM에게 보여줌
    • Tool에서 나온 JSON/텍스트/결과를 LLM에게 다시 넣고,
    • “이 결과를 보고 다음에 뭘 할래?”라고 다시 묻는 구조이다.
  • 반복
    • LLM이 또 Decide() → Action() → Observation…

 

 

 

LangChain의 구조적 한계


이런 LangChain의 구조는 Tool이 몇 개 되지 않을 때는 그럭저럭 잘 동작한다.

왜냐하면 LLM 입장에서 선택지가 적기 때문에 "이 상황에선 이런 Tool을 쓰면 되겠구나"라는 것을,

Prompt만으로도 학습하거나 추론할 수 있기 때문이다.

그래서 작은 시스템에서는 LLM에게 Tool 목록을 던져주고 알아서 골라 쓰게 하는 방식이 꽤 잘 먹힌다.

 

문제는 MCP Tool이 수십, 수백 개가 되고, 이 Tool들이 Side Effect를 가지기 시작하면 발생한다.

이 경우 다음의 문제가 발생한다.

 

(1) 액션 공간 폭발

  • LLM은 매 스텝마다:
    • 어떤 Tool을 쓸지? (수십 개 중 하나)
    • 어떤 순서로 이어갈지?
    • 어느 시점에 “이제 답을 끝낼지?”
  • 이게 사람이 보기엔 “워크플로우 설계 문제”인데,
    • LangChain Agent에선 그걸 전부 LLM의 토큰 단위 추론에 맡겨버리는 구조라서,
    • 길어질수록, 복잡해질수록, 분기 많을수록 점점 불안정해지게 된다.

결과적으로 필요 없는 Tool을 호출한다든지, 같은 Tool을 반복 호출하는 순환 루프에 빠진다던지, 중간 상태를 잃어버려서 앞뒤가 안 맞는 액션을 한다든지 하는 문제가 생김.

 

(2) 글로벌 상태/컨텍스트 관리의 한계

복잡한 시스템에서는:

  • “현재 세션이 어떤 리소스를 이미 잡고 있는지”
  • “어떤 Tool 호출이 선행 조건인지(예: 로그인 → 세션 발급 → 그 세션으로만 API 호출 가능)”
  • “어느 호출이 실패했고, 롤백이 필요한지”

같은 시스템 레벨 상태를 관리해야 하는데,

  • LangChain Agent는 이걸 대부분 “프롬프트 속 자연어 설명 + LLM의 임시 기억”에 의존함.
  • 코드/그래프 레벨로 강제되고 검증되는 State machine이 아니라,
    • “LLM이 잘 기억하겠지…”에 기대는 구조라
    • 스텝이 길어질수록 컨텍스트 깨지고, 순서 꼬이고, 일관성이 깨지기 쉬움.

 

(3) 에러 처리/예외 상황에서 취약

  • MCP Tool이 네트워크 오류, 타임아웃, 이상한 응답을 줄 수 있음.
  • 이때:
    • 재시도할지?
    • 다른 Tool로 fallback 할지?
    • 사용자에게 오류를 전달할지?
  • 이런 로직을 명시적 코드로 짜는 게 아니라, 대충:
    • “에러가 나오면 적절히 재시도해” 같은 프롬프트 문장으로 LLM에게 부탁하는 수준이기 때문에,
    • 특정 상황에선 잘 처리되다가, 다른 상황에선 이상하게 행동하는 식의 non-deterministic behavior가 나옴.

 

 

 

Opaque한 LangChain의 한계를 극복하기 위한 LangGraph의 등장


따라서 현재 구조에서는 Agent의 "생각 과정"이 대략 다음과 같이 된다.

 

  • LangChain: ‘도구 리스트 + 설명 + 지금까지의 대화 + Tool 결과’를 프롬프트에 몽땅 넣고,
    “이제 다음 스텝을 계획해봐” 라고 LLM에 요청
  • LLM 내부에서 일어나는 건:
    • 토큰 레벨 확률에 따라 “어떤 Tool을 선택할지”, “어떤 입력을 줄지”를 생성하는 과정인데,
    • 이게 코드/그래프 형태의 명시적인 로직이 아니라, 완전히 블랙박스임.

즉, 왜 이 시점에서 Tool A가 아닌 Tool B를 골랐는지, 왜 에러에서 재시도가 아닌 종료를 선택했는지,
왜 똑같은 프롬프트와 똑같은 Tool 목록에서 언제는 잘 동작하고, 언제는 이상한 Tool을 쓰는지,

이걸 분석/디버깅하기가 엄청 까다롭다는 의미에서 opaque하다고 할 수 있다.

 

 

 

 

이런 문제를 개선하고자 나온 것이 LangGraph이다.

기존의 BlackBox라고 할 수 있는 Chain 형태를, State Machine 기반의 상태/노드/엣지 기반의 Workflow로 명시적으로 구조화함으로 인해서 LLM의 너무 큰 자율성을 제한시키는 구조이다.

 

  • LLM은:
    • “어떤 노드에서 어떤 결정을 내릴지” 혹은
    • “노드 내부의 세부 로직을 채우는 역할” 정도로 한정

→ 이런 식으로 가면:

  • 어떤 경로로 Tool이 호출될 수 있는지 정적 구조가 눈에 보이고,
  • 특정 제약을 코드/그래프 수준에서 강제할 수 있고,
  • 디버깅/테스트/리플레이가 훨씬 쉬워졌다는 장점이 있다.

 

다음 포스트에서 LangGraph의 구조에 대해 구체적으로 살펴볼 것.