Add Korean translations (#3307)
Some checks failed
Notify Downstream / notify-downstream (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled

This commit is contained in:
Daniel Barreto
2025-08-12 19:58:12 -03:00
committed by GitHub
parent 251ae00b8b
commit a0eadf783b
185 changed files with 36306 additions and 0 deletions

View File

@@ -0,0 +1,316 @@
---
title: 프롬프트 커스터마이징
description: CrewAI를 위한 저수준 프롬프트 커스터마이징에 대해 자세히 알아보고, 다양한 모델과 언어에 대해 매우 맞춤화되고 복잡한 사용 사례를 구현할 수 있습니다.
icon: message-pen
---
## 프롬프트를 커스터마이즈해야 하는 이유
CrewAI의 기본 프롬프트는 많은 시나리오에서 잘 작동하지만, 저수준 커스터마이징은 훨씬 더 유연하고 강력한 에이전트 행동으로 이어집니다. 더 깊은 제어를 통해 얻을 수 있는 이점은 다음과 같습니다:
1. **특정 LLM에 맞게 최적화** GPT-4, Claude, Llama와 같은 다양한 모델은 각자의 고유한 아키텍처에 맞는 프롬프트 형식에서 최고의 성능을 발휘합니다.
2. **언어 변경** 영어를 넘어서는 언어로만 작동하는 에이전트를 구축하여 미묘한 뉘앙스도 정확하게 처리할 수 있습니다.
3. **복잡한 도메인에 특화** 헬스케어, 금융, 법률 등 매우 전문적인 산업군에 맞춰 프롬프트를 조정할 수 있습니다.
4. **톤과 스타일 조정** 에이전트의 톤과 스타일을 좀 더 형식적, 캐주얼, 창의적, 혹은 분석적으로 만들 수 있습니다.
5. **초고도 커스텀 사례 지원** 복잡하고 프로젝트에 특화된 요구사항을 충족하기 위해 고급 프롬프트 구조 및 포맷을 활용할 수 있습니다.
이 가이드에서는 CrewAI의 프롬프트를 더 낮은 레벨에서 활용하여, 에이전트의 사고 및 상호작용 방식을 세밀하게 제어하는 방법을 다룹니다.
## CrewAI의 Prompt 시스템 이해하기
내부적으로 CrewAI는 광범위하게 커스터마이즈할 수 있는 모듈식 prompt 시스템을 사용합니다:
- **Agent 템플릿** 각 agent가 할당된 역할을 수행하는 방식을 결정합니다.
- **Prompt 슬라이스** 작업, 도구 사용, 출력 구조와 같은 특수한 동작을 제어합니다.
- **오류 처리** agent가 실패, 예외, 또는 타임아웃에 어떻게 반응할지 지정합니다.
- **도구별 prompt** 도구가 호출되거나 사용되는 방법에 대한 상세 지침을 정의합니다.
이 요소들이 어떻게 구성되어 있는지 보려면 [CrewAI 저장소의 원본 prompt 템플릿](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json)을 확인하세요. 여기서 필요에 따라 오버라이드하거나 수정하여 고급 동작을 구현할 수 있습니다.
## 기본 시스템 지침 이해하기
<Warning>
**프로덕션 투명성 문제**: CrewAI는 여러분이 인지하지 못하는 사이에 기본 지침을 프롬프트에 자동으로 삽입합니다. 이 섹션에서는 내부적으로 어떤 일이 일어나고 있는지와 완전한 제어권을 얻는 방법을 설명합니다.
</Warning>
여러분이 `role`, `goal`, `backstory`로 에이전트를 정의할 때, CrewAI는 형식 및 동작을 제어하는 추가 시스템 지침을 자동으로 추가합니다. 이러한 기본 삽입을 이해하는 것은 완전한 프롬프트 투명성이 필요한 프로덕션 시스템에서 매우 중요합니다.
### CrewAI가 자동으로 삽입하는 내용
에이전트 구성에 따라 CrewAI는 다양한 기본 지침을 추가합니다:
#### 도구가 없는 에이전트를 위한 안내
```text
"I MUST use these formats, my job depends on it!"
```
#### 도구가 있는 에이전트를 위한 안내
```text
"IMPORTANT: Use the following format in your response:
Thought: you should always think about what to do
Action: the action to take, only one name of [tool_names]
Action Input: the input to the action, just a simple JSON object...
```
#### 구조화된 출력(JSON/Pydantic)의 경우
```text
"Ensure your final answer contains only the content in the following format: {output_format}
Ensure the final output does not include any code block markers like ```json or ```python."
```
### 전체 시스템 프롬프트 보기
LLM에 전달되는 프롬프트가 정확히 무엇인지 확인하려면, 생성된 프롬프트를 확인할 수 있습니다:
```python
from crewai import Agent, Crew, Task
from crewai.utilities.prompts import Prompts
# 에이전트 생성
agent = Agent(
role="Data Analyst",
goal="Analyze data and provide insights",
backstory="You are an expert data analyst with 10 years of experience.",
verbose=True
)
# 샘플 태스크 생성
task = Task(
description="Analyze the sales data and identify trends",
expected_output="A detailed analysis with key insights and trends",
agent=agent
)
# 프롬프트 생성기 생성
prompt_generator = Prompts(
agent=agent,
has_tools=len(agent.tools) > 0,
use_system_prompt=agent.use_system_prompt
)
# 실제 프롬프트 생성 및 확인
generated_prompt = prompt_generator.task_execution()
# LLM에 전달될 전체 시스템 프롬프트 출력
if "system" in generated_prompt:
print("=== SYSTEM PROMPT ===")
print(generated_prompt["system"])
print("\n=== USER PROMPT ===")
print(generated_prompt["user"])
else:
print("=== COMPLETE PROMPT ===")
print(generated_prompt["prompt"])
# 태스크 설명이 어떻게 포맷되는지도 확인할 수 있습니다
print("\n=== TASK CONTEXT ===")
print(f"Task Description: {task.description}")
print(f"Expected Output: {task.expected_output}")
```
### 기본 지침 재정의
프롬프트에 대한 완전한 제어를 얻기 위해 여러 가지 옵션이 있습니다:
#### 옵션 1: 커스텀 템플릿 (권장)
```python
from crewai import Agent
# Define your own system template without default instructions
custom_system_template = """You are {role}. {backstory}
Your goal is: {goal}
Respond naturally and conversationally. Focus on providing helpful, accurate information."""
custom_prompt_template = """Task: {input}
Please complete this task thoughtfully."""
agent = Agent(
role="Research Assistant",
goal="Help users find accurate information",
backstory="You are a helpful research assistant.",
system_template=custom_system_template,
prompt_template=custom_prompt_template,
use_system_prompt=True # Use separate system/user messages
)
```
#### 옵션 2: 사용자 지정 프롬프트 파일
특정 프롬프트 슬라이스를 오버라이드하려면 `custom_prompts.json` 파일을 생성하세요:
```json
{
"slices": {
"no_tools": "\nProvide your best answer in a natural, conversational way.",
"tools": "\nYou have access to these tools: {tools}\n\nUse them when helpful, but respond naturally.",
"formatted_task_instructions": "Format your response as: {output_format}"
}
}
```
그런 다음 crew에서 사용하세요:
```python
crew = Crew(
agents=[agent],
tasks=[task],
prompt_file="custom_prompts.json",
verbose=True
)
```
#### 옵션 3: o1 모델에 대한 시스템 프롬프트 비활성화
```python
agent = Agent(
role="Analyst",
goal="Analyze data",
backstory="Expert analyst",
use_system_prompt=False # Disables system prompt separation
)
```
### 관측 도구를 활용한 디버깅
프로덕션 투명성을 위해 관측 플랫폼과 통합하여 모든 prompt 및 LLM 상호작용을 모니터링하세요. 이를 통해 LLM에 어떤 prompt(기본 지침 포함)가 전송되고 있는지 정확히 확인할 수 있습니다.
다양한 플랫폼(Langfuse, MLflow, Weights & Biases, 커스텀 로깅 솔루션 등)과의 통합에 대한 자세한 가이드는 [관측 문서](/ko/observability/overview)를 참고하세요.
### 프로덕션을 위한 모범 사례
1. **프로덕션에 배포하기 전에 반드시 생성된 prompt를 점검하세요**
2. **prompt 내용을 완전히 제어해야 할 경우에는 커스텀 템플릿을 사용하세요**
3. **지속적인 prompt 모니터링을 위해 관측 도구를 통합하세요** ([Observability 문서](/ko/observability/overview) 참고)
4. **서로 다른 LLM으로 테스트하세요**. 기본 instruction은 모델마다 다르게 작동할 수 있습니다
5. **팀 투명성을 위해 prompt 커스터마이징을 문서화하세요**
<Tip>
기본 instruction은 일관된 agent 동작을 보장하기 위해 존재하지만, 도메인 특화 요구사항과 충돌할 수 있습니다. 위의 커스터마이징 옵션을 사용하여 프로덕션 시스템에서 agent의 동작을 완전히 제어할 수 있습니다.
</Tip>
## 프롬프트 파일 관리 모범 사례
저수준 프롬프트 커스터마이징을 수행할 때는 다음 지침을 따라 조직적이고 유지 관리가 용이하도록 하세요:
1. **파일 분리** 커스터마이징한 프롬프트는 메인 코드베이스 외부의 전용 JSON 파일에 저장하세요.
2. **버전 관리** 리포지토리 내에서 변경 사항을 추적하여 프롬프트 조정 내역이 명확히 문서화되도록 하세요.
3. **모델 또는 언어별 정리** `prompts_llama.json` 또는 `prompts_es.json`과 같이 네이밍 스킴을 사용해 특화된 구성을 빠르게 식별할 수 있도록 하세요.
4. **변경 사항 문서화** 주석을 추가하거나 README를 유지 관리하여 커스터마이징의 목적과 범위를 상세히 기술하세요.
5. **수정 최소화** 실제로 조정이 필요한 특정 부분만 오버라이드하고, 나머지 부분은 기본 기능을 유지하세요.
## 프롬프트를 커스터마이즈하는 가장 간단한 방법
가장 간단한 접근 방법 중 하나는 오버라이드하려는 프롬프트에 대한 JSON 파일을 생성한 다음, 해당 파일을 Crew에 지정하는 것입니다.
1. 업데이트된 프롬프트 슬라이스로 JSON 파일을 만드세요.
2. Crew의 `prompt_file` 파라미터를 통해 그 파일을 참조하세요.
그러면 CrewAI가 기본값과 사용자가 지정한 내용을 병합하므로, 모든 프롬프트를 다시 정의할 필요가 없습니다. 방법은 다음과 같습니다:
### 예시: 기본 프롬프트 커스터마이징
수정하고 싶은 프롬프트를 포함하는 `custom_prompts.json` 파일을 생성하세요. 변경 사항만이 아니라 포함해야 하는 모든 최상위 프롬프트를 반드시 나열해야 합니다:
```json
{
"slices": {
"format": "When responding, follow this structure:\n\nTHOUGHTS: Your step-by-step thinking\nACTION: Any tool you're using\nRESULT: Your final answer or conclusion"
}
}
```
그 다음 아래와 같이 통합하세요:
```python
from crewai import Agent, Crew, Task, Process
# 평소와 같이 에이전트와 태스크를 생성
researcher = Agent(
role="Research Specialist",
goal="Find information on quantum computing",
backstory="You are a quantum physics expert",
verbose=True
)
research_task = Task(
description="Research quantum computing applications",
expected_output="A summary of practical applications",
agent=researcher
)
# 커스텀 프롬프트 파일로 crew를 생성
crew = Crew(
agents=[researcher],
tasks=[research_task],
prompt_file="path/to/custom_prompts.json",
verbose=True
)
# crew 실행
result = crew.kickoff()
```
이 몇 가지 간단한 수정으로, agent가 소통하고 태스크를 해결하는 방식을 세밀하게 제어할 수 있습니다.
## 특정 모델에 맞춘 최적화
모델마다 잘 동작하는 프롬프트의 구조가 다릅니다. 프롬프트를 모델의 뉘앙스에 맞게 더욱 깊이 있게 조정하면 성능이 크게 향상될 수 있습니다.
### 예시: Llama 3.3 프롬프트 템플릿
예를 들어, Meta의 Llama 3.3과 작업할 때는 더 깊은 수준의 커스터마이징이 다음에 설명된 권장 구조를 반영할 수 있습니다:
https://www.llama.com/docs/model-cards-and-prompt-formats/llama3_1/#prompt-template
다음은 Llama 3.3을 코드에서 활용하도록 Agent를 세밀하게 튜닝하는 방법을 보여주는 예시입니다:
```python
from crewai import Agent, Crew, Task, Process
from crewai_tools import DirectoryReadTool, FileReadTool
# Define templates for system, user (prompt), and assistant (response) messages
system_template = """<|begin_of_text|><|start_header_id|>system<|end_header_id|>{{ .System }}<|eot_id|>"""
prompt_template = """<|start_header_id|>user<|end_header_id|>{{ .Prompt }}<|eot_id|>"""
response_template = """<|start_header_id|>assistant<|end_header_id|>{{ .Response }}<|eot_id|>"""
# Create an Agent using Llama-specific layouts
principal_engineer = Agent(
role="Principal Engineer",
goal="Oversee AI architecture and make high-level decisions",
backstory="You are the lead engineer responsible for critical AI systems",
verbose=True,
llm="groq/llama-3.3-70b-versatile", # Using the Llama 3 model
system_template=system_template,
prompt_template=prompt_template,
response_template=response_template,
tools=[DirectoryReadTool(), FileReadTool()]
)
# Define a sample task
engineering_task = Task(
description="Review AI implementation files for potential improvements",
expected_output="A summary of key findings and recommendations",
agent=principal_engineer
)
# Create a Crew for the task
llama_crew = Crew(
agents=[principal_engineer],
tasks=[engineering_task],
process=Process.sequential,
verbose=True
)
# Execute the crew
result = llama_crew.kickoff()
print(result.raw)
```
이와 같이 더 심도 있는 설정을 통해 별도의 JSON 파일 없이도 Llama 기반 워크플로에 대해 포괄적이고 저수준의 제어를 할 수 있습니다.
## 결론
CrewAI에서의 저수준 prompt 커스터마이제이션은 매우 맞춤화되고 복잡한 사용 사례에 대한 문을 엽니다. 잘 구성된 prompt 파일(또는 직접 작성한 인라인 템플릿)을 구축함으로써 다양한 모델, 언어, 특화된 도메인을 수용할 수 있습니다. 이러한 수준의 유연성 덕분에 원하는 AI 동작을 정확하게 설계할 수 있으며, override하지 않을 경우에도 CrewAI가 신뢰할 수 있는 기본값을 제공한다는 점에서 안심할 수 있습니다.
<Check>
이제 CrewAI에서 고급 prompt 커스터마이징을 위한 기초를 갖추었습니다. 모델별 구조나 도메인별 제약에 맞춰 적용하든, 이러한 저수준 접근 방식은 agent 상호작용을 매우 전문적으로 조정할 수 있게 해줍니다.
</Check>

View File

@@ -0,0 +1,133 @@
---
title: 지문 인식
description: CrewAI의 지문 인식 시스템을 사용하여 컴포넌트를 전체 라이프사이클 동안 고유하게 식별하고 추적하는 방법을 알아보세요.
icon: fingerprint
---
## 개요
CrewAI의 Fingerprints는 컴포넌트를 고유하게 식별하고 그 생애주기를 추적할 수 있는 방법을 제공합니다. 각 `Agent`, `Crew`, `Task`는 생성 시 자동으로 고유한 fingerprint를 부여받으며, 이는 수동으로 변경할 수 없습니다.
이러한 fingerprints는 다음과 같은 용도로 사용할 수 있습니다:
- 컴포넌트 사용 감사 및 추적
- 컴포넌트 식별 무결성 보장
- 컴포넌트에 메타데이터 첨부
- 추적 가능한 작업 체인 생성
## 지문(Fingerprints)의 작동 방식
지문(fingerprint)은 `crewai.security` 모듈의 `Fingerprint` 클래스의 인스턴스입니다. 각 지문에는 다음과 같은 정보가 포함되어 있습니다:
- UUID 문자열: 컴포넌트의 고유 식별자로, 자동으로 생성되며 수동으로 설정할 수 없습니다.
- 생성 타임스탬프: 지문이 생성된 시점을 나타내며, 자동으로 설정되고 수동으로 수정할 수 없습니다.
- 메타데이터: 추가 정보를 담은 사전(dictionary)으로, 사용자 정의가 가능합니다.
지문은 컴포넌트가 생성될 때 자동으로 생성되어 할당됩니다. 각 컴포넌트는 읽기 전용 속성을 통해 자신의 지문을 제공합니다.
## 기본 사용법
### 지문 접근하기
```python
from crewai import Agent, Crew, Task
# Create components - fingerprints are automatically generated
agent = Agent(
role="Data Scientist",
goal="Analyze data",
backstory="Expert in data analysis"
)
crew = Crew(
agents=[agent],
tasks=[]
)
task = Task(
description="Analyze customer data",
expected_output="Insights from data analysis",
agent=agent
)
# Access the fingerprints
agent_fingerprint = agent.fingerprint
crew_fingerprint = crew.fingerprint
task_fingerprint = task.fingerprint
# Print the UUID strings
print(f"Agent fingerprint: {agent_fingerprint.uuid_str}")
print(f"Crew fingerprint: {crew_fingerprint.uuid_str}")
print(f"Task fingerprint: {task_fingerprint.uuid_str}")
```
### 지문 메타데이터 작업
지문에 추가적인 맥락 정보를 제공하기 위해 메타데이터를 추가할 수 있습니다:
```python
# Add metadata to the agent's fingerprint
agent.security_config.fingerprint.metadata = {
"version": "1.0",
"department": "Data Science",
"project": "Customer Analysis"
}
# Access the metadata
print(f"Agent metadata: {agent.fingerprint.metadata}")
```
## 지문(Fingerprint) 지속성
지문은 컴포넌트의 생애 주기 전체에 걸쳐 지속되고 변하지 않도록 설계되었습니다. 컴포넌트를 수정하더라도 지문은 동일하게 유지됩니다:
```python
original_fingerprint = agent.fingerprint.uuid_str
# Modify the agent
agent.goal = "New goal for analysis"
# The fingerprint remains unchanged
assert agent.fingerprint.uuid_str == original_fingerprint
```
## 결정론적 지문
UUID와 생성 타임스탬프를 직접 설정할 수는 없지만, `generate` 메서드와 시드(seed)를 사용하여 결정론적 지문을 만들 수 있습니다:
```python
from crewai.security import Fingerprint
# 시드 문자열을 사용하여 결정론적 지문 생성
deterministic_fingerprint = Fingerprint.generate(seed="my-agent-id")
# 동일한 시드로 항상 동일한 지문이 생성됨
same_fingerprint = Fingerprint.generate(seed="my-agent-id")
assert deterministic_fingerprint.uuid_str == same_fingerprint.uuid_str
# 메타데이터도 설정할 수 있음
custom_fingerprint = Fingerprint.generate(
seed="my-agent-id",
metadata={"version": "1.0"}
)
```
## 고급 사용
### Fingerprint 구조
각 fingerprint는 다음과 같은 구조를 가지고 있습니다:
```python
from crewai.security import Fingerprint
fingerprint = agent.fingerprint
# UUID 문자열 - 고유 식별자 (자동 생성)
uuid_str = fingerprint.uuid_str # e.g., "123e4567-e89b-12d3-a456-426614174000"
# 생성 타임스탬프 (자동 생성)
created_at = fingerprint.created_at # datetime 객체
# 메타데이터 - 추가 정보용 (사용자 지정 가능)
metadata = fingerprint.metadata # 딕셔너리, 기본값은 {}
```

View File

@@ -0,0 +1,453 @@
---
title: 효과적인 에이전트 제작
description: 복잡한 문제를 효과적으로 해결하기 위해 협업하는 강력하고 전문화된 AI 에이전트를 설계하는 모범 사례를 배워보세요.
icon: robot
---
## 에이전트 설계의 예술과 과학
CrewAI의 핵심에는 에이전트가 있습니다. 에이전트는 협업 프레임워크 내에서 특정 역할을 수행하도록 설계된 전문화된 AI 엔터티입니다. 기본적인 에이전트를 만드는 것은 간단하지만, 진정으로 효과적이고 탁월한 결과를 만들어내는 에이전트를 설계하려면 주요 설계 원칙과 모범 사례를 이해해야 합니다.
이 가이드는 여러분이 에이전트 설계의 예술을 마스터할 수 있도록 도와줍니다. 이를 통해 효과적으로 협업하고, 비판적으로 사고하며, 특정 요구에 맞춤화된 고품질 결과물을 만들어내는 전문화된 AI 페르소나를 설계할 수 있게 됩니다.
### 에이전트 설계가 중요한 이유
에이전트를 정의하는 방식은 다음에 중대한 영향을 미칩니다:
1. **출력 품질**: 잘 설계된 에이전트는 더 관련성 높고, 품질이 뛰어난 결과를 생성합니다
2. **협업 효율성**: 상호 보완적인 역량을 가진 에이전트들이 함께 더 효율적으로 작업합니다
3. **작업 성과**: 명확한 역할과 목표를 가진 에이전트가 작업을 더 효과적으로 수행합니다
4. **시스템 확장성**: 신중하게 설계된 에이전트는 여러 crew와 다양한 컨텍스트에서 재사용될 수 있습니다
이러한 측면에서 뛰어난 에이전트를 만들기 위한 모범 사례를 함께 살펴보겠습니다.
## 80/20 법칙: 에이전트보다 작업에 집중하세요
효과적인 AI 시스템을 구축할 때 이 중요한 원칙을 기억하세요: **노력의 80%는 작업 설계에, 20%만 에이전트 정의에 투자해야 합니다**.
왜일까요? 아무리 완벽하게 정의된 에이전트라도 잘못된 작업 설계에서는 실패하지만, 잘 설계된 작업은 단순한 에이전트까지도 뛰어나게 만들 수 있기 때문입니다. 즉,
- 대부분의 시간을 명확한 작업 지침 작성에 할애하세요
- 상세한 입력과 예상 결과를 정의하세요
- 실행을 안내할 예시와 컨텍스트를 추가하세요
- 남은 시간에는 에이전트 역할, 목표, 배경에 집중하세요
이는 에이전트 설계가 중요하지 않다는 의미가 아닙니다. 분명히 중요합니다. 하지만 실행 실패의 대부분은 작업 설계에서 발생하므로, 그에 따라 우선순위를 두어야 합니다.
## 효과적인 에이전트 설계의 핵심 원칙
### 1. 역할-목표-배경 이야기 프레임워크
CrewAI에서 가장 강력한 에이전트는 세 가지 핵심 요소의 탄탄한 기반 위에 구축됩니다:
#### 역할: 에이전트의 전문화된 기능
역할은 에이전트가 수행하는 일과 전문 분야를 정의합니다. 역할을 설계할 때는 다음을 준수하세요:
- **구체적이고 전문적으로 작성하세요**: "작가" 대신 "기술 문서 전문가"나 "창의적 스토리텔러"처럼 명확하게 표현하세요.
- **현실 세계의 직업과 일치시키세요**: 역할을 잘 알려진 직업 유형에 기반하세요.
- **도메인 전문성을 포함하세요**: 에이전트의 지식 분야를 명확히 하세요 (예: "시장 동향에 특화된 금융 분석가").
**효과적인 역할 예시:**
```yaml
role: "Senior UX Researcher specializing in user interview analysis"
role: "Full-Stack Software Architect with expertise in distributed systems"
role: "Corporate Communications Director specializing in crisis management"
```
#### 목표: 에이전트의 목적과 동기
목표는 에이전트의 노력을 이끌고 의사 결정 과정을 형성합니다. 효과적인 목표는 다음과 같아야 합니다:
- **명확하고 결과 중심적이어야 함**: 에이전트가 달성하려는 것이 무엇인지 정의합니다.
- **품질 기준을 강조해야 함**: 작업의 품질에 대한 기대치를 포함합니다.
- **성공 기준을 통합해야 함**: "좋음"이 무엇인지 에이전트가 이해할 수 있도록 도와줍니다.
**효과적인 목표의 예시:**
```yaml
goal: "Uncover actionable user insights by analyzing interview data and identifying recurring patterns, unmet needs, and improvement opportunities"
goal: "Design robust, scalable system architectures that balance performance, maintainability, and cost-effectiveness"
goal: "Craft clear, empathetic crisis communications that address stakeholder concerns while protecting organizational reputation"
```
#### 배경 이야기: 에이전트의 경험과 관점
배경 이야기는 에이전트에게 깊이를 부여하며, 문제를 해결하고 타인과 상호작용하는 방식에 영향을 미칩니다. 좋은 배경 이야기는 다음과 같습니다:
- **전문성과 경험을 확립**: 에이전트가 어떻게 자신의 기술을 습득했는지 설명합니다.
- **업무 스타일 및 가치를 정의**: 에이전트가 일에 어떻게 접근하는지 설명합니다.
- **통합된 페르소나 생성**: 배경 이야기의 모든 요소가 역할과 목표에 부합하는지 확인합니다.
**효과적인 배경 이야기 예시:**
```yaml
backstory: "You have spent 15 years conducting and analyzing user research for top tech companies. You have a talent for reading between the lines and identifying patterns that others miss. You believe that good UX is invisible and that the best insights come from listening to what users don't say as much as what they do say."
backstory: "With 20+ years of experience building distributed systems at scale, you've developed a pragmatic approach to software architecture. You've seen both successful and failed systems and have learned valuable lessons from each. You balance theoretical best practices with practical constraints and always consider the maintenance and operational aspects of your designs."
backstory: "As a seasoned communications professional who has guided multiple organizations through high-profile crises, you understand the importance of transparency, speed, and empathy in crisis response. You have a methodical approach to crafting messages that address concerns while maintaining organizational credibility."
```
### 2. 전문가가 일반가보다 우수함
에이전트는 일반적인 역할보다 전문화된 역할을 부여할 때 훨씬 더 우수한 성능을 보입니다. 고도로 집중된 에이전트는 더 정확하고 관련성 높은 결과물을 제공합니다:
**일반적 (효과 적음):**
```yaml
role: "Writer"
```
**전문화 (효과 좋음):**
```yaml
role: "Technical Blog Writer specializing in explaining complex AI concepts to non-technical audiences"
```
**전문가의 이점:**
- 기대되는 출력물에 대한 더 명확한 이해
- 더 일관된 성과
- 특정 작업과의 더 나은 정렬
- 도메인별 판단능력 향상
### 3. 전문화와 다재다능성의 균형
효과적인 에이전트는 전문성(한 가지를 매우 잘하는 것)과 다재다능성(다양한 상황에 적응할 수 있는 것) 사이에서 적절한 균형을 이룹니다:
- **역할에 전문화하고, 적용에는 다재다능하게**: 여러 맥락에서 적용할 수 있는 전문 기술을 가진 에이전트를 만드세요
- **지나치게 좁은 정의는 피하기**: 에이전트가 자신이 전문으로 하는 영역 내에서 다양한 변형을 처리할 수 있도록 하세요
- **협업 맥락을 고려하기**: 함께 일하게 될 다른 에이전트들과 전문 분야가 상호 보완될 수 있도록 에이전트를 설계하세요
### 4. 적절한 전문성 수준 설정
에이전트에게 할당하는 전문성 수준은 작업 접근 방식에 영향을 미칩니다:
- **초급 에이전트**: 단순한 작업, 브레인스토밍, 초기 초안에 적합
- **중급 에이전트**: 대부분의 표준 작업에서 신뢰성 있는 실행에 적합
- **전문가 에이전트**: 깊이와 세밀함이 요구되는 복잡하고 전문적인 작업에 최적
- **월드 클래스 에이전트**: 예외적인 품질이 필요한 중요한 작업에 할당
작업의 복잡성과 품질 요구 사항에 따라 적합한 전문성 수준을 선택하세요. 대부분의 협업 crew에서는 다양한 전문성 수준이 어우러지는 것이 가장 효과적이며, 핵심 전문 기능에는 더 높은 전문성을 배정하는 것이 좋습니다.
## 실제 예시: 적용 전과 적용 후
이러한 모범 사례를 적용하기 전과 후의 agent 정의 예시를 살펴보겠습니다:
### 예시 1: 콘텐츠 제작 에이전트
**이전:**
```yaml
role: "Writer"
goal: "Write good content"
backstory: "You are a writer who creates content for websites."
```
**이후:**
```yaml
role: "B2B Technology Content Strategist"
goal: "Create compelling, technically accurate content that explains complex topics in accessible language while driving reader engagement and supporting business objectives"
backstory: "You have spent a decade creating content for leading technology companies, specializing in translating technical concepts for business audiences. You excel at research, interviewing subject matter experts, and structuring information for maximum clarity and impact. You believe that the best B2B content educates first and sells second, building trust through genuine expertise rather than marketing hype."
```
### 예시 2: 리서치 에이전트
**변경 전:**
```yaml
role: "Researcher"
goal: "Find information"
backstory: "You are good at finding information online."
```
**변경 후:**
```yaml
role: "Academic Research Specialist in Emerging Technologies"
goal: "Discover and synthesize cutting-edge research, identifying key trends, methodologies, and findings while evaluating the quality and reliability of sources"
backstory: "With a background in both computer science and library science, you've mastered the art of digital research. You've worked with research teams at prestigious universities and know how to navigate academic databases, evaluate research quality, and synthesize findings across disciplines. You're methodical in your approach, always cross-referencing information and tracing claims to primary sources before drawing conclusions."
```
## 에이전트를 위한 효과적인 작업 설계하기
에이전트 설계도 중요하지만, 작업 설계는 성공적인 실행을 위해 매우 중요합니다. 에이전트가 성공할 수 있도록 작업을 설계할 때 참고할 수 있는 모범 사례는 다음과 같습니다:
### 효과적인 작업의 구조
잘 설계된 작업은 서로 다른 목적을 가진 두 가지 주요 구성 요소를 가지고 있습니다:
#### 작업 설명: 프로세스
설명은 무엇을 어떻게 해야 하는지에 초점을 맞춰야 하며, 아래를 포함해야 합니다:
- 실행을 위한 상세 지침
- 맥락 및 배경 정보
- 범위 및 제약 조건
- 따라야 할 프로세스 단계
#### 예상 산출물: 결과물
예상 산출물은 최종 결과가 어떻게 보여야 하는지 정의해야 합니다:
- 형식 명세(마크다운, JSON 등)
- 구조 요구사항
- 품질 기준
- 좋은 결과물의 예시(가능할 경우)
### 작업 설계 모범 사례
#### 1. 단일 목적, 단일 산출물
작업은 하나의 명확한 목표에 집중할 때 가장 좋은 성과를 냅니다:
**나쁜 예시(너무 광범위함):**
```yaml
task_description: "Research market trends, analyze the data, and create a visualization."
```
**좋은 예시(집중됨):**
```yaml
# Task 1
research_task:
description: "Research the top 5 market trends in the AI industry for 2024."
expected_output: "A markdown list of the 5 trends with supporting evidence."
# Task 2
analysis_task:
description: "Analyze the identified trends to determine potential business impacts."
expected_output: "A structured analysis with impact ratings (High/Medium/Low)."
# Task 3
visualization_task:
description: "Create a visual representation of the analyzed trends."
expected_output: "A description of a chart showing trends and their impact ratings."
```
#### 2. 입력 및 출력 명시
항상 작업에 사용할 입력값과 출력이 어떻게 보여야 하는지 명확하게 지정하세요:
**예시:**
```yaml
analysis_task:
description: >
Analyze the customer feedback data from the CSV file.
Focus on identifying recurring themes related to product usability.
Consider sentiment and frequency when determining importance.
expected_output: >
A markdown report with the following sections:
1. Executive summary (3-5 bullet points)
2. Top 3 usability issues with supporting data
3. Recommendations for improvement
```
#### 3. 목적 및 맥락 포함
작업이 왜 중요한지, 더 큰 워크플로우에서 어떻게 맞물리는지 설명하세요:
**예시:**
```yaml
competitor_analysis_task:
description: >
Analyze our three main competitors' pricing strategies.
This analysis will inform our upcoming pricing model revision.
Focus on identifying patterns in how they price premium features
and how they structure their tiered offerings.
```
#### 4. 구조화된 출력 도구 사용하기
기계가 읽을 수 있는 출력을 위해서, 포맷을 명확히 지정하세요:
**예시:**
```yaml
data_extraction_task:
description: "Extract key metrics from the quarterly report."
expected_output: "JSON object with the following keys: revenue, growth_rate, customer_acquisition_cost, and retention_rate."
```
## 피해야 할 일반적인 실수
실제 구현에서 얻은 교훈을 바탕으로, 에이전트 및 태스크 설계에서 가장 흔한 실수는 다음과 같습니다:
### 1. 불명확한 작업 지시
**문제:** 작업에 충분한 세부 정보가 없어 에이전트가 효과적으로 실행하기 어렵습니다.
**잘못 설계된 예시:**
```yaml
research_task:
description: "Research AI trends."
expected_output: "A report on AI trends."
```
**개선된 버전:**
```yaml
research_task:
description: >
Research the top emerging AI trends for 2024 with a focus on:
1. Enterprise adoption patterns
2. Technical breakthroughs in the past 6 months
3. Regulatory developments affecting implementation
For each trend, identify key companies, technologies, and potential business impacts.
expected_output: >
A comprehensive markdown report with:
- Executive summary (5 bullet points)
- 5-7 major trends with supporting evidence
- For each trend: definition, examples, and business implications
- References to authoritative sources
```
### 2. 너무 많은 작업을 시도하는 "God Tasks"
**문제:** 여러 복잡한 작업을 하나의 지시 세트로 결합하는 태스크.
**잘못된 설계 예시:**
```yaml
comprehensive_task:
description: "Research market trends, analyze competitor strategies, create a marketing plan, and design a launch timeline."
```
**개선된 버전:**
이 작업을 순차적이고 집중된 태스크로 분리하세요:
```yaml
# Task 1: Research
market_research_task:
description: "Research current market trends in the SaaS project management space."
expected_output: "A markdown summary of key market trends."
# Task 2: Competitive Analysis
competitor_analysis_task:
description: "Analyze strategies of the top 3 competitors based on the market research."
expected_output: "A comparison table of competitor strategies."
context: [market_research_task]
# Continue with additional focused tasks...
```
### 3. 설명과 기대 출력 불일치
**문제:** 작업 설명에서 요구하는 내용과 기대 출력이 서로 다릅니다.
**설계가 미흡한 예시:**
```yaml
analysis_task:
description: "Analyze customer feedback to find areas of improvement."
expected_output: "A marketing plan for the next quarter."
```
**개선된 버전:**
```yaml
analysis_task:
description: "Analyze customer feedback to identify the top 3 areas for product improvement."
expected_output: "A report listing the 3 priority improvement areas with supporting customer quotes and data points."
```
### 4. 당신이 직접 프로세스를 이해하지 못함
**문제:** 당신이 완전히 이해하지 못하는 작업을 에이전트에게 수행하도록 요청함.
**해결책:**
1. 먼저 직접 작업을 수동으로 수행해보세요
2. 프로세스, 의사결정 지점, 정보 출처를 문서화하세요
3. 이 문서를 작업 설명의 기초로 사용하세요
### 5. 계층 구조의 조기 사용
**문제:** 순차적인 프로세스만으로도 충분한 경우에 불필요하게 복잡한 에이전트 계층 구조를 만드는 것.
**해결 방법:** 우선 순차적 프로세스부터 시작하고, 워크플로우의 복잡성이 정말로 필요할 때만 계층적 모델로 전환하세요.
### 6. 모호하거나 일반적인 에이전트 정의
**문제:** 일반적인 에이전트 정의는 일반적인 결과로 이어집니다.
**잘못된 설계 예시:**
```yaml
agent:
role: "Business Analyst"
goal: "Analyze business data"
backstory: "You are good at business analysis."
```
**개선된 버전:**
```yaml
agent:
role: "SaaS Metrics Specialist focusing on growth-stage startups"
goal: "Identify actionable insights from business data that can directly impact customer retention and revenue growth"
backstory: "With 10+ years analyzing SaaS business models, you've developed a keen eye for the metrics that truly matter for sustainable growth. You've helped numerous companies identify the leverage points that turned around their business trajectory. You believe in connecting data to specific, actionable recommendations rather than general observations."
```
## 고급 에이전트 설계 전략
### 협업을 위한 설계
여러 agent가 crew 내에서 함께 작업할 때 다음 사항을 고려하십시오:
- **상호 보완적인 스킬**: 각기 다르지만 상호 보완되는 능력을 가진 agent를 설계하세요.
- **업무 인계 시점**: agent 간에 작업이 어떻게 전달될지 명확한 인터페이스를 정의하세요.
- **건설적인 긴장감**: 때때로 약간씩 다른 관점을 가진 agent를 만들면 생산적인 대화를 통해 더 나은 결과를 이끌어낼 수 있습니다.
예를 들어, 콘텐츠 제작 crew는 다음과 같이 구성될 수 있습니다:
```yaml
# Research Agent
role: "Research Specialist for technical topics"
goal: "Gather comprehensive, accurate information from authoritative sources"
backstory: "You are a meticulous researcher with a background in library science..."
# Writer Agent
role: "Technical Content Writer"
goal: "Transform research into engaging, clear content that educates and informs"
backstory: "You are an experienced writer who excels at explaining complex concepts..."
# Editor Agent
role: "Content Quality Editor"
goal: "Ensure content is accurate, well-structured, and polished while maintaining consistency"
backstory: "With years of experience in publishing, you have a keen eye for detail..."
```
### 전문화된 도구 사용자 생성
일부 agent는 특정 도구를 효과적으로 활용하도록 특별히 설계될 수 있습니다:
```yaml
role: "Data Analysis Specialist"
goal: "Derive meaningful insights from complex datasets through statistical analysis"
backstory: "With a background in data science, you excel at working with structured and unstructured data..."
tools: [PythonREPLTool, DataVisualizationTool, CSVAnalysisTool]
```
### LLM 기능에 맞춘 에이전트 맞춤화
다양한 LLM은 서로 다른 강점을 가지고 있습니다. 이러한 기능을 염두에 두고 에이전트를 설계하세요:
```yaml
# For complex reasoning tasks
analyst:
role: "Data Insights Analyst"
goal: "..."
backstory: "..."
llm: openai/gpt-4o
# For creative content
writer:
role: "Creative Content Writer"
goal: "..."
backstory: "..."
llm: anthropic/claude-3-opus
```
## 에이전트 설계 테스트 및 반복
에이전트 설계는 종종 반복적인 과정입니다. 다음은 실용적인 접근 방식입니다:
1. **프로토타입으로 시작하기**: 초기 에이전트 정의 생성
2. **샘플 작업으로 테스트하기**: 대표적인 작업에서 성능 평가
3. **출력물 분석**: 강점과 약점 파악
4. **정의 수정**: 관찰에 따라 역할, 목표, 백스토리 조정
5. **협업 테스트**: 에이전트가 crew 환경에서 어떻게 작동하는지 평가
## 결론
효과적인 agent를 만드는 것은 예술이자 과학입니다. 여러분의 특정 요구에 맞춘 역할, 목표, 그리고 backstory를 신중하게 정의하고, 잘 설계된 task와 결합함으로써 뛰어난 결과를 만들어내는 전문화된 AI 협업자를 만들 수 있습니다.
agent와 task의 설계는 반복적인 과정임을 기억하세요. 이러한 모범 사례로 시작하여 agent가 실제로 동작하는 모습을 관찰하고, 배운 점을 바탕으로 접근 방식을 개선하세요. 그리고 항상 80/20 법칙을 명심하세요. agent로부터 최고의 결과를 얻기 위해서는 명확하고 집중된 task를 만드는 데 대부분의 노력을 집중하는 것이 중요합니다.
<Check>
축하합니다! 이제 효과적인 agent 설계의 원칙과 실천법을 이해하셨습니다. 이 기술들을 적용하여 강력하고 전문화된 agent들이 복잡한 task를 매끄럽게 협력하여 완수할 수 있도록 만드세요.
</Check>
## 다음 단계
- 특정 사용 사례에 맞는 다양한 agent 구성을 실험해 보세요
- [첫 crew 만들기](/ko/guides/crews/first-crew)에 대해 배우며 agent들이 어떻게 함께 작동하는지 확인해 보세요
- 더 발전된 오케스트레이션을 위해 [CrewAI Flows](/ko/guides/flows/first-flow)를 탐색해 보세요

View File

@@ -0,0 +1,503 @@
---
title: CrewAI 사용 사례 평가
description: AI 애플리케이션 요구 사항을 평가하고 복잡성과 정밀도 요구 사항에 따라 Crews와 Flows 중 올바른 접근 방식을 선택하는 방법을 알아보세요.
icon: scale-balanced
---
## 의사결정 프레임워크 이해하기
CrewAI로 AI 애플리케이션을 구축할 때 가장 중요한 결정 중 하나는 특정 사용 사례에 적합한 방식을 선택하는 것입니다. Crew를 사용할까요? Flow를 사용할까요? 아니면 둘의 조합을 사용할까요? 이 가이드는 요구 사항을 평가하고 정보에 기반한 아키텍처 결정을 내리는 데 도움이 됩니다.
이 결정의 핵심은 애플리케이션에서의 **복잡성**과 **정밀성**의 관계를 이해하는 것입니다:
<Frame caption="CrewAI 애플리케이션을 위한 복잡성 vs. 정밀성 매트릭스">
<img src="/images/complexity_precision.png" alt="복잡성 vs. 정밀성 매트릭스" />
</Frame>
이 매트릭스를 통해 다양한 방식이 복잡성과 정밀성에 대한 요구 사항과 어떻게 일치하는지 시각적으로 확인할 수 있습니다. 각 사분면이 의미하는 바와 그것이 아키텍처 선택에 어떻게 도움이 되는지 함께 살펴보겠습니다.
## 복잡성-정밀도 행렬 설명
### 복잡성이란 무엇인가?
CrewAI 애플리케이션의 맥락에서 **복잡성**은 다음을 의미합니다:
- 요구되는 뚜렷한 단계 또는 작업 수
- 수행해야 할 작업의 다양성
- 서로 다른 구성 요소 간의 상호 의존성
- 조건부 로직과 분기의 필요성
- 전체 워크플로우의 정교함
### 정밀성이란 무엇인가?
**정밀성**은 이 맥락에서 다음을 의미합니다:
- 최종 결과물에 요구되는 정확성
- 구조화되고 예측 가능한 결과의 필요성
- 재현성의 중요성
- 각 단계에 대한 통제 수준
- 출력의 변동 허용치
### 네 가지 사분면
#### 1. 낮은 복잡도, 낮은 정밀도
**특징:**
- 단순하고 직관적인 작업
- 출력 결과의 일부 변형 허용
- 제한된 단계 수
- 창의적이거나 탐색적인 응용
**권장 접근법:** 최소한의 에이전트를 가진 Simple Crews
**예시 사용 사례:**
- 기본 콘텐츠 생성
- 아이디어 브레인스토밍
- 간단한 요약 작업
- 창의적 글쓰기 보조
#### 2. 낮은 복잡성, 높은 정밀도
**특징:**
- 정확하고 구조화된 결과물이 요구되는 단순한 워크플로우
- 재현 가능한 결과가 필요한 경우
- 단계는 제한적이지만, 높은 정확도가 요구됨
- 주로 데이터 처리 또는 변환이 포함됨
**권장 방식:** 직접적인 LLM 호출이나 구조화된 출력이 있는 간단한 Crew 사용
**예시 활용 사례:**
- 데이터 추출 및 변환
- 양식 작성 및 검증
- 구조화된 콘텐츠 생성(JSON, XML)
- 단순 분류 작업
#### 3. 높은 복잡성, 낮은 정밀도
**특징:**
- 여러 단계로 이루어진 다단계 프로세스
- 창의적이거나 탐색적인 출력물
- 구성 요소 간의 복잡한 상호작용
- 최종 결과의 변동성 허용
**권장 접근 방식:** 여러 전문화된 agent가 포함된 Complex Crew
**예시 사용 사례:**
- 연구 및 분석
- 콘텐츠 생성 파이프라인
- 탐색적 데이터 분석
- 창의적 문제 해결
#### 4. 높은 복잡성, 높은 정밀도
**특징:**
- 구조화된 산출물이 요구되는 복잡한 워크플로
- 엄격한 정확성 요구사항을 가진 여러 상호 의존적인 단계
- 정교한 처리와 정밀한 결과 모두 필요
- 종종 임무에 중요한 애플리케이션
**권장 접근 방식:** 검증 단계를 포함한 여러 Crew를 오케스트레이션하는 Flows
**예시 사용 사례:**
- 엔터프라이즈 의사결정 지원 시스템
- 복잡한 데이터 처리 파이프라인
- 다단계 문서 처리
- 규제 산업 애플리케이션
## 크루와 플로우 중에서 선택하기
### Crews를 선택해야 할 때
Crews는 다음과 같은 경우에 이상적입니다:
1. **협업 지능이 필요할 때** - 서로 다른 전문성을 가진 여러 agent들이 함께 작업해야 할 때
2. **문제가 창발적 사고를 요구할 때** - 다양한 관점과 접근 방식에서의 해결책이 이득이 될 때
3. **작업이 주로 창의적이거나 분석적일 때** - 작업이 리서치, 콘텐츠 제작, 분석을 포함할 때
4. **엄격한 구조보다는 적응력을 중시할 때** - agent의 자율성이 workflow에 도움이 될 때
5. **출력 형식이 다소 유연할 수 있을 때** - 출력 구조에 약간의 변동이 허용될 때
```python
# Example: Research Crew for market analysis
from crewai import Agent, Crew, Process, Task
# Create specialized agents
researcher = Agent(
role="Market Research Specialist",
goal="Find comprehensive market data on emerging technologies",
backstory="You are an expert at discovering market trends and gathering data."
)
analyst = Agent(
role="Market Analyst",
goal="Analyze market data and identify key opportunities",
backstory="You excel at interpreting market data and spotting valuable insights."
)
# Define their tasks
research_task = Task(
description="Research the current market landscape for AI-powered healthcare solutions",
expected_output="Comprehensive market data including key players, market size, and growth trends",
agent=researcher
)
analysis_task = Task(
description="Analyze the market data and identify the top 3 investment opportunities",
expected_output="Analysis report with 3 recommended investment opportunities and rationale",
agent=analyst,
context=[research_task]
)
# Create the crew
market_analysis_crew = Crew(
agents=[researcher, analyst],
tasks=[research_task, analysis_task],
process=Process.sequential,
verbose=True
)
# Run the crew
result = market_analysis_crew.kickoff()
```
### 플로우를 선택해야 할 때
플로우는 다음과 같은 경우에 이상적입니다:
1. **실행에 대한 정밀한 제어가 필요할 때** - 워크플로우에 정확한 순서 지정과 상태 관리가 필요한 경우
2. **애플리케이션에 복잡한 상태 요구사항이 있을 때** - 여러 단계에 걸쳐 상태를 유지하고 변환해야 하는 경우
3. **구조화되고 예측 가능한 출력이 필요할 때** - 애플리케이션에서 일관되고 포맷된 결과가 필요한 경우
4. **워크플로우에 조건부 로직이 포함될 때** - 중간 결과에 따라 다른 경로를 선택해야 하는 경우
5. **AI와 절차적 코드를 결합해야 할 때** - 솔루션에 AI 기능과 전통적인 프로그래밍이 모두 필요한 경우
```python
# Example: Customer Support Flow with structured processing
from crewai.flow.flow import Flow, listen, router, start
from pydantic import BaseModel
from typing import List, Dict
# Define structured state
class SupportTicketState(BaseModel):
ticket_id: str = ""
customer_name: str = ""
issue_description: str = ""
category: str = ""
priority: str = "medium"
resolution: str = ""
satisfaction_score: int = 0
class CustomerSupportFlow(Flow[SupportTicketState]):
@start()
def receive_ticket(self):
# In a real app, this might come from an API
self.state.ticket_id = "TKT-12345"
self.state.customer_name = "Alex Johnson"
self.state.issue_description = "Unable to access premium features after payment"
return "Ticket received"
@listen(receive_ticket)
def categorize_ticket(self, _):
# Use a direct LLM call for categorization
from crewai import LLM
llm = LLM(model="openai/gpt-4o-mini")
prompt = f"""
Categorize the following customer support issue into one of these categories:
- Billing
- Account Access
- Technical Issue
- Feature Request
- Other
Issue: {self.state.issue_description}
Return only the category name.
"""
self.state.category = llm.call(prompt).strip()
return self.state.category
@router(categorize_ticket)
def route_by_category(self, category):
# Route to different handlers based on category
return category.lower().replace(" ", "_")
@listen("billing")
def handle_billing_issue(self):
# Handle billing-specific logic
self.state.priority = "high"
# More billing-specific processing...
return "Billing issue handled"
@listen("account_access")
def handle_access_issue(self):
# Handle access-specific logic
self.state.priority = "high"
# More access-specific processing...
return "Access issue handled"
# Additional category handlers...
@listen("billing", "account_access", "technical_issue", "feature_request", "other")
def resolve_ticket(self, resolution_info):
# Final resolution step
self.state.resolution = f"Issue resolved: {resolution_info}"
return self.state.resolution
# Run the flow
support_flow = CustomerSupportFlow()
result = support_flow.kickoff()
```
### 크루와 플로우를 결합해야 할 때
가장 정교한 애플리케이션은 종종 크루와 플로우를 결합할 때 이점을 얻습니다:
1. **복잡한 다단계 프로세스** - 플로우를 사용해 전체 프로세스를 오케스트레이션하고, 크루를 통해 복잡한 하위 작업을 처리합니다.
2. **창의성과 구조가 모두 필요한 애플리케이션** - 창의적인 작업에는 크루를 사용하고, 구조적인 처리는 플로우로 처리합니다.
3. **엔터프라이즈급 AI 애플리케이션** - 플로우로 상태 및 프로세스 흐름을 관리하면서, 크루를 활용해 특화된 작업을 수행합니다.
```python
# Example: Content Production Pipeline combining Crews and Flows
from crewai.flow.flow import Flow, listen, start
from crewai import Agent, Crew, Process, Task
from pydantic import BaseModel
from typing import List, Dict
class ContentState(BaseModel):
topic: str = ""
target_audience: str = ""
content_type: str = ""
outline: Dict = {}
draft_content: str = ""
final_content: str = ""
seo_score: int = 0
class ContentProductionFlow(Flow[ContentState]):
@start()
def initialize_project(self):
# Set initial parameters
self.state.topic = "Sustainable Investing"
self.state.target_audience = "Millennial Investors"
self.state.content_type = "Blog Post"
return "Project initialized"
@listen(initialize_project)
def create_outline(self, _):
# Use a research crew to create an outline
researcher = Agent(
role="Content Researcher",
goal=f"Research {self.state.topic} for {self.state.target_audience}",
backstory="You are an expert researcher with deep knowledge of content creation."
)
outliner = Agent(
role="Content Strategist",
goal=f"Create an engaging outline for a {self.state.content_type}",
backstory="You excel at structuring content for maximum engagement."
)
research_task = Task(
description=f"Research {self.state.topic} focusing on what would interest {self.state.target_audience}",
expected_output="Comprehensive research notes with key points and statistics",
agent=researcher
)
outline_task = Task(
description=f"Create an outline for a {self.state.content_type} about {self.state.topic}",
expected_output="Detailed content outline with sections and key points",
agent=outliner,
context=[research_task]
)
outline_crew = Crew(
agents=[researcher, outliner],
tasks=[research_task, outline_task],
process=Process.sequential,
verbose=True
)
# Run the crew and store the result
result = outline_crew.kickoff()
# Parse the outline (in a real app, you might use a more robust parsing approach)
import json
try:
self.state.outline = json.loads(result.raw)
except:
# Fallback if not valid JSON
self.state.outline = {"sections": result.raw}
return "Outline created"
@listen(create_outline)
def write_content(self, _):
# Use a writing crew to create the content
writer = Agent(
role="Content Writer",
goal=f"Write engaging content for {self.state.target_audience}",
backstory="You are a skilled writer who creates compelling content."
)
editor = Agent(
role="Content Editor",
goal="Ensure content is polished, accurate, and engaging",
backstory="You have a keen eye for detail and a talent for improving content."
)
writing_task = Task(
description=f"Write a {self.state.content_type} about {self.state.topic} following this outline: {self.state.outline}",
expected_output="Complete draft content in markdown format",
agent=writer
)
editing_task = Task(
description="Edit and improve the draft content for clarity, engagement, and accuracy",
expected_output="Polished final content in markdown format",
agent=editor,
context=[writing_task]
)
writing_crew = Crew(
agents=[writer, editor],
tasks=[writing_task, editing_task],
process=Process.sequential,
verbose=True
)
# Run the crew and store the result
result = writing_crew.kickoff()
self.state.final_content = result.raw
return "Content created"
@listen(write_content)
def optimize_for_seo(self, _):
# Use a direct LLM call for SEO optimization
from crewai import LLM
llm = LLM(model="openai/gpt-4o-mini")
prompt = f"""
Analyze this content for SEO effectiveness for the keyword "{self.state.topic}".
Rate it on a scale of 1-100 and provide 3 specific recommendations for improvement.
Content: {self.state.final_content[:1000]}... (truncated for brevity)
Format your response as JSON with the following structure:
{{
"score": 85,
"recommendations": [
"Recommendation 1",
"Recommendation 2",
"Recommendation 3"
]
}}
"""
seo_analysis = llm.call(prompt)
# Parse the SEO analysis
import json
try:
analysis = json.loads(seo_analysis)
self.state.seo_score = analysis.get("score", 0)
return analysis
except:
self.state.seo_score = 50
return {"score": 50, "recommendations": ["Unable to parse SEO analysis"]}
# Run the flow
content_flow = ContentProductionFlow()
result = content_flow.kickoff()
```
## 실용적인 평가 프레임워크
특정 사용 사례에 맞는 올바른 접근 방식을 결정하려면 다음 단계별 평가 프레임워크를 따르세요:
### 1단계: 복잡성 평가
아래와 같은 기준으로 애플리케이션의 복잡성을 1~10점 척도로 평가하세요:
1. **단계 수**: 얼마나 많은 개별 작업이 필요한가요?
- 1-3단계: 낮은 복잡성 (1-3)
- 4-7단계: 중간 복잡성 (4-7)
- 8단계 이상: 높은 복잡성 (8-10)
2. **상호 의존성**: 서로 다른 부분 간의 연결성은 어느 정도인가요?
- 의존성이 거의 없음: 낮은 복잡성 (1-3)
- 다소 의존성 있음: 중간 복잡성 (4-7)
- 복잡한 다중 의존성: 높은 복잡성 (8-10)
3. **조건부 논리**: 얼마나 많은 분기 및 의사결정이 필요한가요?
- 선형 프로세스: 낮은 복잡성 (1-3)
- 분기가 일부 있음: 중간 복잡성 (4-7)
- 복잡한 결정 트리: 높은 복잡성 (8-10)
4. **도메인 지식**: 요구되는 지식의 전문성은 어느 정도인가요?
- 일반적인 지식: 낮은 복잡성 (1-3)
- 일부 전문 지식 필요: 중간 복잡성 (4-7)
- 여러 도메인에 대한 깊은 전문성 필요: 높은 복잡성 (8-10)
평균 점수를 계산하여 전체 복잡성을 결정하세요.
### 2단계: 정밀도 요구사항 평가
정밀도 요구사항을 1-10점 척도로 평가하세요. 다음을 고려합니다:
1. **출력 구조**: 출력이 얼마나 구조화되어야 합니까?
- 자유형 텍스트: 낮은 정밀도 (1-3)
- 반구조화: 중간 정밀도 (4-7)
- 엄격한 포맷(JSON, XML): 높은 정밀도 (8-10)
2. **정확성 필요성**: 사실적 정확성이 얼마나 중요합니까?
- 창의적 콘텐츠: 낮은 정밀도 (1-3)
- 정보성 콘텐츠: 중간 정밀도 (4-7)
- 중요한 정보: 높은 정밀도 (8-10)
3. **재현성**: 실행마다 결과가 얼마나 일관되어야 합니까?
- 변동 허용: 낮은 정밀도 (1-3)
- 어느 정도 일관성 필요: 중간 정밀도 (4-7)
- 정확한 재현성 필요: 높은 정밀도 (8-10)
4. **오류 허용도**: 오류의 영향은 어느 정도입니까?
- 영향 적음: 낮은 정밀도 (1-3)
- 영향 보통: 중간 정밀도 (4-7)
- 영향 큼: 높은 정밀도 (8-10)
평균 점수를 계산하여 전체 정밀도 요구사항을 결정하세요.
### 3단계: 매트릭스에 매핑하기
복잡도와 정밀도 점수를 매트릭스에 표시하세요:
- **낮은 복잡도(1-4), 낮은 정밀도(1-4)**: Simple Crews
- **낮은 복잡도(1-4), 높은 정밀도(5-10)**: 직접적인 LLM 호출이 있는 Flows
- **높은 복잡도(5-10), 낮은 정밀도(1-4)**: Complex Crews
- **높은 복잡도(5-10), 높은 정밀도(5-10)**: Crews를 오케스트레이션하는 Flows
### 4단계: 추가 요소 고려
복잡성과 정밀성 외에도 다음을 고려하세요:
1. **개발 시간**: crew는 프로토타입을 더 빠르게 만들 수 있습니다
2. **유지보수 필요**: flow는 장기적인 유지보수에 더 적합합니다
3. **팀 전문성**: 팀이 다양한 접근법에 얼마나 익숙한지 고려하세요
4. **확장성 요구 사항**: flow는 일반적으로 복잡한 애플리케이션에 더 잘 확장됩니다
5. **통합 필요**: 솔루션이 기존 시스템과 어떻게 통합될지 고려하세요
## 결론
Crews와 Flows 중에서 선택하거나 결합하는 것은 CrewAI 애플리케이션의 효과성, 유지 관리성, 확장성에 영향을 미치는 중요한 아키텍처적 결정입니다. 복잡성과 정밀성이라는 차원에서 사용 사례를 평가함으로써, 귀하의 특정 요구 사항에 부합하는 정보에 기반한 결정을 내릴 수 있습니다.
가장 좋은 접근방식은 애플리케이션이 성숙해지면서 종종 진화한다는 점을 기억하세요. 귀하의 요구를 충족하는 가장 간단한 해결책으로 시작하고, 경험이 쌓이고 요구 사항이 명확해지면 아키텍처를 개선할 준비를 하세요.
<Check>
이제 CrewAI 사용 사례를 평가하고, 복잡성과 정밀성 요구 사항에 따라 올바른 접근법을 선택할 수 있는 프레임워크를 갖추게 되었습니다. 이를 통해 보다 효과적이고 유지 관리 가능하며 확장성 있는 AI 애플리케이션을 구축할 수 있습니다.
</Check>
## 다음 단계
- [효과적인 에이전트 만들기](/ko/guides/agents/crafting-effective-agents)에 대해 더 알아보기
- [처음으로 crew 만들기](/ko/guides/crews/first-crew) 살펴보기
- [flow 상태 관리 마스터하기](/ko/guides/flows/mastering-flow-state)에 깊이 파고들기
- 더 깊은 이해를 위해 [핵심 개념](/ko/concepts/agents) 확인하기

View File

@@ -0,0 +1,391 @@
---
title: 첫 번째 크루 만들기
description: 복잡한 문제를 함께 해결할 수 있는 협업 AI 팀을 만드는 단계별 튜토리얼입니다.
icon: users-gear
---
## 협업 AI의 힘을 발휘하기
여러 AI 에이전트가 각자의 전문성을 바탕으로 원활하게 협력하며 복잡한 문제를 해결한다고 상상해 보세요. 각자 고유한 기술을 발휘해 공동의 목표를 달성합니다. 이것이 바로 CrewAI의 힘입니다. CrewAI 프레임워크를 통해 단일 AI로는 달성할 수 없는 과업을 협업 AI 시스템으로 실현할 수 있습니다.
이 가이드에서는 연구 크루를 만들어 주제를 조사 및 분석하고, 종합적인 보고서를 작성하는 과정을 안내합니다. 이 실용적인 예시는 AI 에이전트들이 어떻게 협력하여 복잡한 작업을 수행할 수 있는지 보여 주지만, CrewAI로 실현할 수 있는 가능성의 시작에 불과합니다.
### 무엇을 만들고 배우게 될까요
이 가이드를 마치면 다음을 할 수 있게 됩니다:
1. **특화된 AI 연구팀 조직**: 각기 다른 역할과 책임을 가진 연구팀을 만듭니다
2. **여러 AI 에이전트 간의 협업 조율**
3. **정보 수집, 분석, 보고서 생성을 포함한 복잡한 workflow 자동화**
4. **더 야심찬 프로젝트에도 적용할 수 있는 기초 역량 구축**
이 가이드에서는 간단한 research crew를 만들지만, 동일한 패턴과 기법을 활용하여 다음과 같은 훨씬 더 정교한 팀도 만들 수 있습니다:
- 전문 writer, editor, fact-checker가 참여하는 다단계 콘텐츠 생성
- 단계별 지원 에이전트가 있는 복잡한 고객 서비스 시스템
- 데이터 수집, 시각화, 인사이트 생성까지 하는 자율 business analyst
- 아이디어 구상, 디자인, 구현 계획까지 진행하는 product development 팀
이제 여러분의 첫 crew를 만들어 봅시다!
### 필수 조건
시작하기 전에 다음을 확인하세요:
1. [설치 가이드](/ko/installation)를 참고하여 CrewAI를 설치했는지 확인하세요.
2. [LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)를 참고하여 환경에 LLM API 키를 설정했는지 확인하세요.
3. Python에 대한 기본적인 이해
## 1단계: 새로운 CrewAI 프로젝트 생성
먼저, CLI를 사용하여 새로운 CrewAI 프로젝트를 생성해봅시다. 이 명령어는 필요한 모든 파일을 포함한 전체 프로젝트 구조를 설정해 주어, 보일러플레이트 코드를 설정하는 대신 에이전트와 그들의 작업 정의에 집중할 수 있습니다.
```bash
crewai create crew research_crew
cd research_crew
```
이렇게 하면 crew에 필요한 기본 구조를 갖춘 프로젝트가 생성됩니다. CLI는 다음을 자동으로 생성합니다:
- 필요한 파일이 포함된 프로젝트 디렉터리
- 에이전트와 작업에 대한 구성 파일
- 기본 crew 구현
- crew를 실행하는 메인 스크립트
<Frame caption="CrewAI 프레임워크 개요">
<img src="/images/crews.png" alt="CrewAI Framework Overview" />
</Frame>
## 2단계: 프로젝트 구조 살펴보기
CLI가 생성한 프로젝트 구조를 이해하는 시간을 가져봅시다. CrewAI는 Python 프로젝트의 모범 사례를 따르므로, crew가 더 복잡해질수록 코드를 쉽게 유지 관리하고 확장할 수 있습니다.
```
research_crew/
├── .gitignore
├── pyproject.toml
├── README.md
├── .env
└── src/
└── research_crew/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
```
이 구조는 Python 프로젝트의 모범 사례를 따르며, 코드를 체계적으로 구성할 수 있도록 해줍니다. 설정 파일(YAML)과 구현 코드(Python)의 분리로 인해, 기본 코드를 변경하지 않고도 crew의 동작을 쉽게 수정할 수 있습니다.
## 3단계: 에이전트 구성하기
이제 재미있는 단계가 시작됩니다 - 여러분의 AI 에이전트를 정의하는 것입니다! CrewAI에서 에이전트는 특정 역할, 목표 및 배경을 가진 전문화된 엔터티로, 이들이 어떻게 행동할지를 결정합니다. 각각 고유한 성격과 목적을 지닌 연극의 등장인물로 생각하면 됩니다.
우리의 리서치 crew를 위해 두 명의 에이전트를 만들겠습니다:
1. 정보를 찾아 정리하는 데 뛰어난 **리서처**
2. 연구 결과를 해석하고 통찰력 있는 보고서를 작성할 수 있는 **애널리스트**
이러한 전문화된 에이전트를 정의하기 위해 `agents.yaml` 파일을 수정해봅시다. `llm` 항목은 사용 중인 제공업체에 맞게 설정하세요.
```yaml
# src/research_crew/config/agents.yaml
researcher:
role: >
Senior Research Specialist for {topic}
goal: >
Find comprehensive and accurate information about {topic}
with a focus on recent developments and key insights
backstory: >
You are an experienced research specialist with a talent for
finding relevant information from various sources. You excel at
organizing information in a clear and structured manner, making
complex topics accessible to others.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
analyst:
role: >
Data Analyst and Report Writer for {topic}
goal: >
Analyze research findings and create a comprehensive, well-structured
report that presents insights in a clear and engaging way
backstory: >
You are a skilled analyst with a background in data interpretation
and technical writing. You have a talent for identifying patterns
and extracting meaningful insights from research data, then
communicating those insights effectively through well-crafted reports.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
```
각 에이전트가 고유한 역할, 목표, 그리고 배경을 가지고 있다는 점에 주목하세요. 이 요소들은 단순한 설명 그 이상으로, 실제로 에이전트가 자신의 과업을 어떻게 접근하는지에 적극적으로 영향을 미칩니다. 이러한 부분을 신중하게 설계함으로써 서로 보완하는 전문적인 역량과 관점을 가진 에이전트를 만들 수 있습니다.
## 4단계: 작업 정의하기
이제 agent들을 정의했으니, 이들에게 수행할 구체적인 작업을 지정해야 합니다. CrewAI의 작업(task)은 agent가 수행할 구체적인 업무를 나타내며, 자세한 지침과 예상 결과물이 포함됩니다.
연구 crew를 위해 두 가지 주요 작업을 정의하겠습니다:
1. 포괄적인 정보 수집을 위한 **연구 작업**
2. 인사이트 있는 보고서 생성을 위한 **분석 작업**
`tasks.yaml` 파일을 다음과 같이 수정해 보겠습니다:
```yaml
# src/research_crew/config/tasks.yaml
research_task:
description: >
{topic}에 대해 철저한 연구를 수행하세요. 다음에 중점을 두세요:
1. 주요 개념 및 정의
2. 역사적 발전과 최근 동향
3. 주요 과제와 기회
4. 주목할 만한 적용 사례 또는 케이스 스터디
5. 향후 전망과 잠재적 발전
반드시 명확한 섹션으로 구성된 구조화된 형식으로 결과를 정리하세요.
expected_output: >
{topic}의 모든 요구 사항을 다루는, 잘 구성된 섹션이 포함된 포괄적인 연구 문서.
필요에 따라 구체적인 사실, 수치, 예시를 포함하세요.
agent: researcher
analysis_task:
description: >
연구 결과를 분석하고 {topic}에 대한 포괄적인 보고서를 작성하세요.
보고서에는 다음 내용이 포함되어야 합니다:
1. 간결한 요약(executive summary)으로 시작
2. 연구의 모든 주요 정보 포함
3. 동향과 패턴에 대한 통찰력 있는 분석 제공
4. 추천사항 또는 미래 고려 사항 제시
5. 명확한 제목과 함께 전문적이고 읽기 쉬운 형식으로 작성
expected_output: >
연구 결과와 추가 분석, 인사이트를 포함한 {topic}에 대한 정제되고 전문적인 보고서.
보고서는 간결한 요약, 본문, 결론 등으로 잘 구조화되어 있어야 합니다.
agent: analyst
context:
- research_task
output_file: output/report.md
```
분석 작업 내의 `context` 필드에 주목하세요. 이 강력한 기능을 통해 analyst가 연구 작업의 결과물을 참조할 수 있습니다. 이를 통해 정보가 human team에서처럼 agent 간에 자연스럽게 흐르는 워크플로우가 만들어집니다.
## 5단계: 크루 구성 설정하기
이제 크루를 구성하여 모든 것을 하나로 모을 시간입니다. 크루는 에이전트들이 함께 작업을 완료하는 방식을 조율하는 컨테이너 역할을 합니다.
`crew.py` 파일을 다음과 같이 수정해보겠습니다:
```python
# src/research_crew/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ResearchCrew():
"""Research crew for comprehensive topic analysis and reporting"""
agents: List[BaseAgent]
tasks: List[Task]
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()]
)
@agent
def analyst(self) -> Agent:
return Agent(
config=self.agents_config['analyst'], # type: ignore[index]
verbose=True
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config['research_task'] # type: ignore[index]
)
@task
def analysis_task(self) -> Task:
return Task(
config=self.tasks_config['analysis_task'], # type: ignore[index]
output_file='output/report.md'
)
@crew
def crew(self) -> Crew:
"""Creates the research crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
이 코드에서는 다음을 수행합니다:
1. researcher 에이전트를 생성하고, SerperDevTool을 장착하여 웹 검색 기능을 추가합니다.
2. analyst 에이전트를 생성합니다.
3. research와 analysis 작업(task)을 설정합니다.
4. 크루가 작업을 순차적으로 수행하도록 설정합니다(analyst가 researcher가 끝날 때까지 대기).
여기서 마법이 일어납니다. 몇 줄의 코드만으로도, 특화된 에이전트들이 조율된 프로세스 내에서 협업하는 협동 AI 시스템을 정의할 수 있습니다.
## 6단계: 메인 스크립트 설정
이제 우리 crew를 실행할 메인 스크립트를 설정해 보겠습니다. 이곳에서 crew가 리서치할 구체적인 주제를 지정합니다.
```python
#!/usr/bin/env python
# src/research_crew/main.py
import os
from research_crew.crew import ResearchCrew
# Create output directory if it doesn't exist
os.makedirs('output', exist_ok=True)
def run():
"""
Run the research crew.
"""
inputs = {
'topic': 'Artificial Intelligence in Healthcare'
}
# Create and run the crew
result = ResearchCrew().crew().kickoff(inputs=inputs)
# Print the result
print("\n\n=== FINAL REPORT ===\n\n")
print(result.raw)
print("\n\nReport has been saved to output/report.md")
if __name__ == "__main__":
run()
```
이 스크립트는 환경을 준비하고, 리서치 주제를 지정하며, crew의 작업을 시작합니다. CrewAI의 강력함은 이 코드가 얼마나 간단한지에서 드러납니다. 여러 AI 에이전트를 관리하는 모든 복잡함이 프레임워크에 의해 처리됩니다.
## 7단계: 환경 변수 설정하기
프로젝트 루트에 `.env` 파일을 생성하고 API 키를 입력하세요:
```sh
SERPER_API_KEY=your_serper_api_key
# Add your provider's API key here too.
```
선택한 provider를 구성하는 방법에 대한 자세한 내용은 [LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)를 참고하세요. Serper API 키는 [Serper.dev](https://serper.dev/)에서 받을 수 있습니다.
## 8단계: 필수 종속성 설치
CrewAI CLI를 사용하여 필요한 종속성을 설치하세요:
```bash
crewai install
```
이 명령어는 다음을 수행합니다:
1. 프로젝트 구성에서 종속성을 읽어옵니다
2. 필요하다면 가상 환경을 생성합니다
3. 모든 필수 패키지를 설치합니다
## 9단계: Crew 실행하기
이제 흥미로운 순간입니다 - crew를 실행하여 AI 협업이 어떻게 이루어지는지 직접 확인해보세요!
```bash
crewai run
```
이 명령어를 실행하면 crew가 즉시 작동하는 모습을 볼 수 있습니다. researcher는 지정된 주제에 대한 정보를 수집하고, analyst가 그 연구를 바탕으로 종합 보고서를 작성합니다. 에이전트들의 사고 과정, 행동, 결과물이 실시간으로 표시되며 서로 협력하여 작업을 완수하는 모습을 확인할 수 있습니다.
## 10단계: 결과물 검토
crew가 작업을 완료하면, 최종 보고서는 `output/report.md` 파일에서 확인할 수 있습니다. 보고서에는 다음과 같은 내용이 포함됩니다:
1. 요약 보고서
2. 주제에 대한 상세 정보
3. 분석 및 인사이트
4. 권장사항 또는 향후 고려사항
지금까지 달성한 것을 잠시 돌아보세요. 여러분은 여러 AI 에이전트가 협업하여 각자의 전문적인 기술을 발휘함으로써, 단일 에이전트가 혼자서 이루어낼 수 있는 것보다 더 뛰어난 결과를 만들어내는 시스템을 구축한 것입니다.
## 기타 CLI 명령어 탐색
CrewAI는 crew 작업을 위한 몇 가지 유용한 CLI 명령어를 추가로 제공합니다:
```bash
# 모든 사용 가능한 명령어 보기
crewai --help
# crew 실행
crewai run
# crew 테스트
crewai test
# crew 메모리 초기화
crewai reset-memories
# 특정 task에서 재실행
crewai replay -t <task_id>
```
## 가능한 것의 예술: 당신의 첫 crew를 넘어서
이 가이드에서 구축한 것은 시작에 불과합니다. 여러분이 배운 기술과 패턴은 점점 더 정교한 AI 시스템을 만드는 데 적용할 수 있습니다. 다음은 이 기본 research crew를 확장할 수 있는 몇 가지 방법입니다:
### 팀원 확장하기
더 전문화된 에이전트를 팀원으로 추가할 수 있습니다:
- 연구 결과를 검증하는 **팩트체커**
- 차트와 그래프를 만드는 **데이터 시각화 담당자**
- 특정 분야에 전문 지식을 가진 **도메인 전문가**
- 분석의 약점을 파악하는 **비평가**
### 도구 및 기능 추가
에이전트에 추가 도구를 통해 기능을 확장할 수 있습니다:
- 실시간 연구를 위한 웹 브라우징 도구
- 데이터 분석을 위한 CSV/데이터베이스 도구
- 데이터 처리를 위한 코드 실행 도구
- 외부 서비스와의 API 연결
### 더 복잡한 워크플로우 생성
더 정교한 프로세스를 구현할 수 있습니다:
- 매니저 에이전트가 워커 에이전트에게 위임하는 계층적 프로세스
- 반복적 피드백 루프로 정제하는 반복 프로세스
- 여러 에이전트가 동시에 작업하는 병렬 프로세스
- 중간 결과에 따라 적응하는 동적 프로세스
### 다양한 도메인에 적용하기
동일한 패턴은 다음과 같은 분야에서 crew를 구성하는 데 적용할 수 있습니다:
- **콘텐츠 제작**: 작가, 에디터, 팩트체커, 디자이너가 함께 협업
- **고객 서비스**: 분류 담당자, 전문가, 품질 관리자가 함께 협업
- **제품 개발**: 연구원, 디자이너, 기획자가 협업
- **데이터 분석**: 데이터 수집가, 분석가, 시각화 전문가
## 다음 단계
이제 첫 crew를 구축했으니, 다음과 같은 작업을 시도해 볼 수 있습니다:
1. 다양한 에이전트 구성 및 성격을 실험해 보세요
2. 더 복잡한 작업 구조와 워크플로우를 시도해 보세요
3. 맞춤 도구를 구현하여 에이전트에게 새로운 기능을 제공하세요
4. crew를 다양한 주제나 문제 도메인에 적용해 보세요
5. [CrewAI Flows](/ko/guides/flows/first-flow)를 탐색하여 절차적 프로그래밍을 활용한 더 고급 워크플로우를 경험해 보세요
<Check>
축하합니다! 이제 주어진 모든 주제를 조사하고 분석할 수 있는 첫 번째 CrewAI crew를 성공적으로 구축하셨습니다. 이 기본적인 경험은 협업 인텔리전스를 통해 복잡하고 다단계의 문제를 해결할 수 있는 점점 더 정교한 AI 시스템을 제작하는 데 필요한 역량을 갖추는 데 도움이 됩니다.
</Check>

View File

@@ -0,0 +1,612 @@
---
title: 첫 Flow 빌드하기
description: 정밀한 실행 제어가 가능한 구조화된 이벤트 기반 워크플로우를 만드는 방법을 배웁니다.
icon: diagram-project
---
## Flows로 AI 워크플로우 제어하기
CrewAI Flows는 AI 오케스트레이션의 새로운 수준을 제공합니다. 즉, AI agent crew의 협업 능력과 절차적 프로그래밍의 정밀성 및 유연성을 결합합니다. crew가 agent 협업에서 탁월하다면, flow는 AI 시스템의 다양한 구성요소가 어떻게 그리고 언제 상호작용하는지에 대해 세밀하게 제어할 수 있게 해줍니다.
이 가이드에서는 원하는 주제에 대한 포괄적인 학습 가이드를 생성하는 강력한 CrewAI Flow를 만드는 과정을 소개합니다. 이 튜토리얼을 통해 Flow가 일반 코드, 직접적인 LLM 호출, crew 기반 처리 등을 결합하여 AI 워크플로우에 구조적이고 이벤트 기반의 제어를 제공하는 방법을 시연할 것입니다.
### 플로우의 강력한 점
플로우를 통해 다음과 같은 작업을 할 수 있습니다:
1. **다양한 AI 상호작용 패턴 결합** - 복잡한 협업 작업에는 crew를 사용하고, 더 단순한 작업에는 직접적인 LLM 호출과 절차적 논리에는 일반 코드를 사용하세요.
2. **이벤트 기반 시스템 구축** - 구성 요소가 특정 이벤트와 데이터 변경에 어떻게 반응할지 정의할 수 있습니다.
3. **구성 요소 간 상태 유지** - 애플리케이션의 다양한 부분 간에 데이터를 공유하고 변환할 수 있습니다.
4. **외부 시스템과 통합** - 데이터베이스, API, 사용자 인터페이스와 같은 외부 시스템과 AI 워크플로우를 원활하게 연동할 수 있습니다.
5. **복잡한 실행 경로 생성** - 조건부 분기, 병렬 처리 및 동적인 워크플로우를 설계할 수 있습니다.
### 무엇을 구축하고 배우게 될까요
이 가이드가 끝나면 여러분은 다음을 달성할 수 있습니다:
1. **사용자 입력, AI 계획, 그리고 멀티 에이전트 콘텐츠 생성이 결합된 정교한 콘텐츠 생성 시스템을 구축**했습니다.
2. **시스템의 다양한 구성 요소 간 정보 흐름을 오케스트레이션(조율)**했습니다.
3. **이전 단계의 완료에 따라 각 단계가 반응하는 이벤트 기반 아키텍처를 구현**했습니다.
4. **더 복잡한 AI 애플리케이션을 확장하고 맞춤화할 수 있는 기반을 구축**했습니다.
이번 가이드의 creator flow는 다음과 같은 훨씬 더 발전된 애플리케이션에 적용할 수 있는 기본 패턴을 보여줍니다:
- 여러 전문화된 하위 시스템을 결합하는 대화형 AI assistant
- AI 기반 변환을 포함한 복잡한 데이터 처리 파이프라인
- 외부 서비스 및 API와 통합되는 자율적 에이전트
- 인간이 개입하는 프로세스를 포함한 다단계 의사결정 시스템
함께 여러분의 첫 번째 flow를 만들어 봅시다!
## 사전 준비 사항
시작하기 전에 다음을 확인하세요:
1. [설치 가이드](/ko/installation)에 따라 CrewAI를 설치했는지 확인하십시오.
2. [LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)에 따라 환경에 LLM API 키를 설정했는지 확인하십시오.
3. Python에 대한 기본적인 이해
## 1단계: 새로운 CrewAI Flow 프로젝트 생성
먼저, CLI를 사용하여 새로운 CrewAI Flow 프로젝트를 생성해봅시다. 이 명령어는 필요한 모든 디렉터리와 템플릿 파일이 포함된 기본 프로젝트 구조를 만들어줍니다.
```bash
crewai create flow guide_creator_flow
cd guide_creator_flow
```
이렇게 하면 flow에 필요한 기본 구조를 가진 프로젝트가 생성됩니다.
<Frame caption="CrewAI Framework 개요">
<img src="/images/flows.png" alt="CrewAI Framework 개요" />
</Frame>
## 2단계: 프로젝트 구조 이해하기
생성된 프로젝트는 다음과 같은 구조를 가지고 있습니다. 잠시 시간을 내어 이 구조에 익숙해지세요. 구조를 이해하면 앞으로 더 복잡한 flow를 만드는 데 도움이 됩니다.
```
guide_creator_flow/
├── .gitignore
├── pyproject.toml
├── README.md
├── .env
├── main.py
├── crews/
│ └── poem_crew/
│ ├── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ └── poem_crew.py
└── tools/
└── custom_tool.py
```
이 구조는 flow의 다양한 구성 요소를 명확하게 분리해줍니다:
- `main.py` 파일의 main flow 로직
- `crews` 디렉터리의 특화된 crew들
- `tools` 디렉터리의 custom tool들
이제 이 구조를 수정하여 guide creator flow를 만들 것입니다. 이 flow는 포괄적인 학습 가이드 생성을 조직하는 역할을 합니다.
## 3단계: Content Writer Crew 추가
우리 flow에는 콘텐츠 생성 프로세스를 처리할 전문화된 crew가 필요합니다. CrewAI CLI를 사용하여 content writer crew를 추가해봅시다:
```bash
crewai flow add-crew content-crew
```
이 명령어는 자동으로 crew에 필요한 디렉터리와 템플릿 파일을 생성합니다. content writer crew는 가이드의 각 섹션을 작성하고 검토하는 역할을 담당하며, 메인 애플리케이션에 의해 조율되는 전체 flow 내에서 작업하게 됩니다.
## 4단계: 콘텐츠 작가 Crew 구성
이제 콘텐츠 작가 crew를 위해 생성된 파일을 수정해보겠습니다. 우리는 가이드의 고품질 콘텐츠를 만들기 위해 협업하는 두 명의 전문 에이전트 - 작가와 리뷰어 - 를 설정할 것입니다.
1. 먼저, 에이전트 구성 파일을 업데이트하여 콘텐츠 제작 팀을 정의합니다:
`llm`을 사용 중인 공급자로 설정해야 함을 기억하세요.
```yaml
# src/guide_creator_flow/crews/content_crew/config/agents.yaml
content_writer:
role: >
교육 콘텐츠 작가
goal: >
할당된 주제를 철저히 설명하고 독자에게 소중한 통찰력을 제공하는 흥미롭고 유익한 콘텐츠를 제작합니다
backstory: >
당신은 명확하고 흥미로운 콘텐츠를 만드는 데 능숙한 교육 전문 작가입니다. 복잡한 개념도 쉽게 설명하며,
정보를 독자가 이해하기 쉽게 조직할 수 있습니다.
llm: provider/model-id # 예: openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
content_reviewer:
role: >
교육 콘텐츠 검토자 및 에디터
goal: >
콘텐츠가 정확하고, 포괄적이며, 잘 구조화되어 있고, 이전에 작성된 섹션과의 일관성을 유지하도록 합니다
backstory: >
당신은 수년간 교육 콘텐츠를 검토해 온 꼼꼼한 에디터입니다. 세부 사항, 명확성, 일관성에 뛰어나며,
원 저자의 목소리를 유지하면서도 콘텐츠의 품질을 향상시키는 데 능숙합니다.
llm: provider/model-id # 예: openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
```
이 에이전트 정의는 AI 에이전트가 콘텐츠 제작을 접근하는 전문화된 역할과 관점을 구성합니다. 각 에이전트가 뚜렷한 목적과 전문성을 지니고 있음을 확인하세요.
2. 다음으로, 작업 구성 파일을 업데이트하여 구체적인 작성 및 검토 작업을 정의합니다:
```yaml
# src/guide_creator_flow/crews/content_crew/config/tasks.yaml
write_section_task:
description: >
주제에 대한 포괄적인 섹션을 작성하세요: "{section_title}"
섹션 설명: {section_description}
대상 독자: {audience_level} 수준 학습자
작성 시 아래 사항을 반드시 지켜주세요:
1. 섹션 주제에 대한 간략한 소개로 시작
2. 모든 주요 개념을 예시와 함께 명확하게 설명
3. 적절하다면 실용적인 활용 사례나 연습문제 포함
4. 주요 포인트 요약으로 마무리
5. 대략 500-800단어 분량
콘텐츠는 적절한 제목, 목록, 강조를 포함해 Markdown 형식으로 작성하세요.
이전에 작성된 섹션:
{previous_sections}
반드시 콘텐츠가 이전에 쓴 섹션과 일관성을 유지하고 앞에서 설명된 개념을 바탕으로 작성되도록 하세요.
expected_output: >
주제를 철저히 설명하고 대상 독자에게 적합한, 구조가 잘 잡힌 Markdown 형식의 포괄적 섹션
agent: content_writer
review_section_task:
description: >
아래 "{section_title}" 섹션의 내용을 검토하고 개선하세요:
{draft_content}
대상 독자: {audience_level} 수준 학습자
이전에 작성된 섹션:
{previous_sections}
검토 시 아래 사항을 반드시 지켜주세요:
1. 문법/철자 오류 수정
2. 명확성 및 가독성 향상
3. 내용이 포괄적이고 정확한지 확인
4. 이전에 쓴 섹션과 일관성 유지
5. 구조와 흐름 강화
6. 누락된 핵심 정보 추가
개선된 버전의 섹션을 Markdown 형식으로 제공하세요.
expected_output: >
원래의 구조를 유지하면서도 명확성, 정확성, 일관성을 향상시킨 세련된 개선본
agent: content_reviewer
context:
- write_section_task
```
이 작업 정의는 에이전트에게 세부적인 지침을 제공하여 우리의 품질 기준에 부합하는 콘텐츠를 생산하게 합니다. review 작업의 `context` 파라미터를 통해 리뷰어가 작가의 결과물에 접근할 수 있는 워크플로우가 생성됨에 주의하세요.
3. 이제 crew 구현 파일을 업데이트하여 에이전트와 작업이 어떻게 연동되는지 정의합니다:
```python
# src/guide_creator_flow/crews/content_crew/content_crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ContentCrew():
"""Content writing crew"""
agents: List[BaseAgent]
tasks: List[Task]
@agent
def content_writer(self) -> Agent:
return Agent(
config=self.agents_config['content_writer'], # type: ignore[index]
verbose=True
)
@agent
def content_reviewer(self) -> Agent:
return Agent(
config=self.agents_config['content_reviewer'], # type: ignore[index]
verbose=True
)
@task
def write_section_task(self) -> Task:
return Task(
config=self.tasks_config['write_section_task'] # type: ignore[index]
)
@task
def review_section_task(self) -> Task:
return Task(
config=self.tasks_config['review_section_task'], # type: ignore[index]
context=[self.write_section_task()]
)
@crew
def crew(self) -> Crew:
"""Creates the content writing crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
이 crew 정의는 에이전트와 작업 간의 관계를 설정하여, 콘텐츠 작가가 초안을 작성하고 리뷰어가 이를 개선하는 순차적 과정을 만듭니다. 이 crew는 독립적으로도 작동할 수 있지만, 우리의 플로우에서 더 큰 시스템의 일부로 오케스트레이션될 예정입니다.
## 5단계: 플로우(Flow) 생성
이제 가장 흥미로운 부분입니다 - 전체 가이드 생성 과정을 오케스트레이션할 플로우를 만드는 단계입니다. 이곳에서 우리는 일반 Python 코드, 직접적인 LLM 호출, 그리고 우리의 컨텐츠 제작 crew를 결합하여 일관된 시스템으로 만듭니다.
우리의 플로우는 다음과 같은 일을 수행합니다:
1. 주제와 대상 독자 수준에 대한 사용자 입력을 받습니다.
2. 구조화된 가이드 개요를 만들기 위해 직접 LLM 호출을 합니다.
3. 컨텐츠 writer crew를 사용하여 각 섹션을 순차적으로 처리합니다.
4. 모든 내용을 결합하여 최종 종합 문서를 완성합니다.
`main.py` 파일에 우리의 플로우를 생성해봅시다:
```python
#!/usr/bin/env python
import json
import os
from typing import List, Dict
from pydantic import BaseModel, Field
from crewai import LLM
from crewai.flow.flow import Flow, listen, start
from guide_creator_flow.crews.content_crew.content_crew import ContentCrew
# Define our models for structured data
class Section(BaseModel):
title: str = Field(description="Title of the section")
description: str = Field(description="Brief description of what the section should cover")
class GuideOutline(BaseModel):
title: str = Field(description="Title of the guide")
introduction: str = Field(description="Introduction to the topic")
target_audience: str = Field(description="Description of the target audience")
sections: List[Section] = Field(description="List of sections in the guide")
conclusion: str = Field(description="Conclusion or summary of the guide")
# Define our flow state
class GuideCreatorState(BaseModel):
topic: str = ""
audience_level: str = ""
guide_outline: GuideOutline = None
sections_content: Dict[str, str] = {}
class GuideCreatorFlow(Flow[GuideCreatorState]):
"""Flow for creating a comprehensive guide on any topic"""
@start()
def get_user_input(self):
"""Get input from the user about the guide topic and audience"""
print("\n=== Create Your Comprehensive Guide ===\n")
# Get user input
self.state.topic = input("What topic would you like to create a guide for? ")
# Get audience level with validation
while True:
audience = input("Who is your target audience? (beginner/intermediate/advanced) ").lower()
if audience in ["beginner", "intermediate", "advanced"]:
self.state.audience_level = audience
break
print("Please enter 'beginner', 'intermediate', or 'advanced'")
print(f"\nCreating a guide on {self.state.topic} for {self.state.audience_level} audience...\n")
return self.state
@listen(get_user_input)
def create_guide_outline(self, state):
"""Create a structured outline for the guide using a direct LLM call"""
print("Creating guide outline...")
# Initialize the LLM
llm = LLM(model="openai/gpt-4o-mini", response_format=GuideOutline)
# Create the messages for the outline
messages = [
{"role": "system", "content": "You are a helpful assistant designed to output JSON."},
{"role": "user", "content": f"""
Create a detailed outline for a comprehensive guide on "{state.topic}" for {state.audience_level} level learners.
The outline should include:
1. A compelling title for the guide
2. An introduction to the topic
3. 4-6 main sections that cover the most important aspects of the topic
4. A conclusion or summary
For each section, provide a clear title and a brief description of what it should cover.
"""}
]
# Make the LLM call with JSON response format
response = llm.call(messages=messages)
# Parse the JSON response
outline_dict = json.loads(response)
self.state.guide_outline = GuideOutline(**outline_dict)
# Ensure output directory exists before saving
os.makedirs("output", exist_ok=True)
# Save the outline to a file
with open("output/guide_outline.json", "w") as f:
json.dump(outline_dict, f, indent=2)
print(f"Guide outline created with {len(self.state.guide_outline.sections)} sections")
return self.state.guide_outline
@listen(create_guide_outline)
def write_and_compile_guide(self, outline):
"""Write all sections and compile the guide"""
print("Writing guide sections and compiling...")
completed_sections = []
# Process sections one by one to maintain context flow
for section in outline.sections:
print(f"Processing section: {section.title}")
# Build context from previous sections
previous_sections_text = ""
if completed_sections:
previous_sections_text = "# Previously Written Sections\n\n"
for title in completed_sections:
previous_sections_text += f"## {title}\n\n"
previous_sections_text += self.state.sections_content.get(title, "") + "\n\n"
else:
previous_sections_text = "No previous sections written yet."
# Run the content crew for this section
result = ContentCrew().crew().kickoff(inputs={
"section_title": section.title,
"section_description": section.description,
"audience_level": self.state.audience_level,
"previous_sections": previous_sections_text,
"draft_content": ""
})
# Store the content
self.state.sections_content[section.title] = result.raw
completed_sections.append(section.title)
print(f"Section completed: {section.title}")
# Compile the final guide
guide_content = f"# {outline.title}\n\n"
guide_content += f"## Introduction\n\n{outline.introduction}\n\n"
# Add each section in order
for section in outline.sections:
section_content = self.state.sections_content.get(section.title, "")
guide_content += f"\n\n{section_content}\n\n"
# Add conclusion
guide_content += f"## Conclusion\n\n{outline.conclusion}\n\n"
# Save the guide
with open("output/complete_guide.md", "w") as f:
f.write(guide_content)
print("\nComplete guide compiled and saved to output/complete_guide.md")
return "Guide creation completed successfully"
def kickoff():
"""Run the guide creator flow"""
GuideCreatorFlow().kickoff()
print("\n=== Flow Complete ===")
print("Your comprehensive guide is ready in the output directory.")
print("Open output/complete_guide.md to view it.")
def plot():
"""Generate a visualization of the flow"""
flow = GuideCreatorFlow()
flow.plot("guide_creator_flow")
print("Flow visualization saved to guide_creator_flow.html")
if __name__ == "__main__":
kickoff()
```
이 플로우에서 일어나는 과정을 분석해봅시다:
1. 구조화된 데이터에 대한 Pydantic 모델을 정의하여 타입 안전성과 명확한 데이터 표현을 보장합니다.
2. 플로우 단계별로 데이터를 유지하기 위한 state 클래스를 생성합니다.
3. 세 가지 주요 플로우 단계를 구현합니다:
- `@start()` 데코레이터로 사용자 입력을 받습니다.
- 직접 LLM 호출로 가이드 개요를 생성합니다.
- content crew로 각 섹션을 처리합니다.
4. `@listen()` 데코레이터를 활용해 단계 간 이벤트 기반 관계를 설정합니다.
이것이 바로 flows의 힘입니다 - 다양한 처리 유형(사용자 상호작용, 직접적인 LLM 호출, crew 기반 작업)을 하나의 일관된 이벤트 기반 시스템으로 결합할 수 있습니다.
## 6단계: 환경 변수 설정하기
프로젝트 루트에 `.env` 파일을 생성하고 API 키를 입력하세요. 공급자 구성에 대한 자세한 내용은 [LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)를 참고하세요.
```sh .env
OPENAI_API_KEY=your_openai_api_key
# or
GEMINI_API_KEY=your_gemini_api_key
# or
ANTHROPIC_API_KEY=your_anthropic_api_key
```
## 7단계: 의존성 설치
필수 의존성을 설치합니다:
```bash
crewai install
```
## 8단계: Flow 실행하기
이제 여러분의 flow가 실제로 작동하는 모습을 볼 차례입니다! CrewAI CLI를 사용하여 flow를 실행하세요:
```bash
crewai flow kickoff
```
이 명령어를 실행하면 flow가 다음과 같이 작동하는 것을 확인할 수 있습니다:
1. 주제와 대상 수준을 입력하라는 메시지가 표시됩니다.
2. 가이드의 체계적인 개요를 생성합니다.
3. 각 섹션을 처리할 때 content writer와 reviewer가 협업합니다.
4. 마지막으로 모든 내용을 종합하여 완성도 높은 가이드를 만듭니다.
이는 여러 구성요소(인공지능 및 비인공지능 모두)가 포함된 복잡한 프로세스를 flows가 어떻게 조정할 수 있는지 보여줍니다.
## 9단계: Flow 시각화하기
flow의 강력한 기능 중 하나는 구조를 시각화할 수 있다는 점입니다.
```bash
crewai flow plot
```
이 명령은 flow의 구조를 보여주는 HTML 파일을 생성하며, 각 단계 간의 관계와 그 사이에 흐르는 데이터를 확인할 수 있습니다. 이러한 시각화는 복잡한 flow를 이해하고 디버깅하는 데 매우 유용합니다.
## 10단계: 출력물 검토하기
flow가 완료되면 `output` 디렉토리에서 두 개의 파일을 찾을 수 있습니다:
1. `guide_outline.json`: 가이드의 구조화된 개요가 포함되어 있습니다
2. `complete_guide.md`: 모든 섹션이 포함된 종합적인 가이드입니다
이 파일들을 잠시 검토하고 여러분이 구축한 시스템을 되돌아보세요. 이 시스템은 사용자 입력, 직접적인 AI 상호작용, 협업 에이전트 작업을 결합하여 복잡하고 고품질의 결과물을 만들어냅니다.
## 가능한 것의 예술: 첫 번째 Flow 그 이상
이 가이드에서 배운 내용은 훨씬 더 정교한 AI 시스템을 만드는 데 기반이 됩니다. 다음은 이 기본 flow를 확장할 수 있는 몇 가지 방법입니다:
### 사용자 상호작용 향상
더욱 인터랙티브한 플로우를 만들 수 있습니다:
- 입력 및 출력을 위한 웹 인터페이스
- 실시간 진행 상황 업데이트
- 인터랙티브한 피드백 및 개선 루프
- 다단계 사용자 상호작용
### 추가 처리 단계 추가하기
다음과 같은 추가 단계로 flow를 확장할 수 있습니다:
- 개요 작성 전 사전 리서치
- 일러스트를 위한 이미지 생성
- 기술 가이드용 코드 스니펫 생성
- 최종 품질 보증 및 사실 확인
### 더 복잡한 Flows 생성하기
더 정교한 flow 패턴을 구현할 수 있습니다:
- 사용자 선호도나 콘텐츠 유형에 따른 조건 분기
- 독립적인 섹션의 병렬 처리
- 피드백과 함께하는 반복적 개선 루프
- 외부 API 및 서비스와의 통합
### 다양한 도메인에 적용하기
동일한 패턴을 사용하여 다음과 같은 flow를 만들 수 있습니다:
- **대화형 스토리텔링**: 사용자 입력을 바탕으로 개인화된 이야기를 생성
- **비즈니스 인텔리전스**: 데이터를 처리하고, 인사이트를 도출하며, 리포트를 생성
- **제품 개발**: 아이디어 구상, 디자인, 기획을 지원
- **교육 시스템**: 개인화된 학습 경험을 제공
## 주요 특징 시연
이 guide creator flow에서는 CrewAI의 여러 강력한 기능을 시연합니다:
1. **사용자 상호작용**: flow는 사용자로부터 직접 입력을 수집합니다
2. **직접적인 LLM 호출**: 효율적이고 단일 목적의 AI 상호작용을 위해 LLM 클래스를 사용합니다
3. **Pydantic을 통한 구조화된 데이터**: 타입 안정성을 보장하기 위해 Pydantic 모델을 사용합니다
4. **컨텍스트를 활용한 순차 처리**: 섹션을 순서대로 작성하면서 이전 섹션을 컨텍스트로 제공합니다
5. **멀티 에이전트 crew**: 콘텐츠 생성을 위해 특화된 에이전트(writer 및 reviewer)를 활용합니다
6. **상태 관리**: 프로세스의 다양한 단계에 걸쳐 상태를 유지합니다
7. **이벤트 기반 아키텍처**: 이벤트에 응답하기 위해 `@listen` 데코레이터를 사용합니다
## 플로우 구조 이해하기
플로우의 주요 구성 요소를 분해하여 자신만의 플로우를 만드는 방법을 이해할 수 있도록 도와드리겠습니다:
### 1. 직접 LLM 호출
Flow를 사용하면 간단하고 구조화된 응답이 필요할 때 언어 모델에 직접 호출할 수 있습니다:
```python
llm = LLM(
model="model-id-here", # gpt-4o, gemini-2.0-flash, anthropic/claude...
response_format=GuideOutline
)
response = llm.call(messages=messages)
```
특정하고 구조화된 출력이 필요할 때 crew를 사용하는 것보다 더 효율적입니다.
### 2. 이벤트 기반 아키텍처
Flows는 데코레이터를 사용하여 컴포넌트 간의 관계를 설정합니다:
```python
@start()
def get_user_input(self):
# First step in the flow
# ...
@listen(get_user_input)
def create_guide_outline(self, state):
# This runs when get_user_input completes
# ...
```
이렇게 하면 애플리케이션에 명확하고 선언적인 구조가 만들어집니다.
### 3. 상태 관리
flow는 단계 간 상태를 유지하여 데이터를 쉽게 공유할 수 있습니다:
```python
class GuideCreatorState(BaseModel):
topic: str = ""
audience_level: str = ""
guide_outline: GuideOutline = None
sections_content: Dict[str, str] = {}
```
이 방식은 flow 전반에 걸쳐 데이터를 추적하고 변환하는 타입 안전(type-safe)한 방법을 제공합니다.
### 4. Crew 통합
Flow는 복잡한 협업 작업을 위해 crew와 원활하게 통합될 수 있습니다:
```python
result = ContentCrew().crew().kickoff(inputs={
"section_title": section.title,
# ...
})
```
이를 통해 애플리케이션의 각 부분에 적합한 도구를 사용할 수 있습니다. 단순한 작업에는 직접적인 LLM 호출을, 복잡한 협업에는 crew를 사용할 수 있습니다.
## 다음 단계
이제 첫 번째 flow를 구축했으니 다음을 시도해 볼 수 있습니다:
1. 더 복잡한 flow 구조와 패턴을 실험해 보세요.
2. `@router()`를 사용하여 flow에서 조건부 분기를 만들어 보세요.
3. 더 복잡한 병렬 실행을 위해 `and_` 및 `or_` 함수를 탐색해 보세요.
4. flow를 외부 API, 데이터베이스 또는 사용자 인터페이스에 연결해 보세요.
5. 여러 전문화된 crew를 하나의 flow에서 결합해 보세요.
<Check>
축하합니다! 정규 코드, 직접적인 LLM 호출, crew 기반 처리를 결합하여 포괄적인 가이드를 생성하는 첫 번째 CrewAI Flow를 성공적으로 구축하셨습니다. 이러한 기초적인 역량을 바탕으로 절차적 제어와 협업적 인텔리전스를 결합하여 복잡하고 다단계의 문제를 해결할 수 있는 점점 더 정교한 AI 애플리케이션을 만들 수 있습니다.
</Check>

View File

@@ -0,0 +1,770 @@
---
title: 플로우 상태 관리 마스터하기
description: 견고한 AI 애플리케이션 구축을 위한 CrewAI 플로우에서 상태를 관리, 유지 및 활용하는 종합 가이드입니다.
icon: diagram-project
---
## 플로우에서 State의 힘 이해하기
State 관리는 모든 고급 AI 워크플로우의 중추입니다. CrewAI Flows에서 state 시스템은 컨텍스트를 유지하고, 단계 간 데이터를 공유하며, 복잡한 애플리케이션 로직을 구축할 수 있도록 해줍니다. State 관리에 능숙해지는 것은 신뢰할 수 있고, 유지보수가 용이하며, 강력한 AI 애플리케이션을 만들기 위해 필수적입니다.
이 가이드는 CrewAI Flows에서 state를 관리하는 데 꼭 알아야 할 기본 개념부터 고급 기법까지, 실용적인 코드 예제와 함께 단계별로 안내합니다.
### 상태 관리가 중요한 이유
효과적인 상태 관리는 다음을 가능하게 합니다:
1. **실행 단계 간의 컨텍스트 유지** - 워크플로의 다양한 단계 간에 정보를 원활하게 전달할 수 있습니다.
2. **복잡한 조건부 논리 구성** - 누적된 데이터를 기반으로 의사 결정을 내릴 수 있습니다.
3. **지속적인 애플리케이션 생성** - 워크플로 진행 상황을 저장하고 복원할 수 있습니다.
4. **에러를 우아하게 처리** - 더 견고한 애플리케이션을 위한 복구 패턴을 구현할 수 있습니다.
5. **애플리케이션 확장** - 적절한 데이터 조직을 통해 복잡한 워크플로를 지원할 수 있습니다.
6. **대화형 애플리케이션 활성화** - 컨텍스트 기반 AI 상호작용을 위해 대화 내역을 저장하고 접근할 수 있습니다.
이러한 기능을 효과적으로 활용하는 방법을 살펴보겠습니다.
## 상태 관리 기본 사항
### Flow 상태 라이프사이클
CrewAI Flow에서 상태는 예측 가능한 라이프사이클을 따릅니다:
1. **초기화** - flow가 생성될 때, 상태는 초기화됩니다(빈 딕셔너리 또는 Pydantic 모델 인스턴스로)
2. **수정** - flow 메서드는 실행되는 동안 상태에 접근하고 이를 수정합니다
3. **전달** - 상태는 flow 메서드들 사이에 자동으로 전달됩니다
4. **영속화** (선택 사항) - 상태는 스토리지에 저장될 수 있고 나중에 다시 불러올 수 있습니다
5. **완료** - 최종 상태는 모든 실행된 메서드의 누적 변경 사항을 반영합니다
이 라이프사이클을 이해하는 것은 효과적인 flow를 설계하는 데 매우 중요합니다.
### 상태 관리의 두 가지 접근 방식
CrewAI에서는 흐름에서 상태를 관리하는 두 가지 방법을 제공합니다:
1. **비구조적 상태** - 유연성을 위해 딕셔너리와 유사한 객체 사용
2. **구조적 상태** - 타입 안전성과 검증을 위해 Pydantic 모델 사용
각 접근 방식을 자세히 살펴보겠습니다.
## 비구조적 상태 관리
비구조적 상태는 사전(dictionary)과 유사한 방식을 사용하여, 단순한 애플리케이션에 유연성과 단순성을 제공합니다.
### 작동 방식
비구조화된 상태의 경우:
- `self.state`를 통해 상태에 접근하며, 이는 딕셔너리처럼 동작합니다
- 언제든지 키를 자유롭게 추가, 수정, 삭제할 수 있습니다
- 모든 상태는 모든 flow 메서드에서 자동으로 사용할 수 있습니다
### 기본 예제
다음은 비구조적 상태 관리를 보여주는 간단한 예제입니다:
```python
from crewai.flow.flow import Flow, listen, start
class UnstructuredStateFlow(Flow):
@start()
def initialize_data(self):
print("Initializing flow data")
# Add key-value pairs to state
self.state["user_name"] = "Alex"
self.state["preferences"] = {
"theme": "dark",
"language": "English"
}
self.state["items"] = []
# The flow state automatically gets a unique ID
print(f"Flow ID: {self.state['id']}")
return "Initialized"
@listen(initialize_data)
def process_data(self, previous_result):
print(f"Previous step returned: {previous_result}")
# Access and modify state
user = self.state["user_name"]
print(f"Processing data for {user}")
# Add items to a list in state
self.state["items"].append("item1")
self.state["items"].append("item2")
# Add a new key-value pair
self.state["processed"] = True
return "Processed"
@listen(process_data)
def generate_summary(self, previous_result):
# Access multiple state values
user = self.state["user_name"]
theme = self.state["preferences"]["theme"]
items = self.state["items"]
processed = self.state.get("processed", False)
summary = f"User {user} has {len(items)} items with {theme} theme. "
summary += "Data is processed." if processed else "Data is not processed."
return summary
# Run the flow
flow = UnstructuredStateFlow()
result = flow.kickoff()
print(f"Final result: {result}")
print(f"Final state: {flow.state}")
```
### 비구조적 상태를 사용할 때
비구조적 상태는 다음과 같은 경우에 이상적입니다:
- 빠른 프로토타이핑 및 간단한 플로우
- 동적으로 변화하는 상태 요구
- 구조가 사전에 알려지지 않을 수 있는 경우
- 간단한 상태 요구가 있는 플로우
비구조적 상태는 유연하지만, 타입 검사 및 스키마 검증이 없기 때문에 복잡한 애플리케이션에서 오류가 발생할 수 있습니다.
## 구조화된 상태 관리
구조화된 상태는 Pydantic 모델을 사용하여 flow의 상태에 대한 스키마를 정의함으로써 타입 안전성, 검증, 그리고 더 나은 개발자 경험을 제공합니다.
### 작동 방식
구조화된 상태에서는:
- 상태 구조를 나타내는 Pydantic 모델을 정의합니다.
- 이 모델 타입을 유형 매개변수로 Flow 클래스에 전달합니다.
- `self.state`를 통해 상태에 접근할 수 있으며, 이는 Pydantic 모델 인스턴스처럼 동작합니다.
- 모든 필드는 정의된 타입에 따라 검증됩니다.
- IDE 자동 완성 및 타입 체크 지원을 받을 수 있습니다.
### 기본 예제
구조화된 상태 관리를 구현하는 방법은 다음과 같습니다:
```python
from crewai.flow.flow import Flow, listen, start
from pydantic import BaseModel, Field
from typing import List, Dict, Optional
# Define your state model
class UserPreferences(BaseModel):
theme: str = "light"
language: str = "English"
class AppState(BaseModel):
user_name: str = ""
preferences: UserPreferences = UserPreferences()
items: List[str] = []
processed: bool = False
completion_percentage: float = 0.0
# Create a flow with typed state
class StructuredStateFlow(Flow[AppState]):
@start()
def initialize_data(self):
print("Initializing flow data")
# Set state values (type-checked)
self.state.user_name = "Taylor"
self.state.preferences.theme = "dark"
# The ID field is automatically available
print(f"Flow ID: {self.state.id}")
return "Initialized"
@listen(initialize_data)
def process_data(self, previous_result):
print(f"Processing data for {self.state.user_name}")
# Modify state (with type checking)
self.state.items.append("item1")
self.state.items.append("item2")
self.state.processed = True
self.state.completion_percentage = 50.0
return "Processed"
@listen(process_data)
def generate_summary(self, previous_result):
# Access state (with autocompletion)
summary = f"User {self.state.user_name} has {len(self.state.items)} items "
summary += f"with {self.state.preferences.theme} theme. "
summary += "Data is processed." if self.state.processed else "Data is not processed."
summary += f" Completion: {self.state.completion_percentage}%"
return summary
# Run the flow
flow = StructuredStateFlow()
result = flow.kickoff()
print(f"Final result: {result}")
print(f"Final state: {flow.state}")
```
### 구조화된 상태의 이점
구조화된 상태를 사용하면 여러 가지 장점이 있습니다:
1. **타입 안정성** - 개발 단계에서 타입 오류를 잡을 수 있습니다
2. **자체 문서화** - 상태 모델이 어떤 데이터가 사용 가능한지 명확히 문서화합니다
3. **검증** - 데이터 타입과 제약 조건을 자동으로 검증합니다
4. **IDE 지원** - 자동 완성과 인라인 문서화를 받을 수 있습니다
5. **기본값** - 누락된 데이터에 대한 대체값을 쉽게 정의할 수 있습니다
### 구조화된 상태를 사용할 때
구조화된 상태는 다음과 같은 경우에 권장됩니다:
- 명확하게 정의된 데이터 스키마를 가진 복잡한 플로우
- 여러 개발자가 동일한 코드를 작업하는 팀 프로젝트
- 데이터 검증이 중요한 애플리케이션
- 특정 데이터 타입 및 제약 조건을 강제로 적용해야 하는 플로우
## 자동 상태 ID
비구조화 상태와 구조화 상태 모두 상태 인스턴스를 추적하고 관리하는 데 도움이 되는 고유한 식별자(UUID)를 자동으로 부여받습니다.
### 작동 방식
- 비구조화 state의 경우, ID는 `self.state["id"]`로 접근할 수 있습니다.
- 구조화 state의 경우, ID는 `self.state.id`로 접근할 수 있습니다.
- 이 ID는 flow가 생성될 때 자동으로 생성됩니다.
- ID는 flow의 생명주기 동안 동일하게 유지됩니다.
- ID는 추적, 로깅, 저장된 state의 조회에 사용할 수 있습니다.
이 UUID는 persistence를 구현하거나 여러 flow 실행을 추적할 때 특히 유용합니다.
## 동적 상태 업데이트
구조화된 상태를 사용하든 비구조화된 상태를 사용하든, flow의 실행 중 언제든지 상태를 동적으로 업데이트할 수 있습니다.
### 단계 간 데이터 전달
Flow 메서드는 값을 반환할 수 있으며, 이러한 반환값은 리스닝 메서드의 인자로 전달됩니다:
```python
from crewai.flow.flow import Flow, listen, start
class DataPassingFlow(Flow):
@start()
def generate_data(self):
# This return value will be passed to listening methods
return "Generated data"
@listen(generate_data)
def process_data(self, data_from_previous_step):
print(f"Received: {data_from_previous_step}")
# You can modify the data and pass it along
processed_data = f"{data_from_previous_step} - processed"
# Also update state
self.state["last_processed"] = processed_data
return processed_data
@listen(process_data)
def finalize_data(self, processed_data):
print(f"Received processed data: {processed_data}")
# Access both the passed data and state
last_processed = self.state.get("last_processed", "")
return f"Final: {processed_data} (from state: {last_processed})"
```
이 패턴을 사용하면 직접적인 데이터 전달과 state 업데이트를 결합하여 최대한 유연하게 작업할 수 있습니다.
## 플로우 상태 지속
CrewAI의 가장 강력한 기능 중 하나는 실행 간에 플로우 상태를 지속할 수 있다는 점입니다. 이를 통해 중단, 재개, 심지어 실패 후에도 복구할 수 있는 워크플로우를 구현할 수 있습니다.
### @persist() 데코레이터
`@persist()` 데코레이터는 상태 지속을 자동화하여 flow의 상태를 실행의 주요 지점마다 저장합니다.
#### 클래스 수준 지속성
클래스 수준에서 `@persist()`를 적용하면 모든 메서드 실행 후 상태가 저장됩니다:
```python
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
from pydantic import BaseModel
class CounterState(BaseModel):
value: int = 0
@persist() # Apply to the entire flow class
class PersistentCounterFlow(Flow[CounterState]):
@start()
def increment(self):
self.state.value += 1
print(f"Incremented to {self.state.value}")
return self.state.value
@listen(increment)
def double(self, value):
self.state.value = value * 2
print(f"Doubled to {self.state.value}")
return self.state.value
# First run
flow1 = PersistentCounterFlow()
result1 = flow1.kickoff()
print(f"First run result: {result1}")
# Second run - state is automatically loaded
flow2 = PersistentCounterFlow()
result2 = flow2.kickoff()
print(f"Second run result: {result2}") # Will be higher due to persisted state
```
#### 메서드 수준 지속성
더 세밀한 제어를 위해 `@persist()`를 특정 메서드에 적용할 수 있습니다:
```python
from crewai.flow.flow import Flow, listen, start
from crewai.flow.persistence import persist
class SelectivePersistFlow(Flow):
@start()
def first_step(self):
self.state["count"] = 1
return "First step"
@persist() # Only persist after this method
@listen(first_step)
def important_step(self, prev_result):
self.state["count"] += 1
self.state["important_data"] = "This will be persisted"
return "Important step completed"
@listen(important_step)
def final_step(self, prev_result):
self.state["count"] += 1
return f"Complete with count {self.state['count']}"
```
## 고급 상태 패턴
### 상태 기반 조건부 로직
state를 사용하여 flow에서 복잡한 조건부 로직을 구현할 수 있습니다:
```python
from crewai.flow.flow import Flow, listen, router, start
from pydantic import BaseModel
class PaymentState(BaseModel):
amount: float = 0.0
is_approved: bool = False
retry_count: int = 0
class PaymentFlow(Flow[PaymentState]):
@start()
def process_payment(self):
# Simulate payment processing
self.state.amount = 100.0
self.state.is_approved = self.state.amount < 1000
return "Payment processed"
@router(process_payment)
def check_approval(self, previous_result):
if self.state.is_approved:
return "approved"
elif self.state.retry_count < 3:
return "retry"
else:
return "rejected"
@listen("approved")
def handle_approval(self):
return f"Payment of ${self.state.amount} approved!"
@listen("retry")
def handle_retry(self):
self.state.retry_count += 1
print(f"Retrying payment (attempt {self.state.retry_count})...")
# Could implement retry logic here
return "Retry initiated"
@listen("rejected")
def handle_rejection(self):
return f"Payment of ${self.state.amount} rejected after {self.state.retry_count} retries."
```
### 복잡한 상태 변환 처리
복잡한 상태 변환의 경우, 전용 메서드를 만들어 처리할 수 있습니다.
```python
from crewai.flow.flow import Flow, listen, start
from pydantic import BaseModel
from typing import List, Dict
class UserData(BaseModel):
name: str
active: bool = True
login_count: int = 0
class ComplexState(BaseModel):
users: Dict[str, UserData] = {}
active_user_count: int = 0
class TransformationFlow(Flow[ComplexState]):
@start()
def initialize(self):
# Add some users
self.add_user("alice", "Alice")
self.add_user("bob", "Bob")
self.add_user("charlie", "Charlie")
return "Initialized"
@listen(initialize)
def process_users(self, _):
# Increment login counts
for user_id in self.state.users:
self.increment_login(user_id)
# Deactivate one user
self.deactivate_user("bob")
# Update active count
self.update_active_count()
return f"Processed {len(self.state.users)} users"
# Helper methods for state transformations
def add_user(self, user_id: str, name: str):
self.state.users[user_id] = UserData(name=name)
self.update_active_count()
def increment_login(self, user_id: str):
if user_id in self.state.users:
self.state.users[user_id].login_count += 1
def deactivate_user(self, user_id: str):
if user_id in self.state.users:
self.state.users[user_id].active = False
self.update_active_count()
def update_active_count(self):
self.state.active_user_count = sum(
1 for user in self.state.users.values() if user.active
)
```
이와 같은 헬퍼 메서드 생성 패턴은 flow 메서드를 깔끔하게 유지하면서 복잡한 상태 조작을 가능하게 해줍니다.
## Crews로 상태 관리하기
CrewAI에서 가장 강력한 패턴 중 하나는 flow 상태 관리와 crew 실행을 결합하는 것입니다.
### 크루에 상태 전달하기
플로우 상태를 사용하여 크루에 매개변수를 전달할 수 있습니다:
```python
from crewai.flow.flow import Flow, listen, start
from crewai import Agent, Crew, Process, Task
from pydantic import BaseModel
class ResearchState(BaseModel):
topic: str = ""
depth: str = "medium"
results: str = ""
class ResearchFlow(Flow[ResearchState]):
@start()
def get_parameters(self):
# In a real app, this might come from user input
self.state.topic = "Artificial Intelligence Ethics"
self.state.depth = "deep"
return "Parameters set"
@listen(get_parameters)
def execute_research(self, _):
# Create agents
researcher = Agent(
role="Research Specialist",
goal=f"Research {self.state.topic} in {self.state.depth} detail",
backstory="You are an expert researcher with a talent for finding accurate information."
)
writer = Agent(
role="Content Writer",
goal="Transform research into clear, engaging content",
backstory="You excel at communicating complex ideas clearly and concisely."
)
# Create tasks
research_task = Task(
description=f"Research {self.state.topic} with {self.state.depth} analysis",
expected_output="Comprehensive research notes in markdown format",
agent=researcher
)
writing_task = Task(
description=f"Create a summary on {self.state.topic} based on the research",
expected_output="Well-written article in markdown format",
agent=writer,
context=[research_task]
)
# Create and run crew
research_crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process=Process.sequential,
verbose=True
)
# Run crew and store result in state
result = research_crew.kickoff()
self.state.results = result.raw
return "Research completed"
@listen(execute_research)
def summarize_results(self, _):
# Access the stored results
result_length = len(self.state.results)
return f"Research on {self.state.topic} completed with {result_length} characters of results."
```
### State에서 Crew 출력 처리하기
Crew가 완료되면, 해당 출력을 처리하여 flow state에 저장할 수 있습니다:
```python
@listen(execute_crew)
def process_crew_results(self, _):
# Parse the raw results (assuming JSON output)
import json
try:
results_dict = json.loads(self.state.raw_results)
self.state.processed_results = {
"title": results_dict.get("title", ""),
"main_points": results_dict.get("main_points", []),
"conclusion": results_dict.get("conclusion", "")
}
return "Results processed successfully"
except json.JSONDecodeError:
self.state.error = "Failed to parse crew results as JSON"
return "Error processing results"
```
## 상태 관리 모범 사례
### 1. 상태를 집중적으로 유지하세요
상태를 설계할 때 꼭 필요한 내용만 포함하도록 하세요:
```python
# Too broad
class BloatedState(BaseModel):
user_data: Dict = {}
system_settings: Dict = {}
temporary_calculations: List = []
debug_info: Dict = {}
# ...many more fields
# Better: Focused state
class FocusedState(BaseModel):
user_id: str
preferences: Dict[str, str]
completion_status: Dict[str, bool]
```
### 2. 복잡한 플로우를 위한 구조화된 상태 사용
플로우의 복잡도가 증가할수록 구조화된 상태의 가치는 점점 커집니다:
```python
# Simple flow can use unstructured state
class SimpleGreetingFlow(Flow):
@start()
def greet(self):
self.state["name"] = "World"
return f"Hello, {self.state['name']}!"
# Complex flow benefits from structured state
class UserRegistrationState(BaseModel):
username: str
email: str
verification_status: bool = False
registration_date: datetime = Field(default_factory=datetime.now)
last_login: Optional[datetime] = None
class RegistrationFlow(Flow[UserRegistrationState]):
# Methods with strongly-typed state access
```
### 3. 문서 상태 전이
복잡한 흐름의 경우, 실행 중에 상태가 어떻게 변하는지 문서화하세요:
```python
@start()
def initialize_order(self):
"""
Initialize order state with empty values.
State before: {}
State after: {order_id: str, items: [], status: 'new'}
"""
self.state.order_id = str(uuid.uuid4())
self.state.items = []
self.state.status = "new"
return "Order initialized"
```
### 4. 상태 오류를 정상적으로 처리하기
상태 접근에 대한 오류 처리를 구현하세요:
```python
@listen(previous_step)
def process_data(self, _):
try:
# Try to access a value that might not exist
user_preference = self.state.preferences.get("theme", "default")
except (AttributeError, KeyError):
# Handle the error gracefully
self.state.errors = self.state.get("errors", [])
self.state.errors.append("Failed to access preferences")
user_preference = "default"
return f"Used preference: {user_preference}"
```
### 5. 상태를 사용하여 진행 상황 추적
긴 실행 흐름에서 진행 상황을 추적하기 위해 상태를 활용하세요:
```python
class ProgressTrackingFlow(Flow):
@start()
def initialize(self):
self.state["total_steps"] = 3
self.state["current_step"] = 0
self.state["progress"] = 0.0
self.update_progress()
return "Initialized"
def update_progress(self):
"""Helper method to calculate and update progress"""
if self.state.get("total_steps", 0) > 0:
self.state["progress"] = (self.state.get("current_step", 0) /
self.state["total_steps"]) * 100
print(f"Progress: {self.state['progress']:.1f}%")
@listen(initialize)
def step_one(self, _):
# Do work...
self.state["current_step"] = 1
self.update_progress()
return "Step 1 complete"
# Additional steps...
```
### 6. 가능한 경우 불변(Immutable) 연산 사용하기
특히 구조화된 상태에서는 명확성을 위해 불변 연산을 선호하세요:
```python
# 리스트를 즉시 수정하는 대신:
self.state.items.append(new_item) # 변경 가능한 연산
# 새로운 상태를 생성하는 것을 고려하세요:
from pydantic import BaseModel
from typing import List
class ItemState(BaseModel):
items: List[str] = []
class ImmutableFlow(Flow[ItemState]):
@start()
def add_item(self):
# 추가된 항목과 함께 새로운 리스트 생성
self.state.items = [*self.state.items, "new item"]
return "Item added"
```
## 플로우 상태 디버깅
### 상태 변경 로깅
개발할 때 상태 변화를 추적하기 위해 로깅을 추가하세요:
```python
import logging
logging.basicConfig(level=logging.INFO)
class LoggingFlow(Flow):
def log_state(self, step_name):
logging.info(f"State after {step_name}: {self.state}")
@start()
def initialize(self):
self.state["counter"] = 0
self.log_state("initialize")
return "Initialized"
@listen(initialize)
def increment(self, _):
self.state["counter"] += 1
self.log_state("increment")
return f"Incremented to {self.state['counter']}"
```
### 상태 시각화
디버깅을 위해 상태를 시각화하는 메서드를 추가할 수 있습니다:
```python
def visualize_state(self):
"""Create a simple visualization of the current state"""
import json
from rich.console import Console
from rich.panel import Panel
console = Console()
if hasattr(self.state, "model_dump"):
# Pydantic v2
state_dict = self.state.model_dump()
elif hasattr(self.state, "dict"):
# Pydantic v1
state_dict = self.state.dict()
else:
# Unstructured state
state_dict = dict(self.state)
# Remove id for cleaner output
if "id" in state_dict:
state_dict.pop("id")
state_json = json.dumps(state_dict, indent=2, default=str)
console.print(Panel(state_json, title="Current Flow State"))
```
## 결론
CrewAI Flows에서 상태 관리를 마스터하면 컨텍스트를 유지하고, 복잡한 결정을 내리며, 일관된 결과를 제공하는 정교하고 견고한 AI 애플리케이션을 구축할 수 있는 힘을 얻게 됩니다.
비구조화 상태든 구조화 상태든 적절한 상태 관리 방식을 구현하면 유지 관리가 용이하고, 확장 가능하며, 실제 문제를 효과적으로 해결할 수 있는 플로우를 만들 수 있습니다.
더 복잡한 플로우를 개발할수록 좋은 상태 관리는 유연성과 구조성 사이의 올바른 균형을 찾는 것임을 기억하세요. 이를 통해 코드가 강력하면서도 이해하기 쉬워집니다.
<Check>
이제 CrewAI Flows에서 상태 관리의 개념과 실습을 마스터하셨습니다! 이 지식을 통해 컨텍스트를 효과적으로 유지하고, 단계 간 데이터를 공유하며, 정교한 애플리케이션 로직을 구현하는 견고한 AI 워크플로우를 만들 수 있습니다.
</Check>
## 다음 단계
- flow에서 구조화된 state와 비구조화된 state를 모두 실험해 보세요
- 장기 실행 워크플로를 위해 state 영속성을 구현해 보세요
- [첫 crew 만들기](/ko/guides/crews/first-crew)를 탐색하여 crew와 flow가 어떻게 함께 작동하는지 확인해 보세요
- 더 고급 기능을 원한다면 [Flow 참고 문서](/ko/concepts/flows)를 확인해 보세요