- 들어가며
- AI 에이전트(AI Agent)란?
- AI 에이전트의 핵심 기능 요소
- 단일 AI 에이전트의 실제 활용
- 단일 에이전트 시스템의 한계
- 왜 멀티 에이전트 시스템(Multi-Agent-System, MAS)이 필요한가
- 멀티 에이전트 시스템의 필요성
- 멀티 에이전트 시스템의 개념과 특징
- 멀티 에이전트 시스템의 장점
- 대표적인 멀티 에이전트 아키텍처
- Swarm 아키텍처란?
- Langgraph란?
- MoA(Mixture of Agents) 아키텍처 선택 배경
- MoA 아키텍처란?
- 예제 코드
- SWARM vs Langgraph
들어가며
AI 에이전트(AI Agent)란?
ol {margin-bottom:0in;margin-top:0in;}ul {margin-bottom:0in;margin-top:0in;}li {margin-top:.0in;margin-bottom:8pt;}ol.scriptor-listCounterResetlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6976 {counter-reset: section;}ol.scriptor-listCounterlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6976 {list-style-type:bullet;}li.listItemlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6976::before {counter-increment: section;content: none; display: inline-block;}
- “AI agent는 사용자나 다른 시스템을 대신하여 자율적으로 작업을 수행하는 시스템으로, 가용한 도구(tool)와 워크플로우를 설계해 목표를 달성한다.”
AI 에이전트의 핵심 기능 요소
|
기능
|
설명
|
|
인식
|
에이전트는 센서, API, 사용자 인터페이스, 외부 데이터 소스 등을 통해 환경 정보를 수집하고 해석합니다. 예: 챗봇은 사용자 메시지를 텍스트로 인식하고, 로봇은 카메라·라이다 등으로 주위를 감지.
|
|
추론 및 계획
|
수집된 정보를 바탕으로 분석하고, 목표에 맞는 행동 전략을 세웁니다. 필요할 경우 외부 도구를 호출하거나 하위 작업(subtask)으로 분할하기도 합니다.
|
|
행동
|
계획된 행동을 실제로 실행하여 환경을 변화시킵니다. 예: API 호출, 데이터베이스 갱신, 사용자 응답 생성 등.
|
|
학습 및 적응
|
과거 상호작용, 결과, 오류 등을 바탕으로 성능을 점진적으로 개선하고, 환경 변화에 적응합니다. 메모리나 피드백 루프(feedback loop)를 통해 경험을 저장하고 활용합니다.
|
단일 AI 에이전트의 실제 활용
범용 에이전트: 일상의 디지털 비서
전문화된 에이전트: 특정 업무의 전문가
단일 에이전트 시스템의 한계
|
한계
|
설명
|
|
확장성 부족
|
작업량이나 상태 공간이 커질수록 병목이 생기고 처리 능력 한계에 도달함
|
|
전문화 및 역할 분리 어려움
|
서로 다른 기능을 하나의 에이전트에 몰아넣으면 복잡성과 유지보수가 증가
|
|
단일 실패 지점
|
하나의 에이전트 오류가 전체 시스템의 기능 정지나 성능 저하로 이어질 수 있음
|
|
복잡한 상호작용 처리의 한계
|
멀티 사용자·외부 시스템 등 복잡한 상호작용을 하나의 에이전트가 감당하기 어려움
|
|
유연성·적응성 저하
|
변화에 대응하기 어렵고 수정이 전체 시스템에 영향을 미침
|
|
병렬 처리 활용 미흡
|
동시다발적 요청이나 작업을 병렬로 효율적으로 처리하기 어렵다
|
왜 멀티 에이전트 시스템(Multi-Agent-System, MAS)이 필요한가
멀티 에이전트 시스템의 필요성
멀티 에이전트 시스템의 개념과 특징
- 자율성 : 각 에이전트는 외부 명령 없이 스스로 판단하고 행동할 수 있습니다.
- 분산 협력 : 중앙 집중 제어 없이, 여러 에이전트가 서로 협력하거나 조정하면서 문제를 해결합니다.
- 상호작용 : 에이전트들은 메시지 전달, 정보 공유, 협상, 공동 계획 등 다양한 방식으로 상호작용합니다.
- 적응성 : 환경 변화나 조건 변경에 적응할 수 있고, 에이전트가 동적으로 재구성되기도 합니다.
멀티 에이전트 시스템의 장점
ol {margin-bottom:0in;margin-top:0in;}ul {margin-bottom:0in;margin-top:0in;}li {margin-top:.0in;margin-bottom:8pt;}ol.scriptor-listCounterResetlist!list-e0c11cbd-701c-411d-b168-260799df728f0 {counter-reset: section;}ol.scriptor-listCounterlist!list-e0c11cbd-701c-411d-b168-260799df728f0 {list-style-type:bullet;}li.listItemlist!list-e0c11cbd-701c-411d-b168-260799df728f0::before {counter-increment: section;content: none; display: inline-block;}
- 유연성 : 상태 변화나 요구 변경이 있을 때 에이전트를 추가·삭제하거나 조정할 수 있어 시스템 전체를 유연하게 관리할 수 있습니다.
- 확장성 : 여러 에이전트가 병렬로 운영되면 더 복잡한 문제나 더 많은 작업량을 처리할 수 있습니다.
- 도메인 전문화 : 각 에이전트가 특정 도메인 또는 역할에 특화될 수 있어, 전문화된 처리가 가능해집니다.
- 성능 향상 : 에이전트들이 자원을 공유하고, 중복 학습을 줄이며, 협업을 통해 더 나은 결과를 낼 수 있습니다.
- 견고성 / 내결함성 : 일부 에이전트가 실패하더라도 전체 시스템은 계속 작동할 수 있어 신뢰성이 높습니다.
- 현실 적합성 : 교통 제어, 공급망, 스마트시티, 로봇 군집 등처럼 분산되고 복합적인 현실 환경에 더욱 적절합니다.
- 가시성 및 최적화 : 각 에이전트의 동작을 개별적으로 모니터링할 수 있어, 병목이나 오류 지점을 찾고 최적화하기 용이하다는 이점도 최근 강조됩니다.
대표적인 멀티 에이전트 아키텍처
ol {margin-bottom:0in;margin-top:0in;}ul {margin-bottom:0in;margin-top:0in;}li {margin-top:.0in;margin-bottom:8pt;}ol.scriptor-listCounterResetlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6977 {counter-reset: section;}ol.scriptor-listCounterlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6977 {list-style-type:bullet;}li.listItemlist!list-ded41739-9ab3-45a3-b0ec-d5c34b6fc6977::before {counter-increment: section;content: none; display: inline-block;}
- Swarm: OpenAI가 개발한 Swarm은 멀티 에이전트 시스템의 핵심 개념인 루틴'과 핸드오프를 중심으로 구성되어 있습니다. 이를 통해 에이전트 간의 협업을 직관적으로 구현할 수 있습니다. Swarm은 실험적이고 학습 중심의 프레임워크로, 에이전트의 협업 패턴을 명확하게 이해하고자 하는 개발자에게 적합합니다
- LangGraph : LangChain에서 개발한 상태 기반의 오케스트레이션 프레임워크로, 복잡한 에이전트 워크플로우를 관리하고 확장하는 데 강점을 지니고 있습니다. LangGraph는 에이전트 간의 협업을 위한 고급 기능을 제공하며, 대규모 시스템 구축에 적합한 인프라를 갖추고 있습니다
Swarm 아키텍처란?
ol {margin-bottom:0in;margin-top:0in;}ul {margin-bottom:0in;margin-top:0in;}li {margin-top:.0in;margin-bottom:8pt;}ol.scriptor-listCounterResetlist!list-e0c11cbd-701c-411d-b168-260799df728f1 {counter-reset: section;}ol.scriptor-listCounterlist!list-e0c11cbd-701c-411d-b168-260799df728f1 {list-style-type:bullet;}li.listItemlist!list-e0c11cbd-701c-411d-b168-260799df728f1::before {counter-increment: section;content: none; display: inline-block;}
- 생산(프로덕션) 지향: 로깅·오류 처리·모니터링·보안 기능을 갖춤.
- 다양한 아키텍처 패턴: 계층형, 병렬, 혼합 등 여러 실행 패턴을 직접 제공.
- 멀티 모델·툴 통합: OpenAI, Anthropic 등 여러 모델과 툴/플러그인 연동 지원.
- 확장성/배포 옵션: 컨테이너·클라우드 배포, 동시성·로드밸런싱 고려된 설계.
Langgraph 란?
ol {margin-bottom:0in;margin-top:0in;}ul {margin-bottom:0in;margin-top:0in;}li {margin-top:.0in;margin-bottom:8pt;}ol.scriptor-listCounterResetlist!list-8be71cbf-d34e-4aaf-b911-b95c47621b4c2 {counter-reset: section;}ol.scriptor-listCounterlist!list-8be71cbf-d34e-4aaf-b911-b95c47621b4c2 {list-style-type:bullet;}li.listItemlist!list-8be71cbf-d34e-4aaf-b911-b95c47621b4c2::before {counter-increment: section;content: none; display: inline-block;}
- 그래프 기반 오케스트레이션: 노드(에이전트·툴)와 엣지(제어흐름)를 명확히 모델링.
- 상태(stateful) 설계: AgentState 등으로 명시적 상태 관리를 지원하며 장기 메모리 통합 가능.
- 디버깅·관찰성: LangGraph Studio(시각화/디버깅 도구), LangSmith(관찰성 통합) 등 개발자 경험(UX)에 집중.
- 오픈소스 + 플랫폼 모델: 라이브러리는 MIT 라이선스, 플랫폼(관리형)은 유료/프리미엄 옵션 존재.
MoA(Mixture of Agents) 아키텍처 선택 배경
- 두 프레임워크의 차이를 명확히 보여주기 위해서는 동일한 아키텍처 패턴을 적용해야 합니다
- MoA는 양쪽 모두에서 구현 가능하며, 각 프레임워크의 특성을 잘 드러낼 수 있는 구조입니다
- 전문화: 각 에이전트가 특정 도메인(리스크 계산, 포트폴리오 분석, 시장 모니터링)에 집중
- 병렬 처리: 여러 에이전트가 동시에 작업 수행
- 협업: 집계자가 모든 결과를 종합하여 최종 결론 도출
- 모듈화: 각 에이전트를 독립적으로 개선하거나 교체 가능
- MoA는 구조가 직관적이어서 멀티 에이전트 시스템을 처음 접하는 독자도 쉽게 이해할 수 있습니다
- "분업 → 집계 → 의사결정"이라는 단순하면서도 강력한 패턴을 보여줍니다
MoA 아키텍처란?

- 구성 : 여러 전문 에이전트(예: 위험 계산, 포트폴리오 분석, 시장 리스크 탐지)와 최종 집계 에이전트로 이루어진 구조
- 특징 : 각 에이전트가 독립적으로 작업한 뒤, 집계자가 모든 결과를 통합
- 장점 :
- 모듈화와 확장성이 뛰어남
- 각 에이전트의 전문성 극대화
- 최종 보고서를 종합적·체계적으로 제공
예제 코드
두 코드의 간단한 구성 차이
|
구분
|
Swarm
|
LangGraph
|
|
핵심 개념
|
개별 에이전트(Agents)들의 자율적 행동을 통해 집단 지능 Emergent
|
노드(Node) 기반 그래프 구조에서 언어 모델과 데이터 연결, 흐름 중심 처리
|
|
구조적 단위
|
에이전트(Agent)
|
노드(Node) / 엣지(Edge)
|
|
통신/상호작용 방식
|
에이전트 간 메시지, 신호, 환경 공유를 통한 간접적 상호작용
|
노드 간 그래프 연결을 통한 직접 데이터 흐름과 의존성 관리
|
|
데이터 처리 방식
|
분산 처리, 각 에이전트가 지역적 정보를 처리하고 공유
|
그래프 기반 처리, 노드 단위로 데이터와 연산 명시적 흐름 정의
|
|
제어 방식
|
탈중앙화, Emergent Behavior
|
중앙화/분산화 혼합 가능, 명시적 데이터 흐름 제어
|
|
목표 지향성
|
전체 시스템 목표는 Emergent, 각 에이전트는 개별 목표
|
전체 프로세스 목표를 그래프 구조로 명시, 노드 단위로 목표 수행
|
|
확장성
|
에이전트 수 증가 시 자연스럽게 확장 가능
|
그래프 노드/엣지 증가에 따라 확장 가능, 연결 구조 복잡도 증가 가능
|
|
대표적 활용 예시
|
로봇 군집 제어, 분산 의사결정, 시뮬레이션
|
LLM 기반 데이터 파이프라인, 지식 그래프, 멀티모달 처리
|
# .env
AGENT_MODEL_NAME=gpt-5-nano
AGENT_MAX_LOOPS=3
API_HOST=0.0.0.0
API_PORT=8000
LOG_LEVEL=info
OPENAI_API_KEY = ""
OLLAMA_BASE_URL = ""
OLLAMA_API_BASE = ${OLLAMA_BASE_URL}
ANTHROPIC_API_KEY=""
AGENT_MODEL_NAME=gpt-4o-mini
Swarm 예제 코드
# swarms_main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from swarms import Agent, MixtureOfAgents
import uvicorn
from typing import Optional
import time
from dotenv import load_dotenv
import os
load_dotenv()
MODEL_NAME = os.getenv("AGENT_MODEL_NAME", "gpt-4o-mini")
app = FastAPI(
title="Swarms 3-Layer MoA API",
description="REST API for a 3-layer Mixture-of-Agents system (gpt-5 + qwen3)",
version="0.0.1"
)
class SwarmRequest(BaseModel):
task: str
max_loops: Optional[int] = 1
class SwarmResponse(BaseModel):
success: bool
result: str
execution_time: Optional[float] = None
risk_metrics_agent = Agent(
agent_name="Risk-Metrics-Calculator",
agent_description="Calculates key risk metrics like VaR, Sharpe ratio, and volatility",
system_prompt="""You are a risk metrics specialist. Calculate and explain:
- Value at Risk (VaR)
- Sharpe ratio
- Volatility
- Maximum drawdown
- Beta coefficient
Provide clear, numerical results with brief explanations.""",
max_loops=1,
model_name=MODEL_NAME, # ollama/llama3.1
random_model_enabled=False,
dynamic_temperature_enabled=True,
output_type="str-all-except-first",
max_tokens=4096,
)
portfolio_risk_agent = Agent(
agent_name="Portfolio-Risk-Analyzer",
agent_description="Analyzes portfolio diversification and concentration risk",
system_prompt="""You are a portfolio risk analyst. Focus on:
- Portfolio diversification analysis
- Concentration risk assessment
- Correlation analysis
- Sector/asset allocation risk
- Liquidity risk evaluation
Provide actionable insights for risk reduction.""",
max_loops=1,
model_name=MODEL_NAME, # ollama/llama3.1
random_model_enabled=False,
dynamic_temperature_enabled=True,
output_type="str-all-except-first",
max_tokens=4096,
)
market_risk_agent = Agent(
agent_name="Market-Risk-Monitor",
agent_description="Monitors market conditions and identifies risk factors",
system_prompt="""You are a market risk monitor. Identify and assess:
- Market volatility trends
- Economic risk factors
- Geopolitical risks
- Interest rate risks
- Currency risks
Provide current risk alerts and trends.""",
max_loops=1,
model_name=MODEL_NAME, # ollama/llama3.1
random_model_enabled=False,
dynamic_temperature_enabled=True,
output_type="str-all-except-first",
max_tokens=4096,
)
aggregator = Agent(
agent_name="Strategic-Aggregator",
agent_description="Synthesizes analyses from all agents to provide strategic recommendations",
system_prompt="""You are a strategic decision aggregator responsible for:
1. Synthesizing specialist analyses
2. Identifying key insights
3. Evaluating trade-offs
4. Making recommendations
5. Providing action plans""",
max_loops=1,
# model_name=MODEL_NAME, # ollama/llama3.1
# random_model_enabled=False,
# dynamic_temperature_enabled=True,
output_type="str-all-except-first",
max_tokens=4096,
allow_fallback=False,
)
swarm = MixtureOfAgents(
agents=[
risk_metrics_agent,
portfolio_risk_agent,
market_risk_agent,
],
aggregator_agent=aggregator,
aggregator_system_prompt="""Synthesize the analyses from all specialists to provide:
1. Comprehensive situation analysis
2. Key opportunities and risks
3. Strategic recommendations
4. Implementation considerations
5. Success metrics""",
aggregator_model_name=MODEL_NAME,
layers=1,
max_loops=1,
output_type="final",
)
@app.get("/")
async def root():
return {"message": "3-Layer Swarm API is running!", "status": "healthy"}
@app.post("/swarm/run", response_model=SwarmResponse)
async def run_swarm(request: SwarmRequest):
try:
start_time = time.time()
result = swarm.run(request.task)
exec_time = time.time() - start_time
return SwarmResponse(
success=True,
result=str(result),
execution_time=exec_time
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Swarm execution failed: {str(e)}")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
# langgraph_main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
import time
from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_ollama import ChatOllama
from langchain.prompts import PromptTemplate, ChatPromptTemplate
import uvicorn
import os
from dotenv import load_dotenv
load_dotenv()
MODEL_NAME = os.getenv("AGENT_MODEL_NAME", "gemma3:27b")
app = FastAPI(
title="LangGraph 3-Layer MoA API",
description="REST API for a 3-layer Mixture-of-Agents system (LangGraph: gpt-5 + qwen3)",
version="0.0.1"
)
class LangGraphRequest(BaseModel):
task: str
max_loops: Optional[int] = 1
class LangGraphResponse(BaseModel):
success: bool
result: str
execution_time: Optional[float] = None
# ===== Graph State =====
class GraphState(TypedDict, total=False):
task: str
risk_metrics: str
portfolio_risk: str
market_risk: str
final: str
def get_agent(model_name: str, prompt: str, temperature: float = 0.7):
if "gpt" in model_name.lower():
model = ChatOpenAI(model=model_name, temperature=temperature)
else:
ollama_base_url = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
model = ChatOllama(
model=model_name,
temperature=temperature,
base_url=ollama_base_url
)
system_prompt = ChatPromptTemplate.from_messages([
("system", prompt),
("user", "{input}")
])
return system_prompt | model
risk_metrics_prompt = """You are a risk metrics specialist. Calculate and explain:
- Value at Risk (VaR)
- Sharpe ratio
- Volatility
- Maximum drawdown
- Beta coefficient
Provide clear, numerical results with brief explanations.
"""
portfolio_risk_prompt = """You are a portfolio risk analyst. Focus on:
- Portfolio diversification analysis
- Concentration risk assessment
- Correlation analysis
- Sector/asset allocation risk
- Liquidity risk evaluation
Provide actionable insights for risk reduction.
"""
market_risk_prompt = """You are a market risk monitor. Identify and assess:
- Market volatility trends
- Economic risk factors
- Geopolitical risks
- Interest rate risks
- Currency risks
Provide current risk alerts and trends.
"""
aggregator_prompt = """You are a strategic decision aggregator responsible for:
1. Synthesizing specialist analyses
2. Identifying key insights
3. Evaluating trade-offs
4. Making recommendations
5. Providing action plans
Combine the provided proposals into a single, coherent, prioritized report.
"""
# ===== Agents =====
risk_metrics_proposer = get_agent(MODEL_NAME, risk_metrics_prompt)
portfolio_risk_proposer = get_agent(MODEL_NAME, portfolio_risk_prompt)
market_risk_proposer = get_agent(MODEL_NAME, market_risk_prompt)
aggregator = get_agent(MODEL_NAME, aggregator_prompt, temperature=0.3)
# ===== Nodes =====
def risk_metrics_proposer_node(state: GraphState) -> GraphState:
task = state["task"]
resp = risk_metrics_proposer.invoke(task)
return {"risk_metrics": resp.content}
def portfolio_risk_proposer_node(state: GraphState) -> GraphState:
task = state["task"]
resp = portfolio_risk_proposer.invoke(task)
return {"portfolio_risk": resp.content}
def market_risk_proposer_node(state: GraphState) -> GraphState:
task = state["task"]
resp = market_risk_proposer.invoke(task)
return {"market_risk": resp.content}
def aggregator_node(state: GraphState) -> GraphState:
proposals = []
if "risk_metrics" in state:
proposals.append(("Risk Metrics Proposer", state["risk_metrics"]))
if "portfolio_risk" in state:
proposals.append(("Portfolio Risk Proposer", state["portfolio_risk"]))
if "market_risk" in state:
proposals.append(("Market Risk Proposer", state["market_risk"]))
joined = "\n\n---\n\n".join(f"{agent}:\n{content}" for agent, content in proposals)
resp = aggregator.invoke(joined)
return {**state, "final": resp.content}
# ===== Build Graph =====
def build_graph():
workflow = StateGraph(GraphState)
workflow.add_node("risk_metrics_state", risk_metrics_proposer_node)
workflow.add_node("portfolio_risk_state", portfolio_risk_proposer_node)
workflow.add_node("market_risk_state", market_risk_proposer_node)
workflow.add_node("aggregator", aggregator_node)
workflow.set_entry_point("risk_metrics_state")
workflow.set_entry_point("portfolio_risk_state")
workflow.set_entry_point("market_risk_state")
workflow.add_edge("risk_metrics_state", "aggregator")
workflow.add_edge("portfolio_risk_state", "aggregator")
workflow.add_edge("market_risk_state", "aggregator")
workflow.add_edge("aggregator", END)
return workflow.compile()
graph = build_graph()
# ===== API Endpoint =====
@app.post("/langgraph/run", response_model=LangGraphResponse)
async def run_langgraph(request: LangGraphRequest):
try:
print(f"Using MODEL_NAME: {MODEL_NAME}")
start_time = time.time()
result = graph.invoke({"task": request.task})
exec_time = time.time() - start_time
return LangGraphResponse(
success=True,
result=result["final"],
execution_time=exec_time
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"LangGraph execution failed: {str(e)}")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8001)
# multi_agent_test.ipynb
# conda create -n swarm_env python=3.11 -y
# conda activate swarm_env
# pip install swarms fastapi uvicorn requests python-dotenv langgraph langchain-openai langchain-ollama langchain
# python swarm_main.py
# python langgraph_main.py
#-------------------------------------------------------------------------
import requests
task = "Calculate VaR and Sharpe ratio for a portfolio with 15% annual return and 20% volatility"
response = requests.post(
"http://localhost:8000/swarm/run",
json={"task": task}
)
if response.status_code == 200:
print("Swarm Result:")
print(response.json())
else:
print("Swarm API Error:", response.text)
#------------------------------------------------------
response = requests.post(
"http://localhost:8001/langgraph/run",
json={"task": task}
)
if response.status_code == 200:
print("LangGraph Result:")
print(response.json())
else:
print("LangGraph API Error:", response.text)
SWARM vs Langgraph
|
구분
|
Swarm
|
LangGraph
|
|
핵심 개념
|
다양한 멀티에이전트 실행 패턴을 지원하는 프로덕션 지향 프레임워크
|
워크플로우를 그래프(노드·엣지) 로 모델링하는 상태 기반 오케스트레이션
|
|
강점
|
확장성, 배포·모니터링, 보안 등 엔터프라이즈 환경 최적화
|
시각화·디버깅 도구(LangGraph Studio), 상태 관리, LangChain 생태계 연동
|
|
단점
|
초기 설정·운영이 복잡, 학습 곡선이 있음
|
그래프 개념 학습 필요, 엔터프라이즈급 보안·배포 기능은 상대적으로 약함
|
|
사용자 대상
|
대규모 운영·프로덕션 환경이 필요한 기업 팀
|
대화형 서비스·실험적 프로젝트·LangChain 기반 앱 개발자
|
- Swarm은 엔터프라이즈 환경에서의 안정성, 확장성, 배포·모니터링 지원에 강점을 가진 반면, 초기 설정과 운영이 다소 복잡합니다.
- LangGraph는 시각화, 디버깅, 상태 관리가 용이하며 실험적 프로젝트나 LangChain 기반 앱 개발에 최적화되어 있습니다.