CrewAI
는 문제를 역할로 본다. 가 그래프라면, 이쪽은 직무 기술서다. 마다 역할·목표·배경을 글로 적고, 를 부여하고, 로 묶는다.
4요소 한눈에
, , , . 이 넷이 거의 전부다.
| 요소 | 책임 |
|---|---|
| Agent | 역할·목표·백스토리·도구를 가진 LLM 워커 |
| Task | 한 단위 작업. 설명 + 기대 결과 + 담당 Agent |
| Process | Task 실행 흐름 (sequential / hierarchical) |
| Crew | 위 셋을 묶은 실행 단위 |
Agent — 역할 카드
는 시스템 프롬프트 대신 세 가지 자연어 필드로 정의된다. role은 직책, goal은 평가 기준, backstory는 행동 양식을 깐다. 이 셋이 합쳐져 LLM의 페르소나가 된다. 를 자연어로 강제하는 셈이고, 도 같은 필드를 보면 바로 잡힌다. 결과적으로 한 가 한 가지 일만 잘하도록 묶인다.
from crewai import Agent
researcher = Agent(
role="시니어 데이터 리서처",
goal="주제에 대한 최신 동향을 신뢰 가능한 출처로 정리한다",
backstory=(
"당신은 10년 차 리서처다. 출처 없는 주장은 절대 쓰지 않으며, "
"발견한 사실을 짧고 단정한 문장으로 옮기는 데 능하다."
),
verbose=True,
)Task — 일감의 단위
는 에 붙어 다닌다. description은 무엇을, expected_output은 결과 형태를, agent는 누가를 정한다. 된 Agent가 단일 책임의 Task를 받는 구도다.
expected_output은 단순한 한 줄이 아니라 형식 명세다. “1500자 이내, 단락 3개, 마지막에 출처” 같은 식. 이게 명확하면 후속 의 입력이 안정된다.
from crewai import Task
research = Task(
description="주제 {topic}에 대해 최근 12개월 동향을 조사한다.",
expected_output="800~1200자 마크다운. 단락 3개. 끝에 출처 URL 목록.",
agent=researcher,
)Process — 실행 흐름
는 두 모드뿐이다. 은 리스트를 위에서 아래로 줄 세워 실행하고, 앞선 Task 출력이 다음 Task 컨텍스트로 흘러간다.
은 위에 가 생기고, 그 매니저가 워커들을 어느 순서로 부를지 LLM이 직접 정한다. 의 CrewAI 버전이다.
sequential Crew
가장 흔한 사용법이다. · 리스트를 만들고 Process.sequential로 묶으면 끝. 앞 의 출력이 그대로 다음 입력에 합쳐진다.
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer],
tasks=[research, write],
process=Process.sequential,
verbose=True,
)
result = crew.kickoff(inputs={"topic": "A2A 프로토콜"})
print(result)hierarchical Crew
모드는 Process.hierarchical을 주고 manager_llm을 같이 넘긴다. CrewAI가 임시 를 자동 생성해 워커들을 디스패치한다.
직접 만든 매니저 를 쓰고 싶으면 manager_agent=...로 주입한다. 11장의 흐름이 그대로 살아 있다.
from crewai import Crew, Process
from crewai.llm import LLM
crew = Crew(
agents=[researcher, writer, fact_checker],
tasks=[research, write, verify],
process=Process.hierarchical,
manager_llm=LLM(model="claude-sonnet-4-5"),
verbose=True,
)
result = crew.kickoff(inputs={"topic": "A2A 프로토콜"})CrewAI Tool
은 가 호출할 수 있는 함수 단위다. 두 가지 길이 있다. crewai_tools의 기성품 (SerperDevTool, FileReadTool 등)을 갖다 쓰는 길과, BaseTool을 상속해 커스텀을 짜는 길이다. 결과는 자동으로 다음 LLM 호출에 합쳐진다.
from crewai.tools import BaseTool
from crewai_tools import SerperDevTool
class WordCountTool(BaseTool):
name: str = "word_count"
description: str = "텍스트의 단어 수를 센다."
def _run(self, text: str) -> int:
return len(text.split())
researcher = Agent(
role="리서처",
goal="...",
backstory="...",
tools=[SerperDevTool(), WordCountTool()],
)메모리 — Crew의 기억
는 단위로 켜는 RAG 보조 메모리다. memory=True 한 줄이면 단기·장기·엔터티 메모리가 자동으로 붙는다. 는 디스크 RAG, 단기는 컨텍스트 윈도 보강용이다. 22장의 주제와 직접 연결된다.
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer],
tasks=[research, write],
process=Process.sequential,
memory=True, # 단기 + 장기 + 엔터티 메모리 일괄 활성화
embedder={
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
)sequential vs hierarchical 언제 무엇
- sequential: 파이프라인이 명확할 때. “조사 → 작성 → 검수”처럼 흐름이 정해진 경우.
- : 어떤 워커를 언제 부를지 동적으로 결정해야 할 때. 가 비결정성을 흡수한다.
YAML 구성 — 코드 밖으로
· 정의를 YAML로 빼두면 비개발자도 역할 카피를 수정할 수 있다. CrewBase 데코레이터와 함께 표준 디렉터리 구조(config/agents.yaml, config/tasks.yaml)를 따른다. 정의만 코드에 남는다.
# config/agents.yaml
researcher:
role: 시니어 데이터 리서처
goal: 주제 동향을 출처와 함께 정리
backstory: 10년 차 리서처…
# config/tasks.yaml
research:
description: "주제 {topic}에 대해…"
expected_output: "800~1200자 마크다운…"
agent: researcher
# crew.py — 코드에선 글루만
from crewai.project import CrewBase, agent, task, crew
@CrewBase
class ResearchCrew:
@agent
def researcher(self): return Agent(config=self.agents_config["researcher"])
@task
def research(self): return Task(config=self.tasks_config["research"])
@crew
def crew(self): return Crew(agents=self.agents, tasks=self.tasks)운영 체크리스트
도입 전 점검:
- 모든 의
role·goal이 한 문장에 안 들어가면 역할이 너무 넓다 - 의
expected_output이 형식 명세 수준으로 구체적인가 - 선택이 의식적인가 (기본값 sequential을 그냥 둔 건 아닌가)
- 가 켜져 있다면 임베더와 저장 위치를 알고 있는가
- 들의 호출 비용 상한이 있는가 (
max_usage_count)
이 다섯이 통과되면 멀티 운영의 기본은 잡힌다.
다음 장으로
역할 중심 협업의 다른 축, 대화 중심 — v0.4. 같은 문제를 메시지 흐름으로 풀어내는 모델로 19장에서 만난다. 의 역할 카드가 그쪽에선 대화 참여자로 모습이 바뀐다.