Compare commits

..

2 Commits

Author SHA1 Message Date
Alex
7780565f1b fix: address review comments for MCP stdio command allowlist
- Remove unused pytest import from test_stdio_config.py
- Strip file extension from base_command for Windows compatibility
  (e.g., python.exe -> python) using os.path.splitext

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-02 13:14:00 -07:00
Iris Clawd
52c4e4a309 feat: add command allowlist validation for MCP stdio transport
Add an optional allowed_commands parameter to StdioTransport that
validates the command basename against an allowlist before spawning
a subprocess. This provides defense-in-depth against configuration-
driven command injection as MCP server discovery becomes more dynamic.

- DEFAULT_ALLOWED_COMMANDS includes common runtimes: python, python3,
  node, npx, uvx, uv, deno, docker
- Validation checks os.path.basename(command) for cross-platform support
- Users can extend the allowlist, pass a custom set, or set
  allowed_commands=None to disable the check entirely
- No breaking change: all currently documented MCP server examples use
  commands in the default allowlist
- MCPServerStdio config model updated with allowed_commands field
- tool_resolver passes allowed_commands through to StdioTransport

Closes #5080
2026-04-02 13:13:43 -07:00
153 changed files with 4996 additions and 10891 deletions

View File

@@ -28,7 +28,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: ${{ matrix.python-version }}
enable-cache: false

View File

@@ -35,7 +35,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: "3.12"
enable-cache: true

View File

@@ -26,7 +26,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: "3.11"
enable-cache: false

View File

@@ -95,7 +95,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: "3.12"
enable-cache: false

View File

@@ -65,7 +65,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: "3.12"
enable-cache: false

View File

@@ -36,7 +36,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: ${{ matrix.python-version }}
enable-cache: false

View File

@@ -33,7 +33,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: ${{ matrix.python-version }}
enable-cache: false

View File

@@ -40,7 +40,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
version: "0.8.4"
python-version: ${{ matrix.python-version }}
enable-cache: false

View File

@@ -1,105 +0,0 @@
name: Vulnerability Scan
on:
pull_request:
push:
branches: [main]
schedule:
# Run weekly on Monday at 9:00 UTC
- cron: '0 9 * * 1'
permissions:
contents: read
jobs:
pip-audit:
name: pip-audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore global uv cache
id: cache-restore
uses: actions/cache/restore@v4
with:
path: |
~/.cache/uv
~/.local/share/uv
.venv
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-main-py3.11-
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.11.3"
python-version: "3.11"
enable-cache: false
- name: Install dependencies
run: uv sync --all-groups --all-extras --no-install-project
- name: Install pip-audit
run: uv pip install pip-audit
- name: Run pip-audit
run: |
uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json \
--ignore-vuln CVE-2025-69872 \
--ignore-vuln CVE-2026-25645 \
--ignore-vuln CVE-2026-27448 \
--ignore-vuln CVE-2026-27459 \
--ignore-vuln PYSEC-2023-235
# Ignored CVEs:
# CVE-2025-69872 - diskcache 5.6.3: no fix available (latest version)
# CVE-2026-25645 - requests 2.32.5: fix requires 2.33.0, blocked by crewai-tools ~=2.32.5 pin
# CVE-2026-27448 - pyopenssl 25.3.0: fix requires 26.0.0, blocked by snowflake-connector-python <26.0.0 pin
# CVE-2026-27459 - pyopenssl 25.3.0: same as above
# PYSEC-2023-235 - couchbase: fixed in 4.6.0 (already upgraded), advisory not yet updated
continue-on-error: true
- name: Display results
if: always()
run: |
if [ -f pip-audit-report.json ]; then
echo "## pip-audit Results" >> $GITHUB_STEP_SUMMARY
echo '```json' >> $GITHUB_STEP_SUMMARY
cat pip-audit-report.json | python3 -m json.tool >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
# Fail if vulnerabilities found
python3 -c "
import json, sys
with open('pip-audit-report.json') as f:
data = json.load(f)
vulns = [d for d in data.get('dependencies', []) if d.get('vulns')]
if vulns:
print(f'::error::Found vulnerabilities in {len(vulns)} package(s)')
for v in vulns:
for vuln in v['vulns']:
print(f' - {v[\"name\"]}=={v[\"version\"]}: {vuln[\"id\"]}')
sys.exit(1)
print('No known vulnerabilities found')
"
else
echo "::error::pip-audit failed to produce a report. Check the pip-audit step logs."
exit 1
fi
- name: Upload pip-audit report
if: always()
uses: actions/upload-artifact@v4
with:
name: pip-audit-report
path: pip-audit-report.json
- name: Save uv caches
if: steps.cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
~/.cache/uv
~/.local/share/uv
.venv
key: uv-main-py3.11-${{ hashFiles('uv.lock') }}

View File

@@ -21,7 +21,7 @@ repos:
types: [python]
exclude: ^(lib/crewai/src/crewai/cli/templates/|lib/crewai/tests/|lib/crewai-tools/tests/|lib/crewai-files/tests/)
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.3
rev: 0.9.3
hooks:
- id: uv-lock
- repo: https://github.com/commitizen-tools/commitizen

View File

@@ -4,99 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
icon: "clock"
mode: "wide"
---
<Update label="6 أبريل 2026">
## v1.14.0a3
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a3)
## ما الذي تغير
### الوثائق
- تحديث سجل التغييرات والإصدار لـ v1.14.0a2
## المساهمون
@joaomdmoura
</Update>
<Update label="6 أبريل 2026">
## v1.14.0a2
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a2)
# ملاحظات الإصدار 1.14.0a2
## التعليمات:
- ترجم جميع عناوين الأقسام والوصف بشكل طبيعي
- احتفظ بتنسيق markdown (##، ###، -، إلخ) كما هو
- احتفظ بجميع الأسماء الصحيحة، ومعرفات الشيفرة، وأسماء الفئات، والمصطلحات التقنية دون تغيير
(مثل "CrewAI"، "LiteAgent"، "ChromaDB"، "MCP"، "@username")
- احتفظ بقسم ## المساهمون وأسماء مستخدمي GitHub كما هي
- لا تضف أو تزيل أي محتوى، فقط ترجم
## المميزات الجديدة
- تمت إضافة دعم لـ "ChromaDB" لتحسين أداء قاعدة البيانات.
- تحسينات على "LiteAgent" لزيادة الكفاءة.
## الإصلاحات
- إصلاح مشكلة تتعلق بـ "MCP" التي كانت تؤدي إلى تعطل التطبيق.
- معالجة الأخطاء المتعلقة بواجهة المستخدم في "CrewAI".
## المساهمون
- @username1
- @username2
- @username3
</Update>
<Update label="2 أبريل 2026">
## v1.13.0
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0)
## ما الذي تغير
### الميزات
- إضافة نموذج RuntimeState RootModel لتوحيد تسلسل الحالة
- تعزيز مستمع الأحداث مع نطاقات جديدة للقياس عن أحداث المهارة والذاكرة
- إضافة امتداد A2UI مع دعم v0.8/v0.9، والمخططات، والوثائق
- إصدار بيانات استخدام الرموز في حدث LLMCallCompletedEvent
- تحديث تلقائي لمستودع اختبار النشر أثناء الإصدار
- تحسين مرونة الإصدار المؤسسي وتجربة المستخدم
### إصلاحات الأخطاء
- إضافة بيانات اعتماد مستودع الأدوات إلى تثبيت crewai
- إضافة بيانات اعتماد مستودع الأدوات إلى بناء uv في نشر الأدوات
- تمرير بيانات التعريف عبر الإعدادات بدلاً من معلمات الأدوات
- معالجة نماذج GPT-5.x التي لا تدعم معلمة API `stop`
- إضافة GPT-5 وسلسلة o إلى بادئات الرؤية متعددة الوسائط
- مسح ذاكرة التخزين المؤقت uv للحزم التي تم نشرها حديثًا في الإصدار المؤسسي
- تحديد lancedb أقل من 0.30.1 لضمان التوافق مع Windows
- إصلاح مستويات أذونات RBAC لتتناسب مع خيارات واجهة المستخدم الفعلية
- إصلاح عدم الدقة في قدرات الوكيل عبر جميع اللغات
### الوثائق
- إضافة فيديو توضيحي لمهارات وكيل البرمجة إلى صفحات البدء
- إضافة دليل شامل لتكوين SSO
- إضافة مصفوفة شاملة لأذونات RBAC ودليل النشر
- تحديث سجل التغييرات والإصدار إلى v1.13.0
### الأداء
- تقليل الحمل الزائد للإطار باستخدام حافلة الأحداث الكسولة، وتخطي التتبع عند تعطيله
### إعادة الهيكلة
- تحويل Flow إلى Pydantic BaseModel
- تحويل فئات LLM إلى Pydantic BaseModel
- استبدال InstanceOf[T] بتعليقات نوع عادية
- إزالة دليل LLM الخاص بالطرف الثالث غير المستخدم
## المساهمون
@alex-clawd, @dependabot[bot], @greysonlalonde, @iris-clawd, @joaomdmoura, @lorenzejay, @lucasgomide, @thiagomoretto
</Update>
<Update label="2 أبريل 2026">
## v1.13.0a7

View File

@@ -1,232 +0,0 @@
---
title: Checkpointing
description: حفظ حالة التنفيذ تلقائيا حتى تتمكن الطواقم والتدفقات والوكلاء من الاستئناف بعد الفشل.
icon: floppy-disk
mode: "wide"
---
<Warning>
الـ Checkpointing في اصدار مبكر. قد تتغير واجهات البرمجة في الاصدارات المستقبلية.
</Warning>
## نظرة عامة
يقوم الـ Checkpointing بحفظ حالة التنفيذ تلقائيا اثناء التشغيل. اذا فشل طاقم او تدفق او وكيل اثناء التنفيذ، يمكنك الاستعادة من اخر نقطة حفظ والاستئناف دون اعادة تنفيذ العمل المكتمل.
## البداية السريعة
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=True, # يستخدم الافتراضيات: ./.checkpoints, عند task_completed
)
result = crew.kickoff()
```
تتم كتابة ملفات نقاط الحفظ في `./.checkpoints/` بعد اكتمال كل مهمة.
## التكوين
استخدم `CheckpointConfig` للتحكم الكامل:
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
on_events=["task_completed", "crew_kickoff_completed"],
max_checkpoints=5,
),
)
```
### حقول CheckpointConfig
| الحقل | النوع | الافتراضي | الوصف |
|:------|:------|:----------|:------|
| `directory` | `str` | `"./.checkpoints"` | مسار ملفات نقاط الحفظ |
| `on_events` | `list[str]` | `["task_completed"]` | انواع الاحداث التي تطلق نقطة حفظ |
| `provider` | `BaseProvider` | `JsonProvider()` | واجهة التخزين |
| `max_checkpoints` | `int \| None` | `None` | الحد الاقصى للملفات؛ يتم حذف الاقدم اولا |
### الوراثة والانسحاب
يقبل حقل `checkpoint` في Crew و Flow و Agent قيم `CheckpointConfig` او `True` او `False` او `None`:
| القيمة | السلوك |
|:-------|:-------|
| `None` (افتراضي) | يرث من الاصل. الوكيل يرث اعدادات الطاقم. |
| `True` | تفعيل بالاعدادات الافتراضية. |
| `False` | انسحاب صريح. يوقف الوراثة من الاصل. |
| `CheckpointConfig(...)` | اعدادات مخصصة. |
```python
crew = Crew(
agents=[
Agent(role="Researcher", ...), # يرث checkpoint من الطاقم
Agent(role="Writer", ..., checkpoint=False), # منسحب، بدون نقاط حفظ
],
tasks=[...],
checkpoint=True,
)
```
## الاستئناف من نقطة حفظ
```python
# استعادة واستئناف
crew = Crew.from_checkpoint("./my_checkpoints/20260407T120000_abc123.json")
result = crew.kickoff() # يستأنف من اخر مهمة مكتملة
```
يتخطى الطاقم المستعاد المهام المكتملة ويستأنف من اول مهمة غير مكتملة.
## يعمل على Crew و Flow و Agent
### Crew
```python
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task, review_task],
checkpoint=CheckpointConfig(directory="./crew_cp"),
)
```
المشغل الافتراضي: `task_completed` (نقطة حفظ واحدة لكل مهمة مكتملة).
### Flow
```python
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig
class MyFlow(Flow):
@start()
def step_one(self):
return "data"
@listen(step_one)
def step_two(self, data):
return process(data)
flow = MyFlow(
checkpoint=CheckpointConfig(
directory="./flow_cp",
on_events=["method_execution_finished"],
),
)
result = flow.kickoff()
# استئناف
flow = MyFlow.from_checkpoint("./flow_cp/20260407T120000_abc123.json")
result = flow.kickoff()
```
### Agent
```python
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
checkpoint=CheckpointConfig(
directory="./agent_cp",
on_events=["lite_agent_execution_completed"],
),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])
```
## مزودات التخزين
يتضمن CrewAI مزودي تخزين لنقاط الحفظ.
### JsonProvider (افتراضي)
يكتب كل نقطة حفظ كملف JSON منفصل.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
provider=JsonProvider(),
max_checkpoints=5,
),
)
```
### SqliteProvider
يخزن جميع نقاط الحفظ في ملف قاعدة بيانات SQLite واحد.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./.checkpoints.db",
provider=SqliteProvider(max_checkpoints=50),
),
)
```
<Note>
عند استخدام `SqliteProvider`، حقل `directory` هو مسار ملف قاعدة البيانات، وليس مجلدا.
</Note>
## انواع الاحداث
يقبل حقل `on_events` اي مجموعة من سلاسل انواع الاحداث. الخيارات الشائعة:
| حالة الاستخدام | الاحداث |
|:---------------|:--------|
| بعد كل مهمة (Crew) | `["task_completed"]` |
| بعد كل طريقة في التدفق | `["method_execution_finished"]` |
| بعد تنفيذ الوكيل | `["agent_execution_completed"]`, `["lite_agent_execution_completed"]` |
| عند اكتمال الطاقم فقط | `["crew_kickoff_completed"]` |
| بعد كل استدعاء LLM | `["llm_call_completed"]` |
| على كل شيء | `["*"]` |
<Warning>
استخدام `["*"]` او احداث عالية التردد مثل `llm_call_completed` سيكتب العديد من ملفات نقاط الحفظ وقد يؤثر على الاداء. استخدم `max_checkpoints` للحد من استخدام المساحة.
</Warning>
## نقاط الحفظ اليدوية
للتحكم الكامل، سجل معالج الاحداث الخاص بك واستدع `state.checkpoint()` مباشرة:
```python
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
# معالج متزامن
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source, event, state):
path = state.checkpoint("./my_checkpoints")
print(f"تم حفظ نقطة الحفظ: {path}")
# معالج غير متزامن
@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source, event, state):
path = await state.acheckpoint("./my_checkpoints")
print(f"تم حفظ نقطة الحفظ: {path}")
```
وسيط `state` هو `RuntimeState` الذي يتم تمريره تلقائيا بواسطة ناقل الاحداث عندما يقبل المعالج 3 معاملات. يمكنك تسجيل معالجات على اي نوع حدث مدرج في وثائق [Event Listeners](/ar/concepts/event-listener).
الـ Checkpointing يعمل بافضل جهد: اذا فشلت كتابة نقطة حفظ، يتم تسجيل الخطأ ولكن التنفيذ يستمر دون انقطاع.

View File

@@ -106,7 +106,7 @@ mode: "wide"
```
<Tip>
يستغرق النشر الأول عادة حوالي دقيقة واحدة.
يستغرق النشر الأول عادة 10-15 دقيقة لبناء صور الحاويات. عمليات النشر اللاحقة أسرع بكثير.
</Tip>
</Step>
@@ -188,7 +188,7 @@ crewai deploy remove <deployment_id>
1. انقر على زر "Deploy" لبدء عملية النشر
2. يمكنك مراقبة التقدم عبر شريط التقدم
3. يستغرق النشر الأول عادة حوالي دقيقة واحدة
3. يستغرق النشر الأول عادة حوالي 10-15 دقيقة؛ عمليات النشر اللاحقة ستكون أسرع
<Frame>
![تقدم النشر](/images/enterprise/deploy-progress.png)

View File

@@ -1,132 +0,0 @@
---
title: "تدريب الطواقم"
description: "قم بتدريب طواقمك المنشورة مباشرة من منصة CrewAI AMP لتحسين أداء الوكلاء بمرور الوقت"
icon: "dumbbell"
mode: "wide"
---
يتيح لك التدريب تحسين أداء الطاقم من خلال تشغيل جلسات تدريب تكرارية مباشرة من علامة تبويب **Training** في CrewAI AMP. تستخدم المنصة **وضع التدريب التلقائي** — حيث تتولى العملية التكرارية تلقائياً، على عكس تدريب CLI الذي يتطلب ملاحظات بشرية تفاعلية لكل تكرار.
بعد اكتمال التدريب، يقوم CrewAI بتقييم مخرجات الوكلاء ودمج الملاحظات في اقتراحات قابلة للتنفيذ لكل وكيل. يتم بعد ذلك تطبيق هذه الاقتراحات على تشغيلات الطاقم المستقبلية لتحسين جودة المخرجات.
<Tip>
للحصول على تفاصيل حول كيفية عمل تدريب CrewAI، راجع صفحة [مفاهيم التدريب](/ar/concepts/training).
</Tip>
## المتطلبات الأساسية
<CardGroup cols={2}>
<Card title="نشر نشط" icon="rocket">
تحتاج إلى حساب CrewAI AMP مع نشر نشط في حالة **Ready** (نوع Crew).
</Card>
<Card title="صلاحية التشغيل" icon="key">
يجب أن يكون لحسابك صلاحية تشغيل للنشر الذي تريد تدريبه.
</Card>
</CardGroup>
## كيفية تدريب طاقم
<Steps>
<Step title="افتح علامة تبويب Training">
انتقل إلى **Deployments**، انقر على نشرك، ثم اختر علامة تبويب **Training**.
</Step>
<Step title="أدخل اسم التدريب">
قدم **Training Name** — سيصبح هذا اسم ملف `.pkl` المستخدم لتخزين نتائج التدريب. على سبيل المثال، "Expert Mode Training" ينتج `expert_mode_training.pkl`.
</Step>
<Step title="املأ مدخلات الطاقم">
أدخل حقول إدخال الطاقم. هذه هي نفس المدخلات التي ستقدمها للتشغيل العادي — يتم تحميلها ديناميكياً بناءً على تكوين طاقمك.
</Step>
<Step title="ابدأ التدريب">
انقر على **Train Crew**. يتغير الزر إلى "Training..." مع مؤشر دوران أثناء تشغيل العملية.
خلف الكواليس:
- يتم إنشاء سجل تدريب للنشر الخاص بك
- تستدعي المنصة نقطة نهاية التدريب التلقائي للنشر
- يقوم الطاقم بتشغيل تكراراته تلقائياً — لا حاجة لملاحظات يدوية
</Step>
<Step title="راقب التقدم">
تعرض لوحة **Current Training Status**:
- **Status** — الحالة الحالية لجلسة التدريب
- **Nº Iterations** — عدد تكرارات التدريب المُهيأة
- **Filename** — ملف `.pkl` الذي يتم إنشاؤه
- **Started At** — وقت بدء التدريب
- **Training Inputs** — المدخلات التي قدمتها
</Step>
</Steps>
## فهم نتائج التدريب
بمجرد اكتمال التدريب، سترى بطاقات نتائج لكل وكيل تحتوي على المعلومات التالية:
- **Agent Role** — اسم/دور الوكيل في طاقمك
- **Final Quality** — درجة من 0 إلى 10 تقيّم جودة مخرجات الوكيل
- **Final Summary** — ملخص لأداء الوكيل أثناء التدريب
- **Suggestions** — توصيات قابلة للتنفيذ لتحسين سلوك الوكيل
### تحرير الاقتراحات
يمكنك تحسين الاقتراحات لأي وكيل:
<Steps>
<Step title="انقر على Edit">
في بطاقة نتائج أي وكيل، انقر على زر **Edit** بجوار الاقتراحات.
</Step>
<Step title="عدّل الاقتراحات">
حدّث نص الاقتراحات ليعكس التحسينات التي تريدها بشكل أفضل.
</Step>
<Step title="احفظ التغييرات">
انقر على **Save**. تتم مزامنة الاقتراحات المُعدّلة مع النشر وتُستخدم في جميع التشغيلات المستقبلية.
</Step>
</Steps>
## استخدام بيانات التدريب
لتطبيق نتائج التدريب على طاقمك:
1. لاحظ **Training Filename** (ملف `.pkl`) من جلسة التدريب المكتملة.
2. حدد اسم الملف هذا في تكوين kickoff أو التشغيل الخاص بنشرك.
3. يقوم الطاقم تلقائياً بتحميل ملف التدريب وتطبيق الاقتراحات المخزنة على كل وكيل.
هذا يعني أن الوكلاء يستفيدون من الملاحظات المُنشأة أثناء التدريب في كل تشغيل لاحق.
## التدريبات السابقة
يعرض الجزء السفلي من علامة تبويب Training **سجل جميع جلسات التدريب السابقة** للنشر. استخدم هذا لمراجعة التدريبات السابقة، ومقارنة النتائج، أو اختيار ملف تدريب مختلف للاستخدام.
## معالجة الأخطاء
إذا فشل تشغيل التدريب، تعرض لوحة الحالة حالة خطأ مع رسالة تصف ما حدث خطأ.
الأسباب الشائعة لفشل التدريب:
- **لم يتم تحديث وقت تشغيل النشر** — تأكد من أن نشرك يعمل بأحدث إصدار
- **أخطاء تنفيذ الطاقم** — مشاكل في منطق مهام الطاقم أو تكوين الوكيل
- **مشاكل الشبكة** — مشاكل الاتصال بين المنصة والنشر
## القيود
<Info>
ضع هذه القيود في الاعتبار عند التخطيط لسير عمل التدريب الخاص بك:
- **تدريب نشط واحد في كل مرة** لكل نشر — انتظر حتى ينتهي التشغيل الحالي قبل بدء آخر
- **وضع التدريب التلقائي فقط** — لا تدعم المنصة الملاحظات التفاعلية لكل تكرار مثل CLI
- **بيانات التدريب خاصة بالنشر** — ترتبط نتائج التدريب بمثيل وإصدار النشر المحدد
</Info>
## الموارد ذات الصلة
<CardGroup cols={3}>
<Card title="مفاهيم التدريب" icon="book" href="/ar/concepts/training">
تعلم كيف يعمل تدريب CrewAI.
</Card>
<Card title="تشغيل الطاقم" icon="play" href="/ar/enterprise/guides/kickoff-crew">
قم بتشغيل طاقمك المنشور من منصة AMP.
</Card>
<Card title="النشر على AMP" icon="cloud-arrow-up" href="/ar/enterprise/guides/deploy-to-amp">
انشر طاقمك واجعله جاهزاً للتدريب.
</Card>
</CardGroup>

View File

@@ -204,8 +204,8 @@ python3 --version
## الخطوات التالية
<CardGroup cols={2}>
<Card title="بدء سريع: Flow + وكيل" icon="code" href="/ar/quickstart">
اتبع البداية السريعة لإنشاء Flow وتشغيل طاقم بوكيل واحد وإنتاج تقرير.
<Card title="ابنِ أول Agent لك" icon="code" href="/ar/quickstart">
اتبع دليل البداية السريعة لإنشاء أول Agent في CrewAI والحصول على تجربة عملية.
</Card>
<Card
title="انضم إلى المجتمع"

View File

@@ -138,9 +138,9 @@ mode: "wide"
<Card
title="البداية السريعة"
icon="bolt"
href="/ar/quickstart"
href="en/quickstart"
>
أنشئ Flow وشغّل طاقمًا بوكيل واحد وأنشئ تقريرًا من البداية للنهاية.
اتبع دليل البداية السريعة لإنشاء أول Agent في CrewAI والحصول على تجربة عملية.
</Card>
<Card
title="انضم إلى المجتمع"

View File

@@ -1,6 +1,6 @@
---
title: البدء السريع
description: ابنِ أول Flow في CrewAI خلال دقائق — التنسيق والحالة وفريقًا بوكيل واحد ينتج تقريرًا فعليًا.
description: ابنِ أول وكيل ذكاء اصطناعي مع CrewAI في أقل من 5 دقائق.
icon: rocket
mode: "wide"
---
@@ -13,266 +13,376 @@ mode: "wide"
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
في هذا الدليل ستُنشئ **Flow** يحدد موضوع بحث، ويشغّل **طاقمًا بوكيل واحد** (باحث يستخدم البحث على الويب)، وينتهي بتقرير **Markdown** على القرص. يُعد Flow الطريقة الموصى بها لتنظيم التطبيقات الإنتاجية: يمتلك **الحالة** و**ترتيب التنفيذ**، بينما **الوكلاء** ينفّذون العمل داخل خطوة الطاقم.
## ابنِ أول وكيل CrewAI
إذا لم تُكمل تثبيت CrewAI بعد، اتبع [دليل التثبيت](/ar/installation) أولًا.
لننشئ طاقماً بسيطاً يساعدنا في `البحث` و`إعداد التقارير` عن `أحدث تطورات الذكاء الاصطناعي` لموضوع أو مجال معين.
## المتطلبات الأساسية
قبل المتابعة، تأكد من إنهاء تثبيت CrewAI.
إذا لم تكن قد ثبّتها بعد، يمكنك القيام بذلك باتباع [دليل التثبيت](/ar/installation).
- بيئة Python وواجهة سطر أوامر CrewAI (راجع [التثبيت](/ar/installation))
- نموذج لغوي مهيأ بالمفاتيح الصحيحة — راجع [LLMs](/ar/concepts/llms#setting-up-your-llm)
- مفتاح API من [Serper.dev](https://serper.dev/) (`SERPER_API_KEY`) للبحث على الويب في هذا الدرس
## ابنِ أول Flow لك
اتبع الخطوات أدناه للبدء!
<Steps>
<Step title="أنشئ مشروع Flow">
من الطرفية، أنشئ مشروع Flow (اسم المجلد يستخدم شرطة سفلية، مثل `latest_ai_flow`):
<Step title="إنشاء طاقمك">
أنشئ مشروع طاقم جديد عبر تشغيل الأمر التالي في الطرفية.
سينشئ هذا مجلداً جديداً باسم `latest-ai-development` مع البنية الأساسية لطاقمك.
<CodeGroup>
```shell Terminal
crewai create flow latest-ai-flow
cd latest_ai_flow
crewai create crew latest-ai-development
```
</CodeGroup>
يُنشئ ذلك تطبيق 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="الانتقال إلى مشروع الطاقم الجديد">
<CodeGroup>
```shell Terminal
cd latest_ai_development
```
</CodeGroup>
</Step>
<Step title="تعديل ملف `agents.yaml`">
<Tip>
يمكنك أيضاً تعديل الوكلاء حسب الحاجة ليناسبوا حالة الاستخدام الخاصة بك أو نسخ ولصق كما هو في مشروعك.
أي متغير مُستكمل في ملفات `agents.yaml` و`tasks.yaml` مثل `{topic}` سيُستبدل بقيمة المتغير في ملف `main.py`.
</Tip>
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
# src/latest_ai_development/config/agents.yaml
researcher:
role: >
باحث بيانات أول في {topic}
{topic} Senior Data Researcher
goal: >
اكتشاف أحدث التطورات في {topic}
Uncover cutting-edge developments in {topic}
backstory: >
أنت باحث مخضرم تكشف أحدث المستجدات في {topic}.
تجد المعلومات الأكثر صلة وتعرضها بوضوح.
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.
```
</Step>
<Step title="اضبط مهمة واحدة في `tasks.yaml`">
<Step title="تعديل ملف `tasks.yaml`">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
# src/latest_ai_development/config/tasks.yaml
research_task:
description: >
أجرِ بحثًا معمقًا عن {topic}. استخدم البحث على الويب للعثور على معلومات
حديثة وموثوقة. السنة الحالية 2026.
Conduct a thorough research about {topic}
Make sure you find any interesting and relevant information given
the current year is 2025.
expected_output: >
تقرير بصيغة Markdown بأقسام واضحة: الاتجاهات الرئيسية، أدوات أو شركات بارزة،
والآثار. بين 800 و1200 كلمة تقريبًا. دون إحاطة المستند بأكمله بكتل كود.
A list with 10 bullet points of the most relevant information about {topic}
agent: researcher
output_file: output/report.md
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
output_file: report.md
```
</Step>
<Step title="اربط صف الطاقم (`content_crew.py`)">
اجعل الطاقم المُولَّد يشير إلى YAML وأرفق `SerperDevTool` بالباحث.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
<Step title="تعديل ملف `crew.py`">
```python crew.py
# src/latest_ai_development/crew.py
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.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ResearchCrew:
"""طاقم بحث بوكيل واحد داخل Flow."""
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment 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]
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
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]
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]
output_file='output/report.md' # This is the file that will be contain the final report.
)
@crew
def crew(self) -> Crew:
"""Creates the LatestAiDevelopment crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=True,
)
```
</Step>
<Step title="[اختياري] إضافة دوال قبل وبعد تشغيل الطاقم">
```python crew.py
# src/latest_ai_development/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff
from crewai_tools import SerperDevTool
<Step title="عرّف Flow في `main.py`">
اربط الطاقم بـ Flow: خطوة `@start()` تضبط الموضوع في **الحالة**، وخطوة `@listen` تشغّل الطاقم. يظل `output_file` للمهمة يكتب `output/report.md`.
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
```python main.py
# src/latest_ai_flow/main.py
from pydantic import BaseModel
@before_kickoff
def before_kickoff_function(self, inputs):
print(f"Before kickoff function with inputs: {inputs}")
return inputs # You can return the inputs or modify them as needed
from crewai.flow import Flow, listen, start
@after_kickoff
def after_kickoff_function(self, result):
print(f"After kickoff function with result: {result}")
return result # You can return the result or modify it as needed
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
class ResearchFlowState(BaseModel):
topic: str = ""
report: str = ""
class LatestAiFlow(Flow[ResearchFlowState]):
@start()
def prepare_topic(self, crewai_trigger_payload: dict | None = None):
if crewai_trigger_payload:
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
else:
self.state.topic = "AI Agents"
print(f"الموضوع: {self.state.topic}")
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("اكتمل طاقم البحث.")
@listen(run_research)
def summarize(self):
print("مسار التقرير: output/report.md")
def kickoff():
LatestAiFlow().kickoff()
def plot():
LatestAiFlow().plot()
if __name__ == "__main__":
kickoff()
# ... remaining code
```
<Tip>
إذا كان اسم الحزمة ليس `latest_ai_flow`، عدّل استيراد `ResearchCrew` ليطابق مسار الوحدة في مشروعك.
</Tip>
</Step>
<Step title="لا تتردد في تمرير مدخلات مخصصة لطاقمك">
على سبيل المثال، يمكنك تمرير مدخل `topic` لطاقمك لتخصيص البحث وإعداد التقارير.
```python main.py
#!/usr/bin/env python
# src/latest_ai_development/main.py
import sys
from latest_ai_development.crew import LatestAiDevelopmentCrew
def run():
"""
Run the crew.
"""
inputs = {
'topic': 'AI Agents'
}
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
```
</Step>
<Step title="تعيين متغيرات البيئة">
قبل تشغيل طاقمك، تأكد من تعيين المفاتيح التالية كمتغيرات بيئة في ملف `.env`:
- مفتاح API لـ [Serper.dev](https://serper.dev/): `SERPER_API_KEY=YOUR_KEY_HERE`
- إعداد النموذج الذي اخترته، مثل مفتاح API. راجع
[دليل إعداد LLM](/ar/concepts/llms#setting-up-your-llm) لمعرفة كيفية إعداد النماذج من أي مزود.
</Step>
<Step title="قفل وتثبيت التبعيات">
- اقفل التبعيات وثبّتها باستخدام أمر CLI:
<CodeGroup>
```shell Terminal
crewai install
```
</CodeGroup>
- إذا كانت لديك حزم إضافية تريد تثبيتها، يمكنك القيام بذلك عبر:
<CodeGroup>
```shell Terminal
uv add <package-name>
```
</CodeGroup>
</Step>
<Step title="تشغيل طاقمك">
- لتشغيل طاقمك، نفّذ الأمر التالي في جذر مشروعك:
<CodeGroup>
```bash Terminal
crewai run
```
</CodeGroup>
</Step>
<Step title="متغيرات البيئة">
في جذر المشروع، ضبط `.env`:
<Step title="البديل المؤسسي: الإنشاء في Crew Studio">
لمستخدمي CrewAI AMP، يمكنك إنشاء نفس الطاقم دون كتابة كود:
- `SERPER_API_KEY` — من [Serper.dev](https://serper.dev/)
- مفاتيح مزوّد النموذج حسب الحاجة — راجع [إعداد LLM](/ar/concepts/llms#setting-up-your-llm)
1. سجّل الدخول إلى حساب CrewAI AMP (أنشئ حساباً مجانياً على [app.crewai.com](https://app.crewai.com))
2. افتح Crew Studio
3. اكتب ما هي الأتمتة التي تحاول بناءها
4. أنشئ مهامك بصرياً واربطها بالتسلسل
5. هيئ مدخلاتك وانقر "تحميل الكود" أو "نشر"
![واجهة Crew Studio للبدء السريع](/images/enterprise/crew-studio-interface.png)
<Card title="جرّب CrewAI AMP" icon="rocket" href="https://app.crewai.com">
ابدأ حسابك المجاني في CrewAI AMP
</Card>
</Step>
<Step title="عرض التقرير النهائي">
يجب أن ترى المخرجات في وحدة التحكم ويجب إنشاء ملف `report.md` في جذر مشروعك مع التقرير النهائي.
<Step title="التثبيت والتشغيل">
<CodeGroup>
```shell Terminal
crewai install
crewai run
```
</CodeGroup>
يُنفّذ `crewai run` نقطة دخول Flow المعرّفة في المشروع (نفس أمر الطواقم؛ نوع المشروع `"flow"` في `pyproject.toml`).
</Step>
<Step title="تحقق من المخرجات">
يجب أن ترى سجلات من Flow والطاقم. افتح **`output/report.md`** للتقرير المُولَّد (مقتطف):
إليك مثالاً على شكل التقرير:
<CodeGroup>
```markdown output/report.md
# وكلاء الذكاء الاصطناعي في 2026: المشهد والاتجاهات
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
## ملخص تنفيذي
## 1. Introduction to AI Agents
In 2025, Artificial Intelligence (AI) agents are at the forefront of innovation across various industries. As intelligent systems that can perform tasks typically requiring human cognition, AI agents are paving the way for significant advancements in operational efficiency, decision-making, and overall productivity within sectors like Human Resources (HR) and Finance. This report aims to detail the rise of AI agents, their frameworks, applications, and potential implications on the workforce.
## أبرز الاتجاهات
- **استخدام الأدوات والتنسيق** — …
- **التبني المؤسسي** — …
## 2. Benefits of AI Agents
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
## الآثار
- **Task Automation**: AI agents can carry out repetitive tasks such as data entry, scheduling, and payroll processing without human intervention, greatly reducing the time and resources spent on these activities.
- **Improved Efficiency**: By quickly processing large datasets and performing analyses that would take humans significantly longer, AI agents enhance operational efficiency. This allows teams to focus on strategic tasks that require higher-level thinking.
- **Enhanced Decision-Making**: AI agents can analyze trends and patterns in data, provide insights, and even suggest actions, helping stakeholders make informed decisions based on factual data rather than intuition alone.
## 3. Popular AI Agent Frameworks
Several frameworks have emerged to facilitate the development of AI agents, each with its own unique features and capabilities. Some of the most popular frameworks include:
- **Autogen**: A framework designed to streamline the development of AI agents through automation of code generation.
- **Semantic Kernel**: Focuses on natural language processing and understanding, enabling agents to comprehend user intentions better.
- **Promptflow**: Provides tools for developers to create conversational agents that can navigate complex interactions seamlessly.
- **Langchain**: Specializes in leveraging various APIs to ensure agents can access and utilize external data effectively.
- **CrewAI**: Aimed at collaborative environments, CrewAI strengthens teamwork by facilitating communication through AI-driven insights.
- **MemGPT**: Combines memory-optimized architectures with generative capabilities, allowing for more personalized interactions with users.
These frameworks empower developers to build versatile and intelligent agents that can engage users, perform advanced analytics, and execute various tasks aligned with organizational goals.
## 4. AI Agents in Human Resources
AI agents are revolutionizing HR practices by automating and optimizing key functions:
- **Recruiting**: AI agents can screen resumes, schedule interviews, and even conduct initial assessments, thus accelerating the hiring process while minimizing biases.
- **Succession Planning**: AI systems analyze employee performance data and potential, helping organizations identify future leaders and plan appropriate training.
- **Employee Engagement**: Chatbots powered by AI can facilitate feedback loops between employees and management, promoting an open culture and addressing concerns promptly.
As AI continues to evolve, HR departments leveraging these agents can realize substantial improvements in both efficiency and employee satisfaction.
## 5. AI Agents in Finance
The finance sector is seeing extensive integration of AI agents that enhance financial practices:
- **Expense Tracking**: Automated systems manage and monitor expenses, flagging anomalies and offering recommendations based on spending patterns.
- **Risk Assessment**: AI models assess credit risk and uncover potential fraud by analyzing transaction data and behavioral patterns.
- **Investment Decisions**: AI agents provide stock predictions and analytics based on historical data and current market conditions, empowering investors with informative insights.
The incorporation of AI agents into finance is fostering a more responsive and risk-aware financial landscape.
## 6. Market Trends and Investments
The growth of AI agents has attracted significant investment, especially amidst the rising popularity of chatbots and generative AI technologies. Companies and entrepreneurs are eager to explore the potential of these systems, recognizing their ability to streamline operations and improve customer engagement.
Conversely, corporations like Microsoft are taking strides to integrate AI agents into their product offerings, with enhancements to their Copilot 365 applications. This strategic move emphasizes the importance of AI literacy in the modern workplace and indicates the stabilizing of AI agents as essential business tools.
## 7. Future Predictions and Implications
Experts predict that AI agents will transform essential aspects of work life. As we look toward the future, several anticipated changes include:
- Enhanced integration of AI agents across all business functions, creating interconnected systems that leverage data from various departmental silos for comprehensive decision-making.
- Continued advancement of AI technologies, resulting in smarter, more adaptable agents capable of learning and evolving from user interactions.
- Increased regulatory scrutiny to ensure ethical use, especially concerning data privacy and employee surveillance as AI agents become more prevalent.
To stay competitive and harness the full potential of AI agents, organizations must remain vigilant about latest developments in AI technology and consider continuous learning and adaptation in their strategic planning.
## 8. Conclusion
The emergence of AI agents is undeniably reshaping the workplace landscape in 5. With their ability to automate tasks, enhance efficiency, and improve decision-making, AI agents are critical in driving operational success. Organizations must embrace and adapt to AI developments to thrive in an increasingly digital business environment.
```
</CodeGroup>
سيكون الملف الفعلي أطول ويعكس نتائج بحث مباشرة.
</CodeGroup>
</Step>
</Steps>
## كيف يترابط هذا
1. **Flow** — يشغّل `LatestAiFlow` أولًا `prepare_topic` ثم `run_research` ثم `summarize`. الحالة (`topic`، `report`) على Flow.
2. **الطاقم** — يشغّل `ResearchCrew` مهمة واحدة بوكيل واحد: الباحث يستخدم **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).
<Check>
أصبح لديك Flow كامل مع طاقم وكيل وتقرير محفوظ — قاعدة قوية لإضافة خطوات أو طواقم أو أدوات.
تهانينا!
لقد أعددت مشروع طاقمك بنجاح وأنت جاهز للبدء في بناء سير العمل الوكيلي الخاص بك!
</Check>
### اتساق التسمية
### ملاحظة حول اتساق التسمية
يجب أن تطابق مفاتيح YAML (`researcher`، `research_task`) أسماء الدوال في صف `@CrewBase`. راجع [Crews](/ar/concepts/crews) لنمط الديكورات الكامل.
يجب أن تتطابق الأسماء التي تستخدمها في ملفات YAML (`agents.yaml` و`tasks.yaml`) مع أسماء الدوال في كود Python الخاص بك.
على سبيل المثال، يمكنك الإشارة إلى الوكيل لمهام محددة من ملف `tasks.yaml`.
يتيح اتساق التسمية هذا لـ CrewAI ربط تكويناتك بكودك تلقائياً؛ وإلا فلن تتعرف مهمتك على المرجع بشكل صحيح.
## النشر
ادفع Flow إلى **[CrewAI AMP](https://app.crewai.com)** بعد أن يعمل محليًا ويكون المشروع في مستودع **GitHub**. من جذر المشروع:
<CodeGroup>
```bash المصادقة
crewai login
```
```bash إنشاء نشر
crewai deploy create
```
```bash الحالة والسجلات
crewai deploy status
crewai deploy logs
```
```bash إرسال التحديثات بعد تغيير الكود
crewai deploy push
```
```bash عرض النشرات أو حذفها
crewai deploy list
crewai deploy remove <deployment_id>
```
</CodeGroup>
#### أمثلة على المراجع
<Tip>
غالبًا ما يستغرق **النشر الأول حوالي دقيقة**. المتطلبات الكاملة ومسار الواجهة الويب في [النشر على AMP](/ar/enterprise/guides/deploy-to-amp).
لاحظ كيف نستخدم نفس الاسم للوكيل في ملف `agents.yaml`
(`email_summarizer`) واسم الدالة في ملف `crew.py`
(`email_summarizer`).
</Tip>
```yaml agents.yaml
email_summarizer:
role: >
Email Summarizer
goal: >
Summarize emails into a concise and clear summary
backstory: >
You will create a 5 bullet point summary of the report
llm: provider/model-id # Add your choice of model here
```
<Tip>
لاحظ كيف نستخدم نفس الاسم للمهمة في ملف `tasks.yaml`
(`email_summarizer_task`) واسم الدالة في ملف `crew.py`
(`email_summarizer_task`).
</Tip>
```yaml tasks.yaml
email_summarizer_task:
description: >
Summarize the email into a 5 bullet point summary
expected_output: >
A 5 bullet point summary of the email
agent: email_summarizer
context:
- reporting_task
- research_task
```
## نشر طاقمك
أسهل طريقة لنشر طاقمك في الإنتاج هي من خلال [CrewAI AMP](http://app.crewai.com).
شاهد هذا الفيديو التعليمي لعرض خطوة بخطوة لنشر طاقمك على [CrewAI AMP](http://app.crewai.com) باستخدام CLI.
<iframe
className="w-full aspect-video rounded-xl"
src="https://www.youtube.com/embed/3EqSV-CYDZA"
title="CrewAI Deployment Guide"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
<CardGroup cols={2}>
<Card title="دليل النشر" icon="book" href="/ar/enterprise/guides/deploy-to-amp">
النشر على AMP خطوة بخطوة (CLI ولوحة التحكم).
<Card title="النشر على المؤسسة" icon="rocket" href="http://app.crewai.com">
ابدأ مع CrewAI AMP وانشر طاقمك في بيئة إنتاج
بنقرات قليلة فقط.
</Card>
<Card
title="المجتمع"
title="انضم إلى المجتمع"
icon="comments"
href="https://community.crewai.com"
>
ناقش الأفكار وشارك مشاريعك وتواصل مع مطوري CrewAI.
انضم إلى مجتمعنا مفتوح المصدر لمناقشة الأفكار ومشاركة مشاريعك والتواصل
مع مطورين آخرين لـ CrewAI.
</Card>
</CardGroup>

File diff suppressed because it is too large Load Diff

View File

@@ -4,78 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="Apr 06, 2026">
## v1.14.0a3
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a3)
## What's Changed
### Documentation
- Update changelog and version for v1.14.0a2
## Contributors
@joaomdmoura
</Update>
<Update label="Apr 06, 2026">
## v1.14.0a2
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a2)
Release 1.14.0a2
</Update>
<Update label="Apr 02, 2026">
## v1.13.0
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0)
## What's Changed
### Features
- Add RuntimeState RootModel for unified state serialization
- Enhance event listener with new telemetry spans for skill and memory events
- Add A2UI extension with v0.8/v0.9 support, schemas, and docs
- Emit token usage data in LLMCallCompletedEvent
- Auto-update deployment test repo during release
- Improve enterprise release resilience and UX
### Bug Fixes
- Add tool repository credentials to crewai install
- Add tool repository credentials to uv build in tool publish
- Pass fingerprint metadata via config instead of tool args
- Handle GPT-5.x models not supporting the `stop` API parameter
- Add GPT-5 and o-series to multimodal vision prefixes
- Bust uv cache for freshly published packages in enterprise release
- Cap lancedb below 0.30.1 for Windows compatibility
- Fix RBAC permission levels to match actual UI options
- Fix inaccuracies in agent-capabilities across all languages
### Documentation
- Add coding agent skills demo video to getting started pages
- Add comprehensive SSO configuration guide
- Add comprehensive RBAC permissions matrix and deployment guide
- Update changelog and version for v1.13.0
### Performance
- Reduce framework overhead with lazy event bus, skip tracing when disabled
### Refactoring
- Convert Flow to Pydantic BaseModel
- Convert LLM classes to Pydantic BaseModel
- Replace InstanceOf[T] with plain type annotations
- Remove unused third_party LLM directory
## Contributors
@alex-clawd, @dependabot[bot], @greysonlalonde, @iris-clawd, @joaomdmoura, @lorenzejay, @lucasgomide, @thiagomoretto
</Update>
<Update label="Apr 02, 2026">
## v1.13.0a7

View File

@@ -1,236 +0,0 @@
---
title: Checkpointing
description: Automatically save execution state so crews, flows, and agents can resume after failures.
icon: floppy-disk
mode: "wide"
---
<Warning>
Checkpointing is in early release. APIs may change in future versions.
</Warning>
## Overview
Checkpointing automatically saves execution state during a run. If a crew, flow, or agent fails mid-execution, you can restore from the last checkpoint and resume without re-running completed work.
## Quick Start
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=True, # uses defaults: ./.checkpoints, on task_completed
)
result = crew.kickoff()
```
Checkpoint files are written to `./.checkpoints/` after each completed task.
## Configuration
Use `CheckpointConfig` for full control:
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
on_events=["task_completed", "crew_kickoff_completed"],
max_checkpoints=5,
),
)
```
### CheckpointConfig Fields
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `directory` | `str` | `"./.checkpoints"` | Filesystem path for checkpoint files |
| `on_events` | `list[str]` | `["task_completed"]` | Event types that trigger a checkpoint |
| `provider` | `BaseProvider` | `JsonProvider()` | Storage backend |
| `max_checkpoints` | `int \| None` | `None` | Max files to keep; oldest pruned first |
### Inheritance and Opt-Out
The `checkpoint` field on Crew, Flow, and Agent accepts `CheckpointConfig`, `True`, `False`, or `None`:
| Value | Behavior |
|:------|:---------|
| `None` (default) | Inherit from parent. An agent inherits its crew's config. |
| `True` | Enable with defaults. |
| `False` | Explicit opt-out. Stops inheritance from parent. |
| `CheckpointConfig(...)` | Custom configuration. |
```python
crew = Crew(
agents=[
Agent(role="Researcher", ...), # inherits crew's checkpoint
Agent(role="Writer", ..., checkpoint=False), # opted out, no checkpoints
],
tasks=[...],
checkpoint=True,
)
```
## Resuming from a Checkpoint
```python
# Restore and resume
crew = Crew.from_checkpoint("./my_checkpoints/20260407T120000_abc123.json")
result = crew.kickoff() # picks up from last completed task
```
The restored crew skips already-completed tasks and resumes from the first incomplete one.
## Works on Crew, Flow, and Agent
### Crew
```python
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task, review_task],
checkpoint=CheckpointConfig(directory="./crew_cp"),
)
```
Default trigger: `task_completed` (one checkpoint per finished task).
### Flow
```python
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig
class MyFlow(Flow):
@start()
def step_one(self):
return "data"
@listen(step_one)
def step_two(self, data):
return process(data)
flow = MyFlow(
checkpoint=CheckpointConfig(
directory="./flow_cp",
on_events=["method_execution_finished"],
),
)
result = flow.kickoff()
# Resume
flow = MyFlow.from_checkpoint("./flow_cp/20260407T120000_abc123.json")
result = flow.kickoff()
```
### Agent
```python
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
checkpoint=CheckpointConfig(
directory="./agent_cp",
on_events=["lite_agent_execution_completed"],
),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])
```
## Storage Providers
CrewAI ships with two checkpoint storage providers.
### JsonProvider (default)
Writes each checkpoint as a separate JSON file. Simple, human-readable, easy to inspect.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
provider=JsonProvider(), # this is the default
max_checkpoints=5, # prunes oldest files
),
)
```
Files are named `<timestamp>_<uuid>.json` inside the directory.
### SqliteProvider
Stores all checkpoints in a single SQLite database file. Better for high-frequency checkpointing and avoids many small files.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./.checkpoints.db",
provider=SqliteProvider(max_checkpoints=50),
),
)
```
`SqliteProvider` accepts its own `max_checkpoints` parameter that prunes old rows via SQL. WAL journal mode is enabled for concurrent read access.
<Note>
When using `SqliteProvider`, the `directory` field is the database file path, not a directory. The `max_checkpoints` on `CheckpointConfig` controls filesystem pruning (for `JsonProvider`), while `SqliteProvider.max_checkpoints` controls row pruning in the database.
</Note>
## Event Types
The `on_events` field accepts any combination of event type strings. Common choices:
| Use Case | Events |
|:---------|:-------|
| After each task (Crew) | `["task_completed"]` |
| After each flow method | `["method_execution_finished"]` |
| After agent execution | `["agent_execution_completed"]`, `["lite_agent_execution_completed"]` |
| On crew completion only | `["crew_kickoff_completed"]` |
| After every LLM call | `["llm_call_completed"]` |
| On everything | `["*"]` |
<Warning>
Using `["*"]` or high-frequency events like `llm_call_completed` will write many checkpoint files and may impact performance. Use `max_checkpoints` to limit disk usage.
</Warning>
## Manual Checkpointing
For full control, register your own event handler and call `state.checkpoint()` directly:
```python
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
# Sync handler
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source, event, state):
path = state.checkpoint("./my_checkpoints")
print(f"Saved checkpoint: {path}")
# Async handler
@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source, event, state):
path = await state.acheckpoint("./my_checkpoints")
print(f"Saved checkpoint: {path}")
```
The `state` argument is the `RuntimeState` passed automatically by the event bus when your handler accepts 3 parameters. You can register handlers on any event type listed in the [Event Listeners](/en/concepts/event-listener) documentation.
Checkpointing is best-effort: if a checkpoint write fails, the error is logged but execution continues uninterrupted.

View File

@@ -106,7 +106,7 @@ The CLI automatically detects your project type from `pyproject.toml` and builds
```
<Tip>
The first deployment typically takes around 1 minute.
The first deployment typically takes 10-15 minutes as it builds the container images. Subsequent deployments are much faster.
</Tip>
</Step>
@@ -188,7 +188,7 @@ You need to push your crew to a GitHub repository. If you haven't created a crew
1. Click the "Deploy" button to start the deployment process
2. You can monitor the progress through the progress bar
3. The first deployment typically takes around 1 minute
3. The first deployment typically takes around 10-15 minutes; subsequent deployments will be faster
<Frame>
![Deploy Progress](/images/enterprise/deploy-progress.png)

View File

@@ -1,132 +0,0 @@
---
title: "Training Crews"
description: "Train your deployed crews directly from the CrewAI AMP platform to improve agent performance over time"
icon: "dumbbell"
mode: "wide"
---
Training lets you improve crew performance by running iterative training sessions directly from the **Training** tab in CrewAI AMP. The platform uses **auto-train mode** — it handles the iterative process automatically, unlike CLI training which requires interactive human feedback per iteration.
After training completes, CrewAI evaluates agent outputs and consolidates feedback into actionable suggestions for each agent. These suggestions are then applied to future crew runs to improve output quality.
<Tip>
For details on how CrewAI training works under the hood, see the [Training Concepts](/en/concepts/training) page.
</Tip>
## Prerequisites
<CardGroup cols={2}>
<Card title="Active deployment" icon="rocket">
You need a CrewAI AMP account with an active deployment in **Ready** status (Crew type).
</Card>
<Card title="Run permission" icon="key">
Your account must have run permission for the deployment you want to train.
</Card>
</CardGroup>
## How to train a crew
<Steps>
<Step title="Open the Training tab">
Navigate to **Deployments**, click your deployment, then select the **Training** tab.
</Step>
<Step title="Enter a training name">
Provide a **Training Name** — this becomes the `.pkl` filename used to store training results. For example, "Expert Mode Training" produces `expert_mode_training.pkl`.
</Step>
<Step title="Fill in the crew inputs">
Enter the crew's input fields. These are the same inputs you'd provide for a normal kickoff — they're dynamically loaded based on your crew's configuration.
</Step>
<Step title="Start training">
Click **Train Crew**. The button changes to "Training..." with a spinner while the process runs.
Behind the scenes:
- A training record is created for your deployment
- The platform calls the deployment's auto-train endpoint
- The crew runs its iterations automatically — no manual feedback required
</Step>
<Step title="Monitor progress">
The **Current Training Status** panel displays:
- **Status** — Current state of the training run
- **Nº Iterations** — Number of training iterations configured
- **Filename** — The `.pkl` file being generated
- **Started At** — When training began
- **Training Inputs** — The inputs you provided
</Step>
</Steps>
## Understanding training results
Once training completes, you'll see per-agent result cards with the following information:
- **Agent Role** — The name/role of the agent in your crew
- **Final Quality** — A score from 0 to 10 evaluating the agent's output quality
- **Final Summary** — A summary of the agent's performance during training
- **Suggestions** — Actionable recommendations for improving the agent's behavior
### Editing suggestions
You can refine the suggestions for any agent:
<Steps>
<Step title="Click Edit">
On any agent's result card, click the **Edit** button next to the suggestions.
</Step>
<Step title="Modify suggestions">
Update the suggestions text to better reflect the improvements you want.
</Step>
<Step title="Save changes">
Click **Save**. The edited suggestions sync back to the deployment and are used in all future runs.
</Step>
</Steps>
## Using trained data
To apply training results to your crew:
1. Note the **Training Filename** (the `.pkl` file) from your completed training session.
2. Specify this filename in your deployment's kickoff or run configuration.
3. The crew automatically loads the training file and applies the stored suggestions to each agent.
This means agents benefit from the feedback generated during training on every subsequent run.
## Previous trainings
The bottom of the Training tab displays a **history of all past training sessions** for the deployment. Use this to review previous training runs, compare results, or select a different training file to use.
## Error handling
If a training run fails, the status panel shows an error state along with a message describing what went wrong.
Common causes of training failures:
- **Deployment runtime not updated** — Ensure your deployment is running the latest version
- **Crew execution errors** — Issues within the crew's task logic or agent configuration
- **Network issues** — Connectivity problems between the platform and the deployment
## Limitations
<Info>
Keep these constraints in mind when planning your training workflow:
- **One active training at a time** per deployment — wait for the current run to finish before starting another
- **Auto-train mode only** — the platform does not support interactive per-iteration feedback like the CLI does
- **Training data is deployment-specific** — training results are tied to the specific deployment instance and version
</Info>
## Related resources
<CardGroup cols={3}>
<Card title="Training Concepts" icon="book" href="/en/concepts/training">
Learn how CrewAI training works under the hood.
</Card>
<Card title="Kickoff Crew" icon="play" href="/en/enterprise/guides/kickoff-crew">
Run your deployed crew from the AMP platform.
</Card>
<Card title="Deploy to AMP" icon="cloud-arrow-up" href="/en/enterprise/guides/deploy-to-amp">
Get your crew deployed and ready for training.
</Card>
</CardGroup>

View File

@@ -171,9 +171,6 @@ We recommend using the `YAML` template scaffolding for a structured approach to
```shell
uv add <package-name>
```
<Note>
As a supply-chain security measure, CrewAI's internal packages use `exclude-newer = "3 days"` in their `pyproject.toml` files. This means transitive dependencies pulled in by CrewAI won't resolve packages released less than 3 days ago. Your own direct dependencies are not affected by this policy. If you notice a transitive dependency is behind, you can pin the version you want explicitly in your project's dependencies.
</Note>
- To run your crew, execute the following command in the root of your project:
```bash
crewai run
@@ -207,8 +204,9 @@ For teams and organizations, CrewAI offers enterprise deployment options that el
## Next Steps
<CardGroup cols={2}>
<Card title="Quickstart: Flow + agent" icon="code" href="/en/quickstart">
Follow the quickstart to scaffold a Flow, run a one-agent crew, and produce a report.
<Card title="Build Your First Agent" icon="code" href="/en/quickstart">
Follow our quickstart guide to create your first CrewAI agent and get
hands-on experience.
</Card>
<Card
title="Join the Community"

View File

@@ -140,7 +140,7 @@ For any production-ready application, **start with a Flow**.
icon="bolt"
href="en/quickstart"
>
Scaffold a Flow, run a crew with one agent, and generate a report end to end.
Follow our quickstart guide to create your first CrewAI agent and get hands-on experience.
</Card>
<Card
title="Join the Community"

View File

@@ -1,6 +1,6 @@
---
title: Quickstart
description: Build your first CrewAI Flow in minutes — orchestration, state, and an agent crew that produces a real report.
description: Build your first AI agent with CrewAI in under 5 minutes.
icon: rocket
mode: "wide"
---
@@ -13,37 +13,39 @@ You can install it with `npx skills add crewaiinc/skills`
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
In this guide you will **create a Flow** that sets a research topic, runs a **crew with one agent** (a researcher using web search), and ends with a **markdown report** on disk. Flows are the recommended way to structure production apps: they own **state** and **execution order**, while **agents** do the work inside a crew step.
## Build your first CrewAI Agent
If you have not installed CrewAI yet, follow the [installation guide](/en/installation) first.
Let's create a simple crew that will help us `research` and `report` on the `latest AI developments` for a given topic or subject.
## Prerequisites
Before we proceed, make sure you have finished installing CrewAI.
If you haven't installed them yet, you can do so by following the [installation guide](/en/installation).
- Python environment and the CrewAI CLI (see [installation](/en/installation))
- An LLM configured with the right API keys — see [LLMs](/en/concepts/llms#setting-up-your-llm)
- A [Serper.dev](https://serper.dev/) API key (`SERPER_API_KEY`) for web search in this tutorial
## Build your first Flow
Follow the steps below to get Crewing! 🚣‍♂️
<Steps>
<Step title="Create a Flow project">
From your terminal, scaffold a Flow project (the folder name uses underscores, e.g. `latest_ai_flow`):
<Step title="Create your crew">
Create a new crew project by running the following command in your terminal.
This will create a new directory called `latest-ai-development` with the basic structure for your crew.
<CodeGroup>
```shell Terminal
crewai create flow latest-ai-flow
cd latest_ai_flow
crewai create crew latest-ai-development
```
</CodeGroup>
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="Navigate to your new crew project">
<CodeGroup>
```shell Terminal
cd latest_ai_development
```
</CodeGroup>
</Step>
<Step title="Modify your `agents.yaml` file">
<Tip>
You can also modify the agents as needed to fit your use case or copy and paste as is to your project.
Any variable interpolated in your `agents.yaml` and `tasks.yaml` files like `{topic}` will be replaced by the value of the variable in the `main.py` file.
</Tip>
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
# src/latest_ai_development/config/agents.yaml
researcher:
role: >
{topic} Senior Data Researcher
@@ -51,232 +53,336 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal
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.
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.
```
</Step>
<Step title="Configure one task in `tasks.yaml`">
<Step title="Modify your `tasks.yaml` file">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
# src/latest_ai_development/config/tasks.yaml
research_task:
description: >
Conduct thorough research about {topic}. Use web search to find current,
credible information. The current year is 2026.
Conduct a thorough research about {topic}
Make sure you find any interesting and relevant information given
the current year is 2025.
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.
A list with 10 bullet points of the most relevant information about {topic}
agent: researcher
output_file: output/report.md
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
output_file: report.md
```
</Step>
<Step title="Wire the crew class (`content_crew.py`)">
Point the generated crew at your YAML and attach `SerperDevTool` to the researcher.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
<Step title="Modify your `crew.py` file">
```python crew.py
# src/latest_ai_development/crew.py
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.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ResearchCrew:
"""Single-agent research crew used inside the Flow."""
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment 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]
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
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]
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]
output_file='output/report.md' # This is the file that will be contain the final report.
)
@crew
def crew(self) -> Crew:
"""Creates the LatestAiDevelopment crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=True,
)
```
</Step>
<Step title="[Optional] Add before and after crew functions">
```python crew.py
# src/latest_ai_development/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff
from crewai_tools import SerperDevTool
<Step title="Define the Flow in `main.py`">
Connect the crew to a Flow: a `@start()` step sets the topic in **state**, and a `@listen` step runs the crew. The tasks `output_file` still writes `output/report.md`.
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
```python main.py
# src/latest_ai_flow/main.py
from pydantic import BaseModel
@before_kickoff
def before_kickoff_function(self, inputs):
print(f"Before kickoff function with inputs: {inputs}")
return inputs # You can return the inputs or modify them as needed
from crewai.flow import Flow, listen, start
@after_kickoff
def after_kickoff_function(self, result):
print(f"After kickoff function with result: {result}")
return result # You can return the result or modify it as needed
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
class ResearchFlowState(BaseModel):
topic: str = ""
report: str = ""
class LatestAiFlow(Flow[ResearchFlowState]):
@start()
def prepare_topic(self, crewai_trigger_payload: dict | None = None):
if crewai_trigger_payload:
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
else:
self.state.topic = "AI Agents"
print(f"Topic: {self.state.topic}")
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("Research crew finished.")
@listen(run_research)
def summarize(self):
print("Report path: output/report.md")
def kickoff():
LatestAiFlow().kickoff()
def plot():
LatestAiFlow().plot()
if __name__ == "__main__":
kickoff()
# ... remaining code
```
<Tip>
If your package name differs from `latest_ai_flow`, change the import of `ResearchCrew` to match your projects module path.
</Tip>
</Step>
<Step title="Feel free to pass custom inputs to your crew">
For example, you can pass the `topic` input to your crew to customize the research and reporting.
```python main.py
#!/usr/bin/env python
# src/latest_ai_development/main.py
import sys
from latest_ai_development.crew import LatestAiDevelopmentCrew
def run():
"""
Run the crew.
"""
inputs = {
'topic': 'AI Agents'
}
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
```
</Step>
<Step title="Set your environment variables">
Before running your crew, make sure you have the following keys set as environment variables in your `.env` file:
- A [Serper.dev](https://serper.dev/) API key: `SERPER_API_KEY=YOUR_KEY_HERE`
- The configuration for your choice of model, such as an API key. See the
[LLM setup guide](/en/concepts/llms#setting-up-your-llm) to learn how to configure models from any provider.
</Step>
<Step title="Lock and install the dependencies">
- Lock the dependencies and install them by using the CLI command:
<CodeGroup>
```shell Terminal
crewai install
```
</CodeGroup>
- If you have additional packages that you want to install, you can do so by running:
<CodeGroup>
```shell Terminal
uv add <package-name>
```
</CodeGroup>
</Step>
<Step title="Run your crew">
- To run your crew, execute the following command in the root of your project:
<CodeGroup>
```bash Terminal
crewai run
```
</CodeGroup>
</Step>
<Step title="Set environment variables">
In `.env` at the project root, set:
<Step title="Enterprise Alternative: Create in Crew Studio">
For CrewAI AMP users, you can create the same crew without writing code:
- `SERPER_API_KEY` — from [Serper.dev](https://serper.dev/)
- Your model provider keys as required — see [LLM setup](/en/concepts/llms#setting-up-your-llm)
1. Log in to your CrewAI AMP account (create a free account at [app.crewai.com](https://app.crewai.com))
2. Open Crew Studio
3. Type what is the automation you're trying to build
4. Create your tasks visually and connect them in sequence
5. Configure your inputs and click "Download Code" or "Deploy"
![Crew Studio Quickstart](/images/enterprise/crew-studio-interface.png)
<Card title="Try CrewAI AMP" icon="rocket" href="https://app.crewai.com">
Start your free account at CrewAI AMP
</Card>
</Step>
<Step title="View your final report">
You should see the output in the console and the `report.md` file should be created in the root of your project with the final report.
<Step title="Install and run">
<CodeGroup>
```shell Terminal
crewai install
crewai run
```
</CodeGroup>
`crewai run` executes the Flow entrypoint defined in your project (same command as for crews; project type is `"flow"` in `pyproject.toml`).
</Step>
<Step title="Check the output">
You should see logs from the Flow and the crew. Open **`output/report.md`** for the generated report (excerpt):
Here's an example of what the report should look like:
<CodeGroup>
```markdown output/report.md
# AI Agents in 2026: Landscape and Trends
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
## Executive summary
## 1. Introduction to AI Agents
In 2025, Artificial Intelligence (AI) agents are at the forefront of innovation across various industries. As intelligent systems that can perform tasks typically requiring human cognition, AI agents are paving the way for significant advancements in operational efficiency, decision-making, and overall productivity within sectors like Human Resources (HR) and Finance. This report aims to detail the rise of AI agents, their frameworks, applications, and potential implications on the workforce.
## Key trends
- **Tool use and orchestration** — …
- **Enterprise adoption** — …
## 2. Benefits of AI Agents
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
## Implications
- **Task Automation**: AI agents can carry out repetitive tasks such as data entry, scheduling, and payroll processing without human intervention, greatly reducing the time and resources spent on these activities.
- **Improved Efficiency**: By quickly processing large datasets and performing analyses that would take humans significantly longer, AI agents enhance operational efficiency. This allows teams to focus on strategic tasks that require higher-level thinking.
- **Enhanced Decision-Making**: AI agents can analyze trends and patterns in data, provide insights, and even suggest actions, helping stakeholders make informed decisions based on factual data rather than intuition alone.
## 3. Popular AI Agent Frameworks
Several frameworks have emerged to facilitate the development of AI agents, each with its own unique features and capabilities. Some of the most popular frameworks include:
- **Autogen**: A framework designed to streamline the development of AI agents through automation of code generation.
- **Semantic Kernel**: Focuses on natural language processing and understanding, enabling agents to comprehend user intentions better.
- **Promptflow**: Provides tools for developers to create conversational agents that can navigate complex interactions seamlessly.
- **Langchain**: Specializes in leveraging various APIs to ensure agents can access and utilize external data effectively.
- **CrewAI**: Aimed at collaborative environments, CrewAI strengthens teamwork by facilitating communication through AI-driven insights.
- **MemGPT**: Combines memory-optimized architectures with generative capabilities, allowing for more personalized interactions with users.
These frameworks empower developers to build versatile and intelligent agents that can engage users, perform advanced analytics, and execute various tasks aligned with organizational goals.
## 4. AI Agents in Human Resources
AI agents are revolutionizing HR practices by automating and optimizing key functions:
- **Recruiting**: AI agents can screen resumes, schedule interviews, and even conduct initial assessments, thus accelerating the hiring process while minimizing biases.
- **Succession Planning**: AI systems analyze employee performance data and potential, helping organizations identify future leaders and plan appropriate training.
- **Employee Engagement**: Chatbots powered by AI can facilitate feedback loops between employees and management, promoting an open culture and addressing concerns promptly.
As AI continues to evolve, HR departments leveraging these agents can realize substantial improvements in both efficiency and employee satisfaction.
## 5. AI Agents in Finance
The finance sector is seeing extensive integration of AI agents that enhance financial practices:
- **Expense Tracking**: Automated systems manage and monitor expenses, flagging anomalies and offering recommendations based on spending patterns.
- **Risk Assessment**: AI models assess credit risk and uncover potential fraud by analyzing transaction data and behavioral patterns.
- **Investment Decisions**: AI agents provide stock predictions and analytics based on historical data and current market conditions, empowering investors with informative insights.
The incorporation of AI agents into finance is fostering a more responsive and risk-aware financial landscape.
## 6. Market Trends and Investments
The growth of AI agents has attracted significant investment, especially amidst the rising popularity of chatbots and generative AI technologies. Companies and entrepreneurs are eager to explore the potential of these systems, recognizing their ability to streamline operations and improve customer engagement.
Conversely, corporations like Microsoft are taking strides to integrate AI agents into their product offerings, with enhancements to their Copilot 365 applications. This strategic move emphasizes the importance of AI literacy in the modern workplace and indicates the stabilizing of AI agents as essential business tools.
## 7. Future Predictions and Implications
Experts predict that AI agents will transform essential aspects of work life. As we look toward the future, several anticipated changes include:
- Enhanced integration of AI agents across all business functions, creating interconnected systems that leverage data from various departmental silos for comprehensive decision-making.
- Continued advancement of AI technologies, resulting in smarter, more adaptable agents capable of learning and evolving from user interactions.
- Increased regulatory scrutiny to ensure ethical use, especially concerning data privacy and employee surveillance as AI agents become more prevalent.
To stay competitive and harness the full potential of AI agents, organizations must remain vigilant about latest developments in AI technology and consider continuous learning and adaptation in their strategic planning.
## 8. Conclusion
The emergence of AI agents is undeniably reshaping the workplace landscape in 5. With their ability to automate tasks, enhance efficiency, and improve decision-making, AI agents are critical in driving operational success. Organizations must embrace and adapt to AI developments to thrive in an increasingly digital business environment.
```
</CodeGroup>
Your actual file will be longer and reflect live search results.
</CodeGroup>
</Step>
</Steps>
## 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.
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).
<Check>
You now have an end-to-end Flow with an agent crew and a saved report — a solid base to add more steps, crews, or tools.
Congratulations!
You have successfully set up your crew project and are ready to start building your own agentic workflows!
</Check>
### Naming consistency
### Note on Consistency in Naming
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 you use in your YAML files (`agents.yaml` and `tasks.yaml`) should match the method names in your Python code.
For example, you can reference the agent for specific tasks from `tasks.yaml` file.
This naming consistency allows CrewAI to automatically link your configurations with your code; otherwise, your task won't recognize the reference properly.
## Deploying
Push your Flow to **[CrewAI AMP](https://app.crewai.com)** once it runs locally and your project is in a **GitHub** repository. From the project root:
<CodeGroup>
```bash Authenticate
crewai login
```
```bash Create deployment
crewai deploy create
```
```bash Check status & logs
crewai deploy status
crewai deploy logs
```
```bash Ship updates after you change code
crewai deploy push
```
```bash List or remove deployments
crewai deploy list
crewai deploy remove <deployment_id>
```
</CodeGroup>
#### Example References
<Tip>
The first deploy usually takes **around 1 minute**. Full prerequisites and the web UI flow are in [Deploy to AMP](/en/enterprise/guides/deploy-to-amp).
Note how we use the same name for the agent in the `agents.yaml`
(`email_summarizer`) file as the method name in the `crew.py`
(`email_summarizer`) file.
</Tip>
```yaml agents.yaml
email_summarizer:
role: >
Email Summarizer
goal: >
Summarize emails into a concise and clear summary
backstory: >
You will create a 5 bullet point summary of the report
llm: provider/model-id # Add your choice of model here
```
<Tip>
Note how we use the same name for the task in the `tasks.yaml`
(`email_summarizer_task`) file as the method name in the `crew.py`
(`email_summarizer_task`) file.
</Tip>
```yaml tasks.yaml
email_summarizer_task:
description: >
Summarize the email into a 5 bullet point summary
expected_output: >
A 5 bullet point summary of the email
agent: email_summarizer
context:
- reporting_task
- research_task
```
## Deploying Your Crew
The easiest way to deploy your crew to production is through [CrewAI AMP](http://app.crewai.com).
Watch this video tutorial for a step-by-step demonstration of deploying your crew to [CrewAI AMP](http://app.crewai.com) using the CLI.
<iframe
className="w-full aspect-video rounded-xl"
src="https://www.youtube.com/embed/3EqSV-CYDZA"
title="CrewAI Deployment Guide"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
<CardGroup cols={2}>
<Card title="Deploy guide" icon="book" href="/en/enterprise/guides/deploy-to-amp">
Step-by-step AMP deployment (CLI and dashboard).
<Card title="Deploy on Enterprise" icon="rocket" href="http://app.crewai.com">
Get started with CrewAI AMP and deploy your crew in a production environment
with just a few clicks.
</Card>
<Card
title="Join the Community"
icon="comments"
href="https://community.crewai.com"
>
Discuss ideas, share projects, and connect with other CrewAI developers.
Join our open source community to discuss ideas, share your projects, and
connect with other CrewAI developers.
</Card>
</CardGroup>

View File

@@ -4,86 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
icon: "clock"
mode: "wide"
---
<Update label="2026년 4월 6일">
## v1.14.0a3
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a3)
## 변경 사항
### 문서
- v1.14.0a2의 변경 로그 및 버전 업데이트
## 기여자
@joaomdmoura
</Update>
<Update label="2026년 4월 6일">
## v1.14.0a2
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a2)
## 릴리스 1.14.0a2
### 지침:
- 모든 섹션 제목과 설명을 자연스럽게 번역합니다.
- 마크다운 형식을 그대로 유지합니다 (##, ###, -, 등).
- 모든 고유 명사, 코드 식별자, 클래스 이름 및 기술 용어는 변경하지 않습니다.
(예: "CrewAI", "LiteAgent", "ChromaDB", "MCP", "@username")
- ## 기여자 섹션과 GitHub 사용자 이름은 변경하지 않습니다.
- 내용을 추가하거나 제거하지 않고 오직 번역만 합니다.
</Update>
<Update label="2026년 4월 2일">
## v1.13.0
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0)
## 변경 사항
### 기능
- 통합 상태 직렬화를 위한 RuntimeState RootModel 추가
- 기술 및 메모리 이벤트에 대한 새로운 텔레메트리 스팬으로 이벤트 리스너 강화
- v0.8/v0.9 지원, 스키마 및 문서가 포함된 A2UI 확장 추가
- LLMCallCompletedEvent에서 토큰 사용 데이터 방출
- 릴리스 중 배포 테스트 리포 자동 업데이트
- 기업 릴리스의 복원력 및 사용자 경험 개선
### 버그 수정
- crewai 설치에 도구 리포지토리 자격 증명 추가
- 도구 게시의 uv 빌드에 도구 리포지토리 자격 증명 추가
- 도구 인수 대신 구성으로 지문 메타데이터 전달
- `stop` API 매개변수를 지원하지 않는 GPT-5.x 모델 처리
- 멀티모달 비전 접두사에 GPT-5 및 o-series 추가
- 기업 릴리스에서 새로 게시된 패키지에 대한 uv 캐시 무효화
- Windows 호환성을 위해 lancedb를 0.30.1 이하로 제한
- 실제 UI 옵션과 일치하도록 RBAC 권한 수준 수정
- 모든 언어에서 에이전트 기능의 부정확성 수정
### 문서
- 시작하기 페이지에 코딩 에이전트 기술 데모 비디오 추가
- 포괄적인 SSO 구성 가이드 추가
- 포괄적인 RBAC 권한 매트릭스 및 배포 가이드 추가
- v1.13.0에 대한 변경 로그 및 버전 업데이트
### 성능
- 비활성화 시 추적 건너뛰기와 함께 지연 이벤트 버스를 사용하여 프레임워크 오버헤드 감소
### 리팩토링
- Flow를 Pydantic BaseModel로 변환
- LLM 클래스를 Pydantic BaseModel로 변환
- InstanceOf[T]를 일반 타입 주석으로 교체
- 사용되지 않는 third_party LLM 디렉토리 제거
## 기여자
@alex-clawd, @dependabot[bot], @greysonlalonde, @iris-clawd, @joaomdmoura, @lorenzejay, @lucasgomide, @thiagomoretto
</Update>
<Update label="2026년 4월 2일">
## v1.13.0a7

View File

@@ -1,232 +0,0 @@
---
title: Checkpointing
description: 실행 상태를 자동으로 저장하여 크루, 플로우, 에이전트가 실패 후 재개할 수 있습니다.
icon: floppy-disk
mode: "wide"
---
<Warning>
체크포인팅은 초기 릴리스 단계입니다. API는 향후 버전에서 변경될 수 있습니다.
</Warning>
## 개요
체크포인팅은 실행 중 자동으로 실행 상태를 저장합니다. 크루, 플로우 또는 에이전트가 실행 도중 실패하면 마지막 체크포인트에서 복원하여 이미 완료된 작업을 다시 실행하지 않고 재개할 수 있습니다.
## 빠른 시작
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=True, # 기본값 사용: ./.checkpoints, task_completed 이벤트
)
result = crew.kickoff()
```
각 태스크가 완료된 후 `./.checkpoints/`에 체크포인트 파일이 기록됩니다.
## 설정
`CheckpointConfig`를 사용하여 세부 설정을 제어합니다:
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
on_events=["task_completed", "crew_kickoff_completed"],
max_checkpoints=5,
),
)
```
### CheckpointConfig 필드
| 필드 | 타입 | 기본값 | 설명 |
|:-----|:-----|:-------|:-----|
| `directory` | `str` | `"./.checkpoints"` | 체크포인트 파일 경로 |
| `on_events` | `list[str]` | `["task_completed"]` | 체크포인트를 트리거하는 이벤트 타입 |
| `provider` | `BaseProvider` | `JsonProvider()` | 스토리지 백엔드 |
| `max_checkpoints` | `int \| None` | `None` | 보관할 최대 파일 수; 오래된 것부터 삭제 |
### 상속 및 옵트아웃
Crew, Flow, Agent의 `checkpoint` 필드는 `CheckpointConfig`, `True`, `False`, `None`을 받습니다:
| 값 | 동작 |
|:---|:-----|
| `None` (기본값) | 부모에서 상속. 에이전트는 크루의 설정을 상속합니다. |
| `True` | 기본값으로 활성화. |
| `False` | 명시적 옵트아웃. 부모 상속을 중단합니다. |
| `CheckpointConfig(...)` | 사용자 정의 설정. |
```python
crew = Crew(
agents=[
Agent(role="Researcher", ...), # 크루의 checkpoint 상속
Agent(role="Writer", ..., checkpoint=False), # 옵트아웃, 체크포인트 없음
],
tasks=[...],
checkpoint=True,
)
```
## 체크포인트에서 재개
```python
# 복원 및 재개
crew = Crew.from_checkpoint("./my_checkpoints/20260407T120000_abc123.json")
result = crew.kickoff() # 마지막으로 완료된 태스크부터 재개
```
복원된 크루는 이미 완료된 태스크를 건너뛰고 첫 번째 미완료 태스크부터 재개합니다.
## Crew, Flow, Agent에서 사용 가능
### Crew
```python
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task, review_task],
checkpoint=CheckpointConfig(directory="./crew_cp"),
)
```
기본 트리거: `task_completed` (완료된 태스크당 하나의 체크포인트).
### Flow
```python
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig
class MyFlow(Flow):
@start()
def step_one(self):
return "data"
@listen(step_one)
def step_two(self, data):
return process(data)
flow = MyFlow(
checkpoint=CheckpointConfig(
directory="./flow_cp",
on_events=["method_execution_finished"],
),
)
result = flow.kickoff()
# 재개
flow = MyFlow.from_checkpoint("./flow_cp/20260407T120000_abc123.json")
result = flow.kickoff()
```
### Agent
```python
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
checkpoint=CheckpointConfig(
directory="./agent_cp",
on_events=["lite_agent_execution_completed"],
),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])
```
## 스토리지 프로바이더
CrewAI는 두 가지 체크포인트 스토리지 프로바이더를 제공합니다.
### JsonProvider (기본값)
각 체크포인트를 별도의 JSON 파일로 저장합니다.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
provider=JsonProvider(),
max_checkpoints=5,
),
)
```
### SqliteProvider
모든 체크포인트를 단일 SQLite 데이터베이스 파일에 저장합니다.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./.checkpoints.db",
provider=SqliteProvider(max_checkpoints=50),
),
)
```
<Note>
`SqliteProvider`를 사용할 때 `directory` 필드는 디렉토리가 아닌 데이터베이스 파일 경로입니다.
</Note>
## 이벤트 타입
`on_events` 필드는 이벤트 타입 문자열의 조합을 받습니다. 일반적인 선택:
| 사용 사례 | 이벤트 |
|:----------|:-------|
| 각 태스크 완료 후 (Crew) | `["task_completed"]` |
| 각 플로우 메서드 완료 후 | `["method_execution_finished"]` |
| 에이전트 실행 완료 후 | `["agent_execution_completed"]`, `["lite_agent_execution_completed"]` |
| 크루 완료 시에만 | `["crew_kickoff_completed"]` |
| 모든 LLM 호출 후 | `["llm_call_completed"]` |
| 모든 이벤트 | `["*"]` |
<Warning>
`["*"]` 또는 `llm_call_completed`와 같은 고빈도 이벤트를 사용하면 많은 체크포인트 파일이 생성되어 성능에 영향을 줄 수 있습니다. `max_checkpoints`를 사용하여 디스크 사용량을 제한하세요.
</Warning>
## 수동 체크포인팅
완전한 제어를 위해 자체 이벤트 핸들러를 등록하고 `state.checkpoint()`를 직접 호출할 수 있습니다:
```python
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
# 동기 핸들러
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source, event, state):
path = state.checkpoint("./my_checkpoints")
print(f"체크포인트 저장: {path}")
# 비동기 핸들러
@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source, event, state):
path = await state.acheckpoint("./my_checkpoints")
print(f"체크포인트 저장: {path}")
```
`state` 인수는 핸들러가 3개의 매개변수를 받을 때 이벤트 버스가 자동으로 전달하는 `RuntimeState`입니다. [Event Listeners](/ko/concepts/event-listener) 문서에 나열된 모든 이벤트 타입에 핸들러를 등록할 수 있습니다.
체크포인팅은 best-effort입니다: 체크포인트 기록이 실패하면 오류가 로그에 기록되지만 실행은 중단 없이 계속됩니다.

View File

@@ -105,7 +105,7 @@ CLI는 `pyproject.toml`에서 프로젝트 유형을 자동으로 감지하고
```
<Tip>
첫 배포는 보통 약 1분 정도 소요됩니다.
첫 배포는 컨테이너 이미지를 빌드하므로 일반적으로 10~15분 정도 소요됩니다. 이후 배포는 훨씬 빠릅니다.
</Tip>
</Step>
@@ -187,7 +187,7 @@ Crew를 GitHub 저장소에 푸시해야 합니다. 아직 Crew를 만들지 않
1. "Deploy" 버튼을 클릭하여 배포 프로세스를 시작합니다.
2. 진행 바를 통해 진행 상황을 모니터링할 수 있습니다.
3. 첫 번째 배포에는 일반적으로 약 1분 정도 소요니다
3. 첫 번째 배포에는 일반적으로 약 10-15분 정도 소요되며, 이후 배포는 더 빠릅니다.
<Frame>
![Deploy Progress](/images/enterprise/deploy-progress.png)

View File

@@ -1,132 +0,0 @@
---
title: "Crew 훈련"
description: "CrewAI AMP 플랫폼에서 직접 배포된 Crew를 훈련하여 시간이 지남에 따라 에이전트 성능을 개선하세요"
icon: "dumbbell"
mode: "wide"
---
훈련을 통해 CrewAI AMP의 **Training** 탭에서 직접 반복 훈련 세션을 실행하여 Crew 성능을 개선할 수 있습니다. 플랫폼은 **자동 훈련 모드**를 사용합니다 — 반복 프로세스를 자동으로 처리하며, 반복마다 대화형 피드백이 필요한 CLI 훈련과는 다릅니다.
훈련이 완료되면 CrewAI는 에이전트 출력을 평가하고 각 에이전트에 대한 실행 가능한 제안으로 피드백을 통합합니다. 이러한 제안은 향후 Crew 실행에 적용되어 출력 품질을 개선합니다.
<Tip>
CrewAI 훈련이 내부적으로 어떻게 작동하는지에 대한 자세한 내용은 [훈련 개념](/ko/concepts/training) 페이지를 참조하세요.
</Tip>
## 사전 요구 사항
<CardGroup cols={2}>
<Card title="활성 배포" icon="rocket">
**Ready** 상태의 활성 배포(Crew 유형)가 있는 CrewAI AMP 계정이 필요합니다.
</Card>
<Card title="실행 권한" icon="key">
훈련하려는 배포에 대한 실행 권한이 계정에 있어야 합니다.
</Card>
</CardGroup>
## Crew 훈련 방법
<Steps>
<Step title="Training 탭 열기">
**Deployments**로 이동하여 배포를 클릭한 다음 **Training** 탭을 선택합니다.
</Step>
<Step title="훈련 이름 입력">
**Training Name**을 입력합니다 — 이것은 훈련 결과를 저장하는 데 사용되는 `.pkl` 파일 이름이 됩니다. 예를 들어, "Expert Mode Training"은 `expert_mode_training.pkl`을 생성합니다.
</Step>
<Step title="Crew 입력값 작성">
Crew의 입력 필드를 입력합니다. 이는 일반 kickoff에 제공하는 것과 동일한 입력값입니다 — Crew 구성에 따라 동적으로 로드됩니다.
</Step>
<Step title="훈련 시작">
**Train Crew**를 클릭합니다. 프로세스가 실행되는 동안 버튼이 스피너와 함께 "Training..."으로 변경됩니다.
내부적으로:
- 배포에 대한 훈련 레코드가 생성됩니다
- 플랫폼이 배포의 자동 훈련 엔드포인트를 호출합니다
- Crew가 자동으로 반복을 실행합니다 — 수동 피드백이 필요하지 않습니다
</Step>
<Step title="진행 상황 모니터링">
**Current Training Status** 패널에 다음이 표시됩니다:
- **Status** — 훈련 실행의 현재 상태
- **Nº Iterations** — 구성된 훈련 반복 횟수
- **Filename** — 생성 중인 `.pkl` 파일
- **Started At** — 훈련 시작 시간
- **Training Inputs** — 제공한 입력값
</Step>
</Steps>
## 훈련 결과 이해
훈련이 완료되면 다음 정보가 포함된 에이전트별 결과 카드가 표시됩니다:
- **Agent Role** — Crew에서 에이전트의 이름/역할
- **Final Quality** — 에이전트 출력 품질을 평가하는 0~10점 점수
- **Final Summary** — 훈련 중 에이전트 성능 요약
- **Suggestions** — 에이전트 동작 개선을 위한 실행 가능한 권장 사항
### 제안 편집
모든 에이전트의 제안을 개선할 수 있습니다:
<Steps>
<Step title="Edit 클릭">
에이전트의 결과 카드에서 제안 옆에 있는 **Edit** 버튼을 클릭합니다.
</Step>
<Step title="제안 수정">
원하는 개선 사항을 더 잘 반영하도록 제안 텍스트를 업데이트합니다.
</Step>
<Step title="변경 사항 저장">
**Save**를 클릭합니다. 편집된 제안이 배포에 다시 동기화되고 이후 모든 실행에 사용됩니다.
</Step>
</Steps>
## 훈련 데이터 사용
Crew에 훈련 결과를 적용하려면:
1. 완료된 훈련 세션에서 **Training Filename**(`.pkl` 파일)을 확인합니다.
2. 배포의 kickoff 또는 실행 구성에서 이 파일 이름을 지정합니다.
3. Crew가 자동으로 훈련 파일을 로드하고 저장된 제안을 각 에이전트에 적용합니다.
이는 에이전트가 이후 모든 실행에서 훈련 중에 생성된 피드백의 혜택을 받는다는 것을 의미합니다.
## 이전 훈련
Training 탭 하단에는 배포에 대한 **모든 과거 훈련 세션 기록**이 표시됩니다. 이전 훈련 실행을 검토하거나 결과를 비교하거나 사용할 다른 훈련 파일을 선택하는 데 사용합니다.
## 오류 처리
훈련 실행이 실패하면 상태 패널에 무엇이 잘못되었는지 설명하는 메시지와 함께 오류 상태가 표시됩니다.
훈련 실패의 일반적인 원인:
- **배포 런타임이 업데이트되지 않음** — 배포가 최신 버전을 실행하고 있는지 확인하세요
- **Crew 실행 오류** — Crew의 작업 로직 또는 에이전트 구성 내 문제
- **네트워크 문제** — 플랫폼과 배포 간의 연결 문제
## 제한 사항
<Info>
훈련 워크플로를 계획할 때 다음 제약 사항을 염두에 두세요:
- **배포당 한 번에 하나의 활성 훈련** — 다른 훈련을 시작하기 전에 현재 실행이 완료될 때까지 기다리세요
- **자동 훈련 모드만** — 플랫폼은 CLI처럼 반복당 대화형 피드백을 지원하지 않습니다
- **훈련 데이터는 배포별** — 훈련 결과는 특정 배포 인스턴스 및 버전에 연결됩니다
</Info>
## 관련 리소스
<CardGroup cols={3}>
<Card title="훈련 개념" icon="book" href="/ko/concepts/training">
CrewAI 훈련이 내부적으로 어떻게 작동하는지 알아보세요.
</Card>
<Card title="Crew 시작" icon="play" href="/ko/enterprise/guides/kickoff-crew">
AMP 플랫폼에서 배포된 Crew를 실행하세요.
</Card>
<Card title="AMP에 배포" icon="cloud-arrow-up" href="/ko/enterprise/guides/deploy-to-amp">
Crew를 배포하고 훈련 준비를 완료하세요.
</Card>
</CardGroup>

View File

@@ -197,8 +197,9 @@ CrewAI는 의존성 관리와 패키지 처리를 위해 `uv`를 사용합니다
## 다음 단계
<CardGroup cols={2}>
<Card title="퀵스타트: Flow + 에이전트" icon="code" href="/ko/quickstart">
Flow를 만들고 에이전트 한 명짜리 crew를 실행해 보고서까지 만드는 방법을 따라 해 보세요.
<Card title="첫 번째 Agent 만들기" icon="code" href="/ko/quickstart">
빠른 시작 가이드를 따라 CrewAI 에이전트를 처음 만들어보고 직접 경험해
보세요.
</Card>
<Card
title="커뮤니티 참여하기"

View File

@@ -138,9 +138,9 @@ Crews의 기능:
<Card
title="빠른 시작"
icon="bolt"
href="/ko/quickstart"
href="ko/quickstart"
>
Flow를 만들고 에이전트 한 명 crew를 실행해 끝까지 보고서를 생성해 보세요.
빠른 시작 가이드를 따라 첫 번째 CrewAI agent를 만들고 직접 경험해 보세요.
</Card>
<Card
title="커뮤니티 가입하기"

View File

@@ -1,6 +1,6 @@
---
title: 퀵스타트
description: 몇 분 안에 첫 CrewAI Flow를 만듭니다 — 오케스트레이션, 상태, 그리고 실제 보고서를 만드는 에이전트 crew까지.
description: 5분 이내에 CrewAI로 첫 번째 AI 에이전트를 구축해보세요.
icon: rocket
mode: "wide"
---
@@ -13,266 +13,375 @@ mode: "wide"
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
이 가이드에서는 **Flow**를 만들어 연구 주제를 정하고, **에이전트 한 명으로 구성된 crew**(웹 검색을 쓰는 연구원)를 실행한 뒤, 디스크에 **Markdown 보고서**를 남깁니다. Flow는 프로덕션 앱을 구성하는 권장 방식으로, **상태**와 **실행 순서**를 담당하고 **에이전트**는 crew 단계 안에서 실제 작업을 수행합니다.
## 첫 번째 CrewAI Agent 만들기
CrewAI를 아직 설치하지 않았다면 먼저 [설치 가이드](/ko/installation)를 따르세요.
이제 주어진 주제나 항목에 대해 `최신 AI 개발 동향`을 `연구`하고 `보고`하는 간단한 crew를 만들어보겠습니다.
## 사전 요건
진행하기 전에 CrewAI 설치를 완료했는지 확인하세요.
아직 설치하지 않았다면, [설치 가이드](/ko/installation)를 참고해 설치할 수 있습니다.
- Python 환경과 CrewAI CLI([설치](/ko/installation) 참고)
- 올바른 API 키로 설정한 LLM — [LLM](/ko/concepts/llms#setting-up-your-llm) 참고
- 이 튜토리얼의 웹 검색용 [Serper.dev](https://serper.dev/) API 키(`SERPER_API_KEY`)
## 첫 번째 Flow 만들기
아래 단계를 따라 Crewing을 시작하세요! 🚣‍♂️
<Steps>
<Step title="Flow 프로젝트 생성">
터미널에서 Flow 프로젝트를 생성합니다(폴더 이름은 밑줄 형식입니다. 예: `latest_ai_flow`).
<Step title="crew 생성하기">
터미널에서 아래 명령어를 실행하여 새로운 crew 프로젝트를 만드세요.
이 작업은 `latest-ai-development`라는 새 디렉터리와 기본 구조를 생성합니다.
<CodeGroup>
```shell Terminal
crewai create flow latest-ai-flow
cd latest_ai_flow
crewai create crew latest-ai-development
```
</CodeGroup>
이렇게 하면 `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="새로운 crew 프로젝트로 이동하기">
<CodeGroup>
```shell Terminal
cd latest_ai_development
```
</CodeGroup>
</Step>
<Step title="`agents.yaml` 파일 수정하기">
<Tip>
프로젝트에 맞게 agent를 수정하거나 복사/붙여넣기를 할 수 있습니다.
`agents.yaml` 및 `tasks.yaml` 파일에서 `{topic}`과 같은 변수를 사용하면, 이는 `main.py` 파일의 변수 값으로 대체됩니다.
</Tip>
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
# src/latest_ai_development/config/agents.yaml
researcher:
role: >
{topic} 시니어 데이터 리서처
{topic} Senior Data Researcher
goal: >
{topic} 분야의 최신 동향을 파악한다
Uncover cutting-edge developments in {topic}
backstory: >
당신은 {topic}의 최신 흐름을 찾아내는 데 능숙한 연구원입니다.
가장 관련성 높은 정보를 찾아 명확하게 전달합니다.
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.
```
</Step>
<Step title="`tasks.yaml`에 작업 하나 설정">
<Step title="`tasks.yaml` 파일 수정하기">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
# src/latest_ai_development/config/tasks.yaml
research_task:
description: >
{topic}에 대해 철저히 조사하세요. 웹 검색으로 최신이고 신뢰할 수 있는 정보를 찾으세요.
현재 연도는 2026년입니다.
Conduct a thorough research about {topic}
Make sure you find any interesting and relevant information given
the current year is 2025.
expected_output: >
마크다운 보고서로, 주요 트렌드·주목할 도구나 기업·시사점 등으로 섹션을 나누세요.
분량은 약 800~1200단어. 문서 전체를 코드 펜스로 감싸지 마세요.
A list with 10 bullet points of the most relevant information about {topic}
agent: researcher
output_file: output/report.md
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
output_file: report.md
```
</Step>
<Step title="crew 클래스 연결 (`content_crew.py`)">
생성된 crew가 YAML을 읽고 연구원에게 `SerperDevTool`을 붙이도록 합니다.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
<Step title="`crew.py` 파일 수정하기">
```python crew.py
# src/latest_ai_development/crew.py
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.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ResearchCrew:
"""Flow 안에서 사용하는 단일 에이전트 연구 crew."""
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment 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]
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
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]
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]
output_file='output/report.md' # This is the file that will be contain the final report.
)
@crew
def crew(self) -> Crew:
"""Creates the LatestAiDevelopment crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=True,
)
```
</Step>
<Step title="[선택 사항] crew 실행 전/후 함수 추가">
```python crew.py
# src/latest_ai_development/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff
from crewai_tools import SerperDevTool
<Step title="`main.py`에서 Flow 정의">
crew를 Flow에 연결합니다: `@start()` 단계에서 주제를 **상태**에 넣고, `@listen` 단계에서 crew를 실행합니다. 작업의 `output_file`은 그대로 `output/report.md`에 씁니다.
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
```python main.py
# src/latest_ai_flow/main.py
from pydantic import BaseModel
@before_kickoff
def before_kickoff_function(self, inputs):
print(f"Before kickoff function with inputs: {inputs}")
return inputs # You can return the inputs or modify them as needed
from crewai.flow import Flow, listen, start
@after_kickoff
def after_kickoff_function(self, result):
print(f"After kickoff function with result: {result}")
return result # You can return the result or modify it as needed
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
class ResearchFlowState(BaseModel):
topic: str = ""
report: str = ""
class LatestAiFlow(Flow[ResearchFlowState]):
@start()
def prepare_topic(self, crewai_trigger_payload: dict | None = None):
if crewai_trigger_payload:
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
else:
self.state.topic = "AI Agents"
print(f"주제: {self.state.topic}")
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("연구 crew 실행 완료.")
@listen(run_research)
def summarize(self):
print("보고서 경로: output/report.md")
def kickoff():
LatestAiFlow().kickoff()
def plot():
LatestAiFlow().plot()
if __name__ == "__main__":
kickoff()
# ... remaining code
```
<Tip>
패키지 이름이 `latest_ai_flow`가 아니면 `ResearchCrew` import 경로를 프로젝트 모듈 경로에 맞게 바꾸세요.
</Tip>
</Step>
<Step title="crew에 커스텀 입력값 전달하기">
예를 들어, crew에 `topic` 입력값을 넘겨 연구 및 보고서 출력을 맞춤화할 수 있습니다.
```python main.py
#!/usr/bin/env python
# src/latest_ai_development/main.py
import sys
from latest_ai_development.crew import LatestAiDevelopmentCrew
def run():
"""
Run the crew.
"""
inputs = {
'topic': 'AI Agents'
}
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
```
</Step>
<Step title="환경 변수 설정">
crew를 실행하기 전에 `.env` 파일에 아래 키가 환경 변수로 설정되어 있는지 확인하세요:
- [Serper.dev](https://serper.dev/) API 키: `SERPER_API_KEY=YOUR_KEY_HERE`
- 사용하려는 모델의 설정, 예: API 키. 다양한 공급자의 모델 설정은
[LLM 설정 가이드](/ko/concepts/llms#setting-up-your-llm)를 참고하세요.
</Step>
<Step title="의존성 잠그고 설치하기">
- CLI 명령어로 의존성을 잠그고 설치하세요:
<CodeGroup>
```shell Terminal
crewai install
```
</CodeGroup>
- 추가 설치가 필요한 패키지가 있다면, 아래와 같이 실행하면 됩니다:
<CodeGroup>
```shell Terminal
uv add <package-name>
```
</CodeGroup>
</Step>
<Step title="crew 실행하기">
- 프로젝트 루트에서 다음 명령어로 crew를 실행하세요:
<CodeGroup>
```bash Terminal
crewai run
```
</CodeGroup>
</Step>
<Step title="환경 변수">
프로젝트 루트의 `.env`에 다음을 설정합니다.
<Step title="엔터프라이즈 대안: Crew Studio에서 생성">
CrewAI AMP 사용자는 코드를 작성하지 않고도 동일한 crew를 생성할 수 있습니다:
- `SERPER_API_KEY` — [Serper.dev](https://serper.dev/)에서 발급
- 모델 제공자 키 — [LLM 설정](/ko/concepts/llms#setting-up-your-llm) 참고
1. CrewAI AMP 계정에 로그인하세요([app.crewai.com](https://app.crewai.com)에서 무료 계정 만들기)
2. Crew Studio 열기
3. 구현하려는 자동화 내용을 입력하세요
4. 미션을 시각적으로 생성하고 순차적으로 연결하세요
5. 입력값을 구성하고 "Download Code" 또는 "Deploy"를 클릭하세요
![Crew Studio Quickstart](/images/enterprise/crew-studio-interface.png)
<Card title="CrewAI AMP 체험하기" icon="rocket" href="https://app.crewai.com">
CrewAI AOP에서 무료 계정을 시작하세요
</Card>
</Step>
<Step title="최종 보고서 확인하기">
콘솔에서 출력 결과를 확인할 수 있으며 프로젝트 루트에 `report.md` 파일로 최종 보고서가 생성됩니다.
<Step title="설치 및 실행">
<CodeGroup>
```shell Terminal
crewai install
crewai run
```
</CodeGroup>
`crewai run`은 프로젝트에 정의된 Flow 진입점을 실행합니다(crew와 동일한 명령이며, `pyproject.toml`의 프로젝트 유형은 `"flow"`입니다).
</Step>
<Step title="결과 확인">
Flow와 crew 로그가 출력되어야 합니다. 생성된 보고서는 **`output/report.md`**에서 확인하세요(발췌):
보고서 예시는 다음과 같습니다:
<CodeGroup>
```markdown output/report.md
# 2026년 AI 에이전트: 동향과 전망
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
## 요약
## 1. Introduction to AI Agents
In 2025, Artificial Intelligence (AI) agents are at the forefront of innovation across various industries. As intelligent systems that can perform tasks typically requiring human cognition, AI agents are paving the way for significant advancements in operational efficiency, decision-making, and overall productivity within sectors like Human Resources (HR) and Finance. This report aims to detail the rise of AI agents, their frameworks, applications, and potential implications on the workforce.
## 주요 트렌드
- **도구 사용과 오케스트레이션** — …
- **엔터프라이즈 도입** — …
## 2. Benefits of AI Agents
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
## 시사점
- **Task Automation**: AI agents can carry out repetitive tasks such as data entry, scheduling, and payroll processing without human intervention, greatly reducing the time and resources spent on these activities.
- **Improved Efficiency**: By quickly processing large datasets and performing analyses that would take humans significantly longer, AI agents enhance operational efficiency. This allows teams to focus on strategic tasks that require higher-level thinking.
- **Enhanced Decision-Making**: AI agents can analyze trends and patterns in data, provide insights, and even suggest actions, helping stakeholders make informed decisions based on factual data rather than intuition alone.
## 3. Popular AI Agent Frameworks
Several frameworks have emerged to facilitate the development of AI agents, each with its own unique features and capabilities. Some of the most popular frameworks include:
- **Autogen**: A framework designed to streamline the development of AI agents through automation of code generation.
- **Semantic Kernel**: Focuses on natural language processing and understanding, enabling agents to comprehend user intentions better.
- **Promptflow**: Provides tools for developers to create conversational agents that can navigate complex interactions seamlessly.
- **Langchain**: Specializes in leveraging various APIs to ensure agents can access and utilize external data effectively.
- **CrewAI**: Aimed at collaborative environments, CrewAI strengthens teamwork by facilitating communication through AI-driven insights.
- **MemGPT**: Combines memory-optimized architectures with generative capabilities, allowing for more personalized interactions with users.
These frameworks empower developers to build versatile and intelligent agents that can engage users, perform advanced analytics, and execute various tasks aligned with organizational goals.
## 4. AI Agents in Human Resources
AI agents are revolutionizing HR practices by automating and optimizing key functions:
- **Recruiting**: AI agents can screen resumes, schedule interviews, and even conduct initial assessments, thus accelerating the hiring process while minimizing biases.
- **Succession Planning**: AI systems analyze employee performance data and potential, helping organizations identify future leaders and plan appropriate training.
- **Employee Engagement**: Chatbots powered by AI can facilitate feedback loops between employees and management, promoting an open culture and addressing concerns promptly.
As AI continues to evolve, HR departments leveraging these agents can realize substantial improvements in both efficiency and employee satisfaction.
## 5. AI Agents in Finance
The finance sector is seeing extensive integration of AI agents that enhance financial practices:
- **Expense Tracking**: Automated systems manage and monitor expenses, flagging anomalies and offering recommendations based on spending patterns.
- **Risk Assessment**: AI models assess credit risk and uncover potential fraud by analyzing transaction data and behavioral patterns.
- **Investment Decisions**: AI agents provide stock predictions and analytics based on historical data and current market conditions, empowering investors with informative insights.
The incorporation of AI agents into finance is fostering a more responsive and risk-aware financial landscape.
## 6. Market Trends and Investments
The growth of AI agents has attracted significant investment, especially amidst the rising popularity of chatbots and generative AI technologies. Companies and entrepreneurs are eager to explore the potential of these systems, recognizing their ability to streamline operations and improve customer engagement.
Conversely, corporations like Microsoft are taking strides to integrate AI agents into their product offerings, with enhancements to their Copilot 365 applications. This strategic move emphasizes the importance of AI literacy in the modern workplace and indicates the stabilizing of AI agents as essential business tools.
## 7. Future Predictions and Implications
Experts predict that AI agents will transform essential aspects of work life. As we look toward the future, several anticipated changes include:
- Enhanced integration of AI agents across all business functions, creating interconnected systems that leverage data from various departmental silos for comprehensive decision-making.
- Continued advancement of AI technologies, resulting in smarter, more adaptable agents capable of learning and evolving from user interactions.
- Increased regulatory scrutiny to ensure ethical use, especially concerning data privacy and employee surveillance as AI agents become more prevalent.
To stay competitive and harness the full potential of AI agents, organizations must remain vigilant about latest developments in AI technology and consider continuous learning and adaptation in their strategic planning.
## 8. Conclusion
The emergence of AI agents is undeniably reshaping the workplace landscape in 5. With their ability to automate tasks, enhance efficiency, and improve decision-making, AI agents are critical in driving operational success. Organizations must embrace and adapt to AI developments to thrive in an increasingly digital business environment.
```
</CodeGroup>
실제 파일은 더 길고 실시간 검색 결과를 반영합니다.
</CodeGroup>
</Step>
</Steps>
## 한 번에 이해하기
1. **Flow** — `LatestAiFlow`는 `prepare_topic` → `run_research` → `summarize` 순으로 실행됩니다. 상태(`topic`, `report`)는 Flow에 있습니다.
2. **Crew** — `ResearchCrew`는 에이전트 한 명·작업 하나로 실행됩니다. 연구원이 **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)를 참고하세요.
<Check>
에이전트 crew와 저장된 보고서까지 이어진 Flow를 완성했습니다. 이제 단계·crew·도구를 더해 확장할 수 있습니다.
축하합니다!
crew 프로젝트 설정이 완료되었으며, 이제 자신만의 agentic workflow 구축을 바로 시작하실 수 있습니다!
</Check>
### 이름 일치
### 명명 일관성에 대한 참고
YAML 키(`researcher`, `research_task`)는 `@CrewBase` 클래스의 메서드 이름과 같아야 합니다. 전체 데코레이터 패턴은 [Crews](/ko/concepts/crews)를 참고하세요.
YAML 파일(`agents.yaml` 및 `tasks.yaml`)에서 사용하는 이름은 Python 코드의 메서드 이름과 일치해야 합니다.
예를 들어, 특정 task에 대한 agent를 `tasks.yaml` 파일에서 참조할 수 있습니다.
이러한 명명 일관성을 지키면 CrewAI가 설정과 코드를 자동으로 연결할 수 있습니다. 그렇지 않으면 task가 참조를 제대로 인식하지 못할 수 있습니다.
## 배포
로컬에서 정상 실행되고 프로젝트가 **GitHub** 저장소에 있으면 Flow를 **[CrewAI AMP](https://app.crewai.com)**에 올릴 수 있습니다. 프로젝트 루트에서:
<CodeGroup>
```bash 인증
crewai login
```
```bash 배포 생성
crewai deploy create
```
```bash 상태 및 로그
crewai deploy status
crewai deploy logs
```
```bash 코드 변경 후 반영
crewai deploy push
```
```bash 배포 목록 또는 삭제
crewai deploy list
crewai deploy remove <deployment_id>
```
</CodeGroup>
#### 예시 참조
<Tip>
첫 배포는 보통 **약 1분** 정도 걸립니다. 전체 사전 요건과 웹 UI 절차는 [AMP에 배포](/ko/enterprise/guides/deploy-to-amp)를 참고하세요.
`agents.yaml` (`email_summarizer`) 파일에서 에이전트 이름과 `crew.py`
(`email_summarizer`) 파일에서 메서드 이름이 동일하게 사용되는 점에 주목하세요.
</Tip>
```yaml agents.yaml
email_summarizer:
role: >
Email Summarizer
goal: >
Summarize emails into a concise and clear summary
backstory: >
You will create a 5 bullet point summary of the report
llm: provider/model-id # Add your choice of model here
```
<Tip>
`tasks.yaml` (`email_summarizer_task`) 파일에서 태스크 이름과 `crew.py`
(`email_summarizer_task`) 파일에서 메서드 이름이 동일하게 사용되는 점에
주목하세요.
</Tip>
```yaml tasks.yaml
email_summarizer_task:
description: >
Summarize the email into a 5 bullet point summary
expected_output: >
A 5 bullet point summary of the email
agent: email_summarizer
context:
- reporting_task
- research_task
```
## Crew 배포하기
production 환경에 crew를 배포하는 가장 쉬운 방법은 [CrewAI AMP](http://app.crewai.com)를 통해서입니다.
CLI를 사용하여 [CrewAI AMP](http://app.crewai.com)에 crew를 배포하는 단계별 시연은 이 영상 튜토리얼을 참고하세요.
<iframe
className="w-full aspect-video rounded-xl"
src="https://www.youtube.com/embed/3EqSV-CYDZA"
title="CrewAI Deployment Guide"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
<CardGroup cols={2}>
<Card title="배포 가이드" icon="book" href="/ko/enterprise/guides/deploy-to-amp">
AMP 배포 단계별 안내(CLI 및 대시보드).
<Card title="Enterprise에 배포" icon="rocket" href="http://app.crewai.com">
CrewAI AOP로 시작하여 몇 번의 클릭만으로 production 환경에 crew를
배포하세요.
</Card>
<Card
title="커뮤니티"
title="커뮤니티 참여하기"
icon="comments"
href="https://community.crewai.com"
>
아이디어를 나누고 프로젝트를 공유하며 다른 CrewAI 개발자와 소통하세요.
오픈 소스 커뮤니티에 참여하여 아이디어를 나누고, 프로젝트를 공유하며, 다른
CrewAI 개발자들과 소통하세요.
</Card>
</CardGroup>

View File

@@ -4,86 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
icon: "clock"
mode: "wide"
---
<Update label="06 abr 2026">
## v1.14.0a3
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a3)
## O que Mudou
### Documentação
- Atualizar changelog e versão para v1.14.0a2
## Contribuidores
@joaomdmoura
</Update>
<Update label="06 abr 2026">
## v1.14.0a2
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.0a2)
## Lançamento 1.14.0a2
### Instruções:
- Traduza todos os cabeçalhos de seção e descrições de forma natural
- Mantenha a formatação markdown (##, ###, -, etc.) exatamente como está
- Mantenha todos os nomes próprios, identificadores de código, nomes de classes e termos técnicos inalterados
(por exemplo, "CrewAI", "LiteAgent", "ChromaDB", "MCP", "@username")
- Mantenha a seção ## Contribuidores e os nomes de usuários do GitHub inalterados
- Não adicione nem remova nenhum conteúdo, apenas traduza
</Update>
<Update label="02 abr 2026">
## v1.13.0
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0)
## O que Mudou
### Funcionalidades
- Adicionar RuntimeState RootModel para serialização de estado unificado
- Melhorar o listener de eventos com novos spans de telemetria para eventos de habilidade e memória
- Adicionar extensão A2UI com suporte a v0.8/v0.9, esquemas e documentação
- Emitir dados de uso de token no LLMCallCompletedEvent
- Atualizar automaticamente o repositório de testes de implantação durante o lançamento
- Melhorar a resiliência e a experiência do usuário na versão empresarial
### Correções de Bugs
- Adicionar credenciais do repositório de ferramentas ao crewai install
- Adicionar credenciais do repositório de ferramentas ao uv build na publicação de ferramentas
- Passar metadados de impressão digital via configuração em vez de argumentos de ferramenta
- Lidar com modelos GPT-5.x que não suportam o parâmetro API `stop`
- Adicionar GPT-5 e a série o aos prefixos de visão multimodal
- Limpar cache uv para pacotes recém-publicados na versão empresarial
- Limitar lancedb abaixo de 0.30.1 para compatibilidade com Windows
- Corrigir níveis de permissão RBAC para corresponder às opções reais da interface do usuário
- Corrigir imprecisões nas capacidades do agente em todos os idiomas
### Documentação
- Adicionar vídeo de demonstração de habilidades do agente de codificação às páginas de introdução
- Adicionar guia abrangente de configuração SSO
- Adicionar matriz de permissões RBAC abrangente e guia de implantação
- Atualizar changelog e versão para v1.13.0
### Desempenho
- Reduzir a sobrecarga do framework com bus de eventos preguiçoso, pular rastreamento quando desativado
### Refatoração
- Converter Flow para Pydantic BaseModel
- Converter classes LLM para Pydantic BaseModel
- Substituir InstanceOf[T] por anotações de tipo simples
- Remover diretório LLM de terceiros não utilizado
## Contribuidores
@alex-clawd, @dependabot[bot], @greysonlalonde, @iris-clawd, @joaomdmoura, @lorenzejay, @lucasgomide, @thiagomoretto
</Update>
<Update label="02 abr 2026">
## v1.13.0a7

View File

@@ -1,232 +0,0 @@
---
title: Checkpointing
description: Salve automaticamente o estado de execucao para que crews, flows e agentes possam retomar apos falhas.
icon: floppy-disk
mode: "wide"
---
<Warning>
O checkpointing esta em versao inicial. As APIs podem mudar em versoes futuras.
</Warning>
## Visao Geral
O checkpointing salva automaticamente o estado de execucao durante uma execucao. Se uma crew, flow ou agente falhar no meio da execucao, voce pode restaurar a partir do ultimo checkpoint e retomar sem reexecutar o trabalho ja concluido.
## Inicio Rapido
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=True, # usa padroes: ./.checkpoints, em task_completed
)
result = crew.kickoff()
```
Os arquivos de checkpoint sao gravados em `./.checkpoints/` apos cada tarefa concluida.
## Configuracao
Use `CheckpointConfig` para controle total:
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
on_events=["task_completed", "crew_kickoff_completed"],
max_checkpoints=5,
),
)
```
### Campos do CheckpointConfig
| Campo | Tipo | Padrao | Descricao |
|:------|:-----|:-------|:----------|
| `directory` | `str` | `"./.checkpoints"` | Caminho para os arquivos de checkpoint |
| `on_events` | `list[str]` | `["task_completed"]` | Tipos de evento que acionam um checkpoint |
| `provider` | `BaseProvider` | `JsonProvider()` | Backend de armazenamento |
| `max_checkpoints` | `int \| None` | `None` | Maximo de arquivos a manter; os mais antigos sao removidos primeiro |
### Heranca e Desativacao
O campo `checkpoint` em Crew, Flow e Agent aceita `CheckpointConfig`, `True`, `False` ou `None`:
| Valor | Comportamento |
|:------|:--------------|
| `None` (padrao) | Herda do pai. Um agente herda a configuracao da crew. |
| `True` | Ativa com padroes. |
| `False` | Desativacao explicita. Interrompe a heranca do pai. |
| `CheckpointConfig(...)` | Configuracao personalizada. |
```python
crew = Crew(
agents=[
Agent(role="Researcher", ...), # herda checkpoint da crew
Agent(role="Writer", ..., checkpoint=False), # desativado, sem checkpoints
],
tasks=[...],
checkpoint=True,
)
```
## Retomando a partir de um Checkpoint
```python
# Restaurar e retomar
crew = Crew.from_checkpoint("./my_checkpoints/20260407T120000_abc123.json")
result = crew.kickoff() # retoma a partir da ultima tarefa concluida
```
A crew restaurada pula tarefas ja concluidas e retoma a partir da primeira incompleta.
## Funciona em Crew, Flow e Agent
### Crew
```python
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task, review_task],
checkpoint=CheckpointConfig(directory="./crew_cp"),
)
```
Gatilho padrao: `task_completed` (um checkpoint por tarefa finalizada).
### Flow
```python
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig
class MyFlow(Flow):
@start()
def step_one(self):
return "data"
@listen(step_one)
def step_two(self, data):
return process(data)
flow = MyFlow(
checkpoint=CheckpointConfig(
directory="./flow_cp",
on_events=["method_execution_finished"],
),
)
result = flow.kickoff()
# Retomar
flow = MyFlow.from_checkpoint("./flow_cp/20260407T120000_abc123.json")
result = flow.kickoff()
```
### Agent
```python
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
checkpoint=CheckpointConfig(
directory="./agent_cp",
on_events=["lite_agent_execution_completed"],
),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])
```
## Provedores de Armazenamento
O CrewAI inclui dois provedores de armazenamento para checkpoints.
### JsonProvider (padrao)
Grava cada checkpoint como um arquivo JSON separado.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./my_checkpoints",
provider=JsonProvider(),
max_checkpoints=5,
),
)
```
### SqliteProvider
Armazena todos os checkpoints em um unico arquivo SQLite.
```python
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
directory="./.checkpoints.db",
provider=SqliteProvider(max_checkpoints=50),
),
)
```
<Note>
Ao usar `SqliteProvider`, o campo `directory` e o caminho do arquivo de banco de dados, nao um diretorio.
</Note>
## Tipos de Evento
O campo `on_events` aceita qualquer combinacao de strings de tipo de evento. Escolhas comuns:
| Caso de Uso | Eventos |
|:------------|:--------|
| Apos cada tarefa (Crew) | `["task_completed"]` |
| Apos cada metodo do flow | `["method_execution_finished"]` |
| Apos execucao do agente | `["agent_execution_completed"]`, `["lite_agent_execution_completed"]` |
| Apenas na conclusao da crew | `["crew_kickoff_completed"]` |
| Apos cada chamada LLM | `["llm_call_completed"]` |
| Em tudo | `["*"]` |
<Warning>
Usar `["*"]` ou eventos de alta frequencia como `llm_call_completed` gravara muitos arquivos de checkpoint e pode impactar o desempenho. Use `max_checkpoints` para limitar o uso de disco.
</Warning>
## Checkpointing Manual
Para controle total, registre seu proprio handler de evento e chame `state.checkpoint()` diretamente:
```python
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
# Handler sincrono
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source, event, state):
path = state.checkpoint("./my_checkpoints")
print(f"Checkpoint salvo: {path}")
# Handler assincrono
@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source, event, state):
path = await state.acheckpoint("./my_checkpoints")
print(f"Checkpoint salvo: {path}")
```
O argumento `state` e o `RuntimeState` passado automaticamente pelo barramento de eventos quando seu handler aceita 3 parametros. Voce pode registrar handlers em qualquer tipo de evento listado na documentacao de [Event Listeners](/pt-BR/concepts/event-listener).
O checkpointing e best-effort: se uma gravacao de checkpoint falhar, o erro e registrado no log, mas a execucao continua sem interrupcao.

View File

@@ -105,7 +105,7 @@ A CLI detecta automaticamente o tipo do seu projeto a partir do `pyproject.toml`
```
<Tip>
A primeira implantação normalmente leva cerca de 1 minuto.
A primeira implantação normalmente leva de 10 a 15 minutos, pois as imagens dos containers são construídas. As próximas implantações são bem mais rápidas.
</Tip>
</Step>
@@ -187,7 +187,7 @@ Você precisa enviar seu crew para um repositório do GitHub. Caso ainda não te
1. Clique no botão "Deploy" para iniciar o processo de implantação
2. Você pode monitorar o progresso pela barra de progresso
3. A primeira implantação geralmente demora cerca de 1 minuto
3. A primeira implantação geralmente demora de 10 a 15 minutos; as próximas serão mais rápidas
<Frame>
![Progresso da Implantação](/images/enterprise/deploy-progress.png)

View File

@@ -1,132 +0,0 @@
---
title: "Treinamento de Crews"
description: "Treine seus crews implantados diretamente da plataforma CrewAI AMP para melhorar o desempenho dos agentes ao longo do tempo"
icon: "dumbbell"
mode: "wide"
---
O treinamento permite que você melhore o desempenho do crew executando sessões de treinamento iterativas diretamente da aba **Training** no CrewAI AMP. A plataforma usa o **modo de auto-treinamento** — ela gerencia o processo iterativo automaticamente, diferente do treinamento via CLI que requer feedback humano interativo por iteração.
Após a conclusão do treinamento, o CrewAI avalia as saídas dos agentes e consolida o feedback em sugestões acionáveis para cada agente. Essas sugestões são então aplicadas às execuções futuras do crew para melhorar a qualidade das saídas.
<Tip>
Para detalhes sobre como o treinamento do CrewAI funciona internamente, consulte a página [Conceitos de Treinamento](/pt-BR/concepts/training).
</Tip>
## Pré-requisitos
<CardGroup cols={2}>
<Card title="Implantação ativa" icon="rocket">
Você precisa de uma conta CrewAI AMP com uma implantação ativa em status **Ready** (tipo Crew).
</Card>
<Card title="Permissão de execução" icon="key">
Sua conta deve ter permissão de execução para a implantação que deseja treinar.
</Card>
</CardGroup>
## Como treinar um crew
<Steps>
<Step title="Abra a aba Training">
Navegue até **Deployments**, clique na sua implantação e selecione a aba **Training**.
</Step>
<Step title="Insira um nome de treinamento">
Forneça um **Training Name** — este será o nome do arquivo `.pkl` usado para armazenar os resultados do treinamento. Por exemplo, "Expert Mode Training" produz `expert_mode_training.pkl`.
</Step>
<Step title="Preencha as entradas do crew">
Insira os campos de entrada do crew. Estas são as mesmas entradas que você forneceria para um kickoff normal — elas são carregadas dinamicamente com base na configuração do seu crew.
</Step>
<Step title="Inicie o treinamento">
Clique em **Train Crew**. O botão muda para "Training..." com um spinner enquanto o processo é executado.
Por trás dos panos:
- Um registro de treinamento é criado para sua implantação
- A plataforma chama o endpoint de auto-treinamento da implantação
- O crew executa suas iterações automaticamente — nenhum feedback manual é necessário
</Step>
<Step title="Monitore o progresso">
O painel **Current Training Status** exibe:
- **Status** — Estado atual da execução do treinamento
- **Nº Iterations** — Número de iterações de treinamento configuradas
- **Filename** — O arquivo `.pkl` sendo gerado
- **Started At** — Quando o treinamento começou
- **Training Inputs** — As entradas que você forneceu
</Step>
</Steps>
## Entendendo os resultados do treinamento
Uma vez que o treinamento for concluído, você verá cards de resultado por agente com as seguintes informações:
- **Agent Role** — O nome/função do agente no seu crew
- **Final Quality** — Uma pontuação de 0 a 10 avaliando a qualidade da saída do agente
- **Final Summary** — Um resumo do desempenho do agente durante o treinamento
- **Suggestions** — Recomendações acionáveis para melhorar o comportamento do agente
### Editando sugestões
Você pode refinar as sugestões para qualquer agente:
<Steps>
<Step title="Clique em Edit">
No card de resultado de qualquer agente, clique no botão **Edit** ao lado das sugestões.
</Step>
<Step title="Modifique as sugestões">
Atualize o texto das sugestões para refletir melhor as melhorias que você deseja.
</Step>
<Step title="Salve as alterações">
Clique em **Save**. As sugestões editadas são sincronizadas de volta à implantação e usadas em todas as execuções futuras.
</Step>
</Steps>
## Usando dados de treinamento
Para aplicar os resultados do treinamento ao seu crew:
1. Anote o **Training Filename** (o arquivo `.pkl`) da sua sessão de treinamento concluída.
2. Especifique este nome de arquivo na configuração de kickoff ou execução da sua implantação.
3. O crew carrega automaticamente o arquivo de treinamento e aplica as sugestões armazenadas a cada agente.
Isso significa que os agentes se beneficiam do feedback gerado durante o treinamento em cada execução subsequente.
## Treinamentos anteriores
A parte inferior da aba Training exibe um **histórico de todas as sessões de treinamento anteriores** da implantação. Use isso para revisar execuções de treinamento anteriores, comparar resultados ou selecionar um arquivo de treinamento diferente para usar.
## Tratamento de erros
Se uma execução de treinamento falhar, o painel de status mostra um estado de erro junto com uma mensagem descrevendo o que deu errado.
Causas comuns de falhas de treinamento:
- **Runtime da implantação não atualizado** — Certifique-se de que sua implantação está executando a versão mais recente
- **Erros de execução do crew** — Problemas na lógica de tarefas do crew ou configuração do agente
- **Problemas de rede** — Problemas de conectividade entre a plataforma e a implantação
## Limitações
<Info>
Tenha estas restrições em mente ao planejar seu fluxo de trabalho de treinamento:
- **Um treinamento ativo por vez** por implantação — aguarde a execução atual terminar antes de iniciar outra
- **Apenas modo de auto-treinamento** — a plataforma não suporta feedback interativo por iteração como o CLI
- **Dados de treinamento são específicos da implantação** — os resultados do treinamento estão vinculados à instância e versão específicas da implantação
</Info>
## Recursos relacionados
<CardGroup cols={3}>
<Card title="Conceitos de Treinamento" icon="book" href="/pt-BR/concepts/training">
Aprenda como o treinamento do CrewAI funciona internamente.
</Card>
<Card title="Kickoff Crew" icon="play" href="/pt-BR/enterprise/guides/kickoff-crew">
Execute seu crew implantado a partir da plataforma AMP.
</Card>
<Card title="Implantar no AMP" icon="cloud-arrow-up" href="/pt-BR/enterprise/guides/deploy-to-amp">
Faça a implantação do seu crew e deixe-o pronto para treinamento.
</Card>
</CardGroup>

View File

@@ -200,11 +200,12 @@ Para equipes e organizações, o CrewAI oferece opções de implantação corpor
<CardGroup cols={2}>
<Card
title="Início rápido: Flow + agente"
title="Construa Seu Primeiro Agente"
icon="code"
href="/pt-BR/quickstart"
>
Siga o guia rápido para gerar um Flow, executar um crew com um agente e produzir um relatório.
Siga nosso guia de início rápido para criar seu primeiro agente CrewAI e
obter experiência prática.
</Card>
<Card
title="Junte-se à Comunidade"

View File

@@ -140,7 +140,7 @@ Para qualquer aplicação pronta para produção, **comece com um Flow**.
icon="bolt"
href="/pt-BR/quickstart"
>
Gere um Flow, execute um crew com um agente e produza um relatório ponta a ponta.
Siga nosso guia rápido para criar seu primeiro agente CrewAI e colocar a mão na massa.
</Card>
<Card
title="Junte-se à Comunidade"

View File

@@ -1,6 +1,6 @@
---
title: Guia Rápido
description: Crie seu primeiro Flow CrewAI em minutos — orquestração, estado e um crew com um agente que gera um relatório real.
description: Construa seu primeiro agente de IA com a CrewAI em menos de 5 minutos.
icon: rocket
mode: "wide"
---
@@ -13,266 +13,370 @@ Você pode instalar com `npx skills add crewaiinc/skills`
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
Neste guia você vai **criar um Flow** que define um tópico de pesquisa, executa um **crew com um agente** (um pesquisador com busca na web) e termina com um **relatório em Markdown** no disco. Flows são a forma recomendada de estruturar apps em produção: eles controlam **estado** e **ordem de execução**, enquanto os **agentes** fazem o trabalho dentro da etapa do crew.
## Construa seu primeiro Agente CrewAI
Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/installation).
Vamos criar uma tripulação simples que nos ajudará a `pesquisar` e `relatar` sobre os `últimos avanços em IA` para um determinado tópico ou assunto.
## Pré-requisitos
Antes de prosseguir, certifique-se de ter concluído a instalação da CrewAI.
Se ainda não instalou, faça isso seguindo o [guia de instalação](/pt-BR/installation).
- Ambiente Python e a CLI do CrewAI (veja [instalação](/pt-BR/installation))
- Um LLM configurado com as chaves corretas — veja [LLMs](/pt-BR/concepts/llms#setting-up-your-llm)
- Uma chave de API do [Serper.dev](https://serper.dev/) (`SERPER_API_KEY`) para busca na web neste tutorial
## Construa seu primeiro Flow
Siga os passos abaixo para começar a tripular! 🚣‍♂️
<Steps>
<Step title="Crie um projeto Flow">
No terminal, gere um projeto Flow (o nome da pasta usa sublinhados, ex.: `latest_ai_flow`):
<Step title="Crie sua tripulação">
Crie um novo projeto de tripulação executando o comando abaixo em seu terminal.
Isso criará um novo diretório chamado `latest-ai-development` com a estrutura básica para sua tripulação.
<CodeGroup>
```shell Terminal
crewai create flow latest-ai-flow
cd latest_ai_flow
crewai create crew latest-ai-development
```
</CodeGroup>
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="Navegue até o novo projeto da sua tripulação">
<CodeGroup>
```shell Terminal
cd latest_ai_development
```
</CodeGroup>
</Step>
<Step title="Modifique seu arquivo `agents.yaml`">
<Tip>
Você também pode modificar os agentes conforme necessário para atender ao seu caso de uso ou copiar e colar como está para seu projeto.
Qualquer variável interpolada nos seus arquivos `agents.yaml` e `tasks.yaml`, como `{topic}`, será substituída pelo valor da variável no arquivo `main.py`.
</Tip>
```yaml agents.yaml
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
# src/latest_ai_development/config/agents.yaml
researcher:
role: >
Pesquisador(a) Sênior de Dados em {topic}
Pesquisador Sênior de Dados em {topic}
goal: >
Descobrir os desenvolvimentos mais recentes em {topic}
Descobrir os avanços 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.
Você é um pesquisador experiente com talento para descobrir os últimos avanços em {topic}. Conhecido por sua habilidade em encontrar as informações mais relevantes e apresentá-las de forma clara e concisa.
reporting_analyst:
role: >
Analista de Relatórios em {topic}
goal: >
Criar relatórios detalhados com base na análise de dados e descobertas de pesquisa em {topic}
backstory: >
Você é um analista meticuloso com um olhar atento aos detalhes. É conhecido por sua capacidade de transformar dados complexos em relatórios claros e concisos, facilitando o entendimento e a tomada de decisão por parte dos outros.
```
</Step>
<Step title="Configure uma tarefa em `tasks.yaml`">
<Step title="Modifique seu arquivo `tasks.yaml`">
```yaml tasks.yaml
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
# src/latest_ai_development/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.
Realize uma pesquisa aprofundada sobre {topic}.
Certifique-se de encontrar informações interessantes e relevantes considerando que o ano atual é 2025.
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.
Uma lista com 10 tópicos dos dados mais relevantes sobre {topic}
agent: researcher
output_file: output/report.md
reporting_task:
description: >
Revise o contexto obtido e expanda cada tópico em uma seção completa para um relatório.
Certifique-se de que o relatório seja detalhado e contenha todas as informações relevantes.
expected_output: >
Um relatório completo com os principais tópicos, cada um com uma seção detalhada de informações.
Formate como markdown sem usar '```'
agent: reporting_analyst
output_file: report.md
```
</Step>
<Step title="Conecte a classe do crew (`content_crew.py`)">
Aponte o crew gerado para o YAML e anexe `SerperDevTool` ao pesquisador.
```python content_crew.py
# src/latest_ai_flow/crews/content_crew/content_crew.py
from typing import List
<Step title="Modifique seu arquivo `crew.py`">
```python crew.py
# src/latest_ai_development/crew.py
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.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class ResearchCrew:
"""Crew de pesquisa com um agente, usado dentro do Flow."""
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment 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]
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()],
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]
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]
output_file='output/report.md' # Este é o arquivo que conterá o relatório final.
)
@crew
def crew(self) -> Crew:
"""Creates the LatestAiDevelopment crew"""
return Crew(
agents=self.agents,
tasks=self.tasks,
agents=self.agents, # Criado automaticamente pelo decorador @agent
tasks=self.tasks, # Criado automaticamente pelo decorador @task
process=Process.sequential,
verbose=True,
)
```
</Step>
<Step title="[Opcional] Adicione funções de pré e pós execução da tripulação">
```python crew.py
# src/latest_ai_development/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task, before_kickoff, after_kickoff
from crewai_tools import SerperDevTool
<Step title="Defina o Flow em `main.py`">
Conecte o crew a um Flow: um passo `@start()` define o tópico no **estado** e um `@listen` executa o crew. O `output_file` da tarefa continua gravando `output/report.md`.
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
```python main.py
# src/latest_ai_flow/main.py
from pydantic import BaseModel
@before_kickoff
def before_kickoff_function(self, inputs):
print(f"Before kickoff function with inputs: {inputs}")
return inputs # You can return the inputs or modify them as needed
from crewai.flow import Flow, listen, start
@after_kickoff
def after_kickoff_function(self, result):
print(f"After kickoff function with result: {result}")
return result # You can return the result or modify it as needed
from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew
class ResearchFlowState(BaseModel):
topic: str = ""
report: str = ""
class LatestAiFlow(Flow[ResearchFlowState]):
@start()
def prepare_topic(self, crewai_trigger_payload: dict | None = None):
if crewai_trigger_payload:
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
else:
self.state.topic = "AI Agents"
print(f"Tópico: {self.state.topic}")
@listen(prepare_topic)
def run_research(self):
result = ResearchCrew().crew().kickoff(inputs={"topic": self.state.topic})
self.state.report = result.raw
print("Crew de pesquisa concluído.")
@listen(run_research)
def summarize(self):
print("Relatório em: output/report.md")
def kickoff():
LatestAiFlow().kickoff()
def plot():
LatestAiFlow().plot()
if __name__ == "__main__":
kickoff()
# ... remaining code
```
<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.
</Tip>
</Step>
<Step title="Fique à vontade para passar entradas personalizadas para sua tripulação">
Por exemplo, você pode passar o input `topic` para sua tripulação para personalizar a pesquisa e o relatório.
```python main.py
#!/usr/bin/env python
# src/latest_ai_development/main.py
import sys
from latest_ai_development.crew import LatestAiDevelopmentCrew
def run():
"""
Run the crew.
"""
inputs = {
'topic': 'AI Agents'
}
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
```
</Step>
<Step title="Defina suas variáveis de ambiente">
Antes de executar sua tripulação, certifique-se de ter as seguintes chaves configuradas como variáveis de ambiente no seu arquivo `.env`:
- Uma chave da API do [Serper.dev](https://serper.dev/): `SERPER_API_KEY=YOUR_KEY_HERE`
- A configuração do modelo de sua escolha, como uma chave de API. Veja o
[guia de configuração do LLM](/pt-BR/concepts/llms#setting-up-your-llm) para aprender como configurar modelos de qualquer provedor.
</Step>
<Step title="Trave e instale as dependências">
- Trave e instale as dependências utilizando o comando da CLI:
<CodeGroup>
```shell Terminal
crewai install
```
</CodeGroup>
- Se quiser instalar pacotes adicionais, faça isso executando:
<CodeGroup>
```shell Terminal
uv add <package-name>
```
</CodeGroup>
</Step>
<Step title="Execute sua tripulação">
- Para executar sua tripulação, rode o seguinte comando na raiz do projeto:
<CodeGroup>
```bash Terminal
crewai run
```
</CodeGroup>
</Step>
<Step title="Variáveis de ambiente">
Na raiz do projeto, no arquivo `.env`, defina:
<Step title="Alternativa para Empresas: Crie no Crew Studio">
Para usuários do CrewAI AMP, você pode criar a mesma tripulação sem escrever código:
- `SERPER_API_KEY` — obtida em [Serper.dev](https://serper.dev/)
- As chaves do provedor de modelo conforme necessário — veja [configuração de LLM](/pt-BR/concepts/llms#setting-up-your-llm)
1. Faça login na sua conta CrewAI AMP (crie uma conta gratuita em [app.crewai.com](https://app.crewai.com))
2. Abra o Crew Studio
3. Digite qual automação deseja construir
4. Crie suas tarefas visualmente e conecte-as em sequência
5. Configure seus inputs e clique em "Download Code" ou "Deploy"
![Crew Studio Quickstart](/images/enterprise/crew-studio-interface.png)
<Card title="Experimente o CrewAI AMP" icon="rocket" href="https://app.crewai.com">
Comece sua conta gratuita no CrewAI AMP
</Card>
</Step>
<Step title="Veja seu relatório final">
Você verá a saída no console e o arquivo `report.md` deve ser criado na raiz do seu projeto com o relatório final.
<Step title="Instalar e executar">
<CodeGroup>
```shell Terminal
crewai install
crewai run
```
</CodeGroup>
O `crewai run` executa o ponto de entrada do Flow definido no projeto (o mesmo comando dos crews; o tipo do projeto é `"flow"` no `pyproject.toml`).
</Step>
<Step title="Confira o resultado">
Você deve ver logs do Flow e do crew. Abra **`output/report.md`** para o relatório gerado (trecho):
Veja um exemplo de como o relatório deve ser:
<CodeGroup>
```markdown output/report.md
# Agentes de IA em 2026: panorama e tendências
# Relatório Abrangente sobre a Ascensão e o Impacto dos Agentes de IA em 2025
## Resumo executivo
## 1. Introduction to AI Agents
In 2025, Artificial Intelligence (AI) agents are at the forefront of innovation across various industries. As intelligent systems that can perform tasks typically requiring human cognition, AI agents are paving the way for significant advancements in operational efficiency, decision-making, and overall productivity within sectors like Human Resources (HR) and Finance. This report aims to detail the rise of AI agents, their frameworks, applications, and potential implications on the workforce.
## Principais tendências
- **Uso de ferramentas e orquestração** — …
- **Adoção empresarial** — …
## 2. Benefits of AI Agents
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
## Implicações
- **Task Automation**: AI agents can carry out repetitive tasks such as data entry, scheduling, and payroll processing without human intervention, greatly reducing the time and resources spent on these activities.
- **Improved Efficiency**: By quickly processing large datasets and performing analyses that would take humans significantly longer, AI agents enhance operational efficiency. This allows teams to focus on strategic tasks that require higher-level thinking.
- **Enhanced Decision-Making**: AI agents can analyze trends and patterns in data, provide insights, and even suggest actions, helping stakeholders make informed decisions based on factual data rather than intuition alone.
## 3. Popular AI Agent Frameworks
Several frameworks have emerged to facilitate the development of AI agents, each with its own unique features and capabilities. Some of the most popular frameworks include:
- **Autogen**: A framework designed to streamline the development of AI agents through automation of code generation.
- **Semantic Kernel**: Focuses on natural language processing and understanding, enabling agents to comprehend user intentions better.
- **Promptflow**: Provides tools for developers to create conversational agents that can navigate complex interactions seamlessly.
- **Langchain**: Specializes in leveraging various APIs to ensure agents can access and utilize external data effectively.
- **CrewAI**: Aimed at collaborative environments, CrewAI strengthens teamwork by facilitating communication through AI-driven insights.
- **MemGPT**: Combines memory-optimized architectures with generative capabilities, allowing for more personalized interactions with users.
These frameworks empower developers to build versatile and intelligent agents that can engage users, perform advanced analytics, and execute various tasks aligned with organizational goals.
## 4. AI Agents in Human Resources
AI agents are revolutionizing HR practices by automating and optimizing key functions:
- **Recruiting**: AI agents can screen resumes, schedule interviews, and even conduct initial assessments, thus accelerating the hiring process while minimizing biases.
- **Succession Planning**: AI systems analyze employee performance data and potential, helping organizations identify future leaders and plan appropriate training.
- **Employee Engagement**: Chatbots powered by AI can facilitate feedback loops between employees and management, promoting an open culture and addressing concerns promptly.
As AI continues to evolve, HR departments leveraging these agents can realize substantial improvements in both efficiency and employee satisfaction.
## 5. AI Agents in Finance
The finance sector is seeing extensive integration of AI agents that enhance financial practices:
- **Expense Tracking**: Automated systems manage and monitor expenses, flagging anomalies and offering recommendations based on spending patterns.
- **Risk Assessment**: AI models assess credit risk and uncover potential fraud by analyzing transaction data and behavioral patterns.
- **Investment Decisions**: AI agents provide stock predictions and analytics based on historical data and current market conditions, empowering investors with informative insights.
The incorporation of AI agents into finance is fostering a more responsive and risk-aware financial landscape.
## 6. Market Trends and Investments
The growth of AI agents has attracted significant investment, especially amidst the rising popularity of chatbots and generative AI technologies. Companies and entrepreneurs are eager to explore the potential of these systems, recognizing their ability to streamline operations and improve customer engagement.
Conversely, corporations like Microsoft are taking strides to integrate AI agents into their product offerings, with enhancements to their Copilot 365 applications. This strategic move emphasizes the importance of AI literacy in the modern workplace and indicates the stabilizing of AI agents as essential business tools.
## 7. Future Predictions and Implications
Experts predict that AI agents will transform essential aspects of work life. As we look toward the future, several anticipated changes include:
- Enhanced integration of AI agents across all business functions, creating interconnected systems that leverage data from various departmental silos for comprehensive decision-making.
- Continued advancement of AI technologies, resulting in smarter, more adaptable agents capable of learning and evolving from user interactions.
- Increased regulatory scrutiny to ensure ethical use, especially concerning data privacy and employee surveillance as AI agents become more prevalent.
To stay competitive and harness the full potential of AI agents, organizations must remain vigilant about latest developments in AI technology and consider continuous learning and adaptation in their strategic planning.
## 8. Conclusion
The emergence of AI agents is undeniably reshaping the workplace landscape in 5. With their ability to automate tasks, enhance efficiency, and improve decision-making, AI agents are critical in driving operational success. Organizations must embrace and adapt to AI developments to thrive in an increasingly digital business environment.
```
</CodeGroup>
O arquivo real será mais longo e refletirá resultados de busca ao vivo.
</CodeGroup>
</Step>
</Steps>
## 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.
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).
<Check>
Você tem um Flow ponta a ponta com um crew de agente e um relatório salvo — uma base sólida para novas etapas, crews ou ferramentas.
Parabéns!
Você configurou seu projeto de tripulação com sucesso e está pronto para começar a construir seus próprios fluxos de trabalho baseados em agentes!
</Check>
### Consistência de nomes
### Observação sobre Consistência nos 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 utilizados nos seus arquivos YAML (`agents.yaml` e `tasks.yaml`) devem corresponder aos nomes dos métodos no seu código Python.
Por exemplo, você pode referenciar o agente para tarefas específicas a partir do arquivo `tasks.yaml`.
Essa consistência de nomes permite que a CrewAI conecte automaticamente suas configurações ao seu código; caso contrário, sua tarefa não reconhecerá a referência corretamente.
## Implantação
Envie seu Flow para o **[CrewAI AMP](https://app.crewai.com)** quando rodar localmente e o projeto estiver em um repositório **GitHub**. Na raiz do projeto:
<CodeGroup>
```bash Autenticar
crewai login
```
```bash Criar implantação
crewai deploy create
```
```bash Status e logs
crewai deploy status
crewai deploy logs
```
```bash Enviar atualizações após mudanças no código
crewai deploy push
```
```bash Listar ou remover implantações
crewai deploy list
crewai deploy remove <deployment_id>
```
</CodeGroup>
#### Exemplos de Referências
<Tip>
A primeira implantação costuma levar **cerca de 1 minuto**. Pré-requisitos completos e fluxo na interface web estão em [Implantar no AMP](/pt-BR/enterprise/guides/deploy-to-amp).
Observe como usamos o mesmo nome para o agente no arquivo `agents.yaml`
(`email_summarizer`) e no método do arquivo `crew.py` (`email_summarizer`).
</Tip>
```yaml agents.yaml
email_summarizer:
role: >
Email Summarizer
goal: >
Summarize emails into a concise and clear summary
backstory: >
You will create a 5 bullet point summary of the report
llm: provider/model-id # Add your choice of model here
```
<Tip>
Observe como usamos o mesmo nome para a tarefa no arquivo `tasks.yaml`
(`email_summarizer_task`) e no método no arquivo `crew.py`
(`email_summarizer_task`).
</Tip>
```yaml tasks.yaml
email_summarizer_task:
description: >
Summarize the email into a 5 bullet point summary
expected_output: >
A 5 bullet point summary of the email
agent: email_summarizer
context:
- reporting_task
- research_task
```
## Fazendo o Deploy da Sua Tripulação
A forma mais fácil de fazer deploy da sua tripulação em produção é através da [CrewAI AMP](http://app.crewai.com).
Assista a este vídeo tutorial para uma demonstração detalhada de como fazer deploy da sua tripulação na [CrewAI AMP](http://app.crewai.com) usando a CLI.
<iframe
className="w-full aspect-video rounded-xl"
src="https://www.youtube.com/embed/3EqSV-CYDZA"
title="CrewAI Deployment Guide"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
<CardGroup cols={2}>
<Card title="Guia de implantação" icon="book" href="/pt-BR/enterprise/guides/deploy-to-amp">
AMP passo a passo (CLI e painel).
<Card title="Deploy no Enterprise" icon="rocket" href="http://app.crewai.com">
Comece com o CrewAI AMP e faça o deploy da sua tripulação em ambiente de
produção com apenas alguns cliques.
</Card>
<Card
title="Comunidade"
title="Junte-se à Comunidade"
icon="comments"
href="https://community.crewai.com"
>
Troque ideias, compartilhe projetos e conecte-se com outros desenvolvedores CrewAI.
Participe da nossa comunidade open source para discutir ideias, compartilhar
seus projetos e conectar-se com outros desenvolvedores CrewAI.
</Card>
</CardGroup>

View File

@@ -17,9 +17,6 @@ dependencies = [
"av~=13.0.0",
]
[tool.uv]
exclude-newer = "3 days"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -152,4 +152,4 @@ __all__ = [
"wrap_file_source",
]
__version__ = "1.14.0a3"
__version__ = "1.13.0a7"

View File

@@ -10,7 +10,8 @@ requires-python = ">=3.10, <3.14"
dependencies = [
"pytube~=15.0.0",
"requests~=2.32.5",
"crewai==1.14.0a3",
"docker~=7.1.0",
"crewai==1.13.0a7",
"tiktoken~=0.8.0",
"beautifulsoup4~=4.13.4",
"python-docx~=1.2.0",
@@ -141,9 +142,6 @@ contextual = [
]
[tool.uv]
exclude-newer = "3 days"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -35,6 +35,9 @@ from crewai_tools.tools.browserbase_load_tool.browserbase_load_tool import (
from crewai_tools.tools.code_docs_search_tool.code_docs_search_tool import (
CodeDocsSearchTool,
)
from crewai_tools.tools.code_interpreter_tool.code_interpreter_tool import (
CodeInterpreterTool,
)
from crewai_tools.tools.composio_tool.composio_tool import ComposioTool
from crewai_tools.tools.contextualai_create_agent_tool.contextual_create_agent_tool import (
ContextualAICreateAgentTool,
@@ -222,6 +225,7 @@ __all__ = [
"BrowserbaseLoadTool",
"CSVSearchTool",
"CodeDocsSearchTool",
"CodeInterpreterTool",
"ComposioTool",
"ContextualAICreateAgentTool",
"ContextualAIParseTool",
@@ -305,4 +309,4 @@ __all__ = [
"ZapierActionTools",
]
__version__ = "1.14.0a3"
__version__ = "1.13.0a7"

View File

@@ -24,6 +24,9 @@ from crewai_tools.tools.browserbase_load_tool.browserbase_load_tool import (
from crewai_tools.tools.code_docs_search_tool.code_docs_search_tool import (
CodeDocsSearchTool,
)
from crewai_tools.tools.code_interpreter_tool.code_interpreter_tool import (
CodeInterpreterTool,
)
from crewai_tools.tools.composio_tool.composio_tool import ComposioTool
from crewai_tools.tools.contextualai_create_agent_tool.contextual_create_agent_tool import (
ContextualAICreateAgentTool,
@@ -207,6 +210,7 @@ __all__ = [
"BrowserbaseLoadTool",
"CSVSearchTool",
"CodeDocsSearchTool",
"CodeInterpreterTool",
"ComposioTool",
"ContextualAICreateAgentTool",
"ContextualAIParseTool",

View File

@@ -0,0 +1,6 @@
FROM python:3.12-alpine
RUN pip install requests beautifulsoup4
# Set the working directory
WORKDIR /workspace

View File

@@ -0,0 +1,95 @@
# CodeInterpreterTool
## Description
This tool is used to give the Agent the ability to run code (Python3) from the code generated by the Agent itself. The code is executed in a Docker container for secure isolation.
It is incredibly useful since it allows the Agent to generate code, run it in an isolated environment, get the result and use it to make decisions.
## ⚠️ Security Requirements
**Docker is REQUIRED** for safe code execution. The tool will refuse to execute code without Docker to prevent security vulnerabilities.
### Why Docker is Required
Previous versions included a "restricted sandbox" fallback when Docker was unavailable. This has been **removed** due to critical security vulnerabilities:
- The Python-based sandbox could be escaped via object introspection
- Attackers could recover the original `__import__` function and access any module
- This allowed arbitrary command execution on the host system
**Docker provides real process isolation** and is the only secure way to execute untrusted code.
## Requirements
- **Docker (REQUIRED)** - Install from [docker.com](https://docs.docker.com/get-docker/)
## Installation
Install the crewai_tools package
```shell
pip install 'crewai[tools]'
```
## Example
Remember that when using this tool, the code must be generated by the Agent itself. The code must be Python3 code. It will take some time the first time to run because it needs to build the Docker image.
### Basic Usage (Docker Container - Recommended)
```python
from crewai_tools import CodeInterpreterTool
Agent(
...
tools=[CodeInterpreterTool()],
)
```
### Custom Dockerfile
If you need to pass your own Dockerfile:
```python
from crewai_tools import CodeInterpreterTool
Agent(
...
tools=[CodeInterpreterTool(user_dockerfile_path="<Dockerfile_path>")],
)
```
### Manual Docker Host Configuration
If it is difficult to connect to the Docker daemon automatically (especially for macOS users), you can set up the Docker host manually:
```python
from crewai_tools import CodeInterpreterTool
Agent(
...
tools=[CodeInterpreterTool(
user_docker_base_url="<Docker Host Base Url>",
user_dockerfile_path="<Dockerfile_path>"
)],
)
```
### Unsafe Mode (NOT RECOMMENDED)
If you absolutely cannot use Docker and **fully trust the code source**, you can use unsafe mode:
```python
from crewai_tools import CodeInterpreterTool
# WARNING: Only use with fully trusted code!
Agent(
...
tools=[CodeInterpreterTool(unsafe_mode=True)],
)
```
**⚠️ SECURITY WARNING:** `unsafe_mode=True` executes code directly on the host without any isolation. Only use this if:
- You completely trust the code being executed
- You understand the security risks
- You cannot install Docker in your environment
For production use, **always use Docker** (the default mode).

View File

@@ -0,0 +1,424 @@
"""Code Interpreter Tool for executing Python code in isolated environments.
This module provides a tool for executing Python code either in a Docker container for
safe isolation or directly in a restricted sandbox. It includes mechanisms for blocking
potentially unsafe operations and importing restricted modules.
"""
import importlib.util
import os
import subprocess
import sys
from types import ModuleType
from typing import Any, ClassVar, TypedDict
from crewai.tools import BaseTool
from docker import ( # type: ignore[import-untyped]
DockerClient,
from_env as docker_from_env,
)
from docker.errors import ImageNotFound, NotFound # type: ignore[import-untyped]
from pydantic import BaseModel, Field
from typing_extensions import Unpack
from crewai_tools.printer import Printer
class RunKwargs(TypedDict, total=False):
"""Keyword arguments for the _run method."""
code: str
libraries_used: list[str]
class CodeInterpreterSchema(BaseModel):
"""Schema for defining inputs to the CodeInterpreterTool.
This schema defines the required parameters for code execution,
including the code to run and any libraries that need to be installed.
"""
code: str = Field(
...,
description="Python3 code used to be interpreted in the Docker container. ALWAYS PRINT the final result and the output of the code",
)
libraries_used: list[str] = Field(
...,
description="List of libraries used in the code with proper installing names separated by commas. Example: numpy,pandas,beautifulsoup4",
)
class SandboxPython:
"""INSECURE: A restricted Python execution environment with known vulnerabilities.
WARNING: This class does NOT provide real security isolation and is vulnerable to
sandbox escape attacks via Python object introspection. Attackers can recover the
original __import__ function and bypass all restrictions.
DO NOT USE for untrusted code execution. Use Docker containers instead.
This class attempts to restrict access to dangerous modules and built-in functions
but provides no real security boundary against a motivated attacker.
"""
BLOCKED_MODULES: ClassVar[set[str]] = {
"os",
"sys",
"subprocess",
"shutil",
"importlib",
"inspect",
"tempfile",
"sysconfig",
"builtins",
}
UNSAFE_BUILTINS: ClassVar[set[str]] = {
"exec",
"eval",
"open",
"compile",
"input",
"globals",
"locals",
"vars",
"help",
"dir",
}
@staticmethod
def restricted_import(
name: str,
custom_globals: dict[str, Any] | None = None,
custom_locals: dict[str, Any] | None = None,
fromlist: list[str] | None = None,
level: int = 0,
) -> ModuleType:
"""A restricted import function that blocks importing of unsafe modules.
Args:
name: The name of the module to import.
custom_globals: Global namespace to use.
custom_locals: Local namespace to use.
fromlist: List of items to import from the module.
level: The level value passed to __import__.
Returns:
The imported module if allowed.
Raises:
ImportError: If the module is in the blocked modules list.
"""
if name in SandboxPython.BLOCKED_MODULES:
raise ImportError(f"Importing '{name}' is not allowed.")
return __import__(name, custom_globals, custom_locals, fromlist or (), level)
@staticmethod
def safe_builtins() -> dict[str, Any]:
"""Creates a dictionary of built-in functions with unsafe ones removed.
Returns:
A dictionary of safe built-in functions and objects.
"""
import builtins
safe_builtins = {
k: v
for k, v in builtins.__dict__.items()
if k not in SandboxPython.UNSAFE_BUILTINS
}
safe_builtins["__import__"] = SandboxPython.restricted_import
return safe_builtins
@staticmethod
def exec(code: str, locals_: dict[str, Any]) -> None:
"""Executes Python code in a restricted environment.
Args:
code: The Python code to execute as a string.
locals_: A dictionary that will be used for local variable storage.
"""
exec(code, {"__builtins__": SandboxPython.safe_builtins()}, locals_) # noqa: S102
class CodeInterpreterTool(BaseTool):
"""A tool for executing Python code in isolated environments.
This tool provides functionality to run Python code either in a Docker container
for safe isolation or directly in a restricted sandbox. It can handle installing
Python packages and executing arbitrary Python code.
"""
name: str = "Code Interpreter"
description: str = "Interprets Python3 code strings with a final print statement."
args_schema: type[BaseModel] = CodeInterpreterSchema
default_image_tag: str = "code-interpreter:latest"
code: str | None = None
user_dockerfile_path: str | None = None
user_docker_base_url: str | None = None
unsafe_mode: bool = False
@staticmethod
def _get_installed_package_path() -> str:
"""Gets the installation path of the crewai_tools package.
Returns:
The directory path where the package is installed.
Raises:
RuntimeError: If the package cannot be found.
"""
spec = importlib.util.find_spec("crewai_tools")
if spec is None or spec.origin is None:
raise RuntimeError("Cannot find crewai_tools package installation path")
return os.path.dirname(spec.origin)
def _verify_docker_image(self) -> None:
"""Verifies if the Docker image is available or builds it if necessary.
Checks if the required Docker image exists. If not, builds it using either a
user-provided Dockerfile or the default one included with the package.
Raises:
FileNotFoundError: If the Dockerfile cannot be found.
"""
client = (
docker_from_env()
if self.user_docker_base_url is None
else DockerClient(base_url=self.user_docker_base_url)
)
try:
client.images.get(self.default_image_tag)
except ImageNotFound:
if self.user_dockerfile_path and os.path.exists(self.user_dockerfile_path):
dockerfile_path = self.user_dockerfile_path
else:
package_path = self._get_installed_package_path()
dockerfile_path = os.path.join(
package_path, "tools/code_interpreter_tool"
)
if not os.path.exists(dockerfile_path):
raise FileNotFoundError(
f"Dockerfile not found in {dockerfile_path}"
) from None
client.images.build(
path=dockerfile_path,
tag=self.default_image_tag,
rm=True,
)
def _run(self, **kwargs: Unpack[RunKwargs]) -> str:
"""Runs the code interpreter tool with the provided arguments.
Args:
**kwargs: Keyword arguments that should include 'code' and 'libraries_used'.
Returns:
The output of the executed code as a string.
"""
code: str | None = kwargs.get("code", self.code)
libraries_used: list[str] = kwargs.get("libraries_used", [])
if not code:
return "No code provided to execute."
if self.unsafe_mode:
return self.run_code_unsafe(code, libraries_used)
return self.run_code_safety(code, libraries_used)
@staticmethod
def _install_libraries(container: Any, libraries: list[str]) -> None:
"""Installs required Python libraries in the Docker container.
Args:
container: The Docker container where libraries will be installed.
libraries: A list of library names to install using pip.
"""
for library in libraries:
container.exec_run(["pip", "install", library])
def _init_docker_container(self) -> Any:
"""Initializes and returns a Docker container for code execution.
Stops and removes any existing container with the same name before creating
a new one. Maps the current working directory to /workspace in the container.
Returns:
A Docker container object ready for code execution.
"""
container_name = "code-interpreter"
client = docker_from_env()
current_path = os.getcwd()
# Check if the container is already running
try:
existing_container = client.containers.get(container_name)
existing_container.stop()
existing_container.remove()
except NotFound:
pass # Container does not exist, no need to remove
return client.containers.run(
self.default_image_tag,
detach=True,
tty=True,
working_dir="/workspace",
name=container_name,
volumes={current_path: {"bind": "/workspace", "mode": "rw"}},
)
@staticmethod
def _check_docker_available() -> bool:
"""Checks if Docker is available and running on the system.
Attempts to run the 'docker info' command to verify Docker availability.
Prints appropriate messages if Docker is not installed or not running.
Returns:
True if Docker is available and running, False otherwise.
"""
try:
subprocess.run(
["docker", "info"], # noqa: S607
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=1,
)
return True
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
Printer.print(
"Docker is installed but not running or inaccessible.",
color="bold_purple",
)
return False
except FileNotFoundError:
Printer.print("Docker is not installed", color="bold_purple")
return False
def run_code_safety(self, code: str, libraries_used: list[str]) -> str:
"""Runs code in the safest available environment.
Requires Docker to be available for secure code execution. Fails closed
if Docker is not available to prevent sandbox escape vulnerabilities.
Args:
code: The Python code to execute as a string.
libraries_used: A list of Python library names to install before execution.
Returns:
The output of the executed code as a string.
Raises:
RuntimeError: If Docker is not available, as the restricted sandbox
is vulnerable to escape attacks and should not be used
for untrusted code execution.
"""
if self._check_docker_available():
return self.run_code_in_docker(code, libraries_used)
error_msg = (
"Docker is required for safe code execution but is not available. "
"The restricted sandbox fallback has been removed due to security vulnerabilities "
"that allow sandbox escape via Python object introspection. "
"Please install Docker (https://docs.docker.com/get-docker/) or use unsafe_mode=True "
"if you trust the code source and understand the security risks."
)
Printer.print(error_msg, color="bold_red")
raise RuntimeError(error_msg)
def run_code_in_docker(self, code: str, libraries_used: list[str]) -> str:
"""Runs Python code in a Docker container for safe isolation.
Creates a Docker container, installs the required libraries, executes the code,
and then cleans up by stopping and removing the container.
Args:
code: The Python code to execute as a string.
libraries_used: A list of Python library names to install before execution.
Returns:
The output of the executed code as a string, or an error message if execution failed.
"""
Printer.print("Running code in Docker environment", color="bold_blue")
self._verify_docker_image()
container = self._init_docker_container()
self._install_libraries(container, libraries_used)
exec_result: Any = container.exec_run(["python3", "-c", code])
container.stop()
container.remove()
if exec_result.exit_code != 0:
return f"Something went wrong while running the code: \n{exec_result.output.decode('utf-8')}"
return str(exec_result.output.decode("utf-8"))
@staticmethod
def run_code_in_restricted_sandbox(code: str) -> str:
"""DEPRECATED AND INSECURE: Runs Python code in a restricted sandbox environment.
WARNING: This method is vulnerable to sandbox escape attacks via Python object
introspection and should NOT be used for untrusted code execution. It has been
deprecated and is only kept for backward compatibility with trusted code.
The "restricted" environment can be bypassed by attackers who can:
- Use object graph introspection to recover the original __import__ function
- Access any Python module including os, subprocess, sys, etc.
- Execute arbitrary commands on the host system
Use run_code_in_docker() for secure code execution, or run_code_unsafe()
if you explicitly acknowledge the security risks.
Args:
code: The Python code to execute as a string.
Returns:
The value of the 'result' variable from the executed code,
or an error message if execution failed.
"""
Printer.print(
"WARNING: Running code in INSECURE restricted sandbox (vulnerable to escape attacks)",
color="bold_red",
)
exec_locals: dict[str, Any] = {}
try:
SandboxPython.exec(code=code, locals_=exec_locals)
return exec_locals.get("result", "No result variable found.") # type: ignore[no-any-return]
except Exception as e:
return f"An error occurred: {e!s}"
@staticmethod
def run_code_unsafe(code: str, libraries_used: list[str]) -> str:
"""Runs code directly on the host machine without any safety restrictions.
WARNING: This mode is unsafe and should only be used in trusted environments
with code from trusted sources.
Args:
code: The Python code to execute as a string.
libraries_used: A list of Python library names to install before execution.
Returns:
The value of the 'result' variable from the executed code,
or an error message if execution failed.
"""
Printer.print("WARNING: Running code in unsafe mode", color="bold_magenta")
# Install libraries on the host machine
for library in libraries_used:
subprocess.run( # noqa: S603
[sys.executable, "-m", "pip", "install", library], check=False
)
# Execute the code
try:
exec_locals: dict[str, Any] = {}
exec(code, {}, exec_locals) # noqa: S102
return exec_locals.get("result", "No result variable found.") # type: ignore[no-any-return]
except Exception as e:
return f"An error occurred: {e!s}"

View File

@@ -97,7 +97,6 @@ def test_extract_init_params_schema(mock_tool_extractor):
assert init_params_schema.keys() == {
"$defs",
"properties",
"required",
"title",
"type",
}

View File

@@ -0,0 +1,253 @@
import sys
from unittest.mock import patch
from crewai_tools.tools.code_interpreter_tool.code_interpreter_tool import (
CodeInterpreterTool,
SandboxPython,
)
import pytest
@pytest.fixture
def printer_mock():
with patch("crewai_tools.printer.Printer.print") as mock:
yield mock
@pytest.fixture
def docker_unavailable_mock():
with patch(
"crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.CodeInterpreterTool._check_docker_available",
return_value=False,
) as mock:
yield mock
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
def test_run_code_in_docker(docker_mock, printer_mock):
tool = CodeInterpreterTool()
code = "print('Hello, World!')"
libraries_used = ["numpy", "pandas"]
expected_output = "Hello, World!\n"
docker_mock().containers.run().exec_run().exit_code = 0
docker_mock().containers.run().exec_run().output = expected_output.encode()
result = tool.run_code_in_docker(code, libraries_used)
assert result == expected_output
printer_mock.assert_called_with(
"Running code in Docker environment", color="bold_blue"
)
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
def test_run_code_in_docker_with_error(docker_mock, printer_mock):
tool = CodeInterpreterTool()
code = "print(1/0)"
libraries_used = ["numpy", "pandas"]
expected_output = "Something went wrong while running the code: \nZeroDivisionError: division by zero\n"
docker_mock().containers.run().exec_run().exit_code = 1
docker_mock().containers.run().exec_run().output = (
b"ZeroDivisionError: division by zero\n"
)
result = tool.run_code_in_docker(code, libraries_used)
assert result == expected_output
printer_mock.assert_called_with(
"Running code in Docker environment", color="bold_blue"
)
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
def test_run_code_in_docker_with_script(docker_mock, printer_mock):
tool = CodeInterpreterTool()
code = """print("This is line 1")
print("This is line 2")"""
libraries_used = []
expected_output = "This is line 1\nThis is line 2\n"
docker_mock().containers.run().exec_run().exit_code = 0
docker_mock().containers.run().exec_run().output = expected_output.encode()
result = tool.run_code_in_docker(code, libraries_used)
assert result == expected_output
printer_mock.assert_called_with(
"Running code in Docker environment", color="bold_blue"
)
def test_docker_unavailable_raises_error(printer_mock, docker_unavailable_mock):
"""Test that execution fails when Docker is unavailable in safe mode."""
tool = CodeInterpreterTool()
code = """
result = 2 + 2
print(result)
"""
with pytest.raises(RuntimeError) as exc_info:
tool.run(code=code, libraries_used=[])
assert "Docker is required for safe code execution" in str(exc_info.value)
assert "sandbox escape" in str(exc_info.value)
def test_restricted_sandbox_running_with_blocked_modules():
"""Test that restricted modules cannot be imported when using the deprecated sandbox directly."""
tool = CodeInterpreterTool()
restricted_modules = SandboxPython.BLOCKED_MODULES
for module in restricted_modules:
code = f"""
import {module}
result = "Import succeeded"
"""
# Note: run_code_in_restricted_sandbox is deprecated and insecure
# This test verifies the old behavior but should not be used in production
result = tool.run_code_in_restricted_sandbox(code)
assert f"An error occurred: Importing '{module}' is not allowed" in result
def test_restricted_sandbox_running_with_blocked_builtins():
"""Test that restricted builtins are not available when using the deprecated sandbox directly."""
tool = CodeInterpreterTool()
restricted_builtins = SandboxPython.UNSAFE_BUILTINS
for builtin in restricted_builtins:
code = f"""
{builtin}("test")
result = "Builtin available"
"""
# Note: run_code_in_restricted_sandbox is deprecated and insecure
# This test verifies the old behavior but should not be used in production
result = tool.run_code_in_restricted_sandbox(code)
assert f"An error occurred: name '{builtin}' is not defined" in result
def test_restricted_sandbox_running_with_no_result_variable(
printer_mock, docker_unavailable_mock
):
"""Test behavior when no result variable is set in deprecated sandbox."""
tool = CodeInterpreterTool()
code = """
x = 10
"""
# Note: run_code_in_restricted_sandbox is deprecated and insecure
# This test verifies the old behavior but should not be used in production
result = tool.run_code_in_restricted_sandbox(code)
assert result == "No result variable found."
def test_unsafe_mode_running_with_no_result_variable(
printer_mock, docker_unavailable_mock
):
"""Test behavior when no result variable is set."""
tool = CodeInterpreterTool(unsafe_mode=True)
code = """
x = 10
"""
result = tool.run(code=code, libraries_used=[])
printer_mock.assert_called_with(
"WARNING: Running code in unsafe mode", color="bold_magenta"
)
assert result == "No result variable found."
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.subprocess.run")
def test_unsafe_mode_installs_libraries_without_shell(
subprocess_run_mock, printer_mock, docker_unavailable_mock
):
"""Test that library installation uses subprocess.run with shell=False, not os.system."""
tool = CodeInterpreterTool(unsafe_mode=True)
code = "result = 1"
libraries_used = ["numpy", "pandas"]
tool.run(code=code, libraries_used=libraries_used)
assert subprocess_run_mock.call_count == 2
for call, library in zip(subprocess_run_mock.call_args_list, libraries_used):
args, kwargs = call
# Must be list form (no shell expansion possible)
assert args[0] == [sys.executable, "-m", "pip", "install", library]
# shell= must not be True (defaults to False)
assert kwargs.get("shell", False) is False
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.subprocess.run")
def test_unsafe_mode_library_name_with_shell_metacharacters_does_not_invoke_shell(
subprocess_run_mock, printer_mock, docker_unavailable_mock
):
"""Test that a malicious library name cannot inject shell commands."""
tool = CodeInterpreterTool(unsafe_mode=True)
code = "result = 1"
malicious_library = "numpy; rm -rf /"
tool.run(code=code, libraries_used=[malicious_library])
subprocess_run_mock.assert_called_once()
args, kwargs = subprocess_run_mock.call_args
# The entire malicious string is passed as a single argument — no shell parsing
assert args[0] == [sys.executable, "-m", "pip", "install", malicious_library]
assert kwargs.get("shell", False) is False
def test_unsafe_mode_running_unsafe_code(printer_mock, docker_unavailable_mock):
"""Test behavior when no result variable is set."""
tool = CodeInterpreterTool(unsafe_mode=True)
code = """
import os
os.system("ls -la")
result = eval("5/1")
"""
result = tool.run(code=code, libraries_used=[])
printer_mock.assert_called_with(
"WARNING: Running code in unsafe mode", color="bold_magenta"
)
assert 5.0 == result
@pytest.mark.xfail(
reason=(
"run_code_in_restricted_sandbox is known to be vulnerable to sandbox "
"escape via object introspection. This test encodes the desired secure "
"behavior (no escape possible) and will start passing once the "
"vulnerability is fixed or the function is removed."
)
)
def test_sandbox_escape_vulnerability_demonstration(printer_mock):
"""Demonstrate that the restricted sandbox is vulnerable to escape attacks.
This test shows that an attacker can use Python object introspection to bypass
the restricted sandbox and access blocked modules like 'os'. This is why the
sandbox should never be used for untrusted code execution.
NOTE: This test uses the deprecated run_code_in_restricted_sandbox directly
to demonstrate the vulnerability. In production, Docker is now required.
"""
tool = CodeInterpreterTool()
# Classic Python sandbox escape via object introspection
escape_code = """
# Recover the real __import__ function via object introspection
for cls in ().__class__.__bases__[0].__subclasses__():
if cls.__name__ == 'catch_warnings':
# Get the real builtins module
real_builtins = cls()._module.__builtins__
real_import = real_builtins['__import__']
# Now we can import os and execute commands
os = real_import('os')
# Demonstrate we have escaped the sandbox
result = "SANDBOX_ESCAPED" if hasattr(os, 'system') else "FAILED"
break
"""
# The deprecated sandbox is vulnerable to this attack
result = tool.run_code_in_restricted_sandbox(escape_code)
# Desired behavior: the restricted sandbox should prevent this escape.
# If this assertion fails, run_code_in_restricted_sandbox remains vulnerable.
assert result != "SANDBOX_ESCAPED", (
"The restricted sandbox was bypassed via object introspection. "
"This indicates run_code_in_restricted_sandbox is still vulnerable and "
"is why Docker is now required for safe code execution."
)

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,6 @@ dependencies = [
"uv~=0.9.13",
"aiosqlite~=0.21.0",
"pyyaml~=6.0",
"aiofiles~=24.1.0",
"lancedb>=0.29.2,<0.30.1",
]
@@ -55,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
[project.optional-dependencies]
tools = [
"crewai-tools==1.14.0a3",
"crewai-tools==1.13.0a7",
]
embeddings = [
"tiktoken~=0.8.0"
@@ -84,7 +83,7 @@ voyageai = [
"voyageai~=0.3.5",
]
litellm = [
"litellm~=1.83.0",
"litellm>=1.74.9,<=1.82.6",
]
bedrock = [
"boto3~=1.40.45",
@@ -116,9 +115,6 @@ qdrant-edge = [
crewai = "crewai.cli.cli:crewai"
[tool.uv]
exclude-newer = "3 days"
# PyTorch index configuration, since torch 2.5.0 is not compatible with python 3.13
[[tool.uv.index]]
name = "pytorch-nightly"

View File

@@ -16,7 +16,7 @@ from crewai.knowledge.knowledge import Knowledge
from crewai.llm import LLM
from crewai.llms.base_llm import BaseLLM
from crewai.process import Process
from crewai.state.checkpoint_config import CheckpointConfig # noqa: F401
from crewai.runtime_state import _entity_discriminator
from crewai.task import Task
from crewai.tasks.llm_guardrail import LLMGuardrail
from crewai.tasks.task_output import TaskOutput
@@ -46,7 +46,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
_suppress_pydantic_deprecation_warnings()
__version__ = "1.14.0a3"
__version__ = "1.13.0a7"
_telemetry_submitted = False
@@ -99,8 +99,8 @@ def __getattr__(name: str) -> Any:
try:
from crewai.agents.agent_builder.base_agent import BaseAgent as _BaseAgent
from crewai.agents.agent_builder.base_agent_executor import (
BaseAgentExecutor as _BaseAgentExecutor,
from crewai.agents.agent_builder.base_agent_executor_mixin import (
CrewAgentExecutorMixin as _CrewAgentExecutorMixin,
)
from crewai.agents.tools_handler import ToolsHandler as _ToolsHandler
from crewai.experimental.agent_executor import AgentExecutor as _AgentExecutor
@@ -118,18 +118,10 @@ try:
"Flow": Flow,
"BaseLLM": BaseLLM,
"Task": Task,
"BaseAgentExecutor": _BaseAgentExecutor,
"CrewAgentExecutorMixin": _CrewAgentExecutorMixin,
"ExecutionContext": ExecutionContext,
"StandardPromptResult": _StandardPromptResult,
"SystemPromptResult": _SystemPromptResult,
}
from crewai.tools.base_tool import BaseTool as _BaseTool
from crewai.tools.structured_tool import CrewStructuredTool as _CrewStructuredTool
_base_namespace["BaseTool"] = _BaseTool
_base_namespace["CrewStructuredTool"] = _CrewStructuredTool
try:
from crewai.a2a.config import (
A2AClientConfig as _A2AClientConfig,
@@ -163,55 +155,41 @@ try:
**sys.modules[_BaseAgent.__module__].__dict__,
}
import crewai.state.runtime as _runtime_state_mod
for _mod_name in (
_BaseAgent.__module__,
Agent.__module__,
Crew.__module__,
Flow.__module__,
Task.__module__,
"crewai.agents.crew_agent_executor",
_runtime_state_mod.__name__,
_AgentExecutor.__module__,
):
sys.modules[_mod_name].__dict__.update(_resolve_namespace)
from crewai.agents.crew_agent_executor import (
CrewAgentExecutor as _CrewAgentExecutor,
)
from crewai.tasks.conditional_task import ConditionalTask as _ConditionalTask
_BaseAgentExecutor.model_rebuild(force=True, _types_namespace=_full_namespace)
_BaseAgent.model_rebuild(force=True, _types_namespace=_full_namespace)
Task.model_rebuild(force=True, _types_namespace=_full_namespace)
_ConditionalTask.model_rebuild(force=True, _types_namespace=_full_namespace)
_CrewAgentExecutor.model_rebuild(force=True, _types_namespace=_full_namespace)
Crew.model_rebuild(force=True, _types_namespace=_full_namespace)
Flow.model_rebuild(force=True, _types_namespace=_full_namespace)
_AgentExecutor.model_rebuild(force=True, _types_namespace=_full_namespace)
from typing import Annotated
from pydantic import Field
from crewai.state.runtime import RuntimeState
from pydantic import Discriminator, RootModel, Tag
Entity = Annotated[
Flow | Crew | Agent, # type: ignore[type-arg]
Field(discriminator="entity_type"),
Annotated[Flow, Tag("flow")] # type: ignore[type-arg]
| Annotated[Crew, Tag("crew")]
| Annotated[Agent, Tag("agent")],
Discriminator(_entity_discriminator),
]
RuntimeState.model_rebuild(
force=True,
_types_namespace={**_full_namespace, "Entity": Entity},
)
RuntimeState = RootModel[list[Entity]]
try:
Agent.model_rebuild(force=True, _types_namespace=_full_namespace)
except PydanticUserError:
pass
except (ImportError, PydanticUserError):
import logging as _logging
@@ -227,7 +205,6 @@ __all__ = [
"BaseLLM",
"Crew",
"CrewOutput",
"Entity",
"ExecutionContext",
"Flow",
"Knowledge",

View File

@@ -9,6 +9,8 @@ import contextvars
from datetime import datetime
import json
from pathlib import Path
import shutil
import subprocess
import time
from typing import (
TYPE_CHECKING,
@@ -25,6 +27,7 @@ from pydantic import (
BeforeValidator,
ConfigDict,
Field,
InstanceOf,
PrivateAttr,
model_validator,
)
@@ -114,6 +117,7 @@ except ImportError:
if TYPE_CHECKING:
from crewai_files import FileInput
from crewai_tools import CodeInterpreterTool
from crewai.a2a.config import A2AClientConfig, A2AConfig, A2AServerConfig
from crewai.agents.agent_builder.base_agent import PlatformAppOrAction
@@ -191,12 +195,12 @@ class Agent(BaseAgent):
llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(description="Language model that will run the agent.", default=None)
function_calling_llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(description="Language model that will run the agent.", default=None)
system_template: str | None = Field(
default=None, description="System format for the agent."
@@ -208,9 +212,7 @@ class Agent(BaseAgent):
default=None, description="Response format for the agent."
)
allow_code_execution: bool | None = Field(
default=False,
deprecated=True,
description="Deprecated. CodeInterpreterTool is no longer available. Use dedicated sandbox services instead.",
default=False, description="Enable code execution for the agent."
)
respect_context_window: bool = Field(
default=True,
@@ -235,8 +237,7 @@ class Agent(BaseAgent):
)
code_execution_mode: Literal["safe", "unsafe"] = Field(
default="safe",
deprecated=True,
description="Deprecated. CodeInterpreterTool is no longer available. Use dedicated sandbox services instead.",
description="Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution).",
)
planning_config: PlanningConfig | None = Field(
default=None,
@@ -296,8 +297,8 @@ class Agent(BaseAgent):
Can be a single A2AConfig/A2AClientConfig/A2AServerConfig, or a list of any number of A2AConfig/A2AClientConfig with a single A2AServerConfig.
""",
)
agent_executor: CrewAgentExecutor | AgentExecutor | None = Field(
default=None, description="An instance of the CrewAgentExecutor class."
agent_executor: InstanceOf[CrewAgentExecutor] | InstanceOf[AgentExecutor] | None = (
Field(default=None, description="An instance of the CrewAgentExecutor class.")
)
executor_class: Annotated[
type[CrewAgentExecutor] | type[AgentExecutor],
@@ -329,13 +330,7 @@ class Agent(BaseAgent):
self._setup_agent_executor()
if self.allow_code_execution:
warnings.warn(
"allow_code_execution is deprecated and will be removed in v2.0. "
"CodeInterpreterTool is no longer available. "
"Use dedicated sandbox services like E2B or Modal.",
DeprecationWarning,
stacklevel=2,
)
self._validate_docker_installation()
self.set_skills()
@@ -1016,10 +1011,10 @@ class Agent(BaseAgent):
)
self.agent_executor = self.executor_class(
llm=self.llm,
task=task,
task=task, # type: ignore[arg-type]
i18n=self.i18n,
agent=self,
crew=self.crew,
crew=self.crew, # type: ignore[arg-type]
tools=parsed_tools,
prompt=prompt,
original_tools=raw_tools,
@@ -1062,8 +1057,7 @@ class Agent(BaseAgent):
if self.agent_executor is None:
raise RuntimeError("Agent executor is not initialized.")
if task is not None:
self.agent_executor.task = task
self.agent_executor.task = task
self.agent_executor.tools = tools
self.agent_executor.original_tools = raw_tools
self.agent_executor.prompt = prompt
@@ -1082,7 +1076,7 @@ class Agent(BaseAgent):
self.agent_executor.tools_handler = self.tools_handler
self.agent_executor.request_within_rpm_limit = rpm_limit_fn
if isinstance(self.agent_executor.llm, BaseLLM):
if self.agent_executor.llm:
existing_stop = getattr(self.agent_executor.llm, "stop", [])
self.agent_executor.llm.stop = list(
set(
@@ -1129,15 +1123,20 @@ class Agent(BaseAgent):
return [AddImageTool()]
def get_code_execution_tools(self) -> list[Any]:
"""Deprecated: CodeInterpreterTool is no longer available."""
warnings.warn(
"CodeInterpreterTool is no longer available. "
"Use dedicated sandbox services like E2B or Modal.",
DeprecationWarning,
stacklevel=2,
)
return []
def get_code_execution_tools(self) -> list[CodeInterpreterTool]:
"""Return code interpreter tools based on the agent's execution mode."""
try:
from crewai_tools import (
CodeInterpreterTool,
)
unsafe_mode = self.code_execution_mode == "unsafe"
return [CodeInterpreterTool(unsafe_mode=unsafe_mode)]
except ModuleNotFoundError:
self._logger.log(
"info", "Coding tools not available. Install crewai_tools. "
)
return []
@staticmethod
def get_output_converter(
@@ -1217,14 +1216,28 @@ class Agent(BaseAgent):
self._logger.log("warning", f"Failed to inject date: {e!s}")
def _validate_docker_installation(self) -> None:
"""Deprecated: No-op. CodeInterpreterTool is no longer available."""
warnings.warn(
"CodeInterpreterTool is no longer available. "
"Use dedicated sandbox services like E2B or Modal.",
DeprecationWarning,
stacklevel=2,
)
return
"""Check if Docker is installed and running."""
docker_path = shutil.which("docker")
if not docker_path:
raise RuntimeError(
f"Docker is not installed. Please install Docker to use code execution with agent: {self.role}"
)
try:
subprocess.run( # noqa: S603
[str(docker_path), "info"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except subprocess.CalledProcessError as e:
raise RuntimeError(
f"Docker is not running. Please start Docker to use code execution with agent: {self.role}"
) from e
except subprocess.TimeoutExpired as e:
raise RuntimeError(
f"Docker command timed out. Please check your Docker installation for agent: {self.role}"
) from e
def __repr__(self) -> str:
return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"

View File

@@ -14,8 +14,8 @@ from pydantic import (
BaseModel,
BeforeValidator,
Field,
InstanceOf,
PrivateAttr,
SerializeAsAny,
field_validator,
model_validator,
)
@@ -24,7 +24,7 @@ from pydantic_core import PydanticCustomError
from typing_extensions import Self
from crewai.agent.internal.meta import AgentMeta
from crewai.agents.agent_builder.base_agent_executor import BaseAgentExecutor
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
from crewai.agents.cache.cache_handler import CacheHandler
from crewai.agents.tools_handler import ToolsHandler
@@ -39,7 +39,6 @@ from crewai.memory.unified_memory import Memory
from crewai.rag.embeddings.types import EmbedderConfig
from crewai.security.security_config import SecurityConfig
from crewai.skills.models import Skill
from crewai.state.checkpoint_config import CheckpointConfig
from crewai.tools.base_tool import BaseTool, Tool
from crewai.types.callback import SerializableCallable
from crewai.utilities.config import process_config
@@ -52,7 +51,6 @@ from crewai.utilities.string_utils import interpolate_only
if TYPE_CHECKING:
from crewai.context import ExecutionContext
from crewai.crew import Crew
from crewai.state.provider.core import BaseProvider
def _validate_crew_ref(value: Any) -> Any:
@@ -65,31 +63,7 @@ def _serialize_crew_ref(value: Any) -> str | None:
return str(value.id) if hasattr(value, "id") else str(value)
_LLM_TYPE_REGISTRY: dict[str, str] = {
"base": "crewai.llms.base_llm.BaseLLM",
"litellm": "crewai.llm.LLM",
"openai": "crewai.llms.providers.openai.completion.OpenAICompletion",
"anthropic": "crewai.llms.providers.anthropic.completion.AnthropicCompletion",
"azure": "crewai.llms.providers.azure.completion.AzureCompletion",
"bedrock": "crewai.llms.providers.bedrock.completion.BedrockCompletion",
"gemini": "crewai.llms.providers.gemini.completion.GeminiCompletion",
}
def _validate_llm_ref(value: Any) -> Any:
if isinstance(value, dict):
import importlib
llm_type = value.get("llm_type")
if not llm_type or llm_type not in _LLM_TYPE_REGISTRY:
raise ValueError(
f"Unknown or missing llm_type: {llm_type!r}. "
f"Expected one of {list(_LLM_TYPE_REGISTRY)}"
)
dotted = _LLM_TYPE_REGISTRY[llm_type]
mod_path, cls_name = dotted.rsplit(".", 1)
cls = getattr(importlib.import_module(mod_path), cls_name)
return cls(**value)
return value
@@ -101,37 +75,12 @@ def _resolve_agent(value: Any, info: Any) -> Any:
return Agent.model_validate(value, context=getattr(info, "context", None))
_EXECUTOR_TYPE_REGISTRY: dict[str, str] = {
"base": "crewai.agents.agent_builder.base_agent_executor.BaseAgentExecutor",
"crew": "crewai.agents.crew_agent_executor.CrewAgentExecutor",
"experimental": "crewai.experimental.agent_executor.AgentExecutor",
}
def _validate_executor_ref(value: Any) -> Any:
if isinstance(value, dict):
import importlib
executor_type = value.get("executor_type")
if not executor_type or executor_type not in _EXECUTOR_TYPE_REGISTRY:
raise ValueError(
f"Unknown or missing executor_type: {executor_type!r}. "
f"Expected one of {list(_EXECUTOR_TYPE_REGISTRY)}"
)
dotted = _EXECUTOR_TYPE_REGISTRY[executor_type]
mod_path, cls_name = dotted.rsplit(".", 1)
cls = getattr(importlib.import_module(mod_path), cls_name)
return cls.model_validate(value)
return value
def _serialize_llm_ref(value: Any) -> dict[str, Any] | None:
def _serialize_llm_ref(value: Any) -> str | None:
if value is None:
return None
if isinstance(value, str):
return {"model": value}
result: dict[str, Any] = value.model_dump()
return result
return value
return getattr(value, "model", str(value))
_SLUG_RE: Final[re.Pattern[str]] = re.compile(
@@ -248,19 +197,13 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
max_iter: int = Field(
default=25, description="Maximum iterations for an agent to execute a task"
)
agent_executor: SerializeAsAny[BaseAgentExecutor] | None = Field(
agent_executor: InstanceOf[CrewAgentExecutorMixin] | None = Field(
default=None, description="An instance of the CrewAgentExecutor class."
)
@field_validator("agent_executor", mode="before")
@classmethod
def _validate_agent_executor(cls, v: Any) -> Any:
return _validate_executor_ref(v)
llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(default=None, description="Language model that will run the agent.")
crew: Annotated[
Crew | str | None,
@@ -300,11 +243,6 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
default_factory=SecurityConfig,
description="Security configuration for the agent, including fingerprinting.",
)
checkpoint: CheckpointConfig | bool | None = Field(
default=None,
description="Automatic checkpointing configuration. "
"True for defaults, False to opt out, None to inherit.",
)
callbacks: list[SerializableCallable] = Field(
default_factory=list, description="Callbacks to be used for the agent"
)
@@ -338,30 +276,6 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
)
execution_context: ExecutionContext | None = Field(default=None)
@classmethod
def from_checkpoint(
cls, path: str, *, provider: BaseProvider | None = None
) -> Self:
"""Restore an Agent from a checkpoint file."""
from crewai.context import apply_execution_context
from crewai.state.provider.json_provider import JsonProvider
from crewai.state.runtime import RuntimeState
state = RuntimeState.from_checkpoint(
path,
provider=provider or JsonProvider(),
context={"from_checkpoint": True},
)
for entity in state.root:
if isinstance(entity, cls):
if entity.execution_context is not None:
apply_execution_context(entity.execution_context)
if entity.agent_executor is not None:
entity.agent_executor.agent = entity
entity.agent_executor._resuming = True
return entity
raise ValueError(f"No {cls.__name__} found in checkpoint: {path}")
@model_validator(mode="before")
@classmethod
def process_model_config(cls, values: Any) -> dict[str, Any]:

View File

@@ -2,40 +2,37 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel, Field, PrivateAttr
from crewai.agents.parser import AgentFinish
from crewai.memory.utils import sanitize_scope_name
from crewai.utilities.printer import Printer
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.types import LLMMessage
if TYPE_CHECKING:
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agent import Agent
from crewai.crew import Crew
from crewai.task import Task
from crewai.utilities.i18n import I18N
from crewai.utilities.types import LLMMessage
class BaseAgentExecutor(BaseModel):
model_config = {"arbitrary_types_allowed": True}
executor_type: str = "base"
crew: Crew | None = Field(default=None, exclude=True)
agent: BaseAgent | None = Field(default=None, exclude=True)
task: Task | None = Field(default=None, exclude=True)
iterations: int = Field(default=0)
max_iter: int = Field(default=25)
messages: list[LLMMessage] = Field(default_factory=list)
_resuming: bool = PrivateAttr(default=False)
_i18n: I18N | None = PrivateAttr(default=None)
_printer: Printer = PrivateAttr(default_factory=Printer)
class CrewAgentExecutorMixin:
crew: Crew | None
agent: Agent
task: Task | None
iterations: int
max_iter: int
messages: list[LLMMessage]
_i18n: I18N
_printer: Printer = Printer()
def _save_to_memory(self, output: AgentFinish) -> None:
"""Save task result to unified memory (memory or crew._memory)."""
if self.agent is None:
return
"""Save task result to unified memory (memory or crew._memory).
Extends the memory's root_scope with agent-specific path segment
(e.g., '/crew/research-crew/agent/researcher') so that agent memories
are scoped hierarchically under their crew.
"""
memory = getattr(self.agent, "memory", None) or (
getattr(self.crew, "_memory", None) if self.crew else None
)
@@ -52,9 +49,11 @@ class BaseAgentExecutor(BaseModel):
)
extracted = memory.extract_memories(raw)
if extracted:
# Get the memory's existing root_scope
base_root = getattr(memory, "root_scope", None)
if isinstance(base_root, str) and base_root:
# Memory has a root_scope — extend it with agent info
agent_role = self.agent.role or "unknown"
sanitized_role = sanitize_scope_name(agent_role)
agent_root = f"{base_root.rstrip('/')}/agent/{sanitized_role}"
@@ -64,6 +63,7 @@ class BaseAgentExecutor(BaseModel):
extracted, agent_role=self.agent.role, root_scope=agent_root
)
else:
# No base root_scope — don't inject one, preserve backward compat
memory.remember_many(extracted, agent_role=self.agent.role)
except Exception as e:
self.agent._logger.log("error", f"Failed to save to memory: {e}")

View File

@@ -1,34 +1,71 @@
"""Token usage tracking utilities."""
"""Token usage tracking utilities.
from pydantic import BaseModel, Field
This module provides utilities for tracking token consumption and request
metrics during agent execution.
"""
from crewai.types.usage_metrics import UsageMetrics
class TokenProcess(BaseModel):
"""Track token usage during agent processing."""
class TokenProcess:
"""Track token usage during agent processing.
total_tokens: int = Field(default=0)
prompt_tokens: int = Field(default=0)
cached_prompt_tokens: int = Field(default=0)
completion_tokens: int = Field(default=0)
successful_requests: int = Field(default=0)
Attributes:
total_tokens: Total number of tokens used.
prompt_tokens: Number of tokens used in prompts.
cached_prompt_tokens: Number of cached prompt tokens used.
completion_tokens: Number of tokens used in completions.
successful_requests: Number of successful requests made.
"""
def __init__(self) -> None:
"""Initialize token tracking with zero values."""
self.total_tokens: int = 0
self.prompt_tokens: int = 0
self.cached_prompt_tokens: int = 0
self.completion_tokens: int = 0
self.successful_requests: int = 0
def sum_prompt_tokens(self, tokens: int) -> None:
"""Add prompt tokens to the running totals.
Args:
tokens: Number of prompt tokens to add.
"""
self.prompt_tokens += tokens
self.total_tokens += tokens
def sum_completion_tokens(self, tokens: int) -> None:
"""Add completion tokens to the running totals.
Args:
tokens: Number of completion tokens to add.
"""
self.completion_tokens += tokens
self.total_tokens += tokens
def sum_cached_prompt_tokens(self, tokens: int) -> None:
"""Add cached prompt tokens to the running total.
Args:
tokens: Number of cached prompt tokens to add.
"""
self.cached_prompt_tokens += tokens
def sum_successful_requests(self, requests: int) -> None:
"""Add successful requests to the running total.
Args:
requests: Number of successful requests to add.
"""
self.successful_requests += requests
def get_summary(self) -> UsageMetrics:
"""Get a summary of all tracked metrics.
Returns:
UsageMetrics object with current totals.
"""
return UsageMetrics(
total_tokens=self.total_tokens,
prompt_tokens=self.prompt_tokens,

View File

@@ -1,4 +1,3 @@
# mypy: disable-error-code="union-attr,arg-type"
"""Agent executor for crew AI agents.
Handles agent execution flow including LLM interactions, tool execution,
@@ -13,20 +12,12 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
import contextvars
import inspect
import logging
from typing import TYPE_CHECKING, Annotated, Any, Literal, cast
from typing import TYPE_CHECKING, Any, Literal, cast
from pydantic import (
AliasChoices,
BaseModel,
BeforeValidator,
ConfigDict,
Field,
ValidationError,
)
from pydantic.functional_serializers import PlainSerializer
from pydantic import BaseModel, GetCoreSchemaHandler, ValidationError
from pydantic_core import CoreSchema, core_schema
from crewai.agents.agent_builder.base_agent import _serialize_llm_ref, _validate_llm_ref
from crewai.agents.agent_builder.base_agent_executor import BaseAgentExecutor
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
from crewai.agents.parser import (
AgentAction,
AgentFinish,
@@ -47,7 +38,6 @@ from crewai.hooks.tool_hooks import (
get_after_tool_call_hooks,
get_before_tool_call_hooks,
)
from crewai.types.callback import SerializableCallable
from crewai.utilities.agent_utils import (
aget_llm_response,
convert_tools_to_openai_schema,
@@ -68,8 +58,8 @@ from crewai.utilities.agent_utils import (
from crewai.utilities.constants import TRAINING_DATA_FILE
from crewai.utilities.file_store import aget_all_files, get_all_files
from crewai.utilities.i18n import I18N, get_i18n
from crewai.utilities.printer import Printer
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.token_counter_callback import TokenCalcHandler
from crewai.utilities.tool_utils import (
aexecute_tool_and_check_finality,
execute_tool_and_check_finality,
@@ -80,8 +70,11 @@ from crewai.utilities.training_handler import CrewTrainingHandler
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from crewai.agent import Agent
from crewai.agents.tools_handler import ToolsHandler
from crewai.crew import Crew
from crewai.llms.base_llm import BaseLLM
from crewai.task import Task
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.tools.tool_types import ToolResult
@@ -89,59 +82,87 @@ if TYPE_CHECKING:
from crewai.utilities.types import LLMMessage
class CrewAgentExecutor(BaseAgentExecutor):
class CrewAgentExecutor(CrewAgentExecutorMixin):
"""Executor for crew agents.
Manages the execution lifecycle of an agent including prompt formatting,
LLM interactions, tool execution, and feedback handling.
"""
executor_type: Literal["crew"] = "crew"
llm: Annotated[
BaseLLM | str | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
] = Field(default=None)
prompt: SystemPromptResult | StandardPromptResult | None = Field(default=None)
tools: list[CrewStructuredTool] = Field(default_factory=list)
tools_names: str = Field(default="")
stop: list[str] = Field(
default_factory=list, validation_alias=AliasChoices("stop", "stop_words")
)
tools_description: str = Field(default="")
tools_handler: ToolsHandler | None = Field(default=None)
step_callback: SerializableCallable | None = Field(default=None, exclude=True)
original_tools: list[BaseTool] = Field(default_factory=list)
function_calling_llm: Annotated[
BaseLLM | str | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
] = Field(default=None)
respect_context_window: bool = Field(default=False)
request_within_rpm_limit: SerializableCallable | None = Field(
default=None, exclude=True
)
callbacks: list[TokenCalcHandler] = Field(default_factory=list, exclude=True)
response_model: type[BaseModel] | None = Field(default=None, exclude=True)
ask_for_human_input: bool = Field(default=False)
log_error_after: int = Field(default=3)
before_llm_call_hooks: list[SerializableCallable] = Field(
default_factory=list, exclude=True
)
after_llm_call_hooks: list[SerializableCallable] = Field(
default_factory=list, exclude=True
)
def __init__(
self,
llm: BaseLLM,
task: Task,
crew: Crew,
agent: Agent,
prompt: SystemPromptResult | StandardPromptResult,
max_iter: int,
tools: list[CrewStructuredTool],
tools_names: str,
stop_words: list[str],
tools_description: str,
tools_handler: ToolsHandler,
step_callback: Any = None,
original_tools: list[BaseTool] | None = None,
function_calling_llm: BaseLLM | Any | None = None,
respect_context_window: bool = False,
request_within_rpm_limit: Callable[[], bool] | None = None,
callbacks: list[Any] | None = None,
response_model: type[BaseModel] | None = None,
i18n: I18N | None = None,
) -> None:
"""Initialize executor.
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True)
def __init__(self, i18n: I18N | None = None, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._i18n = i18n or get_i18n()
if not self.before_llm_call_hooks:
self.before_llm_call_hooks.extend(get_before_llm_call_hooks())
if not self.after_llm_call_hooks:
self.after_llm_call_hooks.extend(get_after_llm_call_hooks())
if self.llm and not isinstance(self.llm, str):
Args:
llm: Language model instance.
task: Task to execute.
crew: Crew instance.
agent: Agent to execute.
prompt: Prompt templates.
max_iter: Maximum iterations.
tools: Available tools.
tools_names: Tool names string.
stop_words: Stop word list.
tools_description: Tool descriptions.
tools_handler: Tool handler instance.
step_callback: Optional step callback.
original_tools: Original tool list.
function_calling_llm: Optional function calling LLM.
respect_context_window: Respect context limits.
request_within_rpm_limit: RPM limit check function.
callbacks: Optional callbacks list.
response_model: Optional Pydantic model for structured outputs.
"""
self._i18n: I18N = i18n or get_i18n()
self.llm = llm
self.task = task
self.agent = agent
self.crew = crew
self.prompt = prompt
self.tools = tools
self.tools_names = tools_names
self.stop = stop_words
self.max_iter = max_iter
self.callbacks = callbacks or []
self._printer: Printer = Printer()
self.tools_handler = tools_handler
self.original_tools = original_tools or []
self.step_callback = step_callback
self.tools_description = tools_description
self.function_calling_llm = function_calling_llm
self.respect_context_window = respect_context_window
self.request_within_rpm_limit = request_within_rpm_limit
self.response_model = response_model
self.ask_for_human_input = False
self.messages: list[LLMMessage] = []
self.iterations = 0
self.log_error_after = 3
self.before_llm_call_hooks: list[Callable[..., Any]] = []
self.after_llm_call_hooks: list[Callable[..., Any]] = []
self.before_llm_call_hooks.extend(get_before_llm_call_hooks())
self.after_llm_call_hooks.extend(get_after_llm_call_hooks())
if self.llm:
# This may be mutating the shared llm object and needs further evaluation
existing_stop = getattr(self.llm, "stop", [])
self.llm.stop = list(
set(
@@ -158,11 +179,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
Returns:
bool: True if tool should be used or not.
"""
from crewai.llms.base_llm import BaseLLM
return (
self.llm.supports_stop_words() if isinstance(self.llm, BaseLLM) else False
)
return self.llm.supports_stop_words() if self.llm else False
def _setup_messages(self, inputs: dict[str, Any]) -> None:
"""Set up messages for the agent execution.
@@ -174,7 +191,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
if provider.setup_messages(cast(ExecutorContext, cast(object, self))):
return
if self.prompt is not None and "system" in self.prompt:
if "system" in self.prompt:
system_prompt = self._format_prompt(
cast(str, self.prompt.get("system", "")), inputs
)
@@ -183,7 +200,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
)
self.messages.append(format_message_for_llm(system_prompt, role="system"))
self.messages.append(format_message_for_llm(user_prompt))
elif self.prompt is not None:
else:
user_prompt = self._format_prompt(self.prompt.get("prompt", ""), inputs)
self.messages.append(format_message_for_llm(user_prompt))
@@ -198,11 +215,9 @@ class CrewAgentExecutor(BaseAgentExecutor):
Returns:
Dictionary with agent output.
"""
if self._resuming:
self._resuming = False
else:
self._setup_messages(inputs)
self._inject_multimodal_files(inputs)
self._setup_messages(inputs)
self._inject_multimodal_files(inputs)
self._show_start_logs()
@@ -329,7 +344,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=self._printer,
i18n=self._i18n,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
verbose=self.agent.verbose,
)
@@ -338,7 +353,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
answer = get_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -413,8 +428,8 @@ class CrewAgentExecutor(BaseAgentExecutor):
formatted_answer, tool_result
)
self._invoke_step_callback(formatted_answer)
self._append_message(formatted_answer.text)
self._invoke_step_callback(formatted_answer) # type: ignore[arg-type]
self._append_message(formatted_answer.text) # type: ignore[union-attr]
except OutputParserError as e:
formatted_answer = handle_output_parser_exception( # type: ignore[assignment]
@@ -435,7 +450,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
respect_context_window=self.respect_context_window,
printer=self._printer,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
i18n=self._i18n,
verbose=self.agent.verbose,
@@ -485,7 +500,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=self._printer,
i18n=self._i18n,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
verbose=self.agent.verbose,
)
@@ -499,7 +514,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
# without executing them. The executor handles tool execution
# via _handle_native_tool_calls to properly manage message history.
answer = get_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -572,7 +587,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
respect_context_window=self.respect_context_window,
printer=self._printer,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
i18n=self._i18n,
verbose=self.agent.verbose,
@@ -592,7 +607,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
answer = get_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -951,7 +966,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
before_hook_context = ToolCallHookContext(
tool_name=func_name,
tool_input=args_dict or {},
tool=structured_tool,
tool=structured_tool, # type: ignore[arg-type]
agent=self.agent,
task=self.task,
crew=self.crew,
@@ -1016,7 +1031,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
after_hook_context = ToolCallHookContext(
tool_name=func_name,
tool_input=args_dict or {},
tool=structured_tool,
tool=structured_tool, # type: ignore[arg-type]
agent=self.agent,
task=self.task,
crew=self.crew,
@@ -1104,11 +1119,9 @@ class CrewAgentExecutor(BaseAgentExecutor):
Returns:
Dictionary with agent output.
"""
if self._resuming:
self._resuming = False
else:
self._setup_messages(inputs)
await self._ainject_multimodal_files(inputs)
self._setup_messages(inputs)
await self._ainject_multimodal_files(inputs)
self._show_start_logs()
@@ -1171,7 +1184,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=self._printer,
i18n=self._i18n,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
verbose=self.agent.verbose,
)
@@ -1180,7 +1193,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
answer = await aget_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -1254,8 +1267,8 @@ class CrewAgentExecutor(BaseAgentExecutor):
formatted_answer, tool_result
)
await self._ainvoke_step_callback(formatted_answer)
self._append_message(formatted_answer.text)
await self._ainvoke_step_callback(formatted_answer) # type: ignore[arg-type]
self._append_message(formatted_answer.text) # type: ignore[union-attr]
except OutputParserError as e:
formatted_answer = handle_output_parser_exception( # type: ignore[assignment]
@@ -1275,7 +1288,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
respect_context_window=self.respect_context_window,
printer=self._printer,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
i18n=self._i18n,
verbose=self.agent.verbose,
@@ -1319,7 +1332,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
printer=self._printer,
i18n=self._i18n,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
verbose=self.agent.verbose,
)
@@ -1333,7 +1346,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
# without executing them. The executor handles tool execution
# via _handle_native_tool_calls to properly manage message history.
answer = await aget_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -1405,7 +1418,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
respect_context_window=self.respect_context_window,
printer=self._printer,
messages=self.messages,
llm=cast("BaseLLM", self.llm),
llm=self.llm,
callbacks=self.callbacks,
i18n=self._i18n,
verbose=self.agent.verbose,
@@ -1425,7 +1438,7 @@ class CrewAgentExecutor(BaseAgentExecutor):
enforce_rpm_limit(self.request_within_rpm_limit)
answer = await aget_llm_response(
llm=cast("BaseLLM", self.llm),
llm=self.llm,
messages=self.messages,
callbacks=self.callbacks,
printer=self._printer,
@@ -1674,3 +1687,14 @@ class CrewAgentExecutor(BaseAgentExecutor):
return format_message_for_llm(
self._i18n.slice("feedback_instructions").format(feedback=feedback)
)
@classmethod
def __get_pydantic_core_schema__(
cls, _source_type: Any, _handler: GetCoreSchemaHandler
) -> CoreSchema:
"""Generate Pydantic core schema for BaseClient Protocol.
This allows the Protocol to be used in Pydantic models without
requiring arbitrary_types_allowed=True.
"""
return core_schema.any_schema()

View File

@@ -30,7 +30,7 @@ from crewai.utilities.types import LLMMessage
if TYPE_CHECKING:
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agent import Agent
from crewai.task import Task
logger = logging.getLogger(__name__)
@@ -56,7 +56,7 @@ class PlannerObserver:
def __init__(
self,
agent: BaseAgent,
agent: Agent,
task: Task | None = None,
kickoff_input: str = "",
) -> None:

View File

@@ -48,7 +48,7 @@ from crewai.utilities.types import LLMMessage
if TYPE_CHECKING:
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agent import Agent
from crewai.agents.tools_handler import ToolsHandler
from crewai.crew import Crew
from crewai.llms.base_llm import BaseLLM
@@ -88,7 +88,7 @@ class StepExecutor:
self,
llm: BaseLLM,
tools: list[CrewStructuredTool],
agent: BaseAgent,
agent: Agent,
original_tools: list[BaseTool] | None = None,
tools_handler: ToolsHandler | None = None,
task: Task | None = None,

View File

@@ -27,7 +27,7 @@ from crewai.cli.tools.main import ToolCommand
from crewai.cli.train_crew import train_crew
from crewai.cli.triggers.main import TriggersCommand
from crewai.cli.update_crew import update_crew
from crewai.cli.utils import build_env_with_all_tool_credentials, read_toml
from crewai.cli.utils import build_env_with_tool_repository_credentials, read_toml
from crewai.memory.storage.kickoff_task_outputs_storage import (
KickoffTaskOutputsSQLiteStorage,
)
@@ -48,18 +48,24 @@ def crewai() -> None:
@click.argument("uv_args", nargs=-1, type=click.UNPROCESSED)
def uv(uv_args: tuple[str, ...]) -> None:
"""A wrapper around uv commands that adds custom tool authentication through env vars."""
env = os.environ.copy()
try:
# Verify pyproject.toml exists first
read_toml()
except FileNotFoundError as e:
pyproject_data = read_toml()
sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
for source_config in sources.values():
if isinstance(source_config, dict):
index = source_config.get("index")
if index:
index_env = build_env_with_tool_repository_credentials(index)
env.update(index_env)
except (FileNotFoundError, KeyError) as e:
raise SystemExit(
"Error. A valid pyproject.toml file is required. Check that a valid pyproject.toml file exists in the current directory."
) from e
except Exception as e:
raise SystemExit(f"Error: {e}") from e
env = build_env_with_all_tool_credentials()
try:
subprocess.run( # noqa: S603
["uv", *uv_args], # noqa: S607

View File

@@ -46,7 +46,7 @@ def create_flow(name: str) -> None:
tools_template_files = ["tools/__init__.py", "tools/custom_tool.py"]
crew_folders = [
"content_crew",
"poem_crew",
]
def process_file(src_file: Path, dst_file: Path) -> None:

View File

@@ -2,8 +2,6 @@ import subprocess
import click
from crewai.cli.utils import build_env_with_all_tool_credentials
# Be mindful about changing this.
# on some environments we don't use this command but instead uv sync directly
@@ -15,14 +13,7 @@ def install_crew(proxy_options: list[str]) -> None:
"""
try:
command = ["uv", "sync", *proxy_options]
# Inject tool repository credentials so uv can authenticate
# against private package indexes (e.g. crewai tool repository).
# Without this, `uv sync` fails with 401 Unauthorized when the
# project depends on tools from a private index.
env = build_env_with_all_tool_credentials()
subprocess.run(command, check=True, capture_output=False, text=True, env=env) # noqa: S603
subprocess.run(command, check=True, capture_output=False, text=True) # noqa: S603
except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while running the crew: {e}", err=True)

View File

@@ -1,10 +1,11 @@
from enum import Enum
import os
import subprocess
import click
from packaging import version
from crewai.cli.utils import build_env_with_all_tool_credentials, read_toml
from crewai.cli.utils import build_env_with_tool_repository_credentials, read_toml
from crewai.cli.version import get_crewai_version
@@ -55,7 +56,19 @@ def execute_command(crew_type: CrewType) -> None:
"""
command = ["uv", "run", "kickoff" if crew_type == CrewType.FLOW else "run_crew"]
env = build_env_with_all_tool_credentials()
env = os.environ.copy()
try:
pyproject_data = read_toml()
sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
for source_config in sources.values():
if isinstance(source_config, dict):
index = source_config.get("index")
if index:
index_env = build_env_with_tool_repository_credentials(index)
env.update(index_env)
except Exception: # noqa: S110
pass
try:
subprocess.run(command, capture_output=False, text=True, check=True, env=env) # noqa: S603

View File

@@ -120,11 +120,11 @@ my_crew/
my_flow/
├── src/my_flow/
│ ├── crews/ # Multiple crew definitions
│ │ └── content_crew/
│ │ └── poem_crew/
│ │ ├── config/
│ │ │ ├── agents.yaml
│ │ │ └── tasks.yaml
│ │ └── content_crew.py
│ │ └── poem_crew.py
│ ├── tools/ # Custom tools
│ ├── main.py # Flow orchestration
│ └── ...

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.14.0a3"
"crewai[tools]==1.13.0a7"
]
[project.scripts]

View File

@@ -38,7 +38,7 @@ crewai run
This command initializes the {{name}} Flow as defined in your configuration.
This example, unmodified, will run a content creation flow on AI Agents and save the output to `output/post.md`.
This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder.
## Understanding Your Crew

View File

@@ -1,33 +0,0 @@
planner:
role: >
Content Planner
goal: >
Plan a detailed and engaging blog post outline on {topic}
backstory: >
You're an experienced content strategist who excels at creating
structured outlines for blog posts. You know how to organize ideas
into a logical flow that keeps readers engaged from start to finish.
writer:
role: >
Content Writer
goal: >
Write a compelling and well-structured blog post on {topic}
based on the provided outline
backstory: >
You're a skilled writer with a talent for turning outlines into
engaging, informative blog posts. Your writing is clear, conversational,
and backed by solid reasoning. You adapt your tone to the subject matter
while keeping things accessible to a broad audience.
editor:
role: >
Content Editor
goal: >
Review and polish the blog post on {topic} to ensure it is
publication-ready
backstory: >
You're a meticulous editor with years of experience refining written
content. You have an eye for clarity, flow, grammar, and consistency.
You improve prose without changing the author's voice and ensure every
piece you touch is polished and professional.

View File

@@ -1,50 +0,0 @@
planning_task:
description: >
Create a detailed outline for a blog post about {topic}.
The outline should include:
- A compelling title
- An introduction hook
- 3-5 main sections with key points to cover in each
- A conclusion with a call to action
Make the outline detailed enough that a writer can produce
a full blog post from it without additional research.
expected_output: >
A structured blog post outline with a title, introduction notes,
detailed section breakdowns, and conclusion notes.
agent: planner
writing_task:
description: >
Using the outline provided, write a full blog post about {topic}.
Requirements:
- Follow the outline structure closely
- Write in a clear, engaging, and conversational tone
- Each section should be 2-3 paragraphs
- Include a strong introduction and conclusion
- Target around 800-1200 words
expected_output: >
A complete blog post in markdown format, ready for editing.
The post should follow the outline and be well-written with
clear transitions between sections.
agent: writer
editing_task:
description: >
Review and edit the blog post about {topic}.
Focus on:
- Fixing any grammar or spelling errors
- Improving sentence clarity and flow
- Ensuring consistent tone throughout
- Strengthening the introduction and conclusion
- Removing any redundancy
Do not rewrite the post — refine and polish it.
expected_output: >
The final, polished blog post in markdown format without '```'.
Publication-ready with clean formatting and professional prose.
agent: editor
output_file: output/post.md

View File

@@ -0,0 +1 @@
"""Poem crew template."""

View File

@@ -0,0 +1,11 @@
poem_writer:
role: >
CrewAI Poem Writer
goal: >
Generate a funny, light heartedpoem about how CrewAI
is awesome with a sentence count of {sentence_count}
backstory: >
You're a creative poet with a talent for capturing the essence of any topic
in a beautiful and engaging way. Known for your ability to craft poems that
resonate with readers, you bring a unique perspective and artistic flair to
every piece you write.

View File

@@ -0,0 +1,7 @@
write_poem:
description: >
Write a poem about how CrewAI is awesome.
Ensure the poem is engaging and adheres to the specified sentence count of {sentence_count}.
expected_output: >
A beautifully crafted poem about CrewAI, with exactly {sentence_count} sentences.
agent: poem_writer

View File

@@ -8,8 +8,8 @@ from crewai.project import CrewBase, agent, crew, task
@CrewBase
class ContentCrew:
"""Content Crew"""
class PoemCrew:
"""Poem Crew"""
agents: list[BaseAgent]
tasks: list[Task]
@@ -20,50 +20,26 @@ class ContentCrew:
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
# If you would like to add tools to your crew, you can learn more about it here:
# If you would lik to add tools to your crew, you can learn more about it here:
# https://docs.crewai.com/concepts/agents#agent-tools
@agent
def planner(self) -> Agent:
def poem_writer(self) -> Agent:
return Agent(
config=self.agents_config["planner"], # type: ignore[index]
)
@agent
def writer(self) -> Agent:
return Agent(
config=self.agents_config["writer"], # type: ignore[index]
)
@agent
def editor(self) -> Agent:
return Agent(
config=self.agents_config["editor"], # type: ignore[index]
config=self.agents_config["poem_writer"], # type: ignore[index]
)
# To learn more about structured task outputs,
# task dependencies, and task callbacks, check out the documentation:
# https://docs.crewai.com/concepts/tasks#overview-of-a-task
@task
def planning_task(self) -> Task:
def write_poem(self) -> Task:
return Task(
config=self.tasks_config["planning_task"], # type: ignore[index]
)
@task
def writing_task(self) -> Task:
return Task(
config=self.tasks_config["writing_task"], # type: ignore[index]
)
@task
def editing_task(self) -> Task:
return Task(
config=self.tasks_config["editing_task"], # type: ignore[index]
config=self.tasks_config["write_poem"], # type: ignore[index]
)
@crew
def crew(self) -> Crew:
"""Creates the Content Crew"""
"""Creates the Research Crew"""
# To learn how to add knowledge sources to your crew, check out the documentation:
# https://docs.crewai.com/concepts/knowledge#what-is-knowledge

View File

@@ -1,64 +1,59 @@
#!/usr/bin/env python
from pathlib import Path
from random import randint
from pydantic import BaseModel
from crewai.flow import Flow, listen, start
from {{folder_name}}.crews.content_crew.content_crew import ContentCrew
from {{folder_name}}.crews.poem_crew.poem_crew import PoemCrew
class ContentState(BaseModel):
topic: str = ""
outline: str = ""
draft: str = ""
final_post: str = ""
class PoemState(BaseModel):
sentence_count: int = 1
poem: str = ""
class ContentFlow(Flow[ContentState]):
class PoemFlow(Flow[PoemState]):
@start()
def plan_content(self, crewai_trigger_payload: dict = None):
print("Planning content")
def generate_sentence_count(self, crewai_trigger_payload: dict = None):
print("Generating sentence count")
# Use trigger payload if available
if crewai_trigger_payload:
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
# Example: use trigger data to influence sentence count
self.state.sentence_count = crewai_trigger_payload.get('sentence_count', randint(1, 5))
print(f"Using trigger payload: {crewai_trigger_payload}")
else:
self.state.topic = "AI Agents"
self.state.sentence_count = randint(1, 5)
print(f"Topic: {self.state.topic}")
@listen(plan_content)
def generate_content(self):
print(f"Generating content on: {self.state.topic}")
@listen(generate_sentence_count)
def generate_poem(self):
print("Generating poem")
result = (
ContentCrew()
PoemCrew()
.crew()
.kickoff(inputs={"topic": self.state.topic})
.kickoff(inputs={"sentence_count": self.state.sentence_count})
)
print("Content generated")
self.state.final_post = result.raw
print("Poem generated", result.raw)
self.state.poem = result.raw
@listen(generate_content)
def save_content(self):
print("Saving content")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
with open(output_dir / "post.md", "w") as f:
f.write(self.state.final_post)
print("Post saved to output/post.md")
@listen(generate_poem)
def save_poem(self):
print("Saving poem")
with open("poem.txt", "w") as f:
f.write(self.state.poem)
def kickoff():
content_flow = ContentFlow()
content_flow.kickoff()
poem_flow = PoemFlow()
poem_flow.kickoff()
def plot():
content_flow = ContentFlow()
content_flow.plot()
poem_flow = PoemFlow()
poem_flow.plot()
def run_with_trigger():
@@ -79,10 +74,10 @@ def run_with_trigger():
# Create flow and kickoff with trigger payload
# The @start() methods will automatically receive crewai_trigger_payload parameter
content_flow = ContentFlow()
poem_flow = PoemFlow()
try:
result = content_flow.kickoff({"crewai_trigger_payload": trigger_payload})
result = poem_flow.kickoff({"crewai_trigger_payload": trigger_payload})
return result
except Exception as e:
raise Exception(f"An error occurred while running the flow with trigger: {e}")

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.14.0a3"
"crewai[tools]==1.13.0a7"
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
readme = "README.md"
requires-python = ">=3.10,<3.14"
dependencies = [
"crewai[tools]==1.14.0a3"
"crewai[tools]==1.13.0a7"
]
[tool.crewai]

View File

@@ -21,7 +21,6 @@ from crewai.cli.utils import (
get_project_description,
get_project_name,
get_project_version,
read_toml,
tree_copy,
tree_find_and_replace,
)
@@ -117,26 +116,11 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
self._print_tools_preview(tools_metadata)
self._print_current_organization()
build_env = os.environ.copy()
try:
pyproject_data = read_toml()
sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
for source_config in sources.values():
if isinstance(source_config, dict):
index = source_config.get("index")
if index:
index_env = build_env_with_tool_repository_credentials(index)
build_env.update(index_env)
except Exception: # noqa: S110
pass
with tempfile.TemporaryDirectory() as temp_build_dir:
subprocess.run( # noqa: S603
["uv", "build", "--sdist", "--out-dir", temp_build_dir], # noqa: S607
check=True,
capture_output=False,
env=build_env,
)
tarball_filename = next(

View File

@@ -484,12 +484,8 @@ def get_flows(flow_path: str = "main.py") -> list[Flow[Any]]:
if flow_instances:
break
except Exception as e:
import logging
logging.getLogger(__name__).debug(
f"Could not load tool repository credentials: {e}"
)
except Exception: # noqa: S110
pass
return flow_instances
@@ -553,31 +549,6 @@ def build_env_with_tool_repository_credentials(
return env
def build_env_with_all_tool_credentials() -> dict[str, Any]:
"""
Build environment dict with credentials for all tool repository indexes
found in pyproject.toml's [tool.uv.sources] section.
Returns:
dict: Environment variables with credentials for all private indexes.
"""
env = os.environ.copy()
try:
pyproject_data = read_toml()
sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
for source_config in sources.values():
if isinstance(source_config, dict):
index = source_config.get("index")
if index:
index_env = build_env_with_tool_repository_credentials(index)
env.update(index_env)
except Exception: # noqa: S110
pass
return env
@contextmanager
def _load_module_from_file(
init_file: Path, module_name: str | None = None

View File

@@ -90,7 +90,7 @@ class ExecutionContext(BaseModel):
flow_id: str | None = Field(default=None)
flow_method_name: str = Field(default="unknown")
event_id_stack: tuple[tuple[str, str], ...] = Field(default_factory=tuple)
event_id_stack: tuple[tuple[str, str], ...] = Field(default=())
last_event_id: str | None = Field(default=None)
triggering_event_id: str | None = Field(default=None)
emission_sequence: int = Field(default=0)

View File

@@ -42,7 +42,6 @@ if TYPE_CHECKING:
from opentelemetry.trace import Span
from crewai.context import ExecutionContext
from crewai.state.provider.core import BaseProvider
try:
from crewai_files import get_supported_content_types
@@ -104,7 +103,6 @@ from crewai.rag.types import SearchResult
from crewai.security.fingerprint import Fingerprint
from crewai.security.security_config import SecurityConfig
from crewai.skills.models import Skill
from crewai.state.checkpoint_config import CheckpointConfig
from crewai.task import Task
from crewai.tasks.conditional_task import ConditionalTask
from crewai.tasks.task_output import TaskOutput
@@ -236,7 +234,7 @@ class Crew(FlowTrackable, BaseModel):
manager_llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(description="Language model that will run the agent.", default=None)
manager_agent: Annotated[
BaseAgent | None,
@@ -245,7 +243,7 @@ class Crew(FlowTrackable, BaseModel):
function_calling_llm: Annotated[
str | LLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(description="Language model that will run the agent.", default=None)
config: Json[dict[str, Any]] | dict[str, Any] | None = Field(default=None)
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
@@ -298,7 +296,7 @@ class Crew(FlowTrackable, BaseModel):
planning_llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(
default=None,
description=(
@@ -323,7 +321,7 @@ class Crew(FlowTrackable, BaseModel):
chat_llm: Annotated[
str | BaseLLM | None,
BeforeValidator(_validate_llm_ref),
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
PlainSerializer(_serialize_llm_ref, return_type=str | None, when_used="json"),
] = Field(
default=None,
description="LLM used to handle chatting with the crew.",
@@ -341,11 +339,6 @@ class Crew(FlowTrackable, BaseModel):
default_factory=SecurityConfig,
description="Security configuration for the crew, including fingerprinting.",
)
checkpoint: CheckpointConfig | bool | None = Field(
default=None,
description="Automatic checkpointing configuration. "
"True for defaults, False to opt out, None to inherit.",
)
token_usage: UsageMetrics | None = Field(
default=None,
description="Metrics for the LLM usage during all tasks execution.",
@@ -360,113 +353,6 @@ class Crew(FlowTrackable, BaseModel):
checkpoint_train: bool | None = Field(default=None)
checkpoint_kickoff_event_id: str | None = Field(default=None)
@classmethod
def from_checkpoint(
cls, path: str, *, provider: BaseProvider | None = None
) -> Crew:
"""Restore a Crew from a checkpoint file, ready to resume via kickoff().
Args:
path: Path to a checkpoint JSON file.
provider: Storage backend to read from. Defaults to JsonProvider.
Returns:
A Crew instance. Call kickoff() to resume from the last completed task.
"""
from crewai.context import apply_execution_context
from crewai.events.event_bus import crewai_event_bus
from crewai.state.provider.json_provider import JsonProvider
from crewai.state.runtime import RuntimeState
state = RuntimeState.from_checkpoint(
path,
provider=provider or JsonProvider(),
context={"from_checkpoint": True},
)
crewai_event_bus.set_runtime_state(state)
for entity in state.root:
if isinstance(entity, cls):
if entity.execution_context is not None:
apply_execution_context(entity.execution_context)
entity._restore_runtime()
return entity
raise ValueError(f"No Crew found in checkpoint: {path}")
def _restore_runtime(self) -> None:
"""Re-create runtime objects after restoring from a checkpoint."""
for agent in self.agents:
agent.crew = self
executor = agent.agent_executor
if executor and executor.messages:
executor.crew = self
executor.agent = agent
executor._resuming = True
else:
agent.agent_executor = None
for task in self.tasks:
if task.agent is not None:
for agent in self.agents:
if agent.role == task.agent.role:
task.agent = agent
if agent.agent_executor is not None and task.output is None:
agent.agent_executor.task = task
break
if self.checkpoint_inputs is not None:
self._inputs = self.checkpoint_inputs
if self.checkpoint_kickoff_event_id is not None:
self._kickoff_event_id = self.checkpoint_kickoff_event_id
if self.checkpoint_train is not None:
self._train = self.checkpoint_train
self._restore_event_scope()
def _restore_event_scope(self) -> None:
"""Rebuild the event scope stack from the checkpoint's event record."""
from crewai.events.base_events import set_emission_counter
from crewai.events.event_bus import crewai_event_bus
from crewai.events.event_context import (
restore_event_scope,
set_last_event_id,
)
state = crewai_event_bus._runtime_state
if state is None:
return
# Restore crew scope and the in-progress task scope. Inner scopes
# (agent, llm, tool) are re-created by the executor on resume.
stack: list[tuple[str, str]] = []
if self._kickoff_event_id:
stack.append((self._kickoff_event_id, "crew_kickoff_started"))
# Find the task_started event for the in-progress task (skipped on resume)
for task in self.tasks:
if task.output is None:
task_id_str = str(task.id)
for node in state.event_record.nodes.values():
if (
node.event.type == "task_started"
and node.event.task_id == task_id_str
):
stack.append((node.event.event_id, "task_started"))
break
break
restore_event_scope(tuple(stack))
# Restore last_event_id and emission counter from the record
last_event_id: str | None = None
max_seq = 0
for node in state.event_record.nodes.values():
seq = node.event.emission_sequence or 0
if seq > max_seq:
max_seq = seq
last_event_id = node.event.event_id
if last_event_id is not None:
set_last_event_id(last_event_id)
if max_seq > 0:
set_emission_counter(max_seq)
@field_validator("id", mode="before")
@classmethod
def _deny_user_set_id(cls, v: UUID4 | None, info: Any) -> UUID4 | None:
@@ -495,8 +381,7 @@ class Crew(FlowTrackable, BaseModel):
@model_validator(mode="after")
def set_private_attrs(self) -> Crew:
"""set private attributes."""
if not getattr(self, "_cache_handler", None):
self._cache_handler = CacheHandler()
self._cache_handler = CacheHandler()
event_listener = EventListener()
# Determine and set tracing state once for this execution
@@ -1170,10 +1055,6 @@ class Crew(FlowTrackable, BaseModel):
Returns:
CrewOutput: Final output of the crew
"""
custom_start = self._get_execution_start_index(tasks)
if custom_start is not None:
start_index = custom_start
task_outputs: list[TaskOutput] = []
pending_tasks: list[tuple[Task, asyncio.Task[TaskOutput], int]] = []
last_sync_output: TaskOutput | None = None
@@ -1355,12 +1236,7 @@ class Crew(FlowTrackable, BaseModel):
manager.crew = self
def _get_execution_start_index(self, tasks: list[Task]) -> int | None:
if self.checkpoint_kickoff_event_id is None:
return None
for i, task in enumerate(tasks):
if task.output is None:
return i
return len(tasks) if tasks else None
return None
def _execute_tasks(
self,

View File

@@ -105,9 +105,6 @@ def setup_agents(
agent.function_calling_llm = function_calling_llm # type: ignore[attr-defined]
if not agent.step_callback: # type: ignore[attr-defined]
agent.step_callback = step_callback # type: ignore[attr-defined]
executor = getattr(agent, "agent_executor", None)
if executor and getattr(executor, "_resuming", False):
continue
agent.create_agent_executor()
@@ -160,8 +157,10 @@ def prepare_task_execution(
# Handle replay skip
if start_index is not None and task_index < start_index:
if task.output:
task_outputs.append(task.output)
if not task.async_execution:
if task.async_execution:
task_outputs.append(task.output)
else:
task_outputs = [task.output]
last_sync_output = task.output
return (
TaskExecutionData(agent=None, tools=[], should_skip=True),
@@ -184,9 +183,7 @@ def prepare_task_execution(
tools_for_task,
)
executor = agent_to_use.agent_executor
if not (executor and executor._resuming):
crew._log_task_start(task, agent_to_use.role)
crew._log_task_start(task, agent_to_use.role)
return (
TaskExecutionData(agent=agent_to_use, tools=tools_for_task),
@@ -278,15 +275,10 @@ def prepare_kickoff(
"""
from crewai.events.base_events import reset_emission_counter
from crewai.events.event_bus import crewai_event_bus
from crewai.events.event_context import (
get_current_parent_id,
reset_last_event_id,
)
from crewai.events.event_context import get_current_parent_id, reset_last_event_id
from crewai.events.types.crew_events import CrewKickoffStartedEvent
resuming = crew.checkpoint_kickoff_event_id is not None
if not resuming and get_current_parent_id() is None:
if get_current_parent_id() is None:
reset_emission_counter()
reset_last_event_id()
@@ -304,29 +296,14 @@ def prepare_kickoff(
normalized = {}
normalized = before_callback(normalized)
if resuming and crew._kickoff_event_id:
if crew.verbose:
from crewai.events.utils.console_formatter import ConsoleFormatter
fmt = ConsoleFormatter(verbose=True)
content = fmt.create_status_content(
"Resuming from Checkpoint",
crew.name or "Crew",
"bright_magenta",
ID=str(crew.id),
)
fmt.print_panel(
content, "\U0001f504 Resuming from Checkpoint", "bright_magenta"
)
else:
started_event = CrewKickoffStartedEvent(crew_name=crew.name, inputs=normalized)
crew._kickoff_event_id = started_event.event_id
future = crewai_event_bus.emit(crew, started_event)
if future is not None:
try:
future.result()
except Exception: # noqa: S110
pass
started_event = CrewKickoffStartedEvent(crew_name=crew.name, inputs=normalized)
crew._kickoff_event_id = started_event.event_id
future = crewai_event_bus.emit(crew, started_event)
if future is not None:
try:
future.result()
except Exception: # noqa: S110
pass
crew._task_output_handler.reset()
crew._logging_color = "bold_purple"

View File

@@ -5,24 +5,17 @@ of events throughout the CrewAI system, supporting both synchronous and asynchro
event handlers with optional dependency management.
"""
from __future__ import annotations
import asyncio
import atexit
from collections.abc import Callable, Generator
from concurrent.futures import Future, ThreadPoolExecutor
from contextlib import contextmanager
import contextvars
import logging
import threading
from typing import TYPE_CHECKING, Any, Final, ParamSpec, TypeVar
from typing import Any, Final, ParamSpec, TypeVar
from typing_extensions import Self
if TYPE_CHECKING:
from crewai.state.runtime import RuntimeState
from crewai.events.base_events import BaseEvent, get_next_emission_sequence
from crewai.events.depends import Depends
from crewai.events.event_context import (
@@ -50,16 +43,10 @@ from crewai.events.types.event_bus_types import (
)
from crewai.events.types.llm_events import LLMStreamChunkEvent
from crewai.events.utils.console_formatter import ConsoleFormatter
from crewai.events.utils.handlers import (
_get_param_count,
is_async_handler,
is_call_handler_safe,
)
from crewai.events.utils.handlers import is_async_handler, is_call_handler_safe
from crewai.utilities.rw_lock import RWLock
logger = logging.getLogger(__name__)
P = ParamSpec("P")
R = TypeVar("R")
@@ -100,7 +87,6 @@ class CrewAIEventsBus:
_futures_lock: threading.Lock
_executor_initialized: bool
_has_pending_events: bool
_runtime_state: RuntimeState | None
def __new__(cls) -> Self:
"""Create or return the singleton instance.
@@ -136,8 +122,6 @@ class CrewAIEventsBus:
# Lazy initialization flags - executor and loop created on first emit
self._executor_initialized = False
self._has_pending_events = False
self._runtime_state: RuntimeState | None = None
self._registered_entity_ids: set[int] = set()
def _ensure_executor_initialized(self) -> None:
"""Lazily initialize the thread pool executor and event loop.
@@ -225,16 +209,25 @@ class CrewAIEventsBus:
) -> Callable[[Callable[P, R]], Callable[P, R]]:
"""Decorator to register an event handler for a specific event type.
Handlers can accept 2 or 3 arguments:
- ``(source, event)`` — standard handler
- ``(source, event, state: RuntimeState)`` — handler with runtime state
Args:
event_type: The event class to listen for
depends_on: Optional dependency or list of dependencies.
depends_on: Optional dependency or list of dependencies. Handlers with
dependencies will execute after their dependencies complete.
Returns:
Decorator function that registers the handler
Example:
>>> from crewai.events import crewai_event_bus, Depends
>>> from crewai.events.types.llm_events import LLMCallStartedEvent
>>>
>>> @crewai_event_bus.on(LLMCallStartedEvent)
>>> def setup_context(source, event):
... print("Setting up context")
>>>
>>> @crewai_event_bus.on(LLMCallStartedEvent, depends_on=Depends(setup_context))
>>> def process(source, event):
... print("Processing (runs after setup_context)")
"""
def decorator(handler: Callable[P, R]) -> Callable[P, R]:
@@ -255,42 +248,6 @@ class CrewAIEventsBus:
return decorator
def set_runtime_state(self, state: RuntimeState) -> None:
"""Set the RuntimeState that will be passed to event handlers."""
with self._instance_lock:
self._runtime_state = state
self._registered_entity_ids = {id(e) for e in state.root}
def register_entity(self, entity: Any) -> None:
"""Add an entity to the RuntimeState, creating it if needed.
Agents that belong to an already-registered Crew are tracked
but not appended to root, since they are serialized as part
of the Crew's agents list.
"""
eid = id(entity)
if eid in self._registered_entity_ids:
return
with self._instance_lock:
if eid in self._registered_entity_ids:
return
self._registered_entity_ids.add(eid)
if getattr(entity, "entity_type", None) == "agent":
crew = getattr(entity, "crew", None)
if crew is not None and id(crew) in self._registered_entity_ids:
return
if self._runtime_state is None:
from crewai import RuntimeState
if RuntimeState is None:
logger.warning(
"RuntimeState unavailable; skipping entity registration."
)
return
self._runtime_state = RuntimeState(root=[entity])
else:
self._runtime_state.root.append(entity)
def off(
self,
event_type: type[BaseEvent],
@@ -337,12 +294,10 @@ class CrewAIEventsBus:
event: The event instance
handlers: Frozenset of sync handlers to call
"""
state = self._runtime_state
errors: list[tuple[SyncHandler, Exception]] = [
(handler, error)
for handler in handlers
if (error := is_call_handler_safe(handler, source, event, state))
is not None
if (error := is_call_handler_safe(handler, source, event)) is not None
]
if errors:
@@ -364,14 +319,7 @@ class CrewAIEventsBus:
event: The event instance
handlers: Frozenset of async handlers to call
"""
state = self._runtime_state
async def _call(handler: AsyncHandler) -> Any:
if _get_param_count(handler) >= 3:
return await handler(source, event, state) # type: ignore[call-arg]
return await handler(source, event) # type: ignore[call-arg]
coros = [_call(handler) for handler in handlers]
coros = [handler(source, event) for handler in handlers]
results = await asyncio.gather(*coros, return_exceptions=True)
for handler, result in zip(handlers, results, strict=False):
if isinstance(result, Exception):
@@ -443,53 +391,6 @@ class CrewAIEventsBus:
if level_async:
await self._acall_handlers(source, event, level_async)
def _register_source(self, source: Any) -> None:
"""Register the source entity in RuntimeState if applicable."""
if (
getattr(source, "entity_type", None) in ("flow", "crew", "agent")
and id(source) not in self._registered_entity_ids
):
self.register_entity(source)
def _record_event(self, event: BaseEvent) -> None:
"""Add an event to the RuntimeState event record."""
if self._runtime_state is not None:
self._runtime_state.event_record.add(event)
def _prepare_event(self, source: Any, event: BaseEvent) -> None:
"""Register source, set scope/sequence metadata, and record the event.
This method mutates ContextVar state (scope stack, last_event_id)
and must only be called from synchronous emit paths.
"""
self._register_source(source)
event.previous_event_id = get_last_event_id()
event.triggered_by_event_id = get_triggering_event_id()
event.emission_sequence = get_next_emission_sequence()
if event.parent_event_id is None:
event_type_name = event.type
if event_type_name in SCOPE_ENDING_EVENTS:
event.parent_event_id = get_enclosing_parent_id()
popped = pop_event_scope()
if popped is None:
handle_empty_pop(event_type_name)
else:
popped_event_id, popped_type = popped
event.started_event_id = popped_event_id
expected_start = VALID_EVENT_PAIRS.get(event_type_name)
if expected_start and popped_type and popped_type != expected_start:
handle_mismatch(event_type_name, popped_type, expected_start)
elif event_type_name in SCOPE_STARTING_EVENTS:
event.parent_event_id = get_current_parent_id()
push_event_scope(event.event_id, event_type_name)
else:
event.parent_event_id = get_current_parent_id()
set_last_event_id(event.event_id)
self._record_event(event)
def emit(self, source: Any, event: BaseEvent) -> Future[None] | None:
"""Emit an event to all registered handlers.
@@ -516,8 +417,29 @@ class CrewAIEventsBus:
... await asyncio.wrap_future(future) # In async test
... # or future.result(timeout=5.0) in sync code
"""
self._prepare_event(source, event)
event.previous_event_id = get_last_event_id()
event.triggered_by_event_id = get_triggering_event_id()
event.emission_sequence = get_next_emission_sequence()
if event.parent_event_id is None:
event_type_name = event.type
if event_type_name in SCOPE_ENDING_EVENTS:
event.parent_event_id = get_enclosing_parent_id()
popped = pop_event_scope()
if popped is None:
handle_empty_pop(event_type_name)
else:
popped_event_id, popped_type = popped
event.started_event_id = popped_event_id
expected_start = VALID_EVENT_PAIRS.get(event_type_name)
if expected_start and popped_type and popped_type != expected_start:
handle_mismatch(event_type_name, popped_type, expected_start)
elif event_type_name in SCOPE_STARTING_EVENTS:
event.parent_event_id = get_current_parent_id()
push_event_scope(event.event_id, event_type_name)
else:
event.parent_event_id = get_current_parent_id()
set_last_event_id(event.event_id)
event_type = type(event)
with self._rwlock.r_locked():
@@ -616,10 +538,6 @@ class CrewAIEventsBus:
source: The object emitting the event
event: The event instance to emit
"""
self._register_source(source)
event.emission_sequence = get_next_emission_sequence()
self._record_event(event)
event_type = type(event)
with self._rwlock.r_locked():

View File

@@ -133,11 +133,6 @@ def triggered_by_scope(event_id: str) -> Generator[None, None, None]:
_triggering_event_id.set(previous)
def restore_event_scope(stack: tuple[tuple[str, str], ...]) -> None:
"""Restore the event scope stack from a checkpoint."""
_event_id_stack.set(stack)
def push_event_scope(event_id: str, event_type: str = "") -> None:
"""Push an event ID and type onto the scope stack."""
config = _event_context_config.get() or _default_config

View File

@@ -73,7 +73,7 @@ class A2ADelegationStartedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_delegation_started"] = "a2a_delegation_started"
type: str = "a2a_delegation_started"
endpoint: str
task_description: str
agent_id: str
@@ -106,7 +106,7 @@ class A2ADelegationCompletedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_delegation_completed"] = "a2a_delegation_completed"
type: str = "a2a_delegation_completed"
status: str
result: str | None = None
error: str | None = None
@@ -140,7 +140,7 @@ class A2AConversationStartedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_conversation_started"] = "a2a_conversation_started"
type: str = "a2a_conversation_started"
agent_id: str
endpoint: str
context_id: str | None = None
@@ -171,7 +171,7 @@ class A2AMessageSentEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_message_sent"] = "a2a_message_sent"
type: str = "a2a_message_sent"
message: str
turn_number: int
context_id: str | None = None
@@ -203,7 +203,7 @@ class A2AResponseReceivedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_response_received"] = "a2a_response_received"
type: str = "a2a_response_received"
response: str
turn_number: int
context_id: str | None = None
@@ -237,7 +237,7 @@ class A2AConversationCompletedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_conversation_completed"] = "a2a_conversation_completed"
type: str = "a2a_conversation_completed"
status: Literal["completed", "failed"]
final_result: str | None = None
error: str | None = None
@@ -263,7 +263,7 @@ class A2APollingStartedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_polling_started"] = "a2a_polling_started"
type: str = "a2a_polling_started"
task_id: str
context_id: str | None = None
polling_interval: float
@@ -286,7 +286,7 @@ class A2APollingStatusEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_polling_status"] = "a2a_polling_status"
type: str = "a2a_polling_status"
task_id: str
context_id: str | None = None
state: str
@@ -309,9 +309,7 @@ class A2APushNotificationRegisteredEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_push_notification_registered"] = (
"a2a_push_notification_registered"
)
type: str = "a2a_push_notification_registered"
task_id: str
context_id: str | None = None
callback_url: str
@@ -336,7 +334,7 @@ class A2APushNotificationReceivedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_push_notification_received"] = "a2a_push_notification_received"
type: str = "a2a_push_notification_received"
task_id: str
context_id: str | None = None
state: str
@@ -361,7 +359,7 @@ class A2APushNotificationSentEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_push_notification_sent"] = "a2a_push_notification_sent"
type: str = "a2a_push_notification_sent"
task_id: str
context_id: str | None = None
callback_url: str
@@ -383,7 +381,7 @@ class A2APushNotificationTimeoutEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_push_notification_timeout"] = "a2a_push_notification_timeout"
type: str = "a2a_push_notification_timeout"
task_id: str
context_id: str | None = None
timeout_seconds: float
@@ -407,7 +405,7 @@ class A2AStreamingStartedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_streaming_started"] = "a2a_streaming_started"
type: str = "a2a_streaming_started"
task_id: str | None = None
context_id: str | None = None
endpoint: str
@@ -436,7 +434,7 @@ class A2AStreamingChunkEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_streaming_chunk"] = "a2a_streaming_chunk"
type: str = "a2a_streaming_chunk"
task_id: str | None = None
context_id: str | None = None
chunk: str
@@ -464,7 +462,7 @@ class A2AAgentCardFetchedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_agent_card_fetched"] = "a2a_agent_card_fetched"
type: str = "a2a_agent_card_fetched"
endpoint: str
a2a_agent_name: str | None = None
agent_card: dict[str, Any] | None = None
@@ -488,7 +486,7 @@ class A2AAuthenticationFailedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_authentication_failed"] = "a2a_authentication_failed"
type: str = "a2a_authentication_failed"
endpoint: str
auth_type: str | None = None
error: str
@@ -519,7 +517,7 @@ class A2AArtifactReceivedEvent(A2AEventBase):
extensions: List of A2A extension URIs in use.
"""
type: Literal["a2a_artifact_received"] = "a2a_artifact_received"
type: str = "a2a_artifact_received"
task_id: str
artifact_id: str
artifact_name: str | None = None
@@ -552,7 +550,7 @@ class A2AConnectionErrorEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_connection_error"] = "a2a_connection_error"
type: str = "a2a_connection_error"
endpoint: str
error: str
error_type: str | None = None
@@ -573,7 +571,7 @@ class A2AServerTaskStartedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_server_task_started"] = "a2a_server_task_started"
type: str = "a2a_server_task_started"
task_id: str
context_id: str
metadata: dict[str, Any] | None = None
@@ -589,7 +587,7 @@ class A2AServerTaskCompletedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_server_task_completed"] = "a2a_server_task_completed"
type: str = "a2a_server_task_completed"
task_id: str
context_id: str
result: str
@@ -605,7 +603,7 @@ class A2AServerTaskCanceledEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_server_task_canceled"] = "a2a_server_task_canceled"
type: str = "a2a_server_task_canceled"
task_id: str
context_id: str
metadata: dict[str, Any] | None = None
@@ -621,7 +619,7 @@ class A2AServerTaskFailedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_server_task_failed"] = "a2a_server_task_failed"
type: str = "a2a_server_task_failed"
task_id: str
context_id: str
error: str
@@ -636,7 +634,7 @@ class A2AParallelDelegationStartedEvent(A2AEventBase):
task_description: Description of the task being delegated.
"""
type: Literal["a2a_parallel_delegation_started"] = "a2a_parallel_delegation_started"
type: str = "a2a_parallel_delegation_started"
endpoints: list[str]
task_description: str
@@ -651,9 +649,7 @@ class A2AParallelDelegationCompletedEvent(A2AEventBase):
results: Summary of results from each agent.
"""
type: Literal["a2a_parallel_delegation_completed"] = (
"a2a_parallel_delegation_completed"
)
type: str = "a2a_parallel_delegation_completed"
endpoints: list[str]
success_count: int
failure_count: int
@@ -679,7 +675,7 @@ class A2ATransportNegotiatedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_transport_negotiated"] = "a2a_transport_negotiated"
type: str = "a2a_transport_negotiated"
endpoint: str
a2a_agent_name: str | None = None
negotiated_transport: str
@@ -712,7 +708,7 @@ class A2AContentTypeNegotiatedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_content_type_negotiated"] = "a2a_content_type_negotiated"
type: str = "a2a_content_type_negotiated"
endpoint: str
a2a_agent_name: str | None = None
skill_name: str | None = None
@@ -742,7 +738,7 @@ class A2AContextCreatedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_context_created"] = "a2a_context_created"
type: str = "a2a_context_created"
context_id: str
created_at: float
metadata: dict[str, Any] | None = None
@@ -759,7 +755,7 @@ class A2AContextExpiredEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_context_expired"] = "a2a_context_expired"
type: str = "a2a_context_expired"
context_id: str
created_at: float
age_seconds: float
@@ -779,7 +775,7 @@ class A2AContextIdleEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_context_idle"] = "a2a_context_idle"
type: str = "a2a_context_idle"
context_id: str
idle_seconds: float
task_count: int
@@ -796,7 +792,7 @@ class A2AContextCompletedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_context_completed"] = "a2a_context_completed"
type: str = "a2a_context_completed"
context_id: str
total_tasks: int
duration_seconds: float
@@ -815,7 +811,7 @@ class A2AContextPrunedEvent(A2AEventBase):
metadata: Custom A2A metadata key-value pairs.
"""
type: Literal["a2a_context_pruned"] = "a2a_context_pruned"
type: str = "a2a_context_pruned"
context_id: str
task_count: int
age_seconds: float

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from collections.abc import Sequence
from typing import Any, Literal
from typing import Any
from pydantic import ConfigDict, model_validator
from typing_extensions import Self
@@ -21,7 +21,7 @@ class AgentExecutionStartedEvent(BaseEvent):
task: Any
tools: Sequence[BaseTool | CrewStructuredTool] | None
task_prompt: str
type: Literal["agent_execution_started"] = "agent_execution_started"
type: str = "agent_execution_started"
model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -38,7 +38,7 @@ class AgentExecutionCompletedEvent(BaseEvent):
agent: BaseAgent
task: Any
output: str
type: Literal["agent_execution_completed"] = "agent_execution_completed"
type: str = "agent_execution_completed"
model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -55,7 +55,7 @@ class AgentExecutionErrorEvent(BaseEvent):
agent: BaseAgent
task: Any
error: str
type: Literal["agent_execution_error"] = "agent_execution_error"
type: str = "agent_execution_error"
model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -73,7 +73,7 @@ class LiteAgentExecutionStartedEvent(BaseEvent):
agent_info: dict[str, Any]
tools: Sequence[BaseTool | CrewStructuredTool] | None
messages: str | list[dict[str, str]]
type: Literal["lite_agent_execution_started"] = "lite_agent_execution_started"
type: str = "lite_agent_execution_started"
model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -83,7 +83,7 @@ class LiteAgentExecutionCompletedEvent(BaseEvent):
agent_info: dict[str, Any]
output: str
type: Literal["lite_agent_execution_completed"] = "lite_agent_execution_completed"
type: str = "lite_agent_execution_completed"
class LiteAgentExecutionErrorEvent(BaseEvent):
@@ -91,7 +91,7 @@ class LiteAgentExecutionErrorEvent(BaseEvent):
agent_info: dict[str, Any]
error: str
type: Literal["lite_agent_execution_error"] = "lite_agent_execution_error"
type: str = "lite_agent_execution_error"
# Agent Eval events
@@ -100,7 +100,7 @@ class AgentEvaluationStartedEvent(BaseEvent):
agent_role: str
task_id: str | None = None
iteration: int
type: Literal["agent_evaluation_started"] = "agent_evaluation_started"
type: str = "agent_evaluation_started"
class AgentEvaluationCompletedEvent(BaseEvent):
@@ -110,7 +110,7 @@ class AgentEvaluationCompletedEvent(BaseEvent):
iteration: int
metric_category: Any
score: Any
type: Literal["agent_evaluation_completed"] = "agent_evaluation_completed"
type: str = "agent_evaluation_completed"
class AgentEvaluationFailedEvent(BaseEvent):
@@ -119,7 +119,7 @@ class AgentEvaluationFailedEvent(BaseEvent):
task_id: str | None = None
iteration: int
error: str
type: Literal["agent_evaluation_failed"] = "agent_evaluation_failed"
type: str = "agent_evaluation_failed"
def _set_agent_fingerprint(event: BaseEvent, agent: BaseAgent) -> None:

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Any, Literal
from typing import TYPE_CHECKING, Any
from crewai.events.base_events import BaseEvent
@@ -37,14 +37,14 @@ class CrewKickoffStartedEvent(CrewBaseEvent):
"""Event emitted when a crew starts execution"""
inputs: dict[str, Any] | None
type: Literal["crew_kickoff_started"] = "crew_kickoff_started"
type: str = "crew_kickoff_started"
class CrewKickoffCompletedEvent(CrewBaseEvent):
"""Event emitted when a crew completes execution"""
output: Any
type: Literal["crew_kickoff_completed"] = "crew_kickoff_completed"
type: str = "crew_kickoff_completed"
total_tokens: int = 0
@@ -52,7 +52,7 @@ class CrewKickoffFailedEvent(CrewBaseEvent):
"""Event emitted when a crew fails to complete execution"""
error: str
type: Literal["crew_kickoff_failed"] = "crew_kickoff_failed"
type: str = "crew_kickoff_failed"
class CrewTrainStartedEvent(CrewBaseEvent):
@@ -61,7 +61,7 @@ class CrewTrainStartedEvent(CrewBaseEvent):
n_iterations: int
filename: str
inputs: dict[str, Any] | None
type: Literal["crew_train_started"] = "crew_train_started"
type: str = "crew_train_started"
class CrewTrainCompletedEvent(CrewBaseEvent):
@@ -69,14 +69,14 @@ class CrewTrainCompletedEvent(CrewBaseEvent):
n_iterations: int
filename: str
type: Literal["crew_train_completed"] = "crew_train_completed"
type: str = "crew_train_completed"
class CrewTrainFailedEvent(CrewBaseEvent):
"""Event emitted when a crew fails to complete training"""
error: str
type: Literal["crew_train_failed"] = "crew_train_failed"
type: str = "crew_train_failed"
class CrewTestStartedEvent(CrewBaseEvent):
@@ -85,20 +85,20 @@ class CrewTestStartedEvent(CrewBaseEvent):
n_iterations: int
eval_llm: str | Any | None
inputs: dict[str, Any] | None
type: Literal["crew_test_started"] = "crew_test_started"
type: str = "crew_test_started"
class CrewTestCompletedEvent(CrewBaseEvent):
"""Event emitted when a crew completes testing"""
type: Literal["crew_test_completed"] = "crew_test_completed"
type: str = "crew_test_completed"
class CrewTestFailedEvent(CrewBaseEvent):
"""Event emitted when a crew fails to complete testing"""
error: str
type: Literal["crew_test_failed"] = "crew_test_failed"
type: str = "crew_test_failed"
class CrewTestResultEvent(CrewBaseEvent):
@@ -107,4 +107,4 @@ class CrewTestResultEvent(CrewBaseEvent):
quality: float
execution_duration: float
model: str
type: Literal["crew_test_result"] = "crew_test_result"
type: str = "crew_test_result"

View File

@@ -6,17 +6,10 @@ from typing import Any, TypeAlias
from crewai.events.base_events import BaseEvent
SyncHandler: TypeAlias = (
Callable[[Any, BaseEvent], None] | Callable[[Any, BaseEvent, Any], None]
)
AsyncHandler: TypeAlias = (
Callable[[Any, BaseEvent], Coroutine[Any, Any, None]]
| Callable[[Any, BaseEvent, Any], Coroutine[Any, Any, None]]
)
SyncHandler: TypeAlias = Callable[[Any, BaseEvent], None]
AsyncHandler: TypeAlias = Callable[[Any, BaseEvent], Coroutine[Any, Any, None]]
SyncHandlerSet: TypeAlias = frozenset[SyncHandler]
AsyncHandlerSet: TypeAlias = frozenset[AsyncHandler]
Handler: TypeAlias = (
Callable[[Any, BaseEvent], Any] | Callable[[Any, BaseEvent, Any], Any]
)
Handler: TypeAlias = Callable[[Any, BaseEvent], Any]
ExecutionPlan: TypeAlias = list[set[Handler]]

View File

@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any
from pydantic import BaseModel, ConfigDict
@@ -17,14 +17,14 @@ class FlowStartedEvent(FlowEvent):
flow_name: str
inputs: dict[str, Any] | None = None
type: Literal["flow_started"] = "flow_started"
type: str = "flow_started"
class FlowCreatedEvent(FlowEvent):
"""Event emitted when a flow is created"""
flow_name: str
type: Literal["flow_created"] = "flow_created"
type: str = "flow_created"
class MethodExecutionStartedEvent(FlowEvent):
@@ -34,7 +34,7 @@ class MethodExecutionStartedEvent(FlowEvent):
method_name: str
state: dict[str, Any] | BaseModel
params: dict[str, Any] | None = None
type: Literal["method_execution_started"] = "method_execution_started"
type: str = "method_execution_started"
class MethodExecutionFinishedEvent(FlowEvent):
@@ -44,7 +44,7 @@ class MethodExecutionFinishedEvent(FlowEvent):
method_name: str
result: Any = None
state: dict[str, Any] | BaseModel
type: Literal["method_execution_finished"] = "method_execution_finished"
type: str = "method_execution_finished"
class MethodExecutionFailedEvent(FlowEvent):
@@ -53,7 +53,7 @@ class MethodExecutionFailedEvent(FlowEvent):
flow_name: str
method_name: str
error: Exception
type: Literal["method_execution_failed"] = "method_execution_failed"
type: str = "method_execution_failed"
model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -78,7 +78,7 @@ class MethodExecutionPausedEvent(FlowEvent):
flow_id: str
message: str
emit: list[str] | None = None
type: Literal["method_execution_paused"] = "method_execution_paused"
type: str = "method_execution_paused"
class FlowFinishedEvent(FlowEvent):
@@ -86,7 +86,7 @@ class FlowFinishedEvent(FlowEvent):
flow_name: str
result: Any | None = None
type: Literal["flow_finished"] = "flow_finished"
type: str = "flow_finished"
state: dict[str, Any] | BaseModel
@@ -110,14 +110,14 @@ class FlowPausedEvent(FlowEvent):
state: dict[str, Any] | BaseModel
message: str
emit: list[str] | None = None
type: Literal["flow_paused"] = "flow_paused"
type: str = "flow_paused"
class FlowPlotEvent(FlowEvent):
"""Event emitted when a flow plot is created"""
flow_name: str
type: Literal["flow_plot"] = "flow_plot"
type: str = "flow_plot"
class FlowInputRequestedEvent(FlowEvent):
@@ -138,7 +138,7 @@ class FlowInputRequestedEvent(FlowEvent):
method_name: str
message: str
metadata: dict[str, Any] | None = None
type: Literal["flow_input_requested"] = "flow_input_requested"
type: str = "flow_input_requested"
class FlowInputReceivedEvent(FlowEvent):
@@ -163,7 +163,7 @@ class FlowInputReceivedEvent(FlowEvent):
response: str | None = None
metadata: dict[str, Any] | None = None
response_metadata: dict[str, Any] | None = None
type: Literal["flow_input_received"] = "flow_input_received"
type: str = "flow_input_received"
class HumanFeedbackRequestedEvent(FlowEvent):
@@ -187,7 +187,7 @@ class HumanFeedbackRequestedEvent(FlowEvent):
message: str
emit: list[str] | None = None
request_id: str | None = None
type: Literal["human_feedback_requested"] = "human_feedback_requested"
type: str = "human_feedback_requested"
class HumanFeedbackReceivedEvent(FlowEvent):
@@ -209,4 +209,4 @@ class HumanFeedbackReceivedEvent(FlowEvent):
feedback: str
outcome: str | None = None
request_id: str | None = None
type: Literal["human_feedback_received"] = "human_feedback_received"
type: str = "human_feedback_received"

View File

@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -20,16 +20,14 @@ class KnowledgeEventBase(BaseEvent):
class KnowledgeRetrievalStartedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge retrieval is started."""
type: Literal["knowledge_search_query_started"] = "knowledge_search_query_started"
type: str = "knowledge_search_query_started"
class KnowledgeRetrievalCompletedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge retrieval is completed."""
query: str
type: Literal["knowledge_search_query_completed"] = (
"knowledge_search_query_completed"
)
type: str = "knowledge_search_query_completed"
retrieved_knowledge: str
@@ -37,13 +35,13 @@ class KnowledgeQueryStartedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge query is started."""
task_prompt: str
type: Literal["knowledge_query_started"] = "knowledge_query_started"
type: str = "knowledge_query_started"
class KnowledgeQueryFailedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge query fails."""
type: Literal["knowledge_query_failed"] = "knowledge_query_failed"
type: str = "knowledge_query_failed"
error: str
@@ -51,12 +49,12 @@ class KnowledgeQueryCompletedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge query is completed."""
query: str
type: Literal["knowledge_query_completed"] = "knowledge_query_completed"
type: str = "knowledge_query_completed"
class KnowledgeSearchQueryFailedEvent(KnowledgeEventBase):
"""Event emitted when a knowledge search query fails."""
query: str
type: Literal["knowledge_search_query_failed"] = "knowledge_search_query_failed"
type: str = "knowledge_search_query_failed"
error: str

View File

@@ -1,5 +1,5 @@
from enum import Enum
from typing import Any, Literal
from typing import Any
from pydantic import BaseModel
@@ -43,7 +43,7 @@ class LLMCallStartedEvent(LLMEventBase):
multimodal content (text, images, etc.)
"""
type: Literal["llm_call_started"] = "llm_call_started"
type: str = "llm_call_started"
messages: str | list[dict[str, Any]] | None = None
tools: list[dict[str, Any]] | None = None
callbacks: list[Any] | None = None
@@ -53,7 +53,7 @@ class LLMCallStartedEvent(LLMEventBase):
class LLMCallCompletedEvent(LLMEventBase):
"""Event emitted when a LLM call completes"""
type: Literal["llm_call_completed"] = "llm_call_completed"
type: str = "llm_call_completed"
messages: str | list[dict[str, Any]] | None = None
response: Any
call_type: LLMCallType
@@ -64,7 +64,7 @@ class LLMCallFailedEvent(LLMEventBase):
"""Event emitted when a LLM call fails"""
error: str
type: Literal["llm_call_failed"] = "llm_call_failed"
type: str = "llm_call_failed"
class FunctionCall(BaseModel):
@@ -82,7 +82,7 @@ class ToolCall(BaseModel):
class LLMStreamChunkEvent(LLMEventBase):
"""Event emitted when a streaming chunk is received"""
type: Literal["llm_stream_chunk"] = "llm_stream_chunk"
type: str = "llm_stream_chunk"
chunk: str
tool_call: ToolCall | None = None
call_type: LLMCallType | None = None
@@ -92,6 +92,6 @@ class LLMStreamChunkEvent(LLMEventBase):
class LLMThinkingChunkEvent(LLMEventBase):
"""Event emitted when a thinking/reasoning chunk is received from a thinking model"""
type: Literal["llm_thinking_chunk"] = "llm_thinking_chunk"
type: str = "llm_thinking_chunk"
chunk: str
response_id: str | None = None

View File

@@ -1,6 +1,6 @@
from collections.abc import Callable
from inspect import getsource
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -12,8 +12,6 @@ class LLMGuardrailBaseEvent(BaseEvent):
from_agent: Any | None = None
agent_role: str | None = None
agent_id: str | None = None
guardrail_type: str | None = None
guardrail_name: str | None = None
def __init__(self, **data: Any) -> None:
super().__init__(**data)
@@ -29,7 +27,7 @@ class LLMGuardrailStartedEvent(LLMGuardrailBaseEvent):
retry_count: The number of times the guardrail has been retried
"""
type: Literal["llm_guardrail_started"] = "llm_guardrail_started"
type: str = "llm_guardrail_started"
guardrail: str | Callable[..., Any]
retry_count: int
@@ -39,17 +37,9 @@ class LLMGuardrailStartedEvent(LLMGuardrailBaseEvent):
super().__init__(**data)
if isinstance(self.guardrail, HallucinationGuardrail):
self.guardrail_type = "hallucination"
self.guardrail_name = self.guardrail.description.strip()
self.guardrail = self.guardrail.description.strip()
elif isinstance(self.guardrail, LLMGuardrail):
self.guardrail_type = "llm"
self.guardrail_name = self.guardrail.description.strip()
if isinstance(self.guardrail, (LLMGuardrail, HallucinationGuardrail)):
self.guardrail = self.guardrail.description.strip()
elif callable(self.guardrail):
self.guardrail_type = "function"
self.guardrail_name = getattr(self.guardrail, "__name__", None)
self.guardrail = getsource(self.guardrail).strip()
@@ -63,8 +53,21 @@ class LLMGuardrailCompletedEvent(LLMGuardrailBaseEvent):
retry_count: The number of times the guardrail has been retried
"""
type: Literal["llm_guardrail_completed"] = "llm_guardrail_completed"
type: str = "llm_guardrail_completed"
success: bool
result: Any
error: str | None = None
retry_count: int
class LLMGuardrailFailedEvent(LLMGuardrailBaseEvent):
"""Event emitted when a guardrail task fails
Attributes:
error: The error message
retry_count: The number of times the guardrail has been retried
"""
type: str = "llm_guardrail_failed"
error: str
retry_count: int

View File

@@ -1,6 +1,6 @@
"""Agent logging events that don't reference BaseAgent to avoid circular imports."""
from typing import Any, Literal
from typing import Any
from pydantic import ConfigDict
@@ -13,7 +13,7 @@ class AgentLogsStartedEvent(BaseEvent):
agent_role: str
task_description: str | None = None
verbose: bool = False
type: Literal["agent_logs_started"] = "agent_logs_started"
type: str = "agent_logs_started"
class AgentLogsExecutionEvent(BaseEvent):
@@ -22,6 +22,6 @@ class AgentLogsExecutionEvent(BaseEvent):
agent_role: str
formatted_answer: Any
verbose: bool = False
type: Literal["agent_logs_execution"] = "agent_logs_execution"
type: str = "agent_logs_execution"
model_config = ConfigDict(arbitrary_types_allowed=True)

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -24,7 +24,7 @@ class MCPEvent(BaseEvent):
class MCPConnectionStartedEvent(MCPEvent):
"""Event emitted when starting to connect to an MCP server."""
type: Literal["mcp_connection_started"] = "mcp_connection_started"
type: str = "mcp_connection_started"
connect_timeout: int | None = None
is_reconnect: bool = (
False # True if this is a reconnection, False for first connection
@@ -34,7 +34,7 @@ class MCPConnectionStartedEvent(MCPEvent):
class MCPConnectionCompletedEvent(MCPEvent):
"""Event emitted when successfully connected to an MCP server."""
type: Literal["mcp_connection_completed"] = "mcp_connection_completed"
type: str = "mcp_connection_completed"
started_at: datetime | None = None
completed_at: datetime | None = None
connection_duration_ms: float | None = None
@@ -46,7 +46,7 @@ class MCPConnectionCompletedEvent(MCPEvent):
class MCPConnectionFailedEvent(MCPEvent):
"""Event emitted when connection to an MCP server fails."""
type: Literal["mcp_connection_failed"] = "mcp_connection_failed"
type: str = "mcp_connection_failed"
error: str
error_type: str | None = None # "timeout", "authentication", "network", etc.
started_at: datetime | None = None
@@ -56,7 +56,7 @@ class MCPConnectionFailedEvent(MCPEvent):
class MCPToolExecutionStartedEvent(MCPEvent):
"""Event emitted when starting to execute an MCP tool."""
type: Literal["mcp_tool_execution_started"] = "mcp_tool_execution_started"
type: str = "mcp_tool_execution_started"
tool_name: str
tool_args: dict[str, Any] | None = None
@@ -64,7 +64,7 @@ class MCPToolExecutionStartedEvent(MCPEvent):
class MCPToolExecutionCompletedEvent(MCPEvent):
"""Event emitted when MCP tool execution completes."""
type: Literal["mcp_tool_execution_completed"] = "mcp_tool_execution_completed"
type: str = "mcp_tool_execution_completed"
tool_name: str
tool_args: dict[str, Any] | None = None
result: Any | None = None
@@ -76,7 +76,7 @@ class MCPToolExecutionCompletedEvent(MCPEvent):
class MCPToolExecutionFailedEvent(MCPEvent):
"""Event emitted when MCP tool execution fails."""
type: Literal["mcp_tool_execution_failed"] = "mcp_tool_execution_failed"
type: str = "mcp_tool_execution_failed"
tool_name: str
tool_args: dict[str, Any] | None = None
error: str
@@ -92,7 +92,7 @@ class MCPConfigFetchFailedEvent(BaseEvent):
failed, or native MCP resolution failed after config was fetched.
"""
type: Literal["mcp_config_fetch_failed"] = "mcp_config_fetch_failed"
type: str = "mcp_config_fetch_failed"
slug: str
error: str
error_type: str | None = None # "not_connected", "api_error", "connection_failed"

View File

@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -23,7 +23,7 @@ class MemoryBaseEvent(BaseEvent):
class MemoryQueryStartedEvent(MemoryBaseEvent):
"""Event emitted when a memory query is started"""
type: Literal["memory_query_started"] = "memory_query_started"
type: str = "memory_query_started"
query: str
limit: int
score_threshold: float | None = None
@@ -32,7 +32,7 @@ class MemoryQueryStartedEvent(MemoryBaseEvent):
class MemoryQueryCompletedEvent(MemoryBaseEvent):
"""Event emitted when a memory query is completed successfully"""
type: Literal["memory_query_completed"] = "memory_query_completed"
type: str = "memory_query_completed"
query: str
results: Any
limit: int
@@ -43,7 +43,7 @@ class MemoryQueryCompletedEvent(MemoryBaseEvent):
class MemoryQueryFailedEvent(MemoryBaseEvent):
"""Event emitted when a memory query fails"""
type: Literal["memory_query_failed"] = "memory_query_failed"
type: str = "memory_query_failed"
query: str
limit: int
score_threshold: float | None = None
@@ -53,7 +53,7 @@ class MemoryQueryFailedEvent(MemoryBaseEvent):
class MemorySaveStartedEvent(MemoryBaseEvent):
"""Event emitted when a memory save operation is started"""
type: Literal["memory_save_started"] = "memory_save_started"
type: str = "memory_save_started"
value: str | None = None
metadata: dict[str, Any] | None = None
agent_role: str | None = None
@@ -62,7 +62,7 @@ class MemorySaveStartedEvent(MemoryBaseEvent):
class MemorySaveCompletedEvent(MemoryBaseEvent):
"""Event emitted when a memory save operation is completed successfully"""
type: Literal["memory_save_completed"] = "memory_save_completed"
type: str = "memory_save_completed"
value: str
metadata: dict[str, Any] | None = None
agent_role: str | None = None
@@ -72,7 +72,7 @@ class MemorySaveCompletedEvent(MemoryBaseEvent):
class MemorySaveFailedEvent(MemoryBaseEvent):
"""Event emitted when a memory save operation fails"""
type: Literal["memory_save_failed"] = "memory_save_failed"
type: str = "memory_save_failed"
value: str | None = None
metadata: dict[str, Any] | None = None
agent_role: str | None = None
@@ -82,14 +82,14 @@ class MemorySaveFailedEvent(MemoryBaseEvent):
class MemoryRetrievalStartedEvent(MemoryBaseEvent):
"""Event emitted when memory retrieval for a task prompt starts"""
type: Literal["memory_retrieval_started"] = "memory_retrieval_started"
type: str = "memory_retrieval_started"
task_id: str | None = None
class MemoryRetrievalCompletedEvent(MemoryBaseEvent):
"""Event emitted when memory retrieval for a task prompt completes successfully"""
type: Literal["memory_retrieval_completed"] = "memory_retrieval_completed"
type: str = "memory_retrieval_completed"
task_id: str | None = None
memory_content: str
retrieval_time_ms: float
@@ -98,6 +98,6 @@ class MemoryRetrievalCompletedEvent(MemoryBaseEvent):
class MemoryRetrievalFailedEvent(MemoryBaseEvent):
"""Event emitted when memory retrieval for a task prompt fails."""
type: Literal["memory_retrieval_failed"] = "memory_retrieval_failed"
type: str = "memory_retrieval_failed"
task_id: str | None = None
error: str

View File

@@ -5,7 +5,7 @@ PlannerObserver analyzes step execution results and decides on plan
continuation, refinement, or replanning.
"""
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -32,7 +32,7 @@ class StepObservationStartedEvent(ObservationEvent):
Fires after every step execution, before the observation LLM call.
"""
type: Literal["step_observation_started"] = "step_observation_started"
type: str = "step_observation_started"
class StepObservationCompletedEvent(ObservationEvent):
@@ -42,7 +42,7 @@ class StepObservationCompletedEvent(ObservationEvent):
the plan is still valid, and what action to take next.
"""
type: Literal["step_observation_completed"] = "step_observation_completed"
type: str = "step_observation_completed"
step_completed_successfully: bool = True
key_information_learned: str = ""
remaining_plan_still_valid: bool = True
@@ -59,7 +59,7 @@ class StepObservationFailedEvent(ObservationEvent):
but the event allows monitoring/alerting on observation failures.
"""
type: Literal["step_observation_failed"] = "step_observation_failed"
type: str = "step_observation_failed"
error: str = ""
@@ -70,7 +70,7 @@ class PlanRefinementEvent(ObservationEvent):
sharpening pending todo descriptions based on new information.
"""
type: Literal["plan_refinement"] = "plan_refinement"
type: str = "plan_refinement"
refined_step_count: int = 0
refinements: list[str] | None = None
@@ -82,7 +82,7 @@ class PlanReplanTriggeredEvent(ObservationEvent):
regenerated from scratch, preserving completed step results.
"""
type: Literal["plan_replan_triggered"] = "plan_replan_triggered"
type: str = "plan_replan_triggered"
replan_reason: str = ""
replan_count: int = 0
completed_steps_preserved: int = 0
@@ -94,6 +94,6 @@ class GoalAchievedEarlyEvent(ObservationEvent):
Remaining steps will be skipped and execution will finalize.
"""
type: Literal["goal_achieved_early"] = "goal_achieved_early"
type: str = "goal_achieved_early"
steps_remaining: int = 0
steps_completed: int = 0

View File

@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -24,7 +24,7 @@ class ReasoningEvent(BaseEvent):
class AgentReasoningStartedEvent(ReasoningEvent):
"""Event emitted when an agent starts reasoning about a task."""
type: Literal["agent_reasoning_started"] = "agent_reasoning_started"
type: str = "agent_reasoning_started"
agent_role: str
task_id: str
@@ -32,7 +32,7 @@ class AgentReasoningStartedEvent(ReasoningEvent):
class AgentReasoningCompletedEvent(ReasoningEvent):
"""Event emitted when an agent finishes its reasoning process."""
type: Literal["agent_reasoning_completed"] = "agent_reasoning_completed"
type: str = "agent_reasoning_completed"
agent_role: str
task_id: str
plan: str
@@ -42,7 +42,7 @@ class AgentReasoningCompletedEvent(ReasoningEvent):
class AgentReasoningFailedEvent(ReasoningEvent):
"""Event emitted when the reasoning process fails."""
type: Literal["agent_reasoning_failed"] = "agent_reasoning_failed"
type: str = "agent_reasoning_failed"
agent_role: str
task_id: str
error: str

View File

@@ -6,7 +6,7 @@ Events emitted during skill discovery, loading, and activation.
from __future__ import annotations
from pathlib import Path
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
@@ -28,14 +28,14 @@ class SkillEvent(BaseEvent):
class SkillDiscoveryStartedEvent(SkillEvent):
"""Event emitted when skill discovery begins."""
type: Literal["skill_discovery_started"] = "skill_discovery_started"
type: str = "skill_discovery_started"
search_path: Path
class SkillDiscoveryCompletedEvent(SkillEvent):
"""Event emitted when skill discovery completes."""
type: Literal["skill_discovery_completed"] = "skill_discovery_completed"
type: str = "skill_discovery_completed"
search_path: Path
skills_found: int
skill_names: list[str]
@@ -44,19 +44,19 @@ class SkillDiscoveryCompletedEvent(SkillEvent):
class SkillLoadedEvent(SkillEvent):
"""Event emitted when a skill is loaded at metadata level."""
type: Literal["skill_loaded"] = "skill_loaded"
type: str = "skill_loaded"
disclosure_level: int = 1
class SkillActivatedEvent(SkillEvent):
"""Event emitted when a skill is activated (promoted to instructions level)."""
type: Literal["skill_activated"] = "skill_activated"
type: str = "skill_activated"
disclosure_level: int = 2
class SkillLoadFailedEvent(SkillEvent):
"""Event emitted when skill loading fails."""
type: Literal["skill_load_failed"] = "skill_load_failed"
type: str = "skill_load_failed"
error: str

View File

@@ -1,20 +1,12 @@
from typing import Any, Literal
from typing import Any
from crewai.events.base_events import BaseEvent
from crewai.tasks.task_output import TaskOutput
def _set_task_fingerprint(event: BaseEvent, task: Any) -> None:
"""Set task identity and fingerprint data on an event."""
if task is None:
return
task_id = getattr(task, "id", None)
if task_id is not None:
event.task_id = str(task_id)
task_name = getattr(task, "name", None) or getattr(task, "description", None)
if task_name:
event.task_name = task_name
if task.fingerprint:
"""Set fingerprint data on an event from a task object."""
if task is not None and task.fingerprint:
event.source_fingerprint = task.fingerprint.uuid_str
event.source_type = "task"
if task.fingerprint.metadata:
@@ -24,7 +16,7 @@ def _set_task_fingerprint(event: BaseEvent, task: Any) -> None:
class TaskStartedEvent(BaseEvent):
"""Event emitted when a task starts"""
type: Literal["task_started"] = "task_started"
type: str = "task_started"
context: str | None
task: Any | None = None
@@ -37,7 +29,7 @@ class TaskCompletedEvent(BaseEvent):
"""Event emitted when a task completes"""
output: TaskOutput
type: Literal["task_completed"] = "task_completed"
type: str = "task_completed"
task: Any | None = None
def __init__(self, **data: Any) -> None:
@@ -49,7 +41,7 @@ class TaskFailedEvent(BaseEvent):
"""Event emitted when a task fails"""
error: str
type: Literal["task_failed"] = "task_failed"
type: str = "task_failed"
task: Any | None = None
def __init__(self, **data: Any) -> None:
@@ -60,7 +52,7 @@ class TaskFailedEvent(BaseEvent):
class TaskEvaluationEvent(BaseEvent):
"""Event emitted when a task evaluation is completed"""
type: Literal["task_evaluation"] = "task_evaluation"
type: str = "task_evaluation"
evaluation_type: str
task: Any | None = None

View File

@@ -1,6 +1,6 @@
from collections.abc import Callable
from datetime import datetime
from typing import Any, Literal
from typing import Any
from pydantic import ConfigDict
@@ -55,7 +55,7 @@ class ToolUsageEvent(BaseEvent):
class ToolUsageStartedEvent(ToolUsageEvent):
"""Event emitted when a tool execution is started"""
type: Literal["tool_usage_started"] = "tool_usage_started"
type: str = "tool_usage_started"
class ToolUsageFinishedEvent(ToolUsageEvent):
@@ -65,35 +65,35 @@ class ToolUsageFinishedEvent(ToolUsageEvent):
finished_at: datetime
from_cache: bool = False
output: Any
type: Literal["tool_usage_finished"] = "tool_usage_finished"
type: str = "tool_usage_finished"
class ToolUsageErrorEvent(ToolUsageEvent):
"""Event emitted when a tool execution encounters an error"""
error: Any
type: Literal["tool_usage_error"] = "tool_usage_error"
type: str = "tool_usage_error"
class ToolValidateInputErrorEvent(ToolUsageEvent):
"""Event emitted when a tool input validation encounters an error"""
error: Any
type: Literal["tool_validate_input_error"] = "tool_validate_input_error"
type: str = "tool_validate_input_error"
class ToolSelectionErrorEvent(ToolUsageEvent):
"""Event emitted when a tool selection encounters an error"""
error: Any
type: Literal["tool_selection_error"] = "tool_selection_error"
type: str = "tool_selection_error"
class ToolExecutionErrorEvent(BaseEvent):
"""Event emitted when a tool execution encounters an error"""
error: Any
type: Literal["tool_execution_error"] = "tool_execution_error"
type: str = "tool_execution_error"
tool_name: str
tool_args: dict[str, Any]
tool_class: Callable[..., Any]

View File

@@ -10,23 +10,6 @@ from crewai.events.base_events import BaseEvent
from crewai.events.types.event_bus_types import AsyncHandler, SyncHandler
@functools.lru_cache(maxsize=256)
def _get_param_count_cached(handler: Any) -> int:
return len(inspect.signature(handler).parameters)
def _get_param_count(handler: Any) -> int:
"""Return the number of parameters a handler accepts, with caching.
Falls back to uncached introspection for unhashable handlers
like functools.partial.
"""
try:
return _get_param_count_cached(handler)
except TypeError:
return len(inspect.signature(handler).parameters)
def is_async_handler(
handler: Any,
) -> TypeIs[AsyncHandler]:
@@ -58,7 +41,6 @@ def is_call_handler_safe(
handler: SyncHandler,
source: Any,
event: BaseEvent,
state: Any = None,
) -> Exception | None:
"""Safely call a single handler and return any exception.
@@ -66,16 +48,12 @@ def is_call_handler_safe(
handler: The handler function to call
source: The object that emitted the event
event: The event instance
state: Optional RuntimeState passed as third arg if handler accepts it
Returns:
Exception if handler raised one, None otherwise
"""
try:
if _get_param_count(handler) >= 3:
handler(source, event, state) # type: ignore[call-arg]
else:
handler(source, event) # type: ignore[call-arg]
handler(source, event)
return None
except Exception as e:
return e

Some files were not shown because too many files have changed in this diff Show More