Compare commits

..

9 Commits

Author SHA1 Message Date
Vinicius Brasil
51d0ce4cc5 Document FlowDefinition fields in the JSON schema
Add a description and examples to every FlowDefinition field and
standardize on `typing.Literal`, so the generated JSON schema documents
itself — each action discriminator, state branch, and config option
explains what it is and shows a realistic value.

Examples live on individual fields only, never at the model level, which
keeps the schema readable for tooling that renders field-level help.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:31:42 -07:00
Vinicius Brasil
b69e5ccc10 Add script action to FlowDefinition
Let a Flow method run trusted inline Python with `call: script`. The code
is compiled once into a generated function and receives the runtime
values as arguments — state, outputs, input, and item — so nothing is
interpolated into the source. This is not sandboxed.

Also extracts the shared `outputs_by_name` helper into `runtime/_outputs`
so the script action and the expression evaluator build the outputs map
the same way, including each-iteration local outputs.

```yaml
methods:
  normalize:
    start: true
    do:
      call: script
      code: |
        import math
        state["rounded"] = math.ceil(state["raw_score"])
        return f"rounded:{state['rounded']}"
```

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:31:23 -07:00
Vinicius Brasil
7bb9bc7e1a Discriminate FlowDefinition state types (#6196)
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Check Documentation Broken Links / Check broken links (push) Waiting to run
Vulnerability Scan / pip-audit (push) Waiting to run
Replace the single FlowStateDefinition model with a `type`-discriminated
union of FlowDictStateDefinition, FlowPydanticStateDefinition,
FlowJsonSchemaStateDefinition, and FlowUnknownStateDefinition.

Each branch only carries the fields it actually uses and forbids extras,
so an invalid combination like a `dict` state with a `ref` now fails
validation instead of being silently accepted. The runtime reads `ref`
and `json_schema` defensively since they no longer exist on every branch.

```yaml
state:
  type: json_schema
  json_schema:
    type: object
    properties:
      topic:
        type: string
```

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:31:07 -07:00
João Moura
ee4f853270 Update installation and quickstart documentation for JSON-first crew projects (#6181)
* Update installation and quickstart documentation for JSON-first crew projects

- Revised the installation guide to reflect the new JSON-first project structure, detailing the creation of `crew.jsonc` and `agents/*.jsonc` files.
- Updated the quickstart guide to demonstrate setting up agents and tasks using JSONC format, replacing previous YAML examples.
- Enhanced the agents and tasks documentation to clarify the transition from YAML to JSONC, including examples and explanations of the new structure.
- Added notes on the classic YAML structure for legacy projects and provided guidance on migrating to the new format.

* docs: clarify json crew quickstart guidance

* docs: address json docs review feedback
2026-06-16 19:55:23 -03:00
João Moura
ebbc0998ef Implement DMN mode support in crew creation and execution (#6194)
* Implement DMN mode support in crew creation and execution

- Added `is_dmn_mode_enabled` utility to check for enterprise non-interactive mode based on the `CREWAI_DMN` environment variable.
- Updated `create` function in `cli.py` to enforce required parameters when DMN mode is active, raising appropriate usage errors.
- Enhanced `create_crew` and `create_json_crew` functions to skip provider prompts and handle folder existence checks in DMN mode.
- Introduced non-interactive defaults for agent and task creation in DMN mode, ensuring seamless project setup without user input.
- Modified `run_crew` to bypass TUI and handle runtime inputs directly when in DMN mode, improving execution flow for JSON-defined crews.
- Added tests to validate DMN mode behavior, ensuring correct handling of required inputs and non-interactive defaults.

* Implement DMN mode support in crew creation and execution

- Introduced `is_dmn_mode_enabled()` utility to check for non-interactive mode based on the `CREWAI_DMN` environment variable.
- Updated `create` function to enforce required parameters when DMN mode is active, raising appropriate usage errors.
- Modified `create_crew` and `create_json_crew` functions to skip provider prompts and utilize non-interactive defaults in DMN mode.
- Enhanced `run_crew` to bypass TUI and handle runtime inputs directly in DMN mode, ensuring smooth execution without user interaction.
- Added tests to validate DMN mode behavior, including requirements for type and name, and ensuring proper handling of existing folders and missing inputs.
2026-06-16 19:48:31 -03:00
João Moura
06ada68083 Enhance crew loading and validation logic (#6182)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
* Enhance crew loading and validation logic

- Updated `crew_loader.py` to pass the project root when loading task and agent definitions, improving the handling of Python references.
- Refactored `json_loader.py` to include additional validation for Python references, ensuring they are resolved within the project root and enforcing depth limits.
- Added tests in `test_crew_loader.py` and `test_json_loader.py` to validate rejection of unsafe Python references and input files outside the project root.
- Improved error handling for JSON project validation, ensuring clearer feedback for invalid configurations.

* Refactor tests for hierarchical verbose manager agent

- Removed `@pytest.mark.vcr()` decorators from `test_hierarchical_verbose_manager_agent` and `test_hierarchical_verbose_false_manager_agent`.
- Introduced mocking for task outputs in both tests to simulate execution without relying on external dependencies.
- Ensured that the `crew.kickoff()` method is called within a context that patches the `Task.execute_sync` method, improving test isolation and reliability.

* Fix JSON loader PR review comments

* Fix JSON loader project root after rebase

* Handle UNC paths in JSON input files
2026-06-16 18:45:26 -03:00
Vinicius Brasil
4eb90ffbf3 Add crew actions to FlowDefinition (#6184) 2026-06-16 12:59:48 -07:00
Vinicius Brasil
a6cf52ec7e Add inline crew definition loading (#6183) 2026-06-16 11:51:22 -07:00
Vinicius Brasil
9d44d0a5e5 Serialize concrete Pydantic subclasses (#6187) 2026-06-16 11:00:07 -07:00
93 changed files with 4695 additions and 3266 deletions

View File

@@ -70,13 +70,39 @@ mode: "wide"
## إنشاء الوكلاء
هناك طريقتان لإنشاء الوكلاء في CrewAI: باستخدام **تهيئة YAML (موصى بها)** أو تعريفهم **مباشرة في الكود**.
هناك طريقتان شائعتان لإنشاء الوكلاء في CrewAI: باستخدام **تهيئة JSONC (الموصى بها للـ crews الجديدة)** أو تعريفهم **مباشرة في الكود**.
### تهيئة YAML (موصى بها)
### تهيئة JSONC (موصى بها)
توفر تهيئة YAML طريقة أنظف وأكثر قابلية للصيانة لتعريف الوكلاء. نوصي بشدة باستخدام هذا النهج في مشاريع CrewAI.
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` تستخدم تهيئة JSON-first. يُعرّف كل Agent في `agents/<agent_name>.jsonc`، ويحدد `crew.jsonc` أي Agents تدخل في الـ crew.
بعد إنشاء مشروع CrewAI كما هو موضح في قسم [التثبيت](/ar/installation)، انتقل إلى ملف `src/latest_ai_development/config/agents.yaml` وعدّل القالب ليتوافق مع متطلباتك.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You find the most relevant information and present it clearly.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
استخدم `{placeholder}` داخل `role` أو `goal` أو `backstory`. ضع القيم الافتراضية في `inputs` داخل `crew.jsonc`؛ وسيطلب `crewai run` أي قيم ناقصة. يمكن وضع حقول السلوك مثل `verbose` و `allow_delegation` و `max_iter` و `memory` و `cache` و `planning_config` في المستوى الأعلى أو داخل `settings`.
<Note>
يدعم JSONC التعليقات والفواصل النهائية. إذا وُجد `agents/<name>.jsonc` و `agents/<name>.json` معًا، يستخدم CrewAI ملف JSONC.
</Note>
### تهيئة YAML الكلاسيكية
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew <name> --classic` تستخدم `config/agents.yaml` وفئة `@CrewBase` في `crew.py`.
تظل تهيئة YAML مدعومة للمشاريع الحالية المبنية بـ Python/YAML وللفِرق التي تفضل تعريف الوكلاء من خلال فئة `@CrewBase`.
بعد إنشاء مشروع كلاسيكي، انتقل إلى ملف `src/<project_name>/config/agents.yaml` وعدّل القالب ليتوافق مع متطلباتك.
<Note>
ستُستبدل المتغيرات في ملفات YAML (مثل `{topic}`) بقيم من مدخلاتك عند تشغيل الطاقم:
@@ -88,7 +114,7 @@ crew.kickoff(inputs={'topic': 'AI Agents'})
إليك مثالًا على كيفية تهيئة الوكلاء باستخدام YAML:
```yaml agents.yaml
# src/latest_ai_development/config/agents.yaml
# src/<project_name>/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
@@ -113,7 +139,7 @@ reporting_analyst:
لاستخدام تهيئة YAML في الكود، أنشئ فئة طاقم ترث من `CrewBase`:
```python Code
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process
from crewai.project import CrewBase, agent, crew
from crewai_tools import SerperDevTool

View File

@@ -52,6 +52,8 @@ crewai create crew my_new_crew
crewai create flow my_new_flow
```
افتراضيًا، ينشئ `crewai create crew` مشروعًا JSON-first يحتوي على `crew.jsonc` و `agents/*.jsonc`. استخدم `crewai create crew my_new_crew --classic` فقط إذا أردت البنية القديمة Python/YAML مع `crew.py` و `config/agents.yaml` و `config/tasks.yaml`.
### 2. الإصدار
عرض الإصدار المثبت من CrewAI.
@@ -142,7 +144,20 @@ crewai chat
```
<Note>
مهم: عيّن خاصية `chat_llm` في ملف `crew.py` لتفعيل هذا الأمر.
مهم: عيّن خاصية `chat_llm` في تعريف الـ crew لتفعيل هذا الأمر.
للـ crews بنمط JSON-first، أضفها إلى `crew.jsonc`:
```jsonc
{
"name": "My Crew",
"agents": ["researcher"],
"tasks": [],
"chat_llm": "openai/gpt-4o"
}
```
للـ crews الكلاسيكية Python/YAML، عيّنها في `crew.py`:
```python
@crew

View File

@@ -40,11 +40,52 @@ mode: "wide"
## إنشاء الأطقم
هناك طريقتان لإنشاء الأطقم في CrewAI: باستخدام **تهيئة YAML (موصى بها)** أو تعريفها **مباشرة في الكود**.
هناك طريقتان رئيسيتان لإنشاء الأطقم في CrewAI: باستخدام **تهيئة JSONC (الموصى بها للـ crews الجديدة)** أو تعريفها **مباشرة في الكود** للمشاريع الكلاسيكية والحالات المتقدمة.
### تهيئة YAML (موصى بها)
### تهيئة JSONC (موصى بها)
توفر تهيئة YAML طريقة أنظف وأكثر قابلية للصيانة لتعريف الأطقم وتتسق مع كيفية تعريف الوكلاء والمهام في مشاريع CrewAI.
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` تستخدم `crew.jsonc` لإعدادات الـ crew والمهام، وملفًا منفصلًا لكل Agent داخل `agents/`. يكتشف `crewai run` ملف `crew.jsonc` أو `crew.json`، ويحمّل الـ Agents المشار إليها، ويطلب قيم placeholders الناقصة، ثم يبدأ الـ crew.
```jsonc crew.jsonc
{
"name": "Market Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research",
"description": "Research {topic} and collect the most relevant facts.",
"expected_output": "Structured research notes about {topic}.",
"agent": "researcher"
},
{
"name": "analysis",
"description": "Analyze the research and write a concise report.",
"expected_output": "A markdown report with findings and recommendations.",
"agent": "analyst",
"context": ["research"],
"output_file": "output/report.md"
}
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "AI Agents"
}
}
```
كل عنصر في `agents` يُحل أولًا إلى `agents/<name>.jsonc` ثم إلى `agents/<name>.json`. للـ crews الهرمية، استخدم `"process": "hierarchical"` مع `manager_llm` أو `manager_agent`.
<Warning>
شغّل مشاريع JSON crew من مصادر تثق بها فقط. أدوات `custom:<name>` ومراجع `{"python": "module.attribute"}` تنفذ كود Python محليًا عند تحميل الـ crew.
</Warning>
### تهيئة YAML الكلاسيكية
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew <name> --classic` تستخدم `crew.py` و `config/agents.yaml` و `config/tasks.yaml` والمزيّنات `@CrewBase` و `@agent` و `@task` و `@crew`.
تظل هذه الطريقة مدعومة للمشاريع الحالية المبنية بـ Python/YAML وللفِرق التي تحتاج تحكمًا صريحًا عبر decorators.
```python code
from crewai import Agent, Crew, Task, Process

View File

@@ -830,7 +830,7 @@ if __name__ == "__main__":
crewai create flow name_of_flow
```
سيولّد هذا الأمر مشروع CrewAI جديد مع هيكل المجلدات اللازم. يتضمن المشروع المولّد فريق Crew مُعد مسبقًا يُسمى `poem_crew` ويعمل بالفعل. يمكنك استخدام هذا الفريق كقالب بنسخه ولصقه وتعديله لإنشاء فرق أخرى.
سيولّد هذا الأمر مشروع CrewAI جديد مع هيكل المجلدات اللازم. يتضمن المشروع المولّد فريق Crew مُعد مسبقًا يُسمى `poem_crew` ويعمل بالفعل. يستخدم الـ embedded crew الابتدائي بنية Python/YAML الكلاسيكية؛ أما crews المستقلة الجديدة التي تُنشأ عبر `crewai create crew` فتستخدم بنية JSON-first.
### هيكل المجلدات
@@ -860,7 +860,29 @@ crewai create flow name_of_flow
- `config/tasks.yaml`: يحدد المهام للفريق.
- `poem_crew.py`: يحتوي على تعريف الفريق، بما في ذلك الـ Agents والمهام والفريق نفسه.
يمكنك نسخ ولصق وتعديل `poem_crew` لإنشاء فرق أخرى.
يمكنك نسخ ولصق وتعديل `poem_crew` لإنشاء crews كلاسيكية مضمّنة أخرى.
للـ crews المضمّنة بنمط JSON-first، استخدم مجلدًا يحتوي على `crew.jsonc` و `agents/*.jsonc`:
```text
crews/
└── research_crew/
├── agents/
│ └── researcher.jsonc
└── crew.jsonc
```
ثم حمّلها من خطوة في Flow:
```python
from pathlib import Path
from crewai.project import load_crew
crew, default_inputs = load_crew(
Path(__file__).parent / "crews" / "research_crew" / "crew.jsonc"
)
result = crew.kickoff(inputs={**default_inputs, "topic": "AI Agents"})
```
### ربط فرق Crew في `main.py`

View File

@@ -73,13 +73,48 @@ crew = Crew(
## إنشاء المهام
هناك طريقتان لإنشاء المهام في CrewAI: باستخدام **إعداد YAML (موصى به)** أو تعريفها **مباشرة في الكود**.
هناك طريقتان شائعتان لإنشاء المهام في CrewAI: باستخدام **تهيئة JSONC (الموصى بها للـ crews الجديدة)** أو تعريفها **مباشرة في الكود**.
### إعداد YAML (موصى به)
### تهيئة JSONC (موصى بها)
يوفر استخدام إعداد YAML طريقة أنظف وأكثر قابلية للصيانة لتعريف المهام. نوصي بشدة باستخدام هذا النهج لتعريف المهام في مشاريع CrewAI.
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` تعرّف المهام في `crew.jsonc`.
بعد إنشاء مشروع CrewAI كما هو موضح في قسم [التثبيت](/ar/installation)، انتقل إلى ملف `src/latest_ai_development/config/tasks.yaml` وعدّل القالب ليتوافق مع متطلبات مهامك المحددة.
````jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "reporting_analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research about {topic}.",
"expected_output": "A list of the most relevant information about {topic}.",
"agent": "researcher"
},
{
"name": "reporting_task",
"description": "Review the research and expand it into a detailed report.",
"expected_output": "A polished markdown report.",
"agent": "reporting_analyst",
"context": ["research_task"],
"markdown": true,
"output_file": "report.md"
}
],
"inputs": {
"topic": "AI Agents"
}
}
````
كل مهمة تحتاج إلى `description` و `expected_output`. يجب أن يطابق `agent` اسم Agent مذكورًا في `agents`. يشير `context` إلى أسماء مهام سابقة فقط؛ وترفض الإشارات إلى مهام لاحقة.
### إعداد YAML الكلاسيكي
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew <name> --classic` تستخدم `config/tasks.yaml` وفئة `@CrewBase` في `crew.py`.
يظل إعداد YAML مدعومًا للمشاريع الحالية المبنية بـ Python/YAML وللفِرق التي تفضل تعريف المهام من خلال فئة `@CrewBase`.
بعد إنشاء مشروع كلاسيكي، انتقل إلى ملف `src/<project_name>/config/tasks.yaml` وعدّل القالب ليتوافق مع متطلبات مهامك المحددة.
<Note>
المتغيرات في ملفات YAML (مثل `{topic}`) سيتم استبدالها بالقيم من مدخلاتك عند تشغيل الفريق:
@@ -115,7 +150,7 @@ reporting_task:
لاستخدام إعداد YAML هذا في كودك، أنشئ فئة فريق ترث من `CrewBase`:
```python crew.py
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

View File

@@ -26,10 +26,10 @@ icon: "arrows-rotate"
## الخطوة 1 — هيكلة طاقم التحقق
أنشئ مشروع طاقم جديد. يُهيكل CrewAI CLI البنية:
أنشئ مشروع crew كلاسيكيًا لأن هذا المثال يربط أداة Python عبر `crew.py`:
```bash
crewai create crew rotation_verifier --skip_provider
crewai create crew rotation_verifier --classic --skip_provider
cd rotation_verifier
```

View File

@@ -374,17 +374,17 @@ git push
**الحل**: تحقق من أن مشروعك يتطابق مع البنية المتوقعة:
- **كل من الطواقم والتدفقات**: يجب أن تكون نقطة الدخول في `src/project_name/main.py`
- **الطواقم**: تستخدم دالة `run()` كنقطة دخول
- **التدفقات**: تستخدم دالة `kickoff()` كنقطة دخول
- **JSON-first Crews**: أبقِ `crew.jsonc` أو `crew.json` و `agents/` في جذر المشروع
- **Crews كلاسيكية**: استخدم `src/project_name/main.py` مع دالة دخول `run()`
- **Flows**: استخدم `src/project_name/main.py` مع دالة دخول `kickoff()`
راجع [التحضير للنشر](/ar/enterprise/guides/prepare-for-deployment) لمخططات البنية المفصلة.
#### مُزخرف CrewBase مفقود
#### مُزخرف CrewBase مفقود في crew كلاسيكية
**العرض**: أخطاء "Crew not found" أو "Config not found" أو أخطاء تهيئة الوكيل/المهمة
**الحل**: تأكد من أن **جميع** فئات الطاقم تستخدم مُزخرف `@CrewBase`:
**الحل**: في crews الكلاسيكية Python/YAML، تأكد من أن جميع فئات الـ crew تستخدم مُزخرف `@CrewBase`. لا تحتاج crews بنمط JSON-first إلى هذا المزخرف.
```python
from crewai.project import CrewBase, agent, crew, task
@@ -404,8 +404,8 @@ class YourCrew():
```
<Info>
ينطبق هذا على الطواقم المستقلة والطواقم المضمنة داخل مشاريع التدفق.
كل فئة طاقم تحتاج المُزخرف.
ينطبق هذا على فئات crew الكلاسيكية المكتوبة في Python، بما في ذلك crews الكلاسيكية المضمنة داخل مشاريع Flow.
يتم التحقق من crews بنمط JSON-first من `crew.jsonc` و `agents/` بدلاً من ذلك.
</Info>
#### نوع pyproject.toml غير صحيح
@@ -442,8 +442,8 @@ type = "flow"
**الحل**:
1. تحقق من سجلات التنفيذ في لوحة تحكم AMP (علامة تبويب Traces)
2. تحقق من أن جميع الأدوات لديها مفاتيح API المطلوبة مُهيأة
3. تأكد من صحة تهيئات الوكلاء في `agents.yaml`
4. تحقق من تهيئات المهام في `tasks.yaml` بحثاً عن أخطاء الصياغة
3. في crews بنمط JSON-first، تحقق من `crew.jsonc` والملفات المشار إليها داخل `agents/`
4. في crews الكلاسيكية، تأكد من صحة `agents.yaml` و `tasks.yaml`
<Card title="تحتاج مساعدة؟" icon="headset" href="mailto:support@crewai.com">
تواصل مع فريق الدعم للمساعدة في مشاكل النشر أو أسئلة حول

View File

@@ -23,10 +23,9 @@ company-ai/
`-- crews/
|-- support_agent/
| |-- pyproject.toml
| `-- src/
| `-- support_agent/
| |-- main.py
| `-- crew.py
| |-- crew.jsonc
| `-- agents/
| `-- support_agent.jsonc
`-- research_flow/
|-- pyproject.toml
`-- src/
@@ -47,7 +46,7 @@ crews/support_agent
عند تعيين دليل عمل، يستخدم AMP ذلك المجلد من أجل:
- التحقق من المشروع، بما في ذلك `pyproject.toml` و`src/` ونقطة دخول Crew أو Flow
- التحقق من المشروع، بما في ذلك `pyproject.toml` وملفات crew JSON وأي نقطة دخول كلاسيكية لـ Crew أو Flow
- تثبيت الاعتماديات باستخدام `uv`
- دليل العمل للعملية قيد التشغيل
- متغير البيئة `CREW_ROOT_DIR`
@@ -150,11 +149,16 @@ crews/support_agent
## ملفات القفل وUV Workspaces
يجب أن يحتوي المجلد المحدد على `pyproject.toml` ودليل `src/` الخاصين بالأتمتة.
يجب أن يحتوي المجلد المحدد على `pyproject.toml` وملفات المشروع المناسبة لنوع
الأتمتة:
- crew بنمط JSON-first: ملف `crew.jsonc` أو `crew.json` مع مجلد `agents/`
- Crew كلاسيكي أو Flow: مجلد `src/` مع نقطة دخول Python المتوقعة
يمكن أن يوجد ملف `uv.lock` أو `poetry.lock` إما في المجلد المحدد أو في جذر
المستودع.
يدعم هذا التخطيطين الشائعين في monorepo:
يدعم هذا تخطيطي ملفات القفل الشائعين:
<Tabs>
<Tab title="ملف قفل المشروع">
@@ -164,9 +168,9 @@ crews/support_agent
`-- support_agent/
|-- pyproject.toml
|-- uv.lock
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
@@ -179,9 +183,9 @@ crews/support_agent
`-- crews/
`-- support_agent/
|-- pyproject.toml
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
</Tabs>

View File

@@ -24,7 +24,7 @@ mode: "wide"
<CardGroup cols={2}>
<Card title="مشاريع الطاقم" icon="users">
فرق وكلاء ذكاء اصطناعي مستقلة مع `crew.py` يحدد الوكلاء والمهام. الأفضل للمهام المركزة والتعاونية.
فرق وكلاء ذكاء اصطناعي مستقلة. الـ crews الجديدة تستخدم بنية JSON-first مع `crew.jsonc` و `agents/`؛ ويمكن للـ crews الكلاسيكية الاستمرار في استخدام `crew.py`.
</Card>
<Card title="مشاريع التدفق" icon="diagram-project">
سير عمل مُنسّق مع طواقم مضمنة في مجلد `crews/`. الأفضل للعمليات المعقدة متعددة المراحل.
@@ -33,19 +33,19 @@ mode: "wide"
| الجانب | الطاقم | التدفق |
|--------|--------|--------|
| **بنية المشروع** | `src/project_name/` مع `crew.py` | `src/project_name/` مع مجلد `crews/` |
| **موقع المنطق الرئيسي** | `src/project_name/crew.py` | `src/project_name/main.py` (فئة Flow) |
| **دالة نقطة الدخول** | `run()` في `main.py` | `kickoff()` في `main.py` |
| **بنية المشروع** | جذر المشروع مع `crew.jsonc` و `agents/` | `src/project_name/` مع مجلد `crews/` |
| **موقع المنطق الرئيسي** | `crew.jsonc` (كلاسيكي: `src/project_name/crew.py`) | `src/project_name/main.py` (فئة Flow) |
| **دالة نقطة الدخول** | تُحمّل من `crew.jsonc` (كلاسيكي: `run()` في `main.py`) | `kickoff()` في `main.py` |
| **نوع pyproject.toml** | `type = "crew"` | `type = "flow"` |
| **أمر CLI للإنشاء** | `crewai create crew name` | `crewai create flow name` |
| **موقع التهيئة** | `src/project_name/config/` | `src/project_name/crews/crew_name/config/` |
| **موقع التهيئة** | `crew.jsonc` و `agents/` و `tools/` اختياريًا | `src/project_name/crews/crew_name/config/` أو مجلدات crew JSON مضمنة |
| **يمكن أن يحتوي طواقم أخرى** | لا | نعم (في مجلد `crews/`) |
## مرجع بنية المشروع
### بنية مشروع الطاقم
عند تشغيل `crewai create crew my_crew`، تحصل على هذه البنية:
عند تشغيل `crewai create crew my_crew`، تحصل على بنية JSON-first:
```
my_crew/
@@ -54,24 +54,25 @@ my_crew/
├── README.md
├── .env
├── uv.lock # REQUIRED for deployment
── src/
└── my_crew/
├── __init__.py
├── main.py # Entry point with run() function
├── crew.py # Crew class with @CrewBase decorator
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml # Agent definitions
└── tasks.yaml # Task definitions
── crew.jsonc # إعدادات الـ crew والمهام والعملية والمدخلات
├── agents/
└── researcher.jsonc # تعريفات الـ Agents
├── tools/ # أدوات custom:<name> اختيارية
├── knowledge/
└── skills/
```
<Warning>
بنية `src/project_name/` المتداخلة ضرورية للطواقم.
وضع الملفات في المستوى الخاطئ سيسبب فشل النشر.
في crews بنمط JSON-first، أبقِ `crew.jsonc` و `agents/` و `tools/` و `knowledge/` و `skills/`
في جذر المشروع. وضعها داخل `src/` يمنع `crewai run` والتحقق قبل النشر من العثور على تعريف الـ crew.
</Warning>
<Info>
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew my_crew --classic` تستخدم البنية القديمة
`src/project_name/crew.py` و `src/project_name/config/agents.yaml` و
`src/project_name/config/tasks.yaml`. تظل هذه البنية مدعومة للـ crews المكتوبة في Python مع decorators.
</Info>
### بنية مشروع التدفق
عند تشغيل `crewai create flow my_flow`، تحصل على هذه البنية:
@@ -100,9 +101,9 @@ my_flow/
```
<Info>
كلا الطواقم والتدفقات تستخدم بنية `src/project_name/`.
الفرق الرئيسي أن التدفقات لها مجلد `crews/` للطواقم المضمنة،
بينما الطواقم لها `crew.py` مباشرة في مجلد المشروع.
الـ crews المستقلة بنمط JSON-first تستخدم ملفات JSON في جذر المشروع. أما Flows فتظل تستخدم
`src/project_name/` ويمكن أن تحتوي crews مضمنة كلاسيكية أو مجلدات crew JSON يتم تحميلها عبر
`crewai.project.load_crew`.
</Info>
## قائمة فحص ما قبل النشر
@@ -154,60 +155,89 @@ git commit -m "Add uv.lock for deployment"
git push
```
### 3. التحقق من استخدام مُزخرف CrewBase
### 3. التحقق من تعريف الـ Crew
**يجب أن تستخدم كل فئة طاقم مُزخرف `@CrewBase`.** ينطبق هذا على:
<Tabs>
<Tab title="JSON-first Crews">
يجب أن تحتوي crews بنمط JSON-first على `crew.jsonc` أو `crew.json` في جذر المشروع.
يجب أن يشير مصفوفة `agents` إلى ملفات داخل `agents/`، ويجب أن تشير كل task إلى اسم Agent صحيح.
- مشاريع الطاقم المستقلة
- الطواقم المضمنة داخل مشاريع التدفق
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Research {topic}.",
"expected_output": "A concise report.",
"agent": "researcher"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
```python
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
تُشار الأدوات المخصصة بصيغة `"custom:<name>"` ويجب تنفيذها في
`tools/<name>.py` كصنف يرث من `BaseTool`.
</Tab>
<Tab title="Crews كلاسيكية Python/YAML">
يجب أن تستخدم الـ crews الكلاسيكية وPython crews المضمنة داخل Flows مزخرف `@CrewBase`.
@CrewBase # This decorator is REQUIRED
class MyCrew():
"""My crew description"""
```python
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
agents: List[BaseAgent]
tasks: List[Task]
@CrewBase
class MyCrew():
"""My crew description"""
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
agents: List[BaseAgent]
tasks: List[Task]
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
<Warning>
إذا نسيت مُزخرف `@CrewBase`، سيفشل النشر بأخطاء حول
تهيئات الوكلاء أو المهام المفقودة.
</Warning>
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
</Tab>
</Tabs>
### 4. التحقق من نقاط دخول المشروع
كل من الطواقم والتدفقات لها نقطة دخول في `src/project_name/main.py`:
لا تحتاج crews المستقلة بنمط JSON-first إلى ملف `src/project_name/main.py` مكتوب يدويًا؛
يقوم `crewai run` وتغليف النشر بتحميل `crew.jsonc` مباشرة. تستخدم crews الكلاسيكية وFlows نقاط دخول Python:
<Tabs>
<Tab title="للطواقم">
<Tab title="JSON-first Crews">
شغّل محليًا من جذر المشروع:
```bash
crewai run
```
</Tab>
<Tab title="Crews كلاسيكية">
تستخدم نقطة الدخول دالة `run()`:
```python
@@ -278,16 +308,17 @@ grep -A2 "\[tool.crewai\]" pyproject.toml
# 2. Verify uv.lock exists
ls -la uv.lock || echo "ERROR: uv.lock missing! Run 'uv lock'"
# 3. Verify src/ structure exists
ls -la src/*/main.py 2>/dev/null || echo "No main.py found in src/"
# 3. For JSON-first crews, verify crew.jsonc and agents/
([ -f crew.jsonc ] || [ -f crew.json ]) || echo "No crew.jsonc or crew.json found"
test -d agents || echo "No agents/ directory found"
# 4. For Crews - verify crew.py exists
# 4. For classic Crews - verify crew.py exists
ls -la src/*/crew.py 2>/dev/null || echo "No crew.py (expected for Crews)"
# 5. For Flows - verify crews/ folder exists
ls -la src/*/crews/ 2>/dev/null || echo "No crews/ folder (expected for Flows)"
# 6. Check for CrewBase usage
# 6. For classic Python crews - check for CrewBase usage
grep -r "@CrewBase" . --include="*.py"
```
@@ -297,8 +328,9 @@ grep -r "@CrewBase" . --include="*.py"
|-------|-------|---------|
| `uv.lock` مفقود | فشل البناء أثناء حل الاعتماديات | شغّل `uv lock` وارفعه |
| `type` خاطئ في pyproject.toml | نجاح البناء لكن فشل وقت التشغيل | غيّر إلى النوع الصحيح |
| مُزخرف `@CrewBase` مفقود | أخطاء "Config not found" | أضف المُزخرف لجميع فئات الطاقم |
| ملفات في الجذر بدل `src/` | نقطة الدخول غير موجودة | انقلها إلى `src/project_name/` |
| `crew.jsonc` أو `agents/` مفقود في crew بنمط JSON-first | لا يمكن العثور على تعريف الـ crew | أبقِ `crew.jsonc` و `agents/` في جذر المشروع |
| مُزخرف `@CrewBase` مفقود في crew كلاسيكية | أخطاء "Config not found" | أضف المُزخرف لجميع فئات الـ crew الكلاسيكية |
| ملفات كلاسيكية في الجذر بدل `src/` | نقطة الدخول غير موجودة | انقل ملفات Python الكلاسيكية إلى `src/project_name/` |
| `run()` أو `kickoff()` مفقودة | لا يمكن بدء الأتمتة | أضف دالة الدخول الصحيحة |
## الخطوات التالية

View File

@@ -43,7 +43,7 @@ CrewAI مُصمَّم أصلاً للعمل مع الذكاء الاصطناعي
| المهارة | متى تُستخدم |
|---------|-------------|
| `getting-started` | مشاريع جديدة، الاختيار بين `LLM.call()` / `Agent` / `Crew` / `Flow`، ربط `crew.py` / `main.py` |
| `getting-started` | مشاريع جديدة، الاختيار بين `LLM.call()` / `Agent` / `Crew` / `Flow`، ربط `crew.jsonc` / `main.py` |
| `design-agent` | ضبط الوكلاء — الدور، الهدف، الخلفية، الأدوات، نماذج اللغة، الذاكرة، الحدود الآمنة |
| `design-task` | وصف المهام، التبعيات، المخرجات المنظمة (`output_pydantic`، `output_json`)، المراجعة البشرية |
| `ask-docs` | الاستعلام من [خادم CrewAI docs MCP](https://docs.crewai.com/mcp) للحصول على تفاصيل واجهة البرمجة الحالية |
@@ -64,7 +64,7 @@ CrewAI مُصمَّم أصلاً للعمل مع الذكاء الاصطناعي
<Step title="يحصل وكيلك فوراً على خبرة CrewAI">
تعلّم الحزمة وكيلك:
- **Flows** — تطبيقات ذات حالة، خطوات، وتشغيل crews
- **Crews والوكلاء** — أنماط YAML أولاً، الأدوار، المهام، التفويض
- **Crews والوكلاء** — أنماط JSON-first (`crew.jsonc` و `agents/*.jsonc`)، الأدوار، المهام، التفويض
- **الأدوات والتكاملات** — البحث، واجهات API، خوادم MCP، وأدوات CrewAI الشائعة
- **هيكل المشروع** — هياكل CLI واتفاقيات المستودع
- **أنماط محدثة** — يتماشى مع وثائق CrewAI الحالية وأفضل الممارسات

View File

@@ -1,321 +1,140 @@
---
title: ابنِ أول Crew لك
description: دليل تفصيلي لإنشاء فريق AI تعاوني يعمل معًا لحل المشكلات المعقدة.
title: ابنِ أول Crew
description: دليل خطوة بخطوة لإنشاء فريق AI تعاوني باستخدام تهيئة JSON-first.
icon: users-gear
mode: "wide"
---
## إطلاق قوة الذكاء الاصطناعي التعاوني
## بناء Crew للبحث
تخيل أن لديك فريقًا من Agents الذكاء الاصطناعي المتخصصة تعمل معًا بسلاسة لحل مشكلات معقدة، كل منها يساهم بمهاراته الفريدة لتحقيق هدف مشترك. هذه هي قوة CrewAI - إطار عمل يمكّنك من إنشاء أنظمة ذكاء اصطناعي تعاونية يمكنها إنجاز مهام تفوق بكثير ما يمكن لـ AI واحد تحقيقه بمفرده.
في هذا الدليل ستنشئ crew من Agentين: واحد للبحث وآخر لكتابة تقرير markdown. مشاريع الـ crew الجديدة هي JSON-first: تُعرّف الـ Agents في `agents/*.jsonc`، وتُعرّف المهام وإعدادات الـ crew في `crew.jsonc`، ويحمّل `crewai run` هذا التعريف مباشرة.
في هذا الدليل، سنمشي عبر إنشاء Crew بحث يساعدنا في البحث والتحليل حول موضوع ما، ثم إنشاء تقرير شامل. يوضح هذا المثال العملي كيف يمكن لـ Agents الذكاء الاصطناعي التعاون لإنجاز مهام معقدة، لكنه مجرد البداية لما هو ممكن مع CrewAI.
### المتطلبات
### ما ستبنيه وتتعلمه
1. تثبيت CrewAI من [دليل التثبيت](/ar/installation)
2. إعداد مفتاح LLM من [دليل LLMs](/ar/concepts/llms#setting-up-your-llm)
3. مفتاح [Serper.dev](https://serper.dev/) إذا أردت استخدام البحث على الويب
بنهاية هذا الدليل، ستكون قد:
1. **أنشأت فريق بحث AI متخصص** بأدوار ومسؤوليات مميزة
2. **نسّقت التعاون** بين عدة Agents ذكاء اصطناعي
3. **أتممت سير عمل معقد** يتضمن جمع المعلومات والتحليل وإنشاء التقارير
4. **بنيت مهارات أساسية** يمكنك تطبيقها على مشاريع أكثر طموحًا
### المتطلبات المسبقة
قبل البدء، تأكد من:
1. تثبيت CrewAI باتباع [دليل التثبيت](/ar/installation)
2. إعداد مفتاح API لنموذج LLM في بيئتك، باتباع [دليل إعداد LLM](/ar/concepts/llms#setting-up-your-llm)
3. فهم أساسي لـ Python
## الخطوة 1: إنشاء مشروع CrewAI جديد
أولاً، لننشئ مشروع CrewAI جديد باستخدام CLI. سينشئ هذا الأمر هيكل مشروع كامل بجميع الملفات الضرورية.
## الخطوة 1: إنشاء Crew جديدة
```bash
crewai create crew research_crew
cd research_crew
```
سينتج هيكل مشروع بالبنية الأساسية المطلوبة لـ Crew. ينشئ CLI تلقائيًا:
البنية الناتجة:
- مجلد مشروع بالملفات اللازمة
- ملفات تهيئة للـ Agents والمهام
- تطبيق Crew أساسي
- سكريبت رئيسي لتشغيل الـ Crew
<Frame caption="نظرة عامة على إطار عمل CrewAI">
<img src="/images/crews.png" alt="نظرة عامة على إطار عمل CrewAI" />
</Frame>
## الخطوة 2: استكشاف هيكل المشروع
لنخصص لحظة لفهم هيكل المشروع الذي أنشأه CLI.
```
```text
research_crew/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── 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
├── skills/
└── tools/
```
يتبع هذا الهيكل أفضل الممارسات لمشاريع Python ويسهّل تنظيم الكود. فصل ملفات التهيئة (YAML) عن كود التنفيذ (Python) يسهّل تعديل سلوك Crew دون تغيير الكود الأساسي.
<Tip>
إذا احتجت إلى البنية القديمة التي تحتوي على `crew.py` و `config/agents.yaml` و `config/tasks.yaml`، استخدم `crewai create crew research_crew --classic`.
</Tip>
## الخطوة 3: تهيئة الـ Agents
## الخطوة 2: تعريف الـ Agents
الآن يأتي الجزء الممتع - تعريف Agents الذكاء الاصطناعي! في CrewAI، الـ Agents هي كيانات متخصصة بأدوار وأهداف وخلفيات محددة تشكّل سلوكها.
عدّل ملف `agents/researcher.jsonc` الذي أنشأه القالب، ثم أضف `agents/analyst.jsonc`. يجب أن تطابق أسماء الملفات الأسماء المشار إليها في `crew.jsonc`.
لـ Crew البحث لدينا، سننشئ Agent اثنين:
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...
```jsonc agents/researcher.jsonc
{
"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 who organizes complex information into clear, useful notes.",
// استبدله بالنموذج الذي تستخدمه، مثل "openai/gpt-4o".
"llm": "provider/model-id",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
لاحظ كيف أن لكل Agent دور وهدف وخلفية مميزة. هذه العناصر ليست وصفية فحسب - بل تشكّل بنشاط كيف يتعامل الـ Agent مع مهامه.
## الخطوة 4: تعريف المهام
مع تعريف الـ Agents، نحتاج الآن لمنحهم مهام محددة. لنعدّل ملف `tasks.yaml`:
```yaml
# src/research_crew/config/tasks.yaml
research_task:
description: >
Conduct thorough research on {topic}. Focus on:
1. Key concepts and definitions
2. Historical development and recent trends
3. Major challenges and opportunities
4. Notable applications or case studies
5. Future outlook and potential developments
Make sure to organize your findings in a structured format with clear sections.
expected_output: >
A comprehensive research document with well-organized sections covering
all the requested aspects of {topic}. Include specific facts, figures,
and examples where relevant.
agent: researcher
analysis_task:
description: >
Analyze the research findings and create a comprehensive report on {topic}.
Your report should:
1. Begin with an executive summary
2. Include all key information from the research
3. Provide insightful analysis of trends and patterns
4. Offer recommendations or future considerations
5. Be formatted in a professional, easy-to-read style with clear headings
expected_output: >
A polished, professional report on {topic} that presents the research
findings with added analysis and insights. The report should be well-structured
with an executive summary, main sections, and conclusion.
agent: analyst
context:
- research_task
output_file: output/report.md
```jsonc agents/analyst.jsonc
{
"role": "Report Analyst for {topic}",
"goal": "Turn research findings into a clear, well-structured report.",
"backstory": "You are a careful analyst with strong technical writing skills and a talent for extracting useful insights.",
// استبدله بالنموذج الذي تستخدمه، مثل "openai/gpt-4o".
"llm": "provider/model-id",
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
لاحظ حقل `context` في مهمة التحليل - هذه ميزة قوية تتيح للمحلل الوصول إلى مخرجات مهمة البحث.
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `anthropic/claude-sonnet-4-6` أو `gemini/gemini-2.0-flash-001`.
## الخطوة 5: تهيئة الـ Crew
## الخطوة 3: تعريف المهام وإعدادات الـ Crew
الآن حان الوقت لجمع كل شيء معًا. لنعدّل ملف `crew.py`:
استبدل `crew.jsonc` بما يلي:
```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,
)
```
## الخطوة 6: إعداد السكريبت الرئيسي
```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'
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research on {topic}. Focus on key concepts, recent developments, major challenges, notable applications, and future outlook.",
"expected_output": "A comprehensive research document with organized sections, specific facts, and useful examples about {topic}.",
"agent": "researcher"
},
{
"name": "analysis_task",
"description": "Analyze the research findings and create a polished report on {topic}. Include an executive summary, key insights, trend analysis, and recommendations.",
"expected_output": "A professional markdown report with clear headings, a concise summary, main findings, and recommendations.",
"agent": "analyst",
"context": ["research_task"],
"output_file": "output/report.md",
"markdown": true
}
# 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()
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "Artificial Intelligence in Healthcare"
}
}
```
## الخطوة 7: إعداد متغيرات البيئة
يشير `context` إلى أسماء مهام سابقة، لذلك يحصل analyst على مخرجات مهمة البحث. يوفر `inputs` قيمة افتراضية لـ `{topic}`. إذا حذفت القيمة الافتراضية، سيطلبها `crewai run`.
أنشئ ملف `.env` في جذر مشروعك بمفاتيح API:
## الخطوة 4: متغيرات البيئة
عدّل `.env`:
```sh
SERPER_API_KEY=your_serper_api_key
# Add your provider's API key here too.
# أضف مفتاح مزود النموذج أيضًا.
```
راجع [دليل إعداد LLM](/ar/concepts/llms#setting-up-your-llm) لتفاصيل تهيئة المزود المفضل لديك. يمكنك الحصول على مفتاح Serper API من [Serper.dev](https://serper.dev/).
## الخطوة 8: تثبيت التبعيات
## الخطوة 5: التثبيت والتشغيل
```bash
crewai install
```
## الخطوة 9: تشغيل الـ Crew
الآن اللحظة المثيرة - حان وقت تشغيل Crew ومشاهدة التعاون بين الـ AI!
```bash
crewai run
```
عند تشغيل هذا الأمر، سترى Crew يعمل. سيجمع الباحث معلومات حول الموضوع المحدد، ثم سينشئ المحلل تقريرًا شاملاً بناءً على ذلك البحث.
بعد انتهاء التشغيل، افتح `output/report.md`.
## الخطوة 10: مراجعة المخرجات
بمجرد إتمام Crew عمله، ستجد التقرير النهائي في ملف `output/report.md`. سيتضمن التقرير:
1. ملخص تنفيذي
2. معلومات مفصلة عن الموضوع
3. تحليل ورؤى
4. توصيات أو اعتبارات مستقبلية
## استكشاف أوامر CLI الأخرى
يوفر CrewAI عدة أوامر CLI مفيدة للعمل مع Crews:
```bash
# View all available commands
crewai --help
# Run the crew
crewai run
# Test the crew
crewai test
# Reset crew memories
crewai reset-memories
# Replay from a specific task
crewai replay -t <task_id>
```
## ما بعد أول Crew: فن الممكن
ما بنيته في هذا الدليل مجرد البداية. يمكنك توسيع Crew البحث الأساسي بإضافة Agents متخصصة أخرى وأدوات وقدرات إضافية وسير عمل أكثر تعقيدًا. يمكن تطبيق نفس الأنماط لإنشاء Crews لإنشاء المحتوى وخدمة العملاء وتطوير المنتجات وتحليل البيانات.
## الخطوات التالية
1. جرّب تهيئات وشخصيات Agent مختلفة
2. جرب هياكل مهام وسير عمل أكثر تعقيدًا
3. طبّق أدوات مخصصة لمنح الـ Agents قدرات جديدة
4. طبّق Crew على مواضيع أو مجالات مشكلات مختلفة
5. استكشف [CrewAI Flows](/ar/guides/flows/first-flow) لسير عمل أكثر تقدمًا مع البرمجة الإجرائية
<Warning>
شغّل مشاريع JSON crew من مصادر تثق بها فقط. أدوات `custom:<name>` ومراجع `{"python": "module.attribute"}` تنفذ Python محليًا عند تحميل الـ crew.
</Warning>
<Check>
تهانينا! لقد بنيت بنجاح أول CrewAI Crew يمكنه البحث والتحليل في أي موضوع تقدمه. هذه التجربة الأساسية أهّلتك بالمهارات لإنشاء أنظمة AI متطورة بشكل متزايد يمكنها معالجة مشكلات معقدة متعددة المراحل من خلال الذكاء التعاوني.
أصبحت لديك crew تعمل بأسلوب JSON-first تبحث في موضوع وتكتب تقريرًا.
</Check>

View File

@@ -42,21 +42,26 @@ cd guide_creator_flow
## الخطوة 2: فهم هيكل المشروع
يستخدم الـ crew المبدئي المضمّن في مشروع Flow بنية Python/YAML الكلاسيكية. لاستخدام crew بنمط JSON-first داخل Flow، أنشئ `crew.jsonc` و `agents/*.jsonc` داخل مجلد الـ crew وحمّله عبر `crewai.project.load_crew` كما في [Flows](/ar/concepts/flows#building-your-crews).
```
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
── src/
└── guide_creator_flow/
── __init__.py
├── main.py
├── crews/
│ └── poem_crew/
── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ └── poem_crew.py
└── tools/
└── custom_tool.py
```
يوفر هذا الهيكل فصلاً واضحًا بين مكونات Flow المختلفة. سنعدّل هذا الهيكل لإنشاء Flow منشئ الدليل.
@@ -69,139 +74,77 @@ crewai flow add-crew content-crew
## الخطوة 4: تهيئة Crew كتابة المحتوى
1. حدّث ملف تهيئة الـ Agents. تذكر تعيين `llm` للمزود الذي تستخدمه.
سنهيئ crew كتابة المحتوى باستخدام JSONC. سنعرّف Agent للكتابة وAgent للمراجعة، ثم نحمّل `crew.jsonc` من خطوة Flow.
```yaml
# src/guide_creator_flow/crews/content_crew/config/agents.yaml
content_writer:
role: >
Educational Content Writer
goal: >
Create engaging, informative content that thoroughly explains the assigned topic
and provides valuable insights to the reader
backstory: >
You are a talented educational writer with expertise in creating clear, engaging
content. You have a gift for explaining complex concepts in accessible language
and organizing information in a way that helps readers build their understanding.
llm: provider/model-id
1. أنشئ `src/guide_creator_flow/crews/content_crew/agents/content_writer.jsonc`:
content_reviewer:
role: >
Educational Content Reviewer and Editor
goal: >
Ensure content is accurate, comprehensive, well-structured, and maintains
consistency with previously written sections
backstory: >
You are a meticulous editor with years of experience reviewing educational
content. You have an eye for detail, clarity, and coherence.
llm: provider/model-id
```jsonc
{
"role": "Educational Content Writer",
"goal": "Create engaging, informative content that thoroughly explains the assigned topic and provides valuable insights to the reader.",
"backstory": "You are a talented educational writer who explains complex concepts in accessible language and organizes information clearly.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
2. حدّث ملف تهيئة المهام:
2. أنشئ `src/guide_creator_flow/crews/content_crew/agents/content_reviewer.jsonc`:
```yaml
# src/guide_creator_flow/crews/content_crew/config/tasks.yaml
write_section_task:
description: >
Write a comprehensive section on the topic: "{section_title}"
Section description: {section_description}
Target audience: {audience_level} level learners
Your content should:
1. Begin with a brief introduction to the section topic
2. Explain all key concepts clearly with examples
3. Include practical applications or exercises where appropriate
4. End with a summary of key points
5. Be approximately 500-800 words in length
Format your content in Markdown with appropriate headings, lists, and emphasis.
Previously written sections:
{previous_sections}
expected_output: >
A well-structured, comprehensive section in Markdown format that thoroughly
explains the topic and is appropriate for the target audience.
agent: content_writer
review_section_task:
description: >
Review and improve the following section on "{section_title}":
{draft_content}
Target audience: {audience_level} level learners
Previously written sections:
{previous_sections}
Your review should:
1. Fix any grammatical or spelling errors
2. Improve clarity and readability
3. Ensure content is comprehensive and accurate
4. Verify consistency with previously written sections
5. Enhance the structure and flow
6. Add any missing key information
expected_output: >
An improved, polished version of the section that maintains the original
structure but enhances clarity, accuracy, and consistency.
agent: content_reviewer
context:
- write_section_task
```jsonc
{
"role": "Educational Content Reviewer and Editor",
"goal": "Ensure content is accurate, comprehensive, well-structured, and consistent with previously written sections.",
"backstory": "You are a meticulous editor with an eye for detail, clarity, and coherence.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
3. حدّث ملف تنفيذ Crew:
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `gemini/gemini-2.0-flash-001` أو `anthropic/claude-sonnet-4-6`.
3. أنشئ `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
```jsonc
{
"name": "Content Crew",
"agents": ["content_writer", "content_reviewer"],
"tasks": [
{
"name": "write_section_task",
"description": "Write a comprehensive section on the topic: \"{section_title}\".\n\nSection description: {section_description}\nTarget audience: {audience_level} level learners\n\nYour content should begin with a brief introduction, explain key concepts clearly with examples, include practical applications where appropriate, end with a summary, and be approximately 500-800 words.\n\nPreviously written sections:\n{previous_sections}",
"expected_output": "A well-structured, comprehensive section in Markdown format that thoroughly explains the topic and is appropriate for the target audience.",
"agent": "content_writer",
"markdown": true
},
{
"name": "review_section_task",
"description": "Review and improve this section on \"{section_title}\":\n\n{draft_content}\n\nTarget audience: {audience_level} level learners\nPreviously written sections:\n{previous_sections}\n\nFix errors, improve clarity, verify consistency, enhance structure, and add missing key information.",
"expected_output": "An improved, polished version of the section that maintains the original structure but enhances clarity, accuracy, and consistency.",
"agent": "content_reviewer",
"context": ["write_section_task"],
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
4. استبدل `src/guide_creator_flow/crews/content_crew/content_crew.py` بمحمل صغير:
```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
from pathlib import Path
@CrewBase
class ContentCrew():
"""Content writing crew"""
from crewai.project import load_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,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
## الخطوة 5: إنشاء Flow

View File

@@ -113,7 +113,7 @@ python3 --version
# إنشاء مشروع CrewAI
نوصي باستخدام قالب `YAML` لنهج منظم في تعريف الـ Agents والمهام. إليك كيفية البدء:
يقوم `crewai create crew` الآن بإنشاء مشروع crew بأسلوب JSON-first. توضع الـ Agents في `agents/*.jsonc`، وتوضع المهام وإعدادات الـ crew في `crew.jsonc`، ويحمّل `crewai run` هذا التعريف مباشرة.
<Steps>
<Step title="إنشاء هيكل المشروع">
@@ -126,21 +126,20 @@ python3 --version
```
my_project/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── pyproject.toml
├── README.md
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
- إذا احتجت إلى البنية القديمة Python/YAML التي تحتوي على `crew.py` و `config/agents.yaml` و `config/tasks.yaml`، شغّل:
```shell
crewai create crew <your_project_name> --classic
```
</Step>
@@ -149,15 +148,15 @@ python3 --version
- سيحتوي مشروعك على هذه الملفات الأساسية:
| الملف | الغرض |
| --- | --- |
| `agents.yaml` | تعريف الـ Agents وأدوارهم |
| `tasks.yaml` | إعداد مهام الـ Agents وسير العمل |
| `crew.jsonc` | إعداد الـ crew وترتيب المهام والعملية وقيم الإدخال الافتراضية |
| `agents/*.jsonc` | تعريف دور كل Agent وهدفه و backstory والـ LLM والأدوات والسلوك |
| `.env` | تخزين مفاتيح API ومتغيرات البيئة |
| `main.py` | نقطة دخول المشروع وتدفق التنفيذ |
| `crew.py` | تنسيق وإدارة الـ Crew |
| `tools/` | مجلد الأدوات المخصصة |
| `knowledge/` | مجلد قاعدة المعرفة |
| `tools/` | ملفات Python اختيارية لأدوات `custom:<name>` |
| `knowledge/` | ملفات معرفة اختيارية للـ Agents |
| `skills/` | ملفات skills اختيارية تطبق على الـ crew |
- ابدأ بتحرير `agents.yaml` و`tasks.yaml` لتعريف سلوك الـ Crew.
- ابدأ بتحرير `crew.jsonc` والملفات داخل `agents/` لتعريف سلوك الـ crew.
- استخدم قيم `{placeholder}` في نصوص الـ Agents والمهام، ثم ضع القيم الافتراضية في `inputs` داخل `crew.jsonc`. عند تشغيل `crewai run` ستطلب CLI أي قيم ناقصة.
- احتفظ بالمعلومات الحساسة مثل مفاتيح API في `.env`.
</Step>

View File

@@ -5,11 +5,15 @@ icon: "at"
mode: "wide"
---
يشرح هذا الدليل كيفية استخدام التعليقات التوضيحية للإشارة بشكل صحيح إلى **الوكلاء** و**المهام** والمكونات الأخرى في ملف `crew.py`.
يشرح هذا الدليل كيفية استخدام التعليقات التوضيحية للإشارة بشكل صحيح إلى **الوكلاء** و**المهام** والمكونات الأخرى في ملف `crew.py` كلاسيكي.
<Note>
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` هي JSON-first وتستخدم `crew.jsonc` مع `agents/*.jsonc`. استخدم هذا الدليل عند العمل في مشروع كلاسيكي أُنشئ عبر `crewai create crew <name> --classic`، أو عند ترحيل مشروع Python/YAML موجود، أو عندما تحتاج تحكمًا عبر decorators في Python.
</Note>
## مقدمة
تُستخدم التعليقات التوضيحية في إطار عمل CrewAI لتزيين الفئات والطرق، مما يوفر بيانات وصفية ووظائف للمكونات المختلفة في طاقمك. تساعد هذه التعليقات التوضيحية في تنظيم وهيكلة الكود الخاص بك، مما يجعله أكثر قابلية للقراءة والصيانة.
تُستخدم التعليقات التوضيحية في إطار عمل CrewAI لتزيين الفئات والطرق، مما يوفر بيانات وصفية ووظائف للمكونات المختلفة في طاقمك. في مشاريع Python/YAML الكلاسيكية، تنظم الكود الذي يحمّل `config/agents.yaml` و `config/tasks.yaml` ويعيد كائن `Crew`.
## التعليقات التوضيحية المتاحة
@@ -113,9 +117,9 @@ def crew(self) -> Crew:
تُستخدم التعليقة التوضيحية `@crew` لتزيين الطريقة التي تنشئ وتُرجع كائن `Crew`. تجمع هذه الطريقة جميع المكونات (الوكلاء والمهام) في طاقم وظيفي.
## إعداد YAML
## إعداد YAML الكلاسيكي
تُخزن إعدادات الوكلاء عادةً في ملف YAML. إليك مثالاً على كيفية ظهور ملف `agents.yaml` لوكيل الباحث:
في المشاريع الكلاسيكية، تُخزن إعدادات الوكلاء عادةً في ملف YAML. إليك مثالاً على كيفية ظهور ملف `agents.yaml` لوكيل الباحث:
```yaml
researcher:
@@ -146,6 +150,6 @@ researcher:
- **تسمية متسقة**: استخدم اصطلاحات تسمية واضحة ومتسقة لطرقك. على سبيل المثال، يمكن تسمية طرق الوكلاء بأسماء أدوارهم (مثل researcher، reporting_analyst).
- **متغيرات البيئة**: استخدم متغيرات البيئة للمعلومات الحساسة مثل مفاتيح API.
- **المرونة**: صمم طاقمك ليكون مرناً بالسماح بإضافة أو إزالة الوكلاء والمهام بسهولة.
- **توافق YAML-الكود**: تأكد من أن الأسماء والهياكل في ملفات YAML تتوافق بشكل صحيح مع الطرق المزينة في كود Python الخاص بك.
- **توافق YAML-الكود**: في المشاريع الكلاسيكية، تأكد من أن الأسماء والهياكل في ملفات YAML تتوافق بشكل صحيح مع الطرق المزينة في كود Python الخاص بك.
باتباع هذه الإرشادات واستخدام التعليقات التوضيحية بشكل صحيح، يمكنك إنشاء أطقم منظمة جيداً وسهلة الصيانة باستخدام إطار عمل CrewAI.
باتباع هذه الإرشادات واستخدام التعليقات التوضيحية بشكل صحيح، يمكنك الحفاظ على أطقم كلاسيكية منظمة وسهلة الصيانة. للـ crews الجديدة، استخدم بنية JSON-first في [Crews](/ar/concepts/crews).

View File

@@ -39,84 +39,60 @@ mode: "wide"
يُنشئ ذلك تطبيق Flow ضمن `src/latest_ai_flow/`، بما في ذلك طاقمًا أوليًا في `crews/content_crew/` ستستبدله بطاقم بحث **بوكيل واحد** في الخطوات التالية.
</Step>
<Step title="اضبط وكيلًا واحدًا في `agents.yaml`">
استبدل محتوى `src/latest_ai_flow/crews/content_crew/config/agents.yaml` بباحث واحد. تُملأ المتغيرات مثل `{topic}` من `crew.kickoff(inputs=...)`.
<Step title="اضبط وكيلًا واحدًا في JSONC">
أنشئ `src/latest_ai_flow/crews/content_crew/agents/researcher.jsonc` (أنشئ مجلد `agents/` إذا لزم). تُملأ المتغيرات مثل `{topic}` من `crew.kickoff(inputs=...)`.
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
researcher:
role: >
باحث بيانات أول في {topic}
goal: >
اكتشاف أحدث التطورات في {topic}
backstory: >
أنت باحث مخضرم تكشف أحدث المستجدات في {topic}.
تجد المعلومات الأكثر صلة وتعرضها بوضوح.
```jsonc agents/researcher.jsonc
{
"role": "باحث بيانات أول في {topic}",
"goal": "اكتشاف أحدث التطورات في {topic}",
"backstory": "أنت باحث يجد المعلومات الأكثر صلة ويعرضها بوضوح.",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true
}
}
```
</Step>
<Step title="اضبط مهمة واحدة في `tasks.yaml`">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
research_task:
description: >
أجرِ بحثًا معمقًا عن {topic}. استخدم البحث على الويب للعثور على معلومات
حديثة وموثوقة. السنة الحالية 2026.
expected_output: >
تقرير بصيغة Markdown بأقسام واضحة: الاتجاهات الرئيسية، أدوات أو شركات بارزة،
والآثار. بين 800 و1200 كلمة تقريبًا. دون إحاطة المستند بأكمله بكتل كود.
agent: researcher
output_file: output/report.md
<Step title="اضبط الـ crew في `crew.jsonc`">
أنشئ `src/latest_ai_flow/crews/content_crew/crew.jsonc`:
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "أجرِ بحثًا معمقًا عن {topic}. استخدم البحث على الويب للعثور على معلومات حديثة وموثوقة.",
"expected_output": "تقرير بصيغة Markdown بأقسام واضحة: الاتجاهات الرئيسية، أدوات أو شركات بارزة، والآثار. بين 800 و1200 كلمة تقريبًا. دون إحاطة المستند بأكمله بكتل كود.",
"agent": "researcher",
"output_file": "output/report.md",
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
</Step>
<Step title="اربط صف الطاقم (`content_crew.py`)">
اجعل الطاقم المُولَّد يشير إلى YAML وأرفق `SerperDevTool` بالباحث.
<Step title="حمّل crew JSON (`content_crew.py`)">
استبدل `content_crew.py` المُولّد بمحمل صغير يحول `crew.jsonc` إلى `Crew`.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
from pathlib import Path
from crewai import Agent, Crew, Process, Task
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.project import load_crew
@CrewBase
class ResearchCrew:
"""طاقم بحث بوكيل واحد داخل Flow."""
agents: List[BaseAgent]
tasks: List[Task]
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config["research_task"], # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
</Step>
@@ -130,7 +106,7 @@ mode: "wide"
from crewai.flow import Flow, listen, start
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
from latest_ai_flow.crews.content_crew.content_crew import kickoff_content_crew
class ResearchFlowState(BaseModel):
@@ -149,7 +125,7 @@ mode: "wide"
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
result = kickoff_content_crew(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("اكتمل طاقم البحث.")
@@ -171,7 +147,7 @@ mode: "wide"
```
<Tip>
إذا كان اسم الحزمة ليس `latest_ai_flow`، عدّل استيراد `ResearchCrew` ليطابق مسار الوحدة في مشروعك.
إذا كان اسم الحزمة ليس `latest_ai_flow`، عدّل استيراد `kickoff_content_crew` ليطابق مسار الوحدة في مشروعك.
</Tip>
</Step>
@@ -198,7 +174,7 @@ mode: "wide"
<CodeGroup>
```markdown output/report.md
# وكلاء الذكاء الاصطناعي في 2026: المشهد والاتجاهات
# وكلاء الذكاء الاصطناعي: المشهد والاتجاهات الحديثة
## ملخص تنفيذي
@@ -219,7 +195,7 @@ mode: "wide"
## كيف يترابط هذا
1. **Flow** — يشغّل `LatestAiFlow` أولًا `prepare_topic` ثم `run_research` ثم `summarize`. الحالة (`topic`، `report`) على Flow.
2. **الطاقم** — يشغّل `ResearchCrew` مهمة واحدة بوكيل واحد: الباحث يستخدم **Serper** للبحث على الويب ثم يكتب التقرير.
2. **الطاقم** — يحمّل `kickoff_content_crew` ملف `crew.jsonc` ويشغّل مهمة واحدة بوكيل واحد: الباحث يستخدم **Serper** للبحث على الويب ثم يكتب التقرير.
3. **المُخرَج** — يكتب `output_file` للمهمة التقرير في `output/report.md`.
للتعمق في أنماط Flow (التوجيه، الاستمرارية، الإنسان في الحلقة)، راجع [ابنِ أول Flow](/ar/guides/flows/first-flow) و[Flows](/ar/concepts/flows). للطواقم دون Flow، راجع [Crews](/ar/concepts/crews). لوكيل `Agent` واحد و`kickoff()` بلا مهام، راجع [Agents](/ar/concepts/agents#direct-agent-interaction-with-kickoff).
@@ -230,7 +206,10 @@ mode: "wide"
### اتساق التسمية
يجب أن تطابق مفاتيح YAML (`researcher`، `research_task`) أسماء الدوال في صف `@CrewBase`. راجع [Crews](/ar/concepts/crews) لنمط الديكورات الكامل.
يجب أن تطابق الأسماء في `crew.jsonc` الملفات والمراجع:
- `agents: ["researcher"]` يحمّل `agents/researcher.jsonc`
- `tasks[].agent: "researcher"` يربط المهمة بذلك الـ agent
## النشر

View File

@@ -20,7 +20,7 @@ npx skills add crewaiinc/skills
## ما يحصل عليه الوكيل
- **Flows** — تطبيقات ذات حالة وخطوات وkickoffs للـ crew على نمط CrewAI
- **Crews والوكلاء** — أنماط YAML أولاً، أدوار، مهام، وتفويض
- **Crews والوكلاء** — أنماط JSON-first (`crew.jsonc` و `agents/*.jsonc`)، أدوار، مهام، وتفويض
- **الأدوات والتكاملات** — ربط الوكلاء بالبحث وواجهات API وأدوات CrewAI الشائعة
- **هيكل المشروع** — مواءمة مع قوالب CLI واتفاقيات المستودع
- **أنماط محدثة** — تتبع المهارات وثائق CrewAI والممارسات الموصى بها

View File

@@ -71,81 +71,65 @@ The Visual Agent Builder enables:
## Creating Agents
There are two ways to create agents in CrewAI: using **YAML configuration (recommended)** or defining them **directly in code**.
There are two common ways to create agents in CrewAI: using **JSONC project configuration (recommended for new crews)** or defining them **directly in code**.
### YAML Configuration (Recommended)
### JSONC Configuration (Recommended)
Using YAML configuration provides a cleaner, more maintainable way to define agents. We strongly recommend using this approach in your CrewAI projects.
New projects created with `crewai create crew <name>` use JSON-first configuration. Each agent is defined in `agents/<agent_name>.jsonc`, and `crew.jsonc` lists which agents are part of the crew.
After creating your CrewAI project as outlined in the [Installation](/en/installation) section, navigate to the `src/latest_ai_development/config/agents.yaml` file and modify the template to match your requirements.
After creating your CrewAI project as outlined in the [Installation](/en/installation) section, edit the generated files in `agents/`.
<Note>
Variables in your YAML files (like `{topic}`) will be replaced with values from your inputs when running the crew:
```python Code
crew.kickoff(inputs={'topic': 'AI Agents'})
```
Use `{placeholder}` values in `role`, `goal`, or `backstory`. Put defaults in `crew.jsonc` under `inputs`; `crewai run` prompts for any missing values.
</Note>
Here's an example of how to configure agents using YAML:
Here's an example `agents/researcher.jsonc` file:
```yaml agents.yaml
# src/latest_ai_development/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
goal: >
Uncover cutting-edge developments in {topic}
backstory: >
You're a seasoned researcher with a knack for uncovering the latest
developments in {topic}. Known for your ability to find the most relevant
information and present it in a clear and concise manner.
reporting_analyst:
role: >
{topic} Reporting Analyst
goal: >
Create detailed reports based on {topic} data analysis and research findings
backstory: >
You're a meticulous analyst with a keen eye for detail. You're known for
your ability to turn complex data into clear and concise reports, making
it easy for others to understand and act on the information you provide.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You find the most relevant information and present it clearly.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false,
"max_iter": 20
}
}
```
To use this YAML configuration in your code, create a crew class that inherits from `CrewBase`:
Then include that agent from `crew.jsonc`:
```python Code
# src/latest_ai_development/crew.py
from crewai import Agent, Crew, Process
from crewai.project import CrewBase, agent, crew
from crewai_tools import SerperDevTool
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
agents_config = "config/agents.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()]
)
@agent
def reporting_analyst(self) -> Agent:
return Agent(
config=self.agents_config['reporting_analyst'], # type: ignore[index]
verbose=True
)
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Research {topic}",
"expected_output": "A concise briefing about {topic}",
"agent": "researcher"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
Agent files support any public `Agent` field. Common fields include `role`, `goal`, `backstory`, `llm`, `tools`, `function_calling_llm`, `guardrail`, `step_callback`, and `settings`. Behavior options such as `verbose`, `allow_delegation`, `max_iter`, `max_rpm`, `memory`, `cache`, `planning_config`, and `use_system_prompt` can be placed at the top level or under `settings`; values in `settings` take precedence.
<Note>
The names you use in your YAML files (`agents.yaml`) should match the method
names in your Python code.
JSONC supports comments and trailing commas. If both `agents/<name>.jsonc` and `agents/<name>.json` exist, CrewAI uses the JSONC file.
</Note>
### Classic YAML Configuration
Classic projects created with `crewai create crew <name> --classic` use `config/agents.yaml` and a `@CrewBase` class in `crew.py`. This remains supported for teams that want Python decorators or existing YAML projects.
### Direct Code Definition
You can create agents directly in code by instantiating the `Agent` class. Here's a comprehensive example showing all available parameters:

View File

@@ -52,6 +52,8 @@ crewai create crew my_new_crew
crewai create flow my_new_flow
```
By default, `crewai create crew` creates a JSON-first crew project with `crew.jsonc` and `agents/*.jsonc`. Use `crewai create crew my_new_crew --classic` only when you want the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`.
### 2. Version
Show the installed version of CrewAI.
@@ -185,7 +187,20 @@ crewai chat
Ensure you execute these commands from your CrewAI project's root directory.
</Note>
<Note>
IMPORTANT: Set the `chat_llm` property in your `crew.py` file to enable this command.
IMPORTANT: Set the `chat_llm` property in your crew definition to enable this command.
For JSON-first crews, add it to `crew.jsonc`:
```jsonc
{
"name": "My Crew",
"agents": ["researcher"],
"tasks": [],
"chat_llm": "openai/gpt-4o"
}
```
For classic Python/YAML crews, set it in `crew.py`:
```python
@crew
@@ -336,7 +351,7 @@ Notes:
### 12. API Keys
When running `crewai create crew` command, the CLI will show you a list of available LLM providers to choose from, followed by model selection for your chosen provider.
When running the `crewai create crew` command, the CLI shows a list of available LLM providers, followed by model selection for your chosen provider. The selected model is saved in the generated `.env` file and each generated agent JSONC file can set its own `llm`.
Once you've selected an LLM provider and model, you will be prompted for API keys.

View File

@@ -48,108 +48,74 @@ A crew in crewAI represents a collaborative group of agents working together to
## Creating Crews
There are two ways to create crews in CrewAI: using **YAML configuration (recommended)** or defining them **directly in code**.
There are two common ways to create crews in CrewAI: using **JSONC project configuration (recommended for new crews)** or defining them **directly in code**.
### YAML Configuration (Recommended)
### JSONC Configuration (Recommended)
Using YAML configuration provides a cleaner, more maintainable way to define crews and is consistent with how agents and tasks are defined in CrewAI projects.
New projects created with `crewai create crew <name>` use `crew.jsonc` for crew-level settings and tasks, plus one file per agent in `agents/`.
After creating your CrewAI project as outlined in the [Installation](/en/installation) section, you can define your crew in a class that inherits from `CrewBase` and uses decorators to define agents, tasks, and the crew itself.
`crewai run` automatically detects `crew.jsonc` or `crew.json`, loads the referenced agents, prompts for missing placeholders, and kicks off the crew.
#### Example Crew Class with Decorators
#### Example `crew.jsonc`
```python code
from crewai import Agent, Crew, Task, Process
from crewai.project import CrewBase, agent, task, crew, before_kickoff, after_kickoff
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class YourCrewName:
"""Description of your crew"""
agents: List[BaseAgent]
tasks: List[Task]
# Paths to your YAML configuration files
# To see an example agent and task defined in YAML, checkout the following:
# - Task: https://docs.crewai.com/concepts/tasks#yaml-configuration-recommended
# - Agents: https://docs.crewai.com/concepts/agents#yaml-configuration-recommended
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'
@before_kickoff
def prepare_inputs(self, inputs):
# Modify inputs before the crew starts
inputs['additional_data'] = "Some extra information"
return inputs
@after_kickoff
def process_output(self, output):
# Modify output after the crew finishes
output.raw += "\nProcessed after kickoff."
return output
@agent
def agent_one(self) -> Agent:
return Agent(
config=self.agents_config['agent_one'], # type: ignore[index]
verbose=True
)
@agent
def agent_two(self) -> Agent:
return Agent(
config=self.agents_config['agent_two'], # type: ignore[index]
verbose=True
)
@task
def task_one(self) -> Task:
return Task(
config=self.tasks_config['task_one'] # type: ignore[index]
)
@task
def task_two(self) -> Task:
return Task(
config=self.tasks_config['task_two'] # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents, # Automatically collected by the @agent decorator
tasks=self.tasks, # Automatically collected by the @task decorator.
process=Process.sequential,
verbose=True,
)
```jsonc crew.jsonc
{
"name": "Market Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research",
"description": "Research {topic} and collect the most relevant facts.",
"expected_output": "Structured research notes about {topic}.",
"agent": "researcher"
},
{
"name": "analysis",
"description": "Analyze the research and write a concise report.",
"expected_output": "A markdown report with findings and recommendations.",
"agent": "analyst",
"context": ["research"],
"output_file": "output/report.md"
}
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "AI Agents"
}
}
```
How to run the above code:
Each string in `agents` resolves to `agents/<name>.jsonc` first, then `agents/<name>.json`.
```python code
YourCrewName().crew().kickoff(inputs={"any": "input here"})
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Researcher",
"goal": "Find accurate and current information about {topic}.",
"backstory": "You are a careful researcher who cites clear evidence.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"]
}
```
<Note>
Tasks will be executed in the order they are defined.
Tasks run in the order they appear in `tasks` when `process` is `"sequential"`.
</Note>
The `CrewBase` class, along with these decorators, automates the collection of agents and tasks, reducing the need for manual management.
For hierarchical crews, set `"process": "hierarchical"` and provide either `manager_llm` or `manager_agent`. A `manager_agent` can reference an `agents/<name>.jsonc` file that is not included in the top-level `agents` list.
#### Decorators overview from `annotations.py`
JSON crew definitions support crew-level fields such as `process`, `verbose`, `memory`, `cache`, `max_rpm`, `planning`, `planning_llm`, `manager_llm`, `manager_agent`, `function_calling_llm`, `output_log_file`, `stream`, `tracing`, `before_kickoff_callbacks`, and `after_kickoff_callbacks`.
CrewAI provides several decorators in the `annotations.py` file that are used to mark methods within your crew class for special handling:
Python callbacks and custom classes use `{"python": "module.attribute"}`. Custom tools use `"custom:<name>"` and load `tools/<name>.py` at runtime.
- `@CrewBase`: Marks the class as a crew base class.
- `@agent`: Denotes a method that returns an `Agent` object.
- `@task`: Denotes a method that returns a `Task` object.
- `@crew`: Denotes the method that returns the `Crew` object.
- `@before_kickoff`: (Optional) Marks a method to be executed before the crew starts.
- `@after_kickoff`: (Optional) Marks a method to be executed after the crew finishes.
<Warning>
Only run JSON crew projects from sources you trust. `custom:<name>` tools and `{"python": "module.attribute"}` references execute local Python code when the crew loads.
</Warning>
These decorators help in organizing your crew's structure and automatically collecting agents and tasks without manually listing them.
### Classic Python/YAML Configuration
Classic projects created with `crewai create crew <name> --classic` use `crew.py`, `config/agents.yaml`, `config/tasks.yaml`, and the `@CrewBase`, `@agent`, `@task`, and `@crew` decorators. That pattern remains supported and is documented in [Using Annotations](/en/learn/using-annotations).
### Direct Code Definition (Alternative)

View File

@@ -831,7 +831,7 @@ You can generate a new CrewAI project that includes all the scaffolding needed t
crewai create flow name_of_flow
```
This command will generate a new CrewAI project with the necessary folder structure. The generated project includes a prebuilt crew called `poem_crew` that is already working. You can use this crew as a template by copying, pasting, and editing it to create other crews.
This command will generate a new CrewAI project with the necessary folder structure. The generated project includes a prebuilt crew called `poem_crew` that is already working. The starter embedded crew uses the classic Python/YAML layout; new standalone crews created with `crewai create crew` use the JSON-first layout.
### Folder Structure
@@ -855,13 +855,35 @@ After running the `crewai create flow name_of_flow` command, you will see a fold
### Building Your Crews
In the `crews` folder, you can define multiple crews. Each crew will have its own folder containing configuration files and the crew definition file. For example, the `poem_crew` folder contains:
In the `crews` folder, you can define multiple crews. The generated `poem_crew` uses the classic embedded-crew structure:
- `config/agents.yaml`: Defines the agents for the crew.
- `config/tasks.yaml`: Defines the tasks for the crew.
- `poem_crew.py`: Contains the crew definition, including agents, tasks, and the crew itself.
You can copy, paste, and edit the `poem_crew` to create other crews.
You can copy, paste, and edit the `poem_crew` to create other classic embedded crews.
For JSON-first embedded crews, use a folder with `crew.jsonc` and `agents/*.jsonc` instead:
```text
crews/
└── research_crew/
├── agents/
│ └── researcher.jsonc
└── crew.jsonc
```
Then load it from a Flow step:
```python
from pathlib import Path
from crewai.project import load_crew
crew, default_inputs = load_crew(
Path(__file__).parent / "crews" / "research_crew" / "crew.jsonc"
)
result = crew.kickoff(inputs={**default_inputs, "topic": "AI Agents"})
```
### Connecting Crews in `main.py`

View File

@@ -74,104 +74,54 @@ crew = Crew(
## Creating Tasks
There are two ways to create tasks in CrewAI: using **YAML configuration (recommended)** or defining them **directly in code**.
There are two common ways to create tasks in CrewAI: using **JSONC project configuration (recommended for new crews)** or defining them **directly in code**.
### YAML Configuration (Recommended)
### JSONC Configuration (Recommended)
Using YAML configuration provides a cleaner, more maintainable way to define tasks. We strongly recommend using this approach to define tasks in your CrewAI projects.
New projects created with `crewai create crew <name>` define tasks in `crew.jsonc`. The `agents` array points to files in `agents/`, and the `tasks` array defines the ordered work the crew should run.
After creating your CrewAI project as outlined in the [Installation](/en/installation) section, navigate to the `src/latest_ai_development/config/tasks.yaml` file and modify the template to match your specific task requirements.
After creating your CrewAI project as outlined in the [Installation](/en/installation) section, edit the generated `crew.jsonc`.
<Note>
Variables in your YAML files (like `{topic}`) will be replaced with values from your inputs when running the crew:
```python Code
crew.kickoff(inputs={'topic': 'AI Agents'})
```
Use `{placeholder}` values in task `description`, `expected_output`, and `output_file`. Put defaults in the top-level `inputs` object; `crewai run` prompts for any missing values.
</Note>
Here's an example of how to configure tasks using YAML:
Here's an example `crew.jsonc` with two ordered tasks:
````yaml tasks.yaml
research_task:
description: >
Conduct a thorough research about {topic}
Make sure you find any interesting and relevant information given
the current year is 2025.
expected_output: >
A list with 10 bullet points of the most relevant information about {topic}
agent: researcher
reporting_task:
description: >
Review the context you got and expand each topic into a full section for a report.
Make sure the report is detailed and contains any and all relevant information.
expected_output: >
A fully fledge reports with the mains topics, each with a full section of information.
Formatted as markdown without '```'
agent: reporting_analyst
markdown: true
output_file: report.md
````jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "reporting_analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research about {topic}. Include current and relevant information.",
"expected_output": "A list of the most relevant information about {topic}.",
"agent": "researcher"
},
{
"name": "reporting_task",
"description": "Review the research and expand it into a detailed report.",
"expected_output": "A polished markdown report without fenced code blocks.",
"agent": "reporting_analyst",
"context": ["research_task"],
"markdown": true,
"output_file": "report.md"
}
],
"inputs": {
"topic": "AI Agents"
}
}
````
To use this YAML configuration in your code, create a crew class that inherits from `CrewBase`:
Each task must include `description` and `expected_output`. The `agent` value should match an agent name listed in `agents`. `context` is a list of prior task names; forward references are rejected so sequential context stays explicit.
```python crew.py
# src/latest_ai_development/crew.py
Task entries support any public `Task` field. Common fields include `name`, `agent`, `context`, `output_file`, `tools`, `human_input`, `async_execution`, `guardrail`, `guardrails`, `guardrail_max_retries`, `markdown`, `input_files`, `output_json`, `output_pydantic`, `response_model`, and `converter_cls`. Use `"type": "ConditionalTask"` with a `condition` field for conditional tasks.
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
### Classic YAML Configuration
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()]
)
@agent
def reporting_analyst(self) -> Agent:
return Agent(
config=self.agents_config['reporting_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 reporting_task(self) -> Task:
return Task(
config=self.tasks_config['reporting_task'] # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=[
self.researcher(),
self.reporting_analyst()
],
tasks=[
self.research_task(),
self.reporting_task()
],
process=Process.sequential
)
```
<Note>
The names you use in your YAML files (`agents.yaml` and `tasks.yaml`) should
match the method names in your Python code.
</Note>
Classic projects created with `crewai create crew <name> --classic` use `config/tasks.yaml` and a `@CrewBase` class in `crew.py`. This remains supported for existing YAML projects or teams that prefer decorator-based Python wiring.
### Direct Code Definition (Alternative)

View File

@@ -26,10 +26,10 @@ Before running this verification:
## Step 1 — Scaffold a Verification Crew
Create a new crew project. The CrewAI CLI scaffolds the structure:
Create a classic crew project because this example wires a Python tool through `crew.py`:
```bash
crewai create crew rotation_verifier --skip_provider
crewai create crew rotation_verifier --classic --skip_provider
cd rotation_verifier
```

View File

@@ -374,17 +374,17 @@ git push
**Solution**: Verify your project matches the expected structure:
- **Both Crews and Flows**: Must have entry point at `src/project_name/main.py`
- **Crews**: Use a `run()` function as entry point
- **Flows**: Use a `kickoff()` function as entry point
- **JSON-first Crews**: Keep `crew.jsonc` or `crew.json` and `agents/` at the project root
- **Classic Crews**: Use `src/project_name/main.py` with a `run()` entry point
- **Flows**: Use `src/project_name/main.py` with a `kickoff()` entry point
See [Prepare for Deployment](/en/enterprise/guides/prepare-for-deployment) for detailed structure diagrams.
#### Missing CrewBase Decorator
#### Missing CrewBase Decorator in a Classic Crew
**Symptom**: "Crew not found", "Config not found", or agent/task configuration errors
**Solution**: Ensure **all** crew classes use the `@CrewBase` decorator:
**Solution**: For classic Python/YAML crews, ensure all crew classes use the `@CrewBase` decorator. JSON-first crews do not need this decorator.
```python
from crewai.project import CrewBase, agent, crew, task
@@ -404,8 +404,8 @@ class YourCrew():
```
<Info>
This applies to standalone Crews AND crews embedded inside Flow projects.
Every crew class needs the decorator.
This applies to classic Python crew classes, including classic crews embedded inside Flow projects.
JSON-first crews are validated from `crew.jsonc` and `agents/` instead.
</Info>
#### Incorrect pyproject.toml Type
@@ -442,8 +442,8 @@ type = "flow"
**Solution**:
1. Check the execution logs in the AMP dashboard (Traces tab)
2. Verify all tools have required API keys configured
3. Ensure agent configurations in `agents.yaml` are valid
4. Check task configurations in `tasks.yaml` for syntax errors
3. For JSON-first crews, validate `crew.jsonc` and the referenced files in `agents/`
4. For classic crews, ensure `agents.yaml` and `tasks.yaml` are valid
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
Contact our support team for assistance with deployment issues or questions

View File

@@ -24,10 +24,9 @@ company-ai/
`-- crews/
|-- support_agent/
| |-- pyproject.toml
| `-- src/
| `-- support_agent/
| |-- main.py
| `-- crew.py
| |-- crew.jsonc
| `-- agents/
| `-- support_agent.jsonc
`-- research_flow/
|-- pyproject.toml
`-- src/
@@ -48,7 +47,7 @@ folder as the automation project root.
When a working directory is set, AMP uses that folder for:
- Project validation, including `pyproject.toml`, `src/`, and the Crew or Flow entry point
- Project validation, including `pyproject.toml`, JSON crew files, and any classic Crew or Flow entry point
- Dependency installation with `uv`
- The running process working directory
- The `CREW_ROOT_DIR` environment variable
@@ -153,11 +152,16 @@ removes trailing slashes. A blank value uses the repository root.
## Lock Files and UV Workspaces
The selected folder must contain the automation's `pyproject.toml` and `src/`
directory. A `uv.lock` or `poetry.lock` file can live either in the selected
folder or at the repository root.
The selected folder must contain the automation's `pyproject.toml` and the
project files for its type:
This supports both common monorepo layouts:
- JSON-first crew: `crew.jsonc` or `crew.json`, plus `agents/`
- Classic crew or Flow: `src/` with the expected Python entry point
A `uv.lock` or `poetry.lock` file can live either in the selected folder or at
the repository root.
This supports both common lock-file layouts:
<Tabs>
<Tab title="Project lock file">
@@ -167,9 +171,9 @@ This supports both common monorepo layouts:
`-- support_agent/
|-- pyproject.toml
|-- uv.lock
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
@@ -182,9 +186,9 @@ This supports both common monorepo layouts:
`-- crews/
`-- support_agent/
|-- pyproject.toml
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
</Tabs>

View File

@@ -24,7 +24,7 @@ Understanding which type you're deploying is essential because they have differe
<CardGroup cols={2}>
<Card title="Crew Projects" icon="users">
Standalone AI agent teams with `crew.py` defining agents and tasks. Best for focused, collaborative tasks.
Standalone AI agent teams. New crews are JSON-first with `crew.jsonc` and `agents/`; classic crews can still use `crew.py`.
</Card>
<Card title="Flow Projects" icon="diagram-project">
Orchestrated workflows with embedded crews in a `crews/` folder. Best for complex, multi-stage processes.
@@ -33,19 +33,19 @@ Understanding which type you're deploying is essential because they have differe
| Aspect | Crew | Flow |
|--------|------|------|
| **Project structure** | `src/project_name/` with `crew.py` | `src/project_name/` with `crews/` folder |
| **Main logic location** | `src/project_name/crew.py` | `src/project_name/main.py` (Flow class) |
| **Entry point function** | `run()` in `main.py` | `kickoff()` in `main.py` |
| **Project structure** | Project root with `crew.jsonc` and `agents/` | `src/project_name/` with `crews/` folder |
| **Main logic location** | `crew.jsonc` (classic: `src/project_name/crew.py`) | `src/project_name/main.py` (Flow class) |
| **Entry point function** | Loaded from `crew.jsonc` (classic: `run()` in `main.py`) | `kickoff()` in `main.py` |
| **pyproject.toml type** | `type = "crew"` | `type = "flow"` |
| **CLI create command** | `crewai create crew name` | `crewai create flow name` |
| **Config location** | `src/project_name/config/` | `src/project_name/crews/crew_name/config/` |
| **Config location** | `crew.jsonc`, `agents/`, optional `tools/` | `src/project_name/crews/crew_name/config/` or embedded JSON crew folders |
| **Can contain other crews** | No | Yes (in `crews/` folder) |
## Project Structure Reference
### Crew Project Structure
When you run `crewai create crew my_crew`, you get this structure:
When you run `crewai create crew my_crew`, you get the JSON-first structure:
```
my_crew/
@@ -54,24 +54,27 @@ my_crew/
├── README.md
├── .env
├── uv.lock # REQUIRED for deployment
── src/
└── my_crew/
├── __init__.py
├── main.py # Entry point with run() function
├── crew.py # Crew class with @CrewBase decorator
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml # Agent definitions
└── tasks.yaml # Task definitions
── crew.jsonc # Crew settings, tasks, process, inputs
├── agents/
└── researcher.jsonc # Agent definitions
├── tools/ # Optional custom:<name> tools
├── knowledge/
└── skills/
```
<Warning>
The nested `src/project_name/` structure is critical for Crews.
Placing files at the wrong level will cause deployment failures.
For JSON-first crews, keep `crew.jsonc`, `agents/`, `tools/`, `knowledge/`, and `skills/`
at the project root. Placing them under `src/` will prevent `crewai run` and deployment
validation from finding the crew definition.
</Warning>
<Info>
Classic projects created with `crewai create crew my_crew --classic` use the older
`src/project_name/crew.py`, `src/project_name/config/agents.yaml`, and
`src/project_name/config/tasks.yaml` layout. That layout remains supported for
decorator-based Python crews.
</Info>
### Flow Project Structure
When you run `crewai create flow my_flow`, you get this structure:
@@ -100,9 +103,9 @@ my_flow/
```
<Info>
Both Crews and Flows use the `src/project_name/` structure.
The key difference is that Flows have a `crews/` folder for embedded crews,
while Crews have `crew.py` directly in the project folder.
JSON-first standalone crews use project-root JSON files. Flows still use
`src/project_name/` and can contain either classic embedded crews or embedded
JSON crew folders loaded with `crewai.project.load_crew`.
</Info>
## Pre-Deployment Checklist
@@ -154,60 +157,91 @@ git commit -m "Add uv.lock for deployment"
git push
```
### 3. Validate CrewBase Decorator Usage
### 3. Validate the Crew Definition
**Every crew class must use the `@CrewBase` decorator.** This applies to:
<Tabs>
<Tab title="JSON-first Crews">
JSON-first crews must have a `crew.jsonc` or `crew.json` file at the project root.
The `agents` array must reference files in `agents/`, and each task should reference
a valid agent name.
- Standalone crew projects
- Crews embedded inside Flow projects
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Research {topic}.",
"expected_output": "A concise report.",
"agent": "researcher"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
```python
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
Custom tools are referenced as `"custom:<name>"` and must be implemented in
`tools/<name>.py` with a `BaseTool` subclass.
</Tab>
<Tab title="Classic Python/YAML Crews">
Classic crews and Python crews embedded in Flows must use the `@CrewBase` decorator.
@CrewBase # This decorator is REQUIRED
class MyCrew():
"""My crew description"""
```python
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
agents: List[BaseAgent]
tasks: List[Task]
@CrewBase
class MyCrew():
"""My crew description"""
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
agents: List[BaseAgent]
tasks: List[Task]
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
<Warning>
If you forget the `@CrewBase` decorator, your deployment will fail with
errors about missing agents or tasks configurations.
</Warning>
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
</Tab>
</Tabs>
### 4. Check Project Entry Points
Both Crews and Flows have their entry point in `src/project_name/main.py`:
JSON-first standalone crews do not need a hand-written `src/project_name/main.py`; `crewai run`
and deployment packaging load `crew.jsonc` directly. Classic crews and Flows use Python entry
points:
<Tabs>
<Tab title="For Crews">
<Tab title="JSON-first Crews">
Run locally from the project root:
```bash
crewai run
```
</Tab>
<Tab title="Classic Crews">
The entry point uses a `run()` function:
```python
@@ -278,16 +312,17 @@ grep -A2 "\[tool.crewai\]" pyproject.toml
# 2. Verify uv.lock exists
ls -la uv.lock || echo "ERROR: uv.lock missing! Run 'uv lock'"
# 3. Verify src/ structure exists
ls -la src/*/main.py 2>/dev/null || echo "No main.py found in src/"
# 3. For JSON-first crews, verify crew.jsonc and agents/ exist
([ -f crew.jsonc ] || [ -f crew.json ]) || echo "No crew.jsonc or crew.json found"
test -d agents || echo "No agents/ directory found"
# 4. For Crews - verify crew.py exists
# 4. For classic Crews - verify crew.py exists
ls -la src/*/crew.py 2>/dev/null || echo "No crew.py (expected for Crews)"
# 5. For Flows - verify crews/ folder exists
ls -la src/*/crews/ 2>/dev/null || echo "No crews/ folder (expected for Flows)"
# 6. Check for CrewBase usage
# 6. For classic Python crews - check for CrewBase usage
grep -r "@CrewBase" . --include="*.py"
```
@@ -297,8 +332,9 @@ grep -r "@CrewBase" . --include="*.py"
|---------|---------|-----|
| Missing `uv.lock` | Build fails during dependency resolution | Run `uv lock` and commit |
| Wrong `type` in pyproject.toml | Build succeeds but runtime fails | Change to correct type |
| Missing `@CrewBase` decorator | "Config not found" errors | Add decorator to all crew classes |
| Files at root instead of `src/` | Entry point not found | Move to `src/project_name/` |
| Missing `crew.jsonc` or `agents/` in a JSON-first crew | Crew definition not found | Keep `crew.jsonc` and `agents/` at the project root |
| Missing `@CrewBase` decorator in a classic crew | "Config not found" errors | Add decorator to all classic crew classes |
| Classic files at root instead of `src/` | Entry point not found | Move classic Python files to `src/project_name/` |
| Missing `run()` or `kickoff()` | Cannot start automation | Add correct entry function |
## Next Steps

View File

@@ -43,7 +43,7 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee
| Skill | When it runs |
|-------|--------------|
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.py` / `main.py` |
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.jsonc` / `main.py` |
| `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails |
| `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review |
| `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details |
@@ -64,7 +64,7 @@ CrewAI is AI-native. This page brings together everything an AI coding agent nee
<Step title="Your agent gets instant CrewAI expertise">
The skill pack teaches your agent:
- **Flows** — stateful apps, steps, and crew kickoffs
- **Crews & Agents** — YAML-first patterns, roles, tasks, delegation
- **Crews & Agents** — JSON-first patterns (`crew.jsonc`, `agents/*.jsonc`), roles, tasks, delegation
- **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools
- **Project layout** — CLI scaffolds and repo conventions
- **Up-to-date patterns** — tracks current CrewAI docs and best practices

View File

@@ -1,396 +1,162 @@
---
title: Build Your First Crew
description: Step-by-step tutorial to create a collaborative AI team that works together to solve complex problems.
description: Step-by-step tutorial to create a collaborative AI team with JSON-first crew configuration.
icon: users-gear
mode: "wide"
---
## Unleashing the Power of Collaborative AI
## Build a Research Crew
Imagine having a team of specialized AI agents working together seamlessly to solve complex problems, each contributing their unique skills to achieve a common goal. This is the power of CrewAI - a framework that enables you to create collaborative AI systems that can accomplish tasks far beyond what a single AI could achieve alone.
In this guide, we'll walk through creating a research crew that will help us research and analyze a topic, then create a comprehensive report. This practical example demonstrates how AI agents can collaborate to accomplish complex tasks, but it's just the beginning of what's possible with CrewAI.
### What You'll Build and Learn
By the end of this guide, you'll have:
1. **Created a specialized AI research team** with distinct roles and responsibilities
2. **Orchestrated collaboration** between multiple AI agents
3. **Automated a complex workflow** that involves gathering information, analysis, and report generation
4. **Built foundational skills** that you can apply to more ambitious projects
While we're building a simple research crew in this guide, the same patterns and techniques can be applied to create much more sophisticated teams for tasks like:
- Multi-stage content creation with specialized writers, editors, and fact-checkers
- Complex customer service systems with tiered support agents
- Autonomous business analysts that gather data, create visualizations, and generate insights
- Product development teams that ideate, design, and plan implementation
Let's get started building your first crew!
In this guide, you will create a two-agent research crew that gathers information about a topic and writes a markdown report. New crew projects are JSON-first: agents are defined in `agents/*.jsonc`, tasks and crew settings are defined in `crew.jsonc`, and `crewai run` loads the JSON definition directly.
### Prerequisites
Before starting, make sure you have:
1. Installed CrewAI following the [installation guide](/en/installation)
2. Set up your LLM API key in your environment, following the [LLM setup
guide](/en/concepts/llms#setting-up-your-llm)
3. Basic understanding of Python
2. Set up your LLM API key following the [LLM setup guide](/en/concepts/llms#setting-up-your-llm)
3. A [Serper.dev](https://serper.dev/) API key if you want the researcher to use web search
## Step 1: Create a New CrewAI Project
First, let's create a new CrewAI project using the CLI. This command will set up a complete project structure with all the necessary files, allowing you to focus on defining your agents and their tasks rather than setting up boilerplate code.
## Step 1: Create a New Crew
```bash
crewai create crew research_crew
cd research_crew
```
This will generate a project with the basic structure needed for your crew. The CLI automatically creates:
The CLI creates a JSON-first project:
- A project directory with the necessary files
- Configuration files for agents and tasks
- A basic crew implementation
- A main script to run the crew
<Frame caption="CrewAI Framework Overview">
<img src="/images/crews.png" alt="CrewAI Framework Overview" />
</Frame>
## Step 2: Explore the Project Structure
Let's take a moment to understand the project structure created by the CLI. CrewAI follows best practices for Python projects, making it easy to maintain and extend your code as your crews become more complex.
```
```text
research_crew/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── 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
├── skills/
└── tools/
```
This structure follows best practices for Python projects and makes it easy to organize your code. The separation of configuration files (in YAML) from implementation code (in Python) makes it easy to modify your crew's behavior without changing the underlying code.
<Tip>
Need the older `crew.py`, `config/agents.yaml`, and `config/tasks.yaml` layout? Create it with `crewai create crew research_crew --classic`.
</Tip>
## Step 3: Configure Your Agents
## Step 2: Define Your Agents
Now comes the fun part - defining your AI agents! In CrewAI, agents are specialized entities with specific roles, goals, and backstories that shape their behavior. Think of them as characters in a play, each with their own personality and purpose.
Replace the generated `agents/researcher.jsonc` file and add `agents/analyst.jsonc`. The file names are the names you reference from `crew.jsonc`.
For our research crew, we'll create two agents:
1. A **researcher** who excels at finding and organizing information
2. An **analyst** who can interpret research findings and create insightful reports
Let's modify the `agents.yaml` file to define these specialized agents. Be sure
to set `llm` to the provider you are using.
```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...
```jsonc agents/researcher.jsonc
{
"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 who organizes complex information into clear, useful notes.",
// Replace with your model, for example "openai/gpt-4o".
"llm": "provider/model-id",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
Notice how each agent has a distinct role, goal, and backstory. These elements aren't just descriptive - they actively shape how the agent approaches its tasks. By crafting these carefully, you can create agents with specialized skills and perspectives that complement each other.
## Step 4: Define Your Tasks
With our agents defined, we now need to give them specific tasks to perform. Tasks in CrewAI represent the concrete work that agents will perform, with detailed instructions and expected outputs.
For our research crew, we'll define two main tasks:
1. A **research task** for gathering comprehensive information
2. An **analysis task** for creating an insightful report
Let's modify the `tasks.yaml` file:
```yaml
# src/research_crew/config/tasks.yaml
research_task:
description: >
Conduct thorough research on {topic}. Focus on:
1. Key concepts and definitions
2. Historical development and recent trends
3. Major challenges and opportunities
4. Notable applications or case studies
5. Future outlook and potential developments
Make sure to organize your findings in a structured format with clear sections.
expected_output: >
A comprehensive research document with well-organized sections covering
all the requested aspects of {topic}. Include specific facts, figures,
and examples where relevant.
agent: researcher
analysis_task:
description: >
Analyze the research findings and create a comprehensive report on {topic}.
Your report should:
1. Begin with an executive summary
2. Include all key information from the research
3. Provide insightful analysis of trends and patterns
4. Offer recommendations or future considerations
5. Be formatted in a professional, easy-to-read style with clear headings
expected_output: >
A polished, professional report on {topic} that presents the research
findings with added analysis and insights. The report should be well-structured
with an executive summary, main sections, and conclusion.
agent: analyst
context:
- research_task
output_file: output/report.md
```jsonc agents/analyst.jsonc
{
"role": "Report Analyst for {topic}",
"goal": "Turn research findings into a clear, well-structured report.",
"backstory": "You are a careful analyst with strong technical writing skills and a talent for extracting useful insights.",
// Replace with your model, for example "openai/gpt-4o".
"llm": "provider/model-id",
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
Note the `context` field in the analysis task - this is a powerful feature that allows the analyst to access the output of the research task. This creates a workflow where information flows naturally between agents, just as it would in a human team.
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, or `gemini/gemini-2.0-flash-001`.
## Step 5: Configure Your Crew
## Step 3: Define Tasks and Crew Settings
Now it's time to bring everything together by configuring our crew. The crew is the container that orchestrates how agents work together to complete tasks.
Replace `crew.jsonc` with:
Let's modify the `crew.py` file:
```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,
)
```
In this code, we're:
1. Creating the researcher agent and equipping it with the SerperDevTool to search the web
2. Creating the analyst agent
3. Setting up the research and analysis tasks
4. Configuring the crew to run tasks sequentially (the analyst will wait for the researcher to finish)
This is where the magic happens - with just a few lines of code, we've defined a collaborative AI system where specialized agents work together in a coordinated process.
## Step 6: Set Up Your Main Script
Now, let's set up the main script that will run our crew. This is where we provide the specific topic we want our crew to research.
```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'
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research on {topic}. Focus on key concepts, recent developments, major challenges, notable applications, and future outlook.",
"expected_output": "A comprehensive research document with organized sections, specific facts, and useful examples about {topic}.",
"agent": "researcher"
},
{
"name": "analysis_task",
"description": "Analyze the research findings and create a polished report on {topic}. Include an executive summary, key insights, trend analysis, and recommendations.",
"expected_output": "A professional markdown report with clear headings, a concise summary, main findings, and recommendations.",
"agent": "analyst",
"context": ["research_task"],
"output_file": "output/report.md",
"markdown": true
}
# 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()
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "Artificial Intelligence in Healthcare"
}
}
```
This script prepares the environment, specifies our research topic, and kicks off the crew's work. The power of CrewAI is evident in how simple this code is - all the complexity of managing multiple AI agents is handled by the framework.
`context` points to prior task names, so the analyst receives the research task output. The `inputs` object provides default values for `{topic}`. If you remove a default, `crewai run` prompts for it.
## Step 7: Set Up Your Environment Variables
## Step 4: Set Environment Variables
Create a `.env` file in your project root with your API keys:
Open `.env` and add the keys your model and tools need:
```sh
SERPER_API_KEY=your_serper_api_key
# Add your provider's API key here too.
# Add your model provider API key here too.
```
See the [LLM Setup guide](/en/concepts/llms#setting-up-your-llm) for details on configuring your provider of choice. You can get a Serper API key from [Serper.dev](https://serper.dev/).
See the [LLM setup guide](/en/concepts/llms#setting-up-your-llm) for provider-specific keys.
## Step 8: Install Dependencies
Install the required dependencies using the CrewAI CLI:
## Step 5: Install and Run
```bash
crewai install
```
This command will:
1. Read the dependencies from your project configuration
2. Create a virtual environment if needed
3. Install all required packages
## Step 9: Run Your Crew
Now for the exciting moment - it's time to run your crew and see AI collaboration in action!
```bash
crewai run
```
When you run this command, you'll see your crew spring to life. The researcher will gather information about the specified topic, and the analyst will then create a comprehensive report based on that research. You'll see the agents' thought processes, actions, and outputs in real-time as they work together to complete their tasks.
`crewai run` detects `crew.jsonc`, loads the agents from `agents/`, prompts for missing placeholders, and runs the crew. When the run finishes, open `output/report.md`.
## Step 10: Review the Output
## How It Works
Once the crew completes its work, you'll find the final report in the `output/report.md` file. The report will include:
1. `crew.jsonc` defines the crew, task order, process, memory, and runtime inputs.
2. `agents/researcher.jsonc` and `agents/analyst.jsonc` define the agents.
3. The researcher runs first.
4. The analyst runs second with `context: ["research_task"]`.
5. The final task writes `output/report.md`.
1. An executive summary
2. Detailed information about the topic
3. Analysis and insights
4. Recommendations or future considerations
## Extending Your Crew
Take a moment to appreciate what you've accomplished - you've created a system where multiple AI agents collaborated on a complex task, each contributing their specialized skills to produce a result that's greater than what any single agent could achieve alone.
You can add:
## Exploring Other CLI Commands
- More agents by creating new `agents/<name>.jsonc` files and listing them in `crew.jsonc`
- More tasks by appending objects to the `tasks` array
- Built-in tools by adding tool class names such as `"FileReadTool"` or `"SerperDevTool"`
- Custom tools with `"custom:<name>"`, which loads `tools/<name>.py`
- Hierarchical execution with `"process": "hierarchical"` and a `manager_llm` or `manager_agent`
CrewAI offers several other useful CLI commands for working with crews:
```bash
# View all available commands
crewai --help
# Run the crew
crewai run
# Test the crew
crewai test
# Reset crew memories
crewai reset-memories
# Replay from a specific task
crewai replay -t <task_id>
```
## The Art of the Possible: Beyond Your First Crew
What you've built in this guide is just the beginning. The skills and patterns you've learned can be applied to create increasingly sophisticated AI systems. Here are some ways you could extend this basic research crew:
### Expanding Your Crew
You could add more specialized agents to your crew:
- A **fact-checker** to verify research findings
- A **data visualizer** to create charts and graphs
- A **domain expert** with specialized knowledge in a particular area
- A **critic** to identify weaknesses in the analysis
### Adding Tools and Capabilities
You could enhance your agents with additional tools:
- Web browsing tools for real-time research
- CSV/database tools for data analysis
- Code execution tools for data processing
- API connections to external services
### Creating More Complex Workflows
You could implement more sophisticated processes:
- Hierarchical processes where manager agents delegate to worker agents
- Iterative processes with feedback loops for refinement
- Parallel processes where multiple agents work simultaneously
- Dynamic processes that adapt based on intermediate results
### Applying to Different Domains
The same patterns can be applied to create crews for:
- **Content creation**: Writers, editors, fact-checkers, and designers working together
- **Customer service**: Triage agents, specialists, and quality control working together
- **Product development**: Researchers, designers, and planners collaborating
- **Data analysis**: Data collectors, analysts, and visualization specialists
## Next Steps
Now that you've built your first crew, you can:
1. Experiment with different agent configurations and personalities
2. Try more complex task structures and workflows
3. Implement custom tools to give your agents new capabilities
4. Apply your crew to different topics or problem domains
5. Explore [CrewAI Flows](/en/guides/flows/first-flow) for more advanced workflows with procedural programming
<Warning>
Only run JSON crew projects from sources you trust. `custom:<name>` tools and `{"python": "module.attribute"}` references execute local Python code when the crew loads.
</Warning>
<Check>
Congratulations! You've successfully built your first CrewAI crew that can research and analyze any topic you provide. This foundational experience has equipped you with the skills to create increasingly sophisticated AI systems that can tackle complex, multi-stage problems through collaborative intelligence.
You now have a working JSON-first crew that researches a topic and writes a report.
</Check>

View File

@@ -65,7 +65,7 @@ This will generate a project with the basic structure needed for your flow.
## Step 2: Understanding the Project Structure
The generated project has the following structure. Take a moment to familiarize yourself with it, as understanding this structure will help you create more complex flows in the future.
The generated project has the following structure. The starter embedded crew uses the classic Python/YAML layout, and in Step 4 we will replace the content crew with a JSONC crew.
```
guide_creator_flow/
@@ -73,21 +73,24 @@ guide_creator_flow/
├── pyproject.toml
├── README.md
├── .env
── main.py
├── crews/
── poem_crew/
├── config/
├── agents.yaml
│ └── tasks.yaml
── poem_crew.py
└── tools/
└── custom_tool.py
── src/
└── guide_creator_flow/
── __init__.py
├── main.py
├── crews/
│ └── poem_crew/
── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ └── poem_crew.py
└── tools/
└── custom_tool.py
```
This structure provides a clear separation between different components of your flow:
- The main flow logic in the `main.py` file
- Specialized crews in the `crews` directory
- Custom tools in the `tools` directory
- The main flow logic in the `src/guide_creator_flow/main.py` file
- Specialized crews in the `src/guide_creator_flow/crews` directory
- Custom tools in the `src/guide_creator_flow/tools` directory
We'll modify this structure to create our guide creator flow, which will orchestrate the process of generating comprehensive learning guides.
@@ -103,157 +106,82 @@ This command automatically creates the necessary directories and template files
## Step 4: Configure the Content Writer Crew
Now, let's modify the generated files for the content writer crew. We'll set up two specialized agents - a writer and a reviewer - that will collaborate to create high-quality content for our guide.
Now, let's configure the content writer crew with JSONC. We'll set up two specialized agents - a writer and a reviewer - that collaborate to create high-quality content for our guide.
1. First, update the agents configuration file to define our content creation team:
1. Create `src/guide_creator_flow/crews/content_crew/agents/content_writer.jsonc`:
Remember to set `llm` to the provider you are using.
```yaml
# src/guide_creator_flow/crews/content_crew/config/agents.yaml
content_writer:
role: >
Educational Content Writer
goal: >
Create engaging, informative content that thoroughly explains the assigned topic
and provides valuable insights to the reader
backstory: >
You are a talented educational writer with expertise in creating clear, engaging
content. You have a gift for explaining complex concepts in accessible language
and organizing information in a way that helps readers build their understanding.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
content_reviewer:
role: >
Educational Content Reviewer and Editor
goal: >
Ensure content is accurate, comprehensive, well-structured, and maintains
consistency with previously written sections
backstory: >
You are a meticulous editor with years of experience reviewing educational
content. You have an eye for detail, clarity, and coherence. You excel at
improving content while maintaining the original author's voice and ensuring
consistent quality across multiple sections.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
```jsonc
{
"role": "Educational Content Writer",
"goal": "Create engaging, informative content that thoroughly explains the assigned topic and provides valuable insights to the reader.",
"backstory": "You are a talented educational writer who explains complex concepts in accessible language and organizes information clearly.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
These agent definitions establish the specialized roles and perspectives that will shape how our AI agents approach content creation. Notice how each agent has a distinct purpose and expertise.
2. Create `src/guide_creator_flow/crews/content_crew/agents/content_reviewer.jsonc`:
2. Next, update the tasks configuration file to define the specific writing and reviewing tasks:
```yaml
# src/guide_creator_flow/crews/content_crew/config/tasks.yaml
write_section_task:
description: >
Write a comprehensive section on the topic: "{section_title}"
Section description: {section_description}
Target audience: {audience_level} level learners
Your content should:
1. Begin with a brief introduction to the section topic
2. Explain all key concepts clearly with examples
3. Include practical applications or exercises where appropriate
4. End with a summary of key points
5. Be approximately 500-800 words in length
Format your content in Markdown with appropriate headings, lists, and emphasis.
Previously written sections:
{previous_sections}
Make sure your content maintains consistency with previously written sections
and builds upon concepts that have already been explained.
expected_output: >
A well-structured, comprehensive section in Markdown format that thoroughly
explains the topic and is appropriate for the target audience.
agent: content_writer
review_section_task:
description: >
Review and improve the following section on "{section_title}":
{draft_content}
Target audience: {audience_level} level learners
Previously written sections:
{previous_sections}
Your review should:
1. Fix any grammatical or spelling errors
2. Improve clarity and readability
3. Ensure content is comprehensive and accurate
4. Verify consistency with previously written sections
5. Enhance the structure and flow
6. Add any missing key information
Provide the improved version of the section in Markdown format.
expected_output: >
An improved, polished version of the section that maintains the original
structure but enhances clarity, accuracy, and consistency.
agent: content_reviewer
context:
- write_section_task
```jsonc
{
"role": "Educational Content Reviewer and Editor",
"goal": "Ensure content is accurate, comprehensive, well-structured, and consistent with previously written sections.",
"backstory": "You are a meticulous editor with an eye for detail, clarity, and coherence.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
These task definitions provide detailed instructions to our agents, ensuring they produce content that meets our quality standards. Note how the `context` parameter in the review task creates a workflow where the reviewer has access to the writer's output.
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `gemini/gemini-2.0-flash-001`, or `anthropic/claude-sonnet-4-6`.
3. Now, update the crew implementation file to define how our agents and tasks work together:
3. Create `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
```jsonc
{
"name": "Content Crew",
"agents": ["content_writer", "content_reviewer"],
"tasks": [
{
"name": "write_section_task",
"description": "Write a comprehensive section on the topic: \"{section_title}\".\n\nSection description: {section_description}\nTarget audience: {audience_level} level learners\n\nYour content should begin with a brief introduction, explain key concepts clearly with examples, include practical applications where appropriate, end with a summary, and be approximately 500-800 words.\n\nPreviously written sections:\n{previous_sections}",
"expected_output": "A well-structured, comprehensive section in Markdown format that thoroughly explains the topic and is appropriate for the target audience.",
"agent": "content_writer",
"markdown": true
},
{
"name": "review_section_task",
"description": "Review and improve this section on \"{section_title}\":\n\n{draft_content}\n\nTarget audience: {audience_level} level learners\nPreviously written sections:\n{previous_sections}\n\nFix errors, improve clarity, verify consistency, enhance structure, and add missing key information.",
"expected_output": "An improved, polished version of the section that maintains the original structure but enhances clarity, accuracy, and consistency.",
"agent": "content_reviewer",
"context": ["write_section_task"],
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
The `context` field lets the reviewer use the writer's output.
4. Replace `src/guide_creator_flow/crews/content_crew/content_crew.py` with a small loader:
```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
from pathlib import Path
@CrewBase
class ContentCrew():
"""Content writing crew"""
from crewai.project import load_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,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
This crew definition establishes the relationship between our agents and tasks, setting up a sequential process where the content writer creates a draft and then the reviewer improves it. While this crew can function independently, in our flow it will be orchestrated as part of a larger system.
This loader turns `crew.jsonc` into a `Crew` at runtime. While this crew can function independently, in our flow it will be orchestrated as part of a larger system.
## Step 5: Create the Flow
@@ -275,7 +203,7 @@ 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
from guide_creator_flow.crews.content_crew.content_crew import kickoff_content_crew
# Define our models for structured data
class Section(BaseModel):
@@ -380,7 +308,7 @@ class GuideCreatorFlow(Flow[GuideCreatorState]):
previous_sections_text = "No previous sections written yet."
# Run the content crew for this section
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
"section_description": section.description,
"audience_level": self.state.audience_level,
@@ -600,7 +528,7 @@ This provides a type-safe way to track and transform data throughout your flow.
Flows can seamlessly integrate with crews for complex collaborative tasks:
```python
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
# ...
})

View File

@@ -116,7 +116,7 @@ If you haven't installed `uv` yet, follow **step 1** to quickly get it set up on
# Creating a CrewAI Project
We recommend using the `YAML` template scaffolding for a structured approach to defining agents and tasks. Here's how to get started:
`crewai create crew` now creates a JSON-first crew project. Agents live in `agents/*.jsonc`, tasks and crew-level settings live in `crew.jsonc`, and `crewai run` loads that JSON definition directly.
<Steps>
<Step title="Generate Project Scaffolding">
@@ -129,21 +129,20 @@ We recommend using the `YAML` template scaffolding for a structured approach to
```
my_project/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── pyproject.toml
├── README.md
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
- If you need the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`, run:
```shell
crewai create crew <your_project_name> --classic
```
</Step>
@@ -152,15 +151,15 @@ We recommend using the `YAML` template scaffolding for a structured approach to
- Your project will contain these essential files:
| File | Purpose |
| --- | --- |
| `agents.yaml` | Define your AI agents and their roles |
| `tasks.yaml` | Set up agent tasks and workflows |
| `crew.jsonc` | Configure the crew, task order, process, and input defaults |
| `agents/*.jsonc` | Define each agent's role, goal, backstory, LLM, tools, and behavior |
| `.env` | Store API keys and environment variables |
| `main.py` | Project entry point and execution flow |
| `crew.py` | Crew orchestration and coordination |
| `tools/` | Directory for custom agent tools |
| `knowledge/` | Directory for knowledge base |
| `tools/` | Optional Python files for `custom:<name>` tools |
| `knowledge/` | Optional knowledge files for agents |
| `skills/` | Optional skill files applied to the crew |
- Start by editing `agents.yaml` and `tasks.yaml` to define your crew's behavior.
- Start by editing `crew.jsonc` and the files in `agents/` to define your crew's behavior.
- Use `{placeholder}` values in agent and task text, then set defaults in `crew.jsonc` under `inputs`. When you run `crewai run`, the CLI prompts for any missing values.
- Keep sensitive information like API keys in `.env`.
</Step>

View File

@@ -1,15 +1,19 @@
---
title: "Using Annotations in crew.py"
description: "Learn how to use annotations to properly structure agents, tasks, and components in CrewAI"
description: "Learn how to use classic Python annotations to structure agents, tasks, and components in CrewAI"
icon: "at"
mode: "wide"
---
This guide explains how to use annotations to properly reference **agents**, **tasks**, and other components in the `crew.py` file.
This guide explains how to use annotations to properly reference **agents**, **tasks**, and other components in a classic `crew.py` file.
<Note>
New crew projects created with `crewai create crew <name>` are JSON-first and use `crew.jsonc` plus `agents/*.jsonc`. Use this annotations guide when you are working in a classic project created with `crewai create crew <name> --classic`, migrating an existing Python/YAML project, or need decorator-based Python control.
</Note>
## Introduction
Annotations in the CrewAI framework are used to decorate classes and methods, providing metadata and functionality to various components of your crew. These annotations help in organizing and structuring your code, making it more readable and maintainable.
Annotations in the CrewAI framework are used to decorate classes and methods, providing metadata and functionality to various components of your crew. In classic Python/YAML projects, these annotations help organize the code that loads `config/agents.yaml`, `config/tasks.yaml`, and returns the `Crew` object.
## Available Annotations
@@ -113,9 +117,9 @@ def crew(self) -> Crew:
The `@crew` annotation is used to decorate the method that creates and returns the `Crew` object. This method assembles all the components (agents and tasks) into a functional crew.
## YAML Configuration
## Classic YAML Configuration
The agent configurations are typically stored in a YAML file. Here's an example of how the `agents.yaml` file might look for the researcher agent:
In classic projects, agent configurations are typically stored in a YAML file. Here's an example of how the `agents.yaml` file might look for the researcher agent:
```yaml
researcher:
@@ -146,6 +150,6 @@ Note how the `llm` and `tools` in the YAML file correspond to the methods decora
- **Consistent Naming**: Use clear and consistent naming conventions for your methods. For example, agent methods could be named after their roles (e.g., researcher, reporting_analyst).
- **Environment Variables**: Use environment variables for sensitive information like API keys.
- **Flexibility**: Design your crew to be flexible by allowing easy addition or removal of agents and tasks.
- **YAML-Code Correspondence**: Ensure that the names and structures in your YAML files correspond correctly to the decorated methods in your Python code.
- **YAML-Code Correspondence**: In classic projects, ensure that the names and structures in your YAML files correspond correctly to the decorated methods in your Python code.
By following these guidelines and properly using annotations, you can create well-structured and maintainable crews using the CrewAI framework.
By following these guidelines and properly using annotations, you can maintain classic Python/YAML crews cleanly. For new crews, prefer the JSON-first structure covered in [Crews](/en/concepts/crews).

View File

@@ -39,85 +39,60 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
This creates a Flow app under `src/latest_ai_flow/`, including a starter crew under `crews/content_crew/` that you will replace with a minimal **single-agent** research crew in the next steps.
</Step>
<Step title="Configure one agent in `agents.yaml`">
Replace the contents of `src/latest_ai_flow/crews/content_crew/config/agents.yaml` with a single researcher. Variables like `{topic}` are filled from `crew.kickoff(inputs=...)`.
<Step title="Configure one agent in JSONC">
Create `src/latest_ai_flow/crews/content_crew/agents/researcher.jsonc` (create the `agents/` directory if needed). Variables like `{topic}` are filled from `crew.kickoff(inputs=...)`.
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
goal: >
Uncover cutting-edge developments in {topic}
backstory: >
You're a seasoned researcher with a knack for uncovering the latest
developments in {topic}. You find the most relevant information and
present it clearly.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You're a seasoned researcher who finds relevant information and presents it clearly.",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true
}
}
```
</Step>
<Step title="Configure one task in `tasks.yaml`">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
research_task:
description: >
Conduct thorough research about {topic}. Use web search to find current,
credible information. The current year is 2026.
expected_output: >
A markdown report with clear sections: key trends, notable tools or companies,
and implications. Aim for 8001200 words. No fenced code blocks around the whole document.
agent: researcher
output_file: output/report.md
<Step title="Configure the crew in `crew.jsonc`">
Create `src/latest_ai_flow/crews/content_crew/crew.jsonc`:
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research about {topic}. Use web search to find recent, credible information.",
"expected_output": "A markdown report with clear sections: key trends, notable tools or companies, and implications. Aim for 800-1200 words. No fenced code blocks around the whole document.",
"agent": "researcher",
"output_file": "output/report.md",
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
</Step>
<Step title="Wire the crew class (`content_crew.py`)">
Point the generated crew at your YAML and attach `SerperDevTool` to the researcher.
<Step title="Load the JSON crew (`content_crew.py`)">
Replace the generated `content_crew.py` with a small loader that turns `crew.jsonc` into a `Crew`.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
from pathlib import Path
from crewai import Agent, Crew, Process, Task
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.project import load_crew
@CrewBase
class ResearchCrew:
"""Single-agent research crew used inside the Flow."""
agents: List[BaseAgent]
tasks: List[Task]
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config["research_task"], # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
</Step>
@@ -131,7 +106,7 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
from crewai.flow import Flow, listen, start
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
from latest_ai_flow.crews.content_crew.content_crew import kickoff_content_crew
class ResearchFlowState(BaseModel):
@@ -150,7 +125,7 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
result = kickoff_content_crew(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("Research crew finished.")
@@ -172,7 +147,7 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
```
<Tip>
If your package name differs from `latest_ai_flow`, change the import of `ResearchCrew` to match your projects module path.
If your package name differs from `latest_ai_flow`, change the `kickoff_content_crew` import to match your projects module path.
</Tip>
</Step>
@@ -202,7 +177,7 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
<CodeGroup>
```markdown output/report.md
# AI Agents in 2026: Landscape and Trends
# AI Agents: Recent Landscape and Trends
## Executive summary
@@ -223,7 +198,7 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
## How this run fits together
1. **Flow** — `LatestAiFlow` runs `prepare_topic` first, then `run_research`, then `summarize`. State (`topic`, `report`) lives on the Flow.
2. **Crew** — `ResearchCrew` runs one task with one agent: the researcher uses **Serper** to search the web, then writes the structured report.
2. **Crew** — `kickoff_content_crew` loads `crew.jsonc` and runs one task with one agent: the researcher uses **Serper** to search the web, then writes the structured report.
3. **Artifact** — The tasks `output_file` writes the report under `output/report.md`.
To go deeper on Flow patterns (routing, persistence, human-in-the-loop), see [Build your first Flow](/en/guides/flows/first-flow) and [Flows](/en/concepts/flows). For crews without a Flow, see [Crews](/en/concepts/crews). For a single `Agent` and `kickoff()` without tasks, see [Agents](/en/concepts/agents#direct-agent-interaction-with-kickoff).
@@ -234,7 +209,10 @@ You now have an end-to-end Flow with an agent crew and a saved report — a soli
### Naming consistency
YAML keys (`researcher`, `research_task`) must match the method names on your `@CrewBase` class. See [Crews](/en/concepts/crews) for the full decorator pattern.
The names in `crew.jsonc` must match the files and task references you use:
- `agents: ["researcher"]` loads `agents/researcher.jsonc`
- `tasks[].agent: "researcher"` assigns the task to that agent
## Deploying

View File

@@ -20,7 +20,7 @@ That pulls the official skill pack into your agent workflow so it can apply Crew
## What your agent gets
- **Flows** — structure stateful apps, steps, and crew kickoffs the CrewAI way
- **Crews & agents** — YAML-first patterns, roles, tasks, and delegation
- **Crews & agents** — JSON-first patterns (`crew.jsonc`, `agents/*.jsonc`), roles, tasks, and delegation
- **Tools & integrations** — hook agents to search, APIs, and common CrewAI tools
- **Project layout** — align with CLI scaffolds and repo conventions
- **Up-to-date patterns** — skills track current CrewAI docs and recommended practices

View File

@@ -66,13 +66,39 @@ CrewAI AOP에는 코드를 작성하지 않고도 에이전트 생성 및 구성
## 에이전트 생성
CrewAI에서 에이전트를 생성하는 방법에는 **YAML 구성(권장)**을 사용하는 방법과 **코드에서 직접 정의**하는 두 가지가 있습니다.
CrewAI에서 에이전트를 생성하는 일반적인 방법은 **JSONC 프로젝트 구성(새 crew 권장)** 또는 **코드에서 직접 정의**니다.
### YAML 구성 (권장)
### JSONC 구성 (권장)
YAML 구성을 사용하면 에이전트를 보다 깔끔하고 유지 관리하기 쉽도록 정의할 수 있습니다. CrewAI 프로젝트에서 이 방식을 사용하는 것을 강력히 권장합니다.
`crewai create crew <name>`으로 만든 새 프로젝트는 JSON-first 구성을 사용합니다. 각 에이전트는 `agents/<agent_name>.jsonc`에 정의하고, `crew.jsonc`에서 crew에 포함할 에이전트를 나열합니다.
[설치](/ko/installation) 섹션에 설명된 대로 CrewAI 프로젝트를 생성한 후, `src/latest_ai_development/config/agents.yaml` 파일로 이동하여 템플릿을 여러분의 요구 사항에 맞게 수정하세요.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You find the most relevant information and present it clearly.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
`role`, `goal`, `backstory`에 `{placeholder}`를 사용할 수 있습니다. 기본값은 `crew.jsonc`의 `inputs`에 넣고, 빠진 값은 `crewai run`이 실행 시 물어봅니다. `verbose`, `allow_delegation`, `max_iter`, `memory`, `cache`, `planning_config` 같은 동작 필드는 최상위 또는 `settings` 안에 둘 수 있습니다.
<Note>
JSONC는 주석과 trailing comma를 지원합니다. `agents/<name>.jsonc`와 `agents/<name>.json`이 모두 있으면 CrewAI는 JSONC 파일을 사용합니다.
</Note>
### 클래식 YAML 구성
`crewai create crew <name> --classic`으로 만든 클래식 프로젝트는 `config/agents.yaml`과 `crew.py`의 `@CrewBase` 클래스를 사용합니다.
YAML 구성은 기존 Python/YAML 프로젝트와 `@CrewBase` 클래스에서 에이전트를 정의하려는 팀을 위해 계속 지원됩니다.
클래식 프로젝트를 만든 후, `src/<project_name>/config/agents.yaml` 파일로 이동하여 템플릿을 여러분의 요구 사항에 맞게 수정하세요.
<Note>
YAML 파일의 변수(예: `{topic}`)는 crew를 실행할 때 입력값에서 가져온 값으로 대체됩니다:
@@ -84,7 +110,7 @@ crew.kickoff(inputs={'topic': 'AI Agents'})
아래는 YAML을 사용하여 에이전트를 구성하는 예시입니다:
```yaml agents.yaml
# src/latest_ai_development/config/agents.yaml
# src/<project_name>/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
@@ -109,7 +135,7 @@ reporting_analyst:
이 YAML 구성을 코드에서 사용하려면, `CrewBase`를 상속하는 crew 클래스를 생성하세요:
```python Code
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process
from crewai.project import CrewBase, agent, crew
from crewai_tools import SerperDevTool

View File

@@ -52,6 +52,8 @@ crewai create crew my_new_crew
crewai create flow my_new_flow
```
기본적으로 `crewai create crew`는 `crew.jsonc`와 `agents/*.jsonc`가 있는 JSON-first 프로젝트를 만듭니다. `crew.py`, `config/agents.yaml`, `config/tasks.yaml`을 사용하는 기존 Python/YAML 스캐폴드가 필요할 때만 `crewai create crew my_new_crew --classic`을 사용하세요.
### 2. 버전
설치된 CrewAI의 버전을 표시합니다.
@@ -183,7 +185,20 @@ crewai chat
이 명령어들은 CrewAI 프로젝트의 루트 디렉터리에서 실행해야 합니다.
</Note>
<Note>
중요: 이 명령어를 사용하려면 `crew.py` 파일에서 `chat_llm` 속성을 설정해야 합니다.
중요: 이 명령어를 사용하려면 crew 정의에 `chat_llm` 속성을 설정해야 합니다.
JSON-first crew에서는 `crew.jsonc`에 추가합니다:
```jsonc
{
"name": "My Crew",
"agents": ["researcher"],
"tasks": [],
"chat_llm": "openai/gpt-4o"
}
```
클래식 Python/YAML crew에서는 `crew.py`에 설정합니다:
```python
@crew
@@ -313,7 +328,7 @@ CLI를 사용하여 [CrewAI AMP](http://app.crewai.com)에 crew를 배포하는
### 11. API 키
`crewai create crew` 명령어를 실행하면, CLI에서 선택할 수 있는 LLM 제공업체 목록이 표시되고, 그 다음으로 선택한 제공업체에 대한 모델 선택이 이어집니다.
`crewai create crew` 명령어를 실행하면, CLI에서 선택할 수 있는 LLM 제공업체 목록이 표시되고, 그 다음으로 선택한 제공업체에 대한 모델 선택이 이어집니다. 선택한 모델은 생성된 `.env`에 저장되며 각 에이전트 JSONC 파일은 자체 `llm`을 설정할 수 있습니다.
LLM 제공업체와 모델을 선택하면, API 키를 입력하라는 메시지가 표시됩니다.

View File

@@ -41,13 +41,54 @@ crewAI에서 crew는 일련의 작업을 달성하기 위해 함께 협력하는
## 크루 생성하기
CrewAI에서 크루를 생성하는 방법은 두 가지가 있습니다: **YAML 구성(권장)**을 사용하는 방법과 **코드에서 직접 정의**하는 방법입니다.
CrewAI에서 크루를 생성하는 주요 방법은 **JSONC 구성(새 crew 권장)**을 사용하는 방법과 클래식 프로젝트나 고급 사용 사례에서 **코드 직접 정의**하는 방법입니다.
### YAML 구성 (권장)
### JSONC 구성 (권장)
YAML 구성을 사용하면 crew를 정의할 때 더 깔끔하고 유지 관리하기 쉬운 방법을 제공하며, CrewAI 프로젝트에서 agent 및 task를 정의하는 방식과 일관성을 유지할 수 있습니다.
`crewai create crew <name>`으로 만든 새 프로젝트는 crew 수준 설정과 태스크를 `crew.jsonc`에 두고, 각 에이전트를 `agents/`의 별도 파일에 둡니다. `crewai run`은 `crew.jsonc` 또는 `crew.json`을 감지해 에이전트를 로드하고, 빠진 placeholder 값을 물은 뒤 crew를 시작합니다.
[설치](/ko/installation) 섹션에 설명된 대로 CrewAI 프로젝트를 생성한 후, `CrewBase`를 상속받는 클래스에서 데코레이터를 이용해 agent, task, 그리고 crew 자체를 정의할 수 있습니다.
```jsonc crew.jsonc
{
"name": "Market Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research",
"description": "Research {topic} and collect the most relevant facts.",
"expected_output": "Structured research notes about {topic}.",
"agent": "researcher"
},
{
"name": "analysis",
"description": "Analyze the research and write a concise report.",
"expected_output": "A markdown report with findings and recommendations.",
"agent": "analyst",
"context": ["research"],
"output_file": "output/report.md"
}
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "AI Agents"
}
}
```
`agents`의 각 문자열은 먼저 `agents/<name>.jsonc`, 그 다음 `agents/<name>.json`으로 해석됩니다. 계층형 crew는 `"process": "hierarchical"`와 `manager_llm` 또는 `manager_agent`를 사용하세요.
<Warning>
신뢰하는 출처의 JSON crew 프로젝트만 실행하세요. `custom:<name>` 도구와 `{"python": "module.attribute"}` 참조는 crew 로드 시 로컬 Python 코드를 실행합니다.
</Warning>
### 클래식 YAML 구성
`crewai create crew <name> --classic`으로 만든 클래식 프로젝트는 `crew.py`, `config/agents.yaml`, `config/tasks.yaml`, `@CrewBase`, `@agent`, `@task`, `@crew` 데코레이터를 사용합니다.
이 방식은 기존 Python/YAML 프로젝트와 Python 데코레이터 제어가 필요한 팀을 위해 계속 지원됩니다.
클래식 프로젝트를 만든 후, `CrewBase`를 상속받는 클래스에서 데코레이터를 이용해 agent, task, 그리고 crew 자체를 정의할 수 있습니다.
#### 데코레이터가 적용된 예시 Crew 클래스
@@ -416,4 +457,4 @@ crewai log-tasks-outputs
crewai replay -t <task_id>
```
이 명령어들을 사용하면 이전에 실행된 작업의 컨텍스트를 유지하면서 최신 kickoff 작업부터 다시 실행할 수 있습니다.
이 명령어들을 사용하면 이전에 실행된 작업의 컨텍스트를 유지하면서 최신 kickoff 작업부터 다시 실행할 수 있습니다.

View File

@@ -823,7 +823,7 @@ CrewAI에서 여러 crews로 flow를 생성하는 것은 간단합니다.
crewai create flow name_of_flow
```
이 명령어는 필요한 폴더 구조를 갖춘 새 CrewAI 프로젝트를 생성합니다. 생성된 프로젝트에는 이미 동작 중인 미리 구축된 crew인 `poem_crew`가 포함되어 있습니다. 이 crew를 템플릿으로 사용하여 복사, 붙여넣기, 수정함으로써 다른 crew를 만들 수 있습니다.
이 명령어는 필요한 폴더 구조를 갖춘 새 CrewAI 프로젝트를 생성합니다. 생성된 프로젝트에는 이미 동작 중인 미리 구축된 crew인 `poem_crew`가 포함되어 있습니다. 시작용 embedded crew는 클래식 Python/YAML 레이아웃을 사용하며, `crewai create crew`로 만든 새 독립 실행형 crew는 JSON-first 레이아웃을 사용합니다.
### 폴더 구조
@@ -853,7 +853,29 @@ crewai create flow name_of_flow
- `config/tasks.yaml`: 크루의 task를 정의합니다.
- `poem_crew.py`: agent, task, 그리고 크루 자체를 포함한 crew 정의가 들어 있습니다.
`poem_crew`를 복사, 붙여넣기, 그리고 편집하여 다른 크루를 생성할 수 있습니다.
`poem_crew`를 복사, 붙여넣기, 그리고 편집하여 다른 클래식 embedded crew를 생성할 수 있습니다.
JSON-first embedded crew는 `crew.jsonc`와 `agents/*.jsonc`가 있는 폴더를 사용하세요:
```text
crews/
└── research_crew/
├── agents/
│ └── researcher.jsonc
└── crew.jsonc
```
그런 다음 Flow 단계에서 로드합니다:
```python
from pathlib import Path
from crewai.project import load_crew
crew, default_inputs = load_crew(
Path(__file__).parent / "crews" / "research_crew" / "crew.jsonc"
)
result = crew.kickoff(inputs={**default_inputs, "topic": "AI Agents"})
```
### `main.py`에서 Crew 연결하기

View File

@@ -64,13 +64,48 @@ crew = Crew(
## 작업 생성하기
CrewAI에서 작업을 생성하는 방법에는 **YAML 구성(권장)** 을 사용하는 방법과 **코드에서 직접 정의하는 방법** 두 가지가 있습니다.
CrewAI에서 작업을 생성하는 일반적인 방법은 **JSONC 프로젝트 구성(새 crew 권장)** 또는 **코드에서 직접 정의**입니다.
### YAML 구성 (권장)
### JSONC 구성 (권장)
YAML 구성을 사용하면 작업을 정의할 때 더 깔끔하고 유지 관리가 용이한 방법을 제공합니다. CrewAI 프로젝트에서 작업을 정의할 때 이 방식을 사용하는 것을 강력히 권장합니다.
`crewai create crew <name>`으로 만든 새 프로젝트는 `crew.jsonc`에 태스크를 정의합니다.
[설치](/ko/installation) 섹션에 따라 CrewAI 프로젝트를 생성한 후, `src/latest_ai_development/config/tasks.yaml` 파일로 이동하여 템플릿을 귀하의 특정 작업 요구 사항에 맞게 수정하세요.
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "reporting_analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research about {topic}.",
"expected_output": "A list of the most relevant information about {topic}.",
"agent": "researcher"
},
{
"name": "reporting_task",
"description": "Review the research and expand it into a detailed report.",
"expected_output": "A polished markdown report.",
"agent": "reporting_analyst",
"context": ["research_task"],
"markdown": true,
"output_file": "report.md"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
각 태스크에는 `description`과 `expected_output`이 필요합니다. `agent` 값은 `agents`에 나열된 에이전트 이름과 일치해야 합니다. `context`는 이전 태스크 이름만 참조할 수 있으며, 이후 태스크 참조는 거부됩니다.
### 클래식 YAML 구성
`crewai create crew <name> --classic`으로 만든 클래식 프로젝트는 `config/tasks.yaml`과 `crew.py`의 `@CrewBase` 클래스를 사용합니다.
YAML 구성은 기존 Python/YAML 프로젝트와 `@CrewBase` 클래스에서 태스크를 정의하려는 팀을 위해 계속 지원됩니다.
클래식 프로젝트를 만든 후, `src/<project_name>/config/tasks.yaml` 파일로 이동하여 템플릿을 작업 요구 사항에 맞게 수정하세요.
<Note>
YAML 파일 내 변수(예: `{topic}`)는 크루를 실행할 때 입력값에서 가져온 값으로 대체됩니다:
@@ -106,7 +141,7 @@ reporting_task:
이 YAML 구성을 코드에서 사용하려면 `CrewBase`를 상속받는 크루 클래스를 생성하세요:
```python crew.py
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

View File

@@ -26,10 +26,10 @@ icon: "arrows-rotate"
## 1단계 — 검증 Crew 스캐폴딩
새 Crew 프로젝트를 만듭니다. CrewAI CLI가 구조를 스캐폴딩합니다:
이 예제는 `crew.py`를 통해 Python 도구를 연결하므로 클래식 crew 프로젝트를 만듭니다:
```bash
crewai create crew rotation_verifier --skip_provider
crewai create crew rotation_verifier --classic --skip_provider
cd rotation_verifier
```

View File

@@ -373,17 +373,17 @@ git push
**해결책**: 프로젝트가 예상 구조와 일치하는지 확인합니다:
- **Crews와 Flows 모두**: 진입점이 `src/project_name/main.py`에 있어야 합니다
- **Crews**: 진입점으로 `run()` 함수 사용
- **Flows**: 진입점으로 `kickoff()` 함수 사용
- **JSON-first Crews**: `crew.jsonc` 또는 `crew.json`과 `agents/`를 프로젝트 루트에 둡니다
- **클래식 Crews**: `src/project_name/main.py`에 `run()` 진입점을 둡니다
- **Flows**: `src/project_name/main.py`에 `kickoff()` 진입점을 둡니다
자세한 구조 다이어그램은 [배포 준비하기](/ko/enterprise/guides/prepare-for-deployment)를 참조하세요.
#### CrewBase 데코레이터 누락
#### 클래식 Crew의 CrewBase 데코레이터 누락
**증상**: "Crew not found", "Config not found" 또는 agent/task 구성 오류
**해결책**: **모든** crew 클래스가 `@CrewBase` 데코레이터를 사용하는지 확인합니다:
**해결책**: 클래식 Python/YAML crew에서는 모든 crew 클래스가 `@CrewBase` 데코레이터를 사용하는지 확인합니다. JSON-first crew에는 이 데코레이터가 필요하지 않습니다.
```python
from crewai.project import CrewBase, agent, crew, task
@@ -403,8 +403,8 @@ class YourCrew():
```
<Info>
이것은 독립 실행형 Crews와 Flow 프로젝트에 포함된 crews 모두에 적용됩니다.
모든 crew 클래스에 데코레이터가 필요합니다.
이것은 Flow 프로젝트에 포함된 클래식 crew를 포함하여 클래식 Python crew 클래스에 적용됩니다.
JSON-first crew는 `crew.jsonc`와 `agents/`를 기준으로 검증됩니다.
</Info>
#### 잘못된 pyproject.toml 타입
@@ -441,8 +441,8 @@ type = "flow"
**해결책**:
1. AMP 대시보드에서 실행 로그를 확인합니다 (Traces 탭)
2. 모든 도구에 필요한 API 키가 구성되어 있는지 확인합니다
3. `agents.yaml`의 agent 구성이 유효한지 확인합니다
4. `tasks.yaml`task 구성에 구문 오류가 없는지 확인합니다
3. JSON-first crew의 경우 `crew.jsonc`와 `agents/`에서 참조한 파일을 검증합니다
4. 클래식 crew의 경우 `agents.yaml`과 `tasks.yaml`이 유효한지 확인합니다
<Card title="도움이 필요하신가요?" icon="headset" href="mailto:support@crewai.com">
배포 문제 또는 AMP 플랫폼에 대한 문의 사항이 있으시면 지원팀에 연락해 주세요.

View File

@@ -24,10 +24,9 @@ company-ai/
`-- crews/
|-- support_agent/
| |-- pyproject.toml
| `-- src/
| `-- support_agent/
| |-- main.py
| `-- crew.py
| |-- crew.jsonc
| `-- agents/
| `-- support_agent.jsonc
`-- research_flow/
|-- pyproject.toml
`-- src/
@@ -48,7 +47,7 @@ AMP는 여전히 전체 저장소를 가져오거나 업로드하지만, 선택
작업 디렉터리가 설정되면 AMP는 해당 폴더를 다음 용도로 사용합니다:
- `pyproject.toml`, `src/`, Crew 또는 Flow 진입점을 포함한 프로젝트 검증
- `pyproject.toml`, JSON crew 파일, 클래식 Crew 또는 Flow 진입점을 포함한 프로젝트 검증
- `uv`를 사용한 종속성 설치
- 실행 중인 프로세스의 작업 디렉터리
- `CREW_ROOT_DIR` 환경 변수
@@ -152,11 +151,16 @@ AMP는 앞뒤 공백을 제거하고, 반복된 슬래시를 하나로 줄이며
## Lock 파일과 UV 워크스페이스
선택한 폴더에는 자동화의 `pyproject.toml`과 `src/` 디렉터리가 있어야
합니다. `uv.lock` 또는 `poetry.lock` 파일은 선택한 폴더나 저장소 루트에
둘 수 있습니다.
선택한 폴더에는 자동화의 `pyproject.toml`과 프로젝트 유형에 맞는 파일이
있어야 합니다:
이 방식은 일반적인 두 가지 모노레포 레이아웃을 모두 지원합니다:
- JSON-first crew: `crew.jsonc` 또는 `crew.json`과 `agents/`
- 클래식 Crew 또는 Flow: Python 진입점이 있는 `src/`
`uv.lock` 또는 `poetry.lock` 파일은 선택한 폴더나 저장소 루트에 둘 수
있습니다.
이 방식은 일반적인 두 가지 lock 파일 배치를 모두 지원합니다:
<Tabs>
<Tab title="프로젝트 lock 파일">
@@ -166,9 +170,9 @@ AMP는 앞뒤 공백을 제거하고, 반복된 슬래시를 하나로 줄이며
`-- support_agent/
|-- pyproject.toml
|-- uv.lock
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
@@ -181,9 +185,9 @@ AMP는 앞뒤 공백을 제거하고, 반복된 슬래시를 하나로 줄이며
`-- crews/
`-- support_agent/
|-- pyproject.toml
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
</Tabs>

View File

@@ -24,7 +24,7 @@ CrewAI AMP에서 **자동화(automations)**는 배포 가능한 Agentic AI 프
<CardGroup cols={2}>
<Card title="Crew 프로젝트" icon="users">
에이전트와 작업을 정의하는 `crew.py`가 있는 독립 실행형 AI 에이전트 팀. 집중적이고 협업적인 작업에 적합합니다.
독립 실행형 AI 에이전트 팀입니다. 새 crew는 `crew.jsonc`와 `agents/`를 사용하는 JSON-first 구조이며, 클래식 crew는 계속 `crew.py`를 사용할 수 있습니다.
</Card>
<Card title="Flow 프로젝트" icon="diagram-project">
`crews/` 폴더에 포함된 crew가 있는 오케스트레이션된 워크플로우. 복잡한 다단계 프로세스에 적합합니다.
@@ -33,19 +33,19 @@ CrewAI AMP에서 **자동화(automations)**는 배포 가능한 Agentic AI 프
| 측면 | Crew | Flow |
|------|------|------|
| **프로젝트 구조** | `crew.py`가 있는 `src/project_name/` | `crews/` 폴더가 있는 `src/project_name/` |
| **메인 로직 위치** | `src/project_name/crew.py` | `src/project_name/main.py` (Flow 클래스) |
| **진입점 함수** | `main.py`의 `run()` | `main.py`의 `kickoff()` |
| **프로젝트 구조** | 프로젝트 루트의 `crew.jsonc`와 `agents/` | `crews/` 폴더가 있는 `src/project_name/` |
| **메인 로직 위치** | `crew.jsonc` (클래식: `src/project_name/crew.py`) | `src/project_name/main.py` (Flow 클래스) |
| **진입점 함수** | `crew.jsonc`에서 로드됨 (클래식: `main.py`의 `run()`) | `main.py`의 `kickoff()` |
| **pyproject.toml 타입** | `type = "crew"` | `type = "flow"` |
| **CLI 생성 명령어** | `crewai create crew name` | `crewai create flow name` |
| **설정 위치** | `src/project_name/config/` | `src/project_name/crews/crew_name/config/` |
| **설정 위치** | `crew.jsonc`, `agents/`, 선택적 `tools/` | `src/project_name/crews/crew_name/config/` 또는 포함된 JSON crew 폴더 |
| **다른 crew 포함 가능** | 아니오 | 예 (`crews/` 폴더 내) |
## 프로젝트 구조 참조
### Crew 프로젝트 구조
`crewai create crew my_crew`를 실행하면 다음 구조를 얻습니다:
`crewai create crew my_crew`를 실행하면 JSON-first 구조를 얻습니다:
```
my_crew/
@@ -54,24 +54,25 @@ my_crew/
├── README.md
├── .env
├── uv.lock # 배포에 필수
── src/
└── my_crew/
├── __init__.py
├── main.py # run() 함수가 있는 진입점
├── crew.py # @CrewBase 데코레이터가 있는 Crew 클래스
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml # 에이전트 정의
└── tasks.yaml # 작업 정의
── crew.jsonc # Crew 설정, 태스크, 프로세스, 입력
├── agents/
└── researcher.jsonc # 에이전트 정의
├── tools/ # 선택적 custom:<name> 도구
├── knowledge/
└── skills/
```
<Warning>
중첩된 `src/project_name/` 구조는 Crews에 매우 중요합니다.
잘못된 레벨에 파일을 배치하면 배포 실패의 원인이 됩니다.
JSON-first crew에서는 `crew.jsonc`, `agents/`, `tools/`, `knowledge/`, `skills/`를
프로젝트 루트에 두세요. 이를 `src/` 아래에 두면 `crewai run`과 배포 검증이 crew 정의를 찾지 못합니다.
</Warning>
<Info>
`crewai create crew my_crew --classic`으로 만든 클래식 프로젝트는 기존
`src/project_name/crew.py`, `src/project_name/config/agents.yaml`,
`src/project_name/config/tasks.yaml` 구조를 사용합니다. 이 구조는 decorator 기반 Python crew를 위해 계속 지원됩니다.
</Info>
### Flow 프로젝트 구조
`crewai create flow my_flow`를 실행하면 다음 구조를 얻습니다:
@@ -100,9 +101,9 @@ my_flow/
```
<Info>
Crews와 Flows 모두 `src/project_name/` 구조를 사용합니다.
핵심 차이점은 Flows포함된 crews를 위한 `crews/` 폴더가 있고,
Crews는 프로젝트 폴더에 직접 `crew.py`가 있다는 것입니다.
JSON-first 독립 실행형 crew는 프로젝트 루트의 JSON 파일을 사용합니다.
Flow는 여전히 `src/project_name/`을 사용하며, 클래식 포함 crew나
`crewai.project.load_crew`로 로드하는 포함 JSON crew 폴더를 둘 수 있습니다.
</Info>
## 배포 전 체크리스트
@@ -154,60 +155,88 @@ git commit -m "Add uv.lock for deployment"
git push
```
### 3. CrewBase 데코레이터 사용 확인
### 3. Crew 정의 검증
**모든 crew 클래스는 `@CrewBase` 데코레이터를 사용해야 합니다.** 이것은 다음에 적용됩니다:
<Tabs>
<Tab title="JSON-first Crews">
JSON-first crew는 프로젝트 루트에 `crew.jsonc` 또는 `crew.json` 파일이 있어야 합니다.
`agents` 배열은 `agents/` 안의 파일을 참조해야 하며, 각 task는 유효한 agent 이름을 참조해야 합니다.
- 독립 실행형 crew 프로젝트
- Flow 프로젝트 내에 포함된 crews
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Research {topic}.",
"expected_output": "A concise report.",
"agent": "researcher"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
```python
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
커스텀 도구는 `"custom:<name>"`으로 참조하며, `tools/<name>.py`에 `BaseTool` 서브클래스로 구현해야 합니다.
</Tab>
<Tab title="클래식 Python/YAML Crews">
클래식 crew와 Flow 안에 포함된 Python crew는 `@CrewBase` 데코레이터를 사용해야 합니다.
@CrewBase # 이 데코레이터는 필수입니다
class MyCrew():
"""내 crew 설명"""
```python
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
agents: List[BaseAgent]
tasks: List[Task]
@CrewBase
class MyCrew():
"""내 crew 설명"""
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
agents: List[BaseAgent]
tasks: List[Task]
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
<Warning>
`@CrewBase` 데코레이터를 잊으면 에이전트나 작업 구성이 누락되었다는
오류와 함께 배포가 실패합니다.
</Warning>
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
</Tab>
</Tabs>
### 4. 프로젝트 진입점 확인
Crews와 Flows 모두 `src/project_name/main.py`에 진입점이 있습니다:
JSON-first 독립 실행형 crew는 직접 작성한 `src/project_name/main.py`가 필요하지 않습니다.
`crewai run`과 배포 패키징이 `crew.jsonc`를 직접 로드합니다. 클래식 crew와 Flow는 Python 진입점을 사용합니다:
<Tabs>
<Tab title="Crews의 경우">
<Tab title="JSON-first Crews">
프로젝트 루트에서 로컬 실행합니다:
```bash
crewai run
```
</Tab>
<Tab title="클래식 Crews">
진입점은 `run()` 함수를 사용합니다:
```python
@@ -278,16 +307,17 @@ grep -A2 "\[tool.crewai\]" pyproject.toml
# 2. uv.lock 존재 확인
ls -la uv.lock || echo "오류: uv.lock이 없습니다! 'uv lock'을 실행하세요"
# 3. src/ 구조 존재 확인
ls -la src/*/main.py 2>/dev/null || echo "src/에서 main.py를 찾을 수 없습니다"
# 3. JSON-first crew의 경우 crew.jsonc와 agents/ 확인
([ -f crew.jsonc ] || [ -f crew.json ]) || echo "crew.jsonc 또는 crew.json을 찾을 수 없습니다"
test -d agents || echo "agents/ 디렉터리를 찾을 수 없습니다"
# 4. Crews의 경우 - crew.py 존재 확인
# 4. 클래식 Crews의 경우 - crew.py 존재 확인
ls -la src/*/crew.py 2>/dev/null || echo "crew.py가 없습니다 (Crews에서 예상됨)"
# 5. Flows의 경우 - crews/ 폴더 존재 확인
ls -la src/*/crews/ 2>/dev/null || echo "crews/ 폴더가 없습니다 (Flows에서 예상됨)"
# 6. CrewBase 사용 확인
# 6. 클래식 Python crews의 경우 - CrewBase 사용 확인
grep -r "@CrewBase" . --include="*.py"
```
@@ -297,8 +327,9 @@ grep -r "@CrewBase" . --include="*.py"
|------|------|----------|
| `uv.lock` 누락 | 의존성 해결 중 빌드 실패 | `uv lock` 실행 후 커밋 |
| pyproject.toml의 잘못된 `type` | 빌드 성공하지만 런타임 실패 | 올바른 타입으로 변경 |
| `@CrewBase` 데코레이터 누락 | "Config not found" 오류 | 모든 crew 클래스에 데코레이터 추가 |
| `src/` 대신 루트에 파일 배치 | 진입점을 찾을 수 없음 | `src/project_name/`으로 이동 |
| JSON-first crew에서 `crew.jsonc` 또는 `agents/` 누락 | Crew 정의를 찾을 수 없음 | `crew.jsonc`와 `agents/`를 프로젝트 루트에 둠 |
| 클래식 crew에서 `@CrewBase` 데코레이터 누락 | "Config not found" 오류 | 모든 클래식 crew 클래스에 데코레이터 추가 |
| 클래식 파일을 `src/` 대신 루트에 배치 | 진입점을 찾을 수 없음 | 클래식 Python 파일을 `src/project_name/`으로 이동 |
| `run()` 또는 `kickoff()` 누락 | 자동화를 시작할 수 없음 | 올바른 진입 함수 추가 |
## 다음 단계

View File

@@ -43,7 +43,7 @@ CrewAI는 AI 네이티브입니다. 이 페이지는 Claude Code, Codex, Cursor,
| 스킬 | 실행 시점 |
|------|-------------|
| `getting-started` | 새 프로젝트 스캐폴딩, `LLM.call()` / `Agent` / `Crew` / `Flow` 선택, `crew.py` / `main.py` 연결 |
| `getting-started` | 새 프로젝트 스캐폴딩, `LLM.call()` / `Agent` / `Crew` / `Flow` 선택, `crew.jsonc` / `main.py` 연결 |
| `design-agent` | 에이전트 구성 — 역할, 목표, 배경 이야기, 도구, LLM, 메모리, 가드레일 |
| `design-task` | 태스크 설명, 의존성, 구조화된 출력(`output_pydantic`, `output_json`), 사람 검토 |
| `ask-docs` | 최신 API 정보를 위해 [CrewAI 문서 MCP 서버](https://docs.crewai.com/mcp) 조회 |
@@ -64,7 +64,7 @@ CrewAI는 AI 네이티브입니다. 이 페이지는 Claude Code, Codex, Cursor,
<Step title="에이전트가 즉시 CrewAI 전문성을 갖춤">
스킬 팩이 에이전트에게 알려 주는 내용:
- **Flow** — 상태ful 앱, 단계, crew 킥오프
- **Crew 및 에이전트** — YAML 우선 패턴, 역할, 태스크, 위임
- **Crew 및 에이전트** — JSON-first 패턴(`crew.jsonc`, `agents/*.jsonc`), 역할, 태스크, 위임
- **도구 및 통합** — 검색, API, MCP 서버, 일반적인 CrewAI 도구
- **프로젝트 레이아웃** — CLI 스캐폴드와 저장소 관례
- **최신 패턴** — 현재 CrewAI 문서와 모범 사례 반영

View File

@@ -1,392 +1,140 @@
---
title: 첫 번째 크루 만들기
description: 복잡한 문제를 함께 해결할 수 있는 협업 AI 팀을 만드는 단계별 튜토리얼입니다.
title: 첫 번째 Crew 만들기
description: JSON-first crew 설정으로 협업 AI 팀을 만드는 단계별 튜토리얼입니다.
icon: users-gear
mode: "wide"
---
## 협업 AI의 힘을 발휘하
## 리서치 Crew 만들
여러 AI 에이전트가 각자의 전문성을 바탕으로 원활하게 협력하며 복잡한 문제를 해결한다고 상상해 보세요. 각자 고유한 기술을 발휘해 공동의 목표를 달성합니다. 이것이 바로 CrewAI의 힘입니다. CrewAI 프레임워크를 통해 단일 AI로는 달성할 수 없는 과업을 협업 AI 시스템으로 실현할 수 있습니다.
이 가이드에서는 두 에이전트가 주제를 조사하고 markdown 보고서를 작성하는 crew를 만듭니다. 새 crew 프로젝트는 JSON-first입니다. 에이전트는 `agents/*.jsonc`, 태스크와 crew 설정은 `crew.jsonc`에 두며, `crewai run`이 이 정의를 직접 로드합니다.
이 가이드에서는 연구 크루를 만들어 주제를 조사 및 분석하고, 종합적인 보고서를 작성하는 과정을 안내합니다. 이 실용적인 예시는 AI 에이전트들이 어떻게 협력하여 복잡한 작업을 수행할 수 있는지 보여 주지만, CrewAI로 실현할 수 있는 가능성의 시작에 불과합니다.
### 준비 사항
### 무엇을 만들고 배우게 될까요
1. [설치 가이드](/ko/installation)에 따라 CrewAI 설치
2. [LLM 설정](/ko/concepts/llms#setting-up-your-llm)에 따라 모델 API 키 설정
3. 웹 검색을 사용할 경우 [Serper.dev](https://serper.dev/) API 키 준비
이 가이드를 마치면 다음을 할 수 있게 됩니다:
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 프로젝트를 생성해봅시다. 이 명령어는 필요한 모든 파일을 포함한 전체 프로젝트 구조를 설정해 주어, 보일러플레이트 코드를 설정하는 대신 에이전트와 그들의 작업 정의에 집중할 수 있습니다.
## 1단계: 새 Crew 만들기
```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가 더 복잡해질수록 코드를 쉽게 유지 관리하고 확장할 수 있습니다.
```
```text
research_crew/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── 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
├── skills/
└── tools/
```
이 구조는 Python 프로젝트의 모범 사례를 따르며, 코드를 체계적으로 구성할 수 있도록 해줍니다. 설정 파일(YAML)과 구현 코드(Python)의 분리로 인해, 기본 코드를 변경하지 않고도 crew의 동작을 쉽게 수정할 수 있습니다.
<Tip>
`crew.py`, `config/agents.yaml`, `config/tasks.yaml`을 쓰는 기존 레이아웃이 필요하면 `crewai create crew research_crew --classic`을 사용하세요.
</Tip>
## 3단계: 에이전트 구성하기
## 2단계: 에이전트 정의
이제 재미있는 단계가 시작됩니다 - 여러분의 AI 에이전트를 정의하는 것입니다! CrewAI에서 에이전트는 특정 역할, 목표 및 배경을 가진 전문화된 엔터티로, 이들이 어떻게 행동할지를 결정합니다. 각각 고유한 성격과 목적을 지닌 연극의 등장인물로 생각하면 됩니다.
생성된 `agents/researcher.jsonc` 파일을 교체하고 `agents/analyst.jsonc`를 추가합니다. 파일 이름이 `crew.jsonc`에서 참조하는 에이전트 이름입니다.
우리의 리서치 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...
```jsonc agents/researcher.jsonc
{
"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 who organizes complex information into clear, useful notes.",
// 사용하는 모델로 바꾸세요. 예: "openai/gpt-4o".
"llm": "provider/model-id",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
각 에이전트가 고유한 역할, 목표, 그리고 배경을 가지고 있다는 점에 주목하세요. 이 요소들은 단순한 설명 그 이상으로, 실제로 에이전트가 자신의 과업을 어떻게 접근하는지에 적극적으로 영향을 미칩니다. 이러한 부분을 신중하게 설계함으로써 서로 보완하는 전문적인 역량과 관점을 가진 에이전트를 만들 수 있습니다.
## 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
```jsonc agents/analyst.jsonc
{
"role": "Report Analyst for {topic}",
"goal": "Turn research findings into a clear, well-structured report.",
"backstory": "You are a careful analyst with strong technical writing skills and a talent for extracting useful insights.",
// 사용하는 모델로 바꾸세요. 예: "openai/gpt-4o".
"llm": "provider/model-id",
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
분석 작업 내의 `context` 필드에 주목하세요. 이 강력한 기능을 통해 analyst가 연구 작업의 결과물을 참조할 수 있습니다. 이를 통해 정보가 human team에서처럼 agent 간에 자연스럽게 흐르는 워크플로우가 만들어집니다.
`provider/model-id`를 `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, `gemini/gemini-2.0-flash-001` 같은 모델로 바꾸세요.
## 5단계: 크루 구성 설정하기
## 3단계: 태스크와 Crew 설정
이제 크루를 구성하여 모든 것을 하나로 모을 시간입니다. 크루는 에이전트들이 함께 작업을 완료하는 방식을 조율하는 컨테이너 역할을 합니다.
`crew.jsonc`를 다음으로 교체합니다:
`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'
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research on {topic}. Focus on key concepts, recent developments, major challenges, notable applications, and future outlook.",
"expected_output": "A comprehensive research document with organized sections, specific facts, and useful examples about {topic}.",
"agent": "researcher"
},
{
"name": "analysis_task",
"description": "Analyze the research findings and create a polished report on {topic}. Include an executive summary, key insights, trend analysis, and recommendations.",
"expected_output": "A professional markdown report with clear headings, a concise summary, main findings, and recommendations.",
"agent": "analyst",
"context": ["research_task"],
"output_file": "output/report.md",
"markdown": true
}
# 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()
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "Artificial Intelligence in Healthcare"
}
}
```
이 스크립트는 환경을 준비하고, 리서치 주제를 지정하며, crew의 작업을 시작합니다. CrewAI의 강력함은 이 코드가 얼마나 간단한지에서 드러납니다. 여러 AI 에이전트를 관리하는 모든 복잡함이 프레임워크에 의해 처리됩니다.
`context`는 이전 태스크 이름을 가리키므로 analyst가 research 태스크 출력을 받습니다. `inputs`는 `{topic}`의 기본값을 제공합니다. 기본값이 없으면 `crewai run`이 실행 중에 물어봅니다.
## 7단계: 환경 변수 설정하기
## 4단계: 환경 변수 설정
프로젝트 루트에 `.env` 파일을 생성하고 API 키를 입력하세요:
`.env`를 편집합니다:
```sh
SERPER_API_KEY=your_serper_api_key
# Add your provider's API key here too.
# 모델 제공자 API 키도 추가하세요.
```
선택한 provider를 구성하는 방법에 대한 자세한 내용은 [LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)를 참고하세요. Serper API 키는 [Serper.dev](https://serper.dev/)에서 받을 수 있습니다.
## 8단계: 필수 종속성 설치
CrewAI CLI를 사용하여 필요한 종속성을 설치하세요:
## 5단계: 설치 및 실행
```bash
crewai install
```
이 명령어는 다음을 수행합니다:
1. 프로젝트 구성에서 종속성을 읽어옵니다
2. 필요하다면 가상 환경을 생성합니다
3. 모든 필수 패키지를 설치합니다
## 9단계: Crew 실행하기
이제 흥미로운 순간입니다 - crew를 실행하여 AI 협업이 어떻게 이루어지는지 직접 확인해보세요!
```bash
crewai run
```
이 명령어를 실행하면 crew가 즉시 작동하는 모습을 볼 수 있습니다. researcher는 지정된 주제에 대한 정보를 수집하고, analyst가 그 연구를 바탕으로 종합 보고서를 작성합니다. 에이전트들의 사고 과정, 행동, 결과물이 실시간으로 표시되며 서로 협력하여 작업을 완수하는 모습을 확인할 수 있습니다.
실행이 끝나면 `output/report.md`를 확인하세요.
## 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)를 탐색하여 절차적 프로그래밍을 활용한 더 고급 워크플로우를 경험해 보세요
<Warning>
신뢰하는 출처의 JSON crew 프로젝트만 실행하세요. `custom:<name>` 도구와 `{"python": "module.attribute"}` 참조는 crew 로드 시 로컬 Python 코드를 실행합니다.
</Warning>
<Check>
축하합니다! 이제 주어진 모든 주제를 조사하고 분석할 수 있는 첫 번째 CrewAI crew를 성공적으로 구축하셨습니다. 이 기본적인 경험은 협업 인텔리전스를 통해 복잡하고 다단계의 문제를 해결할 수 있는 점점 더 정교한 AI 시스템을 제작하는 데 필요한 역량을 갖추는 데 도움이 됩니다.
</Check>
주제를 조사하고 보고서를 작성하는 JSON-first crew를 만들었습니다.
</Check>

View File

@@ -64,7 +64,7 @@ cd guide_creator_flow
## 2단계: 프로젝트 구조 이해하기
생성된 프로젝트는 다음과 같은 구조를 가지고 있습니다. 잠시 시간을 내어 이 구조에 익숙해지세요. 구조를 이해하면 앞으로 더 복잡한 flow를 만드는 데 도움이 됩니다.
생성된 프로젝트는 다음과 같은 구조를 가지고 있습니다. 시작용 embedded crew는 클래식 Python/YAML 레이아웃을 사용합니다. Flow 안에서 JSON-first crew를 사용하려면 crew 폴더에 `crew.jsonc`와 `agents/*.jsonc`를 만들고 `crewai.project.load_crew`로 로드하세요. 예시는 [Flows](/ko/concepts/flows#building-your-crews)를 참고하세요.
```
guide_creator_flow/
@@ -72,21 +72,24 @@ guide_creator_flow/
├── pyproject.toml
├── README.md
├── .env
── main.py
├── crews/
── poem_crew/
├── config/
├── agents.yaml
│ └── tasks.yaml
── poem_crew.py
└── tools/
└── custom_tool.py
── src/
└── guide_creator_flow/
── __init__.py
├── 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들
- `src/guide_creator_flow/main.py` 파일의 main flow 로직
- `src/guide_creator_flow/crews` 디렉터리의 특화된 crew들
- `src/guide_creator_flow/tools` 디렉터리의 custom tool들
이제 이 구조를 수정하여 guide creator flow를 만들 것입니다. 이 flow는 포괄적인 학습 가이드 생성을 조직하는 역할을 합니다.
@@ -102,149 +105,82 @@ crewai flow add-crew content-crew
## 4단계: 콘텐츠 작가 Crew 구성
이제 콘텐츠 작가 crew를 위해 생성된 파일을 수정해보겠습니다. 우리는 가이드의 고품질 콘텐츠를 만들기 위해 협업하는 두 명의 전문 에이전트 - 작가와 리뷰어 - 를 설정할 것입니다.
이제 콘텐츠 작가 crew를 JSONC로 구성합니다. 가이드의 고품질 콘텐츠를 만들기 위해 협업하는 두 명의 전문 에이전트 - 작가와 리뷰어 - 를 설정니다.
1. 먼저, 에이전트 구성 파일을 업데이트하여 콘텐츠 제작 팀을 정의합니다:
1. `src/guide_creator_flow/crews/content_crew/agents/content_writer.jsonc`를 만듭니다:
`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...
```jsonc
{
"role": "Educational Content Writer",
"goal": "Create engaging, informative content that thoroughly explains the assigned topic and provides valuable insights to the reader.",
"backstory": "You are a talented educational writer who explains complex concepts in accessible language and organizes information clearly.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
이 에이전트 정의는 AI 에이전트가 콘텐츠 제작을 접근하는 전문화된 역할과 관점을 구성합니다. 각 에이전트가 뚜렷한 목적과 전문성을 지니고 있음을 확인하세요.
2. `src/guide_creator_flow/crews/content_crew/agents/content_reviewer.jsonc`를 만듭니다:
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
```jsonc
{
"role": "Educational Content Reviewer and Editor",
"goal": "Ensure content is accurate, comprehensive, well-structured, and consistent with previously written sections.",
"backstory": "You are a meticulous editor with an eye for detail, clarity, and coherence.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
이 작업 정의는 에이전트에게 세부적인 지침을 제공하여 우리의 품질 기준에 부합하는 콘텐츠를 생산하게 합니다. review 작업의 `context` 파라미터를 통해 리뷰어가 작가의 결과물에 접근할 수 있는 워크플로우가 생성됨에 주의하세요.
`provider/model-id`를 사용하는 모델로 바꾸세요. 예: `openai/gpt-4o`, `gemini/gemini-2.0-flash-001`, `anthropic/claude-sonnet-4-6`.
3. 이제 crew 구현 파일을 업데이트하여 에이전트와 작업이 어떻게 연동되는지 정의합니다:
3. `src/guide_creator_flow/crews/content_crew/crew.jsonc`를 만듭니다:
```jsonc
{
"name": "Content Crew",
"agents": ["content_writer", "content_reviewer"],
"tasks": [
{
"name": "write_section_task",
"description": "Write a comprehensive section on the topic: \"{section_title}\".\n\nSection description: {section_description}\nTarget audience: {audience_level} level learners\n\nYour content should begin with a brief introduction, explain key concepts clearly with examples, include practical applications where appropriate, end with a summary, and be approximately 500-800 words.\n\nPreviously written sections:\n{previous_sections}",
"expected_output": "A well-structured, comprehensive section in Markdown format that thoroughly explains the topic and is appropriate for the target audience.",
"agent": "content_writer",
"markdown": true
},
{
"name": "review_section_task",
"description": "Review and improve this section on \"{section_title}\":\n\n{draft_content}\n\nTarget audience: {audience_level} level learners\nPreviously written sections:\n{previous_sections}\n\nFix errors, improve clarity, verify consistency, enhance structure, and add missing key information.",
"expected_output": "An improved, polished version of the section that maintains the original structure but enhances clarity, accuracy, and consistency.",
"agent": "content_reviewer",
"context": ["write_section_task"],
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
`context` 필드를 통해 리뷰어가 작가의 출력을 사용할 수 있습니다.
4. `src/guide_creator_flow/crews/content_crew/content_crew.py`를 작은 loader로 교체합니다:
```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
from pathlib import Path
@CrewBase
class ContentCrew():
"""Content writing crew"""
from crewai.project import load_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,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
crew 정의는 에이전트와 작업 간의 관계를 설정하여, 콘텐츠 작가가 초안을 작성하고 리뷰어가 이를 개선하는 순차적 과정을 만듭니다. 이 crew는 독립적으로도 작동할 수 있지만, 우리의 플로우에서 더 큰 시스템의 일부로 오케스트레이션될 예정입니다.
loader는 런타임에 `crew.jsonc`를 `Crew`로 바꿉니다. 이 crew는 독립적으로도 작동할 수 있지만, 우리의 플로우에서 더 큰 시스템의 일부로 오케스트레이션니다.
## 5단계: 플로우(Flow) 생성
@@ -266,7 +202,7 @@ 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
from guide_creator_flow.crews.content_crew.content_crew import kickoff_content_crew
# Define our models for structured data
class Section(BaseModel):
@@ -371,7 +307,7 @@ class GuideCreatorFlow(Flow[GuideCreatorState]):
previous_sections_text = "No previous sections written yet."
# Run the content crew for this section
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
"section_description": section.description,
"audience_level": self.state.audience_level,
@@ -590,7 +526,7 @@ class GuideCreatorState(BaseModel):
Flow는 복잡한 협업 작업을 위해 crew와 원활하게 통합될 수 있습니다:
```python
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
# ...
})
@@ -611,4 +547,4 @@ result = ContentCrew().crew().kickoff(inputs={
<Check>
축하합니다! 정규 코드, 직접적인 LLM 호출, crew 기반 처리를 결합하여 포괄적인 가이드를 생성하는 첫 번째 CrewAI Flow를 성공적으로 구축하셨습니다. 이러한 기초적인 역량을 바탕으로 절차적 제어와 협업적 인텔리전스를 결합하여 복잡하고 다단계의 문제를 해결할 수 있는 점점 더 정교한 AI 애플리케이션을 만들 수 있습니다.
</Check>
</Check>

View File

@@ -106,7 +106,7 @@ CrewAI는 의존성 관리와 패키지 처리를 위해 `uv`를 사용합니다
# CrewAI 프로젝트 생성하기
에이전트 및 태스크를 정의할 때 구조적인 접근 방식을 위해 `YAML` 템플릿 스캐폴딩을 사용하는 것을 권장합니다. 다음은 시작 방법입니다:
`crewai create crew`는 이제 JSON-first crew 프로젝트를 생성합니다. 에이전트는 `agents/*.jsonc`에, 태스크와 crew 수준 설정은 `crew.jsonc`에 두며, `crewai run`은 이 JSON 정의를 직접 로드합니다.
<Steps>
<Step title="프로젝트 스캐폴딩 생성">
@@ -119,21 +119,20 @@ CrewAI는 의존성 관리와 패키지 처리를 위해 `uv`를 사용합니다
```
my_project/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── pyproject.toml
├── README.md
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
- `crew.py`, `config/agents.yaml`, `config/tasks.yaml`을 사용하는 기존 Python/YAML 스캐폴드가 필요하다면 다음을 실행하세요:
```shell
crewai create crew <your_project_name> --classic
```
</Step>
@@ -142,15 +141,15 @@ CrewAI는 의존성 관리와 패키지 처리를 위해 `uv`를 사용합니다
- 프로젝트에는 다음과 같은 주요 파일들이 포함되어 있습니다:
| 파일 | 용도 |
| --- | --- |
| `agents.yaml` | AI 에이전트 및 역할 정의 |
| `tasks.yaml` | 에이전트 태스크 및 워크플로우 설정 |
| `crew.jsonc` | crew, 태스크 순서, 프로세스, 기본 입력값 설정 |
| `agents/*.jsonc` | 에이전트의 역할, 목표, backstory, LLM, 도구, 동작 정의 |
| `.env` | API 키 및 환경 변수 저장 |
| `main.py` | 프로젝트 진입점 및 실행 흐름 |
| `crew.py` | Crew 오케스트레이션 및 코디네이션 |
| `tools/` | 커스텀 에이전트 도구 디렉터리 |
| `knowledge/` | 지식 베이스 디렉터리 |
| `tools/` | `custom:<name>` 도구를 위한 선택적 Python 파일 |
| `knowledge/` | 에이전트용 선택적 지식 파일 |
| `skills/` | crew에 적용할 선택적 skill 파일 |
- `agents.yaml` 및 `tasks.yaml`을 편집하여 crew 동작을 정의하는 것부터 시작하세요.
- `crew.jsonc`와 `agents/` 안의 파일을 편집하여 crew 동작을 정의하세요.
- 에이전트와 태스크 텍스트에 `{placeholder}`를 사용하고, `crew.jsonc`의 `inputs`에 기본값을 넣으세요. `crewai run` 실행 시 빠진 값은 CLI가 묻습니다.
- API 키와 같은 민감한 정보는 `.env` 파일에 보관하세요.
</Step>

View File

@@ -5,11 +5,15 @@ icon: "at"
mode: "wide"
---
이 가이드는 `crew.py` 파일에서 **agent**, **task**, 및 기타 구성 요소를 올바르게 참조하기 위해 주석을 사용하는 방법을 설명합니다.
이 가이드는 클래식 `crew.py` 파일에서 **agent**, **task**, 및 기타 구성 요소를 올바르게 참조하기 위해 어노테이션을 사용하는 방법을 설명합니다.
<Note>
`crewai create crew <name>`으로 만든 새 프로젝트는 JSON-first이며 `crew.jsonc`와 `agents/*.jsonc`를 사용합니다. 이 가이드는 `crewai create crew <name> --classic`으로 만든 클래식 프로젝트, 기존 Python/YAML 프로젝트 마이그레이션, 또는 Python 데코레이터 제어가 필요한 경우에 사용하세요.
</Note>
## 소개
CrewAI 프레임워크에서 어노테이션은 클래스와 메소드를 데코레이트하는 데 사용되며, crew의 다양한 컴포넌트에 메타데이터와 기능을 제공합니다. 이러한 어노테이션은 코드의 구성과 구조화를 돕고, 코드의 가독성과 유지 관리를 용이하게 만듭니다.
CrewAI 프레임워크에서 어노테이션은 클래스와 메소드를 데코레이트하는 데 사용되며, crew의 다양한 컴포넌트에 메타데이터와 기능을 제공합니다. 클래식 Python/YAML 프로젝트에서는 `config/agents.yaml`, `config/tasks.yaml`을 로드하고 `Crew` 객체를 반환하는 코드를 구조화합니다.
## 사용 가능한 어노테이션
@@ -113,9 +117,9 @@ def crew(self) -> Crew:
`@crew` 어노테이션은 `Crew` 객체를 생성하고 반환하는 메서드를 데코레이션하는 데 사용됩니다. 이 메서드는 모든 구성 요소(agents와 tasks)를 기능적인 crew로 조합합니다.
## YAML 구성
## 클래식 YAML 구성
에이전트 구성은 일반적으로 YAML 파일에 저장됩니다. 아래는 연구원 에이전트에 대한 `agents.yaml` 파일 예시입니다.
클래식 프로젝트에서 에이전트 구성은 일반적으로 YAML 파일에 저장됩니다. 아래는 연구원 에이전트에 대한 `agents.yaml` 파일 예시입니다.
```yaml
researcher:
@@ -146,6 +150,6 @@ YAML 파일의 `llm`과 `tools`가 Python 클래스에서 `@llm` 및 `@tool`로
- **일관성 있는 명명**: 메서드에 대해 명확하고 일관성 있는 명명 규칙을 사용하세요. 예를 들어, agent 메서드는 역할에 따라 이름을 지정할 수 있습니다(예: researcher, reporting_analyst).
- **환경 변수**: API 키와 같은 민감한 정보를 위해 환경 변수를 사용하세요.
- **유연성**: agent와 task를 쉽게 추가 및 제거할 수 있도록 crew를 유연하게 설계하세요.
- **YAML-코드 일치**: YAML 파일의 이름과 구조가 Python 코드의 데코레이터가 적용된 메서드와 정확히 일치하는지 확인하세요.
- **YAML-코드 일치**: 클래식 프로젝트에서는 YAML 파일의 이름과 구조가 Python 코드의 데코레이터가 적용된 메서드와 정확히 일치하는지 확인하세요.
이 지침을 따르고 주석을 올바르게 사용하면 CrewAI 프레임워크를 이용해 구조적이고 유지보수가 쉬운 crew를 만들 수 있습니다.
이 지침을 따르고 어노테이션을 올바르게 사용하면 클래식 crew를 구조적이고 유지보수하기 쉽게 유지할 수 있습니다. 새 crew에는 [Crews](/ko/concepts/crews)의 JSON-first 구조를 권장합니다.

View File

@@ -39,84 +39,60 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
이렇게 하면 `src/latest_ai_flow/` 아래에 Flow 앱이 만들어지고, 다음 단계에서 **단일 에이전트** 연구 crew로 바꿀 시작용 crew가 `crews/content_crew/`에 포함됩니다.
</Step>
<Step title="`agents.yaml`에 에이전트 하나 설정">
`src/latest_ai_flow/crews/content_crew/config/agents.yaml` 내용을 한 명의 연구원만 남기도록 바꿉니다. `{topic}` 같은 변수는 `crew.kickoff(inputs=...)`로 채워집니다.
<Step title="JSONC로 에이전트 하나 설정">
`src/latest_ai_flow/crews/content_crew/agents/researcher.jsonc`를 만듭니다(`agents/` 디렉터리가 없으면 생성). `{topic}` 같은 변수는 `crew.kickoff(inputs=...)`로 채워집니다.
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
researcher:
role: >
{topic} 시니어 데이터 리서처
goal: >
{topic} 분야의 최신 동향을 파악한다
backstory: >
당신은 {topic}의 최신 흐름을 찾아내는 데 능숙한 연구원입니다.
가장 관련성 높은 정보를 찾아 명확하게 전달합니다.
```jsonc agents/researcher.jsonc
{
"role": "{topic} 시니어 데이터 리서처",
"goal": "{topic} 분야의 최신 동향을 파악한다",
"backstory": "당신은 가장 관련성 높은 정보를 찾아 명확하게 전달하는 연구원입니다.",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true
}
}
```
</Step>
<Step title="`tasks.yaml`에 작업 하나 설정">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
research_task:
description: >
{topic}에 대해 철저히 조사하세요. 웹 검색으로 최신이고 신뢰할 수 있는 정보를 찾으세요.
현재 연도는 2026년입니다.
expected_output: >
마크다운 보고서로, 주요 트렌드·주목할 도구나 기업·시사점 등으로 섹션을 나누세요.
분량은 약 800~1200단어. 문서 전체를 코드 펜스로 감싸지 마세요.
agent: researcher
output_file: output/report.md
<Step title="`crew.jsonc`에 crew 설정">
`src/latest_ai_flow/crews/content_crew/crew.jsonc`를 만듭니다:
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "{topic}에 대해 철저히 조사하세요. 웹 검색으로 최신이고 신뢰할 수 있는 정보를 찾으세요.",
"expected_output": "마크다운 보고서로, 주요 트렌드·주목할 도구나 기업·시사점 등으로 섹션을 나누세요. 분량은 약 800~1200단어. 문서 전체를 코드 펜스로 감싸지 마세요.",
"agent": "researcher",
"output_file": "output/report.md",
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
</Step>
<Step title="crew 클래스 연결 (`content_crew.py`)">
생성된 crew가 YAML을 읽고 연구원에게 `SerperDevTool`을 붙이도록 합니다.
<Step title="JSON crew 로드 (`content_crew.py`)">
생성된 `content_crew.py`를 `crew.jsonc`를 `Crew`로 바꾸는 작은 loader로 교체합니다.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
from pathlib import Path
from crewai import Agent, Crew, Process, Task
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.project import load_crew
@CrewBase
class ResearchCrew:
"""Flow 안에서 사용하는 단일 에이전트 연구 crew."""
agents: List[BaseAgent]
tasks: List[Task]
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config["research_task"], # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
</Step>
@@ -130,7 +106,7 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
from crewai.flow import Flow, listen, start
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
from latest_ai_flow.crews.content_crew.content_crew import kickoff_content_crew
class ResearchFlowState(BaseModel):
@@ -149,7 +125,7 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
result = kickoff_content_crew(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("연구 crew 실행 완료.")
@@ -171,7 +147,7 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
```
<Tip>
패키지 이름이 `latest_ai_flow`가 아니면 `ResearchCrew` import 경로를 프로젝트 모듈 경로에 맞게 바꾸세요.
패키지 이름이 `latest_ai_flow`가 아니면 `kickoff_content_crew` import 경로를 프로젝트 모듈 경로에 맞게 바꾸세요.
</Tip>
</Step>
@@ -198,7 +174,7 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
<CodeGroup>
```markdown output/report.md
# 2026년 AI 에이전트: 동향과 전망
# AI 에이전트: 최신 동향과 전망
## 요약
@@ -219,7 +195,7 @@ CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/install
## 한 번에 이해하기
1. **Flow** — `LatestAiFlow`는 `prepare_topic` → `run_research` → `summarize` 순으로 실행됩니다. 상태(`topic`, `report`)는 Flow에 있습니다.
2. **Crew** — `ResearchCrew`는 에이전트 한 명·작업 하나로 실행니다. 연구원이 **Serper**로 웹을 검색하고 구조화된 보고서를 씁니다.
2. **Crew** — `kickoff_content_crew`가 `crew.jsonc`를 로드하고 에이전트 한 명·작업 하나로 실행니다. 연구원이 **Serper**로 웹을 검색하고 구조화된 보고서를 씁니다.
3. **결과물** — 작업의 `output_file`이 `output/report.md`에 보고서를 씁니다.
Flow 패턴(라우팅, 지속성, human-in-the-loop)을 더 보려면 [첫 Flow 만들기](/ko/guides/flows/first-flow)와 [Flows](/ko/concepts/flows)를 참고하세요. Flow 없이 crew만 쓰려면 [Crews](/ko/concepts/crews)를, 작업 없이 단일 `Agent`의 `kickoff()`만 쓰려면 [Agents](/ko/concepts/agents#direct-agent-interaction-with-kickoff)를 참고하세요.
@@ -230,7 +206,10 @@ Flow 패턴(라우팅, 지속성, human-in-the-loop)을 더 보려면 [첫 Flow
### 이름 일치
YAML 키(`researcher`, `research_task`)는 `@CrewBase` 클래스의 메서드 이름과 같아야 합니다. 전체 데코레이터 패턴은 [Crews](/ko/concepts/crews)를 참고하세요.
`crew.jsonc`의 이름은 파일과 참조에 맞아야 합니다:
- `agents: ["researcher"]`는 `agents/researcher.jsonc`를 로드합니다.
- `tasks[].agent: "researcher"`는 해당 태스크를 그 에이전트에 배정합니다.
## 배포

View File

@@ -20,7 +20,7 @@ npx skills add crewaiinc/skills
## 에이전트가 얻는 것
- **Flows** — CrewAI 방식의 상태ful 앱, 단계, crew kickoff
- **Crew & 에이전트** — YAML 우선 패턴, 역할, 작업, 위임
- **Crew & 에이전트** — JSON-first 패턴(`crew.jsonc`, `agents/*.jsonc`), 역할, 작업, 위임
- **도구 & 통합** — 검색, API, 일반적인 CrewAI 도구 연결
- **프로젝트 구조** — CLI 스캐폴드 및 저장소 관례와 정렬
- **최신 패턴** — 스킬이 현재 CrewAI 문서 및 권장 사항을 반영

View File

@@ -71,13 +71,39 @@ O Construtor Visual de Agentes permite:
## Criando Agentes
Existem duas maneiras de criar agentes no CrewAI: usando **configuração YAML (recomendado)** ou definindo-os **diretamente em código**.
Existem duas formas comuns de criar agentes no CrewAI: usando **configuração JSONC (recomendado para novas crews)** ou definindo-os **diretamente em código**.
### Configuração em YAML (Recomendado)
### Configuração JSONC (Recomendado)
Usar configuração em YAML proporciona uma maneira mais limpa e fácil de manter para definir agentes. Recomendamos fortemente esse método em seus projetos CrewAI.
Novos projetos criados com `crewai create crew <name>` usam configuração JSON-first. Cada agente fica em `agents/<agent_name>.jsonc`, e `crew.jsonc` lista quais agentes fazem parte da crew.
Depois de criar seu projeto CrewAI conforme descrito na seção de [Instalação](/pt-BR/installation), navegue até o arquivo `src/latest_ai_development/config/agents.yaml` e edite o template para atender aos seus requisitos.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You find the most relevant information and present it clearly.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
Use `{placeholder}` em `role`, `goal` ou `backstory`. Defina padrões em `crew.jsonc` dentro de `inputs`; `crewai run` pergunta por valores que estiverem faltando. Campos de comportamento como `verbose`, `allow_delegation`, `max_iter`, `memory`, `cache` e `planning_config` podem ficar no topo ou em `settings`.
<Note>
JSONC aceita comentários e vírgulas finais. Se `agents/<name>.jsonc` e `agents/<name>.json` existirem, CrewAI usa o arquivo JSONC.
</Note>
### Configuração YAML Clássica
Projetos clássicos criados com `crewai create crew <name> --classic` usam `config/agents.yaml` e uma classe `@CrewBase` em `crew.py`.
A configuração YAML continua suportada para projetos existentes em Python/YAML e para equipes que preferem definir agentes a partir de uma classe `@CrewBase`.
Depois de criar um projeto clássico, navegue até o arquivo `src/<project_name>/config/agents.yaml` e edite o template para atender aos seus requisitos.
<Note>
Variáveis em seus arquivos YAML (como `{topic}`) serão substituídas pelos valores fornecidos em seus inputs ao executar o crew:
@@ -89,7 +115,7 @@ crew.kickoff(inputs={'topic': 'AI Agents'})
Veja um exemplo de como configurar agentes usando YAML:
```yaml agents.yaml
# src/latest_ai_development/config/agents.yaml
# src/<project_name>/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
@@ -114,7 +140,7 @@ reporting_analyst:
Para usar essa configuração YAML no seu código, crie uma classe de crew que herda de `CrewBase`:
```python Code
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process
from crewai.project import CrewBase, agent, crew
from crewai_tools import SerperDevTool

View File

@@ -53,6 +53,8 @@ crewai create crew my_new_crew
crewai create flow my_new_flow
```
Por padrão, `crewai create crew` cria um projeto JSON-first com `crew.jsonc` e `agents/*.jsonc`. Use `crewai create crew my_new_crew --classic` somente quando quiser o scaffold antigo em Python/YAML com `crew.py`, `config/agents.yaml` e `config/tasks.yaml`.
### 2. Version
Mostre a versão instalada do CrewAI.
@@ -203,7 +205,20 @@ crewai chat
Garanta que você execute estes comandos a partir do diretório raiz do seu projeto CrewAI.
</Note>
<Note>
IMPORTANTE: Defina a propriedade `chat_llm` no seu arquivo `crew.py` para habilitar este comando.
IMPORTANTE: Defina a propriedade `chat_llm` na definição da sua crew para habilitar este comando.
Para crews JSON-first, adicione em `crew.jsonc`:
```jsonc
{
"name": "My Crew",
"agents": ["researcher"],
"tasks": [],
"chat_llm": "openai/gpt-4o"
}
```
Para crews clássicas Python/YAML, defina em `crew.py`:
```python
@crew
@@ -334,7 +349,7 @@ Assista ao vídeo tutorial para uma demonstração passo-a-passo de implantaçã
### 11. Chaves de API
Ao executar o comando `crewai create crew`, o CLI primeiro mostrará os 5 provedores de LLM mais comuns e pedirá para você selecionar um.
Ao executar o comando `crewai create crew`, o CLI mostrará provedores de LLM disponíveis e depois a seleção de modelo para o provedor escolhido. O modelo selecionado é salvo no `.env` gerado, e cada agente JSONC pode definir seu próprio `llm`.
Após selecionar um provedor de LLM, será solicitado que você informe as chaves de API.

View File

@@ -41,13 +41,54 @@ Uma crew no crewAI representa um grupo colaborativo de agentes trabalhando em co
## Criando Crews
Existem duas maneiras de criar crews no CrewAI: utilizando **configuração YAML (recomendado)** ou definindo diretamente **em código**.
Existem duas maneiras principais de criar crews no CrewAI: utilizando **configuração JSONC (recomendada para novas crews)** ou definindo a crew **em código** para projetos clássicos e casos avançados.
### Configuração YAML (Recomendado)
### Configuração JSONC (Recomendado)
O uso da configuração YAML proporciona uma forma mais limpa e fácil de manter para definir crews, sendo consistente com a definição de agentes e tasks em projetos CrewAI.
Novos projetos criados com `crewai create crew <name>` usam `crew.jsonc` para configurações da crew e tarefas, além de um arquivo por agente em `agents/`. `crewai run` detecta `crew.jsonc` ou `crew.json`, carrega os agentes referenciados, pergunta por placeholders ausentes e inicia a crew.
Após criar seu projeto CrewAI conforme descrito na seção [Instalação](/pt-BR/installation), você pode definir sua crew em uma classe que herda de `CrewBase` e utiliza decorators para definir agentes, tarefas e a própria crew.
```jsonc crew.jsonc
{
"name": "Market Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research",
"description": "Research {topic} and collect the most relevant facts.",
"expected_output": "Structured research notes about {topic}.",
"agent": "researcher"
},
{
"name": "analysis",
"description": "Analyze the research and write a concise report.",
"expected_output": "A markdown report with findings and recommendations.",
"agent": "analyst",
"context": ["research"],
"output_file": "output/report.md"
}
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "AI Agents"
}
}
```
Cada string em `agents` resolve primeiro para `agents/<name>.jsonc` e depois para `agents/<name>.json`. Para crews hierárquicas, use `"process": "hierarchical"` com `manager_llm` ou `manager_agent`.
<Warning>
Execute projetos JSON apenas de fontes confiáveis. Ferramentas `custom:<name>` e referências `{"python": "module.attribute"}` executam código Python local quando a crew é carregada.
</Warning>
### Configuração YAML Clássica
Projetos clássicos criados com `crewai create crew <name> --classic` usam `crew.py`, `config/agents.yaml`, `config/tasks.yaml` e os decorators `@CrewBase`, `@agent`, `@task` e `@crew`.
Essa abordagem continua suportada para projetos existentes em Python/YAML e para equipes que precisam de controle explícito via decorators.
Após criar um projeto clássico, você pode definir sua crew em uma classe que herda de `CrewBase` e utiliza decorators para definir agentes, tarefas e a própria crew.
#### Exemplo de Classe Crew com Decorators

View File

@@ -820,7 +820,7 @@ Você pode gerar um novo projeto CrewAI que já inclui toda a estrutura para cri
crewai create flow name_of_flow
```
Esse comando irá gerar um novo projeto CrewAI com a estrutura de pastas necessária. O projeto gerado inclui uma crew pré-criada chamada `poem_crew`, já funcional. Você pode usar essa crew como modelo, copiando, colando e editando para criar outras crews.
Esse comando irá gerar um novo projeto CrewAI com a estrutura de pastas necessária. O projeto gerado inclui uma crew pré-criada chamada `poem_crew`, já funcional. A crew embutida inicial usa a estrutura clássica Python/YAML; novas crews independentes criadas com `crewai create crew` usam a estrutura JSON-first.
### Estrutura de Pastas
@@ -850,7 +850,29 @@ Na pasta `crews`, você pode definir múltiplas crews. Cada crew tem sua própri
- `config/tasks.yaml`: Define as tarefas da crew.
- `poem_crew.py`: Contém a definição da crew, incluindo agentes, tarefas, etc.
Você pode copiar, colar e editar a `poem_crew` para criar outras crews.
Você pode copiar, colar e editar a `poem_crew` para criar outras crews clássicas embutidas.
Para crews embutidas JSON-first, use uma pasta com `crew.jsonc` e `agents/*.jsonc`:
```text
crews/
└── research_crew/
├── agents/
│ └── researcher.jsonc
└── crew.jsonc
```
Depois carregue a crew em uma etapa do Flow:
```python
from pathlib import Path
from crewai.project import load_crew
crew, default_inputs = load_crew(
Path(__file__).parent / "crews" / "research_crew" / "crew.jsonc"
)
result = crew.kickoff(inputs={**default_inputs, "topic": "AI Agents"})
```
### Conectando Crews no `main.py`

View File

@@ -67,13 +67,48 @@ crew = Crew(
## Criando Tarefas
Existem duas maneiras de criar tarefas no CrewAI: utilizando **configuração YAML (recomendado)** ou definindo-as **diretamente no código**.
Existem duas formas comuns de criar tarefas no CrewAI: usando **configuração JSONC (recomendado para novas crews)** ou definindo-as **diretamente no código**.
### Configuração YAML (Recomendado)
### Configuração JSONC (Recomendado)
Utilizar configuração YAML oferece uma forma mais limpa e de fácil manutenção para definir tarefas. Recomendamos fortemente esse método em seus projetos CrewAI.
Novos projetos criados com `crewai create crew <name>` definem tarefas no `crew.jsonc`.
Após criar seu projeto CrewAI conforme indicado na seção [Instalação](/pt-BR/installation), navegue até o arquivo `src/latest_ai_development/config/tasks.yaml` e modifique o template para refletir os requisitos específicos das tarefas.
````jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "reporting_analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research about {topic}.",
"expected_output": "A list of the most relevant information about {topic}.",
"agent": "researcher"
},
{
"name": "reporting_task",
"description": "Review the research and expand it into a detailed report.",
"expected_output": "A polished markdown report.",
"agent": "reporting_analyst",
"context": ["research_task"],
"markdown": true,
"output_file": "report.md"
}
],
"inputs": {
"topic": "AI Agents"
}
}
````
Cada tarefa precisa de `description` e `expected_output`. O valor de `agent` deve corresponder a um agente listado em `agents`. `context` referencia nomes de tarefas anteriores; referências futuras são rejeitadas. Campos comuns incluem `name`, `agent`, `context`, `output_file`, `tools`, `human_input`, `async_execution`, `guardrail`, `guardrails`, `markdown`, `output_json`, `output_pydantic` e `response_model`.
### Configuração YAML Clássica
Projetos clássicos criados com `crewai create crew <name> --classic` usam `config/tasks.yaml` e uma classe `@CrewBase` em `crew.py`.
A configuração YAML continua suportada para projetos existentes em Python/YAML e para equipes que preferem definir tarefas a partir de uma classe `@CrewBase`.
Após criar um projeto clássico, navegue até o arquivo `src/<project_name>/config/tasks.yaml` e modifique o template para refletir os requisitos específicos das tarefas.
<Note>
Variáveis em seus arquivos YAML (como `{topic}`) serão substituídas por valores vindos dos seus inputs ao executar o crew:
@@ -109,7 +144,7 @@ reporting_task:
Para usar essa configuração YAML em seu código, crie uma classe crew que herda de `CrewBase`:
```python crew.py
# src/latest_ai_development/crew.py
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

View File

@@ -26,10 +26,10 @@ Antes de executar esta verificação:
## Passo 1 — Estruturar um Crew de Verificação
Crie um novo projeto de crew. O CrewAI CLI estrutura tudo:
Crie um projeto de crew clássico porque este exemplo conecta uma ferramenta Python via `crew.py`:
```bash
crewai create crew rotation_verifier --skip_provider
crewai create crew rotation_verifier --classic --skip_provider
cd rotation_verifier
```

View File

@@ -373,17 +373,17 @@ git push
**Solução**: Verifique se seu projeto corresponde à estrutura esperada:
- **Tanto Crews quanto Flows**: Devem ter ponto de entrada em `src/project_name/main.py`
- **Crews**: Usam uma função `run()` como ponto de entrada
- **Flows**: Usam uma função `kickoff()` como ponto de entrada
- **Crews JSON-first**: Mantenha `crew.jsonc` ou `crew.json` e `agents/` na raiz do projeto
- **Crews clássicas**: Use `src/project_name/main.py` com uma função de entrada `run()`
- **Flows**: Use `src/project_name/main.py` com uma função de entrada `kickoff()`
Veja [Preparar para Implantação](/pt-BR/enterprise/guides/prepare-for-deployment) para diagramas de estrutura detalhados.
#### Decorador CrewBase Ausente
#### Decorador CrewBase Ausente em uma Crew Clássica
**Sintoma**: Erros "Crew not found", "Config not found" ou erros de configuração de agent/task
**Solução**: Certifique-se de que **todas** as classes crew usam o decorador `@CrewBase`:
**Solução**: Para crews clássicas Python/YAML, certifique-se de que todas as classes crew usam o decorador `@CrewBase`. Crews JSON-first não precisam desse decorador.
```python
from crewai.project import CrewBase, agent, crew, task
@@ -403,8 +403,8 @@ class YourCrew():
```
<Info>
Isso se aplica a Crews independentes E crews embutidos dentro de projetos Flow.
Toda classe crew precisa do decorador.
Isso se aplica a classes crew Python clássicas, incluindo crews clássicas embutidas em projetos Flow.
Crews JSON-first são validadas a partir de `crew.jsonc` e `agents/`.
</Info>
#### Tipo Incorreto no pyproject.toml
@@ -441,8 +441,8 @@ type = "flow"
**Solução**:
1. Verifique os logs de execução no dashboard AMP (aba Traces)
2. Verifique se todas as ferramentas têm as chaves API necessárias configuradas
3. Certifique-se de que as configurações de agents em `agents.yaml` são válidas
4. Verifique se há erros de sintaxe nas configurações de tasks em `tasks.yaml`
3. Para crews JSON-first, valide `crew.jsonc` e os arquivos referenciados em `agents/`
4. Para crews clássicas, verifique se `agents.yaml` e `tasks.yaml` são válidos
<Card title="Precisa de Ajuda?" icon="headset" href="mailto:support@crewai.com">
Entre em contato com nossa equipe de suporte para ajuda com questões de

View File

@@ -24,10 +24,9 @@ company-ai/
`-- crews/
|-- support_agent/
| |-- pyproject.toml
| `-- src/
| `-- support_agent/
| |-- main.py
| `-- crew.py
| |-- crew.jsonc
| `-- agents/
| `-- support_agent.jsonc
`-- research_flow/
|-- pyproject.toml
`-- src/
@@ -48,7 +47,7 @@ selecionada como a raiz do projeto da automação.
Quando um diretório de trabalho é definido, o AMP usa essa pasta para:
- Validação do projeto, incluindo `pyproject.toml`, `src/` e o ponto de entrada do Crew ou Flow
- Validação do projeto, incluindo `pyproject.toml`, arquivos de crew JSON e qualquer ponto de entrada clássico de Crew ou Flow
- Instalação de dependências com `uv`
- O diretório de trabalho do processo em execução
- A variável de ambiente `CREW_ROOT_DIR`
@@ -155,11 +154,16 @@ remove barras finais. Um valor em branco usa a raiz do repositório.
## Arquivos Lock e Workspaces UV
A pasta selecionada deve conter o `pyproject.toml` e o diretório `src/` da
automação. Um arquivo `uv.lock` ou `poetry.lock` pode ficar na pasta selecionada
ou na raiz do repositório.
A pasta selecionada deve conter o `pyproject.toml` e os arquivos exigidos pelo
tipo de projeto:
Isso oferece suporte aos dois layouts comuns de monorepo:
- Crew JSON-first: `crew.jsonc` ou `crew.json`, além de `agents/`
- Crew clássico ou Flow: `src/` com o ponto de entrada Python esperado
Um arquivo `uv.lock` ou `poetry.lock` pode ficar na pasta selecionada ou na raiz
do repositório.
Isso oferece suporte aos dois layouts comuns de arquivo lock:
<Tabs>
<Tab title="Arquivo lock do projeto">
@@ -169,9 +173,9 @@ Isso oferece suporte aos dois layouts comuns de monorepo:
`-- support_agent/
|-- pyproject.toml
|-- uv.lock
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
@@ -184,9 +188,9 @@ Isso oferece suporte aos dois layouts comuns de monorepo:
`-- crews/
`-- support_agent/
|-- pyproject.toml
`-- src/
`-- support_agent/
`-- main.py
|-- crew.jsonc
`-- agents/
`-- support_agent.jsonc
```
</Tab>
</Tabs>

View File

@@ -24,7 +24,7 @@ Entender qual tipo você está implantando é essencial porque eles têm estrutu
<CardGroup cols={2}>
<Card title="Projetos Crew" icon="users">
Equipes de agentes de IA independentes com `crew.py` definindo agentes e tarefas. Ideal para tarefas focadas e colaborativas.
Equipes independentes de agentes de IA. Novas crews são JSON-first com `crew.jsonc` e `agents/`; crews clássicas ainda podem usar `crew.py`.
</Card>
<Card title="Projetos Flow" icon="diagram-project">
Workflows orquestrados com crews embutidos em uma pasta `crews/`. Ideal para processos complexos de múltiplas etapas.
@@ -33,19 +33,19 @@ Entender qual tipo você está implantando é essencial porque eles têm estrutu
| Aspecto | Crew | Flow |
|---------|------|------|
| **Estrutura do projeto** | `src/project_name/` com `crew.py` | `src/project_name/` com pasta `crews/` |
| **Localização da lógica principal** | `src/project_name/crew.py` | `src/project_name/main.py` (classe Flow) |
| **Função de ponto de entrada** | `run()` em `main.py` | `kickoff()` em `main.py` |
| **Estrutura do projeto** | Raiz do projeto com `crew.jsonc` e `agents/` | `src/project_name/` com pasta `crews/` |
| **Localização da lógica principal** | `crew.jsonc` (clássico: `src/project_name/crew.py`) | `src/project_name/main.py` (classe Flow) |
| **Função de ponto de entrada** | Carregada a partir de `crew.jsonc` (clássico: `run()` em `main.py`) | `kickoff()` em `main.py` |
| **Tipo no pyproject.toml** | `type = "crew"` | `type = "flow"` |
| **Comando CLI de criação** | `crewai create crew name` | `crewai create flow name` |
| **Localização da configuração** | `src/project_name/config/` | `src/project_name/crews/crew_name/config/` |
| **Localização da configuração** | `crew.jsonc`, `agents/`, `tools/` opcional | `src/project_name/crews/crew_name/config/` ou pastas de crew JSON embutidas |
| **Pode conter outros crews** | Não | Sim (na pasta `crews/`) |
## Referência de Estrutura de Projeto
### Estrutura de Projeto Crew
Quando você executa `crewai create crew my_crew`, você obtém esta estrutura:
Quando você executa `crewai create crew my_crew`, recebe a estrutura JSON-first:
```
my_crew/
@@ -54,24 +54,26 @@ my_crew/
├── README.md
├── .env
├── uv.lock # OBRIGATÓRIO para implantação
── src/
└── my_crew/
├── __init__.py
├── main.py # Ponto de entrada com função run()
├── crew.py # Classe Crew com decorador @CrewBase
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml # Definições de agentes
└── tasks.yaml # Definições de tarefas
── crew.jsonc # Configurações, tarefas, processo e inputs
├── agents/
└── researcher.jsonc # Definições de agentes
├── tools/ # Ferramentas custom:<name> opcionais
├── knowledge/
└── skills/
```
<Warning>
A estrutura aninhada `src/project_name/` é crítica para Crews.
Colocar arquivos no nível errado causará falhas na implantação.
Para crews JSON-first, mantenha `crew.jsonc`, `agents/`, `tools/`, `knowledge/` e `skills/`
na raiz do projeto. Colocá-los dentro de `src/` impede que `crewai run` e a validação de
implantação encontrem a definição da crew.
</Warning>
<Info>
Projetos clássicos criados com `crewai create crew my_crew --classic` usam a estrutura antiga
`src/project_name/crew.py`, `src/project_name/config/agents.yaml` e
`src/project_name/config/tasks.yaml`. Essa estrutura continua suportada para crews Python com decorators.
</Info>
### Estrutura de Projeto Flow
Quando você executa `crewai create flow my_flow`, você obtém esta estrutura:
@@ -100,9 +102,9 @@ my_flow/
```
<Info>
Tanto Crews quanto Flows usam a estrutura `src/project_name/`.
A diferença chave é que Flows têm uma pasta `crews/` para crews embutidos,
enquanto Crews têm `crew.py` diretamente na pasta do projeto.
Crews independentes JSON-first usam arquivos JSON na raiz do projeto. Flows ainda usam
`src/project_name/` e podem conter crews embutidas clássicas ou pastas de crew JSON carregadas com
`crewai.project.load_crew`.
</Info>
## Checklist Pré-Implantação
@@ -154,60 +156,90 @@ git commit -m "Add uv.lock for deployment"
git push
```
### 3. Validar Uso do Decorador CrewBase
### 3. Validar a Definição da Crew
**Toda classe crew deve usar o decorador `@CrewBase`.** Isso se aplica a:
<Tabs>
<Tab title="Crews JSON-first">
Crews JSON-first precisam ter `crew.jsonc` ou `crew.json` na raiz do projeto.
O array `agents` deve apontar para arquivos em `agents/`, e cada task deve referenciar
um nome de agent válido.
- Projetos crew independentes
- Crews embutidos dentro de projetos Flow
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Research {topic}.",
"expected_output": "A concise report.",
"agent": "researcher"
}
],
"inputs": {
"topic": "AI Agents"
}
}
```
```python
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
Ferramentas customizadas são referenciadas como `"custom:<name>"` e devem existir em
`tools/<name>.py` com uma subclasse de `BaseTool`.
</Tab>
<Tab title="Crews Python/YAML Clássicas">
Crews clássicas e crews Python embutidas em Flows devem usar o decorador `@CrewBase`.
@CrewBase # Este decorador é OBRIGATÓRIO
class MyCrew():
"""Descrição do meu crew"""
```python
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
agents: List[BaseAgent]
tasks: List[Task]
@CrewBase
class MyCrew():
"""Descrição do meu crew"""
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
agents: List[BaseAgent]
tasks: List[Task]
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
@agent
def my_agent(self) -> Agent:
return Agent(
config=self.agents_config['my_agent'], # type: ignore[index]
verbose=True
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
@task
def my_task(self) -> Task:
return Task(
config=self.tasks_config['my_task'] # type: ignore[index]
)
<Warning>
Se você esquecer o decorador `@CrewBase`, sua implantação falhará com
erros sobre configurações de agents ou tasks ausentes.
</Warning>
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
</Tab>
</Tabs>
### 4. Verificar Pontos de Entrada do Projeto
Tanto Crews quanto Flows têm seu ponto de entrada em `src/project_name/main.py`:
Crews JSON-first independentes não precisam de um `src/project_name/main.py` escrito manualmente;
`crewai run` e o empacotamento de implantação carregam `crew.jsonc` diretamente. Crews clássicas e Flows usam pontos de entrada Python:
<Tabs>
<Tab title="Para Crews">
<Tab title="Crews JSON-first">
Execute localmente a partir da raiz do projeto:
```bash
crewai run
```
</Tab>
<Tab title="Crews Clássicas">
O ponto de entrada usa uma função `run()`:
```python
@@ -278,16 +310,17 @@ grep -A2 "\[tool.crewai\]" pyproject.toml
# 2. Verificar se uv.lock existe
ls -la uv.lock || echo "ERRO: uv.lock ausente! Execute 'uv lock'"
# 3. Verificar se estrutura src/ existe
ls -la src/*/main.py 2>/dev/null || echo "Nenhum main.py encontrado em src/"
# 3. Para crews JSON-first, verificar crew.jsonc e agents/
([ -f crew.jsonc ] || [ -f crew.json ]) || echo "Nenhum crew.jsonc ou crew.json encontrado"
test -d agents || echo "Nenhum diretório agents/ encontrado"
# 4. Para Crews - verificar se crew.py existe
# 4. Para Crews clássicas - verificar se crew.py existe
ls -la src/*/crew.py 2>/dev/null || echo "Nenhum crew.py (esperado para Crews)"
# 5. Para Flows - verificar se pasta crews/ existe
ls -la src/*/crews/ 2>/dev/null || echo "Nenhuma pasta crews/ (esperado para Flows)"
# 6. Verificar uso do CrewBase
# 6. Para crews Python clássicas - verificar uso do CrewBase
grep -r "@CrewBase" . --include="*.py"
```
@@ -297,8 +330,9 @@ grep -r "@CrewBase" . --include="*.py"
|------|---------|----------|
| `uv.lock` ausente | Build falha durante resolução de dependências | Execute `uv lock` e faça commit |
| `type` errado no pyproject.toml | Build bem-sucedido mas falha em runtime | Altere para o tipo correto |
| Decorador `@CrewBase` ausente | Erros "Config not found" | Adicione decorador a todas as classes crew |
| Arquivos na raiz ao invés de `src/` | Ponto de entrada não encontrado | Mova para `src/project_name/` |
| `crew.jsonc` ou `agents/` ausente em uma crew JSON-first | Definição da crew não encontrada | Mantenha `crew.jsonc` e `agents/` na raiz do projeto |
| Decorador `@CrewBase` ausente em uma crew clássica | Erros "Config not found" | Adicione o decorador a todas as classes crew clássicas |
| Arquivos clássicos na raiz ao invés de `src/` | Ponto de entrada não encontrado | Mova arquivos Python clássicos para `src/project_name/` |
| `run()` ou `kickoff()` ausente | Não é possível iniciar automação | Adicione a função de entrada correta |
## Próximos Passos

View File

@@ -43,7 +43,7 @@ O CrewAI é nativo de IA. Esta página reúne o que um agente de codificação c
| Skill | Quando é usada |
|-------|----------------|
| `getting-started` | Novos projetos, escolha entre `LLM.call()` / `Agent` / `Crew` / `Flow`, arquivos `crew.py` / `main.py` |
| `getting-started` | Novos projetos, escolha entre `LLM.call()` / `Agent` / `Crew` / `Flow`, arquivos `crew.jsonc` / `main.py` |
| `design-agent` | Configurar agentes — papel, objetivo, história, ferramentas, LLMs, memória, guardrails |
| `design-task` | Descrever tarefas, dependências, saída estruturada (`output_pydantic`, `output_json`), revisão humana |
| `ask-docs` | Consultar o [servidor MCP da documentação CrewAI](https://docs.crewai.com/mcp) em tempo real para detalhes de API |
@@ -64,7 +64,7 @@ O CrewAI é nativo de IA. Esta página reúne o que um agente de codificação c
<Step title="Seu agente ganha expertise imediata em CrewAI">
O pacote ensina ao seu agente:
- **Flows** — apps com estado, passos e disparo de crews
- **Crews e agentes** — padrões YAML-first, papéis, tarefas, delegação
- **Crews e agentes** — padrões JSON-first (`crew.jsonc`, `agents/*.jsonc`), papéis, tarefas, delegação
- **Ferramentas e integrações** — busca, APIs, servidores MCP e ferramentas comuns do CrewAI
- **Estrutura do projeto** — scaffolds da CLI e convenções de repositório
- **Padrões atualizados** — alinhado à documentação e às melhores práticas atuais do CrewAI

View File

@@ -1,393 +1,142 @@
---
title: Monte sua Primeira Crew
description: Tutorial passo a passo para criar uma equipe colaborativa de IA que trabalha junta para resolver problemas complexos.
title: Crie sua primeira Crew
description: Tutorial passo a passo para criar uma equipe colaborativa de IA com configuração JSON-first.
icon: users-gear
mode: "wide"
---
## Liberando o Poder da IA Colaborativa
## Crie uma Crew de Pesquisa
Imagine ter uma equipe de agentes de IA especializados trabalhando juntos de forma harmoniosa para resolver problemas complexos, cada um contribuindo com suas habilidades únicas para alcançar um objetivo comum. Esse é o poder da CrewAI um framework que permite criar sistemas colaborativos de IA que podem realizar tarefas muito além do que uma única IA conseguiria sozinha.
Neste guia, vamos criar uma crew de pesquisa que irá nos ajudar a pesquisar e analisar um tema, e então criar um relatório abrangente. Este exemplo prático demonstra como agentes de IA podem colaborar para realizar tarefas complexas, mas é apenas o começo do que é possível com a CrewAI.
### O que Você Vai Construir e Aprender
Ao final deste guia, você terá:
1. **Criado uma equipe de pesquisa em IA especializada** com papéis e responsabilidades distintas
2. **Orquestrado a colaboração** entre múltiplos agentes de IA
3. **Automatizado um fluxo de trabalho complexo** que envolve coleta de informações, análise e geração de relatórios
4. **Desenvolvido habilidades fundamentais** que podem ser aplicadas em projetos mais ambiciosos
Embora estejamos criando uma crew de pesquisa simples neste guia, os mesmos padrões e técnicas podem ser aplicados para criar equipes muito mais sofisticadas para tarefas como:
- Criação de conteúdo em múltiplas etapas com redatores, editores e checadores de fatos especializados
- Sistemas de atendimento ao cliente complexos com agentes de suporte em diferentes níveis
- Analistas de negócios autônomos que coletam dados, criam visualizações e geram insights
- Equipes de desenvolvimento de produto que idealizam, projetam e planejam a implementação
Vamos começar a construir sua primeira crew!
Neste guia, você criará uma crew com dois agentes que pesquisa um tópico e escreve um relatório em markdown. Novos projetos de crew são JSON-first: agentes ficam em `agents/*.jsonc`, tarefas e configurações ficam em `crew.jsonc`, e `crewai run` carrega essa definição diretamente.
### Pré-requisitos
Antes de começar, certifique-se de que você:
Antes de começar:
1. Instalou a CrewAI seguindo o [guia de instalação](/pt-BR/installation)
2. Configurou sua chave de API de LLM no ambiente, conforme o [guia de configuração do LLM](/pt-BR/concepts/llms#setting-up-your-llm)
3. Tem conhecimento básico de Python
1. Instale o CrewAI seguindo o [guia de instalação](/pt-BR/installation)
2. Configure sua chave de LLM seguindo o [guia de LLMs](/pt-BR/concepts/llms#setting-up-your-llm)
3. Tenha uma chave [Serper.dev](https://serper.dev/) se quiser usar busca web
## Passo 1: Crie um Novo Projeto CrewAI
Primeiro, vamos criar um novo projeto CrewAI usando a CLI. Este comando irá configurar toda a estrutura do projeto com os arquivos necessários, permitindo que você foque em definir seus agentes e suas tarefas, em vez de se preocupar com código boilerplate.
## Etapa 1: Criar uma nova Crew
```bash
crewai create crew research_crew
cd research_crew
```
Isso irá gerar um projeto com a estrutura básica necessária para sua crew. A CLI cria automaticamente:
Estrutura criada:
- Um diretório de projeto com os arquivos necessários
- Arquivos de configuração para agentes e tarefas
- Uma implementação básica da crew
- Um script principal para rodar a crew
<Frame caption="CrewAI Framework Overview">
<img src="/images/crews.png" alt="CrewAI Framework Overview" />
</Frame>
## Passo 2: Explore a Estrutura do Projeto
Vamos dedicar um momento para entender a estrutura do projeto criada pela CLI. A CrewAI segue boas práticas para projetos Python, tornando fácil manter e estender seu código à medida que suas crews se tornam mais complexas.
```
```text
research_crew/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── 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
├── skills/
└── tools/
```
Esta estrutura segue as melhores práticas para projetos Python e facilita a organização do seu código. A separação dos arquivos de configuração (em YAML) do código de implementação (em Python) permite modificar o comportamento da sua crew sem alterar o código subjacente.
<Tip>
Precisa do layout antigo com `crew.py`, `config/agents.yaml` e `config/tasks.yaml`? Use `crewai create crew research_crew --classic`.
</Tip>
## Passo 3: Configure seus Agentes
## Etapa 2: Definir os agentes
Agora vem a parte divertida definir seus agentes de IA! Na CrewAI, agentes são entidades especializadas com papéis, objetivos e históricos específicos que moldam seu comportamento. Pense neles como personagens em uma peça, cada um com sua personalidade e propósito próprios.
Substitua o arquivo gerado `agents/researcher.jsonc` e adicione `agents/analyst.jsonc`. Os nomes dos arquivos são os nomes referenciados em `crew.jsonc`.
Para nossa crew de pesquisa, vamos criar dois agentes:
1. Um **pesquisador** que é especialista em encontrar e organizar informações
2. Um **analista** que pode interpretar os resultados da pesquisa e criar relatórios perspicazes
Vamos modificar o arquivo `agents.yaml` para definir esses agentes especializados. Certifique-se de
definir `llm` para o provedor que você está utilizando.
```yaml
# src/research_crew/config/agents.yaml
researcher:
role: >
Especialista Sênior em Pesquisa para {topic}
goal: >
Encontrar informações abrangentes e precisas sobre {topic}
com foco em desenvolvimentos recentes e insights chave
backstory: >
Você é um especialista em pesquisa experiente com talento para
encontrar informações relevantes de diversas fontes. Você se destaca em
organizar informações de forma clara e estruturada, tornando temas complexos acessíveis para outros.
llm: provider/model-id # ex: openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
analyst:
role: >
Analista de Dados e Redator de Relatórios para {topic}
goal: >
Analisar os resultados da pesquisa e criar um relatório abrangente e bem estruturado
que apresente os insights de forma clara e envolvente
backstory: >
Você é um analista habilidoso com experiência em interpretação de dados
e redação técnica. Tem talento para identificar padrões
e extrair insights relevantes dos dados de pesquisa, comunicando esses insights de forma eficaz por meio de relatórios bem elaborados.
llm: provider/model-id # ex: openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
```jsonc agents/researcher.jsonc
{
"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 who organizes complex information into clear, useful notes.",
// Substitua pelo seu modelo, por exemplo "openai/gpt-4o".
"llm": "provider/model-id",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
Perceba como cada agente tem um papel, objetivo e histórico distintos. Esses elementos não são apenas descritivos eles efetivamente moldam como o agente aborda suas tarefas. Ao criar cuidadosamente esses detalhes, você pode ter agentes com habilidades e perspectivas que se complementam.
## Passo 4: Defina suas Tarefas
Com nossos agentes definidos, agora precisamos atribuir tarefas específicas para eles realizarem. Tarefas na CrewAI representam o trabalho concreto que os agentes irão executar, com instruções detalhadas e saídas esperadas.
Para nossa crew de pesquisa, vamos definir duas tarefas principais:
1. Uma **tarefa de pesquisa** para coletar informações abrangentes
2. Uma **tarefa de análise** para criar um relatório com insights
Vamos modificar o arquivo `tasks.yaml`:
```yaml
# src/research_crew/config/tasks.yaml
research_task:
description: >
Realize uma pesquisa aprofundada sobre {topic}. Foque em:
1. Conceitos e definições chave
2. Desenvolvimento histórico e tendências recentes
3. Principais desafios e oportunidades
4. Aplicações relevantes ou estudos de caso
5. Perspectivas futuras e novos desenvolvimentos
Certifique-se de organizar seus achados em um formato estruturado, com seções claras.
expected_output: >
Um documento de pesquisa abrangente com seções bem organizadas cobrindo
todos os aspectos solicitados de {topic}. Inclua fatos, números
e exemplos específicos sempre que possível.
agent: researcher
analysis_task:
description: >
Analise os resultados da pesquisa e crie um relatório abrangente sobre {topic}.
Seu relatório deve:
1. Iniciar com um resumo executivo
2. Incluir todas as informações relevantes da pesquisa
3. Oferecer uma análise perspicaz de tendências e padrões
4. Apresentar recomendações ou considerações futuras
5. Estar formatado de forma profissional, clara e com títulos bem definidos
expected_output: >
Um relatório profissional, polido e estruturado sobre {topic} com apresentação dos resultados da pesquisa,
acrescentando análise e insights. O relatório deve ser bem estruturado,
incluindo resumo executivo, sessões principais e conclusão.
agent: analyst
context:
- research_task
output_file: output/report.md
```jsonc agents/analyst.jsonc
{
"role": "Report Analyst for {topic}",
"goal": "Turn research findings into a clear, well-structured report.",
"backstory": "You are a careful analyst with strong technical writing skills and a talent for extracting useful insights.",
// Substitua pelo seu modelo, por exemplo "openai/gpt-4o".
"llm": "provider/model-id",
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
Note o campo `context` na tarefa de análise esse é um recurso poderoso que permite ao analista acessar a saída da tarefa de pesquisa. Isso cria um fluxo de trabalho em que a informação circula naturalmente entre os agentes, como aconteceria em uma equipe humana.
Substitua `provider/model-id` pelo modelo usado, como `openai/gpt-4o`, `anthropic/claude-sonnet-4-6` ou `gemini/gemini-2.0-flash-001`.
## Passo 5: Configure sua Crew
## Etapa 3: Definir tarefas e configurações
Agora é hora de juntar tudo configurando nossa crew. A crew é o container que orquestra como os agentes trabalham juntos para completar as tarefas.
Substitua `crew.jsonc` por:
Vamos modificar o arquivo `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,
)
```
Neste código, estamos:
1. Criando o agente pesquisador e equipando-o com o SerperDevTool para buscas web
2. Criando o agente analista
3. Definindo as tarefas de pesquisa e análise
4. Configurando a crew para executar as tarefas sequencialmente (o analista espera o pesquisador terminar)
É aqui que a mágica acontece com poucas linhas de código, definimos um sistema colaborativo de IA onde agentes especializados trabalham juntos em um processo coordenado.
## Passo 6: Prepare seu Script Principal
Agora, vamos preparar o script principal que irá rodar nossa crew. É aqui que informamos o tema específico que queremos pesquisar.
```python
#!/usr/bin/env python
# src/research_crew/main.py
import os
from research_crew.crew import ResearchCrew
# Crie o diretório de saída se não existir
os.makedirs('output', exist_ok=True)
def run():
"""
Rodar a crew de pesquisa.
"""
inputs = {
'topic': 'Inteligência Artificial na Saúde'
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research_task",
"description": "Conduct thorough research on {topic}. Focus on key concepts, recent developments, major challenges, notable applications, and future outlook.",
"expected_output": "A comprehensive research document with organized sections, specific facts, and useful examples about {topic}.",
"agent": "researcher"
},
{
"name": "analysis_task",
"description": "Analyze the research findings and create a polished report on {topic}. Include an executive summary, key insights, trend analysis, and recommendations.",
"expected_output": "A professional markdown report with clear headings, a concise summary, main findings, and recommendations.",
"agent": "analyst",
"context": ["research_task"],
"output_file": "output/report.md",
"markdown": true
}
# Criar e rodar a crew
result = ResearchCrew().crew().kickoff(inputs=inputs)
# Imprimir o resultado
print("\n\n=== RELATÓRIO FINAL ===\n\n")
print(result.raw)
print("\n\nRelatório salvo em output/report.md")
if __name__ == "__main__":
run()
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "Artificial Intelligence in Healthcare"
}
}
```
Este script prepara o ambiente, define o tema de pesquisa e inicia o trabalho da crew. O poder da CrewAI fica evidente em como esse código é simples toda a complexidade do gerenciamento de múltiplos agentes de IA é tratada pelo framework.
`context` aponta para tarefas anteriores, então o analista recebe a saída da pesquisa. `inputs` define valores padrão para `{topic}`; se um valor faltar, `crewai run` perguntará no terminal.
## Passo 7: Defina suas Variáveis de Ambiente
## Etapa 4: Variáveis de ambiente
Crie um arquivo `.env` na raiz do seu projeto com suas chaves de API:
Edite `.env`:
```sh
SERPER_API_KEY=sua_serper_api_key
# Adicione a chave de API do seu provedor também.
SERPER_API_KEY=your_serper_api_key
# Adicione também a chave do seu provedor de modelo.
```
Confira o [guia de configuração do LLM](/pt-BR/concepts/llms#setting-up-your-llm) para detalhes sobre como configurar o provedor de sua escolha. Você pode obter a chave da Serper em [Serper.dev](https://serper.dev/).
## Passo 8: Instale as Dependências
Instale as dependências necessárias usando a CLI da CrewAI:
## Etapa 5: Instalar e executar
```bash
crewai install
```
Este comando irá:
1. Ler as dependências da configuração do seu projeto
2. Criar um ambiente virtual se necessário
3. Instalar todos os pacotes necessários
## Passo 9: Execute sua Crew
Agora chega o momento empolgante é hora de rodar sua crew e assistir à colaboração de IA em ação!
```bash
crewai run
```
Ao rodar esse comando, você verá sua crew ganhando vida. O pesquisador irá coletar informações sobre o tema especificado, e o analista irá criar um relatório abrangente baseado nessa pesquisa. Você poderá acompanhar em tempo real o raciocínio, as ações e os resultados dos agentes à medida que colaboram para concluir as tarefas.
Quando a execução terminar, abra `output/report.md`.
## Passo 10: Revise o Resultado
Após a conclusão do trabalho da crew, você encontrará o relatório final em `output/report.md`. O relatório incluirá:
1. Um resumo executivo
2. Informações detalhadas sobre o tema
3. Análises e insights
4. Recomendações ou considerações futuras
Tire um momento para valorizar o que você realizou você criou um sistema no qual múltiplos agentes de IA colaboraram em uma tarefa complexa, cada um contribuindo com suas habilidades especializadas para gerar um resultado maior do que qualquer agente conseguiria sozinho.
## Explorando Outros Comandos da CLI
A CrewAI oferece vários outros comandos úteis de CLI para trabalhar com crews:
```bash
# Ver todos os comandos disponíveis
crewai --help
# Rodar a crew
crewai run
# Testar a crew
crewai test
# Resetar as memórias da crew
crewai reset-memories
# Repetir a partir de uma tarefa específica
crewai replay -t <task_id>
```
## O que Mais é Possível: Além da sua Primeira Crew
O que você construiu neste guia é só o começo. As habilidades e padrões aprendidos aqui podem ser aplicados para criar sistemas de IA cada vez mais sofisticados. Veja algumas maneiras de expandir sua crew de pesquisa básica:
### Expandindo sua Crew
Você pode adicionar mais agentes especializados à sua crew:
- Um **checador de fatos** para verificar as informações encontradas
- Um **visualizador de dados** para criar gráficos e tabelas
- Um **especialista de domínio** com conhecimento aprofundado em uma área específica
- Um **crítico** para identificar pontos fracos na análise
### Adicionando Ferramentas e Capacidades
Você pode potencializar seus agentes com ferramentas adicionais:
- Ferramentas de navegação web para pesquisa em tempo real
- Ferramentas para CSV ou bancos de dados para análise de dados
- Ferramentas de execução de código para processamento de dados
- Conexões de API com serviços externos
### Criando Fluxos de Trabalho Mais Complexos
Você pode implementar processos mais sofisticados:
- Processos hierárquicos em que agentes gestores delegam para agentes
- Processos iterativos com loops de feedback para refinamento
- Processos paralelos onde múltiplos agentes trabalham simultaneamente
- Processos dinâmicos que se adaptam a resultados intermediários
### Aplicando em Diferentes Domínios
Os mesmos padrões podem ser aplicados para criar crews para:
- **Criação de conteúdo:** Redatores, editores, checadores de fatos e designers trabalhando juntos
- **Atendimento ao cliente:** Agentes de triagem, especialistas e controle de qualidade atuando colaborativamente
- **Desenvolvimento de produto:** Pesquisadores, designers e planejadores trabalhando em conjunto
- **Análise de dados:** Coletores de dados, analistas e especialistas em visualização
## Próximos Passos
Agora que você montou sua primeira crew, você pode:
1. Experimentar diferentes configurações e personalidades de agentes
2. Testar estruturas de tarefas e fluxos de trabalho mais complexos
3. Implementar ferramentas customizadas para dar novas capacidades aos agentes
4. Aplicar sua crew em outros temas ou domínios de problemas
5. Explorar [CrewAI Flows](/pt-BR/guides/flows/first-flow) para fluxos de trabalho avançados usando programação procedural
<Warning>
Execute projetos JSON crew apenas de fontes confiáveis. Ferramentas `custom:<name>` e referências `{"python": "module.attribute"}` executam Python local ao carregar a crew.
</Warning>
<Check>
Parabéns! Você construiu com sucesso sua primeira crew com o CrewAI, capaz de pesquisar e analisar qualquer tema que desejar. Essa experiência fundamental lhe deu as habilidades para criar sistemas de IA cada vez mais sofisticados, aptos a encarar problemas complexos e de múltiplas etapas por meio da inteligência colaborativa.
Você criou uma crew JSON-first funcional que pesquisa um tópico e escreve um relatório.
</Check>

View File

@@ -64,7 +64,7 @@ Isso gerará um projeto com a estrutura básica necessária para seu flow.
## Passo 2: Entendendo a Estrutura do Projeto
O projeto gerado possui a seguinte estrutura. Reserve um momento para conhecê-la, pois isso ajudará você a criar flows mais complexos no futuro.
O projeto gerado possui a seguinte estrutura. A crew inicial embutida usa o layout clássico Python/YAML. Para usar uma crew JSON-first dentro de um Flow, crie `crew.jsonc` e `agents/*.jsonc` na pasta da crew e carregue com `crewai.project.load_crew`, como mostrado em [Flows](/pt-BR/concepts/flows#building-your-crews).
```
guide_creator_flow/
@@ -72,21 +72,24 @@ guide_creator_flow/
├── pyproject.toml
├── README.md
├── .env
── main.py
├── crews/
── poem_crew/
├── config/
├── agents.yaml
│ └── tasks.yaml
── poem_crew.py
└── tools/
└── custom_tool.py
── src/
└── guide_creator_flow/
── __init__.py
├── main.py
├── crews/
│ └── poem_crew/
── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ └── poem_crew.py
└── tools/
└── custom_tool.py
```
Esta estrutura oferece uma separação clara entre os diferentes componentes do seu flow:
- A lógica principal do flow no arquivo `main.py`
- Crews especializados no diretório `crews`
- Ferramentas customizadas no diretório `tools`
- A lógica principal do flow no arquivo `src/guide_creator_flow/main.py`
- Crews especializados no diretório `src/guide_creator_flow/crews`
- Ferramentas customizadas no diretório `src/guide_creator_flow/tools`
Vamos modificar esta estrutura para criar nosso flow de criação de guias, que irá orquestrar o processo de geração de guias de aprendizagem abrangentes.
@@ -102,156 +105,82 @@ Este comando cria automaticamente os diretórios e arquivos de template necessá
## Passo 4: Configure o Crew de Redator de Conteúdo
Agora, vamos modificar os arquivos gerados para o crew de redatores. Vamos configurar dois agentes especializados um escritor e um revisor que irão colaborar para criar um conteúdo de alta qualidade para o nosso guia.
Agora, vamos configurar o crew de redatores com JSONC. Vamos definir dois agentes especializados - um escritor e um revisor - que colaboram para criar conteúdo de alta qualidade para o guia.
1. Primeiro, atualize o arquivo de configuração de agents para definir a equipe de criação de conteúdo:
1. Crie `src/guide_creator_flow/crews/content_crew/agents/content_writer.jsonc`:
Lembre-se de configurar o `llm` com o provedor que está utilizando.
```yaml
# src/guide_creator_flow/crews/content_crew/config/agents.yaml
content_writer:
role: >
Redator de Conteúdo Educacional
goal: >
Criar conteúdo envolvente e informativo que explique completamente o tema proposto
e forneça insights valiosos ao leitor
backstory: >
Você é um talentoso escritor educacional com experiência em criar conteúdo claro
e atraente. Você tem facilidade para explicar conceitos complexos em linguagem acessível
e organizar as informações de forma a ajudar o leitor a construir seu entendimento.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
content_reviewer:
role: >
Revisor(a) e Editor(a) de Conteúdo Educacional
goal: >
Garantir que o conteúdo seja preciso, abrangente, bem estruturado e mantenha
consistência com as seções previamente escritas
backstory: >
Você é um editor(a) meticuloso(a) com anos de experiência revisando conteúdo educacional.
Tem atenção aos detalhes, clareza e coesão. Você se destaca em aprimorar conteúdo
mantendo o estilo do autor original e garantindo qualidade consistente em várias seções.
llm: provider/model-id # e.g. openai/gpt-4o, google/gemini-2.0-flash, anthropic/claude...
```jsonc
{
"role": "Educational Content Writer",
"goal": "Create engaging, informative content that thoroughly explains the assigned topic and provides valuable insights to the reader.",
"backstory": "You are a talented educational writer who explains complex concepts in accessible language and organizes information clearly.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
Essas definições de agents estabelecem papéis e perspectivas especializadas que irão moldar como nossos agentes de IA abordam a criação de conteúdo. Note como cada agent possui um propósito e expertise distintos.
2. Crie `src/guide_creator_flow/crews/content_crew/agents/content_reviewer.jsonc`:
2. Em seguida, atualize o arquivo de configuração de tarefas para definir as tarefas específicas de escrita e revisão:
```yaml
# src/guide_creator_flow/crews/content_crew/config/tasks.yaml
write_section_task:
description: >
Escreva uma seção abrangente sobre o tema: "{section_title}"
Descrição da seção: {section_description}
Público-alvo: {audience_level} aprendizes
Seu conteúdo deve:
1. Começar com uma breve introdução ao tema da seção
2. Explicar claramente todos os conceitos principais com exemplos
3. Incluir aplicações práticas ou exercícios onde apropriado
4. Terminar com um resumo dos pontos principais
5. Ter aproximadamente 500-800 palavras
Formate seu conteúdo em Markdown com títulos, listas e ênfase apropriados.
Seções previamente escritas:
{previous_sections}
Certifique-se de que seu conteúdo mantenha consistência com as seções já escritas
e amplie os conceitos que já foram explicados.
expected_output: >
Uma seção bem estruturada e abrangente em formato Markdown que explique
totalmente o tema e é apropriada para o público-alvo.
agent: content_writer
review_section_task:
description: >
Revise e melhore a seguinte seção sobre "{section_title}":
{draft_content}
Público-alvo: {audience_level} aprendizes
Seções previamente escritas:
{previous_sections}
Sua revisão deve:
1. Corrigir qualquer erro gramatical ou de ortografia
2. Melhorar clareza e legibilidade
3. Garantir que o conteúdo seja abrangente e preciso
4. Verificar a consistência com as seções já escritas
5. Aprimorar a estrutura e o fluxo
6. Adicionar qualquer informação-chave ausente
Forneça a versão aprimorada da seção em formato Markdown.
expected_output: >
Uma versão melhorada e refinada da seção, mantendo a estrutura original,
mas aprimorando clareza, precisão e consistência.
agent: content_reviewer
context:
- write_section_task
```jsonc
{
"role": "Educational Content Reviewer and Editor",
"goal": "Ensure content is accurate, comprehensive, well-structured, and consistent with previously written sections.",
"backstory": "You are a meticulous editor with an eye for detail, clarity, and coherence.",
"llm": "provider/model-id",
"settings": {
"verbose": true
}
}
```
Essas definições de tarefas fornecem instruções detalhadas para nossos agents, garantindo que eles produzam conteúdo que atenda aos padrões de qualidade. Observe como o parâmetro `context` na tarefa de revisão cria um fluxo onde o revisor tem acesso à produção do redator.
Substitua `provider/model-id` pelo modelo que você usa, como `openai/gpt-4o`, `gemini/gemini-2.0-flash-001` ou `anthropic/claude-sonnet-4-6`.
3. Agora, atualize o arquivo de implementação do crew para definir como nossos agents e tasks trabalham juntos:
3. Crie `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
```jsonc
{
"name": "Content Crew",
"agents": ["content_writer", "content_reviewer"],
"tasks": [
{
"name": "write_section_task",
"description": "Write a comprehensive section on the topic: \"{section_title}\".\n\nSection description: {section_description}\nTarget audience: {audience_level} level learners\n\nYour content should begin with a brief introduction, explain key concepts clearly with examples, include practical applications where appropriate, end with a summary, and be approximately 500-800 words.\n\nPreviously written sections:\n{previous_sections}",
"expected_output": "A well-structured, comprehensive section in Markdown format that thoroughly explains the topic and is appropriate for the target audience.",
"agent": "content_writer",
"markdown": true
},
{
"name": "review_section_task",
"description": "Review and improve this section on \"{section_title}\":\n\n{draft_content}\n\nTarget audience: {audience_level} level learners\nPreviously written sections:\n{previous_sections}\n\nFix errors, improve clarity, verify consistency, enhance structure, and add missing key information.",
"expected_output": "An improved, polished version of the section that maintains the original structure but enhances clarity, accuracy, and consistency.",
"agent": "content_reviewer",
"context": ["write_section_task"],
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
O campo `context` permite que o revisor use a saída do escritor.
4. Substitua `src/guide_creator_flow/crews/content_crew/content_crew.py` por um pequeno loader:
```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
from pathlib import Path
@CrewBase
class ContentCrew():
"""Crew de redação de conteúdo"""
from crewai.project import load_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:
"""Cria o crew de redação de conteúdo"""
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
Essa definição de crew estabelece o relacionamento entre nossos agents e tasks, definindo um processo sequencial onde o redator cria o rascunho e o revisor o aprimora. Embora este crew possa funcionar de forma independente, em nosso flow ele será orquestrado como parte de um sistema maior.
Esse loader transforma `crew.jsonc` em uma `Crew` em runtime. Embora essa crew possa funcionar de forma independente, no nosso flow ela será orquestrada como parte de um sistema maior.
## Passo 5: Crie o Flow
@@ -273,7 +202,7 @@ 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
from guide_creator_flow.crews.content_crew.content_crew import kickoff_content_crew
# Definir nossos modelos para dados estruturados
class Section(BaseModel):
@@ -378,7 +307,7 @@ class GuideCreatorFlow(Flow[GuideCreatorState]):
previous_sections_text = "No previous sections written yet."
# Executar a crew de conteúdo para esta seção
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
"section_description": section.description,
"audience_level": self.state.audience_level,
@@ -597,7 +526,7 @@ Isso fornece uma maneira segura e tipada de rastrear e transformar dados ao long
Flows podem integrar crews para tarefas colaborativas complexas:
```python
result = ContentCrew().crew().kickoff(inputs={
result = kickoff_content_crew(inputs={
"section_title": section.title,
# ...
})

View File

@@ -106,7 +106,7 @@ Se você ainda não instalou o `uv`, siga o **passo 1** para instalá-lo rapidam
# Criando um Projeto CrewAI
Recomendamos utilizar o template de scaffolding `YAML` para uma abordagem estruturada na definição dos agentes e tarefas. Veja como começar:
`crewai create crew` agora cria um projeto de crew JSON-first. Os agentes ficam em `agents/*.jsonc`, as tarefas e configurações da crew ficam em `crew.jsonc`, e `crewai run` carrega essa definição JSON diretamente.
<Steps>
<Step title="Gerar Scaffolding do Projeto">
@@ -120,39 +120,38 @@ Recomendamos utilizar o template de scaffolding `YAML` para uma abordagem estrut
```
my_project/
├── .gitignore
├── .env
├── agents/
│ └── researcher.jsonc
├── crew.jsonc
├── knowledge/
├── pyproject.toml
├── README.md
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
</Frame>
- Se você precisar do scaffold antigo em Python/YAML com `crew.py`, `config/agents.yaml` e `config/tasks.yaml`, execute:
```shell
crewai create crew <your_project_name> --classic
```
</Step>
<Step title="Personalize Seu Projeto">
- Seu projeto conterá estes arquivos essenciais:
| Arquivo | Finalidade |
| --- | --- |
| `agents.yaml` | Defina seus agentes de IA e seus papéis |
| `tasks.yaml` | Configure as tarefas e fluxos de trabalho dos agentes |
| `crew.jsonc` | Configure a crew, a ordem das tarefas, o processo e os inputs padrão |
| `agents/*.jsonc` | Defina o papel, objetivo, backstory, LLM, ferramentas e comportamento de cada agente |
| `.env` | Armazene chaves de API e variáveis de ambiente |
| `main.py` | Ponto de entrada e fluxo de execução do projeto |
| `crew.py` | Orquestração e coordenação do crew |
| `tools/` | Diretório para ferramentas customizadas dos agentes |
| `knowledge/` | Diretório para base de conhecimento |
| `tools/` | Arquivos Python opcionais para ferramentas `custom:<name>` |
| `knowledge/` | Arquivos opcionais de conhecimento para agentes |
| `skills/` | Arquivos opcionais de skills aplicadas à crew |
- Comece editando `agents.yaml` e `tasks.yaml` para definir o comportamento do seu crew.
- Comece editando `crew.jsonc` e os arquivos em `agents/` para definir o comportamento da crew.
- Use valores `{placeholder}` nos textos de agentes e tarefas e defina padrões em `crew.jsonc` dentro de `inputs`. Ao executar `crewai run`, a CLI pergunta por valores que estiverem faltando.
- Mantenha informações sensíveis como chaves de API no arquivo `.env`.
</Step>

View File

@@ -5,11 +5,15 @@ icon: "at"
mode: "wide"
---
Este guia explica como utilizar anotações para referenciar corretamente **agentes**, **tarefas** e outros componentes no arquivo `crew.py`.
Este guia explica como utilizar anotações para referenciar corretamente **agentes**, **tarefas** e outros componentes em um arquivo `crew.py` clássico.
<Note>
Novos projetos criados com `crewai create crew <name>` são JSON-first e usam `crew.jsonc` com `agents/*.jsonc`. Use este guia ao trabalhar em um projeto clássico criado com `crewai create crew <name> --classic`, ao migrar um projeto Python/YAML existente ou quando precisar de controle via decorators em Python.
</Note>
## Introdução
As anotações no framework CrewAI são utilizadas para decorar classes e métodos, fornecendo metadados e funcionalidades para diversos componentes do seu crew. Essas anotações auxiliam na organização e estruturação do seu código, tornando-o mais legível e fácil de manter.
As anotações no framework CrewAI são utilizadas para decorar classes e métodos, fornecendo metadados e funcionalidades para diversos componentes do seu crew. Em projetos clássicos Python/YAML, elas organizam o código que carrega `config/agents.yaml`, `config/tasks.yaml` e retorna o objeto `Crew`.
## Anotações Disponíveis
@@ -113,9 +117,9 @@ def crew(self) -> Crew:
A anotação `@crew` é usada para decorar o método que cria e retorna o objeto `Crew`. Este método reúne todos os componentes (agentes e tarefas) em um crew funcional.
## Configuração YAML
## Configuração YAML Clássica
As configurações dos agentes geralmente são armazenadas em um arquivo YAML. Veja um exemplo de como o arquivo `agents.yaml` pode ser estruturado para o agente researcher:
Em projetos clássicos, as configurações dos agentes geralmente são armazenadas em um arquivo YAML. Veja um exemplo de como o arquivo `agents.yaml` pode ser estruturado para o agente researcher:
```yaml
researcher:
@@ -146,6 +150,6 @@ Repare como os campos `llm` e `tools` no arquivo YAML correspondem aos métodos
- **Nomenclatura Consistente**: Utilize nomenclatura clara e consistente para seus métodos. Por exemplo, métodos de agentes podem ser nomeados de acordo com suas funções (ex: researcher, reporting_analyst).
- **Variáveis de Ambiente**: Utilize variáveis de ambiente para informações sensíveis como chaves de API.
- **Flexibilidade**: Estruture seu crew de forma flexível, permitindo fácil adição ou remoção de agentes e tarefas.
- **Correspondência YAML-Código**: Assegure que os nomes e estruturas nos arquivos YAML correspondam corretamente aos métodos decorados em seu código Python.
- **Correspondência YAML-Código**: Em projetos clássicos, assegure que os nomes e estruturas nos arquivos YAML correspondam corretamente aos métodos decorados em seu código Python.
Seguindo essas orientações e utilizando corretamente as anotações, você conseguirá criar crews bem estruturados e de fácil manutenção utilizando o framework CrewAI.
Seguindo essas orientações e utilizando corretamente as anotações, você conseguirá manter crews clássicos bem estruturados. Para novas crews, prefira a estrutura JSON-first em [Crews](/pt-BR/concepts/crews).

View File

@@ -39,84 +39,60 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
Isso cria um app Flow em `src/latest_ai_flow/`, incluindo um crew inicial em `crews/content_crew/` que você substituirá por um crew de pesquisa **com um único agente** nos próximos passos.
</Step>
<Step title="Configure um agente em `agents.yaml`">
Substitua o conteúdo de `src/latest_ai_flow/crews/content_crew/config/agents.yaml` por um único pesquisador. Variáveis como `{topic}` são preenchidas a partir de `crew.kickoff(inputs=...)`.
<Step title="Configure um agente em JSONC">
Crie `src/latest_ai_flow/crews/content_crew/agents/researcher.jsonc` (crie o diretório `agents/` se necessário). Variáveis como `{topic}` são preenchidas a partir de `crew.kickoff(inputs=...)`.
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
researcher:
role: >
Pesquisador(a) Sênior de Dados em {topic}
goal: >
Descobrir os desenvolvimentos mais recentes em {topic}
backstory: >
Você é um pesquisador experiente que descobre os últimos avanços em {topic}.
Encontra as informações mais relevantes e apresenta tudo com clareza.
```jsonc agents/researcher.jsonc
{
"role": "Pesquisador(a) Sênior de Dados em {topic}",
"goal": "Descobrir os desenvolvimentos mais recentes em {topic}",
"backstory": "Você é um pesquisador experiente que encontra as informações mais relevantes e apresenta tudo com clareza.",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true
}
}
```
</Step>
<Step title="Configure uma tarefa em `tasks.yaml`">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
research_task:
description: >
Faça uma pesquisa aprofundada sobre {topic}. Use busca na web para obter
informações atuais e confiáveis. O ano atual é 2026.
expected_output: >
Um relatório em markdown com seções claras: tendências principais, ferramentas
ou empresas relevantes e implicações. Entre 800 e 1200 palavras. Sem cercas de código em volta do documento inteiro.
agent: researcher
output_file: output/report.md
<Step title="Configure a crew em `crew.jsonc`">
Crie `src/latest_ai_flow/crews/content_crew/crew.jsonc`:
```jsonc crew.jsonc
{
"name": "Research Crew",
"agents": ["researcher"],
"tasks": [
{
"name": "research_task",
"description": "Faça uma pesquisa aprofundada sobre {topic}. Use busca na web para obter informações recentes e confiáveis.",
"expected_output": "Um relatório em markdown com seções claras: tendências principais, ferramentas ou empresas relevantes e implicações. Entre 800 e 1200 palavras. Sem cercas de código em volta do documento inteiro.",
"agent": "researcher",
"output_file": "output/report.md",
"markdown": true
}
],
"process": "sequential",
"verbose": true
}
```
</Step>
<Step title="Conecte a classe do crew (`content_crew.py`)">
Aponte o crew gerado para o YAML e anexe `SerperDevTool` ao pesquisador.
<Step title="Carregue a crew JSON (`content_crew.py`)">
Substitua o `content_crew.py` gerado por um pequeno loader que transforma `crew.jsonc` em uma `Crew`.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
from pathlib import Path
from crewai import Agent, Crew, Process, Task
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool
from crewai.project import load_crew
@CrewBase
class ResearchCrew:
"""Crew de pesquisa com um agente, usado dentro do Flow."""
agents: List[BaseAgent]
tasks: List[Task]
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
)
@task
def research_task(self) -> Task:
return Task(
config=self.tasks_config["research_task"], # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
def kickoff_content_crew(inputs: dict):
crew, default_inputs = load_crew(Path(__file__).with_name("crew.jsonc"))
return crew.kickoff(inputs={**default_inputs, **inputs})
```
</Step>
@@ -130,7 +106,7 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
from crewai.flow import Flow, listen, start
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
from latest_ai_flow.crews.content_crew.content_crew import kickoff_content_crew
class ResearchFlowState(BaseModel):
@@ -149,7 +125,7 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
result = kickoff_content_crew(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("Crew de pesquisa concluído.")
@@ -171,7 +147,7 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
```
<Tip>
Se o nome do pacote não for `latest_ai_flow`, ajuste o import de `ResearchCrew` para o caminho de módulo do seu projeto.
Se o nome do pacote não for `latest_ai_flow`, ajuste o import de `kickoff_content_crew` para o caminho de módulo do seu projeto.
</Tip>
</Step>
@@ -198,7 +174,7 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
<CodeGroup>
```markdown output/report.md
# Agentes de IA em 2026: panorama e tendências
# Agentes de IA: panorama e tendências recentes
## Resumo executivo
@@ -219,7 +195,7 @@ Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/i
## Como isso se encaixa
1. **Flow** — `LatestAiFlow` executa `prepare_topic`, depois `run_research`, depois `summarize`. O estado (`topic`, `report`) fica no Flow.
2. **Crew** — `ResearchCrew` executa uma tarefa com um agente: o pesquisador usa **Serper** na web e escreve o relatório.
2. **Crew** — `kickoff_content_crew` carrega `crew.jsonc` e executa uma tarefa com um agente: o pesquisador usa **Serper** na web e escreve o relatório.
3. **Artefato** — O `output_file` da tarefa grava o relatório em `output/report.md`.
Para ir além em Flows (roteamento, persistência, human-in-the-loop), veja [Construa seu primeiro Flow](/pt-BR/guides/flows/first-flow) e [Flows](/pt-BR/concepts/flows). Para crews sem Flow, veja [Crews](/pt-BR/concepts/crews). Para um único `Agent` com `kickoff()` sem tarefas, veja [Agents](/pt-BR/concepts/agents#direct-agent-interaction-with-kickoff).
@@ -230,7 +206,10 @@ Você tem um Flow ponta a ponta com um crew de agente e um relatório salvo —
### Consistência de nomes
As chaves do YAML (`researcher`, `research_task`) devem coincidir com os nomes dos métodos na classe `@CrewBase`. Veja [Crews](/pt-BR/concepts/crews) para o padrão completo com decoradores.
Os nomes em `crew.jsonc` devem coincidir com os arquivos e referências:
- `agents: ["researcher"]` carrega `agents/researcher.jsonc`
- `tasks[].agent: "researcher"` atribui a tarefa a esse agente
## Implantação

View File

@@ -20,7 +20,7 @@ Isso adiciona o pacote de skills ao fluxo do seu agente para aplicar convençõe
## O que seu agente ganha
- **Flows** — apps com estado, passos e kickoffs de crew no estilo CrewAI
- **Crews e agentes** — padrões YAML-first, papéis, tarefas e delegação
- **Crews e agentes** — padrões JSON-first (`crew.jsonc`, `agents/*.jsonc`), papéis, tarefas e delegação
- **Ferramentas e integrações** — conectar agentes a busca, APIs e ferramentas comuns
- **Layout de projeto** — alinhar com scaffolds da CLI e convenções do repositório
- **Padrões atualizados** — skills acompanham a documentação e as práticas recomendadas

View File

@@ -17,6 +17,7 @@ from crewai_cli.user_data import (
from crewai_cli.utils import (
build_env_with_all_tool_credentials,
enable_prompt_line_editing,
is_dmn_mode_enabled,
read_toml,
)
@@ -162,7 +163,13 @@ def create(
classic: bool = False,
) -> None:
"""Create a new crew, or flow."""
dmn_mode = is_dmn_mode_enabled()
if not type:
if dmn_mode:
raise click.UsageError(
"TYPE is required when CREWAI_DMN is set. "
"Use `crewai create crew <name>` or `crewai create flow <name>`."
)
from crewai_cli.tui_picker import pick
options = [
@@ -177,11 +184,15 @@ def create(
raise SystemExit(0)
click.echo()
if not name:
if dmn_mode:
raise click.UsageError("NAME is required when CREWAI_DMN is set.")
enable_prompt_line_editing()
name = click.prompt(
click.style(f" Name of your {type}", fg="cyan", bold=True),
prompt_suffix=click.style(" ", fg="bright_white"), # noqa: RUF001
)
if dmn_mode:
skip_provider = True
if type == "crew":
if classic:
from crewai_cli.create_crew import create_crew

View File

@@ -11,7 +11,12 @@ from crewai_cli.provider import (
select_model,
select_provider,
)
from crewai_cli.utils import copy_template, load_env_vars, write_env_file
from crewai_cli.utils import (
copy_template,
is_dmn_mode_enabled,
load_env_vars,
write_env_file,
)
def get_reserved_script_names() -> set[str]:
@@ -120,6 +125,8 @@ def create_folder_structure(
folder_path = Path(folder_name)
if folder_path.exists():
if is_dmn_mode_enabled():
raise click.ClickException(f"Folder {folder_name} already exists.")
if not click.confirm(
f"Folder {folder_name} already exists. Do you want to override it?"
):
@@ -201,6 +208,8 @@ def create_crew(
) -> None:
folder_path, folder_name, class_name = create_folder_structure(name, parent_folder)
env_vars = load_env_vars(folder_path)
if is_dmn_mode_enabled():
skip_provider = True
if not skip_provider:
if not provider:
provider_models = get_provider_data()

View File

@@ -14,7 +14,12 @@ from rich.text import Text
from crewai_cli.constants import ENV_VARS
from crewai_cli.tui_picker import pick_many, pick_one
from crewai_cli.utils import enable_prompt_line_editing, load_env_vars, write_env_file
from crewai_cli.utils import (
enable_prompt_line_editing,
is_dmn_mode_enabled,
load_env_vars,
write_env_file,
)
# ── Provider / model data ───────────────────────────────────────
@@ -641,6 +646,43 @@ def _wizard_agents_and_tasks(
return agents, tasks, crew_settings
def _default_agents_and_tasks(
default_llm: str | None = None,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, Any]]:
"""Return deterministic scaffold data for non-interactive project creation."""
llm = default_llm or "openai/gpt-4o"
agents = [
{
"name": "researcher",
"role": "Senior Researcher",
"goal": "Research the requested topic and identify useful findings.",
"backstory": (
"You are an experienced researcher who finds relevant information "
"and presents it clearly."
),
"llm": llm,
"tools": [],
"planning": False,
"allow_delegation": False,
}
]
tasks = [
{
"name": "research_task",
"description": "Research current AI trends and write a concise summary.",
"expected_output": "A concise markdown report with key findings.",
"agent": "researcher",
"context": [],
}
]
crew_settings = {
"process": "sequential",
"memory": False,
"inputs": {},
}
return agents, tasks, crew_settings
# ── JSONC generation from wizard data ──────────────────────────
@@ -1029,7 +1071,9 @@ def create_json_crew(
import keyword
import shutil
enable_prompt_line_editing()
dmn_mode = is_dmn_mode_enabled()
if not dmn_mode:
enable_prompt_line_editing()
name = name.rstrip("/")
if not name.strip():
@@ -1048,6 +1092,8 @@ def create_json_crew(
folder_path = Path(folder_name)
if folder_path.exists():
if dmn_mode:
raise click.ClickException(f"Folder {folder_name} already exists.")
if not click.confirm(f"Folder {folder_name} already exists. Override?"):
click.secho("Cancelled.", fg="yellow")
sys.exit(0)
@@ -1056,10 +1102,14 @@ def create_json_crew(
click.echo()
click.secho(f" Creating crew: {name}", fg="green", bold=True)
agents, tasks, crew_settings = _wizard_agents_and_tasks(
skip_provider=skip_provider,
default_llm=_default_model_for_provider(provider),
)
default_llm = _default_model_for_provider(provider)
if dmn_mode:
agents, tasks, crew_settings = _default_agents_and_tasks(default_llm)
else:
agents, tasks, crew_settings = _wizard_agents_and_tasks(
skip_provider=skip_provider,
default_llm=default_llm,
)
# Create directories
folder_path.mkdir(parents=True)
@@ -1104,7 +1154,7 @@ def create_json_crew(
(folder_path / "skills" / ".gitkeep").write_text("", encoding="utf-8")
# Setup .env with API keys
if not skip_provider:
if not skip_provider and not dmn_mode:
models = list({a["llm"] for a in agents})
for model in models:
_setup_env(folder_path, model)

View File

@@ -17,6 +17,7 @@ from packaging import version
from crewai_cli.utils import (
build_env_with_all_tool_credentials,
enable_prompt_line_editing,
is_dmn_mode_enabled,
read_toml,
)
from crewai_cli.version import get_crewai_version
@@ -202,6 +203,35 @@ def _prepare_json_crew_for_tui(crew: Any) -> None:
agent.llm.stream = True
def _runtime_inputs_without_prompt(
crew: Any, default_inputs: dict[str, Any]
) -> dict[str, Any]:
"""Return runtime inputs in non-interactive mode or exit on missing values."""
inputs = dict(default_inputs or {})
missing = _missing_input_names(crew, inputs)
if missing:
missing_list = ", ".join(missing)
click.echo(
"Missing runtime inputs for CREWAI_DMN mode: "
f"{missing_list}. Add them to the `inputs` object in crew.json(c).",
err=True,
)
raise SystemExit(1)
return inputs
def _run_json_crew_without_tui(crew_path: Path) -> Any:
"""Run a JSON-defined crew with plain terminal output."""
with _json_loading_status("Preparing crew..."):
crew, default_inputs = _load_json_crew(crew_path)
runtime_inputs = _runtime_inputs_without_prompt(crew, default_inputs)
result = crew.kickoff(inputs=runtime_inputs)
if result is not None:
click.echo(str(result))
return result
def _run_json_crew(trained_agents_file: str | None = None) -> Any:
"""Load and run a JSON-defined crew."""
from dotenv import load_dotenv
@@ -219,6 +249,9 @@ def _run_json_crew(trained_agents_file: str | None = None) -> Any:
if crew_path is None:
raise FileNotFoundError("No crew.jsonc or crew.json found")
if is_dmn_mode_enabled():
return _run_json_crew_without_tui(crew_path)
crew_run_app_cls, crew, default_inputs, task_names, agent_names = (
_load_json_crew_for_tui(crew_path)
)

View File

@@ -29,6 +29,7 @@ __all__ = [
"get_project_description",
"get_project_name",
"get_project_version",
"is_dmn_mode_enabled",
"load_env_vars",
"parse_toml",
"read_toml",
@@ -41,6 +42,14 @@ __all__ = [
console = Console()
def is_dmn_mode_enabled() -> bool:
"""Return True when the enterprise non-interactive mode is enabled."""
value = os.environ.get("CREWAI_DMN")
if value is None:
return False
return value.strip().lower() not in {"", "0", "false", "no", "off"}
def enable_prompt_line_editing() -> None:
"""Enable cursor movement/history editing for Click text prompts when available."""
try:

View File

@@ -4,6 +4,7 @@ from unittest import mock
import pytest
from click.testing import CliRunner
from crewai_cli.cli import (
create,
deploy_create,
deploy_list,
deploy_logs,
@@ -157,6 +158,28 @@ def test_run_rejects_inputs_without_definition(run_flow_definition, run_crew, ru
run_crew.assert_not_called()
@mock.patch("crewai_cli.create_json_crew.create_json_crew")
def test_create_crew_in_dmn_mode_skips_provider_prompts(create_json_crew, runner):
result = runner.invoke(create, ["crew", "DMN Crew"], env={"CREWAI_DMN": "True"})
assert result.exit_code == 0
create_json_crew.assert_called_once_with("DMN Crew", None, True)
def test_create_requires_type_in_dmn_mode(runner):
result = runner.invoke(create, env={"CREWAI_DMN": "True"})
assert result.exit_code == 2
assert "TYPE is required when CREWAI_DMN is set" in result.output
def test_create_requires_name_in_dmn_mode(runner):
result = runner.invoke(create, ["flow"], env={"CREWAI_DMN": "True"})
assert result.exit_code == 2
assert "NAME is required when CREWAI_DMN is set" in result.output
@mock.patch("crewai_cli.cli.AuthenticationCommand")
def test_login(command, runner):
mock_auth = command.return_value

View File

@@ -812,3 +812,34 @@ def test_json_wizard_task_reprompts_on_cancelled_agent_pick(monkeypatch):
assert len(pick_calls) == 2
assert task["agent"] == "second_agent"
def test_json_create_dmn_mode_uses_non_interactive_defaults(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("CREWAI_DMN", "True")
monkeypatch.setattr(
json_crew,
"_wizard_agents_and_tasks",
lambda **_: pytest.fail("DMN mode must not run the wizard"),
)
monkeypatch.setattr(
json_crew,
"_setup_env",
lambda *_args, **_kwargs: pytest.fail("DMN mode must not prompt for env vars"),
)
json_crew.create_json_crew("DMN Crew", provider="anthropic", skip_provider=False)
project_root = tmp_path / "dmn_crew"
assert (project_root / "crew.jsonc").exists()
assert (project_root / "agents" / "researcher.jsonc").exists()
assert not (project_root / ".env").exists()
crew_template = (project_root / "crew.jsonc").read_text()
agent_template = (project_root / "agents" / "researcher.jsonc").read_text()
assert '"memory": false' in crew_template
assert '"description": "Research current AI trends and write a concise summary."' in (
crew_template
)
assert '"llm": "anthropic/claude-opus-4-6"' in agent_template

View File

@@ -402,6 +402,7 @@ def test_missing_input_names_accepts_hyphenated_placeholders():
def _patch_tui_run(monkeypatch, status: str):
"""Stub the TUI pieces of _run_json_crew so only exit handling runs."""
monkeypatch.delenv("CREWAI_DMN", raising=False)
class FakeApp:
def __init__(self, **kwargs):
@@ -446,6 +447,84 @@ def test_run_json_crew_completed_status_returns_result(monkeypatch, tmp_path: Pa
assert run_crew_module._run_json_crew() == "result"
def test_run_json_crew_dmn_mode_bypasses_tui(monkeypatch, tmp_path: Path, capsys):
from types import SimpleNamespace
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("CREWAI_DMN", "True")
crew_path = tmp_path / "crew.jsonc"
crew_path.write_text("{}")
kickoff_calls = []
class FakeCrew:
name = "Demo"
agents = [SimpleNamespace(role="Researcher", goal="Research", backstory="")]
tasks = [
SimpleNamespace(
description="Research",
expected_output="Findings",
output_file=None,
)
]
def kickoff(self, inputs):
kickoff_calls.append(inputs)
return "plain result"
monkeypatch.setattr(run_crew_module, "find_crew_json_file", lambda: crew_path)
monkeypatch.setattr(
run_crew_module,
"_load_json_crew",
lambda _path: (FakeCrew(), {"topic": "AI"}),
)
monkeypatch.setattr(
run_crew_module,
"_load_json_crew_for_tui",
lambda _path: pytest.fail("DMN mode must not start the TUI loader"),
)
assert run_crew_module._run_json_crew() == "plain result"
captured = capsys.readouterr()
assert kickoff_calls == [{"topic": "AI"}]
assert "plain result" in captured.out
def test_run_json_crew_dmn_mode_exits_on_missing_inputs(
monkeypatch, tmp_path: Path, capsys
):
from types import SimpleNamespace
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("CREWAI_DMN", "True")
crew_path = tmp_path / "crew.jsonc"
crew_path.write_text("{}")
crew = SimpleNamespace(
agents=[
SimpleNamespace(
role="Researcher",
goal="Research {topic}",
backstory="",
)
],
tasks=[],
)
monkeypatch.setattr(run_crew_module, "find_crew_json_file", lambda: crew_path)
monkeypatch.setattr(
run_crew_module,
"_load_json_crew",
lambda _path: (crew, {}),
)
with pytest.raises(SystemExit) as exc_info:
run_crew_module._run_json_crew()
captured = capsys.readouterr()
assert exc_info.value.code == 1
assert "Missing runtime inputs for CREWAI_DMN mode: topic" in captured.err
def test_has_json_crew_defers_to_declared_flow_type(monkeypatch, tmp_path: Path):
"""A flow project containing a stray crew.jsonc must still run as a flow."""
monkeypatch.chdir(tmp_path)

View File

@@ -102,6 +102,23 @@ def test_tree_copy_to_existing_directory(temp_tree):
shutil.rmtree(dest_dir)
@pytest.mark.parametrize("value", ["1", "true", "True", "yes", "anything"])
def test_is_dmn_mode_enabled_for_truthy_values(monkeypatch, value):
monkeypatch.setenv("CREWAI_DMN", value)
assert utils.is_dmn_mode_enabled() is True
@pytest.mark.parametrize("value", [None, "", "0", "false", "no", "off"])
def test_is_dmn_mode_enabled_for_falsey_values(monkeypatch, value):
if value is None:
monkeypatch.delenv("CREWAI_DMN", raising=False)
else:
monkeypatch.setenv("CREWAI_DMN", value)
assert utils.is_dmn_mode_enabled() is False
# Tests for extract_available_exports, get_crews, get_flows, fetch_crews,
# is_valid_tool live in lib/crewai/tests/cli/test_utils.py — the canonical
# implementations are in crewai.utilities.project_utils.

View File

@@ -1,23 +0,0 @@
import logging
from typing import Literal
from crewai.tools import BaseTool
from pydantic import BaseModel, Field
logger = logging.getLogger("lead_flow")
class LogLeadInput(BaseModel):
message: str = Field(description="The message to log.")
level: Literal["debug", "info", "warning", "error"] = "info"
class LogLeadTool(BaseTool):
name: str = "log_lead"
description: str = "Log a message about a lead that was not pursued."
args_schema: type[BaseModel] = LogLeadInput
def _run(self, message: str, level: str = "info") -> str:
logger.log(logging.getLevelName(level.upper()), message)
return message

View File

@@ -1,98 +0,0 @@
# uv run --project lib/crewai crewai run --definition lib/crewai/examples/flows/lead_scoring_flow.yaml --inputs '{"lead":{"name":"Dana Lee","company":"Acme","employees":1200}}'
# uv run --project lib/crewai crewai run --definition lib/crewai/examples/flows/lead_scoring_flow.yaml --inputs '{"lead":{"name":"Sam Poe","company":"Tiny LLC","employees":3}}'
schema: crewai.flow/v1
name: LeadScoringFlow
description: Score an inbound lead, then route high-scoring leads to outreach and the rest to a log tool.
state:
type: dict
default:
lead: {}
methods:
score_lead:
start: true
do:
call: crew
with:
name: lead_scoring_crew
verbose: true
agents:
scorer:
role: Lead Qualification Analyst
goal: Assign a 0-100 fit score to inbound lead {name} from {company}
backstory: >
A revenue-ops veteran who scores leads against a clear ideal
customer profile: company size is the dominant signal.
tasks:
- name: score_lead_task
agent: scorer
description: >
Evaluate the inbound lead {name} from {company} ({employees}
employees) against this rubric, where company size dominates:
1000+ employees scores 85-100 (hot), 200-999 scores 70-84 (warm),
and under 200 scores 0-69 (cold). Return an integer score with a
one-line rationale.
expected_output: >
A LeadScore with an integer `score` (0-100), a short `reasoning`,
and a `tier` of "hot", "warm", or "cold".
output_pydantic:
type: object
properties:
score:
type: integer
reasoning:
type: string
tier:
type: string
enum: [hot, warm, cold]
required: [score, reasoning, tier]
inputs:
name: "${state.lead.name}"
company: "${state.lead.company}"
employees: "${state.lead.employees}"
route_by_score:
listen: score_lead
router: true
emit: [qualified, unqualified]
do:
call: expression
expr: "outputs.score_lead.pydantic.score >= 80 ? 'qualified' : 'unqualified'"
run_outreach:
listen: qualified
do:
call: crew
with:
name: outreach_crew
verbose: true
agents:
sdr:
role: Outbound SDR
goal: Draft a tailored first-touch email to {name} at {company}
backstory: >
A top-performing SDR who writes concise, personalized outreach
that earns replies from busy buyers.
tasks:
- name: draft_outreach_task
agent: sdr
description: >
Write a short, personalized first-touch email to {name} at
{company}. Ground the hook in this qualification rationale:
"{reasoning}".
expected_output: A ready-to-send outreach email with a subject line and body.
inputs:
name: "${state.lead.name}"
company: "${state.lead.company}"
reasoning: "${outputs.score_lead.pydantic.reasoning}"
log_unqualified:
listen: unqualified
do:
call: tool
ref: lead_flow.tools:LogLeadTool
with:
message: "${'Skipped low-fit lead ' + state.lead.name + ' (score ' + string(outputs.score_lead.pydantic.score) + ')'}"
level: info

View File

@@ -15,10 +15,13 @@ from crewai.flow.flow_definition import (
FlowConversationalRouterDefinition,
FlowDefinition,
FlowDefinitionDiagnostic,
FlowDictStateDefinition,
FlowHumanFeedbackDefinition,
FlowMethodDefinition,
FlowPersistenceDefinition,
FlowPydanticStateDefinition,
FlowStateDefinition,
FlowUnknownStateDefinition,
_object_ref,
)
from crewai.flow.flow_wrappers import (
@@ -185,12 +188,11 @@ def _build_state_definition(
default = None
if isinstance(state_value, dict):
default = _serialize_static_value(state_value, diagnostics, "state.default")
return FlowStateDefinition(type="dict", default=default)
return FlowDictStateDefinition(default=default)
if isinstance(state_value, type) and issubclass(state_value, PydanticBaseModel):
return FlowStateDefinition(type="pydantic", ref=_state_ref(state_value))
return FlowPydanticStateDefinition(ref=_state_ref(state_value))
if isinstance(state_value, PydanticBaseModel):
return FlowStateDefinition(
type="pydantic",
return FlowPydanticStateDefinition(
ref=_state_ref(state_value),
default=_serialize_static_value(state_value, diagnostics, "state.default"),
)
@@ -201,7 +203,7 @@ def _build_state_definition(
message=f"could not serialize state type {_object_ref(state_value)}",
)
)
return FlowStateDefinition(type="unknown", ref=_state_ref(state_value))
return FlowUnknownStateDefinition(ref=_state_ref(state_value))
def _build_config_definition(

View File

@@ -12,7 +12,7 @@ from __future__ import annotations
import json
import logging
import re
from typing import Any, Literal as TypingLiteral
from typing import Annotated, Any, Literal, TypeAlias
from pydantic import (
BaseModel,
@@ -28,6 +28,7 @@ from crewai.flow.conversational_definition import (
FlowConversationalDefinition,
FlowConversationalRouterDefinition,
)
from crewai.project.crew_definition import CrewDefinition
logger = logging.getLogger(__name__)
@@ -41,17 +42,23 @@ __all__ = [
"FlowConfigDefinition",
"FlowConversationalDefinition",
"FlowConversationalRouterDefinition",
"FlowCrewActionDefinition",
"FlowDefinition",
"FlowDefinitionCondition",
"FlowDefinitionDiagnostic",
"FlowDictStateDefinition",
"FlowEachActionDefinition",
"FlowEachInnerActionDefinition",
"FlowExpressionActionDefinition",
"FlowHumanFeedbackDefinition",
"FlowJsonSchemaStateDefinition",
"FlowMethodDefinition",
"FlowPersistenceDefinition",
"FlowPydanticStateDefinition",
"FlowScriptActionDefinition",
"FlowStateDefinition",
"FlowToolActionDefinition",
"FlowUnknownStateDefinition",
]
@@ -66,32 +73,179 @@ def _object_ref(value: Any) -> str:
class FlowDefinitionDiagnostic(BaseModel):
"""A non-fatal Flow Definition build or validation diagnostic."""
code: str
message: str
severity: TypingLiteral["warning", "error"] = "warning"
path: str | None = None
code: str = Field(
description="Stable diagnostic identifier for tooling and tests.",
examples=["router_without_trigger"],
)
message: str = Field(
description="Human-readable explanation of the diagnostic.",
examples=["router: true requires either start or listen"],
)
severity: Literal["warning", "error"] = Field(
default="warning",
description="Diagnostic severity. Errors indicate an invalid or incomplete contract.",
examples=["error"],
)
path: str | None = Field(
default=None,
description="Dot path to the definition field that produced the diagnostic.",
examples=["methods.decide"],
)
class FlowStateDefinition(BaseModel):
"""Static description of a Flow state contract."""
class FlowDictStateDefinition(BaseModel):
"""Static description of a plain dictionary Flow state contract."""
type: TypingLiteral["dict", "pydantic", "json_schema", "unknown"] = "dict"
ref: str | None = None
json_schema: dict[str, Any] | None = None
default: dict[str, Any] | None = None
model_config = ConfigDict(extra="forbid")
type: Literal["dict"] = Field(
default="dict",
description="Plain dictionary state with optional default values.",
examples=["dict"],
)
default: dict[str, Any] | None = Field(
default=None,
description="Default state values applied before kickoff inputs.",
examples=[{"topic": "AI agents", "limit": 3}],
)
class FlowPydanticStateDefinition(BaseModel):
"""Static description of an importable Pydantic Flow state contract."""
model_config = ConfigDict(extra="forbid")
type: Literal["pydantic"] = Field(
default="pydantic",
description="Importable Pydantic model used as the Flow state type.",
examples=["pydantic"],
)
ref: str | None = Field(
default=None,
description="Import reference for the state model, formatted as module:qualname.",
examples=["my_project.flows:ResearchState"],
)
json_schema: dict[str, Any] | None = Field(
default=None,
description=(
"Fallback JSON Schema used when the Pydantic state ref is unavailable."
),
examples=[
{
"type": "object",
"properties": {"topic": {"type": "string"}},
"required": ["topic"],
}
],
)
default: dict[str, Any] | None = Field(
default=None,
description="Default state values applied before kickoff inputs.",
examples=[{"topic": "AI agents", "limit": 3}],
)
class FlowJsonSchemaStateDefinition(BaseModel):
"""Static description of an inline JSON Schema Flow state contract."""
model_config = ConfigDict(extra="forbid")
type: Literal["json_schema"] = Field(
default="json_schema",
description="Inline JSON Schema used as the Flow state contract.",
examples=["json_schema"],
)
json_schema: dict[str, Any] = Field(
description="JSON Schema used to validate and document flow state.",
examples=[
{
"type": "object",
"properties": {"topic": {"type": "string"}},
"required": ["topic"],
}
],
)
default: dict[str, Any] | None = Field(
default=None,
description="Default state values applied before kickoff inputs.",
examples=[{"topic": "AI agents", "limit": 3}],
)
class FlowUnknownStateDefinition(BaseModel):
"""Static description of a state contract that could not be serialized."""
model_config = ConfigDict(extra="forbid")
type: Literal["unknown"] = Field(
default="unknown",
description="Unknown state representation; runtime falls back to dictionary state.",
examples=["unknown"],
)
ref: str | None = Field(
default=None,
description="Best-effort import reference for the unknown state type.",
examples=["my_project.flows:CustomState"],
)
default: dict[str, Any] | None = Field(
default=None,
description="Default state values applied before kickoff inputs.",
examples=[{"topic": "AI agents", "limit": 3}],
)
FlowStateDefinition: TypeAlias = Annotated[
FlowDictStateDefinition
| FlowPydanticStateDefinition
| FlowJsonSchemaStateDefinition
| FlowUnknownStateDefinition,
Field(discriminator="type"),
]
class FlowConfigDefinition(BaseModel):
"""Serializable Flow-level configuration."""
tracing: bool | None = None
stream: bool = False
memory: dict[str, Any] | None = None
input_provider: str | None = None
suppress_flow_events: bool = False
max_method_calls: int = 100
defer_trace_finalization: bool = False
checkpoint: bool | dict[str, Any] | None = None
tracing: bool | None = Field(
default=None,
description="Override for flow tracing; when omitted, runtime defaults apply.",
examples=[True],
)
stream: bool = Field(
default=False,
description="Whether the flow should emit streaming events when supported.",
examples=[True],
)
memory: dict[str, Any] | None = Field(
default=None,
description="Serializable memory configuration passed to flow execution.",
examples=[{"enabled": True}],
)
input_provider: str | None = Field(
default=None,
description="Import reference or provider key used to supply flow inputs.",
examples=["my_project.inputs:load_inputs"],
)
suppress_flow_events: bool = Field(
default=False,
description="Disable flow event emission for this definition.",
examples=[False],
)
max_method_calls: int = Field(
default=100,
description="Maximum number of method executions allowed during one kickoff.",
examples=[20],
)
defer_trace_finalization: bool = Field(
default=False,
description="Defer trace finalization so callers can complete tracing later.",
examples=[False],
)
checkpoint: bool | dict[str, Any] | None = Field(
default=None,
description="Checkpointing configuration, or true to use default checkpointing.",
examples=[True, {"enabled": True}],
)
class FlowPersistenceDefinition(BaseModel):
@@ -103,9 +257,21 @@ class FlowPersistenceDefinition(BaseModel):
serialized config.
"""
enabled: bool = False
verbose: bool = False
persistence: Any = None
enabled: bool = Field(
default=False,
description="Whether persistence is enabled for this flow or method.",
examples=[True],
)
verbose: bool = Field(
default=False,
description="Whether persistence should emit verbose diagnostic output.",
examples=[False],
)
persistence: Any = Field(
default=None,
description="Persistence backend configuration or import reference.",
examples=[{"ref": "my_project.persistence:FlowStore"}],
)
@field_serializer("persistence", when_used="json")
def _serialize_persistence(self, value: Any) -> Any:
@@ -131,15 +297,53 @@ class FlowHumanFeedbackDefinition(BaseModel):
a serialized config (``llm``) or a ``module:qualname`` ref (``provider``).
"""
message: str
emit: list[str] | None = None
llm: Any = "gpt-4o-mini"
default_outcome: str | None = None
metadata: dict[str, Any] | None = None
provider: Any = None
learn: bool = False
learn_source: str = "hitl"
learn_strict: bool = False
message: str = Field(
description="Prompt shown to the human reviewer when feedback is requested.",
examples=["Review the research summary before publishing."],
)
emit: list[str] | None = Field(
default=None,
description=(
"Allowed feedback outcomes. When set, the method routes like a router "
"using the selected outcome."
),
examples=[["approved", "revise"]],
)
llm: Any = Field(
default="gpt-4o-mini",
description="LLM configuration used to assist or process human feedback.",
examples=["gpt-4o-mini"],
)
default_outcome: str | None = Field(
default=None,
description="Outcome to use when feedback cannot be collected.",
examples=["revise"],
)
metadata: dict[str, Any] | None = Field(
default=None,
description="Serializable metadata attached to the feedback request.",
examples=[{"team": "research"}],
)
provider: Any = Field(
default=None,
description="Feedback provider configuration or import reference.",
examples=["my_project.feedback:provider"],
)
learn: bool = Field(
default=False,
description="Whether feedback should be recorded for later learning workflows.",
examples=[True],
)
learn_source: str = Field(
default="hitl",
description="Source label attached to learned feedback records.",
examples=["hitl"],
)
learn_strict: bool = Field(
default=False,
description="Whether learning should enforce strict validation of feedback data.",
examples=[False],
)
@field_serializer("llm", when_used="json")
def _serialize_llm(self, value: Any) -> dict[str, Any] | str | None:
@@ -159,21 +363,89 @@ class FlowHumanFeedbackDefinition(BaseModel):
class FlowCodeActionDefinition(BaseModel):
"""A Flow method action that executes importable Python code."""
model_config = ConfigDict(populate_by_name=True, extra="forbid")
model_config = ConfigDict(
populate_by_name=True,
extra="forbid",
)
call: TypingLiteral["code"] = "code"
ref: str
with_: dict[str, Any] | None = Field(default=None, alias="with")
call: Literal["code"] = Field(
default="code",
description="Action discriminator. Use code to call importable Python.",
examples=["code"],
)
ref: str = Field(
description="Import reference for the callable, formatted as module:qualname.",
examples=["my_project.flows:normalize_topic"],
)
with_: dict[str, Any] | None = Field(
default=None,
alias="with",
description="Keyword arguments passed to the callable after expression rendering.",
examples=[{"topic": "${state.topic}"}],
)
class FlowToolActionDefinition(BaseModel):
"""A Flow method action that invokes a CrewAI tool."""
model_config = ConfigDict(populate_by_name=True, extra="forbid")
model_config = ConfigDict(
populate_by_name=True,
extra="forbid",
)
call: TypingLiteral["tool"]
ref: str
with_: dict[str, Any] | None = Field(default=None, alias="with")
call: Literal["tool"] = Field(
description="Action discriminator. Use tool to instantiate and run a CrewAI tool.",
examples=["tool"],
)
ref: str = Field(
description="Import reference for a BaseTool class, formatted as module:qualname.",
examples=["my_project.tools:SearchTool"],
)
with_: dict[str, Any] | None = Field(
default=None,
alias="with",
description="Tool input arguments after expression rendering.",
examples=[{"query": "${outputs.normalize_topic}", "limit": 5}],
)
class FlowCrewActionDefinition(BaseModel):
"""A Flow method action that builds and kicks off a CrewAI crew."""
model_config = ConfigDict(
populate_by_name=True,
extra="forbid",
)
call: Literal["crew"] = Field(
description="Action discriminator. Use crew to run an inline Crew definition.",
examples=["crew"],
)
with_: CrewDefinition = Field(
alias="with",
description="Inline Crew definition to load and execute for this action.",
examples=[
{
"name": "inline_research",
"agents": {
"researcher": {
"role": "Researcher",
"goal": "Research {topic}",
"backstory": "Knows the domain.",
}
},
"tasks": [
{
"name": "research_task",
"description": "Research {topic}",
"expected_output": "Findings about {topic}",
"agent": "researcher",
}
],
"inputs": {"topic": "${state.topic}"},
}
],
)
class FlowExpressionActionDefinition(BaseModel):
@@ -181,18 +453,60 @@ class FlowExpressionActionDefinition(BaseModel):
model_config = ConfigDict(extra="forbid")
call: TypingLiteral["expression"]
expr: str
call: Literal["expression"] = Field(
description="Action discriminator. Use expression to evaluate a CEL expression.",
examples=["expression"],
)
expr: str = Field(
description="CEL expression evaluated against state, outputs, and local context.",
examples=["state.topic", "outputs.normalize_topic"],
)
class FlowScriptActionDefinition(BaseModel):
"""A Flow method action that executes trusted inline Python."""
model_config = ConfigDict(extra="forbid")
call: Literal["script"] = Field(
description="Action discriminator. Use script to execute trusted inline Python.",
examples=["script"],
)
code: str = Field(
description=(
"Trusted Python source executed as a generated function. Runtime values are "
"passed as state, outputs, input, and item; they are not interpolated into "
"the source. This is not sandboxed."
),
examples=[
"state['normalized_topic'] = input.strip()\n"
"return state['normalized_topic']"
],
)
language: Literal["python"] = Field(
default="python",
description="Script language. Only python is currently supported.",
examples=["python"],
)
FlowInnerActionDefinition = (
FlowCodeActionDefinition | FlowToolActionDefinition | FlowExpressionActionDefinition
FlowCodeActionDefinition
| FlowToolActionDefinition
| FlowCrewActionDefinition
| FlowExpressionActionDefinition
| FlowScriptActionDefinition
)
class FlowEachInnerActionDefinition(RootModel[dict[str, FlowInnerActionDefinition]]):
"""One named action inside an ``each`` composite action."""
root: dict[str, FlowInnerActionDefinition] = Field(
description="Single-entry mapping from an inner action name to its action.",
examples=[{"clean": {"call": "script", "code": "return item.strip()"}}],
)
@model_validator(mode="after")
def _validate_action_mapping(self) -> FlowEachInnerActionDefinition:
if len(self.root) != 1:
@@ -212,11 +526,35 @@ class FlowEachInnerActionDefinition(RootModel[dict[str, FlowInnerActionDefinitio
class FlowEachActionDefinition(BaseModel):
"""A composite action that runs a sequential mini-pipeline for each item."""
model_config = ConfigDict(populate_by_name=True, extra="forbid")
model_config = ConfigDict(
populate_by_name=True,
extra="forbid",
)
call: TypingLiteral["each"]
in_: str = Field(alias="in")
do: list[FlowEachInnerActionDefinition]
call: Literal["each"] = Field(
description=(
"Action discriminator. Use each to run a sequence of actions for every "
"item in an input list."
),
examples=["each"],
)
in_: str = Field(
alias="in",
description="CEL expression that must evaluate to the list to iterate.",
examples=["state.rows"],
)
do: list[FlowEachInnerActionDefinition] = Field(
description=(
"Ordered inner actions to run for each item. Each entry must be a "
"single-key mapping naming that inner action."
),
examples=[
[
{"clean": {"call": "script", "code": "return item.strip()"}},
{"tag": {"call": "expression", "expr": "outputs.clean"}},
]
],
)
@model_validator(mode="after")
def _validate_inner_action_list(self) -> FlowEachActionDefinition:
@@ -236,7 +574,9 @@ class FlowEachActionDefinition(BaseModel):
FlowActionDefinition = (
FlowCodeActionDefinition
| FlowToolActionDefinition
| FlowCrewActionDefinition
| FlowExpressionActionDefinition
| FlowScriptActionDefinition
| FlowEachActionDefinition
)
@@ -244,14 +584,48 @@ FlowActionDefinition = (
class FlowMethodDefinition(BaseModel):
"""Static definition of one Flow method and its execution roles."""
description: str | None = None
do: FlowActionDefinition
start: bool | FlowDefinitionCondition | None = None
listen: FlowDefinitionCondition | None = None
router: bool = False
emit: list[str] | None = None
human_feedback: FlowHumanFeedbackDefinition | None = None
persist: FlowPersistenceDefinition | None = None
description: str | None = Field(
default=None,
description="Human-readable summary of what this method does.",
examples=["Normalize the incoming topic."],
)
do: FlowActionDefinition = Field(
description="Action executed when this method runs.",
examples=[{"call": "script", "code": "return input.strip()"}],
)
start: bool | FlowDefinitionCondition | None = Field(
default=None,
description=(
"Marks a start method. True starts unconditionally; a condition starts "
"when the kickoff inputs or events satisfy it."
),
examples=[True],
)
listen: FlowDefinitionCondition | None = Field(
default=None,
description="Trigger condition that runs this method after upstream events.",
examples=["seed", {"or": ["approved", "revise"]}],
)
router: bool = Field(
default=False,
description="Whether the method output should be treated as the next event name.",
examples=[True],
)
emit: list[str] | None = Field(
default=None,
description="Declared router events this method may emit.",
examples=[["approved", "revise"]],
)
human_feedback: FlowHumanFeedbackDefinition | None = Field(
default=None,
description="Optional human feedback step applied after the method action.",
examples=[{"message": "Review the research summary before publishing."}],
)
persist: FlowPersistenceDefinition | None = Field(
default=None,
description="Method-level persistence override.",
examples=[{"enabled": True}],
)
@model_validator(mode="after")
def _canonicalize_human_feedback_routing(self) -> FlowMethodDefinition:
@@ -277,19 +651,71 @@ class FlowMethodDefinition(BaseModel):
class FlowDefinition(BaseModel):
"""Static, serializable definition of a Flow."""
model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
schema_: TypingLiteral["crewai.flow/v1"] = Field(
default="crewai.flow/v1", alias="schema"
model_config = ConfigDict(
populate_by_name=True,
arbitrary_types_allowed=True,
)
schema_: Literal["crewai.flow/v1"] = Field(
default="crewai.flow/v1",
alias="schema",
description="Flow Definition schema identifier and version.",
examples=["crewai.flow/v1"],
)
name: str = Field(
description="Unique flow name used in logs, events, and traces.",
examples=["ResearchFlow"],
)
description: str | None = Field(
default=None,
description="Human-readable summary of the flow.",
examples=["Normalize a topic and prepare it for research."],
)
state: FlowStateDefinition | None = Field(
default=None,
description="State contract for kickoff inputs and runtime state.",
examples=[{"type": "dict", "default": {"topic": "AI agents"}}],
)
config: FlowConfigDefinition = Field(
default_factory=FlowConfigDefinition,
description="Serializable flow-level runtime configuration.",
examples=[{"stream": True, "max_method_calls": 20}],
)
persist: FlowPersistenceDefinition | None = Field(
default=None,
description="Flow-level persistence configuration.",
examples=[{"enabled": True}],
)
conversational: FlowConversationalDefinition | None = Field(
default=None,
description="Conversational flow configuration, when the flow supports chat.",
)
methods: dict[str, FlowMethodDefinition] = Field(
default_factory=dict,
description="Mapping of method names to method definitions.",
examples=[
{
"seed": {
"start": True,
"do": {"call": "expression", "expr": "state.topic"},
}
}
],
)
diagnostics: list[FlowDefinitionDiagnostic] = Field(
default_factory=list,
description="Validation diagnostics attached to this definition.",
examples=[
[
{
"code": "router_without_trigger",
"message": "router: true requires either start or listen",
"severity": "error",
"path": "methods.decide",
}
]
],
)
name: str
description: str | None = None
state: FlowStateDefinition | None = None
config: FlowConfigDefinition = Field(default_factory=FlowConfigDefinition)
persist: FlowPersistenceDefinition | None = None
conversational: FlowConversationalDefinition | None = None
methods: dict[str, FlowMethodDefinition] = Field(default_factory=dict)
diagnostics: list[FlowDefinitionDiagnostic] = Field(default_factory=list)
@model_validator(mode="after")
def _validate_method_names(self) -> FlowDefinition:

View File

@@ -193,26 +193,24 @@ def _build_definition_state_model(
kwargs = dict(state_definition.default or {})
model_class: type[BaseModel] | None = None
if state_definition.ref:
state_ref = getattr(state_definition, "ref", None)
if state_ref:
try:
resolved: Any = resolve_ref(state_definition.ref, field="state")
resolved: Any = resolve_ref(state_ref, field="state")
except Exception:
logger.warning(
"Could not import state ref %r", state_definition.ref, exc_info=True
)
logger.warning("Could not import state ref %r", state_ref, exc_info=True)
else:
if isinstance(resolved, type) and issubclass(resolved, BaseModel):
model_class = resolved
else:
logger.warning(
"State ref %r is not a pydantic model", state_definition.ref
)
logger.warning("State ref %r is not a pydantic model", state_ref)
if model_class is None and state_definition.json_schema:
json_schema = getattr(state_definition, "json_schema", None)
if model_class is None and json_schema:
from crewai.utilities.pydantic_schema_utils import create_model_from_schema
try:
model_class = create_model_from_schema(state_definition.json_schema)
model_class = create_model_from_schema(json_schema)
except Exception:
logger.warning(
"Could not build a state model from the declared json_schema",

View File

@@ -6,17 +6,21 @@ import asyncio
from collections.abc import Callable
import contextvars
import inspect
import textwrap
from typing import TYPE_CHECKING, Any, Protocol, cast
from crewai.flow.flow_definition import (
FlowActionDefinition,
FlowCodeActionDefinition,
FlowCrewActionDefinition,
FlowEachActionDefinition,
FlowEachInnerActionDefinition,
FlowExpressionActionDefinition,
FlowScriptActionDefinition,
FlowToolActionDefinition,
)
from crewai.flow.runtime._expressions import evaluate_expression, render_with_block
from crewai.flow.runtime._outputs import outputs_by_name
from crewai.flow.runtime._refs import InvalidRefError, resolve_ref
@@ -104,6 +108,25 @@ class ToolAction:
) from e
class CrewAction:
definition_type = FlowCrewActionDefinition
def __init__(self, flow: Flow[Any], definition: FlowCrewActionDefinition) -> None:
self.flow = flow
self.definition = definition
async def run(self, *_args: Any, **kwargs: Any) -> Any:
from crewai.project.crew_loader import load_crew_from_definition
local_context = _pop_local_context(kwargs)
crew_definition = self.definition.with_
inputs = render_with_block(
self.flow, crew_definition.inputs, local_context=local_context
)
crew, _ = load_crew_from_definition(crew_definition, source="crew action")
return await crew.kickoff_async(inputs=inputs)
class ExpressionAction:
definition_type = FlowExpressionActionDefinition
@@ -120,6 +143,37 @@ class ExpressionAction:
)
class ScriptAction:
definition_type = FlowScriptActionDefinition
def __init__(self, flow: Flow[Any], definition: FlowScriptActionDefinition) -> None:
self.flow = flow
self.definition = definition
self.handler = self._compile_handler()
def run(self, *args: Any, **kwargs: Any) -> Any:
local_context = _pop_local_context(kwargs)
return self.handler(
state=self.flow.state,
outputs=outputs_by_name(
self.flow._method_outputs,
local_outputs=local_context.get("outputs") if local_context else None,
),
input=args[0] if args else None,
item=local_context.get("item") if local_context else None,
)
def _compile_handler(self) -> Callable[..., Any]:
namespace: dict[str, Any] = {
"__name__": f"crewai.flow.script.{self.flow._definition.name}",
}
source = _script_function_source(self.definition.code)
exec( # nosec B102 # noqa: S102
compile(source, namespace["__name__"], "exec"), namespace
)
return cast(Callable[..., Any], namespace["__flow_script__"])
class EachAction:
definition_type = FlowEachActionDefinition
@@ -177,7 +231,9 @@ _ACTION_TYPES: tuple[_ActionType, ...] = (
EachAction,
CodeAction,
ToolAction,
CrewAction,
ExpressionAction,
ScriptAction,
)
@@ -219,3 +275,14 @@ def _pop_local_context(kwargs: dict[str, Any]) -> LocalContext | None:
if not isinstance(local_context, dict):
raise TypeError("flow definition local context must be a mapping")
return cast(LocalContext, local_context)
def _script_function_source(code: str) -> str:
body = code if code.strip() else "pass"
source = (
"def __flow_script__(state, outputs, input, item):\n"
f"{textwrap.indent(body, ' ')}"
)
if not source.endswith("\n"):
source += "\n"
return source

View File

@@ -7,6 +7,7 @@ import json
import re
from typing import TYPE_CHECKING, Any, cast
from crewai.flow.runtime._outputs import outputs_by_name
from crewai.utilities.serialization import to_serializable
@@ -44,7 +45,12 @@ def evaluate_expression(
def _expression_context(
flow: Flow[Any], local_context: dict[str, Any] | None = None
) -> dict[str, Any]:
outputs = _outputs_by_name(flow._method_outputs)
local_outputs = local_context.get("outputs") if local_context else None
outputs = outputs_by_name(
flow._method_outputs,
local_outputs=local_outputs,
serialize=True,
)
context: dict[str, Any] = {
"state": flow._copy_and_serialize_state(),
"outputs": outputs,
@@ -53,29 +59,12 @@ def _expression_context(
local_values = {
key: to_serializable(value, max_depth=0)
for key, value in local_context.items()
if key not in {"outputs", "state"}
}
local_outputs = local_values.pop("outputs", None)
local_values.pop("state", None)
context.update(local_values)
if local_outputs is not None:
if not isinstance(local_outputs, dict):
raise TypeError("flow definition local outputs must be a mapping")
context["outputs"] = {**outputs, **local_outputs}
return context
def _outputs_by_name(method_outputs: list[Any]) -> dict[str, Any]:
outputs: dict[str, Any] = {}
for entry in method_outputs:
method = ""
output = entry
if isinstance(entry, dict) and "output" in entry:
method = str(entry.get("method", ""))
output = entry["output"]
outputs[method] = to_serializable(output, max_depth=0)
return outputs
def _render_value(value: Any, context: dict[str, Any]) -> Any:
if isinstance(value, str):
return _render_string(value, context)

View File

@@ -0,0 +1,42 @@
"""Shared FlowDefinition runtime output helpers."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from crewai.utilities.serialization import to_serializable
def outputs_by_name(
method_outputs: list[Any],
*,
local_outputs: Mapping[str, Any] | None = None,
serialize: bool = False,
) -> dict[str, Any]:
outputs: dict[str, Any] = {}
for entry in method_outputs:
method = ""
output = entry
if isinstance(entry, dict) and "output" in entry:
method = str(entry.get("method", ""))
output = entry["output"]
outputs[method] = _output_value(output, serialize=serialize)
if local_outputs is not None:
if not isinstance(local_outputs, Mapping):
raise TypeError("flow definition local outputs must be a mapping")
outputs.update(
{
key: _output_value(output, serialize=serialize)
for key, output in local_outputs.items()
}
)
return outputs
def _output_value(value: Any, *, serialize: bool) -> Any:
if not serialize:
return value
return to_serializable(value, max_depth=0)

View File

@@ -14,12 +14,22 @@ from crewai.project.annotations import (
tool,
)
from crewai.project.crew_base import CrewBase
from crewai.project.crew_definition import (
CrewAgentDefinition,
CrewDefinition,
CrewTaskDefinition,
PythonReferenceDefinition,
)
from crewai.project.crew_loader import load_crew, load_crew_and_kickoff
from crewai.project.json_loader import load_agent, strip_jsonc_comments
__all__ = [
"CrewAgentDefinition",
"CrewBase",
"CrewDefinition",
"CrewTaskDefinition",
"PythonReferenceDefinition",
"after_kickoff",
"agent",
"before_kickoff",

View File

@@ -0,0 +1,129 @@
"""Definition models for inline CrewAI crew configurations."""
from __future__ import annotations
from typing import Any, TypeAlias
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
__all__ = [
"CrewAgentDefinition",
"CrewDefinition",
"CrewTaskDefinition",
"PythonReferenceDefinition",
]
class PythonReferenceDefinition(BaseModel):
"""Dotted Python reference used by crew definitions."""
python: str
@field_validator("python")
@classmethod
def _validate_python_ref(cls, value: str) -> str:
path = value.strip()
if not path:
raise ValueError("Python reference 'python' must be a string")
if "." not in path:
raise ValueError(
f"Python reference '{path}' must be a dotted import path "
"like 'module.attribute'"
)
return path
class CrewAgentDefinition(BaseModel):
"""Inline agent definition used by a crew definition."""
model_config = ConfigDict(extra="allow")
role: str
goal: str
backstory: str
type: str | PythonReferenceDefinition | None = None
settings: dict[str, Any] = Field(default_factory=dict)
@field_validator("settings", mode="before")
@classmethod
def _validate_settings(cls, value: Any) -> Any:
if value is not None and not isinstance(value, dict):
raise ValueError("agent.settings must be a mapping")
return value or {}
class CrewTaskDefinition(BaseModel):
"""Task definition used by a crew definition."""
model_config = ConfigDict(extra="allow")
description: str
expected_output: str
name: str | None = None
agent: str | None = None
context: list[str] | None = None
type: str | PythonReferenceDefinition | None = None
_CrewAgentsInput: TypeAlias = dict[str, CrewAgentDefinition] | list[dict[str, Any]]
class CrewDefinition(BaseModel):
"""In-memory JSON/YAML crew definition with inline agents and tasks."""
model_config = ConfigDict(extra="allow")
agents: dict[str, CrewAgentDefinition]
tasks: list[CrewTaskDefinition]
inputs: dict[str, Any] = Field(default_factory=dict)
manager_agent: str | PythonReferenceDefinition | None = None
@field_validator("inputs", mode="before")
@classmethod
def _validate_inputs(cls, value: Any) -> Any:
if value is not None and not isinstance(value, dict):
raise ValueError("crew.inputs must be a mapping")
return value or {}
@field_validator(
"agents",
mode="before",
json_schema_input_type=_CrewAgentsInput,
)
@classmethod
def _validate_inline_agents(cls, value: Any) -> Any:
if isinstance(value, dict):
return value
if not isinstance(value, list):
return value
agents: dict[str, Any] = {}
for index, item in enumerate(value):
if not isinstance(item, dict):
raise ValueError(f"agents[{index}] must be an inline agent mapping")
if "name" in item:
name = item["name"]
if not isinstance(name, str) or not name:
raise ValueError(f"agents[{index}].name must be a non-empty string")
agents[name] = {key: val for key, val in item.items() if key != "name"}
continue
if len(item) != 1:
raise ValueError(
f"agents[{index}] must include a name field or be a one-key mapping"
)
name, definition = next(iter(item.items()))
agents[str(name)] = definition
return agents
@model_validator(mode="after")
def _validate_inline_shape(self) -> CrewDefinition:
if not self.agents:
raise ValueError("crew action requires inline agent definitions")
if not self.tasks:
raise ValueError("crew action requires a non-empty tasks list")
return self

View File

@@ -7,10 +7,15 @@ from typing import Any
from pydantic import ValidationError
from crewai.project.crew_definition import CrewDefinition
from crewai.project.json_loader import (
JSONAgentDefinition,
JSONCrewProject,
JSONProjectError,
JSONProjectValidationError,
_AgentDefinitionSource,
_crew_kwargs_from_definition,
_load_json_crew_project_definition,
_task_class_from_definition,
_task_kwargs_from_definition,
load_json_crew_project,
@@ -27,12 +32,73 @@ def load_crew(
default inputs. Agent definitions are resolved from individual
``<name>.jsonc`` / ``<name>.json`` files inside an ``agents/`` directory.
"""
from crewai import Crew, Task
crew_path = Path(source)
project = load_json_crew_project(crew_path, agents_dir=agents_dir)
return _load_crew_project(project, project_root=crew_path.parent)
def build_agent(agent_def: Any) -> Any:
def load_crew_from_definition(
definition: CrewDefinition | dict[str, Any],
*,
source: str | Path = "<inline crew>",
project_root: str | Path | None = None,
) -> tuple[Any, dict[str, Any]]:
"""Load a ``Crew`` from an in-memory JSON/YAML crew definition."""
root = Path(project_root) if project_root is not None else Path.cwd()
source_label = str(source)
crew_definition = (
definition
if isinstance(definition, CrewDefinition)
else CrewDefinition.model_validate(definition)
)
definition_data = crew_definition.model_dump(mode="python", exclude_none=True)
project = _crew_project_from_definition(
definition_data,
source=source_label,
project_root=root,
)
return _load_crew_project(project, project_root=root)
def _crew_project_from_definition(
definition: dict[str, Any],
*,
source: str,
project_root: Path,
) -> JSONCrewProject:
agent_bodies: dict[str, Any] = definition["agents"]
agent_names = list(agent_bodies)
manager_agent = definition.get("manager_agent")
if isinstance(manager_agent, str):
agent_names = [name for name in agent_names if name != manager_agent]
def load_agent_definition_source(agent_name: str) -> _AgentDefinitionSource | None:
body = agent_bodies.get(agent_name)
if body is None:
return None
return body, f"{source}: agents.{agent_name}"
return _load_json_crew_project_definition(
{**definition, "agents": agent_names},
source=source,
agents_dir=project_root / "agents",
project_root=project_root,
load_agent_definition_source=load_agent_definition_source,
missing_agent_hint=None,
collect_errors=False,
)
def _load_crew_project(
project: JSONCrewProject,
*,
project_root: Path,
) -> tuple[Any, dict[str, Any]]:
from crewai import Crew, Task
source_label = str(project.crew_path)
def build_agent(agent_def: JSONAgentDefinition) -> Any:
try:
return agent_def.agent_class(**agent_def.kwargs)
except ValidationError as exc:
@@ -52,22 +118,26 @@ def load_crew(
task_name_map: dict[str, Task] = {}
for index, task_defn in enumerate(project.task_definitions):
source_label = f"{crew_path}: tasks[{index}]"
task_class = _task_class_from_definition(task_defn, f"{source_label}: type")
task_source = f"{source_label}: tasks[{index}]"
task_class = _task_class_from_definition(
task_defn,
f"{task_source}: type",
project_root=project_root,
)
task_kwargs = _task_kwargs_from_definition(
task_defn,
agents_map=agents_map,
task_name_map=task_name_map,
source=source_label,
project_root=crew_path.parent,
source=task_source,
project_root=project_root,
)
try:
task = task_class(**task_kwargs)
except ValidationError as exc:
raise JSONProjectError(f"{source_label}: validation failed: {exc}") from exc
raise JSONProjectError(f"{task_source}: validation failed: {exc}") from exc
except Exception as exc:
raise JSONProjectError(
f"{source_label}: failed to load task: {exc}"
f"{task_source}: failed to load task: {exc}"
) from exc
tasks_list.append(task)
@@ -80,17 +150,18 @@ def load_crew(
agents=[agents_map[name] for name in project.agent_names],
tasks=tasks_list,
agents_map=agents_map,
source=crew_path,
source=source_label,
project_root=project_root,
)
try:
crew = Crew(**crew_kwargs)
except ValidationError as exc:
raise JSONProjectError(f"{crew_path}: validation failed: {exc}") from exc
raise JSONProjectError(f"{source_label}: validation failed: {exc}") from exc
except JSONProjectValidationError:
raise
except Exception as exc:
raise JSONProjectError(f"{crew_path}: failed to load crew: {exc}") from exc
raise JSONProjectError(f"{source_label}: failed to load crew: {exc}") from exc
return crew, project.definition.get("inputs", {})

File diff suppressed because it is too large Load Diff

View File

@@ -99,7 +99,7 @@ def to_serializable(
if isinstance(obj, BaseModel):
try:
return to_serializable(
obj=obj.model_dump(mode="json", exclude=exclude),
obj=obj.model_dump(mode="json", exclude=exclude, serialize_as_any=True),
max_depth=max_depth,
_current_depth=_current_depth + 1,
_ancestors=new_ancestors,

View File

@@ -4,12 +4,14 @@ from __future__ import annotations
import json
from pathlib import Path
import sys
import types
import pytest
from crewai.llms.base_llm import BaseLLM
from crewai.project.json_loader import JSONProjectError, JSONProjectValidationError
from crewai.project.crew_loader import load_crew
from crewai.project.crew_loader import load_crew, load_crew_from_definition
def _write_python_defs(tmp_path: Path) -> None:
@@ -70,6 +72,91 @@ def _input_file_path(value) -> Path:
class TestLoadCrew:
def test_load_crew_from_inline_definition(self):
crew, inputs = load_crew_from_definition(
{
"name": "inline_crew",
"agents": {
"researcher": {
"role": "Researcher",
"goal": "Research {topic}",
"backstory": "Knows things.",
}
},
"tasks": [
{
"name": "research",
"description": "Research {topic}",
"expected_output": "Findings about {topic}",
"agent": "researcher",
}
],
"inputs": {"topic": "AI"},
}
)
assert crew.name == "inline_crew"
assert crew.agents[0].role == "Researcher"
assert crew.tasks[0].description == "Research {topic}"
assert inputs == {"topic": "AI"}
def test_inline_definition_accepts_null_inputs(self):
_, inputs = load_crew_from_definition(
{
"agents": {
"researcher": {
"role": "Researcher",
"goal": "Research",
"backstory": "Knows things.",
}
},
"tasks": [
{
"description": "Research",
"expected_output": "Findings",
"agent": "researcher",
}
],
"inputs": None,
}
)
assert inputs == {}
def test_inline_hierarchical_manager_agent_is_not_duplicated(self):
crew, _ = load_crew_from_definition(
{
"name": "inline_hier_manager_crew",
"agents": {
"worker": {
"role": "Worker",
"goal": "Do work",
"backstory": "Does things.",
},
"manager": {
"role": "Manager",
"goal": "Coordinate work",
"backstory": "Keeps the work moving.",
},
},
"tasks": [
{
"description": "Do work",
"expected_output": "Work done",
"agent": "manager",
}
],
"process": "hierarchical",
"manager_agent": "manager",
}
)
assert len(crew.agents) == 1
assert crew.agents[0].role == "Worker"
assert crew.manager_agent is not None
assert crew.manager_agent.role == "Manager"
assert crew.tasks[0].agent is crew.manager_agent
def test_minimal_crew(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
@@ -475,6 +562,98 @@ class TestLoadCrew:
assert "summary" in task.output_json.model_fields
assert task.converter_cls.__name__ == "SpecialConverter"
def test_crew_rejects_stdlib_python_ref_for_agent_callback(
self, tmp_path: Path
):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(
agents_dir,
"worker",
step_callback={"python": "os.system"},
)
crew_def = {
"name": "unsafe_callback_crew",
"agents": ["worker"],
"tasks": [
{
"name": "work",
"description": "Do work",
"expected_output": "Work done",
"agent": "worker",
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectError, match="project root"):
load_crew(crew_file)
def test_crew_rejects_stdlib_python_ref_for_mcp_tool_filter(
self, tmp_path: Path
):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(
agents_dir,
"worker",
mcps=[
{
"command": "python",
"args": ["server.py"],
"tool_filter": {"python": "os.system"},
}
],
)
crew_def = {
"name": "unsafe_mcp_filter_crew",
"agents": ["worker"],
"tasks": [
{
"name": "work",
"description": "Do work",
"expected_output": "Work done",
"agent": "worker",
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectError, match="project root"):
load_crew(crew_file)
def test_crew_rejects_callable_python_ref_for_object_field(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
):
_write_python_defs(tmp_path)
monkeypatch.syspath_prepend(str(tmp_path))
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(
agents_dir,
"worker",
security_config={"python": "json_refs.always_true"},
)
crew_def = {
"name": "unsafe_object_ref_crew",
"agents": ["worker"],
"tasks": [
{
"name": "work",
"description": "Do work",
"expected_output": "Work done",
"agent": "worker",
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectError, match="supported object reference"):
load_crew(crew_file)
def test_crew_loads_project_relative_input_files(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
@@ -510,6 +689,147 @@ class TestLoadCrew:
assert _input_file_path(input_files["brief"]) == brief_path
assert _input_file_path(input_files["spec"]) == spec_path
def test_crew_rejects_relative_input_file_outside_project(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(agents_dir, "reader")
crew_def = {
"name": "unsafe_input_files_crew",
"agents": ["reader"],
"tasks": [
{
"name": "read",
"description": "Read files",
"expected_output": "File summary",
"agent": "reader",
"input_files": {"secret": "../secret.txt"},
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectValidationError, match="outside the project root"):
load_crew(crew_file)
def test_crew_rejects_absolute_input_file_outside_project(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(agents_dir, "reader")
outside_path = tmp_path.parent / "secret.txt"
crew_def = {
"name": "unsafe_absolute_input_files_crew",
"agents": ["reader"],
"tasks": [
{
"name": "read",
"description": "Read files",
"expected_output": "File summary",
"agent": "reader",
"input_files": {"secret": str(outside_path)},
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectValidationError, match="outside the project root"):
load_crew(crew_file)
def test_crew_rejects_file_uri_input_file_outside_project(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(agents_dir, "reader")
outside_uri = (tmp_path.parent / "secret.txt").as_uri()
crew_def = {
"name": "unsafe_file_uri_input_files_crew",
"agents": ["reader"],
"tasks": [
{
"name": "read",
"description": "Read files",
"expected_output": "File summary",
"agent": "reader",
"input_files": {"secret": outside_uri},
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectValidationError, match="outside the project root"):
load_crew(crew_file)
@pytest.mark.parametrize(
"outside_path",
[
r"C:\Users\alice\.ssh\id_rsa",
"C:/Users/alice/.ssh/id_rsa",
r"\\server\share\secret.txt",
"//server/share/secret.txt",
],
)
def test_crew_rejects_windows_input_file_outside_project(
self, tmp_path: Path, outside_path: str
):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(agents_dir, "reader")
crew_def = {
"name": "unsafe_windows_input_files_crew",
"agents": ["reader"],
"tasks": [
{
"name": "read",
"description": "Read files",
"expected_output": "File summary",
"agent": "reader",
"input_files": {"secret": outside_path},
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
with pytest.raises(JSONProjectValidationError, match="outside the project root"):
load_crew(crew_file)
def test_crew_restores_external_module_cache_after_project_ref(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
):
_write_python_defs(tmp_path)
external_module = types.ModuleType("json_refs")
external_module.__file__ = str(tmp_path.parent / "json_refs.py")
external_module.marker = "external"
monkeypatch.setitem(sys.modules, "json_refs", external_module)
agents_dir = tmp_path / "agents"
agents_dir.mkdir()
_write_agent(
agents_dir,
"worker",
step_callback={"python": "json_refs.task_callback"},
)
crew_def = {
"name": "cache_restore_crew",
"agents": ["worker"],
"tasks": [
{
"name": "work",
"description": "Do work",
"expected_output": "Work done",
"agent": "worker",
}
],
}
crew_file = _write_crew(tmp_path, crew_def)
crew, _ = load_crew(crew_file)
assert crew.agents[0].step_callback.__name__ == "task_callback"
assert sys.modules["json_refs"] is external_module
def test_missing_agent_file_raises(self, tmp_path: Path):
agents_dir = tmp_path / "agents"
agents_dir.mkdir()

View File

@@ -11,6 +11,7 @@ import pytest
from crewai.llms.base_llm import BaseLLM
from crewai.project.json_loader import (
JSONProjectValidationError,
_looks_like_windows_absolute_path,
find_json_project_file,
load_agent,
strip_jsonc_comments,
@@ -74,6 +75,31 @@ def test_find_json_project_file_prefers_jsonc(tmp_path: Path):
assert find_json_project_file(tmp_path, "agent") == jsonc_path
@pytest.mark.parametrize(
"path_value",
[
r"C:\Users\alice\.ssh\id_rsa",
"C:/Users/alice/.ssh/id_rsa",
r"\\server\share\secret.txt",
"//server/share/secret.txt",
],
)
def test_windows_absolute_path_detection(path_value: str):
assert _looks_like_windows_absolute_path(path_value)
@pytest.mark.parametrize(
"path_value",
[
r"folder\file.txt",
"folder/file.txt",
r"\server\share\secret.txt",
],
)
def test_windows_absolute_path_detection_ignores_relative_paths(path_value: str):
assert not _looks_like_windows_absolute_path(path_value)
class TestLoadAgent:
def test_load_minimal_agent(self, tmp_path: Path):
agent_def = {
@@ -480,6 +506,28 @@ class TestValidationDoesNotExecuteTools:
assert "Invalid custom tool name" in str(exc_info.value)
def test_validate_rejects_deep_python_ref_nesting(self, tmp_path):
from crewai.project.json_loader import validate_crew_project
crew_path = self._write_project(
tmp_path,
tool_line='{"tool_type": "some.module.Tool"}',
)
agent_file = tmp_path / "agents" / "worker.jsonc"
agent_def = json.loads(agent_file.read_text())
nested: dict[str, object] = {}
current = nested
for _ in range(70):
child: dict[str, object] = {}
current["nested"] = child
current = child
current["ref"] = {"python": "callbacks.step_callback"}
agent_def["security_config"] = nested
agent_file.write_text(json.dumps(agent_def))
with pytest.raises(JSONProjectValidationError, match="maximum depth"):
validate_crew_project(crew_path, tmp_path / "agents")
class TestCustomToolPathSafety:
@pytest.mark.parametrize(

View File

@@ -3830,7 +3830,6 @@ def test_crew_testing_function(researcher):
assert isinstance(received_events[1], CrewTestCompletedEvent)
@pytest.mark.vcr()
def test_hierarchical_verbose_manager_agent(researcher, writer):
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
@@ -3845,13 +3844,18 @@ def test_hierarchical_verbose_manager_agent(researcher, writer):
verbose=True,
)
crew.kickoff()
mock_task_output = TaskOutput(
description="Mock description", raw="mocked output", agent="mocked agent", messages=[]
)
task.output = mock_task_output
with patch.object(Task, "execute_sync", return_value=mock_task_output):
crew.kickoff()
assert crew.manager_agent is not None
assert crew.manager_agent.verbose
@pytest.mark.vcr()
def test_hierarchical_verbose_false_manager_agent(researcher, writer):
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
@@ -3866,7 +3870,13 @@ def test_hierarchical_verbose_false_manager_agent(researcher, writer):
verbose=False,
)
crew.kickoff()
mock_task_output = TaskOutput(
description="Mock description", raw="mocked output", agent="mocked agent", messages=[]
)
task.output = mock_task_output
with patch.object(Task, "execute_sync", return_value=mock_task_output):
crew.kickoff()
assert crew.manager_agent is not None
assert not crew.manager_agent.verbose

View File

@@ -8,7 +8,7 @@ from pathlib import Path
from typing import Annotated, Literal
import pytest
from pydantic import BaseModel
from pydantic import BaseModel, ValidationError
import crewai.flow.dsl as flow_dsl
import crewai.flow.flow_definition as flow_definition
@@ -41,22 +41,154 @@ def test_flow_public_exports_are_explicit():
"FlowConfigDefinition",
"FlowConversationalDefinition",
"FlowConversationalRouterDefinition",
"FlowCrewActionDefinition",
"FlowDefinition",
"FlowDefinitionCondition",
"FlowDefinitionDiagnostic",
"FlowDictStateDefinition",
"FlowEachActionDefinition",
"FlowEachInnerActionDefinition",
"FlowExpressionActionDefinition",
"FlowHumanFeedbackDefinition",
"FlowJsonSchemaStateDefinition",
"FlowMethodDefinition",
"FlowPersistenceDefinition",
"FlowPydanticStateDefinition",
"FlowScriptActionDefinition",
"FlowStateDefinition",
"FlowToolActionDefinition",
"FlowUnknownStateDefinition",
}
assert "build_flow_structure" in flow_visualization.__all__
assert "calculate_node_levels" not in flow_visualization.__all__
def test_flow_definition_json_schema_carries_reference_descriptions():
schema = flow_definition.FlowDefinition.json_schema()
defs = schema["$defs"]
assert schema["properties"]["schema"]["description"]
assert schema["properties"]["methods"]["description"]
method_properties = defs["FlowMethodDefinition"]["properties"]
assert method_properties["do"]["description"] == "Action executed when this method runs."
assert "Trigger condition" in method_properties["listen"]["description"]
script_properties = defs["FlowScriptActionDefinition"]["properties"]
assert "trusted inline Python" in script_properties["call"]["description"]
assert "not interpolated" in script_properties["code"]["description"]
assert "not sandboxed" in script_properties["code"]["description"]
state_schema = schema["properties"]["state"]["anyOf"][0]
assert state_schema["discriminator"]["propertyName"] == "type"
assert state_schema["discriminator"]["mapping"] == {
"dict": "#/$defs/FlowDictStateDefinition",
"json_schema": "#/$defs/FlowJsonSchemaStateDefinition",
"pydantic": "#/$defs/FlowPydanticStateDefinition",
"unknown": "#/$defs/FlowUnknownStateDefinition",
}
dict_state_properties = defs["FlowDictStateDefinition"]["properties"]
assert dict_state_properties["type"]["description"]
assert "ref" not in dict_state_properties
json_schema_state_properties = defs["FlowJsonSchemaStateDefinition"]["properties"]
assert json_schema_state_properties["json_schema"]["description"]
assert "json_schema" in defs["FlowJsonSchemaStateDefinition"]["required"]
pydantic_state_properties = defs["FlowPydanticStateDefinition"]["properties"]
assert "Fallback JSON Schema" in pydantic_state_properties["json_schema"][
"description"
]
each_properties = defs["FlowEachActionDefinition"]["properties"]
assert "list to iterate" in each_properties["in"]["description"]
assert "Ordered inner actions" in each_properties["do"]["description"]
def test_flow_definition_json_schema_carries_field_examples_only():
schema = flow_definition.FlowDefinition.json_schema()
defs = schema["$defs"]
for model_name in [
"FlowDefinition",
"FlowCodeActionDefinition",
"FlowToolActionDefinition",
"FlowCrewActionDefinition",
"FlowExpressionActionDefinition",
"FlowScriptActionDefinition",
"FlowEachActionDefinition",
"FlowMethodDefinition",
"FlowDictStateDefinition",
"FlowJsonSchemaStateDefinition",
"FlowPydanticStateDefinition",
"FlowUnknownStateDefinition",
"FlowConfigDefinition",
"FlowPersistenceDefinition",
"FlowHumanFeedbackDefinition",
"FlowDefinitionDiagnostic",
]:
model_schema = schema if model_name == "FlowDefinition" else defs[model_name]
assert "examples" not in model_schema
assert schema["properties"]["name"]["examples"] == ["ResearchFlow"]
assert schema["properties"]["schema"]["examples"] == ["crewai.flow/v1"]
assert schema["properties"]["methods"]["examples"][0]["seed"]["do"] == {
"call": "expression",
"expr": "state.topic",
}
script_properties = defs["FlowScriptActionDefinition"]["properties"]
assert script_properties["call"]["examples"] == ["script"]
assert "input.strip()" in script_properties["code"]["examples"][0]
assert script_properties["language"]["examples"] == ["python"]
action_properties = defs["FlowCodeActionDefinition"]["properties"]
assert action_properties["ref"]["examples"] == [
"my_project.flows:normalize_topic"
]
assert action_properties["with"]["examples"] == [{"topic": "${state.topic}"}]
each_properties = defs["FlowEachActionDefinition"]["properties"]
assert each_properties["in"]["examples"] == ["state.rows"]
assert each_properties["do"]["examples"][0][0]["clean"]["call"] == "script"
method_properties = defs["FlowMethodDefinition"]["properties"]
assert method_properties["listen"]["examples"] == [
"seed",
{"or": ["approved", "revise"]},
]
assert method_properties["emit"]["examples"] == [["approved", "revise"]]
def test_flow_state_definition_uses_discriminated_branches():
definition = flow_definition.FlowDefinition.model_validate(
{
"name": "TypedStateFlow",
"state": {
"type": "json_schema",
"json_schema": {"type": "object"},
},
}
)
assert isinstance(
definition.state,
flow_definition.FlowJsonSchemaStateDefinition,
)
with pytest.raises(ValidationError, match="extra_forbidden"):
flow_definition.FlowDefinition.model_validate(
{
"name": "InvalidStateFlow",
"state": {
"type": "dict",
"ref": "my_project.flows:ResearchState",
},
}
)
def test_condition_combinators_return_nested_runtime_tree():
condition = and_("event_a", "event_b", or_("event_c"))

View File

@@ -765,6 +765,252 @@ methods:
)
def test_crew_action_runs_inline_yaml_definition(monkeypatch: pytest.MonkeyPatch):
from crewai import Crew
async def fake_kickoff_async(
self: Crew, inputs: dict[str, Any] | None = None, **_kwargs: Any
) -> dict[str, Any]:
return {
"crew": self.name,
"agents": [agent.role for agent in self.agents],
"tasks": [task.description for task in self.tasks],
"inputs": inputs,
}
monkeypatch.setattr(Crew, "kickoff_async", fake_kickoff_async)
yaml_str = """
schema: crewai.flow/v1
name: CrewFlow
methods:
research:
do:
call: crew
with:
name: inline_research
agents:
researcher:
role: Researcher
goal: Research {topic}
backstory: Knows things.
tasks:
- name: research_task
description: Research {topic}
expected_output: Findings about {topic}
agent: researcher
inputs:
topic: "${state.topic}"
start: true
"""
flow = Flow.from_definition(FlowDefinition.from_yaml(yaml_str))
assert flow.kickoff(inputs={"topic": "AI"}) == {
"crew": "inline_research",
"agents": ["Researcher"],
"tasks": ["Research {topic}"],
"inputs": {"topic": "AI"},
}
def test_crew_action_round_trips_with_inline_definition():
definition = FlowDefinition.from_dict(
{
"schema": "crewai.flow/v1",
"name": "CrewFlow",
"methods": {
"research": {
"start": True,
"do": {
"call": "crew",
"with": {
"name": "inline_research",
"agents": {
"researcher": {
"role": "Researcher",
"goal": "Research {topic}",
"backstory": "Knows things.",
}
},
"tasks": [
{
"name": "research_task",
"description": "Research {topic}",
"expected_output": "Findings about {topic}",
"agent": "researcher",
}
],
"inputs": {"topic": "${state.topic}"},
},
},
}
},
}
)
assert definition.to_dict()["methods"]["research"]["do"]["call"] == "crew"
assert (
definition.to_dict()["methods"]["research"]["do"]["with"]["agents"][
"researcher"
]["role"]
== "Researcher"
)
def test_crew_action_normalizes_named_agent_list_definition():
definition = FlowDefinition.from_dict(
{
"schema": "crewai.flow/v1",
"name": "CrewFlow",
"methods": {
"research": {
"start": True,
"do": {
"call": "crew",
"with": {
"agents": [
{
"name": "researcher",
"role": "Researcher",
"goal": "Research {topic}",
"backstory": "Knows things.",
}
],
"tasks": [
{
"description": "Research {topic}",
"expected_output": "Findings about {topic}",
"agent": "researcher",
}
],
},
},
}
},
}
)
assert (
definition.to_dict()["methods"]["research"]["do"]["with"]["agents"][
"researcher"
]["role"]
== "Researcher"
)
def test_crew_action_json_schema_describes_inline_crew_definitions():
schema_defs = FlowDefinition.json_schema()["$defs"]
agents_schema = schema_defs["CrewDefinition"]["properties"]["agents"]
assert set(schema_defs["CrewDefinition"]["properties"]) >= {
"agents",
"tasks",
"inputs",
}
assert {option["type"] for option in agents_schema["anyOf"]} == {"array", "object"}
assert set(schema_defs["CrewAgentDefinition"]["properties"]) >= {
"role",
"goal",
"backstory",
"settings",
}
assert set(schema_defs["CrewTaskDefinition"]["properties"]) >= {
"description",
"expected_output",
"agent",
"context",
}
def test_crew_action_rejects_incomplete_inline_agent_definition():
with pytest.raises(ValidationError, match="goal"):
FlowDefinition.from_dict(
{
"schema": "crewai.flow/v1",
"name": "CrewFlow",
"methods": {
"research": {
"start": True,
"do": {
"call": "crew",
"with": {
"agents": {
"researcher": {
"role": "Researcher",
"backstory": "Knows things.",
}
},
"tasks": [
{
"description": "Research",
"expected_output": "Findings",
"agent": "researcher",
}
],
},
},
}
},
}
)
def test_crew_action_rejects_ref():
with pytest.raises(ValidationError, match="ref"):
FlowDefinition.from_dict(
{
"schema": "crewai.flow/v1",
"name": "CrewFlow",
"methods": {
"research": {
"start": True,
"do": {
"call": "crew",
"ref": "project.crew:build_crew",
"with": {"inputs": {"topic": "AI"}},
},
}
},
}
)
def test_crew_action_rejects_non_mapping_inputs_in_definition():
with pytest.raises(ValidationError, match="crew.inputs must be a mapping"):
FlowDefinition.from_dict(
{
"schema": "crewai.flow/v1",
"name": "CrewFlow",
"methods": {
"research": {
"start": True,
"do": {
"call": "crew",
"with": {
"agents": {
"researcher": {
"role": "Researcher",
"goal": "Research",
"backstory": "Knows things.",
}
},
"tasks": [
{
"description": "Research",
"expected_output": "Findings",
"agent": "researcher",
}
],
"inputs": "topic",
},
},
}
},
}
)
def test_tool_action_reports_invalid_cel_expression():
yaml_str = f"""
schema: crewai.flow/v1
@@ -899,6 +1145,84 @@ methods:
assert flow.kickoff(inputs={"rows": ["a", "b"]}) == ["async:a", "async:b"]
def test_script_action_runs_python_imports_mutates_state_and_returns_value():
yaml_str = """
schema: crewai.flow/v1
name: ScriptFlow
methods:
normalize:
do:
call: script
code: |
import math
state["rounded"] = math.ceil(state["raw_score"])
return f"rounded:{state['rounded']}"
start: true
"""
flow = Flow.from_definition(FlowDefinition.from_yaml(yaml_str))
assert flow.kickoff(inputs={"raw_score": 3.2}) == "rounded:4"
assert flow.state["rounded"] == 4
def test_script_listener_reads_trigger_input_and_outputs():
yaml_str = """
schema: crewai.flow/v1
name: ScriptFlow
methods:
seed:
do:
call: expression
expr: "'alpha'"
start: true
combine:
do:
call: script
code: |
state["input_matches_output"] = input == outputs["seed"]
return f"{outputs['seed']}:{input}"
listen: seed
"""
flow = Flow.from_definition(FlowDefinition.from_yaml(yaml_str))
assert flow.kickoff() == "alpha:alpha"
assert flow.state["input_matches_output"] is True
def test_script_each_action_reads_item_and_inner_outputs():
yaml_str = """
schema: crewai.flow/v1
name: ScriptEachFlow
methods:
seed:
do:
call: expression
expr: "'global'"
start: true
process_rows:
do:
call: each
in: state.rows
do:
- clean:
call: script
code: |
return item.strip()
- tag:
call: script
code: |
return f"{outputs['seed']}:{outputs['clean']}"
listen: seed
"""
flow = Flow.from_definition(FlowDefinition.from_yaml(yaml_str))
assert flow.kickoff(inputs={"rows": [" a ", " b "]}) == ["global:a", "global:b"]
def test_each_action_uses_iteration_outputs_between_nested_actions():
yaml_str = f"""
schema: crewai.flow/v1

View File

@@ -21,6 +21,10 @@ class Person(BaseModel):
skills: List[str]
class Container(BaseModel):
payload: BaseModel | None = None
@dataclass
class DataclassPerson:
name: str
@@ -114,6 +118,16 @@ def test_pydantic_model_serialization():
)
def test_polymorphic_field_serializes_concrete_subclass():
container = Container(
payload=Address(street="1 Main", city="Tech City", country="Pythonia")
)
assert to_serializable(container) == {
"payload": {"street": "1 Main", "city": "Tech City", "country": "Pythonia"}
}
def test_dataclass_serialization_recurses_into_nested_values():
person = DataclassPerson(
name="Ada",