본문 바로가기
AI(Artificial Intelligence)

"에이전트는 1명일 필요가 없다" : CrewAI의 본질과 멀티에이전트의 딜레마

by forward error correction Circle 2026. 7. 14.
반응형

1. CrewAI 기술이란?

왜 필요한가 — 기존 방식의 한계

2023~2024년 LLM 에이전트를 처음 만들 때 대부분의 팀은 "하나의 큰 에이전트 + 30개의 도구" 패턴으로 시작했습니다. 빠르게 동작은 했지만 운영을 시작하자마자 세 가지 벽에 부딪혔습니다.

첫째, 도구 선택 오류가 폭증합니다. 도구 30개의 설명을 한 번에 컨텍스트에 넣으면 LLM은 비슷한 도구들 사이에서 자주 잘못된 선택을 합니다. 둘째, 시스템 프롬프트가 비대해집니다. "검색 시에는 이렇게, 이메일 작성 시에는 저렇게, 코드 리뷰는 또 이렇게…" 모든 페르소나가 한 프롬프트에 섞이며 어느 한 영역의 품질이 떨어지기 시작합니다. 셋째, 책임 분리가 안 됩니다. 출력이 잘못 나왔을 때 어느 도구·어느 단계·어느 역할에서 망가졌는지 추적하기가 어렵습니다.

해법은 인간 조직의 협업 구조를 흉내내는 것입니다. 리서처·작가·편집자 세 사람이 협업하듯, 각각의 역할을 가진 작은 에이전트들에게 일을 나눠 주고, 그 사이에 매니저를 둘 수도 있습니다. CrewAI는 이 발상을 가장 단순한 데코레이터 한 줄과 YAML로 표현한 프레임워크입니다.

기술 정의

CrewAI는 다음 다섯 요소를 한 패키지로 묶은 오픈소스 멀티에이전트 오케스트레이션 프레임워크입니다(MIT 라이선스, Python 3.10+).

  • Agent — role / goal / backstory를 가진 LLM 페르소나
  • Task — description / expected_output / agent를 가진 일감 단위
  • Crew — 에이전트와 태스크를 묶는 팀, Process를 지정
  • Process — Sequential(순차) 또는 Hierarchical(매니저 LLM이 위임)
  • Tools / Memory / Knowledge — 도구 호출, 단·장기 메모리, RAG용 지식 소스

LangChain·LangGraph가 "낮은 추상화로 무엇이든 만들 수 있게" 하는 프레임워크라면, CrewAI는 "협업이라는 메타포를 정면에 세워 빠르게 돌리게" 하는 프레임워크입니다.

2. CrewAI 기술 특징


특징 설명
역할 기반 페르소나 role / goal / backstory 세 필드만으로 인간이 읽기 쉬운 에이전트를 정의. 시스템 프롬프트의 분할 통치를 강제합니다.
두 가지 프로세스 Sequential(태스크가 줄을 서서 다음 에이전트에게 전달)과 Hierarchical(Manager LLM이 일을 쪼개 위임) 중 1줄로 선택.
YAML 우선 구성 agents.yaml / tasks.yaml로 페르소나·일감을 분리. 비개발자도 프롬프트를 수정할 수 있고 코드 리뷰가 쉬워집니다.
도구 위임 에이전트가 다른 에이전트에게 자연어로 질문하거나 일을 넘기는 기능(allow_delegation=True). 인간 팀의 "이건 네가 더 잘 알 것 같아"를 흉내냅니다.
다층 메모리 Short-term(현재 실행), Long-term(SQLite 영속), Entity(개체 사실), Contextual(요약) 4계층을 자동 관리합니다.
모델 비종속 LiteLLM 위에서 동작 — OpenAI / Anthropic / Bedrock / Vertex / Groq / Ollama / vLLM 등 100여 개 모델을 문자열만 바꿔 사용.
구조화 출력 output_pydantic / output_json으로 Task 결과를 강제 검증. 다음 에이전트가 받는 입력의 일관성을 확보합니다.
CrewAI Flows 2024 후반 도입된 이벤트 기반 워크플로우 — Crew는 협업, Flow는 결정론적 분기·루프·병렬을 담당하는 보완재입니다.
관측성 통합 AgentOps / Langfuse / OpenLIT / MLflow Tracing과 즉시 결합. 에이전트별 토큰·도구 호출 횟수를 추적합니다.

3. CrewAI 기술 동작방식

구성 요소

  • Agent  role / goal / backstory + LLM 모델 + tools 리스트 + max_iter / max_rpm 등 가드레일.
  • Task — 자연어 설명, 예상 출력 형태, 담당 에이전트, 의존 태스크(context=[...]), 출력 파일 경로(선택).
  • Crew — 에이전트·태스크·process·memory·knowledge_sources를 모은 실행 단위. crew.kickoff(inputs={...})로 실행됩니다.
  • Process  Process.sequential(기본) 또는 Process.hierarchical(manager_llm 필요).
  • Tool  @tool 데코레이터 또는 BaseTool 상속. 내장 도구 60+종(검색·파일·코드실행·RAG·MCP).
  • Memory — Short / Long / Entity / Contextual 4종. 기본 SQLite, 임베딩은 OpenAI / Cohere / Ollama 중 선택.
  • Knowledge Sources — PDF·CSV·웹·텍스트를 임베딩해 에이전트의 사전지식으로 주입.

데이터 흐름

  1. Crew.kickoff(inputs) 호출 시 inputs가 모든 태스크의 description / expected_output 자리에 변수 치환됩니다.
  2. Sequential: 태스크 1 → 결과가 태스크 2의 context로 자동 주입 → 태스크 3 …
  3. Hierarchical: 사용자는 매니저 LLM에게 목표만 던지고, 매니저가 어떤 에이전트에게 어떤 순서로 위임할지를 LLM 추론으로 결정합니다.
  4. 각 에이전트는 ReAct 루프로 도구 호출과 추론을 반복(max_iter까지).
  5. Memory가 켜져 있으면 매 턴 임베딩이 저장되고, 다음 태스크 실행 시 contextual memory가 자동 검색·주입됩니다.
  6. 최종 산출물은 CrewOutput 객체 — raw / pydantic / json + per-task 출력 + token_usage가 모두 들어 있습니다.

4. CrewAI 기술 구성 및 흐름도

단계별 구조도 — Sequential

단계별 구조도 — Hierarchical

실제 처리 흐름 — "신규 SaaS 시장조사 보고서" 시나리오

  1. 사용자: "한국 중소기업 회계 SaaS 시장 조사 — 2026년 진입 전략."
  2. Researcher 에이전트가 SerperDevTool로 시장 규모·경쟁사·가격대를 검색.
  3. Analyst 에이전트가 검색 결과를 받아 SWOT 표를 생성, 출력은 SwotMatrix Pydantic 모델로 강제.
  4. Writer 에이전트가 SWOT + 원본 리서치를 결합해 임원용 1페이지 요약을 작성.
  5. Editor 에이전트가 사실 확인이 필요한 항목을 발견하면 delegation으로 Researcher에게 재질문(자연어).
  6. 최종 마크다운이 output_file="report.md"로 저장. CrewOutput에는 토큰 사용량과 각 단계 산출물이 그대로 보존.

5. CrewAI 기술 설치 방법

Python 3.10 이상이 필요합니다. 의존성 충돌이 잦으므로 가상환경(uv 또는 venv) 사용을 권장합니다.

# 기본 설치
pip install crewai

# 내장 도구 묶음 함께 설치 (Serper, ScrapeWebsite, FileRead 등)
pip install "crewai[tools]"

# uv 사용 (권장)
uv add "crewai[tools]"

# 프로젝트 스캐폴딩 (권장 — agents.yaml / tasks.yaml 자동 생성)
crewai create crew my_research_crew
cd my_research_crew

# 실행
crewai run

# 환경 변수 (.env)
OPENAI_API_KEY=sk-...
SERPER_API_KEY=...           # SerperDevTool 사용 시
ANTHROPIC_API_KEY=sk-ant-... # Claude 사용 시

주의 — 설치 트러블슈팅

  • chromadb 빌드 실패: 메모리 기능이 chromadb를 끌어옵니다. 윈도우에서는 Microsoft C++ Build Tools가 없으면 빌드가 깨집니다. WSL2를 쓰거나 사전 빌드 휠을 설치하세요.
  • litellm 버전 충돌: LangChain·LlamaIndex와 함께 쓸 때 자주 충돌합니다. uv pip compile로 락파일을 만들어 고정하세요.
  • Python 3.13은 의존성(특히 onnxruntime)이 아직 미지원인 경우가 있어 3.11~3.12를 권장합니다.
  • 크롬 헤드리스 도구(ScrapeWebsite, Selenium 계열)는 컨테이너에서 --no-sandbox 옵션이 필요합니다.

6. CrewAI 기술 사용 방법

최소 예제 — 2 에이전트 Sequential

from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

researcher = Agent(
    role="시장 리서처",
    goal="{topic}에 대한 최신 시장 동향을 사실 기반으로 정리한다",
    backstory="너는 컨설팅 펌에서 15년 일한 시니어 리서처다. "
              "주장은 항상 출처 URL과 함께 제시한다.",
    tools=[SerperDevTool()],
    llm="openai/gpt-4o-mini",
    max_iter=8,
    verbose=True,
)

writer = Agent(
    role="테크 라이터",
    goal="리서치 자료를 임원이 5분에 읽을 보고서로 정리한다",
    backstory="너는 IT 미디어에서 8년간 일한 테크 라이터다. "
              "두괄식, 한 문장 한 메시지를 지킨다.",
    llm="openai/gpt-4o",
    verbose=True,
)

t_research = Task(
    description="{topic} 시장 규모, 주요 플레이어 5곳, "
                "최근 12개월 동향을 한국어로 정리하라.",
    expected_output="요점 10개 이내의 마크다운 노트, 각 항목에 출처 URL.",
    agent=researcher,
)

t_write = Task(
    description="아래 리서치 노트를 토대로 1페이지 임원 보고서를 작성하라.",
    expected_output="제목 / 핵심 3줄 / 시장 / 경쟁 / 권고 5개 섹션의 마크다운.",
    agent=writer,
    context=[t_research],          # 자동 주입
    output_file="report.md",
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[t_research, t_write],
    process=Process.sequential,
    memory=True,
    verbose=True,
)

result = crew.kickoff(inputs={"topic": "한국 중소기업 회계 SaaS 2026"})
print(result.raw)
print(result.token_usage)

실전 예제 — 커스텀 도구 + 구조화 출력

from crewai.tools import tool
from pydantic import BaseModel
from typing import List

@tool("internal_kb_search")
def internal_kb_search(query: str, top_k: int = 5) -> str:
    """사내 지식베이스에서 query에 대한 top_k개 문서 요약을 반환한다."""
    docs = kb_client.search(query, k=top_k)   # 사내 검색 호출
    return "\n\n".join(f"- {d.title}: {d.snippet}" for d in docs)

class Risk(BaseModel):
    name: str
    severity: str          # "low" | "medium" | "high"
    mitigation: str

class RiskReport(BaseModel):
    risks: List[Risk]
    summary: str

risk_task = Task(
    description="제품 출시 리스크를 사내 KB와 외부 검색으로 식별하라.",
    expected_output="구조화된 RiskReport JSON.",
    agent=analyst,
    tools=[internal_kb_search, SerperDevTool()],
    output_pydantic=RiskReport,    # ← 결과 강제 검증
)

계층형(Hierarchical) — 매니저 LLM

crew = Crew(
    agents=[researcher, analyst, writer],
    tasks=[overall_goal_task],          # 큰 목표 1개만
    process=Process.hierarchical,
    manager_llm="openai/gpt-4o",        # 위임 결정용 모델
    memory=True,
    max_rpm=30,                          # 분당 호출 상한 (rate limit)
)

YAML 구성 (권장 패턴)

# config/agents.yaml
researcher:
  role: 시장 리서처
  goal: "{topic} 시장의 최신 동향을 사실 기반으로 정리한다"
  backstory: 컨설팅 펌 15년차 시니어 리서처
  llm: openai/gpt-4o-mini

writer:
  role: 테크 라이터
  goal: 리서치를 임원용 1페이지 보고서로 다듬는다
  backstory: IT 미디어 8년차

# config/tasks.yaml
research_task:
  description: "{topic} 시장 규모와 주요 플레이어 5곳을 정리하라."
  expected_output: 마크다운 노트
  agent: researcher

write_task:
  description: 리서치 노트를 1페이지 보고서로 다듬어라.
  expected_output: 제목 / 핵심 3줄 / 5개 섹션
  agent: writer

운영 시 고려사항

  • 모델은 역할별로 분리하라. Researcher는 도구를 많이 쓰니 빠르고 저렴한 모델, Writer는 출력 품질이 중요하니 강한 모델 — 비용·품질을 동시에 잡습니다.
  • max_iter / max_rpm은 반드시 설정. 미설정이면 무한 루프와 레이트 리밋 폭발의 직접 원인입니다.
  • memory=True는 강력하지만 SQLite 위치를 컨테이너 밖 볼륨으로 빼두지 않으면 재배포마다 소실됩니다.
  • 도구는 멱등(idempotent)으로. 에이전트가 같은 도구를 두 번 호출해도 부작용이 없어야 합니다(특히 메일 발송·DB INSERT).
  • output_pydantic을 적극 사용해 다음 태스크의 입력 형식을 고정하세요. 자유 텍스트로 넘기면 후속 에이전트가 형식을 추측하다가 환각을 만듭니다.
  • verbose=True는 개발에서만. 프로덕션에서는 AgentOps / Langfuse로 트레이스를 보내야 토큰·비용·실패 원인을 분석할 수 있습니다.
  • 크리티컬한 호출은 Flow로 감싸기. Crew는 비결정적 협업, Flow는 결정론적 분기 — 결제·DB 변경처럼 정확한 순서가 필요한 단계는 Flow에 둡니다.

7. CrewAI 자주 쓰는 명령어 / API

명령 / API용도
crewai create crew NAME 권장 디렉터리·YAML 스캐폴딩 생성
crewai run 현재 프로젝트의 main.py를 실행
crewai install pyproject.toml 기반 의존성 설치
crewai train -n N N회 반복 실행하며 사람 피드백을 모아 프롬프트를 다듬기
crewai test -n N --model M 결정적 평가 — 같은 입력을 N번 돌려 산출물 일관성 측정
crewai replay -t TASK_ID 실패한 태스크 지점부터 재실행 (디버깅에 유용)
crewai reset-memories --all SQLite 장기 메모리 / 임베딩 캐시 전체 초기화
crewai flow create NAME Flow(이벤트 기반 워크플로우) 스캐폴딩
crew.kickoff(inputs=...) 동기 실행 (CrewOutput 반환)
crew.kickoff_async(...) 비동기 실행 (FastAPI / 워커용)
crew.kickoff_for_each(inputs=[...]) 배치 실행 — 입력 리스트마다 동일 Crew 실행
@tool("name") 간단한 함수형 커스텀 도구 등록
BaseTool 상속 스키마·캐시·에러 처리가 정교한 도구 작성
output_pydantic=Model Task 결과를 Pydantic 모델로 강제 검증
allow_delegation=True 에이전트가 동료 에이전트에게 위임·질문 허용

사례 — 배치 실행으로 100건 보고서 자동화

companies = [{"topic": f"{name} 2026 전망"} for name in target_list]

# 100건 동시 실행 (max_rpm으로 자동 throttle)
results = crew.kickoff_for_each(inputs=companies)

for inp, out in zip(companies, results):
    save_to_db(inp["topic"], out.raw, out.token_usage)

8. CrewAI 활용방안

잘 어울리는 시나리오

  • 역할이 자연스럽게 갈라지는 콘텐츠 워크플로우 — 리서치 → 작성 → 편집, 영업 메일 → 검토 → 개인화
  • RAG 위에 얹은 보고서 자동화 — 사내 KB 검색 + 외부 검색 + 분석 + 작성을 분리
  • 티켓 트리아지·근본 원인 분석 — Triager / Investigator / Recommender 3역
  • QA / 평가(Judge) 파이프라인 — Generator + Critic + Refiner 패턴을 코드 100줄에 구현
  • 비개발자가 프롬프트를 직접 손대야 하는 팀 — YAML 분리로 개발 / 도메인 협업 가능

대안 기술 비교

기술강점약점CrewAI를 쓸 때
LangGraph 상태 그래프, 체크포인트, HITL이 강력 노드·엣지를 명시적으로 그려야 함, 진입장벽 "역할 협업"이라는 메타포가 더 잘 맞을 때 — 보고서·콘텐츠·QA 파이프라인.
AutoGen 0.4+ 액터 모델, 대화형 멀티에이전트, MS 백킹 메시지 스키마·런타임이 무겁고 학습곡선 가파름 짧은 시간에 데모를 만들거나, YAML로 페르소나를 비개발자에게 맡길 때.
PydanticAI 단일 에이전트의 타입 안전·관측성 끝판왕 멀티에이전트 협업·매니저 위임은 직접 구현해야 3개 이상 역할이 협업하는 워크플로우면 CrewAI. 한 에이전트로 끝나면 PydanticAI.
OpenAI Agents SDK OpenAI 모델 1차 지원, Handoff·Guardrails 사실상 OpenAI 종속, 매니저·메모리 사상 좁음 다중 벤더 모델·온프레미스(Ollama) 폴백이 필요하면 CrewAI.
LangChain Agent 통합 도구 수 최대 단일 에이전트 + 도구 패러다임에 멈춤 "역할 분리"라는 추상화가 명시적으로 필요하면 CrewAI가 코드량을 절반으로 줄입니다.
Letta(MemGPT) 장기기억 영속화, 컨텍스트 가상화 협업·역할 위임 기능이 약함 기억보다 협업이 중심이면 CrewAI. 두 가지를 결합해 쓰는 사례도 늘고 있습니다.

언제 쓰면 안 되는가

  • 결정론·원자성이 핵심인 트랜잭션 — 결제·송금·재고 차감처럼 정확한 순서가 강제돼야 한다면 Crew 대신 일반 코드 또는 CrewAI Flow.
  • 실시간(≤ 500ms) 응답 — 멀티에이전트는 본질적으로 다중 LLM 호출이라 평균 5~30초가 듭니다. 챗봇 1차 응답에는 부적절합니다.
  • 한 에이전트로 충분한 작업 — 분류·정보 추출·간단한 RAG는 PydanticAI 같은 단일 에이전트가 비용·디버깅 면에서 모두 우월.
  • 20개 이상의 깊은 분기·루프 — 그래프가 복잡해지면 LangGraph 또는 자체 오케스트레이터가 더 명료합니다.
  • 토큰 예산이 매우 빠듯한 환경 — 멀티에이전트는 같은 작업을 단일 에이전트로 하는 것보다 2~5배의 토큰을 씁니다.

현장 트러블슈팅 — 경험에서 나온 함정

① "토큰 비용이 갑자기 5배가 됐어요"

멀티에이전트는 한 에이전트 호출 = 여러 ReAct 턴 × 여러 에이전트입니다. 청구서가 폭발한 적이 한 번이라도 있다면 다음 4가지를 동시에 적용하세요. (1) 역할별로 모델 분리 — Researcher는 gpt-4o-mini, Writer만 gpt-4o. (2) max_iter=8~12로 ReAct 상한 설정. (3) 도구 결과가 길면 도구 안에서 요약·페이지네이션. (4) 메모리는 단계별로 끄고 켜기 — 모든 태스크에 memory=True를 켜면 contextual 검색이 매번 임베딩을 호출합니다.

② "에이전트가 같은 도구를 무한히 호출해요"

대부분 도구 출력이 너무 길거나, 출력이 LLM이 보기에 "성공인지 실패인지 애매한 형태"일 때 일어납니다. 도구는 항상 명시적인 성공/실패 신호를 텍스트에 포함시키세요(예: "OK: 5건 검색" 또는 "ERROR: timeout"). 그리고 반드시 max_iter와 max_rpm을 함께 박아 두세요. 디폴트로는 무한입니다.

③ "환각이 단계마다 부풀어오릅니다 (cascade hallucination)"

Sequential 모드에서는 한 에이전트의 환각이 다음 에이전트의 입력이 되면서 지수적으로 부풀어 오릅니다. 해법은 출력의 형태를 강제하는 것입니다. 모든 중간 태스크의 expected_output을 구체적으로 쓰고, 가능하면 output_pydantic을 적용해 다음 단계가 받는 입력의 스키마를 고정하세요. "출처 URL이 없는 사실은 적지 말 것" 같은 규칙은 backstory와 expected_output에 모두 명시해야 효과가 있습니다.

④ "분명히 도구가 있는데 에이전트가 안 써요"

LLM이 도구를 무시하는 가장 흔한 원인은 도구 docstring이 너무 짧거나 모호한 것입니다. 도구 설명에 (1) 무엇을 받고 (2) 무엇을 반환하며 (3) 언제 써야 하는지를 한 문장씩 적으세요. 그래도 안 쓰면 시스템 프롬프트(backstory)에 "답하기 전에 반드시 internal_kb_search를 1회 이상 호출한다"처럼 강제 절차를 명시하면 즉시 효과가 납니다.

⑤ "Hierarchical 모드에서 Manager가 같은 작업을 계속 위임해요"

Manager LLM은 작업 완료 조건이 모호하면 무한히 재위임합니다. 각 에이전트의 goal에 "완료의 정의(Definition of Done)"를 명시하세요. 예를 들어 "리서치 완료 = 출처 URL이 5개 이상이고 시장 규모 숫자가 1개 이상 포함"처럼. 또한 처음 도입할 때는 Sequential로 시작해 안정화한 뒤 Hierarchical로 전환하는 게 안전합니다. 아무 이유 없이 처음부터 Hierarchical을 쓰는 것은 권장하지 않습니다.

⑥ "프로덕션에서 갑자기 응답이 멈춥니다 (rate limit storm)"

Crew 1회 실행이 LLM 호출 30~80건이 되는 일은 흔합니다. kickoff_for_each로 100건을 동시에 돌리면 순식간에 RPM 한도를 친다는 뜻입니다. Crew 단위에 max_rpm을 박고, LiteLLM의 retry_strategy를 켜고, 가능하면 벤더 1차 + 폴백 1개로 구성하세요. 제 경험상 단일 벤더만 쓰는 운영은 일주일 안에 한 번은 사고가 납니다.

⑦ "메모리가 누적돼서 컨텍스트 한도를 초과합니다"

긴 대화·여러 차례 실행 후 context length exceeded가 뜨는 경우입니다. CrewAI는 contextual memory를 자동 검색·주입하는데, top_k가 크거나 entity memory가 누적되면 입력이 부풀어요. 정기적으로 crewai reset-memories --long로 정리하고, 운영에서는 메모리 임베딩 모델을 가벼운 것(예: text-embedding-3-small)으로 명시 지정하세요.

⑧ "delegation을 켰더니 매 턴 동료에게 사소한 걸 묻습니다"

allow_delegation=True는 강력하지만 남용되기 쉽습니다. 위임은 편집·검토 같은 마지막 단계 에이전트에만 켜고, 리서치·분석 단계 에이전트는 끄세요. 또 backstory에 "스스로 답할 수 있는 질문은 위임하지 않는다"는 규칙을 명시하면 위임 빈도가 절반 이하로 떨어집니다.

⑨ "프롬프트 인젝션으로 도구가 의도치 않게 호출됐어요"

사용자 입력이 backstory·system_prompt에 직접 들어가면 즉시 인젝션 표면이 됩니다. 사용자 입력은 항상 inputs 변수로 분리해 task description에만 주입하고, 파일 내용을 도구로 읽을 때는 결과를 [USER CONTENT] 같은 명시적 마커로 감싸 LLM이 "이건 명령이 아니라 데이터다"를 인식하게 만드세요. 결제·삭제 같은 위험 도구에는 휴먼 인 더 루프 확인 단계를 반드시 끼우세요(CrewAI Flow가 이 용도에 적합합니다).

⑩ "테스트가 너무 비결정적이라 CI에 못 넣겠어요"

Crew 통합 테스트는 본질적으로 비결정적입니다. 두 갈래로 푸세요. (a) 단위 — 도구 함수와 Pydantic 스키마는 LLM 없이 그냥 pytest로. (b) 통합 — 가짜 LLM 또는 녹화 / 재생(replay)으로. crewai test -n 5로 산출물의 품질 점수가 임계치를 넘는지를 합격 조건으로 두는 것도 좋습니다(완벽한 일치를 요구하면 영영 그린이 안 됩니다).

반응형