CrewAI와 LangGraph로 역할 기반 멀티에이전트 파이프라인 구축하기
단일 AI 에이전트로 복잡한 문제를 해결하려 한 적이 있나요? 컨텍스트 창이 금방 가득 차고, 연구자이면서 동시에 코더이고 리뷰어까지 되어야 하는 에이전트는 결국 어느 하나도 제대로 해내지 못합니다. 이 문제를 해결하는 것이 멀티에이전트 협업 패턴입니다. 서로 다른 역할을 가진 여러 에이전트가 팀을 이뤄 복잡한 파이프라인을 처리하는 아키텍처로, 현재 실무 AI 시스템 설계의 핵심 패러다임으로 자리 잡았습니다.
이 글은 Python 기본 문법을 알고 LLM API를 한 번이라도 사용해본 개발자를 대상으로 합니다. 수많은 에이전트 프레임워크 중 CrewAI와 LangGraph를 선택한 이유는 명확합니다. CrewAI는 역할 기반 팀 구성에 특화되어 가장 직관적인 개발 경험을 제공하고, LangGraph는 복잡한 분기·루프·상태 관리가 필요한 프로덕션 워크플로우에서 가장 유연한 제어를 제공합니다. 두 프레임워크는 서로 다른 강점을 가지며, 함께 사용할 때 더 강력해집니다.
이 글을 마치면 LangGraph로 연구 자동화 파이프라인을, CrewAI로 계층형 코드 리뷰 팀을, 그리고 두 프레임워크를 조합한 크로스 프레임워크 파이프라인을 직접 실행할 수 있습니다. 각 프레임워크의 핵심 개념을 이해하고, 여러분의 프로젝트에 어떤 도구가 더 적합한지 판단하는 기준을 얻게 됩니다.
핵심 개념
멀티에이전트 협업이란 무엇인가
멀티에이전트 협업 패턴(Multi-Agent Collaboration Pattern)은 단일 LLM 대신, 서로 다른 역할을 가진 복수의 AI 에이전트가 오케스트레이션을 통해 복잡한 작업을 분담·협력 수행하는 아키텍처입니다. 세 가지 핵심 개념을 기반으로 동작합니다.
| 개념 | 설명 |
|---|---|
| 역할 분리 (Role Specialization) | 연구자, 코더, 리뷰어, 매니저 등 특화된 역할을 에이전트마다 부여합니다. 전문화된 에이전트는 범용 에이전트보다 도메인 작업에서 더 정확한 결과를 냅니다 |
| 오케스트레이터-서브에이전트 패턴 | 중앙 오케스트레이터가 전체 흐름을 관리하고 맥락(context)을 보유하며, 서브에이전트는 위임받은 세부 작업을 수행합니다 |
| 상태 관리 방식 | LangGraph는 공유 상태(Shared State)를 그래프 노드·엣지로 명시적으로 관리하고, CrewAI는 태스크 출력을 다음 에이전트에게 전달하는 방식으로 협업합니다 |
오케스트레이터-서브에이전트 패턴의 핵심 원칙: 서브에이전트는 상태가 없어야(stateless) 합니다. 맥락은 오케스트레이터가 책임집니다. 이 설계가 컨텍스트 오염을 막고, 각 에이전트가 자신의 역할에 집중할 수 있게 해주는 핵심입니다.
CrewAI: 역할 기반 에이전트 팀
CrewAI는 Agent → Task → Crew라는 직관적인 객체 구조를 제공합니다. 비-ML 엔지니어도 이해하기 쉬운 구조이며, 공식적으로 두 가지 프로세스 타입을 지원합니다.
| 프로세스 타입 | 동작 방식 | 적합한 사용 사례 |
|---|---|---|
| Sequential | 에이전트가 순서대로 작업을 처리합니다. 이전 에이전트의 출력이 다음 에이전트의 입력이 됩니다 | 단계가 명확한 파이프라인 (연구 → 작성 → 편집) |
| Hierarchical | 매니저 에이전트(LLM)가 작업을 각 에이전트에 위임하고 결과를 검증합니다 | 복잡한 코드 리뷰, 승인 프로세스 |
각 Agent는 role(역할), goal(목표), backstory(배경 지식)를 통해 행동 방식이 결정됩니다. Task는 각 에이전트가 수행할 구체적인 작업을 정의하며, Crew가 이를 묶어 실행합니다.
LangGraph: 그래프 기반 상태머신
LangGraph는 에이전트 워크플로우를 방향성 그래프(Directed Graph)로 모델링합니다. 노드(에이전트 스텝), 엣지(전환 조건), 상태 스키마의 세 요소로 구성됩니다.
LangGraph의 핵심 차별점은 사이클(루프) 지원입니다. 단순 순차 실행만 가능한 DAG(방향성 비순환 그래프)와 달리, LangGraph는 조건에 따라 이전 노드로 돌아가는 루프를 표현할 수 있습니다. "결과가 만족스럽지 않으면 재시도"하는 패턴이나 반복적 개선이 필요한 워크플로우에서 이 특성이 빛을 발합니다. 코드에서는 다음 네 가지 핵심 API를 사용합니다.
| API | 역할 |
|---|---|
StateGraph(스키마) |
상태 스키마를 기반으로 그래프를 초기화합니다 |
add_node(이름, 함수) |
에이전트 함수를 그래프 노드로 등록합니다 |
add_edge(출발, 도착) |
노드 간 실행 순서(엣지)를 정의합니다 |
compile() |
그래프를 실행 가능한 형태로 컴파일합니다 |
체크포인팅(Checkpointing): 긴 작업 중 특정 시점의 상태를 저장해두는 기능입니다. 오류 발생 시 처음부터 재실행하지 않고 저장된 지점부터 재개할 수 있어, 장시간 실행되는 파이프라인의 안정성을 크게 높여줍니다.
Human-in-the-Loop:
workflow.compile(interrupt_before=["노드명"])설정으로 특정 노드 실행 전 인간의 승인을 대기하는 워크플로우를 구성할 수 있습니다. 자동화와 인간 검토를 유연하게 결합하는 패턴입니다.
이제 이 개념들을 실제 코드로 구현해보겠습니다.
실전 적용
예시 1: LangGraph로 연구 자동화 파이프라인 구성하기
세 개의 전문 에이전트(Research → Analysis → Report)를 그래프 노드로 연결해 자동으로 임원 보고서를 생성하는 파이프라인입니다.
import os
from langgraph.graph import StateGraph, END
from typing import TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_community.tools import TavilySearchResults
# 환경 변수 설정 (실행 전 아래 키를 발급받아 설정하세요)
# export ANTHROPIC_API_KEY="your-anthropic-api-key"
# export TAVILY_API_KEY="your-tavily-api-key"
# 1. 공유 상태 스키마 정의 — 에이전트 간 데이터를 타입 안전하게 전달합니다
class ResearchState(TypedDict):
query: str
raw_data: str
analysis: str
report: str
# 2. LLM 및 도구 초기화
llm = ChatAnthropic(model="claude-sonnet-4-6")
search_tool = TavilySearchResults(max_results=5)
def research_agent(state: ResearchState) -> ResearchState:
"""웹 검색을 수행해 원시 데이터를 수집합니다."""
try:
results = search_tool.invoke(state["query"])
raw_data = "\n".join([r["content"] for r in results])
return {"raw_data": raw_data}
except Exception as e:
# 프로덕션에서는 재시도 로직이나 폴백 데이터 소스를 추가하는 것을 권장합니다
return {"raw_data": f"검색 오류: {str(e)}"}
def analysis_agent(state: ResearchState) -> ResearchState:
"""수집된 데이터를 분석해 인사이트를 추출합니다."""
prompt = f"다음 데이터를 분석하고 핵심 인사이트를 추출하세요:\n{state['raw_data']}"
analysis = llm.invoke(prompt).content
return {"analysis": analysis}
def report_agent(state: ResearchState) -> ResearchState:
"""분석 결과를 기반으로 임원 보고서를 생성합니다."""
prompt = f"다음 분석을 바탕으로 임원용 보고서를 작성하세요:\n{state['analysis']}"
report = llm.invoke(prompt).content
return {"report": report}
# 3. 그래프 구성 및 컴파일
workflow = StateGraph(ResearchState)
workflow.add_node("research", research_agent)
workflow.add_node("analyze", analysis_agent)
workflow.add_node("report", report_agent)
workflow.set_entry_point("research")
workflow.add_edge("research", "analyze")
workflow.add_edge("analyze", "report")
workflow.add_edge("report", END)
graph = workflow.compile()
# 4. 실행
result = graph.invoke({"query": "2025년 멀티에이전트 AI 트렌드"})
print(result["report"])| 코드 구성 요소 | 역할 |
|---|---|
ResearchState |
에이전트 간 공유되는 상태 스키마. 중간 결과물을 타입 안전하게 전달합니다 |
add_node |
각 에이전트 함수를 그래프 노드로 등록합니다 |
add_edge |
노드 간 실행 순서(엣지)를 정의합니다 |
compile() |
그래프를 실행 가능한 형태로 컴파일합니다 |
Human-in-the-Loop를 추가하려면 workflow.compile(interrupt_before=["report"]) 처럼 컴파일 시 interrupt_before를 지정하면, report 노드 실행 전에 인간 검토 단계를 삽입할 수 있습니다.
이번에는 CrewAI로 팀 구조를 구성하는 방식을 살펴보겠습니다.
예시 2: CrewAI로 계층형 코드 리뷰 팀 구성하기
Coder → Reviewer → Tester로 이어지는 계층형 코드 리뷰 자동화 파이프라인입니다. 매니저 LLM이 작업을 각 에이전트에 위임하고 결과를 검증합니다.
import os
from crewai import Agent, Crew, Process, Task
from crewai_tools import CodeInterpreterTool
from langchain_anthropic import ChatAnthropic
# 환경 변수 설정
# export ANTHROPIC_API_KEY="your-anthropic-api-key"
code_interpreter = CodeInterpreterTool()
# 1. 에이전트 정의 — 각 역할에 특화된 목표와 배경 지식을 부여합니다
coder = Agent(
role="시니어 Python 개발자",
goal="명확하고 효율적인 Python 코드를 작성합니다",
backstory="10년 경력의 백엔드 엔지니어로, PEP 8과 SOLID 원칙을 철저히 따릅니다",
tools=[code_interpreter],
verbose=True,
)
reviewer = Agent(
role="코드 리뷰어",
goal="코드의 품질, 보안 취약점, 성능 문제를 검토합니다",
backstory="보안 전문가 출신으로 OWASP Top 10을 숙지하고 있습니다",
verbose=True,
)
tester = Agent(
role="QA 엔지니어",
goal="엣지 케이스를 포함한 포괄적인 테스트 케이스를 작성합니다",
backstory="TDD 방법론을 따르며 pytest를 주로 사용합니다",
tools=[code_interpreter],
verbose=True,
)
# 2. 태스크 정의 — 각 에이전트가 수행할 구체적인 작업을 명시합니다
code_task = Task(
description="사용자 인증 모듈을 Python으로 구현하세요. JWT 토큰 발급 및 검증 기능을 포함합니다.",
expected_output="완성된 Python 코드와 간략한 구현 설명",
agent=coder,
)
review_task = Task(
description="작성된 코드를 검토하고 보안 취약점, 코드 스타일, 성능 개선점을 리포트하세요.",
expected_output="검토 보고서 (취약점 목록, 권고 사항 포함)",
agent=reviewer,
context=[code_task], # 이전 태스크 결과를 컨텍스트로 활용합니다
)
test_task = Task(
description="인증 모듈에 대한 pytest 테스트 케이스를 작성하세요. 정상 케이스와 엣지 케이스를 모두 포함합니다.",
expected_output="pytest 테스트 파일 (커버리지 80% 이상 목표)",
agent=tester,
context=[code_task, review_task],
)
# 3. 계층형 Crew 구성
# manager_llm은 문자열이 아닌 LLM 객체를 전달해야 합니다
manager_llm = ChatAnthropic(model="claude-opus-4-6")
crew = Crew(
agents=[coder, reviewer, tester],
tasks=[code_task, review_task, test_task],
process=Process.hierarchical,
manager_llm=manager_llm, # 복잡한 추론이 필요한 오케스트레이터에는 강력한 모델을 사용하는 것을 권장합니다
verbose=True,
)
result = crew.kickoff()
print(result.raw)| 코드 구성 요소 | 역할 |
|---|---|
backstory |
에이전트의 전문성과 행동 방식을 결정하는 핵심 프롬프트입니다 |
context=[code_task] |
이전 태스크 결과를 다음 에이전트에게 전달합니다 |
Process.hierarchical |
매니저 LLM이 에이전트에게 작업을 위임하는 계층형 구조를 활성화합니다 |
manager_llm |
전체 조율을 담당하는 오케스트레이터 모델입니다. ChatAnthropic 등 LLM 객체 형태로 전달해야 합니다 |
두 프레임워크를 각각 살펴봤으니, 이번에는 함께 결합하는 방식을 알아보겠습니다.
예시 3: 크로스 프레임워크 통합 — LangGraph가 CrewAI를 오케스트레이션하기
LangGraph 오케스트레이터가 CrewAI 크루에 작업을 위임하는 크로스 프레임워크 패턴입니다. CrewAI의 직관적인 역할 기반 팀 구성과 LangGraph의 유연한 상태 관리를 함께 활용합니다.
import os
from langgraph.graph import StateGraph, END
from typing import TypedDict
from crewai import Agent, Crew, Process, Task
from langchain_anthropic import ChatAnthropic
# 환경 변수 설정
# export ANTHROPIC_API_KEY="your-anthropic-api-key"
# 전체 파이프라인 상태 스키마
class PipelineState(TypedDict):
topic: str
research_result: str
final_code: str
# --- 리서치 크루: 에이전트 및 태스크 정의 ---
researcher = Agent(
role="리서치 전문가",
goal="주어진 주제에 대한 심층 기술 조사를 수행합니다",
backstory="기술 트렌드를 분석하는 5년 경력의 리서처입니다",
verbose=True,
)
analyst = Agent(
role="데이터 분석가",
goal="수집된 정보를 구조화하고 핵심 인사이트를 추출합니다",
backstory="데이터 기반 의사결정을 지원하는 분석 전문가입니다",
verbose=True,
)
research_task = Task(
description="{topic}에 대한 최신 기술 동향과 구현 사례를 조사하세요.",
expected_output="기술 동향 요약 보고서",
agent=researcher,
)
analysis_task = Task(
description="조사된 내용을 바탕으로 구현에 필요한 핵심 기술 스택을 분석하세요.",
expected_output="기술 스택 분석 및 권고안",
agent=analyst,
context=[research_task],
)
# --- 코딩 크루: 에이전트 및 태스크 정의 ---
coder = Agent(
role="시니어 Python 개발자",
goal="명확하고 효율적인 Python 코드를 작성합니다",
backstory="10년 경력의 백엔드 엔지니어로, SOLID 원칙을 철저히 따릅니다",
verbose=True,
)
reviewer = Agent(
role="코드 리뷰어",
goal="코드 품질, 보안 취약점, 성능 문제를 검토합니다",
backstory="보안 전문가 출신으로 OWASP Top 10을 숙지하고 있습니다",
verbose=True,
)
tester = Agent(
role="QA 엔지니어",
goal="포괄적인 테스트 케이스를 작성합니다",
backstory="TDD 방법론을 따르며 pytest를 주로 사용합니다",
verbose=True,
)
code_task = Task(
description="{research} 결과를 바탕으로 데이터 파이프라인 모듈을 Python으로 구현하세요.",
expected_output="완성된 Python 구현 코드",
agent=coder,
)
review_task = Task(
description="작성된 코드를 검토하고 보안 취약점과 성능 개선점을 리포트하세요.",
expected_output="코드 리뷰 보고서",
agent=reviewer,
context=[code_task],
)
test_task = Task(
description="구현된 모듈에 대한 pytest 테스트 케이스를 작성하세요.",
expected_output="pytest 테스트 파일",
agent=tester,
context=[code_task, review_task],
)
manager_llm = ChatAnthropic(model="claude-opus-4-6")
# CrewAI 크루를 LangGraph 노드로 래핑합니다
def run_research_crew(state: PipelineState) -> PipelineState:
"""LangGraph 노드: CrewAI 리서치 크루를 실행합니다."""
research_crew = Crew(
agents=[researcher, analyst],
tasks=[research_task, analysis_task],
process=Process.sequential,
)
result = research_crew.kickoff(inputs={"topic": state["topic"]})
return {"research_result": result.raw}
def run_coding_crew(state: PipelineState) -> PipelineState:
"""LangGraph 노드: CrewAI 코딩 크루를 실행합니다."""
coding_crew = Crew(
agents=[coder, reviewer, tester],
tasks=[code_task, review_task, test_task],
process=Process.hierarchical,
manager_llm=manager_llm,
)
result = coding_crew.kickoff(inputs={"research": state["research_result"]})
return {"final_code": result.raw}
# LangGraph로 두 크루를 조율합니다
workflow = StateGraph(PipelineState)
workflow.add_node("research_crew", run_research_crew)
workflow.add_node("coding_crew", run_coding_crew)
workflow.set_entry_point("research_crew")
workflow.add_edge("research_crew", "coding_crew")
workflow.add_edge("coding_crew", END)
pipeline = workflow.compile()
result = pipeline.invoke({"topic": "실시간 데이터 스트리밍 아키텍처"})
print(result["final_code"])| 코드 구성 요소 | 역할 |
|---|---|
PipelineState |
LangGraph와 CrewAI 크루 사이를 연결하는 공유 상태 스키마입니다 |
run_research_crew / run_coding_crew |
CrewAI Crew 인스턴스를 LangGraph 노드 함수로 래핑합니다 |
kickoff(inputs=...) |
Crew 실행 시 동적 입력을 주입합니다. Task description의 {변수명} 템플릿에 매핑됩니다 |
manager_llm |
ChatAnthropic 등 LLM 객체 형태로 전달해야 합니다. 문자열 모델명은 지원하지 않습니다 |
장단점 분석
프레임워크 비교 매트릭스
두 프레임워크를 주요 기준으로 비교하면 다음과 같습니다.
| 비교 항목 | CrewAI | LangGraph |
|---|---|---|
| 초기 설정 | Agent·Task·Crew 구조로 빠르게 시작 가능 | 상태 스키마·노드·엣지·컴파일을 모두 정의해야 하므로 초기 비용이 있음 |
| 직관성 | 비-ML 엔지니어도 이해하기 쉬운 객체 구조 | 그래프 시각화로 흐름 파악 가능하지만 학습 곡선이 있음 |
| 조건 분기 & 루프 | 제한적. 단순 순차·계층형에 최적화 | 강점. 복잡한 조건 분기와 사이클 루프를 그래프로 자연스럽게 표현 |
| 상태 관리 | 태스크 출력 전달 방식 (암묵적) | 타입 안전한 공유 상태 스키마 (명시적) |
| 체크포인팅 | 제한적 | 내장 지원. 중단 후 재개·롤백 가능 |
| Human-in-the-Loop | 제한적 | interrupt_before로 1급 지원 |
| 디버깅 | Task 내 일반 로그가 잘 동작하지 않아 어려움 | LangSmith 통합으로 상세 추적 가능 |
| 적합한 케이스 | 빠른 프로토타입, 역할 기반 팀, 명확한 순차 워크플로우 | 복잡한 분기, 루프, 장시간 실행, 프로덕션 안정성 |
단점 및 주의사항
두 프레임워크 모두에 해당하는 공통 주의사항입니다.
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 비용 증가 | 여러 에이전트가 각각 LLM API를 호출하므로 토큰 비용이 빠르게 증가합니다 | 서브에이전트에는 경량 모델(Claude Haiku 등)을, 오케스트레이터에는 강력한 모델을 배치하는 것을 권장합니다 |
| 컨텍스트 오염 | 서브에이전트 결과가 쌓이면 메인 에이전트의 컨텍스트가 과부하됩니다 | 서브에이전트를 stateless로 설계하고 요약본만 전달하는 것이 좋습니다 |
| 관찰 가능성 | 어떤 에이전트가 무엇을 했는지 추적하기 어렵습니다 | LangSmith 또는 CrewAI 내장 로깅을 처음부터 연동하는 것을 권장합니다 |
| 프롬프트 인젝션 | 에이전트가 다른 에이전트의 출력을 입력으로 받을 때, 악의적 내용이 다음 에이전트 행동을 조작할 수 있습니다 | 에이전트 간 데이터 전달 시 입력 검증과 샌드박스 환경을 적용하는 것을 권장합니다 |
프롬프트 인젝션(Prompt Injection): 멀티에이전트 파이프라인에서 특히 주의가 필요한 보안 위협입니다. 한 에이전트의 출력이 다음 에이전트의 프롬프트로 직접 삽입될 때, 외부 데이터에 포함된 악의적 지시문이 파이프라인 전체 동작을 변경할 수 있습니다.
실무에서 가장 흔한 실수
- 역할을 너무 광범위하게 정의하는 것: "모든 것을 할 수 있는" 에이전트는 단일 에이전트와 다를 바 없습니다. 각 에이전트의 역할을 좁고 명확하게 정의할수록 성능과 결과의 일관성이 높아집니다.
- 오케스트레이터에 경량 모델을 사용하는 것: 서브에이전트 조율과 작업 분배를 담당하는 오케스트레이터는 복잡한 추론을 수행합니다. 비용을 절감하려다 매니저 LLM에 약한 모델을 사용하면 전체 파이프라인 품질이 저하됩니다. 오케스트레이터에는 강력한 모델, 서브에이전트에는 경량 모델을 배치하는 전략이 효과적입니다.
- 관찰 가능성을 초기 설계에서 빠뜨리는 것: 멀티에이전트 시스템은 어떤 에이전트가 무엇을 했는지 추적하기 어렵습니다. LangSmith나 CrewAI 내장 로깅을 처음부터 연동하지 않으면, 오류 발생 시 원인 파악에 큰 시간이 소요될 수 있습니다. 관찰 가능성은 선택이 아닌 필수입니다.
마치며
빠른 프로토타입과 직관적인 역할 기반 팀이 필요하다면 CrewAI가, 복잡한 분기 로직·루프·프로덕션 수준의 상태 관리가 필요하다면 LangGraph가 더 적합한 선택입니다. 그리고 두 프레임워크는 상호 배타적이지 않습니다. 예시 3에서 보았듯, 함께 사용할 때 각 프레임워크의 약점을 보완하고 강점을 극대화할 수 있습니다.
지금 바로 시작해볼 수 있는 3단계:
- CrewAI로 간단한 순차 파이프라인을 먼저 경험해보세요.
pip install crewai로 설치 후, Researcher → Writer → Editor 세 에이전트를Process.sequential로 연결한 블로그 초안 생성기를 만들어보는 것을 권장합니다. CrewAI 공식 Quickstart가 좋은 시작점입니다. - LangGraph의 공식 예제로 상태머신 개념에 익숙해져 보세요.
pip install langgraph langchain-anthropic로 설치 후, LangGraph 공식 튜토리얼의 simple graph 예제부터 시작해 상태 스키마와 노드·엣지 구조를 직접 실행하며 익힐 수 있습니다. - LangSmith를 연동해 에이전트 실행 흐름을 시각화해보세요.
LANGCHAIN_TRACING_V2=true환경 변수를 설정하는 것만으로 LangSmith 대시보드에서 각 에이전트 호출, 토큰 사용량, 실행 시간을 한눈에 파악할 수 있습니다. 관찰 가능성을 먼저 확보하고 복잡도를 높여가는 방식을 권장합니다.
멀티에이전트 파이프라인은 처음 세팅에서 어렵게 느껴질 수 있습니다. 하지만 각 에이전트가 자신의 역할에 집중하며 협력하는 시스템을 처음으로 동작시키는 순간, 단일 에이전트로는 불가능했던 가능성이 열립니다.
다음 글: MCP(Model Context Protocol)와 A2A(Agent-to-Agent Protocol)의 실전 통합 — 외부 도구와 이기종 에이전트를 연결하는 표준 프로토콜 구현 가이드
참고 자료
- LangGraph vs CrewAI vs AutoGen: The Complete Multi-Agent AI Orchestration Guide for 2026 | DEV Community
- Best Multi-Agent Frameworks in 2026: LangGraph, CrewAI | GuruSup
- CrewAI vs LangGraph vs AutoGen: Choosing the Right Multi-Agent AI Framework | DataCamp
- LangGraph vs CrewAI: Let's Learn About the Differences | ZenML Blog
- A Coding Implementation to Advanced LangGraph Multi-Agent Research Pipeline | MarkTechPost
- A Coding Guide to Build a Hierarchical Supervisor Agent Framework with CrewAI and Google Gemini | MarkTechPost
- CrewAI: A Practical Guide to Role-Based Agent Orchestration | DigitalOcean
- Build multi-agent systems with LangGraph and Amazon Bedrock | AWS Blog
- langgraph-supervisor | PyPI
- Hierarchical Process | CrewAI 공식 문서
- Single-Agent vs. Multi-Agent Code Review: Why One AI Isn't Enough | Qodo