콜로소

전체 공개

[개발자] AI에이전트를 어떤걸 이용해서 만드는것이 가장 좋을까?

AI튜터랩

AI튜터랩

조회수 6
발행2026.07.20 16:02
수정2026.08.11 00:01

AI 에이전트 프레임워크, 무엇을 선택해야 할까?

[이미지 삽입 제안: LangGraph, CrewAI, OpenAI Agents SDK, Microsoft Agent Framework, LlamaIndex가 서로 다른 길로 나뉘는 ‘AI 에이전트 프레임워크 선택 지도’]

“AI 에이전트를 만들어야 하는데, LangGraph와 CrewAI 중 무엇을 써야 할까요?”

“멀티 에이전트라면 무조건 AutoGen을 사용해야 하나요?”

“프로토타입은 만들었는데, 실제 서비스로 옮기려니 상태 관리와 예외 처리가 너무 복잡합니다.”

최근 AI 에이전트를 개발하려는 팀들이 가장 먼저 마주하는 문제입니다.

프레임워크마다 예제만 보면 비슷해 보입니다. LLM을 호출하고, 도구를 연결하고, 여러 에이전트가 협업하게 만드는 기능을 대부분 제공하기 때문입니다.

하지만 실제 서비스를 만들기 시작하면 차이가 분명해집니다.

우리가 진짜 선택해야 하는 것은 ‘가장 유명한 프레임워크’가 아니라, 에이전트의 실행 흐름을 어디까지 통제해야 하는가입니다.


❌ 에이전트 프레임워크를 기능 개수로 비교하면 실패합니다

AI 에이전트 프레임워크를 비교할 때 흔히 다음과 같은 질문부터 시작합니다.

  • 멀티 에이전트를 지원하는가?

  • 메모리를 제공하는가?

  • 도구 호출이 가능한가?

  • 스트리밍을 지원하는가?

물론 필요한 질문입니다.

하지만 현재 주요 프레임워크는 대부분 이 기능들을 일정 수준 이상 지원합니다. 단순한 기능 체크리스트만으로는 실제 프로젝트에 맞는 선택을 하기 어렵습니다.

개발자가 먼저 확인해야 할 기준은 다음 세 가지입니다.

1. 실행 흐름을 코드로 명확하게 통제해야 하는가?

에이전트가 자율적으로 판단하도록 맡길 것인지, 아니면 특정 단계와 조건을 반드시 거치게 할 것인지 결정해야 합니다.

예를 들어 고객 문의 에이전트가 환불 요청을 감지하면, 곧바로 환불 API를 실행하게 해서는 안 됩니다.

문의 분석
→ 환불 조건 확인
→ 사용자 정보 검증
→ 담당자 승인
→ 환불 API 호출

이처럼 실행 순서와 분기 조건이 중요한 서비스라면, 대화 중심 프레임워크보다 그래프 또는 워크플로 중심 프레임워크가 적합합니다.

2. 실행 중간 상태를 저장하고 복구해야 하는가?

실제 서비스에서는 LLM 호출 실패, 외부 API 오류, 사용자 응답 대기, 서버 재시작이 발생합니다.

따라서 에이전트가 단순히 “처음부터 다시 실행”되는 것이 아니라, 마지막 성공 지점에서 이어서 실행될 수 있어야 합니다.

3. 사람이 중간에 개입해야 하는가?

결제, 환불, 이메일 발송, 데이터 삭제처럼 위험도가 높은 작업은 사람이 검토한 후 실행해야 합니다.

이 기능을 흔히 Human-in-the-Loop, 즉 실행 과정에 사람의 승인이나 수정을 포함하는 구조라고 부릅니다.


주요 AI 에이전트 프레임워크별 특징

1. OpenAI Agents SDK

OpenAI Agents SDK는 적은 수의 핵심 개념으로 에이전트를 빠르게 만드는 데 초점을 맞춘 프레임워크입니다.

에이전트, 도구, 다른 에이전트로의 핸드오프, 가드레일, 세션과 같은 개념을 비교적 단순한 인터페이스로 제공합니다. 입력·출력 검증을 위한 가드레일과 실행 과정을 확인할 수 있는 트레이싱도 기본 제공됩니다.

적합한 상황

  • OpenAI 모델 중심으로 빠르게 개발할 때

  • 고객 지원, 질의응답, 업무 보조 에이전트를 만들 때

  • 에이전트 간 업무 전달 구조가 필요할 때

  • 복잡한 그래프보다 간단한 추상화를 선호할 때

장점

✅ 학습해야 할 핵심 개념이 적습니다.
✅ 함수 도구와 에이전트 핸드오프를 빠르게 구성할 수 있습니다.
✅ 가드레일과 트레이싱을 기본 기능으로 활용할 수 있습니다.

고려할 점

실행 경로가 복잡하게 분기되거나, 모든 단계의 상태 전이를 세밀하게 제어해야 한다면 별도의 오케스트레이션 계층이 필요할 수 있습니다.


2. CrewAI

CrewAI는 여러 에이전트에게 역할을 부여하고, 하나의 팀처럼 협업하게 만드는 구조에 강점이 있습니다.

에이전트들이 협업하는 Crew와 이벤트 기반 실행 흐름을 구성하는 Flow를 중심으로 설계되어 있습니다. 공식 문서는 에이전트, Crew, Flow와 함께 메모리, 지식, 가드레일, 관측 기능을 주요 구성 요소로 제시합니다.

예를 들어 다음과 같이 역할을 나눌 수 있습니다.

시장 조사 에이전트
→ 자료 조사

분석 에이전트
→ 핵심 내용 분석

작성 에이전트
→ 보고서 작성

검수 에이전트
→ 오류 및 누락 확인

적합한 상황

  • 역할 기반 멀티 에이전트를 빠르게 만들 때

  • 콘텐츠 제작이나 리서치 자동화를 구현할 때

  • 사람으로 구성된 조직처럼 에이전트 역할을 표현하고 싶을 때

  • 자율적인 에이전트 협업을 실험할 때

장점

✅ 멀티 에이전트 구조를 직관적으로 표현할 수 있습니다.
✅ 역할, 목표, 작업을 중심으로 설계하기 쉽습니다.
✅ 프로토타입을 빠르게 확인하기 좋습니다.

고려할 점

에이전트의 자율성이 높아질수록 실행 경로를 예측하고 테스트하기 어려워질 수 있습니다.

따라서 결제나 데이터 변경처럼 결정론적인 통제가 필요한 업무에서는 자율적인 Crew와 명시적인 Flow를 적절히 분리해야 합니다.


3. Microsoft Agent Framework와 Semantic Kernel

Microsoft 생태계에서 에이전트를 개발한다면 Microsoft Agent Framework와 Semantic Kernel을 주목할 수 있습니다.

Microsoft Agent Framework는 AutoGen의 에이전트 추상화와 Semantic Kernel의 엔터프라이즈 기능을 결합하는 방향으로 제공되고 있습니다. 기존 AutoGen은 현재 유지보수 모드이며, Microsoft는 신규 사용자에게 Microsoft Agent Framework를 시작점으로 권장하고 있습니다.

Semantic Kernel은 모델에 종속되지 않는 SDK로, 기존 애플리케이션의 함수와 비즈니스 로직을 플러그인 형태로 AI 에이전트에 연결하는 데 적합합니다. Python뿐 아니라 .NET과 Java 환경도 지원합니다.

적합한 상황

  • Azure와 Microsoft 기술 스택을 사용하고 있을 때

  • 기존 .NET 또는 Java 애플리케이션에 AI 기능을 추가할 때

  • 엔터프라이즈 수준의 인증, 상태 관리, 운영 통합이 중요할 때

  • 기존 비즈니스 로직을 플러그인으로 재사용할 때

장점

✅ Microsoft 엔터프라이즈 환경과 연결하기 좋습니다.
✅ Python 외에 .NET과 Java 개발자도 활용할 수 있습니다.
✅ 기존 애플리케이션의 함수를 에이전트 도구로 전환하기 좋습니다.

고려할 점

기존 AutoGen 예제를 그대로 신규 프로젝트의 기준으로 삼기보다는, 현재 Microsoft Agent Framework로의 전환 방향을 함께 검토해야 합니다.


4. LlamaIndex

LlamaIndex는 문서, 데이터베이스, 벡터 저장소 등 외부 데이터와 연결되는 AI 애플리케이션에 강점이 있습니다.

단순한 에이전트 프레임워크라기보다 데이터 수집, 인덱싱, 검색, 질의와 에이전트 워크플로를 함께 구성하는 데이터 중심 프레임워크에 가깝습니다.

LlamaIndex는 여러 모듈을 연결하는 선언형 쿼리 파이프라인과 단일·멀티 에이전트 워크플로를 제공합니다.

적합한 상황

  • RAG 서비스가 프로젝트의 핵심일 때

  • 문서 검색과 에이전트 실행을 함께 구성할 때

  • 다양한 데이터 소스와 검색 엔진을 연결할 때

  • 검색 결과를 기반으로 보고서나 답변을 생성할 때

장점

✅ 데이터 수집부터 검색까지 다양한 구성 요소를 제공합니다.
✅ RAG와 에이전트를 자연스럽게 결합할 수 있습니다.
✅ 검색기와 쿼리 엔진을 도구로 연결하기 좋습니다.

고려할 점

복잡한 비즈니스 프로세스 전체를 제어하는 것이 핵심이라면, LlamaIndex를 데이터·검색 계층으로 사용하고 LangGraph 같은 오케스트레이션 프레임워크와 결합하는 방식도 고려할 수 있습니다.


5. LangGraph

LangGraph는 에이전트의 실행 과정을 상태를 가진 그래프로 설계하는 저수준 오케스트레이션 프레임워크입니다.

각 작업을 노드로 만들고, 노드 사이의 이동 경로를 엣지로 연결합니다.

사용자 요청

요청 분류

┌ 검색 필요 ─ 검색 실행 ┐
│                       ↓
└ 일반 질문 ───────→ 답변 생성

                      품질 검수

공식 문서에서 LangGraph는 장기 실행과 상태 기반 에이전트를 위한 저수준 프레임워크로 설명되며, 지속 가능한 실행, 스트리밍, Human-in-the-Loop, 상태 저장과 메모리를 주요 기능으로 제공합니다. LangChain을 함께 사용할 수 있지만 LangGraph 자체가 LangChain 사용을 강제하지는 않습니다.

적합한 상황

  • 실행 순서와 조건을 명시적으로 통제해야 할 때

  • 상태를 저장하고 중단된 작업을 이어서 실행해야 할 때

  • 사용자의 승인이나 담당자 검토가 필요할 때

  • 단일 에이전트와 멀티 에이전트를 하나의 구조로 관리할 때

  • 복잡한 서비스 로직을 테스트 가능한 코드로 만들 때

장점

✅ 실행 흐름을 그래프로 명확하게 표현할 수 있습니다.
✅ 조건 분기와 반복 실행을 코드로 통제할 수 있습니다.
✅ 체크포인트를 이용한 상태 저장과 복구가 가능합니다.
✅ 단일 에이전트, 멀티 에이전트, RAG 워크플로를 모두 표현할 수 있습니다.

고려할 점

CrewAI나 OpenAI Agents SDK보다 처음 설계해야 할 코드가 많습니다.

노드, 상태, 엣지, 조건 분기를 직접 정의해야 하기 때문에 간단한 챗봇에는 다소 무겁게 느껴질 수 있습니다.


한눈에 보는 선택 기준

프레임워크핵심 강점추천 프로젝트OpenAI Agents SDK가벼운 추상화, 핸드오프, 가드레일OpenAI 기반 업무·상담 에이전트CrewAI역할 기반 멀티 에이전트리서치, 콘텐츠, 분석 자동화Microsoft Agent FrameworkMicrosoft 생태계와 엔터프라이즈 통합Azure, .NET, Java 기반 서비스LlamaIndex데이터 연결과 RAG문서 검색, 지식 에이전트LangGraph상태와 실행 흐름의 세밀한 통제복잡한 업무 프로세스와 운영 서비스

💡 프레임워크를 하나만 선택해야 하는 것은 아닙니다.

예를 들어 LlamaIndex로 검색 계층을 만들고, LangGraph가 전체 실행 순서와 승인 과정을 관리하게 만들 수 있습니다.

또한 하나의 LangGraph 노드 내부에서 OpenAI Agents SDK로 만든 에이전트를 호출하는 방식도 가능합니다.


왜 범용적인 구현에는 LangGraph가 적합할까?

LangGraph가 항상 최고의 선택이라는 뜻은 아닙니다.

다만 프로젝트 요구사항이 아직 완전히 확정되지 않았거나, 이후 복잡한 서비스로 확장될 가능성이 있다면 비교적 안전한 선택지가 될 수 있습니다.

그 이유는 LangGraph가 특정한 에이전트 협업 패턴을 강제하기보다, 실행 흐름 자체를 표현하는 기반을 제공하기 때문입니다.

하나의 노드는 다음 중 무엇이든 될 수 있습니다.

  • LLM 호출

  • 검색 API 호출

  • 사내 데이터베이스 조회

  • Python 함수 실행

  • 다른 에이전트 호출

  • 사용자 승인 대기

  • 결과 검증

  • 실패 복구 처리

즉, LangGraph를 단순히 “AI 에이전트 프레임워크”가 아니라 AI가 포함된 백엔드 워크플로 엔진처럼 사용할 수 있습니다.


LangGraph로 도구 호출 에이전트 구현하기

이번 예제에서는 사용자의 요청을 받은 에이전트가 필요한 경우 도구를 호출하고, 결과를 바탕으로 최종 답변을 생성하도록 구성하겠습니다.

전체 구조는 다음과 같습니다.

START

agent
  ├─ 도구 호출 필요 → tools → agent
  └─ 도구 호출 없음 → END

[이미지 삽입 제안: START → agent → tools → agent → END로 연결된 LangGraph 순환 구조]


STEP 1. 패키지 설치하기

pip install -U langgraph langchain-openai

환경 변수에 API 키도 등록합니다.

export OPENAI_API_KEY="YOUR_API_KEY"

Windows PowerShell에서는 다음과 같이 설정할 수 있습니다.

$env:OPENAI_API_KEY="YOUR_API_KEY"

LangGraph의 현재 Python 문서는 Python 3.10 이상을 기준으로 안내하고 있습니다.


STEP 2. 에이전트가 사용할 도구 정의하기

예제에서는 상품 재고를 조회하는 간단한 함수를 사용하겠습니다.

from langchain_core.tools import tool


@tool
def get_inventory(product_name: str) -> str:
    """상품 이름으로 현재 재고를 조회한다."""

    inventory = {
        "무선 키보드": 12,
        "게이밍 마우스": 0,
        "27인치 모니터": 5,
    }

    quantity = inventory.get(product_name)

    if quantity is None:
        return f"{product_name} 상품을 찾을 수 없습니다."

    return f"{product_name}의 현재 재고는 {quantity}개입니다."

중요한 부분은 함수의 docstring입니다.

LLM은 함수 이름과 설명, 입력 스키마를 보고 언제 이 도구를 사용해야 하는지 판단합니다.

따라서 도구 설명은 개발자를 위한 주석이 아니라, 에이전트의 행동 기준입니다.


STEP 3. 그래프가 공유할 State 정의하기

State는 각 노드가 읽고 수정하는 공용 데이터입니다.

from langgraph.graph import MessagesState

이번에는 LangGraph가 제공하는 MessagesState를 사용합니다.

직접 상태를 정의하고 싶다면 TypedDict를 사용할 수도 있습니다.

from typing import Annotated
from typing_extensions import TypedDict

from langchain_core.messages import AnyMessage
from langgraph.graph.message import add_messages


class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

add_messages 리듀서는 새로운 메시지가 들어왔을 때 기존 메시지 목록을 덮어쓰지 않고 누적합니다.

LangGraph 공식 Quickstart 역시 상태에 메시지와 실행 중 필요한 값을 저장하고, 그래프 실행 과정에서 이 상태를 유지하는 구조를 사용합니다.


STEP 4. 모델과 도구 연결하기

from langchain_openai import ChatOpenAI

tools = [get_inventory]

model = ChatOpenAI(
    model="gpt-4.1-mini",
    temperature=0,
)

model_with_tools = model.bind_tools(tools)

bind_tools()를 호출하면 모델이 일반 텍스트 응답뿐 아니라 등록된 도구 호출도 선택할 수 있습니다.


STEP 5. Agent 노드 만들기

from langchain_core.messages import SystemMessage


def call_agent(state: MessagesState):
    system_message = SystemMessage(
        content=(
            "당신은 쇼핑몰 고객지원 에이전트입니다. "
            "상품 재고 질문을 받으면 반드시 제공된 재고 조회 도구를 사용하세요. "
            "조회되지 않은 정보를 추측하지 마세요."
        )
    )

    response = model_with_tools.invoke(
        [system_message, *state["messages"]]
    )

    return {"messages": [response]}

LangGraph의 노드는 일반적인 Python 함수입니다.

노드는 현재 State를 입력받고, 변경할 State 값을 딕셔너리 형태로 반환합니다.


STEP 6. 도구 실행 노드 만들기

LangGraph에는 모델이 요청한 도구를 실행하는 ToolNode가 준비되어 있습니다.

from langgraph.prebuilt import ToolNode

tool_node = ToolNode(tools)

직접 도구 실행 로직을 구현할 수도 있지만, 일반적인 Tool Calling 구조라면 ToolNode를 사용하는 편이 간단합니다.


STEP 7. 다음 경로를 결정하는 조건 함수 만들기

모델의 마지막 메시지에 도구 호출 요청이 있으면 tools 노드로 이동하고, 없다면 실행을 종료합니다.

from typing import Literal


def route_agent(
    state: MessagesState,
) -> Literal["tools", "__end__"]:
    last_message = state["messages"][-1]

    if last_message.tool_calls:
        return "tools"

    return "__end__"

이 함수가 에이전트의 행동을 결정하는 것이 아닙니다.

에이전트가 이미 만든 결과를 확인한 뒤, 그래프의 다음 실행 위치를 결정하는 라우터입니다.


STEP 8. 그래프 조립하기

from langgraph.graph import StateGraph, START, END


builder = StateGraph(MessagesState)

builder.add_node("agent", call_agent)
builder.add_node("tools", tool_node)

builder.add_edge(START, "agent")

builder.add_conditional_edges(
    "agent",
    route_agent,
    {
        "tools": "tools",
        "__end__": END,
    },
)

builder.add_edge("tools", "agent")

graph = builder.compile()

여기서 가장 중요한 부분은 다음 연결입니다.

builder.add_edge("tools", "agent")

도구 실행이 끝난 뒤 다시 에이전트 노드로 돌아갑니다.

에이전트는 도구 실행 결과를 확인하고, 추가 도구가 필요한지 판단하거나 최종 답변을 생성합니다.


STEP 9. 에이전트 실행하기

from langchain_core.messages import HumanMessage


result = graph.invoke(
    {
        "messages": [
            HumanMessage(
                content="게이밍 마우스 재고가 있는지 확인해줘."
            )
        ]
    }
)

print(result["messages"][-1].content)

예상되는 실행 흐름은 다음과 같습니다.

1. 사용자가 재고 질문 입력
2. agent가 get_inventory 도구 호출 결정
3. tools가 재고 조회 함수 실행
4. agent가 실행 결과 확인
5. 최종 답변 생성

예상 출력은 다음과 같습니다.

현재 게이밍 마우스 재고는 0개입니다.

전체 코드

from typing import Literal

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

from langgraph.graph import MessagesState, StateGraph, START, END
from langgraph.prebuilt import ToolNode


@tool
def get_inventory(product_name: str) -> str:
    """상품 이름으로 현재 재고를 조회한다."""

    inventory = {
        "무선 키보드": 12,
        "게이밍 마우스": 0,
        "27인치 모니터": 5,
    }

    quantity = inventory.get(product_name)

    if quantity is None:
        return f"{product_name} 상품을 찾을 수 없습니다."

    return f"{product_name}의 현재 재고는 {quantity}개입니다."


tools = [get_inventory]

model = ChatOpenAI(
    model="gpt-4.1-mini",
    temperature=0,
)

model_with_tools = model.bind_tools(tools)


def call_agent(state: MessagesState):
    system_message = SystemMessage(
        content=(
            "당신은 쇼핑몰 고객지원 에이전트입니다. "
            "상품 재고 질문을 받으면 반드시 제공된 재고 조회 도구를 사용하세요. "
            "조회되지 않은 정보를 추측하지 마세요."
        )
    )

    response = model_with_tools.invoke(
        [system_message, *state["messages"]]
    )

    return {"messages": [response]}


def route_agent(
    state: MessagesState,
) -> Literal["tools", "__end__"]:
    last_message = state["messages"][-1]

    if last_message.tool_calls:
        return "tools"

    return "__end__"


builder = StateGraph(MessagesState)

builder.add_node("agent", call_agent)
builder.add_node("tools", ToolNode(tools))

builder.add_edge(START, "agent")

builder.add_conditional_edges(
    "agent",
    route_agent,
    {
        "tools": "tools",
        "__end__": END,
    },
)

builder.add_edge("tools", "agent")

graph = builder.compile()


result = graph.invoke(
    {
        "messages": [
            HumanMessage(
                content="게이밍 마우스 재고가 있는지 확인해줘."
            )
        ]
    }
)

print(result["messages"][-1].content)

STEP 10. 대화 상태 저장하기

지금 만든 그래프는 한 번의 실행이 끝나면 이전 대화 상태를 기억하지 않습니다.

대화를 이어가려면 체크포인터를 추가해야 합니다.

from langgraph.checkpoint.memory import InMemorySaver


checkpointer = InMemorySaver()

graph = builder.compile(
    checkpointer=checkpointer
)

실행할 때는 대화를 구분할 thread_id를 전달합니다.

config = {
    "configurable": {
        "thread_id": "customer-1001"
    }
}

first_result = graph.invoke(
    {
        "messages": [
            HumanMessage(
                content="무선 키보드 재고를 확인해줘."
            )
        ]
    },
    config=config,
)

second_result = graph.invoke(
    {
        "messages": [
            HumanMessage(
                content="그 상품은 몇 개 남았어?"
            )
        ]
    },
    config=config,
)

같은 thread_id를 사용하면 이전 실행의 메시지 상태를 이어서 사용할 수 있습니다.

LangGraph의 체크포인터는 각 그래프 단계의 State를 체크포인트로 저장합니다. 이를 통해 대화 메모리뿐 아니라 Human-in-the-Loop, 이전 상태 재실행, 오류 복구를 구현할 수 있습니다.

⚠️ InMemorySaver는 개발과 테스트 용도에 적합합니다.

프로세스가 종료되면 데이터가 사라지므로 운영 환경에서는 Postgres, Redis, 데이터베이스 기반 체크포인터를 사용해야 합니다. 공식 문서는 로컬 실험에는 SQLite를, 운영 환경에는 Postgres와 같은 영속 저장소를 사용할 수 있다고 안내합니다.


승인 절차가 필요한 에이전트로 확장하기

에이전트가 이메일 발송, 환불, 데이터 삭제 같은 작업을 수행한다면 도구를 바로 실행해서는 안 됩니다.

다음처럼 승인 노드를 추가할 수 있습니다.

agent

위험 도구 여부 판단
  ├─ 일반 도구 → tools
  └─ 위험 도구 → approval

              승인 → tools
              거절 → END

LangGraph의 interrupt()를 사용하면 그래프 실행을 중단하고 외부의 승인 입력을 기다릴 수 있습니다.

from langgraph.types import interrupt


def approval_node(state: MessagesState):
    decision = interrupt(
        {
            "message": "이 작업을 실행할까요?",
            "pending_action": state["messages"][-1].tool_calls,
        }
    )

    return {
        "approval": decision
    }

이후 Command를 이용해 중단된 그래프를 다시 실행할 수 있습니다.

Human-in-the-Loop 구조에서는 실행 중단 시점의 상태를 보존해야 하므로 체크포인터가 필요합니다. LangGraph 공식 문서 역시 interrupt 기반 실행에는 운영 환경용 영속 체크포인터 사용을 권장합니다.


실무에서는 노드를 어떻게 나누어야 할까?

처음 LangGraph를 사용하면 모든 작업을 하나의 agent 노드에 넣기 쉽습니다.

하지만 운영 가능한 구조를 만들려면 역할에 따라 노드를 분리해야 합니다.

classify_request
→ retrieve_context
→ generate_answer
→ validate_answer
→ request_approval
→ execute_action
→ save_result

각 노드는 가능하면 한 가지 책임만 가지게 설계합니다.

좋지 않은 구조

def agent_node(state):
    # 요청 분류
    # 검색
    # 데이터베이스 조회
    # 답변 생성
    # 결과 검증
    # 이메일 전송
    ...

이 구조는 실패 원인을 찾기 어렵고, 특정 단계만 재실행하거나 테스트하기 어렵습니다.

개선된 구조

def classify_request(state):
    ...

def retrieve_context(state):
    ...

def generate_answer(state):
    ...

def validate_answer(state):
    ...

def send_email(state):
    ...

이렇게 나누면 각 노드를 독립적으로 테스트할 수 있고, 실행 기록에서도 어느 단계에서 문제가 발생했는지 확인하기 쉬워집니다.


운영 환경에서 반드시 추가해야 할 것

프로토타입 코드가 동작한다고 바로 서비스에 배포해서는 안 됩니다.

다음 항목을 함께 설계해야 합니다.

1. 무한 반복 방지

에이전트가 계속 도구를 호출하지 않도록 최대 반복 횟수를 제한해야 합니다.

config = {
    "recursion_limit": 20,
    "configurable": {
        "thread_id": "customer-1001"
    },
}

2. 도구 입력값 검증

LLM이 만든 인자를 그대로 데이터베이스나 API에 전달하면 안 됩니다.

Pydantic 모델, 허용 목록, 권한 검사를 이용해 입력값을 검증해야 합니다.

3. 읽기 도구와 쓰기 도구 분리

다음 두 종류의 도구는 위험도가 다릅니다.

읽기 도구
- 상품 조회
- 문서 검색
- 일정 조회

쓰기 도구
- 결제 실행
- 이메일 발송
- 데이터 수정
- 파일 삭제

쓰기 도구에는 승인, 감사 로그, 중복 실행 방지 장치를 추가해야 합니다.

4. 구조화된 출력 사용

다음 노드로 전달할 데이터는 자연어보다 JSON 또는 Pydantic 모델 형태가 안전합니다.

from pydantic import BaseModel


class RequestClassification(BaseModel):
    category: Literal[
        "inventory",
        "refund",
        "general"
    ]
    confidence: float

5. 타임아웃과 재시도 정책

모든 오류를 같은 방식으로 재시도하면 안 됩니다.

429 Rate Limit
→ 일정 시간 후 재시도

500 Server Error
→ 제한된 횟수만 재시도

401 Unauthorized
→ 재시도하지 않고 설정 오류 처리

잘못된 도구 인자
→ 모델에게 수정 요청

6. 실행 추적과 평가

최종 답변만 저장하지 말고 다음 정보도 기록해야 합니다.

  • 어떤 노드를 거쳤는가?

  • 어떤 도구를 호출했는가?

  • 도구 입력과 출력은 무엇인가?

  • 토큰과 비용은 얼마나 사용했는가?

  • 어느 단계에서 실패했는가?

  • 사용자가 결과를 승인했는가?

LangGraph는 LangSmith와 연동해 그래프 실행을 추적하고 디버깅할 수 있습니다. 체크포인트를 활용하면 과거 상태를 확인하거나 특정 지점에서 실행 경로를 다시 검토할 수도 있습니다.


LangGraph가 오히려 필요하지 않은 경우

LangGraph가 범용적이라고 해서 모든 프로젝트에 사용해야 하는 것은 아닙니다.

다음과 같은 경우에는 더 간단한 구현이 좋습니다.

단순 질의응답 챗봇

사용자 입력
→ LLM 호출
→ 답변

그래프 없이 일반적인 API 호출만으로 충분할 수 있습니다.

간단한 도구 호출 에이전트

몇 개의 함수만 호출하고 복잡한 분기가 없다면 OpenAI Agents SDK나 LangChain의 고수준 Agent API가 더 빠를 수 있습니다.

LangGraph 공식 문서도 고수준의 사전 구성된 에이전트가 필요하다면 LangChain Agent를 먼저 검토하고, 세밀한 오케스트레이션이 필요할 때 LangGraph를 사용하도록 안내합니다.

역할극 형태의 멀티 에이전트 실험

조사원, 분석가, 작가처럼 역할이 명확한 협업 구조를 빠르게 검증하려면 CrewAI가 더 직관적일 수 있습니다.


☑ 프레임워크 선택 기준 요약

AI 에이전트 프레임워크를 선택할 때는 다음과 같이 판단해 보세요.

빠르고 단순하게 OpenAI 기반 에이전트를 만들고 싶다
→ OpenAI Agents SDK

여러 에이전트를 역할 중심으로 협업시키고 싶다
→ CrewAI

Azure, .NET, Java 등 Microsoft 환경이 중요하다
→ Microsoft Agent Framework 또는 Semantic Kernel

문서 검색과 RAG가 프로젝트의 중심이다
→ LlamaIndex

상태, 조건 분기, 승인, 복구까지 세밀하게 통제해야 한다
→ LangGraph

특히 실제 운영 서비스에서는 “에이전트가 얼마나 똑똑한가”보다 다음 질문이 더 중요합니다.

에이전트가 어떤 상태에서, 어떤 조건에 따라, 어떤 도구를 실행했는지 설명할 수 있는가?

LangGraph는 이 실행 과정을 코드와 그래프 형태로 명확하게 표현할 수 있다는 점에서 범용적인 선택지가 됩니다.


지금 바로 적용해 보세요

처음부터 거대한 멀티 에이전트 시스템을 만들 필요는 없습니다.

우선 현재 개발 중인 업무를 다음 세 가지로 나눠보세요.

1. State
에이전트가 실행 중 기억해야 하는 정보는 무엇인가?

2. Node
각 단계에서 수행해야 하는 단일 작업은 무엇인가?

3. Edge
어떤 조건에서 다음 단계로 이동해야 하는가?

그다음 가장 작은 그래프부터 구현해 보세요.

START
→ agent
→ tool
→ agent
→ END

이 작은 구조에 상태 저장, 결과 검증, 사용자 승인, 실패 복구 노드를 하나씩 추가하다 보면 단순한 챗봇이 아니라 운영 가능한 AI 에이전트 시스템으로 확장할 수 있습니다.

👉 먼저 여러분의 서비스에서 LLM이 판단해야 하는 부분과, 코드가 반드시 통제해야 하는 부분을 분리해 보세요. 그 경계가 LangGraph 설계의 출발점입니다.