mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-11 05:22:41 +00:00
Compare commits
61 Commits
docs/sso-c
...
devin/1775
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da65140cf8 | ||
|
|
3702a47bfe | ||
|
|
e64b37c5fc | ||
|
|
c132d57a36 | ||
|
|
ad24c3d56e | ||
|
|
0c307f1621 | ||
|
|
f98dde6c62 | ||
|
|
6b6e191532 | ||
|
|
c4e2d7ea3b | ||
|
|
86ce54fc82 | ||
|
|
bf2f4dbce6 | ||
|
|
fdb9b6f090 | ||
|
|
71b4667a0e | ||
|
|
c393bd2ee6 | ||
|
|
baf15a409b | ||
|
|
c907ce473b | ||
|
|
e46402d10d | ||
|
|
bce10f5978 | ||
|
|
d2e57e375b | ||
|
|
d039a075aa | ||
|
|
ce99312db1 | ||
|
|
c571620f8c | ||
|
|
931f3556cf | ||
|
|
914776b7ed | ||
|
|
6ef6fada4d | ||
|
|
1b7be63b60 | ||
|
|
59aa5b2243 | ||
|
|
2e2fae02d2 | ||
|
|
804c26bd01 | ||
|
|
4e46913045 | ||
|
|
335130cb15 | ||
|
|
186ea77c63 | ||
|
|
9e51229e6c | ||
|
|
247d623499 | ||
|
|
c260f3e19f | ||
|
|
d9cf7dda31 | ||
|
|
c14abf1758 | ||
|
|
f10d320ddb | ||
|
|
258f31d44c | ||
|
|
68720fd4e5 | ||
|
|
3132910084 | ||
|
|
c8f3a96779 | ||
|
|
18ada25f01 | ||
|
|
146da8d73a | ||
|
|
98c6109214 | ||
|
|
54a9174c12 | ||
|
|
c26ae969b3 | ||
|
|
205555b786 | ||
|
|
d6714a0e60 | ||
|
|
107bc7f7be | ||
|
|
b1f49b1356 | ||
|
|
accae5ca43 | ||
|
|
68e943be68 | ||
|
|
3283a00e31 | ||
|
|
dfc0f9a317 | ||
|
|
ef79456968 | ||
|
|
6c7ea422e7 | ||
|
|
bb9bcd6823 | ||
|
|
ac14b9127e | ||
|
|
98b7626784 | ||
|
|
e21c506214 |
2
.github/workflows/build-uv-cache.yml
vendored
2
.github/workflows/build-uv-cache.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/generate-tool-specs.yml
vendored
2
.github/workflows/generate-tool-specs.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: true
|
||||
|
||||
|
||||
2
.github/workflows/linter.yml
vendored
2
.github/workflows/linter.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: "3.11"
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -95,7 +95,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/type-checker.yml
vendored
2
.github/workflows/type-checker.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
|
||||
2
.github/workflows/update-test-durations.yml
vendored
2
.github/workflows/update-test-durations.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
version: "0.8.4"
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: false
|
||||
|
||||
|
||||
105
.github/workflows/vulnerability-scan.yml
vendored
Normal file
105
.github/workflows/vulnerability-scan.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
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') }}
|
||||
|
||||
@@ -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.9.3
|
||||
rev: 0.11.3
|
||||
hooks:
|
||||
- id: uv-lock
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
|
||||
@@ -4,6 +4,205 @@ 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
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إضافة امتداد A2UI مع دعم v0.8/v0.9، والمخططات، والوثائق
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح بادئات الرؤية متعددة الأنماط عن طريق إضافة GPT-5 وسلسلة o
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.13.0a6
|
||||
|
||||
## المساهمون
|
||||
|
||||
@alex-clawd, @greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="1 أبريل 2026">
|
||||
## v1.13.0a6
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الوثائق
|
||||
- إصلاح مستويات أذونات RBAC لتتوافق مع خيارات واجهة المستخدم الفعلية (#5210)
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.13.0a5 (#5200)
|
||||
|
||||
### الأداء
|
||||
- تقليل عبء العمل على الإطار من خلال تنفيذ حافلة أحداث كسولة وتجاوز التتبع عند تعطيله (#5187)
|
||||
|
||||
## المساهمون
|
||||
|
||||
@alex-clawd, @joaomdmoura, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="31 مارس 2026">
|
||||
## v1.13.0a5
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a5)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.13.0a4
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="1 أبريل 2026">
|
||||
## v1.13.0a4
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a4)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.13.0a3
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="1 أبريل 2026">
|
||||
## v1.13.0a3
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a3)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إصدار بيانات استخدام الرمز في LLMCallCompletedEvent
|
||||
- استخراج ونشر بيانات الأداة إلى AMP
|
||||
|
||||
### إصلاح الأخطاء
|
||||
- التعامل مع نماذج GPT-5.x التي لا تدعم معلمة API `stop`
|
||||
|
||||
### الوثائق
|
||||
- إصلاح عدم الدقة في قدرات الوكيل عبر جميع اللغات
|
||||
- إضافة نظرة عامة على قدرات الوكيل وتحسين وثائق المهارات
|
||||
- إضافة دليل شامل لتكوين SSO
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.13.0rc1
|
||||
|
||||
### إعادة الهيكلة
|
||||
- تحويل Flow إلى Pydantic BaseModel
|
||||
- تحويل فئات LLM إلى Pydantic BaseModel
|
||||
- استبدال InstanceOf[T] بتعليقات نوع عادية
|
||||
- إزالة الطرق غير المستخدمة
|
||||
|
||||
## المساهمون
|
||||
|
||||
@dependabot[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide, @thiagomoretto
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="27 مارس 2026">
|
||||
## v1.13.0rc1
|
||||
|
||||
|
||||
147
docs/ar/concepts/agent-capabilities.mdx
Normal file
147
docs/ar/concepts/agent-capabilities.mdx
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: "قدرات الوكيل"
|
||||
description: "فهم الطرق الخمس لتوسيع وكلاء CrewAI: الأدوات، MCP، التطبيقات، المهارات، والمعرفة."
|
||||
icon: puzzle-piece
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يمكن توسيع وكلاء CrewAI بـ **خمسة أنواع مميزة من القدرات**، كل منها يخدم غرضًا مختلفًا. فهم متى تستخدم كل نوع — وكيف يعملون معًا — هو المفتاح لبناء وكلاء فعّالين.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="الأدوات" icon="wrench" href="/ar/concepts/tools" color="#3B82F6">
|
||||
**دوال قابلة للاستدعاء** — تمنح الوكلاء القدرة على اتخاذ إجراءات. البحث على الويب، عمليات الملفات، استدعاءات API، تنفيذ الكود.
|
||||
</Card>
|
||||
<Card title="خوادم MCP" icon="plug" href="/ar/mcp/overview" color="#8B5CF6">
|
||||
**خوادم أدوات عن بُعد** — تربط الوكلاء بخوادم أدوات خارجية عبر Model Context Protocol. نفس تأثير الأدوات، لكن مستضافة خارجيًا.
|
||||
</Card>
|
||||
<Card title="التطبيقات" icon="grid-2" color="#EC4899">
|
||||
**تكاملات المنصة** — تربط الوكلاء بتطبيقات SaaS (Gmail، Slack، Jira، Salesforce) عبر منصة CrewAI. تعمل محليًا مع رمز تكامل المنصة.
|
||||
</Card>
|
||||
<Card title="المهارات" icon="bolt" href="/ar/concepts/skills" color="#F59E0B">
|
||||
**خبرة المجال** — تحقن التعليمات والإرشادات والمواد المرجعية في إرشادات الوكلاء. المهارات تخبر الوكلاء *كيف يفكرون*.
|
||||
</Card>
|
||||
<Card title="المعرفة" icon="book" href="/ar/concepts/knowledge" color="#10B981">
|
||||
**حقائق مُسترجعة** — توفر للوكلاء بيانات من المستندات والملفات وعناوين URL عبر البحث الدلالي (RAG). المعرفة تعطي الوكلاء *ما يحتاجون معرفته*.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## التمييز الأساسي
|
||||
|
||||
أهم شيء يجب فهمه: **هذه القدرات تنقسم إلى فئتين**.
|
||||
|
||||
### قدرات الإجراء (الأدوات، MCP، التطبيقات)
|
||||
|
||||
تمنح الوكلاء القدرة على **فعل أشياء** — استدعاء APIs، قراءة الملفات، البحث على الويب، إرسال رسائل البريد الإلكتروني. عند التنفيذ، تتحول الأنواع الثلاثة إلى نفس التنسيق الداخلي (مثيلات `BaseTool`) وتظهر في قائمة أدوات موحدة يمكن للوكيل استدعاؤها.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find and compile market data",
|
||||
backstory="Expert market analyst",
|
||||
tools=[SerperDevTool(), FileReadTool()], # أدوات محلية
|
||||
mcps=["https://mcp.example.com/sse"], # أدوات خادم MCP عن بُعد
|
||||
apps=["gmail", "google_sheets"], # تكاملات المنصة
|
||||
)
|
||||
```
|
||||
|
||||
### قدرات السياق (المهارات، المعرفة)
|
||||
|
||||
تُعدّل **إرشادات** الوكيل — بحقن الخبرة أو التعليمات أو البيانات المُسترجعة قبل أن يبدأ الوكيل في التفكير. لا تمنح الوكلاء إجراءات جديدة؛ بل تُشكّل كيف يفكر الوكلاء وما هي المعلومات التي يمكنهم الوصول إليها.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Security Auditor",
|
||||
goal="Audit cloud infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security with 10 years of experience",
|
||||
skills=["./skills/security-audit"], # تعليمات المجال
|
||||
knowledge_sources=[pdf_source, url_source], # حقائق مُسترجعة
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## متى تستخدم ماذا
|
||||
|
||||
| تحتاج إلى... | استخدم | مثال |
|
||||
| :------------------------------------------------------- | :---------------- | :--------------------------------------- |
|
||||
| الوكيل يبحث على الويب | **الأدوات** | `tools=[SerperDevTool()]` |
|
||||
| الوكيل يستدعي API عن بُعد عبر MCP | **MCP** | `mcps=["https://api.example.com/sse"]` |
|
||||
| الوكيل يرسل بريد إلكتروني عبر Gmail | **التطبيقات** | `apps=["gmail"]` |
|
||||
| الوكيل يتبع إجراءات محددة | **المهارات** | `skills=["./skills/code-review"]` |
|
||||
| الوكيل يرجع لمستندات الشركة | **المعرفة** | `knowledge_sources=[pdf_source]` |
|
||||
| الوكيل يبحث على الويب ويتبع إرشادات المراجعة | **الأدوات + المهارات** | استخدم كليهما معًا |
|
||||
|
||||
---
|
||||
|
||||
## دمج القدرات
|
||||
|
||||
في الممارسة العملية، غالبًا ما يستخدم الوكلاء **أنواعًا متعددة من القدرات معًا**. إليك مثال واقعي:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
# وكيل بحث مجهز بالكامل
|
||||
researcher = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Produce comprehensive market analysis reports",
|
||||
backstory="Expert analyst with deep industry knowledge",
|
||||
|
||||
# الإجراء: ما يمكن للوكيل فعله
|
||||
tools=[
|
||||
SerperDevTool(), # البحث على الويب
|
||||
FileReadTool(), # قراءة الملفات المحلية
|
||||
CodeInterpreterTool(), # تشغيل كود Python للتحليل
|
||||
],
|
||||
mcps=["https://data-api.example.com/sse"], # الوصول لـ API بيانات عن بُعد
|
||||
apps=["google_sheets"], # الكتابة في Google Sheets
|
||||
|
||||
# السياق: ما يعرفه الوكيل
|
||||
skills=["./skills/research-methodology"], # كيفية إجراء البحث
|
||||
knowledge_sources=[company_docs], # بيانات خاصة بالشركة
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## جدول المقارنة
|
||||
|
||||
| الميزة | الأدوات | MCP | التطبيقات | المهارات | المعرفة |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: |
|
||||
| **يمنح الوكيل إجراءات** | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| **يُعدّل الإرشادات** | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| **يتطلب كود** | نعم | إعداد فقط | إعداد فقط | Markdown فقط | إعداد فقط |
|
||||
| **يعمل محليًا** | نعم | يعتمد | نعم (مع متغير بيئة) | غير متاح | نعم |
|
||||
| **يحتاج مفاتيح API** | لكل أداة | لكل خادم | رمز التكامل | لا | المُضمّن فقط |
|
||||
| **يُعيَّن على Agent** | `tools=[]` | `mcps=[]` | `apps=[]` | `skills=[]` | `knowledge_sources=[]` |
|
||||
| **يُعيَّن على Crew** | ❌ | ❌ | ❌ | `skills=[]` | `knowledge_sources=[]` |
|
||||
|
||||
---
|
||||
|
||||
## تعمّق أكثر
|
||||
|
||||
هل أنت مستعد لمعرفة المزيد عن كل نوع من أنواع القدرات؟
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="الأدوات" icon="wrench" href="/ar/concepts/tools">
|
||||
إنشاء أدوات مخصصة، استخدام كتالوج OSS مع أكثر من 75 خيارًا، تكوين التخزين المؤقت والتنفيذ غير المتزامن.
|
||||
</Card>
|
||||
<Card title="تكامل MCP" icon="plug" href="/ar/mcp/overview">
|
||||
الاتصال بخوادم MCP عبر stdio أو SSE أو HTTP. تصفية الأدوات، تكوين المصادقة.
|
||||
</Card>
|
||||
<Card title="المهارات" icon="bolt" href="/ar/concepts/skills">
|
||||
بناء حزم المهارات مع SKILL.md، حقن خبرة المجال، استخدام الكشف التدريجي.
|
||||
</Card>
|
||||
<Card title="المعرفة" icon="book" href="/ar/concepts/knowledge">
|
||||
إضافة المعرفة من ملفات PDF وCSV وعناوين URL والمزيد. تكوين المُضمّنات والاسترجاع.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
232
docs/ar/concepts/checkpointing.mdx
Normal file
232
docs/ar/concepts/checkpointing.mdx
Normal file
@@ -0,0 +1,232 @@
|
||||
---
|
||||
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 يعمل بافضل جهد: اذا فشلت كتابة نقطة حفظ، يتم تسجيل الخطأ ولكن التنفيذ يستمر دون انقطاع.
|
||||
@@ -1,15 +1,217 @@
|
||||
---
|
||||
title: المهارات
|
||||
description: حزم المهارات المبنية على نظام الملفات التي تحقن السياق في إرشادات الوكيل.
|
||||
description: حزم المهارات المبنية على نظام الملفات التي تحقن خبرة المجال والتعليمات في إرشادات الوكلاء.
|
||||
icon: bolt
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
المهارات هي مجلدات مستقلة توفر للوكلاء تعليمات ومراجع وموارد خاصة بالمجال. تُعرّف كل مهارة بملف `SKILL.md` يحتوي على بيانات وصفية YAML ومحتوى Markdown.
|
||||
المهارات هي مجلدات مستقلة توفر للوكلاء **تعليمات وإرشادات ومواد مرجعية خاصة بالمجال**. تُعرّف كل مهارة بملف `SKILL.md` يحتوي على بيانات وصفية YAML ومحتوى Markdown.
|
||||
|
||||
تستخدم المهارات **الكشف التدريجي** — يتم تحميل البيانات الوصفية أولاً، ثم التعليمات الكاملة فقط عند التفعيل، وكتالوجات الموارد فقط عند الحاجة.
|
||||
عند التفعيل، يتم حقن تعليمات المهارة مباشرة في إرشادات مهمة الوكيل — مما يمنح الوكيل خبرة دون الحاجة لأي تغييرات في الكود.
|
||||
|
||||
<Note type="info" title="المهارات مقابل الأدوات — التمييز الأساسي">
|
||||
**المهارات ليست أدوات.** هذه هي نقطة الارتباك الأكثر شيوعًا.
|
||||
|
||||
- **المهارات** تحقن *تعليمات وسياق* في إرشادات الوكيل. تخبر الوكيل *كيف يفكر* في مشكلة ما.
|
||||
- **الأدوات** تمنح الوكيل *دوال قابلة للاستدعاء* لاتخاذ إجراءات (البحث، قراءة الملفات، استدعاء APIs).
|
||||
|
||||
غالبًا ما تحتاج **كليهما**: مهارات للخبرة، وأدوات للإجراء. يتم تكوينهما بشكل مستقل ويُكمّلان بعضهما.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## البداية السريعة
|
||||
|
||||
### 1. إنشاء مجلد المهارة
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # مطلوب — التعليمات
|
||||
├── references/ # اختياري — مستندات مرجعية
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # اختياري — سكربتات قابلة للتنفيذ
|
||||
```
|
||||
|
||||
### 2. كتابة SKILL.md الخاص بك
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Guidelines for conducting thorough code reviews with focus on security and performance.
|
||||
metadata:
|
||||
author: your-team
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
## إرشادات مراجعة الكود
|
||||
|
||||
عند مراجعة الكود، اتبع قائمة التحقق هذه:
|
||||
|
||||
1. **الأمان**: تحقق من ثغرات الحقن وتجاوز المصادقة وكشف البيانات
|
||||
2. **الأداء**: ابحث عن استعلامات N+1 والتخصيصات غير الضرورية والاستدعاءات المحظورة
|
||||
3. **القابلية للقراءة**: تأكد من وضوح التسمية والتعليقات المناسبة والأسلوب المتسق
|
||||
4. **الاختبارات**: تحقق من تغطية اختبار كافية للوظائف الجديدة
|
||||
|
||||
### مستويات الخطورة
|
||||
- **حرج**: ثغرات أمنية، مخاطر فقدان البيانات → حظر الدمج
|
||||
- **رئيسي**: مشاكل أداء، أخطاء منطقية → طلب تغييرات
|
||||
- **ثانوي**: مسائل أسلوبية، اقتراحات تسمية → الموافقة مع تعليقات
|
||||
```
|
||||
|
||||
### 3. ربطها بوكيل
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import GithubSearchTool, FileReadTool
|
||||
|
||||
reviewer = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["./skills"], # يحقن إرشادات المراجعة
|
||||
tools=[GithubSearchTool(), FileReadTool()], # يسمح للوكيل بقراءة الكود
|
||||
)
|
||||
```
|
||||
|
||||
الوكيل الآن لديه **خبرة** (من المهارة) و**قدرات** (من الأدوات) معًا.
|
||||
|
||||
---
|
||||
|
||||
## المهارات + الأدوات: العمل معًا
|
||||
|
||||
إليك أنماط شائعة توضح كيف تُكمّل المهارات والأدوات بعضهما:
|
||||
|
||||
### النمط 1: مهارات فقط (خبرة المجال، بدون إجراءات مطلوبة)
|
||||
|
||||
استخدم عندما يحتاج الوكيل لتعليمات محددة لكن لا يحتاج لاستدعاء خدمات خارجية:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Technical Writer",
|
||||
goal="Write clear API documentation",
|
||||
backstory="Expert technical writer",
|
||||
skills=["./skills/api-docs-style"], # إرشادات وقوالب الكتابة
|
||||
# لا حاجة لأدوات — الوكيل يكتب بناءً على السياق المقدم
|
||||
)
|
||||
```
|
||||
|
||||
### النمط 2: أدوات فقط (إجراءات، بدون خبرة خاصة)
|
||||
|
||||
استخدم عندما يحتاج الوكيل لاتخاذ إجراءات لكن لا يحتاج لتعليمات مجال محددة:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
|
||||
|
||||
agent = Agent(
|
||||
role="Web Researcher",
|
||||
goal="Find information about a topic",
|
||||
backstory="Skilled at finding information online",
|
||||
tools=[SerperDevTool(), ScrapeWebsiteTool()], # يمكنه البحث والاستخراج
|
||||
# لا حاجة لمهارات — البحث العام لا يحتاج إرشادات خاصة
|
||||
)
|
||||
```
|
||||
|
||||
### النمط 3: مهارات + أدوات (خبرة وإجراءات)
|
||||
|
||||
النمط الأكثر شيوعًا في العالم الحقيقي. المهارة توفر *كيف* تقترب من العمل؛ الأدوات توفر *ما* يمكن للوكيل فعله:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
analyst = Agent(
|
||||
role="Security Analyst",
|
||||
goal="Audit infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security and compliance",
|
||||
skills=["./skills/security-audit"], # منهجية وقوائم تحقق التدقيق
|
||||
tools=[
|
||||
SerperDevTool(), # البحث عن ثغرات معروفة
|
||||
FileReadTool(), # قراءة ملفات التكوين
|
||||
CodeInterpreterTool(), # تشغيل سكربتات التحليل
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### النمط 4: مهارات + MCP
|
||||
|
||||
المهارات تعمل مع خوادم MCP بنفس الطريقة التي تعمل بها مع الأدوات:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze customer data and generate reports",
|
||||
backstory="Expert data analyst with strong statistical background",
|
||||
skills=["./skills/data-analysis"], # منهجية التحليل
|
||||
mcps=["https://data-warehouse.example.com/sse"], # وصول بيانات عن بُعد
|
||||
)
|
||||
```
|
||||
|
||||
### النمط 5: مهارات + تطبيقات
|
||||
|
||||
المهارات يمكن أن توجّه كيف يستخدم الوكيل تكاملات المنصة:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Customer Support Agent",
|
||||
goal="Respond to customer inquiries professionally",
|
||||
backstory="Experienced support representative",
|
||||
skills=["./skills/support-playbook"], # قوالب الردود وقواعد التصعيد
|
||||
apps=["gmail", "zendesk"], # يمكنه إرسال رسائل بريد وتحديث التذاكر
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## المهارات على مستوى الطاقم
|
||||
|
||||
يمكن تعيين المهارات على الطاقم لتُطبّق على **جميع الوكلاء**:
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
|
||||
crew = Crew(
|
||||
agents=[researcher, writer, reviewer],
|
||||
tasks=[research_task, write_task, review_task],
|
||||
skills=["./skills"], # جميع الوكلاء يحصلون على هذه المهارات
|
||||
)
|
||||
```
|
||||
|
||||
المهارات على مستوى الوكيل لها الأولوية — إذا تم اكتشاف نفس المهارة في كلا المستويين، يتم استخدام نسخة الوكيل.
|
||||
|
||||
---
|
||||
|
||||
## تنسيق SKILL.md
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: my-skill
|
||||
description: وصف قصير لما تفعله هذه المهارة ومتى تُستخدم.
|
||||
license: Apache-2.0 # اختياري
|
||||
compatibility: crewai>=0.1.0 # اختياري
|
||||
metadata: # اختياري
|
||||
author: your-name
|
||||
version: "1.0"
|
||||
allowed-tools: web-search file-read # اختياري، تجريبي
|
||||
---
|
||||
|
||||
التعليمات للوكيل تُكتب هنا. يتم حقن محتوى Markdown هذا
|
||||
في إرشادات الوكيل عند تفعيل المهارة.
|
||||
```
|
||||
|
||||
### حقول البيانات الوصفية
|
||||
|
||||
| الحقل | مطلوب | الوصف |
|
||||
| :-------------- | :------- | :----------------------------------------------------------------------- |
|
||||
| `name` | نعم | 1-64 حرف. أحرف صغيرة أبجدية رقمية وشرطات. يجب أن يطابق اسم المجلد. |
|
||||
| `description` | نعم | 1-1024 حرف. يصف ما تفعله المهارة ومتى تُستخدم. |
|
||||
| `license` | لا | اسم الترخيص أو مرجع لملف ترخيص مضمّن. |
|
||||
| `compatibility` | لا | حد أقصى 500 حرف. متطلبات البيئة (منتجات، حزم، شبكة). |
|
||||
| `metadata` | لا | تعيين مفتاح-قيمة نصي عشوائي. |
|
||||
| `allowed-tools` | لا | قائمة أدوات معتمدة مسبقًا مفصولة بمسافات. تجريبي. |
|
||||
|
||||
---
|
||||
|
||||
## هيكل المجلد
|
||||
|
||||
@@ -21,79 +223,25 @@ my-skill/
|
||||
└── assets/ # اختياري — ملفات ثابتة (إعدادات، بيانات)
|
||||
```
|
||||
|
||||
يجب أن يتطابق اسم المجلد مع حقل `name` في `SKILL.md`.
|
||||
يجب أن يتطابق اسم المجلد مع حقل `name` في `SKILL.md`. مجلدات `scripts/` و `references/` و `assets/` متاحة في مسار المهارة `path` للوكلاء الذين يحتاجون للإشارة إلى الملفات مباشرة.
|
||||
|
||||
## تنسيق SKILL.md
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: my-skill
|
||||
description: Short description of what this skill does and when to use it.
|
||||
license: Apache-2.0 # optional
|
||||
compatibility: crewai>=0.1.0 # optional
|
||||
metadata: # optional
|
||||
author: your-name
|
||||
version: "1.0"
|
||||
allowed-tools: web-search file-read # optional, space-delimited
|
||||
---
|
||||
|
||||
Instructions for the agent go here. This markdown body is injected
|
||||
into the agent's prompt when the skill is activated.
|
||||
```
|
||||
## المهارات المحمّلة مسبقًا
|
||||
|
||||
### حقول البيانات الوصفية
|
||||
|
||||
| الحقل | مطلوب | القيود |
|
||||
| :-------------- | :------- | :----------------------------------------------------------------------- |
|
||||
| `name` | نعم | 1-64 حرف. أحرف صغيرة أبجدية رقمية وشرطات. بدون شرطات بادئة/لاحقة/متتالية. يجب أن يطابق اسم المجلد. |
|
||||
| `description` | نعم | 1-1024 حرف. يصف ما تفعله المهارة ومتى تُستخدم. |
|
||||
| `license` | لا | اسم الترخيص أو مرجع لملف ترخيص مضمّن. |
|
||||
| `compatibility` | لا | حد أقصى 500 حرف. متطلبات البيئة (منتجات، حزم، شبكة). |
|
||||
| `metadata` | لا | تعيين مفتاح-قيمة نصي عشوائي. |
|
||||
| `allowed-tools` | لا | قائمة أدوات معتمدة مسبقًا مفصولة بمسافات. تجريبي. |
|
||||
|
||||
## الاستخدام
|
||||
|
||||
### المهارات على مستوى الوكيل
|
||||
|
||||
مرر مسارات مجلدات المهارات إلى وكيل:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
backstory="An expert researcher.",
|
||||
skills=["./skills"], # يكتشف جميع المهارات في هذا المجلد
|
||||
)
|
||||
```
|
||||
|
||||
### المهارات على مستوى الطاقم
|
||||
|
||||
تُدمج مسارات المهارات في الطاقم مع كل وكيل:
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
skills=["./skills"],
|
||||
)
|
||||
```
|
||||
|
||||
### المهارات المحمّلة مسبقًا
|
||||
|
||||
يمكنك أيضًا تمرير كائنات `Skill` مباشرة:
|
||||
للمزيد من التحكم، يمكنك اكتشاف المهارات وتفعيلها برمجيًا:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from crewai.skills import discover_skills, activate_skill
|
||||
|
||||
# اكتشاف جميع المهارات في مجلد
|
||||
skills = discover_skills(Path("./skills"))
|
||||
|
||||
# تفعيلها (تحميل محتوى SKILL.md الكامل)
|
||||
activated = [activate_skill(s) for s in skills]
|
||||
|
||||
# تمرير إلى وكيل
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
@@ -102,13 +250,57 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## كيف يتم تحميل المهارات
|
||||
|
||||
يتم تحميل المهارات تدريجيًا — فقط البيانات المطلوبة في كل مرحلة يتم قراءتها:
|
||||
تستخدم المهارات **الكشف التدريجي** — تحمّل فقط ما هو مطلوب في كل مرحلة:
|
||||
|
||||
| المرحلة | ما يتم تحميله | متى |
|
||||
| :--------------- | :------------------------------------------------ | :----------------- |
|
||||
| الاكتشاف | الاسم، الوصف، حقول البيانات الوصفية | `discover_skills()` |
|
||||
| التفعيل | نص محتوى SKILL.md الكامل | `activate_skill()` |
|
||||
| المرحلة | ما يتم تحميله | متى |
|
||||
| :--------- | :------------------------------------ | :------------------ |
|
||||
| الاكتشاف | الاسم، الوصف، حقول البيانات الوصفية | `discover_skills()` |
|
||||
| التفعيل | نص محتوى SKILL.md الكامل | `activate_skill()` |
|
||||
|
||||
أثناء التنفيذ العادي للوكيل، يتم اكتشاف المهارات وتفعيلها تلقائيًا. مجلدات `scripts/` و `references/` و `assets/` متاحة في مسار المهارة `path` للوكلاء الذين يحتاجون للإشارة إلى الملفات مباشرة.
|
||||
أثناء التنفيذ العادي للوكيل (تمرير مسارات المجلدات عبر `skills=["./skills"]`)، يتم اكتشاف المهارات وتفعيلها تلقائيًا. التحميل التدريجي مهم فقط عند استخدام الواجهة البرمجية.
|
||||
|
||||
---
|
||||
|
||||
## المهارات مقابل المعرفة
|
||||
|
||||
كلا المهارات والمعرفة تُعدّل إرشادات الوكيل، لكنهما يخدمان أغراضًا مختلفة:
|
||||
|
||||
| الجانب | المهارات | المعرفة |
|
||||
| :--- | :--- | :--- |
|
||||
| **ما توفره** | تعليمات، إجراءات، إرشادات | حقائق، بيانات، معلومات |
|
||||
| **كيف تُخزّن** | ملفات Markdown (SKILL.md) | مُضمّنة في مخزن متجهي (ChromaDB) |
|
||||
| **كيف تُسترجع** | يتم حقن المحتوى الكامل في الإرشادات | البحث الدلالي يجد الأجزاء ذات الصلة |
|
||||
| **الأفضل لـ** | المنهجيات، قوائم التحقق، أدلة الأسلوب | مستندات الشركة، معلومات المنتج، بيانات مرجعية |
|
||||
| **يُعيّن عبر** | `skills=["./skills"]` | `knowledge_sources=[source]` |
|
||||
|
||||
**القاعدة العامة:** إذا كان الوكيل يحتاج لاتباع *عملية*، استخدم مهارة. إذا كان يحتاج للرجوع إلى *بيانات*، استخدم المعرفة.
|
||||
|
||||
---
|
||||
|
||||
## الأسئلة الشائعة
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل أحتاج لتعيين المهارات والأدوات معًا؟">
|
||||
يعتمد على حالة الاستخدام. المهارات والأدوات **مستقلتان** — يمكنك استخدام أيّ منهما أو كليهما أو لا شيء.
|
||||
|
||||
- **مهارات فقط**: عندما يحتاج الوكيل خبرة لكن لا يحتاج إجراءات خارجية (مثال: الكتابة بإرشادات أسلوبية)
|
||||
- **أدوات فقط**: عندما يحتاج الوكيل إجراءات لكن لا يحتاج منهجية خاصة (مثال: بحث بسيط على الويب)
|
||||
- **كليهما**: عندما يحتاج الوكيل خبرة وإجراءات (مثال: تدقيق أمني بقوائم تحقق محددة وقدرة على فحص الكود)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل توفر المهارات أدوات تلقائيًا؟">
|
||||
**لا.** حقل `allowed-tools` في SKILL.md هو بيانات وصفية تجريبية فقط — لا يُنشئ أو يحقن أي أدوات. يجب عليك دائمًا تعيين الأدوات بشكل منفصل عبر `tools=[]` أو `mcps=[]` أو `apps=[]`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ماذا يحدث إذا عيّنت نفس المهارة على كل من الوكيل والطاقم؟">
|
||||
المهارة على مستوى الوكيل لها الأولوية. يتم إزالة التكرار حسب الاسم — مهارات الوكيل تُعالج أولاً، لذا إذا ظهر نفس اسم المهارة في كلا المستويين، تُستخدم نسخة الوكيل.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ما الحجم الأقصى لمحتوى SKILL.md؟">
|
||||
هناك تحذير ناعم عند 50,000 حرف، لكن بدون حد صارم. حافظ على تركيز المهارات وإيجازها للحصول على أفضل النتائج — الحقن الكبيرة في الإرشادات قد تُشتت انتباه الوكيل.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -10,6 +10,10 @@ mode: "wide"
|
||||
تُمكّن أدوات CrewAI الوكلاء بقدرات تتراوح من البحث على الويب وتحليل البيانات إلى التعاون وتفويض المهام بين الزملاء.
|
||||
توضح هذه الوثائق كيفية إنشاء هذه الأدوات ودمجها والاستفادة منها ضمن إطار عمل CrewAI، بما في ذلك التركيز على أدوات التعاون.
|
||||
|
||||
<Note type="info" title="الأدوات هي أحد أنواع قدرات الوكيل الخمسة">
|
||||
الأدوات تمنح الوكلاء **دوال قابلة للاستدعاء** لاتخاذ إجراءات. تعمل جنبًا إلى جنب مع [MCP](/ar/mcp/overview) (خوادم أدوات عن بُعد) و[التطبيقات](/ar/concepts/agent-capabilities) (تكاملات المنصة) و[المهارات](/ar/concepts/skills) (خبرة المجال) و[المعرفة](/ar/concepts/knowledge) (حقائق مُسترجعة). راجع نظرة عامة على [قدرات الوكيل](/ar/concepts/agent-capabilities) لفهم متى تستخدم كل نوع.
|
||||
</Note>
|
||||
|
||||
## ما هي الأداة؟
|
||||
|
||||
الأداة في CrewAI هي مهارة أو وظيفة يمكن للوكلاء استخدامها لأداء إجراءات مختلفة.
|
||||
|
||||
@@ -7,11 +7,13 @@ mode: "wide"
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يتيح RBAC في CrewAI AMP إدارة وصول آمنة وقابلة للتوسع من خلال مزيج من الأدوار على مستوى المؤسسة وعناصر التحكم في الرؤية على مستوى الأتمتة.
|
||||
يتيح RBAC في CrewAI AMP إدارة وصول آمنة وقابلة للتوسع من خلال طبقتين:
|
||||
|
||||
1. **صلاحيات الميزات** — تتحكم في ما يمكن لكل دور القيام به عبر المنصة (إدارة، قراءة، أو بدون وصول)
|
||||
2. **صلاحيات على مستوى الكيان** — وصول دقيق للأتمتات الفردية ومتغيرات البيئة واتصالات LLM ومستودعات Git
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/users_and_roles.png" alt="نظرة عامة على RBAC في CrewAI AMP" />
|
||||
|
||||
</Frame>
|
||||
|
||||
## المستخدمون والأدوار
|
||||
@@ -39,6 +41,13 @@ mode: "wide"
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### الأدوار المحددة مسبقاً
|
||||
|
||||
| الدور | الوصف |
|
||||
| :---------- | :-------------------------------------------------------------------- |
|
||||
| **Owner** | وصول كامل لجميع الميزات والإعدادات. لا يمكن تقييده. |
|
||||
| **Member** | وصول للقراءة لمعظم الميزات، وصول إدارة لمتغيرات البيئة واتصالات LLM ومشاريع Studio. لا يمكنه تعديل إعدادات المؤسسة أو الإعدادات الافتراضية. |
|
||||
|
||||
### ملخص التهيئة
|
||||
|
||||
| المجال | مكان التهيئة | الخيارات |
|
||||
@@ -46,23 +55,80 @@ mode: "wide"
|
||||
| المستخدمون والأدوار | Settings → Roles | محددة مسبقاً: Owner، Member؛ أدوار مخصصة |
|
||||
| رؤية الأتمتة | Automation → Settings → Visibility | خاص؛ قائمة بيضاء للمستخدمين/الأدوار |
|
||||
|
||||
## التحكم في الوصول على مستوى الأتمتة
|
||||
---
|
||||
|
||||
بالإضافة إلى الأدوار على مستوى المؤسسة، تدعم أتمتات CrewAI إعدادات رؤية دقيقة تتيح لك تقييد الوصول إلى أتمتات محددة حسب المستخدم أو الدور.
|
||||
## مصفوفة صلاحيات الميزات
|
||||
|
||||
هذا مفيد لـ:
|
||||
لكل دور مستوى صلاحية لكل منطقة ميزة. المستويات الثلاثة هي:
|
||||
|
||||
- **إدارة (Manage)** — وصول كامل للقراءة/الكتابة (إنشاء، تعديل، حذف)
|
||||
- **قراءة (Read)** — وصول للعرض فقط
|
||||
- **بدون وصول (No access)** — الميزة مخفية/غير قابلة للوصول
|
||||
|
||||
| الميزة | Owner | Member (افتراضي) | المستويات المتاحة | الوصف |
|
||||
| :------------------------ | :------ | :--------------- | :--------------------------------- | :-------------------------------------------------------------- |
|
||||
| `usage_dashboards` | Manage | Read | Manage / Read / No access | عرض مقاييس وتحليلات الاستخدام |
|
||||
| `crews_dashboards` | Manage | Read | Manage / Read / No access | عرض لوحات النشر والوصول إلى تفاصيل الأتمتة |
|
||||
| `invitations` | Manage | Read | Manage / Read / No access | دعوة أعضاء جدد إلى المؤسسة |
|
||||
| `training_ui` | Manage | Read | Manage / Read / No access | الوصول إلى واجهات التدريب/الضبط الدقيق |
|
||||
| `tools` | Manage | Read | Manage / Read / No access | إنشاء وإدارة الأدوات |
|
||||
| `agents` | Manage | Read | Manage / Read / No access | إنشاء وإدارة الوكلاء |
|
||||
| `environment_variables` | Manage | Manage | Manage / No access | إنشاء وإدارة متغيرات البيئة |
|
||||
| `llm_connections` | Manage | Manage | Manage / No access | تهيئة اتصالات مزودي LLM |
|
||||
| `default_settings` | Manage | No access | Manage / No access | تعديل الإعدادات الافتراضية على مستوى المؤسسة |
|
||||
| `organization_settings` | Manage | No access | Manage / No access | إدارة الفوترة والخطط وتهيئة المؤسسة |
|
||||
| `studio_projects` | Manage | Manage | Manage / No access | إنشاء وتعديل المشاريع في Studio |
|
||||
|
||||
<Tip>
|
||||
عند إنشاء دور مخصص، يمكن ضبط معظم الميزات على **Manage** أو **Read** أو **No access**. ومع ذلك، فإن `environment_variables` و`llm_connections` و`default_settings` و`organization_settings` و`studio_projects` تدعم فقط **Manage** أو **No access** — لا يوجد خيار للقراءة فقط لهذه الميزات.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## النشر من GitHub أو Zip
|
||||
|
||||
من أكثر أسئلة RBAC شيوعاً: _"ما الصلاحيات التي يحتاجها عضو الفريق للنشر؟"_
|
||||
|
||||
### النشر من GitHub
|
||||
|
||||
لنشر أتمتة من مستودع GitHub، يحتاج المستخدم إلى:
|
||||
|
||||
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات حيث يتم إنشاء عمليات النشر
|
||||
2. **الوصول إلى مستودع Git** (إذا كان RBAC على مستوى الكيان لمستودعات Git مفعلاً): يجب منح دور المستخدم الوصول إلى مستودع Git المحدد عبر صلاحيات مستوى الكيان
|
||||
3. **`studio_projects`: `Manage`** — إذا كان يبني الطاقم في Studio قبل النشر
|
||||
|
||||
### النشر من Zip
|
||||
|
||||
لنشر أتمتة من ملف Zip، يحتاج المستخدم إلى:
|
||||
|
||||
1. **`crews_dashboards`**: على الأقل `Read` — مطلوب للوصول إلى لوحة الأتمتات
|
||||
2. **تفعيل نشر Zip**: يجب ألا تكون المؤسسة قد عطلت نشر Zip في إعدادات المؤسسة
|
||||
|
||||
### مرجع سريع: الحد الأدنى من الصلاحيات للنشر
|
||||
|
||||
| الإجراء | صلاحيات الميزات المطلوبة | متطلبات إضافية |
|
||||
| :------------------- | :----------------------------------- | :----------------------------------------------- |
|
||||
| النشر من GitHub | `crews_dashboards: Read` | وصول كيان مستودع Git (إذا كان Git RBAC مفعلاً) |
|
||||
| النشر من Zip | `crews_dashboards: Read` | يجب تفعيل نشر Zip على مستوى المؤسسة |
|
||||
| البناء في Studio | `studio_projects: Manage` | — |
|
||||
| تهيئة مفاتيح LLM | `llm_connections: Manage` | — |
|
||||
| ضبط متغيرات البيئة | `environment_variables: Manage` | وصول مستوى الكيان (إذا كان RBAC الكيان مفعلاً) |
|
||||
|
||||
---
|
||||
|
||||
## التحكم في الوصول على مستوى الأتمتة (صلاحيات الكيان)
|
||||
|
||||
بالإضافة إلى الأدوار على مستوى المؤسسة، يدعم CrewAI صلاحيات دقيقة على مستوى الكيان تقيد الوصول إلى موارد فردية.
|
||||
|
||||
### رؤية الأتمتة
|
||||
|
||||
تدعم الأتمتات إعدادات رؤية تقيد الوصول حسب المستخدم أو الدور. هذا مفيد لـ:
|
||||
|
||||
- الحفاظ على خصوصية الأتمتات الحساسة أو التجريبية
|
||||
- إدارة الرؤية عبر الفرق الكبيرة أو المتعاونين الخارجيين
|
||||
- اختبار الأتمتات في سياقات معزولة
|
||||
|
||||
يمكن تهيئة عمليات النشر كخاصة، مما يعني أن المستخدمين والأدوار المدرجين في القائمة البيضاء فقط سيتمكنون من:
|
||||
|
||||
- عرض عملية النشر
|
||||
- تشغيلها أو التفاعل مع API الخاص بها
|
||||
- الوصول إلى سجلاتها ومقاييسها وإعداداتها
|
||||
|
||||
يتمتع مالك المؤسسة دائماً بالوصول، بغض النظر عن إعدادات الرؤية.
|
||||
يمكن تهيئة عمليات النشر كخاصة، مما يعني أن المستخدمين والأدوار المدرجين في القائمة البيضاء فقط سيتمكنون من التفاعل معها.
|
||||
|
||||
يمكنك تهيئة التحكم في الوصول على مستوى الأتمتة في Automation → Settings → علامة تبويب Visibility.
|
||||
|
||||
@@ -99,9 +165,92 @@ mode: "wide"
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/visibility.png" alt="إعدادات رؤية الأتمتة في CrewAI AMP" />
|
||||
|
||||
</Frame>
|
||||
|
||||
### أنواع صلاحيات النشر
|
||||
|
||||
عند منح وصول على مستوى الكيان لأتمتة محددة، يمكنك تعيين أنواع الصلاحيات التالية:
|
||||
|
||||
| الصلاحية | ما تسمح به |
|
||||
| :------------------- | :-------------------------------------------------- |
|
||||
| `run` | تنفيذ الأتمتة واستخدام API الخاص بها |
|
||||
| `traces` | عرض تتبعات التنفيذ والسجلات |
|
||||
| `manage_settings` | تعديل، إعادة نشر، استرجاع، أو حذف الأتمتة |
|
||||
| `human_in_the_loop` | الرد على طلبات الإنسان في الحلقة (HITL) |
|
||||
| `full_access` | جميع ما سبق |
|
||||
|
||||
### RBAC على مستوى الكيان لموارد أخرى
|
||||
|
||||
عند تفعيل RBAC على مستوى الكيان، يمكن أيضاً التحكم في الوصول لهذه الموارد حسب المستخدم أو الدور:
|
||||
|
||||
| المورد | يتم التحكم فيه بواسطة | الوصف |
|
||||
| :-------------------- | :--------------------------------- | :------------------------------------------------------------- |
|
||||
| متغيرات البيئة | علامة ميزة RBAC الكيان | تقييد أي الأدوار/المستخدمين يمكنهم عرض أو إدارة متغيرات بيئة محددة |
|
||||
| اتصالات LLM | علامة ميزة RBAC الكيان | تقييد الوصول لتهيئات مزودي LLM محددة |
|
||||
| مستودعات Git | إعداد RBAC لمستودعات Git بالمؤسسة | تقييد أي الأدوار/المستخدمين يمكنهم الوصول لمستودعات متصلة محددة |
|
||||
|
||||
---
|
||||
|
||||
## أنماط الأدوار الشائعة
|
||||
|
||||
بينما يأتي CrewAI بدوري Owner وMember، تستفيد معظم الفرق من إنشاء أدوار مخصصة. إليك الأنماط الشائعة:
|
||||
|
||||
### دور المطور
|
||||
|
||||
دور لأعضاء الفريق الذين يبنون وينشرون الأتمتات لكن لا يديرون إعدادات المؤسسة.
|
||||
|
||||
| الميزة | الصلاحية |
|
||||
| :------------------------ | :---------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Read |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Manage |
|
||||
| `agents` | Manage |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | Manage |
|
||||
|
||||
### دور المشاهد / أصحاب المصلحة
|
||||
|
||||
دور للمعنيين غير التقنيين الذين يحتاجون لمراقبة الأتمتات وعرض النتائج.
|
||||
|
||||
| الميزة | الصلاحية |
|
||||
| :------------------------ | :---------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Read |
|
||||
| `invitations` | No access |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | No access |
|
||||
| `llm_connections` | No access |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
### دور مسؤول العمليات / المنصة
|
||||
|
||||
دور لمشغلي المنصة الذين يديرون إعدادات البنية التحتية لكن قد لا يبنون الوكلاء.
|
||||
|
||||
| الميزة | الصلاحية |
|
||||
| :------------------------ | :---------- |
|
||||
| `usage_dashboards` | Manage |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Manage |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | Manage |
|
||||
| `organization_settings` | Read |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
---
|
||||
|
||||
<Card title="تحتاج مساعدة؟" icon="headset" href="mailto:support@crewai.com">
|
||||
تواصل مع فريق الدعم للمساعدة في أسئلة RBAC.
|
||||
</Card>
|
||||
|
||||
@@ -106,7 +106,7 @@ mode: "wide"
|
||||
```
|
||||
|
||||
<Tip>
|
||||
يستغرق النشر الأول عادة 10-15 دقيقة لبناء صور الحاويات. عمليات النشر اللاحقة أسرع بكثير.
|
||||
يستغرق النشر الأول عادة حوالي دقيقة واحدة.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
@@ -188,7 +188,7 @@ crewai deploy remove <deployment_id>
|
||||
|
||||
1. انقر على زر "Deploy" لبدء عملية النشر
|
||||
2. يمكنك مراقبة التقدم عبر شريط التقدم
|
||||
3. يستغرق النشر الأول عادة حوالي 10-15 دقيقة؛ عمليات النشر اللاحقة ستكون أسرع
|
||||
3. يستغرق النشر الأول عادة حوالي دقيقة واحدة
|
||||
|
||||
<Frame>
|
||||

|
||||
|
||||
132
docs/ar/enterprise/guides/training-crews.mdx
Normal file
132
docs/ar/enterprise/guides/training-crews.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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>
|
||||
@@ -5,6 +5,14 @@ icon: wrench
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
### شاهد: بناء Agents و Flows في CrewAI باستخدام Coding Agent Skills
|
||||
|
||||
قم بتثبيت مهارات وكيل البرمجة الخاصة بنا (Claude Code، Codex، ...) لتشغيل وكلاء البرمجة بسرعة مع CrewAI.
|
||||
|
||||
يمكنك تثبيتها باستخدام `npx skills add crewaiinc/skills`
|
||||
|
||||
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
|
||||
|
||||
## فيديو تعليمي
|
||||
|
||||
شاهد هذا الفيديو التعليمي لعرض تفصيلي لعملية التثبيت:
|
||||
@@ -196,8 +204,8 @@ python3 --version
|
||||
## الخطوات التالية
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="ابنِ أول Agent لك" icon="code" href="/ar/quickstart">
|
||||
اتبع دليل البداية السريعة لإنشاء أول Agent في CrewAI والحصول على تجربة عملية.
|
||||
<Card title="بدء سريع: Flow + وكيل" icon="code" href="/ar/quickstart">
|
||||
اتبع البداية السريعة لإنشاء Flow وتشغيل طاقم بوكيل واحد وإنتاج تقرير.
|
||||
</Card>
|
||||
<Card
|
||||
title="انضم إلى المجتمع"
|
||||
|
||||
@@ -16,6 +16,14 @@ mode: "wide"
|
||||
|
||||
مع أكثر من 100,000 مطور معتمد عبر دوراتنا المجتمعية، يُعد CrewAI المعيار لأتمتة الذكاء الاصطناعي الجاهزة للمؤسسات.
|
||||
|
||||
### شاهد: بناء Agents و Flows في CrewAI باستخدام Coding Agent Skills
|
||||
|
||||
قم بتثبيت مهارات وكيل البرمجة الخاصة بنا (Claude Code، Codex، ...) لتشغيل وكلاء البرمجة بسرعة مع CrewAI.
|
||||
|
||||
يمكنك تثبيتها باستخدام `npx skills add crewaiinc/skills`
|
||||
|
||||
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
|
||||
|
||||
## بنية CrewAI المعمارية
|
||||
|
||||
صُممت بنية CrewAI لتحقيق التوازن بين الاستقلالية والتحكم.
|
||||
@@ -130,9 +138,9 @@ mode: "wide"
|
||||
<Card
|
||||
title="البداية السريعة"
|
||||
icon="bolt"
|
||||
href="en/quickstart"
|
||||
href="/ar/quickstart"
|
||||
>
|
||||
اتبع دليل البداية السريعة لإنشاء أول Agent في CrewAI والحصول على تجربة عملية.
|
||||
أنشئ Flow وشغّل طاقمًا بوكيل واحد وأنشئ تقريرًا من البداية للنهاية.
|
||||
</Card>
|
||||
<Card
|
||||
title="انضم إلى المجتمع"
|
||||
|
||||
@@ -1,380 +1,278 @@
|
||||
---
|
||||
title: البدء السريع
|
||||
description: ابنِ أول وكيل ذكاء اصطناعي مع CrewAI في أقل من 5 دقائق.
|
||||
description: ابنِ أول Flow في CrewAI خلال دقائق — التنسيق والحالة وفريقًا بوكيل واحد ينتج تقريرًا فعليًا.
|
||||
icon: rocket
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## ابنِ أول وكيل CrewAI
|
||||
### شاهد: بناء Agents و Flows في CrewAI باستخدام Coding Agent Skills
|
||||
|
||||
لننشئ طاقماً بسيطاً يساعدنا في `البحث` و`إعداد التقارير` عن `أحدث تطورات الذكاء الاصطناعي` لموضوع أو مجال معين.
|
||||
قم بتثبيت مهارات وكيل البرمجة الخاصة بنا (Claude Code، Codex، ...) لتشغيل وكلاء البرمجة بسرعة مع CrewAI.
|
||||
|
||||
قبل المتابعة، تأكد من إنهاء تثبيت CrewAI.
|
||||
إذا لم تكن قد ثبّتها بعد، يمكنك القيام بذلك باتباع [دليل التثبيت](/ar/installation).
|
||||
يمكنك تثبيتها باستخدام `npx skills add crewaiinc/skills`
|
||||
|
||||
اتبع الخطوات أدناه للبدء!
|
||||
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
|
||||
|
||||
في هذا الدليل ستُنشئ **Flow** يحدد موضوع بحث، ويشغّل **طاقمًا بوكيل واحد** (باحث يستخدم البحث على الويب)، وينتهي بتقرير **Markdown** على القرص. يُعد Flow الطريقة الموصى بها لتنظيم التطبيقات الإنتاجية: يمتلك **الحالة** و**ترتيب التنفيذ**، بينما **الوكلاء** ينفّذون العمل داخل خطوة الطاقم.
|
||||
|
||||
إذا لم تُكمل تثبيت 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="إنشاء طاقمك">
|
||||
أنشئ مشروع طاقم جديد عبر تشغيل الأمر التالي في الطرفية.
|
||||
سينشئ هذا مجلداً جديداً باسم `latest-ai-development` مع البنية الأساسية لطاقمك.
|
||||
<Step title="أنشئ مشروع Flow">
|
||||
من الطرفية، أنشئ مشروع Flow (اسم المجلد يستخدم شرطة سفلية، مثل `latest_ai_flow`):
|
||||
|
||||
<CodeGroup>
|
||||
```shell Terminal
|
||||
crewai create crew latest-ai-development
|
||||
crewai create flow latest-ai-flow
|
||||
cd latest_ai_flow
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
يُنشئ ذلك تطبيق Flow ضمن `src/latest_ai_flow/`، بما في ذلك طاقمًا أوليًا في `crews/content_crew/` ستستبدله بطاقم بحث **بوكيل واحد** في الخطوات التالية.
|
||||
</Step>
|
||||
<Step title="الانتقال إلى مشروع الطاقم الجديد">
|
||||
<CodeGroup>
|
||||
```shell Terminal
|
||||
cd latest_ai_development
|
||||
```
|
||||
</CodeGroup>
|
||||
</Step>
|
||||
<Step title="تعديل ملف `agents.yaml`">
|
||||
<Tip>
|
||||
يمكنك أيضاً تعديل الوكلاء حسب الحاجة ليناسبوا حالة الاستخدام الخاصة بك أو نسخ ولصق كما هو في مشروعك.
|
||||
أي متغير مُستكمل في ملفات `agents.yaml` و`tasks.yaml` مثل `{topic}` سيُستبدل بقيمة المتغير في ملف `main.py`.
|
||||
</Tip>
|
||||
|
||||
<Step title="اضبط وكيلًا واحدًا في `agents.yaml`">
|
||||
استبدل محتوى `src/latest_ai_flow/crews/content_crew/config/agents.yaml` بباحث واحد. تُملأ المتغيرات مثل `{topic}` من `crew.kickoff(inputs=...)`.
|
||||
|
||||
```yaml agents.yaml
|
||||
# src/latest_ai_development/config/agents.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
|
||||
researcher:
|
||||
role: >
|
||||
{topic} Senior Data Researcher
|
||||
باحث بيانات أول في {topic}
|
||||
goal: >
|
||||
Uncover cutting-edge developments in {topic}
|
||||
اكتشاف أحدث التطورات في {topic}
|
||||
backstory: >
|
||||
You're a seasoned researcher with a knack for uncovering the latest
|
||||
developments in {topic}. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.
|
||||
|
||||
reporting_analyst:
|
||||
role: >
|
||||
{topic} Reporting Analyst
|
||||
goal: >
|
||||
Create detailed reports based on {topic} data analysis and research findings
|
||||
backstory: >
|
||||
You're a meticulous analyst with a keen eye for detail. You're known for
|
||||
your ability to turn complex data into clear and concise reports, making
|
||||
it easy for others to understand and act on the information you provide.
|
||||
أنت باحث مخضرم تكشف أحدث المستجدات في {topic}.
|
||||
تجد المعلومات الأكثر صلة وتعرضها بوضوح.
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="تعديل ملف `tasks.yaml`">
|
||||
|
||||
<Step title="اضبط مهمة واحدة في `tasks.yaml`">
|
||||
```yaml tasks.yaml
|
||||
# src/latest_ai_development/config/tasks.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
|
||||
research_task:
|
||||
description: >
|
||||
Conduct a thorough research about {topic}
|
||||
Make sure you find any interesting and relevant information given
|
||||
the current year is 2025.
|
||||
أجرِ بحثًا معمقًا عن {topic}. استخدم البحث على الويب للعثور على معلومات
|
||||
حديثة وموثوقة. السنة الحالية 2026.
|
||||
expected_output: >
|
||||
A list with 10 bullet points of the most relevant information about {topic}
|
||||
تقرير بصيغة Markdown بأقسام واضحة: الاتجاهات الرئيسية، أدوات أو شركات بارزة،
|
||||
والآثار. بين 800 و1200 كلمة تقريبًا. دون إحاطة المستند بأكمله بكتل كود.
|
||||
agent: researcher
|
||||
|
||||
reporting_task:
|
||||
description: >
|
||||
Review the context you got and expand each topic into a full section for a report.
|
||||
Make sure the report is detailed and contains any and all relevant information.
|
||||
expected_output: >
|
||||
A fully fledge reports with the mains topics, each with a full section of information.
|
||||
Formatted as markdown without '```'
|
||||
agent: reporting_analyst
|
||||
output_file: report.md
|
||||
output_file: output/report.md
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="تعديل ملف `crew.py`">
|
||||
```python crew.py
|
||||
# src/latest_ai_development/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai_tools import SerperDevTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
<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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
class ResearchCrew:
|
||||
"""طاقم بحث بوكيل واحد داخل Flow."""
|
||||
|
||||
agents: List[BaseAgent]
|
||||
tasks: List[Task]
|
||||
|
||||
agents_config = "config/agents.yaml"
|
||||
tasks_config = "config/tasks.yaml"
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['researcher'], # type: ignore[index]
|
||||
config=self.agents_config["researcher"], # type: ignore[index]
|
||||
verbose=True,
|
||||
tools=[SerperDevTool()]
|
||||
)
|
||||
|
||||
@agent
|
||||
def reporting_analyst(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['reporting_analyst'], # type: ignore[index]
|
||||
verbose=True
|
||||
tools=[SerperDevTool()],
|
||||
)
|
||||
|
||||
@task
|
||||
def research_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['research_task'], # type: ignore[index]
|
||||
)
|
||||
|
||||
@task
|
||||
def reporting_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['reporting_task'], # type: ignore[index]
|
||||
output_file='output/report.md' # This is the file that will be contain the final report.
|
||||
config=self.tasks_config["research_task"], # type: ignore[index]
|
||||
)
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
"""Creates the LatestAiDevelopment crew"""
|
||||
return Crew(
|
||||
agents=self.agents, # Automatically created by the @agent decorator
|
||||
tasks=self.tasks, # Automatically created by the @task decorator
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
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
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
<Step title="عرّف Flow في `main.py`">
|
||||
اربط الطاقم بـ Flow: خطوة `@start()` تضبط الموضوع في **الحالة**، وخطوة `@listen` تشغّل الطاقم. يظل `output_file` للمهمة يكتب `output/report.md`.
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
|
||||
# ... remaining code
|
||||
```
|
||||
|
||||
</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
|
||||
# src/latest_ai_flow/main.py
|
||||
from pydantic import BaseModel
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run the crew.
|
||||
"""
|
||||
inputs = {
|
||||
'topic': 'AI Agents'
|
||||
}
|
||||
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
|
||||
from crewai.flow import Flow, listen, start
|
||||
|
||||
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()
|
||||
```
|
||||
|
||||
</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>
|
||||
<Tip>
|
||||
إذا كان اسم الحزمة ليس `latest_ai_flow`، عدّل استيراد `ResearchCrew` ليطابق مسار الوحدة في مشروعك.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="البديل المؤسسي: الإنشاء في Crew Studio">
|
||||
لمستخدمي CrewAI AMP، يمكنك إنشاء نفس الطاقم دون كتابة كود:
|
||||
<Step title="متغيرات البيئة">
|
||||
في جذر المشروع، ضبط `.env`:
|
||||
|
||||
1. سجّل الدخول إلى حساب CrewAI AMP (أنشئ حساباً مجانياً على [app.crewai.com](https://app.crewai.com))
|
||||
2. افتح Crew Studio
|
||||
3. اكتب ما هي الأتمتة التي تحاول بناءها
|
||||
4. أنشئ مهامك بصرياً واربطها بالتسلسل
|
||||
5. هيئ مدخلاتك وانقر "تحميل الكود" أو "نشر"
|
||||
|
||||

|
||||
|
||||
<Card title="جرّب CrewAI AMP" icon="rocket" href="https://app.crewai.com">
|
||||
ابدأ حسابك المجاني في CrewAI AMP
|
||||
</Card>
|
||||
- `SERPER_API_KEY` — من [Serper.dev](https://serper.dev/)
|
||||
- مفاتيح مزوّد النموذج حسب الحاجة — راجع [إعداد LLM](/ar/concepts/llms#setting-up-your-llm)
|
||||
</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
|
||||
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
|
||||
# وكلاء الذكاء الاصطناعي في 2026: المشهد والاتجاهات
|
||||
|
||||
## 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>
|
||||
|
||||
سيكون الملف الفعلي أطول ويعكس نتائج بحث مباشرة.
|
||||
</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 (`agents.yaml` و`tasks.yaml`) مع أسماء الدوال في كود Python الخاص بك.
|
||||
على سبيل المثال، يمكنك الإشارة إلى الوكيل لمهام محددة من ملف `tasks.yaml`.
|
||||
يتيح اتساق التسمية هذا لـ CrewAI ربط تكويناتك بكودك تلقائياً؛ وإلا فلن تتعرف مهمتك على المرجع بشكل صحيح.
|
||||
يجب أن تطابق مفاتيح YAML (`researcher`، `research_task`) أسماء الدوال في صف `@CrewBase`. راجع [Crews](/ar/concepts/crews) لنمط الديكورات الكامل.
|
||||
|
||||
#### أمثلة على المراجع
|
||||
## النشر
|
||||
|
||||
<Tip>
|
||||
لاحظ كيف نستخدم نفس الاسم للوكيل في ملف `agents.yaml`
|
||||
(`email_summarizer`) واسم الدالة في ملف `crew.py`
|
||||
(`email_summarizer`).
|
||||
</Tip>
|
||||
ادفع Flow إلى **[CrewAI AMP](https://app.crewai.com)** بعد أن يعمل محليًا ويكون المشروع في مستودع **GitHub**. من جذر المشروع:
|
||||
|
||||
```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
|
||||
<CodeGroup>
|
||||
```bash المصادقة
|
||||
crewai login
|
||||
```
|
||||
|
||||
<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
|
||||
```bash إنشاء نشر
|
||||
crewai deploy create
|
||||
```
|
||||
|
||||
## نشر طاقمك
|
||||
```bash الحالة والسجلات
|
||||
crewai deploy status
|
||||
crewai deploy logs
|
||||
```
|
||||
|
||||
أسهل طريقة لنشر طاقمك في الإنتاج هي من خلال [CrewAI AMP](http://app.crewai.com).
|
||||
```bash إرسال التحديثات بعد تغيير الكود
|
||||
crewai deploy push
|
||||
```
|
||||
|
||||
شاهد هذا الفيديو التعليمي لعرض خطوة بخطوة لنشر طاقمك على [CrewAI AMP](http://app.crewai.com) باستخدام CLI.
|
||||
```bash عرض النشرات أو حذفها
|
||||
crewai deploy list
|
||||
crewai deploy remove <deployment_id>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<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>
|
||||
<Tip>
|
||||
غالبًا ما يستغرق **النشر الأول حوالي دقيقة**. المتطلبات الكاملة ومسار الواجهة الويب في [النشر على AMP](/ar/enterprise/guides/deploy-to-amp).
|
||||
</Tip>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="النشر على المؤسسة" icon="rocket" href="http://app.crewai.com">
|
||||
ابدأ مع CrewAI AMP وانشر طاقمك في بيئة إنتاج
|
||||
بنقرات قليلة فقط.
|
||||
<Card title="دليل النشر" icon="book" href="/ar/enterprise/guides/deploy-to-amp">
|
||||
النشر على AMP خطوة بخطوة (CLI ولوحة التحكم).
|
||||
</Card>
|
||||
<Card
|
||||
title="انضم إلى المجتمع"
|
||||
title="المجتمع"
|
||||
icon="comments"
|
||||
href="https://community.crewai.com"
|
||||
>
|
||||
انضم إلى مجتمعنا مفتوح المصدر لمناقشة الأفكار ومشاركة مشاريعك والتواصل
|
||||
مع مطورين آخرين لـ CrewAI.
|
||||
ناقش الأفكار وشارك مشاريعك وتواصل مع مطوري CrewAI.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
1997
docs/docs.json
1997
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,184 @@ 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 release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Add A2UI extension with v0.8/v0.9 support, schemas, and docs
|
||||
|
||||
### Bug Fixes
|
||||
- Fix multimodal vision prefixes by adding GPT-5 and o-series
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.13.0a6
|
||||
|
||||
## Contributors
|
||||
|
||||
@alex-clawd, @greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Apr 01, 2026">
|
||||
## v1.13.0a6
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Fix RBAC permission levels to match actual UI options (#5210)
|
||||
- Update changelog and version for v1.13.0a5 (#5200)
|
||||
|
||||
### Performance
|
||||
- Reduce framework overhead by implementing a lazy event bus and skipping tracing when disabled (#5187)
|
||||
|
||||
## Contributors
|
||||
|
||||
@alex-clawd, @joaomdmoura, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Mar 31, 2026">
|
||||
## v1.13.0a5
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a5)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.13.0a4
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Apr 01, 2026">
|
||||
## v1.13.0a4
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a4)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.13.0a3
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Apr 01, 2026">
|
||||
## v1.13.0a3
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a3)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Emit token usage data in LLMCallCompletedEvent
|
||||
- Extract and publish tool metadata to AMP
|
||||
|
||||
### Bug Fixes
|
||||
- Handle GPT-5.x models not supporting the `stop` API parameter
|
||||
|
||||
### Documentation
|
||||
- Fix inaccuracies in agent-capabilities across all languages
|
||||
- Add Agent Capabilities overview and improve Skills documentation
|
||||
- Add comprehensive SSO configuration guide
|
||||
- Update changelog and version for v1.13.0rc1
|
||||
|
||||
### Refactoring
|
||||
- Convert Flow to Pydantic BaseModel
|
||||
- Convert LLM classes to Pydantic BaseModel
|
||||
- Replace InstanceOf[T] with plain type annotations
|
||||
- Remove unused methods
|
||||
|
||||
## Contributors
|
||||
|
||||
@dependabot[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide, @thiagomoretto
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Mar 27, 2026">
|
||||
## v1.13.0rc1
|
||||
|
||||
|
||||
147
docs/en/concepts/agent-capabilities.mdx
Normal file
147
docs/en/concepts/agent-capabilities.mdx
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: "Agent Capabilities"
|
||||
description: "Understand the five ways to extend CrewAI agents: Tools, MCPs, Apps, Skills, and Knowledge."
|
||||
icon: puzzle-piece
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
CrewAI agents can be extended with **five distinct capability types**, each serving a different purpose. Understanding when to use each one — and how they work together — is key to building effective agents.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tools" icon="wrench" href="/en/concepts/tools" color="#3B82F6">
|
||||
**Callable functions** — give agents the ability to take action. Web searches, file operations, API calls, code execution.
|
||||
</Card>
|
||||
<Card title="MCP Servers" icon="plug" href="/en/mcp/overview" color="#8B5CF6">
|
||||
**Remote tool servers** — connect agents to external tool servers via the Model Context Protocol. Same effect as tools, but hosted externally.
|
||||
</Card>
|
||||
<Card title="Apps" icon="grid-2" color="#EC4899">
|
||||
**Platform integrations** — connect agents to SaaS apps (Gmail, Slack, Jira, Salesforce) via CrewAI's platform. Runs locally with a platform integration token.
|
||||
</Card>
|
||||
<Card title="Skills" icon="bolt" href="/en/concepts/skills" color="#F59E0B">
|
||||
**Domain expertise** — inject instructions, guidelines, and reference material into agent prompts. Skills tell agents *how to think*.
|
||||
</Card>
|
||||
<Card title="Knowledge" icon="book" href="/en/concepts/knowledge" color="#10B981">
|
||||
**Retrieved facts** — provide agents with data from documents, files, and URLs via semantic search (RAG). Knowledge gives agents *what to know*.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## The Key Distinction
|
||||
|
||||
The most important thing to understand: **these capabilities fall into two categories**.
|
||||
|
||||
### Action Capabilities (Tools, MCPs, Apps)
|
||||
|
||||
These give agents the ability to **do things** — call APIs, read files, search the web, send emails. At execution time, all three resolve into the same internal format (`BaseTool` instances) and appear in a unified tool list the agent can call.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find and compile market data",
|
||||
backstory="Expert market analyst",
|
||||
tools=[SerperDevTool(), FileReadTool()], # Local tools
|
||||
mcps=["https://mcp.example.com/sse"], # Remote MCP server tools
|
||||
apps=["gmail", "google_sheets"], # Platform integrations
|
||||
)
|
||||
```
|
||||
|
||||
### Context Capabilities (Skills, Knowledge)
|
||||
|
||||
These modify the agent's **prompt** — injecting expertise, instructions, or retrieved data before the agent starts reasoning. They don't give agents new actions; they shape how agents think and what information they have access to.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Security Auditor",
|
||||
goal="Audit cloud infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security with 10 years of experience",
|
||||
skills=["./skills/security-audit"], # Domain instructions
|
||||
knowledge_sources=[pdf_source, url_source], # Retrieved facts
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use What
|
||||
|
||||
| You need... | Use | Example |
|
||||
| :------------------------------------------------ | :---------------- | :--------------------------------------- |
|
||||
| Agent to search the web | **Tools** | `tools=[SerperDevTool()]` |
|
||||
| Agent to call a remote API via MCP | **MCPs** | `mcps=["https://api.example.com/sse"]` |
|
||||
| Agent to send emails via Gmail | **Apps** | `apps=["gmail"]` |
|
||||
| Agent to follow specific procedures | **Skills** | `skills=["./skills/code-review"]` |
|
||||
| Agent to reference company docs | **Knowledge** | `knowledge_sources=[pdf_source]` |
|
||||
| Agent to search the web AND follow review guidelines | **Tools + Skills** | Use both together |
|
||||
|
||||
---
|
||||
|
||||
## Combining Capabilities
|
||||
|
||||
In practice, agents often use **multiple capability types together**. Here's a realistic example:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
# A fully-equipped research agent
|
||||
researcher = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Produce comprehensive market analysis reports",
|
||||
backstory="Expert analyst with deep industry knowledge",
|
||||
|
||||
# ACTION: What the agent can DO
|
||||
tools=[
|
||||
SerperDevTool(), # Search the web
|
||||
FileReadTool(), # Read local files
|
||||
CodeInterpreterTool(), # Run Python code for analysis
|
||||
],
|
||||
mcps=["https://data-api.example.com/sse"], # Access remote data API
|
||||
apps=["google_sheets"], # Write to Google Sheets
|
||||
|
||||
# CONTEXT: What the agent KNOWS
|
||||
skills=["./skills/research-methodology"], # How to conduct research
|
||||
knowledge_sources=[company_docs], # Company-specific data
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comparison Table
|
||||
|
||||
| Feature | Tools | MCPs | Apps | Skills | Knowledge |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: |
|
||||
| **Gives agent actions** | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| **Modifies prompt** | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| **Requires code** | Yes | Config only | Config only | Markdown only | Config only |
|
||||
| **Runs locally** | Yes | Depends | Yes (with env var) | N/A | Yes |
|
||||
| **Needs API keys** | Per tool | Per server | Integration token | No | Embedder only |
|
||||
| **Set on Agent** | `tools=[]` | `mcps=[]` | `apps=[]` | `skills=[]` | `knowledge_sources=[]` |
|
||||
| **Set on Crew** | ❌ | ❌ | ❌ | `skills=[]` | `knowledge_sources=[]` |
|
||||
|
||||
---
|
||||
|
||||
## Deep Dives
|
||||
|
||||
Ready to learn more about each capability type?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tools" icon="wrench" href="/en/concepts/tools">
|
||||
Create custom tools, use the 75+ OSS catalog, configure caching and async execution.
|
||||
</Card>
|
||||
<Card title="MCP Integration" icon="plug" href="/en/mcp/overview">
|
||||
Connect to MCP servers via stdio, SSE, or HTTP. Filter tools, configure auth.
|
||||
</Card>
|
||||
<Card title="Skills" icon="bolt" href="/en/concepts/skills">
|
||||
Build skill packages with SKILL.md, inject domain expertise, use progressive disclosure.
|
||||
</Card>
|
||||
<Card title="Knowledge" icon="book" href="/en/concepts/knowledge">
|
||||
Add knowledge from PDFs, CSVs, URLs, and more. Configure embedders and retrieval.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
236
docs/en/concepts/checkpointing.mdx
Normal file
236
docs/en/concepts/checkpointing.mdx
Normal file
@@ -0,0 +1,236 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,27 +1,186 @@
|
||||
---
|
||||
title: Skills
|
||||
description: Filesystem-based skill packages that inject context into agent prompts.
|
||||
description: Filesystem-based skill packages that inject domain expertise and instructions into agent prompts.
|
||||
icon: bolt
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Skills are self-contained directories that provide agents with domain-specific instructions, references, and assets. Each skill is defined by a `SKILL.md` file with YAML frontmatter and a markdown body.
|
||||
Skills are self-contained directories that provide agents with **domain-specific instructions, guidelines, and reference material**. Each skill is defined by a `SKILL.md` file with YAML frontmatter and a markdown body.
|
||||
|
||||
Skills use **progressive disclosure** — metadata is loaded first, full instructions only when activated, and resource catalogs only when needed.
|
||||
When activated, a skill's instructions are injected directly into the agent's task prompt — giving the agent expertise without requiring any code changes.
|
||||
|
||||
## Directory Structure
|
||||
<Note type="info" title="Skills vs Tools — The Key Distinction">
|
||||
**Skills are NOT tools.** This is the most common point of confusion.
|
||||
|
||||
- **Skills** inject *instructions and context* into the agent's prompt. They tell the agent *how to think* about a problem.
|
||||
- **Tools** give the agent *callable functions* to take action (search, read files, call APIs).
|
||||
|
||||
You often need **both**: skills for expertise, tools for action. They are configured independently and complement each other.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create a Skill Directory
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # Required — frontmatter + instructions
|
||||
├── scripts/ # Optional — executable scripts
|
||||
├── references/ # Optional — reference documents
|
||||
└── assets/ # Optional — static files (configs, data)
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # Required — instructions
|
||||
├── references/ # Optional — reference docs
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # Optional — executable scripts
|
||||
```
|
||||
|
||||
The directory name must match the `name` field in `SKILL.md`.
|
||||
### 2. Write Your SKILL.md
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Guidelines for conducting thorough code reviews with focus on security and performance.
|
||||
metadata:
|
||||
author: your-team
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
## Code Review Guidelines
|
||||
|
||||
When reviewing code, follow this checklist:
|
||||
|
||||
1. **Security**: Check for injection vulnerabilities, auth bypasses, and data exposure
|
||||
2. **Performance**: Look for N+1 queries, unnecessary allocations, and blocking calls
|
||||
3. **Readability**: Ensure clear naming, appropriate comments, and consistent style
|
||||
4. **Testing**: Verify adequate test coverage for new functionality
|
||||
|
||||
### Severity Levels
|
||||
- **Critical**: Security vulnerabilities, data loss risks → block merge
|
||||
- **Major**: Performance issues, logic errors → request changes
|
||||
- **Minor**: Style issues, naming suggestions → approve with comments
|
||||
```
|
||||
|
||||
### 3. Attach to an Agent
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import GithubSearchTool, FileReadTool
|
||||
|
||||
reviewer = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["./skills"], # Injects review guidelines
|
||||
tools=[GithubSearchTool(), FileReadTool()], # Lets agent read code
|
||||
)
|
||||
```
|
||||
|
||||
The agent now has both **expertise** (from the skill) and **capabilities** (from the tools).
|
||||
|
||||
---
|
||||
|
||||
## Skills + Tools: Working Together
|
||||
|
||||
Here are common patterns showing how skills and tools complement each other:
|
||||
|
||||
### Pattern 1: Skills Only (Domain Expertise, No Actions Needed)
|
||||
|
||||
Use when the agent needs specific instructions but doesn't need to call external services:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Technical Writer",
|
||||
goal="Write clear API documentation",
|
||||
backstory="Expert technical writer",
|
||||
skills=["./skills/api-docs-style"], # Writing guidelines and templates
|
||||
# No tools needed — agent writes based on provided context
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 2: Tools Only (Actions, No Special Expertise)
|
||||
|
||||
Use when the agent needs to take action but doesn't need domain-specific instructions:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
|
||||
|
||||
agent = Agent(
|
||||
role="Web Researcher",
|
||||
goal="Find information about a topic",
|
||||
backstory="Skilled at finding information online",
|
||||
tools=[SerperDevTool(), ScrapeWebsiteTool()], # Can search and scrape
|
||||
# No skills needed — general research doesn't need special guidelines
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 3: Skills + Tools (Expertise AND Actions)
|
||||
|
||||
The most common real-world pattern. The skill provides *how* to approach the work; tools provide *what* the agent can do:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
analyst = Agent(
|
||||
role="Security Analyst",
|
||||
goal="Audit infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security and compliance",
|
||||
skills=["./skills/security-audit"], # Audit methodology and checklists
|
||||
tools=[
|
||||
SerperDevTool(), # Research known vulnerabilities
|
||||
FileReadTool(), # Read config files
|
||||
CodeInterpreterTool(), # Run analysis scripts
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 4: Skills + MCPs
|
||||
|
||||
Skills work alongside MCP servers the same way they work with tools:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze customer data and generate reports",
|
||||
backstory="Expert data analyst with strong statistical background",
|
||||
skills=["./skills/data-analysis"], # Analysis methodology
|
||||
mcps=["https://data-warehouse.example.com/sse"], # Remote data access
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 5: Skills + Apps
|
||||
|
||||
Skills can guide how an agent uses platform integrations:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Customer Support Agent",
|
||||
goal="Respond to customer inquiries professionally",
|
||||
backstory="Experienced support representative",
|
||||
skills=["./skills/support-playbook"], # Response templates and escalation rules
|
||||
apps=["gmail", "zendesk"], # Can send emails and update tickets
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Crew-Level Skills
|
||||
|
||||
Skills can be set on a crew to apply to **all agents**:
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
|
||||
crew = Crew(
|
||||
agents=[researcher, writer, reviewer],
|
||||
tasks=[research_task, write_task, review_task],
|
||||
skills=["./skills"], # All agents get these skills
|
||||
)
|
||||
```
|
||||
|
||||
Agent-level skills take priority — if the same skill is discovered at both levels, the agent's version is used.
|
||||
|
||||
---
|
||||
|
||||
## SKILL.md Format
|
||||
|
||||
@@ -34,7 +193,7 @@ compatibility: crewai>=0.1.0 # optional
|
||||
metadata: # optional
|
||||
author: your-name
|
||||
version: "1.0"
|
||||
allowed-tools: web-search file-read # optional, space-delimited
|
||||
allowed-tools: web-search file-read # optional, experimental
|
||||
---
|
||||
|
||||
Instructions for the agent go here. This markdown body is injected
|
||||
@@ -43,57 +202,46 @@ into the agent's prompt when the skill is activated.
|
||||
|
||||
### Frontmatter Fields
|
||||
|
||||
| Field | Required | Constraints |
|
||||
| Field | Required | Description |
|
||||
| :-------------- | :------- | :----------------------------------------------------------------------- |
|
||||
| `name` | Yes | 1–64 chars. Lowercase alphanumeric and hyphens. No leading/trailing/consecutive hyphens. Must match directory name. |
|
||||
| `name` | Yes | 1–64 chars. Lowercase alphanumeric and hyphens. Must match directory name. |
|
||||
| `description` | Yes | 1–1024 chars. Describes what the skill does and when to use it. |
|
||||
| `license` | No | License name or reference to a bundled license file. |
|
||||
| `compatibility` | No | Max 500 chars. Environment requirements (products, packages, network). |
|
||||
| `metadata` | No | Arbitrary string key-value mapping. |
|
||||
| `allowed-tools` | No | Space-delimited list of pre-approved tools. Experimental. |
|
||||
|
||||
## Usage
|
||||
---
|
||||
|
||||
### Agent-level Skills
|
||||
## Directory Structure
|
||||
|
||||
Pass skill directory paths to an agent:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
backstory="An expert researcher.",
|
||||
skills=["./skills"], # discovers all skills in this directory
|
||||
)
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # Required — frontmatter + instructions
|
||||
├── scripts/ # Optional — executable scripts
|
||||
├── references/ # Optional — reference documents
|
||||
└── assets/ # Optional — static files (configs, data)
|
||||
```
|
||||
|
||||
### Crew-level Skills
|
||||
The directory name must match the `name` field in `SKILL.md`. The `scripts/`, `references/`, and `assets/` directories are available on the skill's `path` for agents that need to reference files directly.
|
||||
|
||||
Skill paths on a crew are merged into every agent:
|
||||
---
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
## Pre-loading Skills
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
skills=["./skills"],
|
||||
)
|
||||
```
|
||||
|
||||
### Pre-loaded Skills
|
||||
|
||||
You can also pass `Skill` objects directly:
|
||||
For more control, you can discover and activate skills programmatically:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from crewai.skills import discover_skills, activate_skill
|
||||
|
||||
# Discover all skills in a directory
|
||||
skills = discover_skills(Path("./skills"))
|
||||
|
||||
# Activate them (loads full SKILL.md body)
|
||||
activated = [activate_skill(s) for s in skills]
|
||||
|
||||
# Pass to an agent
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
@@ -102,14 +250,57 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Skills Are Loaded
|
||||
|
||||
Skills load progressively — only the data needed at each stage is read:
|
||||
Skills use **progressive disclosure** — only loading what's needed at each stage:
|
||||
|
||||
| Stage | What's loaded | When |
|
||||
| :--------------- | :------------------------------------------------ | :----------------- |
|
||||
| Discovery | Name, description, frontmatter fields | `discover_skills()` |
|
||||
| Activation | Full SKILL.md body text | `activate_skill()` |
|
||||
| Stage | What's loaded | When |
|
||||
| :--------- | :------------------------------------ | :------------------ |
|
||||
| Discovery | Name, description, frontmatter fields | `discover_skills()` |
|
||||
| Activation | Full SKILL.md body text | `activate_skill()` |
|
||||
|
||||
During normal agent execution, skills are automatically discovered and activated. The `scripts/`, `references/`, and `assets/` directories are available on the skill's `path` for agents that need to reference files directly.
|
||||
During normal agent execution (passing directory paths via `skills=["./skills"]`), skills are automatically discovered and activated. The progressive loading only matters when using the programmatic API.
|
||||
|
||||
---
|
||||
|
||||
## Skills vs Knowledge
|
||||
|
||||
Both skills and knowledge modify the agent's prompt, but they serve different purposes:
|
||||
|
||||
| Aspect | Skills | Knowledge |
|
||||
| :--- | :--- | :--- |
|
||||
| **What it provides** | Instructions, procedures, guidelines | Facts, data, information |
|
||||
| **How it's stored** | Markdown files (SKILL.md) | Embedded in vector store (ChromaDB) |
|
||||
| **How it's retrieved** | Entire body injected into prompt | Semantic search finds relevant chunks |
|
||||
| **Best for** | Methodology, checklists, style guides | Company docs, product info, reference data |
|
||||
| **Set via** | `skills=["./skills"]` | `knowledge_sources=[source]` |
|
||||
|
||||
**Rule of thumb:** If the agent needs to follow a *process*, use a skill. If the agent needs to reference *data*, use knowledge.
|
||||
|
||||
---
|
||||
|
||||
## Common Questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Do I need to set skills AND tools?">
|
||||
It depends on your use case. Skills and tools are **independent** — you can use either, both, or neither.
|
||||
|
||||
- **Skills alone**: When the agent needs expertise but no external actions (e.g., writing with style guidelines)
|
||||
- **Tools alone**: When the agent needs actions but no special methodology (e.g., simple web search)
|
||||
- **Both**: When the agent needs expertise AND actions (e.g., security audit with specific checklists AND ability to scan code)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do skills automatically provide tools?">
|
||||
**No.** The `allowed-tools` field in SKILL.md is experimental metadata only — it does not provision or inject any tools. You must always set tools separately via `tools=[]`, `mcps=[]`, or `apps=[]`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I set the same skill on both an agent and its crew?">
|
||||
The agent-level skill takes priority. Skills are deduplicated by name — the agent's skills are processed first, so if the same skill name appears at both levels, the agent's version is used.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How large can a SKILL.md body be?">
|
||||
There's a soft warning at 50,000 characters, but no hard limit. Keep skills focused and concise for best results — large prompt injections can dilute the agent's attention.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -10,6 +10,10 @@ mode: "wide"
|
||||
CrewAI tools empower agents with capabilities ranging from web searching and data analysis to collaboration and delegating tasks among coworkers.
|
||||
This documentation outlines how to create, integrate, and leverage these tools within the CrewAI framework, including a new focus on collaboration tools.
|
||||
|
||||
<Note type="info" title="Tools are one of five agent capability types">
|
||||
Tools give agents **callable functions** to take action. They work alongside [MCPs](/en/mcp/overview) (remote tool servers), [Apps](/en/concepts/agent-capabilities) (platform integrations), [Skills](/en/concepts/skills) (domain expertise), and [Knowledge](/en/concepts/knowledge) (retrieved facts). See the [Agent Capabilities](/en/concepts/agent-capabilities) overview to understand when to use each.
|
||||
</Note>
|
||||
|
||||
## What is a Tool?
|
||||
|
||||
A tool in CrewAI is a skill or function that agents can utilize to perform various actions.
|
||||
|
||||
@@ -46,7 +46,7 @@ You can configure users and roles in Settings → Roles.
|
||||
| Role | Description |
|
||||
| :--------- | :-------------------------------------------------------------------------- |
|
||||
| **Owner** | Full access to all features and settings. Cannot be restricted. |
|
||||
| **Member** | Read access to most features, manage access to Studio projects. Cannot modify organization or default settings. |
|
||||
| **Member** | Read access to most features, manage access to environment variables, LLM connections, and Studio projects. Cannot modify organization or default settings. |
|
||||
|
||||
### Configuration summary
|
||||
|
||||
@@ -65,22 +65,22 @@ Every role has a permission level for each feature area. The three levels are:
|
||||
- **Read** — view-only access
|
||||
- **No access** — feature is hidden/inaccessible
|
||||
|
||||
| Feature | Owner | Member (default) | Description |
|
||||
| :------------------------ | :------ | :--------------- | :-------------------------------------------------------------- |
|
||||
| `usage_dashboards` | Manage | Read | View usage metrics and analytics |
|
||||
| `crews_dashboards` | Manage | Read | View deployment dashboards, access automation details |
|
||||
| `invitations` | Manage | Read | Invite new members to the organization |
|
||||
| `training_ui` | Manage | Read | Access training/fine-tuning interfaces |
|
||||
| `tools` | Manage | Read | Create and manage tools |
|
||||
| `agents` | Manage | Read | Create and manage agents |
|
||||
| `environment_variables` | Manage | Read | Create and manage environment variables |
|
||||
| `llm_connections` | Manage | Read | Configure LLM provider connections |
|
||||
| `default_settings` | Manage | No access | Modify organization-wide default settings |
|
||||
| `organization_settings` | Manage | No access | Manage billing, plans, and organization configuration |
|
||||
| `studio_projects` | Manage | Manage | Create and edit projects in Studio |
|
||||
| Feature | Owner | Member (default) | Available levels | Description |
|
||||
| :------------------------ | :------ | :--------------- | :------------------------ | :-------------------------------------------------------------- |
|
||||
| `usage_dashboards` | Manage | Read | Manage / Read / No access | View usage metrics and analytics |
|
||||
| `crews_dashboards` | Manage | Read | Manage / Read / No access | View deployment dashboards, access automation details |
|
||||
| `invitations` | Manage | Read | Manage / Read / No access | Invite new members to the organization |
|
||||
| `training_ui` | Manage | Read | Manage / Read / No access | Access training/fine-tuning interfaces |
|
||||
| `tools` | Manage | Read | Manage / Read / No access | Create and manage tools |
|
||||
| `agents` | Manage | Read | Manage / Read / No access | Create and manage agents |
|
||||
| `environment_variables` | Manage | Manage | Manage / No access | Create and manage environment variables |
|
||||
| `llm_connections` | Manage | Manage | Manage / No access | Configure LLM provider connections |
|
||||
| `default_settings` | Manage | No access | Manage / No access | Modify organization-wide default settings |
|
||||
| `organization_settings` | Manage | No access | Manage / No access | Manage billing, plans, and organization configuration |
|
||||
| `studio_projects` | Manage | Manage | Manage / No access | Create and edit projects in Studio |
|
||||
|
||||
<Tip>
|
||||
When creating a custom role, you can set each feature independently to **Manage**, **Read**, or **No access** to match your team's needs.
|
||||
When creating a custom role, most features can be set to **Manage**, **Read**, or **No access**. However, `environment_variables`, `llm_connections`, `default_settings`, `organization_settings`, and `studio_projects` only support **Manage** or **No access** — there is no read-only option for these features.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
@@ -208,7 +208,7 @@ A role for team members who build and deploy automations but don't manage organi
|
||||
| `tools` | Manage |
|
||||
| `agents` | Manage |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Read |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | Manage |
|
||||
@@ -229,7 +229,7 @@ A role for non-technical stakeholders who need to monitor automations and view r
|
||||
| `llm_connections` | No access |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | Read |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
### Ops / Platform Admin Role
|
||||
|
||||
@@ -247,7 +247,7 @@ A role for platform operators who manage infrastructure settings but may not bui
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | Manage |
|
||||
| `organization_settings` | Read |
|
||||
| `studio_projects` | Read |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ CrewAI Platform supports enterprise Single Sign-On (SSO) across both **SaaS (AMP
|
||||
|
||||
### Supported Providers
|
||||
|
||||
| Provider | SaaS | Factory | Protocol |
|
||||
|---|---|---|---|
|
||||
| **WorkOS** | ✅ (default) | ✅ | OAuth 2.0 / OIDC |
|
||||
| **Microsoft Entra ID** (Azure AD) | ✅ (enterprise) | ✅ | OAuth 2.0 / SAML 2.0 |
|
||||
| **Okta** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC |
|
||||
| **Auth0** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC |
|
||||
| **Keycloak** | — | ✅ | OAuth 2.0 / OIDC |
|
||||
| Provider | SaaS | Factory | Protocol | CLI Support |
|
||||
|---|---|---|---|---|
|
||||
| **WorkOS** | ✅ (default) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Microsoft Entra ID** (Azure AD) | ✅ (enterprise) | ✅ | OAuth 2.0 / SAML 2.0 | ✅ |
|
||||
| **Okta** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Auth0** | ✅ (enterprise) | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
| **Keycloak** | — | ✅ | OAuth 2.0 / OIDC | ✅ |
|
||||
|
||||
### Key Capabilities
|
||||
|
||||
@@ -52,21 +52,270 @@ Enterprise SaaS customers can configure SSO with their own identity provider (En
|
||||
|
||||
---
|
||||
|
||||
## Factory SSO
|
||||
## Factory SSO Setup
|
||||
|
||||
Factory (self-hosted) deployments support SSO with the following identity providers:
|
||||
Factory (self-hosted) deployments require you to configure SSO by setting environment variables in your Helm `values.yaml` and registering an application in your identity provider.
|
||||
|
||||
- **Microsoft Entra ID** (Azure AD)
|
||||
- **Okta**
|
||||
- **Keycloak**
|
||||
- **Auth0**
|
||||
- **WorkOS**
|
||||
### Microsoft Entra ID (Azure AD)
|
||||
|
||||
Each provider requires registering an application in your IdP and configuring the corresponding environment variables in your Helm `values.yaml`.
|
||||
<Steps>
|
||||
<Step title="Register an Application">
|
||||
1. Go to [portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**
|
||||
2. Configure:
|
||||
- **Name:** `CrewAI` (or your preferred name)
|
||||
- **Supported account types:** Accounts in this organizational directory only
|
||||
- **Redirect URI:** Select **Web**, enter `https://<your-domain>/auth/entra_id/callback`
|
||||
3. Click **Register**
|
||||
</Step>
|
||||
|
||||
<Note>
|
||||
Detailed setup guides for each provider — including step-by-step IdP registration, environment variable reference, and CLI enablement — are available in your **Factory admin documentation** (shipped with your Factory installation).
|
||||
</Note>
|
||||
<Step title="Collect Credentials">
|
||||
From the app overview page, copy:
|
||||
- **Application (client) ID** → `ENTRA_ID_CLIENT_ID`
|
||||
- **Directory (tenant) ID** → `ENTRA_ID_TENANT_ID`
|
||||
</Step>
|
||||
|
||||
<Step title="Create Client Secret">
|
||||
1. Navigate to **Certificates & Secrets** → **New client secret**
|
||||
2. Add a description and select expiration period
|
||||
3. Copy the secret value immediately (it won't be shown again) → `ENTRA_ID_CLIENT_SECRET`
|
||||
</Step>
|
||||
|
||||
<Step title="Grant Admin Consent">
|
||||
1. Go to **Enterprise applications** → select your app
|
||||
2. Under **Security** → **Permissions**, click **Grant admin consent**
|
||||
3. Ensure **Microsoft Graph → User.Read** is granted
|
||||
</Step>
|
||||
|
||||
<Step title="Configure App Roles (Recommended)">
|
||||
Under **App registrations** → your app → **App roles**, create:
|
||||
|
||||
| Display Name | Value | Allowed Member Types |
|
||||
|---|---|---|
|
||||
| Member | `member` | Users/Groups |
|
||||
| Factory Admin | `factory-admin` | Users/Groups |
|
||||
|
||||
<Note>
|
||||
The `member` role grants login access. The `factory-admin` role grants admin panel access. Roles are included in the JWT automatically.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Assign Users">
|
||||
1. Under **Properties**, set **Assignment required?** to **Yes**
|
||||
2. Under **Users and groups**, assign users/groups with the appropriate role
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "entra_id"
|
||||
|
||||
secrets:
|
||||
ENTRA_ID_CLIENT_ID: "<Application (client) ID>"
|
||||
ENTRA_ID_CLIENT_SECRET: "<Client Secret>"
|
||||
ENTRA_ID_TENANT_ID: "<Directory (tenant) ID>"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
To allow `crewai login` via Device Authorization Grant:
|
||||
|
||||
1. Under **Authentication** → **Advanced settings**, enable **Allow public client flows**
|
||||
2. Under **Expose an API**, add an Application ID URI (e.g., `api://crewai-cli`)
|
||||
3. Add a scope (e.g., `read`) with **Admins and users** consent
|
||||
4. Under **Manifest**, set `accessTokenAcceptedVersion` to `2`
|
||||
5. Add environment variables:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
ENTRA_ID_DEVICE_AUTHORIZATION_CLIENT_ID: "<Application (client) ID>"
|
||||
ENTRA_ID_CUSTOM_OPENID_SCOPE: "<scope URI, e.g. api://crewai-cli/read>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Okta
|
||||
|
||||
<Steps>
|
||||
<Step title="Create App Integration">
|
||||
1. Open Okta Admin Console → **Applications** → **Create App Integration**
|
||||
2. Select **OIDC - OpenID Connect** → **Web Application** → **Next**
|
||||
3. Configure:
|
||||
- **App integration name:** `CrewAI SSO`
|
||||
- **Sign-in redirect URI:** `https://<your-domain>/auth/okta/callback`
|
||||
- **Sign-out redirect URI:** `https://<your-domain>`
|
||||
- **Assignments:** Choose who can access (everyone or specific groups)
|
||||
4. Click **Save**
|
||||
</Step>
|
||||
|
||||
<Step title="Collect Credentials">
|
||||
From the app details page:
|
||||
- **Client ID** → `OKTA_CLIENT_ID`
|
||||
- **Client Secret** → `OKTA_CLIENT_SECRET`
|
||||
- **Okta URL** (top-right corner, under your username) → `OKTA_SITE`
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Authorization Server">
|
||||
1. Navigate to **Security** → **API**
|
||||
2. Select your authorization server (default: `default`)
|
||||
3. Under **Access Policies**, add a policy and rule:
|
||||
- In the rule, under **Scopes requested**, select **The following scopes** → **OIDC default scopes**
|
||||
4. Note the **Name** and **Audience** of the authorization server
|
||||
|
||||
<Warning>
|
||||
The authorization server name and audience must match `OKTA_AUTHORIZATION_SERVER` and `OKTA_AUDIENCE` exactly. Mismatches cause `401 Unauthorized` or `Invalid token: Signature verification failed` errors.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "okta"
|
||||
|
||||
secrets:
|
||||
OKTA_CLIENT_ID: "<Okta app client ID>"
|
||||
OKTA_CLIENT_SECRET: "<Okta client secret>"
|
||||
OKTA_SITE: "https://your-domain.okta.com"
|
||||
OKTA_AUTHORIZATION_SERVER: "default"
|
||||
OKTA_AUDIENCE: "api://default"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **new** app integration: **OIDC** → **Native Application**
|
||||
2. Enable **Device Authorization** and **Refresh Token** grant types
|
||||
3. Allow everyone in your organization to access
|
||||
4. Add environment variable:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
OKTA_DEVICE_AUTHORIZATION_CLIENT_ID: "<Native app client ID>"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Device Authorization requires a **Native Application** — it cannot use the Web Application created for browser-based SSO.
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Keycloak
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Client">
|
||||
1. Open Keycloak Admin Console → navigate to your realm
|
||||
2. **Clients** → **Create client**:
|
||||
- **Client type:** OpenID Connect
|
||||
- **Client ID:** `crewai-factory` (suggested)
|
||||
3. Capability config:
|
||||
- **Client authentication:** On
|
||||
- **Standard flow:** Checked
|
||||
4. Login settings:
|
||||
- **Root URL:** `https://<your-domain>`
|
||||
- **Valid redirect URIs:** `https://<your-domain>/auth/keycloak/callback`
|
||||
- **Valid post logout redirect URIs:** `https://<your-domain>`
|
||||
5. Click **Save**
|
||||
</Step>
|
||||
|
||||
<Step title="Collect Credentials">
|
||||
- **Client ID** → `KEYCLOAK_CLIENT_ID`
|
||||
- Under **Credentials** tab: **Client secret** → `KEYCLOAK_CLIENT_SECRET`
|
||||
- **Realm name** → `KEYCLOAK_REALM`
|
||||
- **Keycloak server URL** → `KEYCLOAK_SITE`
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "keycloak"
|
||||
|
||||
secrets:
|
||||
KEYCLOAK_CLIENT_ID: "<client ID>"
|
||||
KEYCLOAK_CLIENT_SECRET: "<client secret>"
|
||||
KEYCLOAK_SITE: "https://keycloak.yourdomain.com"
|
||||
KEYCLOAK_REALM: "<realm name>"
|
||||
KEYCLOAK_AUDIENCE: "account"
|
||||
# Only set if using a custom base path (pre-v17 migrations):
|
||||
# KEYCLOAK_BASE_URL: "/auth"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Keycloak includes `account` as the default audience in access tokens. For most installations, `KEYCLOAK_AUDIENCE=account` works without additional configuration. See [Keycloak audience documentation](https://www.keycloak.org/docs/latest/authorization_services/index.html) if you need a custom audience.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **second** client:
|
||||
- **Client type:** OpenID Connect
|
||||
- **Client ID:** `crewai-factory-cli` (suggested)
|
||||
- **Client authentication:** Off (Device Authorization requires a public client)
|
||||
- **Authentication flow:** Check **only** OAuth 2.0 Device Authorization Grant
|
||||
2. Add environment variable:
|
||||
|
||||
```yaml
|
||||
secrets:
|
||||
KEYCLOAK_DEVICE_AUTHORIZATION_CLIENT_ID: "<CLI client ID>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### WorkOS
|
||||
|
||||
<Steps>
|
||||
<Step title="Configure in WorkOS Dashboard">
|
||||
1. Create an application in the [WorkOS Dashboard](https://dashboard.workos.com)
|
||||
2. Configure the redirect URI: `https://<your-domain>/auth/workos/callback`
|
||||
3. Note the **Client ID** and **AuthKit domain**
|
||||
4. Set up organizations in the WorkOS dashboard
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "workos"
|
||||
|
||||
secrets:
|
||||
WORKOS_CLIENT_ID: "<WorkOS client ID>"
|
||||
WORKOS_AUTHKIT_DOMAIN: "<your-authkit-domain.authkit.com>"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
### Auth0
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Application">
|
||||
1. In the [Auth0 Dashboard](https://manage.auth0.com), create a new **Regular Web Application**
|
||||
2. Configure:
|
||||
- **Allowed Callback URLs:** `https://<your-domain>/auth/auth0/callback`
|
||||
- **Allowed Logout URLs:** `https://<your-domain>`
|
||||
3. Note the **Domain**, **Client ID**, and **Client Secret**
|
||||
</Step>
|
||||
|
||||
<Step title="Set Environment Variables">
|
||||
```yaml
|
||||
envVars:
|
||||
AUTH_PROVIDER: "auth0"
|
||||
|
||||
secrets:
|
||||
AUTH0_CLIENT_ID: "<Auth0 client ID>"
|
||||
AUTH0_CLIENT_SECRET: "<Auth0 client secret>"
|
||||
AUTH0_DOMAIN: "<your-tenant.auth0.com>"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable CLI Support (Optional)">
|
||||
1. Create a **Native** application in Auth0 for Device Authorization
|
||||
2. Enable the **Device Authorization** grant type under application settings
|
||||
3. Configure the CLI with the appropriate audience and client ID
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
@@ -139,54 +388,161 @@ crewai config list
|
||||
|
||||
## Role-Based Access Control (RBAC)
|
||||
|
||||
CrewAI Platform provides granular RBAC that integrates with your SSO provider. Permissions can be scoped to individual resources including dashboards, automations, and environment variables.
|
||||
CrewAI Platform provides granular RBAC that integrates with your SSO provider.
|
||||
|
||||
### Permission Model
|
||||
|
||||
| Permission | Description |
|
||||
|---|---|
|
||||
| **Read** | View resources (dashboards, automations, logs) |
|
||||
| **Write** | Create and modify resources |
|
||||
| **Manage** | Full control including deletion and configuration |
|
||||
|
||||
### Resources
|
||||
|
||||
Permissions can be scoped to individual resources:
|
||||
|
||||
- **Usage Dashboard** — Platform usage metrics and analytics
|
||||
- **Automations Dashboard** — Crew and flow management
|
||||
- **Environment Variables** — Secret and configuration management
|
||||
- **Individual Automations** — Per-automation access control
|
||||
|
||||
### Roles
|
||||
|
||||
- **Predefined roles** come out of the box with standard permission sets
|
||||
- **Custom roles** can be created with any combination of Read, Write, and Manage permissions
|
||||
- **Custom roles** can be created with any combination of permissions
|
||||
- **Per-resource assignment** — limit specific automations to individual users or roles
|
||||
|
||||
For detailed RBAC configuration, see the [RBAC documentation](/enterprise/features/rbac).
|
||||
### Factory Admin Access
|
||||
|
||||
For Factory deployments using Entra ID, admin access is controlled via App Roles:
|
||||
|
||||
- Assign the `factory-admin` role to users who need admin panel access
|
||||
- Assign the `member` role for standard platform access
|
||||
- Roles are communicated via JWT claims — no additional configuration needed after IdP setup
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Invalid Redirect URI
|
||||
|
||||
**Symptom:** Authentication fails with a redirect URI mismatch error.
|
||||
|
||||
**Fix:** Ensure the redirect URI in your IdP exactly matches the expected callback URL:
|
||||
|
||||
| Provider | Callback URL |
|
||||
|---|---|
|
||||
| Entra ID | `https://<domain>/auth/entra_id/callback` |
|
||||
| Okta | `https://<domain>/auth/okta/callback` |
|
||||
| Keycloak | `https://<domain>/auth/keycloak/callback` |
|
||||
| WorkOS | `https://<domain>/auth/workos/callback` |
|
||||
| Auth0 | `https://<domain>/auth/auth0/callback` |
|
||||
|
||||
### CLI Login Fails (Device Authorization)
|
||||
|
||||
**Symptom:** `crewai login` returns an error or times out.
|
||||
|
||||
**Fix:**
|
||||
- Verify that Device Authorization Grant is enabled in your IdP
|
||||
- Check that your CLI is configured correctly: `crewai config list`
|
||||
- Ensure your CrewAI CLI version meets the minimum requirements for your provider
|
||||
- For Okta: ensure you have a **Native Application** (not Web) with Device Authorization grant
|
||||
- For Entra ID: ensure **Allow public client flows** is enabled
|
||||
- For Keycloak: ensure the CLI client has **Client authentication: Off** and only Device Authorization Grant enabled
|
||||
- Check that `*_DEVICE_AUTHORIZATION_CLIENT_ID` environment variable is set on the server
|
||||
|
||||
### Token Validation Errors
|
||||
|
||||
**Symptom:** `Invalid token: Signature verification failed` or `401 Unauthorized` after login.
|
||||
|
||||
**Fix:**
|
||||
- Verify that your OAuth2 audience and authorization server settings match your IdP configuration exactly
|
||||
- For SaaS: contact your CrewAI account team if errors persist after verifying CLI settings
|
||||
- **Okta:** Verify `OKTA_AUTHORIZATION_SERVER` and `OKTA_AUDIENCE` match the authorization server's Name and Audience exactly
|
||||
- **Entra ID:** Ensure `accessTokenAcceptedVersion` is set to `2` in the app manifest
|
||||
- **Keycloak:** Verify `KEYCLOAK_AUDIENCE` matches the audience in your access tokens (default: `account`)
|
||||
|
||||
### Admin Consent Not Granted (Entra ID)
|
||||
|
||||
**Symptom:** Users can't log in, see "needs admin approval" message.
|
||||
|
||||
**Fix:** Go to **Enterprise applications** → your app → **Permissions** → **Grant admin consent**. Ensure `User.Read` is granted for Microsoft Graph.
|
||||
|
||||
### 403 Forbidden After Login
|
||||
|
||||
**Symptom:** User authenticates successfully but gets 403 errors.
|
||||
|
||||
**Fix:**
|
||||
- Check that the user is assigned to the CrewAI application in your IdP
|
||||
- Verify the user has the appropriate role assignment in your IdP
|
||||
- Check that the user is assigned to the application in your IdP
|
||||
- For Entra ID with **Assignment required = Yes**: ensure the user has a role assignment (Member or Factory Admin)
|
||||
- For Okta: verify the user or their group is assigned under the app's **Assignments** tab
|
||||
|
||||
### CLI Can't Reach CrewAI Instance
|
||||
### CLI Can't Reach Factory Instance
|
||||
|
||||
**Symptom:** `crewai enterprise configure` fails to connect.
|
||||
|
||||
**Fix:**
|
||||
- Verify the instance URL is reachable from your machine
|
||||
- Verify the Factory URL is reachable from your machine
|
||||
- Check that `enterprise_base_url` is set correctly: `crewai config list`
|
||||
- Ensure TLS certificates are valid and trusted
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
### Common
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `AUTH_PROVIDER` | Authentication provider: `entra_id`, `okta`, `workos`, `auth0`, `keycloak`, `local` |
|
||||
|
||||
### Microsoft Entra ID
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `ENTRA_ID_CLIENT_ID` | ✅ | Application (client) ID from Azure |
|
||||
| `ENTRA_ID_CLIENT_SECRET` | ✅ | Client secret from Azure |
|
||||
| `ENTRA_ID_TENANT_ID` | ✅ | Directory (tenant) ID from Azure |
|
||||
| `ENTRA_ID_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Client ID for Device Authorization Grant |
|
||||
| `ENTRA_ID_CUSTOM_OPENID_SCOPE` | CLI only | Custom scope from "Expose an API" (e.g., `api://crewai-cli/read`) |
|
||||
|
||||
### Okta
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `OKTA_CLIENT_ID` | ✅ | Okta application client ID |
|
||||
| `OKTA_CLIENT_SECRET` | ✅ | Okta client secret |
|
||||
| `OKTA_SITE` | ✅ | Okta organization URL (e.g., `https://your-domain.okta.com`) |
|
||||
| `OKTA_AUTHORIZATION_SERVER` | ✅ | Authorization server name (e.g., `default`) |
|
||||
| `OKTA_AUDIENCE` | ✅ | Authorization server audience (e.g., `api://default`) |
|
||||
| `OKTA_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Native app client ID for Device Authorization |
|
||||
|
||||
### WorkOS
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `WORKOS_CLIENT_ID` | ✅ | WorkOS application client ID |
|
||||
| `WORKOS_AUTHKIT_DOMAIN` | ✅ | AuthKit domain (e.g., `your-domain.authkit.com`) |
|
||||
|
||||
### Auth0
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `AUTH0_CLIENT_ID` | ✅ | Auth0 application client ID |
|
||||
| `AUTH0_CLIENT_SECRET` | ✅ | Auth0 client secret |
|
||||
| `AUTH0_DOMAIN` | ✅ | Auth0 tenant domain (e.g., `your-tenant.auth0.com`) |
|
||||
|
||||
### Keycloak
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `KEYCLOAK_CLIENT_ID` | ✅ | Keycloak client ID |
|
||||
| `KEYCLOAK_CLIENT_SECRET` | ✅ | Keycloak client secret |
|
||||
| `KEYCLOAK_SITE` | ✅ | Keycloak server URL |
|
||||
| `KEYCLOAK_REALM` | ✅ | Keycloak realm name |
|
||||
| `KEYCLOAK_AUDIENCE` | ✅ | Token audience (default: `account`) |
|
||||
| `KEYCLOAK_BASE_URL` | Optional | Base URL path (e.g., `/auth` for pre-v17 migrations) |
|
||||
| `KEYCLOAK_DEVICE_AUTHORIZATION_CLIENT_ID` | CLI only | Public client ID for Device Authorization |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Installation Guide](/installation) — Get started with CrewAI
|
||||
|
||||
@@ -106,7 +106,7 @@ The CLI automatically detects your project type from `pyproject.toml` and builds
|
||||
```
|
||||
|
||||
<Tip>
|
||||
The first deployment typically takes 10-15 minutes as it builds the container images. Subsequent deployments are much faster.
|
||||
The first deployment typically takes around 1 minute.
|
||||
</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 10-15 minutes; subsequent deployments will be faster
|
||||
3. The first deployment typically takes around 1 minute
|
||||
|
||||
<Frame>
|
||||

|
||||
|
||||
132
docs/en/enterprise/guides/training-crews.mdx
Normal file
132
docs/en/enterprise/guides/training-crews.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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>
|
||||
@@ -5,6 +5,14 @@ icon: wrench
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
### Watch: Building CrewAI Agents & Flows with Coding Agent Skills
|
||||
|
||||
Install our coding agent skills (Claude Code, Codex, ...) to quickly get your coding agents up and running with CrewAI.
|
||||
|
||||
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>
|
||||
|
||||
## Video Tutorial
|
||||
|
||||
Watch this video tutorial for a step-by-step demonstration of the installation process:
|
||||
@@ -163,6 +171,9 @@ 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
|
||||
@@ -196,9 +207,8 @@ For teams and organizations, CrewAI offers enterprise deployment options that el
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<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 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>
|
||||
<Card
|
||||
title="Join the Community"
|
||||
|
||||
@@ -16,6 +16,14 @@ It empowers developers to build production-ready multi-agent systems by combinin
|
||||
|
||||
With over 100,000 developers certified through our community courses, CrewAI is the standard for enterprise-ready AI automation.
|
||||
|
||||
### Watch: Building CrewAI Agents & Flows with Coding Agent Skills
|
||||
|
||||
Install our coding agent skills (Claude Code, Codex, ...) to quickly get your coding agents up and running with CrewAI.
|
||||
|
||||
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>
|
||||
|
||||
## The CrewAI Architecture
|
||||
|
||||
CrewAI's architecture is designed to balance autonomy with control.
|
||||
@@ -132,7 +140,7 @@ For any production-ready application, **start with a Flow**.
|
||||
icon="bolt"
|
||||
href="en/quickstart"
|
||||
>
|
||||
Follow our quickstart guide to create your first CrewAI agent and get hands-on experience.
|
||||
Scaffold a Flow, run a crew with one agent, and generate a report end to end.
|
||||
</Card>
|
||||
<Card
|
||||
title="Join the Community"
|
||||
|
||||
344
docs/en/learn/a2ui.mdx
Normal file
344
docs/en/learn/a2ui.mdx
Normal file
@@ -0,0 +1,344 @@
|
||||
---
|
||||
title: Agent-to-UI (A2UI) Protocol
|
||||
description: Enable agents to generate declarative UI surfaces for rich client rendering via the A2UI extension.
|
||||
icon: window-restore
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## A2UI Overview
|
||||
|
||||
A2UI is a declarative UI protocol extension for [A2A](/en/learn/a2a-agent-delegation) that lets agents emit structured JSON messages describing interactive surfaces. Clients receive these messages and render them as rich UI components — forms, cards, lists, modals, and more — without the agent needing to know anything about the client's rendering stack.
|
||||
|
||||
A2UI is built on the A2A extension mechanism and identified by the URI `https://a2ui.org/a2a-extension/a2ui/v0.8`.
|
||||
|
||||
<Note>
|
||||
A2UI requires the `a2a-sdk` package. Install with: `uv add 'crewai[a2a]'` or `pip install 'crewai[a2a]'`
|
||||
</Note>
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The **server extension** scans agent output for A2UI JSON objects
|
||||
2. Valid messages are wrapped as `DataPart` entries with the `application/json+a2ui` MIME type
|
||||
3. The **client extension** augments the agent's system prompt with A2UI instructions and the component catalog
|
||||
4. The client tracks surface state (active surfaces and data models) across conversation turns
|
||||
|
||||
## Server Setup
|
||||
|
||||
Add `A2UIServerExtension` to your `A2AServerConfig` to enable A2UI output:
|
||||
|
||||
```python Code
|
||||
from crewai import Agent
|
||||
from crewai.a2a import A2AServerConfig
|
||||
from crewai.a2a.extensions.a2ui import A2UIServerExtension
|
||||
|
||||
agent = Agent(
|
||||
role="Dashboard Agent",
|
||||
goal="Present data through interactive UI surfaces",
|
||||
backstory="Expert at building clear, actionable dashboards",
|
||||
llm="gpt-4o",
|
||||
a2a=A2AServerConfig(
|
||||
url="https://your-server.com",
|
||||
server_extensions=[A2UIServerExtension()],
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### Server Extension Options
|
||||
|
||||
<ParamField path="catalog_ids" type="list[str] | None" default="None">
|
||||
Component catalog identifiers the server supports. When set, only these catalogs are advertised to clients.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="accept_inline_catalogs" type="bool" default="False">
|
||||
Whether to accept inline catalog definitions from clients in addition to named catalogs.
|
||||
</ParamField>
|
||||
|
||||
## Client Setup
|
||||
|
||||
Add `A2UIClientExtension` to your `A2AClientConfig` to enable A2UI rendering:
|
||||
|
||||
```python Code
|
||||
from crewai import Agent
|
||||
from crewai.a2a import A2AClientConfig
|
||||
from crewai.a2a.extensions.a2ui import A2UIClientExtension
|
||||
|
||||
agent = Agent(
|
||||
role="UI Coordinator",
|
||||
goal="Coordinate tasks and render agent responses as rich UI",
|
||||
backstory="Expert at presenting agent output in interactive formats",
|
||||
llm="gpt-4o",
|
||||
a2a=A2AClientConfig(
|
||||
endpoint="https://dashboard-agent.example.com/.well-known/agent-card.json",
|
||||
client_extensions=[A2UIClientExtension()],
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### Client Extension Options
|
||||
|
||||
<ParamField path="catalog_id" type="str | None" default="None">
|
||||
Preferred component catalog identifier. Defaults to `"standard (v0.8)"` when not set.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="allowed_components" type="list[str] | None" default="None">
|
||||
Restrict which components the agent may use. When `None`, all 18 standard catalog components are available.
|
||||
</ParamField>
|
||||
|
||||
## Message Types
|
||||
|
||||
A2UI defines four server-to-client message types. Each message targets a **surface** identified by `surfaceId`.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="beginRendering">
|
||||
Initializes a new surface with a root component and optional styles.
|
||||
|
||||
```json
|
||||
{
|
||||
"beginRendering": {
|
||||
"surfaceId": "dashboard-1",
|
||||
"root": "main-column",
|
||||
"catalogId": "standard (v0.8)",
|
||||
"styles": {
|
||||
"primaryColor": "#EB6658"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="surfaceUpdate">
|
||||
Sends or updates one or more components on an existing surface.
|
||||
|
||||
```json
|
||||
{
|
||||
"surfaceUpdate": {
|
||||
"surfaceId": "dashboard-1",
|
||||
"components": [
|
||||
{
|
||||
"id": "main-column",
|
||||
"component": {
|
||||
"Column": {
|
||||
"children": { "explicitList": ["title", "content"] },
|
||||
"alignment": "start"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"component": {
|
||||
"Text": {
|
||||
"text": { "literalString": "Dashboard" },
|
||||
"usageHint": "h1"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="dataModelUpdate">
|
||||
Updates the data model bound to a surface, enabling dynamic content.
|
||||
|
||||
```json
|
||||
{
|
||||
"dataModelUpdate": {
|
||||
"surfaceId": "dashboard-1",
|
||||
"path": "/data/model",
|
||||
"contents": [
|
||||
{
|
||||
"key": "userName",
|
||||
"valueString": "Alice"
|
||||
},
|
||||
{
|
||||
"key": "score",
|
||||
"valueNumber": 42
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="deleteSurface">
|
||||
Removes a surface and all its components.
|
||||
|
||||
```json
|
||||
{
|
||||
"deleteSurface": {
|
||||
"surfaceId": "dashboard-1"
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Component Catalog
|
||||
|
||||
A2UI ships with 18 standard components organized into three categories:
|
||||
|
||||
### Content
|
||||
|
||||
| Component | Description | Required Fields |
|
||||
|-----------|-------------|-----------------|
|
||||
| **Text** | Renders text with optional heading/body hints | `text` (StringBinding) |
|
||||
| **Image** | Displays an image with fit and size options | `url` (StringBinding) |
|
||||
| **Icon** | Renders a named icon from a set of 47 icons | `name` (IconBinding) |
|
||||
| **Video** | Embeds a video player | `url` (StringBinding) |
|
||||
| **AudioPlayer** | Embeds an audio player with optional description | `url` (StringBinding) |
|
||||
|
||||
### Layout
|
||||
|
||||
| Component | Description | Required Fields |
|
||||
|-----------|-------------|-----------------|
|
||||
| **Row** | Horizontal flex container | `children` (ChildrenDef) |
|
||||
| **Column** | Vertical flex container | `children` (ChildrenDef) |
|
||||
| **List** | Scrollable list (vertical or horizontal) | `children` (ChildrenDef) |
|
||||
| **Card** | Elevated container for a single child | `child` (str) |
|
||||
| **Tabs** | Tabbed container | `tabItems` (list of TabItem) |
|
||||
| **Divider** | Visual separator (horizontal or vertical) | — |
|
||||
| **Modal** | Overlay triggered by an entry point | `entryPointChild`, `contentChild` (str) |
|
||||
|
||||
### Interactive
|
||||
|
||||
| Component | Description | Required Fields |
|
||||
|-----------|-------------|-----------------|
|
||||
| **Button** | Clickable button that triggers an action | `child` (str), `action` (Action) |
|
||||
| **CheckBox** | Boolean toggle with a label | `label` (StringBinding), `value` (BooleanBinding) |
|
||||
| **TextField** | Text input with type and validation options | `label` (StringBinding) |
|
||||
| **DateTimeInput** | Date and/or time picker | `value` (StringBinding) |
|
||||
| **MultipleChoice** | Selection from a list of options | `selections` (ArrayBinding), `options` (list) |
|
||||
| **Slider** | Numeric range slider | `value` (NumberBinding) |
|
||||
|
||||
## Data Binding
|
||||
|
||||
Components reference values through **bindings** rather than raw literals. This allows surfaces to update dynamically when the data model changes.
|
||||
|
||||
There are two ways to bind a value:
|
||||
|
||||
- **Literal values** — hardcoded directly in the component definition
|
||||
- **Path references** — point to a key in the surface's data model
|
||||
|
||||
```json
|
||||
{
|
||||
"surfaceUpdate": {
|
||||
"surfaceId": "profile-1",
|
||||
"components": [
|
||||
{
|
||||
"id": "greeting",
|
||||
"component": {
|
||||
"Text": {
|
||||
"text": { "path": "/data/model/userName" },
|
||||
"usageHint": "h2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "status",
|
||||
"component": {
|
||||
"Text": {
|
||||
"text": { "literalString": "Online" },
|
||||
"usageHint": "caption"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, `greeting` reads the user's name from the data model (updated via `dataModelUpdate`), while `status` uses a hardcoded literal.
|
||||
|
||||
## Handling User Actions
|
||||
|
||||
Interactive components like `Button` trigger `userAction` events that flow back to the server. Each action includes a `name`, the originating `surfaceId` and `sourceComponentId`, and an optional `context` with key-value pairs.
|
||||
|
||||
```json
|
||||
{
|
||||
"userAction": {
|
||||
"name": "submitForm",
|
||||
"surfaceId": "form-1",
|
||||
"sourceComponentId": "submit-btn",
|
||||
"timestamp": "2026-03-12T10:00:00Z",
|
||||
"context": {
|
||||
"selectedOption": "optionA"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Action context values can also use path bindings to send current data model values back to the server:
|
||||
|
||||
```json
|
||||
{
|
||||
"Button": {
|
||||
"child": "confirm-label",
|
||||
"action": {
|
||||
"name": "confirm",
|
||||
"context": [
|
||||
{
|
||||
"key": "currentScore",
|
||||
"value": { "path": "/data/model/score" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
Use `validate_a2ui_message` to validate server-to-client messages and `validate_a2ui_event` for client-to-server events:
|
||||
|
||||
```python Code
|
||||
from crewai.a2a.extensions.a2ui import validate_a2ui_message
|
||||
from crewai.a2a.extensions.a2ui.validator import (
|
||||
validate_a2ui_event,
|
||||
A2UIValidationError,
|
||||
)
|
||||
|
||||
# Validate a server message
|
||||
try:
|
||||
msg = validate_a2ui_message({"beginRendering": {"surfaceId": "s1", "root": "r1"}})
|
||||
except A2UIValidationError as exc:
|
||||
print(exc.errors)
|
||||
|
||||
# Validate a client event
|
||||
try:
|
||||
event = validate_a2ui_event({
|
||||
"userAction": {
|
||||
"name": "click",
|
||||
"surfaceId": "s1",
|
||||
"sourceComponentId": "btn-1",
|
||||
"timestamp": "2026-03-12T10:00:00Z",
|
||||
}
|
||||
})
|
||||
except A2UIValidationError as exc:
|
||||
print(exc.errors)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Start Simple" icon="play">
|
||||
Begin with a `beginRendering` message and a single `surfaceUpdate`. Add data binding and interactivity once the basic flow works.
|
||||
</Card>
|
||||
|
||||
<Card title="Use Data Binding for Dynamic Content" icon="arrows-rotate">
|
||||
Prefer path bindings over literal values for content that changes. Use `dataModelUpdate` to push new values without resending the full component tree.
|
||||
</Card>
|
||||
|
||||
<Card title="Filter Components" icon="filter">
|
||||
Use the `allowed_components` option on `A2UIClientExtension` to restrict which components the agent may emit, reducing prompt size and keeping output predictable.
|
||||
</Card>
|
||||
|
||||
<Card title="Validate Messages" icon="check">
|
||||
Use `validate_a2ui_message` and `validate_a2ui_event` to catch malformed payloads early, especially when building custom integrations.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Learn More
|
||||
|
||||
- [A2A Agent Delegation](/en/learn/a2a-agent-delegation) — configure agents for remote delegation via the A2A protocol
|
||||
- [A2A Protocol Documentation](https://a2a-protocol.org) — official protocol specification
|
||||
@@ -1,43 +1,49 @@
|
||||
---
|
||||
title: Quickstart
|
||||
description: Build your first AI agent with CrewAI in under 5 minutes.
|
||||
description: Build your first CrewAI Flow in minutes — orchestration, state, and an agent crew that produces a real report.
|
||||
icon: rocket
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Build your first CrewAI Agent
|
||||
### Watch: Building CrewAI Agents & Flows with Coding Agent Skills
|
||||
|
||||
Let's create a simple crew that will help us `research` and `report` on the `latest AI developments` for a given topic or subject.
|
||||
Install our coding agent skills (Claude Code, Codex, ...) to quickly get your coding agents up and running with CrewAI.
|
||||
|
||||
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).
|
||||
You can install it with `npx skills add crewaiinc/skills`
|
||||
|
||||
Follow the steps below to get Crewing! 🚣♂️
|
||||
<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.
|
||||
|
||||
If you have not installed CrewAI yet, follow the [installation guide](/en/installation) first.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- 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
|
||||
|
||||
<Steps>
|
||||
<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.
|
||||
<Step title="Create a Flow project">
|
||||
From your terminal, scaffold a Flow project (the folder name uses underscores, e.g. `latest_ai_flow`):
|
||||
|
||||
<CodeGroup>
|
||||
```shell Terminal
|
||||
crewai create crew latest-ai-development
|
||||
crewai create flow latest-ai-flow
|
||||
cd latest_ai_flow
|
||||
```
|
||||
</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="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>
|
||||
|
||||
<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=...)`.
|
||||
|
||||
```yaml agents.yaml
|
||||
# src/latest_ai_development/config/agents.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
|
||||
researcher:
|
||||
role: >
|
||||
{topic} Senior Data Researcher
|
||||
@@ -45,336 +51,232 @@ Follow the steps below to get Crewing! 🚣♂️
|
||||
Uncover cutting-edge developments in {topic}
|
||||
backstory: >
|
||||
You're a seasoned researcher with a knack for uncovering the latest
|
||||
developments in {topic}. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.
|
||||
|
||||
reporting_analyst:
|
||||
role: >
|
||||
{topic} Reporting Analyst
|
||||
goal: >
|
||||
Create detailed reports based on {topic} data analysis and research findings
|
||||
backstory: >
|
||||
You're a meticulous analyst with a keen eye for detail. You're known for
|
||||
your ability to turn complex data into clear and concise reports, making
|
||||
it easy for others to understand and act on the information you provide.
|
||||
developments in {topic}. You find the most relevant information and
|
||||
present it clearly.
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Modify your `tasks.yaml` file">
|
||||
|
||||
<Step title="Configure one task in `tasks.yaml`">
|
||||
```yaml tasks.yaml
|
||||
# src/latest_ai_development/config/tasks.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
|
||||
research_task:
|
||||
description: >
|
||||
Conduct a thorough research about {topic}
|
||||
Make sure you find any interesting and relevant information given
|
||||
the current year is 2025.
|
||||
Conduct thorough research about {topic}. Use web search to find current,
|
||||
credible information. The current year is 2026.
|
||||
expected_output: >
|
||||
A list with 10 bullet points of the most relevant information about {topic}
|
||||
A markdown report with clear sections: key trends, notable tools or companies,
|
||||
and implications. Aim for 800–1200 words. No fenced code blocks around the whole document.
|
||||
agent: researcher
|
||||
|
||||
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
|
||||
output_file: output/report.md
|
||||
```
|
||||
|
||||
</Step>
|
||||
<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.project import CrewBase, agent, crew, task
|
||||
from crewai_tools import SerperDevTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
<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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
class ResearchCrew:
|
||||
"""Single-agent research crew used inside the Flow."""
|
||||
|
||||
agents: List[BaseAgent]
|
||||
tasks: List[Task]
|
||||
|
||||
agents_config = "config/agents.yaml"
|
||||
tasks_config = "config/tasks.yaml"
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['researcher'], # type: ignore[index]
|
||||
config=self.agents_config["researcher"], # type: ignore[index]
|
||||
verbose=True,
|
||||
tools=[SerperDevTool()]
|
||||
)
|
||||
|
||||
@agent
|
||||
def reporting_analyst(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['reporting_analyst'], # type: ignore[index]
|
||||
verbose=True
|
||||
tools=[SerperDevTool()],
|
||||
)
|
||||
|
||||
@task
|
||||
def research_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['research_task'], # type: ignore[index]
|
||||
)
|
||||
|
||||
@task
|
||||
def reporting_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['reporting_task'], # type: ignore[index]
|
||||
output_file='output/report.md' # This is the file that will be contain the final report.
|
||||
config=self.tasks_config["research_task"], # type: ignore[index]
|
||||
)
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
"""Creates the LatestAiDevelopment crew"""
|
||||
return Crew(
|
||||
agents=self.agents, # Automatically created by the @agent decorator
|
||||
tasks=self.tasks, # Automatically created by the @task decorator
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
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
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
<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 task’s `output_file` still writes `output/report.md`.
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
|
||||
# ... remaining code
|
||||
```
|
||||
|
||||
</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
|
||||
# src/latest_ai_flow/main.py
|
||||
from pydantic import BaseModel
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run the crew.
|
||||
"""
|
||||
inputs = {
|
||||
'topic': 'AI Agents'
|
||||
}
|
||||
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
|
||||
from crewai.flow import Flow, listen, start
|
||||
|
||||
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()
|
||||
```
|
||||
|
||||
</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>
|
||||
<Tip>
|
||||
If your package name differs from `latest_ai_flow`, change the import of `ResearchCrew` to match your project’s module path.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Enterprise Alternative: Create in Crew Studio">
|
||||
For CrewAI AMP users, you can create the same crew without writing code:
|
||||
<Step title="Set environment variables">
|
||||
In `.env` at the project root, set:
|
||||
|
||||
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"
|
||||
|
||||

|
||||
|
||||
<Card title="Try CrewAI AMP" icon="rocket" href="https://app.crewai.com">
|
||||
Start your free account at CrewAI AMP
|
||||
</Card>
|
||||
- `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)
|
||||
</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.
|
||||
|
||||
Here's an example of what the report should look like:
|
||||
<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):
|
||||
|
||||
<CodeGroup>
|
||||
```markdown output/report.md
|
||||
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
|
||||
# AI Agents in 2026: Landscape and Trends
|
||||
|
||||
## 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.
|
||||
## Executive summary
|
||||
…
|
||||
|
||||
## 2. Benefits of AI Agents
|
||||
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
|
||||
## Key trends
|
||||
- **Tool use and orchestration** — …
|
||||
- **Enterprise adoption** — …
|
||||
|
||||
- **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.
|
||||
## Implications
|
||||
…
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Your actual file will be longer and reflect live search results.
|
||||
</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 task’s `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>
|
||||
Congratulations!
|
||||
|
||||
You have successfully set up your crew project and are ready to start building your own agentic workflows!
|
||||
|
||||
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.
|
||||
</Check>
|
||||
|
||||
### Note on Consistency in Naming
|
||||
### Naming consistency
|
||||
|
||||
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.
|
||||
YAML keys (`researcher`, `research_task`) must match the method names on your `@CrewBase` class. See [Crews](/en/concepts/crews) for the full decorator pattern.
|
||||
|
||||
#### Example References
|
||||
## Deploying
|
||||
|
||||
<Tip>
|
||||
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>
|
||||
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:
|
||||
|
||||
```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
|
||||
<CodeGroup>
|
||||
```bash Authenticate
|
||||
crewai login
|
||||
```
|
||||
|
||||
<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
|
||||
```bash Create deployment
|
||||
crewai deploy create
|
||||
```
|
||||
|
||||
## Deploying Your Crew
|
||||
```bash Check status & logs
|
||||
crewai deploy status
|
||||
crewai deploy logs
|
||||
```
|
||||
|
||||
The easiest way to deploy your crew to production is through [CrewAI AMP](http://app.crewai.com).
|
||||
```bash Ship updates after you change code
|
||||
crewai deploy push
|
||||
```
|
||||
|
||||
Watch this video tutorial for a step-by-step demonstration of deploying your crew to [CrewAI AMP](http://app.crewai.com) using the CLI.
|
||||
```bash List or remove deployments
|
||||
crewai deploy list
|
||||
crewai deploy remove <deployment_id>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<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>
|
||||
<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).
|
||||
</Tip>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<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 title="Deploy guide" icon="book" href="/en/enterprise/guides/deploy-to-amp">
|
||||
Step-by-step AMP deployment (CLI and dashboard).
|
||||
</Card>
|
||||
<Card
|
||||
title="Join the Community"
|
||||
icon="comments"
|
||||
href="https://community.crewai.com"
|
||||
>
|
||||
Join our open source community to discuss ideas, share your projects, and
|
||||
connect with other CrewAI developers.
|
||||
Discuss ideas, share projects, and connect with other CrewAI developers.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -4,6 +4,192 @@ 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
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- v0.8/v0.9 지원, 스키마 및 문서가 포함된 A2UI 확장 추가
|
||||
|
||||
### 버그 수정
|
||||
- GPT-5 및 o-series를 추가하여 다중 모드 비전 접두사 수정
|
||||
|
||||
### 문서
|
||||
- v1.13.0a6에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@alex-clawd, @greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 4월 1일">
|
||||
## v1.13.0a6
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 문서
|
||||
- 실제 UI 옵션에 맞게 RBAC 권한 수준 수정 (#5210)
|
||||
- v1.13.0a5에 대한 변경 로그 및 버전 업데이트 (#5200)
|
||||
|
||||
### 성능
|
||||
- 지연 이벤트 버스를 구현하고 비활성화 시 추적을 건너뛰어 프레임워크 오버헤드 감소 (#5187)
|
||||
|
||||
## 기여자
|
||||
|
||||
@alex-clawd, @joaomdmoura, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 3월 31일">
|
||||
## v1.13.0a5
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a5)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 문서
|
||||
- v1.13.0a4에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 4월 1일">
|
||||
## v1.13.0a4
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a4)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 문서
|
||||
- v1.13.0a3에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 4월 1일">
|
||||
## v1.13.0a3
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a3)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- LLMCallCompletedEvent에서 토큰 사용 데이터 발행
|
||||
- 도구 메타데이터를 AMP로 추출 및 게시
|
||||
|
||||
### 버그 수정
|
||||
- `stop` API 매개변수를 지원하지 않는 GPT-5.x 모델 처리
|
||||
|
||||
### 문서
|
||||
- 모든 언어에서 에이전트 기능의 부정확성 수정
|
||||
- 에이전트 기능 개요 추가 및 기술 문서 개선
|
||||
- 포괄적인 SSO 구성 가이드 추가
|
||||
- v1.13.0rc1에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
### 리팩토링
|
||||
- Flow를 Pydantic BaseModel로 변환
|
||||
- LLM 클래스를 Pydantic BaseModel로 변환
|
||||
- InstanceOf[T]를 일반 타입 주석으로 교체
|
||||
- 사용되지 않는 메서드 제거
|
||||
|
||||
## 기여자
|
||||
|
||||
@dependabot[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide, @thiagomoretto
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 3월 27일">
|
||||
## v1.13.0rc1
|
||||
|
||||
|
||||
147
docs/ko/concepts/agent-capabilities.mdx
Normal file
147
docs/ko/concepts/agent-capabilities.mdx
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: "에이전트 기능"
|
||||
description: "CrewAI 에이전트를 확장하는 다섯 가지 방법 이해하기: 도구, MCP, 앱, 스킬, 지식."
|
||||
icon: puzzle-piece
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
CrewAI 에이전트는 **다섯 가지 고유한 기능 유형**으로 확장할 수 있으며, 각각 다른 목적을 가지고 있습니다. 각 유형을 언제 사용해야 하는지, 그리고 어떻게 함께 작동하는지 이해하는 것이 효과적인 에이전트를 구축하는 핵심입니다.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="도구" icon="wrench" href="/ko/concepts/tools" color="#3B82F6">
|
||||
**호출 가능한 함수** — 에이전트가 행동을 취할 수 있게 합니다. 웹 검색, 파일 작업, API 호출, 코드 실행.
|
||||
</Card>
|
||||
<Card title="MCP 서버" icon="plug" href="/ko/mcp/overview" color="#8B5CF6">
|
||||
**원격 도구 서버** — Model Context Protocol을 통해 에이전트를 외부 도구 서버에 연결합니다. 도구와 같은 효과이지만 외부에서 호스팅됩니다.
|
||||
</Card>
|
||||
<Card title="앱" icon="grid-2" color="#EC4899">
|
||||
**플랫폼 통합** — CrewAI 플랫폼을 통해 에이전트를 SaaS 앱(Gmail, Slack, Jira, Salesforce)에 연결합니다. 플랫폼 통합 토큰으로 로컬에서 실행됩니다.
|
||||
</Card>
|
||||
<Card title="스킬" icon="bolt" href="/ko/concepts/skills" color="#F59E0B">
|
||||
**도메인 전문성** — 에이전트 프롬프트에 지침, 가이드라인 및 참조 자료를 주입합니다. 스킬은 에이전트에게 *어떻게 생각할지*를 알려줍니다.
|
||||
</Card>
|
||||
<Card title="지식" icon="book" href="/ko/concepts/knowledge" color="#10B981">
|
||||
**검색된 사실** — 시맨틱 검색(RAG)을 통해 문서, 파일 및 URL에서 에이전트에게 데이터를 제공합니다. 지식은 에이전트에게 *무엇을 알아야 하는지*를 제공합니다.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## 핵심 구분
|
||||
|
||||
가장 중요한 점: **이 기능들은 두 가지 범주로 나뉩니다**.
|
||||
|
||||
### 액션 기능 (도구, MCP, 앱)
|
||||
|
||||
에이전트에게 **무언가를 할 수 있는** 능력을 부여합니다 — API 호출, 파일 읽기, 웹 검색, 이메일 전송. 실행 시점에 세 가지 모두 동일한 내부 형식(`BaseTool` 인스턴스)으로 변환되며, 에이전트가 호출할 수 있는 통합 도구 목록에 나타납니다.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find and compile market data",
|
||||
backstory="Expert market analyst",
|
||||
tools=[SerperDevTool(), FileReadTool()], # 로컬 도구
|
||||
mcps=["https://mcp.example.com/sse"], # 원격 MCP 서버 도구
|
||||
apps=["gmail", "google_sheets"], # 플랫폼 통합
|
||||
)
|
||||
```
|
||||
|
||||
### 컨텍스트 기능 (스킬, 지식)
|
||||
|
||||
에이전트의 **프롬프트**를 수정합니다 — 에이전트가 추론을 시작하기 전에 전문성, 지침 또는 검색된 데이터를 주입합니다. 에이전트에게 새로운 액션을 제공하는 것이 아니라, 에이전트가 어떻게 생각하고 어떤 정보에 접근할 수 있는지를 형성합니다.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Security Auditor",
|
||||
goal="Audit cloud infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security with 10 years of experience",
|
||||
skills=["./skills/security-audit"], # 도메인 지침
|
||||
knowledge_sources=[pdf_source, url_source], # 검색된 사실
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 언제 무엇을 사용할까
|
||||
|
||||
| 필요한 것... | 사용할 것 | 예시 |
|
||||
| :------------------------------------------------------- | :---------------- | :--------------------------------------- |
|
||||
| 에이전트가 웹을 검색 | **도구** | `tools=[SerperDevTool()]` |
|
||||
| 에이전트가 MCP를 통해 원격 API 호출 | **MCP** | `mcps=["https://api.example.com/sse"]` |
|
||||
| 에이전트가 Gmail로 이메일 전송 | **앱** | `apps=["gmail"]` |
|
||||
| 에이전트가 특정 절차를 따름 | **스킬** | `skills=["./skills/code-review"]` |
|
||||
| 에이전트가 회사 문서 참조 | **지식** | `knowledge_sources=[pdf_source]` |
|
||||
| 에이전트가 웹 검색 AND 리뷰 가이드라인 준수 | **도구 + 스킬** | 둘 다 함께 사용 |
|
||||
|
||||
---
|
||||
|
||||
## 기능 조합하기
|
||||
|
||||
실제로 에이전트는 종종 **여러 기능 유형을 함께** 사용합니다. 현실적인 예시입니다:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
# 완전히 갖춘 리서치 에이전트
|
||||
researcher = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Produce comprehensive market analysis reports",
|
||||
backstory="Expert analyst with deep industry knowledge",
|
||||
|
||||
# 액션: 에이전트가 할 수 있는 것
|
||||
tools=[
|
||||
SerperDevTool(), # 웹 검색
|
||||
FileReadTool(), # 로컬 파일 읽기
|
||||
CodeInterpreterTool(), # 분석을 위한 Python 코드 실행
|
||||
],
|
||||
mcps=["https://data-api.example.com/sse"], # 원격 데이터 API 접근
|
||||
apps=["google_sheets"], # Google Sheets에 쓰기
|
||||
|
||||
# 컨텍스트: 에이전트가 아는 것
|
||||
skills=["./skills/research-methodology"], # 연구 수행 방법
|
||||
knowledge_sources=[company_docs], # 회사 특화 데이터
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 비교 테이블
|
||||
|
||||
| 특성 | 도구 | MCP | 앱 | 스킬 | 지식 |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: |
|
||||
| **에이전트에게 액션 부여** | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| **프롬프트 수정** | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| **코드 필요** | 예 | 설정만 | 설정만 | 마크다운만 | 설정만 |
|
||||
| **로컬 실행** | 예 | 경우에 따라 | 예 (환경 변수 필요) | N/A | 예 |
|
||||
| **API 키 필요** | 도구별 | 서버별 | 통합 토큰 | 아니오 | 임베더만 |
|
||||
| **Agent에 설정** | `tools=[]` | `mcps=[]` | `apps=[]` | `skills=[]` | `knowledge_sources=[]` |
|
||||
| **Crew에 설정** | ❌ | ❌ | ❌ | `skills=[]` | `knowledge_sources=[]` |
|
||||
|
||||
---
|
||||
|
||||
## 상세 가이드
|
||||
|
||||
각 기능 유형에 대해 더 알아볼 준비가 되셨나요?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="도구" icon="wrench" href="/ko/concepts/tools">
|
||||
맞춤형 도구 생성, 75개 이상의 OSS 카탈로그 사용, 캐싱 및 비동기 실행 설정.
|
||||
</Card>
|
||||
<Card title="MCP 통합" icon="plug" href="/ko/mcp/overview">
|
||||
stdio, SSE 또는 HTTP를 통해 MCP 서버에 연결. 도구 필터링, 인증 설정.
|
||||
</Card>
|
||||
<Card title="스킬" icon="bolt" href="/ko/concepts/skills">
|
||||
SKILL.md로 스킬 패키지 구축, 도메인 전문성 주입, 점진적 공개 사용.
|
||||
</Card>
|
||||
<Card title="지식" icon="book" href="/ko/concepts/knowledge">
|
||||
PDF, CSV, URL 등에서 지식 추가. 임베더 및 검색 설정.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
232
docs/ko/concepts/checkpointing.mdx
Normal file
232
docs/ko/concepts/checkpointing.mdx
Normal file
@@ -0,0 +1,232 @@
|
||||
---
|
||||
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입니다: 체크포인트 기록이 실패하면 오류가 로그에 기록되지만 실행은 중단 없이 계속됩니다.
|
||||
@@ -1,27 +1,186 @@
|
||||
---
|
||||
title: 스킬
|
||||
description: 에이전트 프롬프트에 컨텍스트를 주입하는 파일 시스템 기반 스킬 패키지.
|
||||
description: 에이전트 프롬프트에 도메인 전문성과 지침을 주입하는 파일 시스템 기반 스킬 패키지.
|
||||
icon: bolt
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
스킬은 에이전트에게 도메인별 지침, 참조 자료, 에셋을 제공하는 자체 포함 디렉터리입니다. 각 스킬은 YAML 프론트매터와 마크다운 본문이 포함된 `SKILL.md` 파일로 정의됩니다.
|
||||
스킬은 에이전트에게 **도메인별 지침, 가이드라인 및 참조 자료**를 제공하는 자체 포함 디렉터리입니다. 각 스킬은 YAML 프론트매터와 마크다운 본문이 포함된 `SKILL.md` 파일로 정의됩니다.
|
||||
|
||||
스킬은 **점진적 공개**를 사용합니다 — 메타데이터가 먼저 로드되고, 활성화 시에만 전체 지침이 로드되며, 필요할 때만 리소스 카탈로그가 로드됩니다.
|
||||
활성화되면 스킬의 지침이 에이전트의 작업 프롬프트에 직접 주입됩니다 — 코드 변경 없이 에이전트에게 전문성을 부여합니다.
|
||||
|
||||
## 디렉터리 구조
|
||||
<Note type="info" title="스킬 vs 도구 — 핵심 구분">
|
||||
**스킬은 도구가 아닙니다.** 이것이 가장 흔한 혼동 포인트입니다.
|
||||
|
||||
- **스킬**은 에이전트의 프롬프트에 *지침과 컨텍스트*를 주입합니다. 에이전트에게 문제에 대해 *어떻게 생각할지*를 알려줍니다.
|
||||
- **도구**는 에이전트에게 행동을 취할 수 있는 *호출 가능한 함수*를 제공합니다 (검색, 파일 읽기, API 호출).
|
||||
|
||||
흔히 **둘 다** 필요합니다: 전문성을 위한 스킬과 행동을 위한 도구. 이들은 독립적으로 구성되며 서로 보완합니다.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 1. 스킬 디렉터리 생성
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # 필수 — 프론트매터 + 지침
|
||||
├── scripts/ # 선택 — 실행 가능한 스크립트
|
||||
├── references/ # 선택 — 참조 문서
|
||||
└── assets/ # 선택 — 정적 파일 (설정, 데이터)
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # 필수 — 지침
|
||||
├── references/ # 선택 — 참조 문서
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # 선택 — 실행 가능한 스크립트
|
||||
```
|
||||
|
||||
디렉터리 이름은 `SKILL.md`의 `name` 필드와 일치해야 합니다.
|
||||
### 2. SKILL.md 작성
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Guidelines for conducting thorough code reviews with focus on security and performance.
|
||||
metadata:
|
||||
author: your-team
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
## 코드 리뷰 가이드라인
|
||||
|
||||
코드를 리뷰할 때 이 체크리스트를 따르세요:
|
||||
|
||||
1. **보안**: 인젝션 취약점, 인증 우회, 데이터 노출 확인
|
||||
2. **성능**: N+1 쿼리, 불필요한 할당, 블로킹 호출 확인
|
||||
3. **가독성**: 명확한 네이밍, 적절한 주석, 일관된 스타일 보장
|
||||
4. **테스트**: 새로운 기능에 대한 적절한 테스트 커버리지 확인
|
||||
|
||||
### 심각도 수준
|
||||
- **크리티컬**: 보안 취약점, 데이터 손실 위험 → 머지 차단
|
||||
- **메이저**: 성능 문제, 로직 오류 → 변경 요청
|
||||
- **마이너**: 스타일 문제, 네이밍 제안 → 코멘트와 함께 승인
|
||||
```
|
||||
|
||||
### 3. 에이전트에 연결
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import GithubSearchTool, FileReadTool
|
||||
|
||||
reviewer = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["./skills"], # 리뷰 가이드라인 주입
|
||||
tools=[GithubSearchTool(), FileReadTool()], # 에이전트가 코드를 읽을 수 있게 함
|
||||
)
|
||||
```
|
||||
|
||||
이제 에이전트는 **전문성** (스킬에서)과 **기능** (도구에서) 모두를 갖추게 됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 스킬 + 도구: 함께 작동하기
|
||||
|
||||
스킬과 도구가 어떻게 보완하는지 보여주는 일반적인 패턴입니다:
|
||||
|
||||
### 패턴 1: 스킬만 (도메인 전문성, 액션 불필요)
|
||||
|
||||
에이전트가 특정 지침이 필요하지만 외부 서비스를 호출할 필요가 없을 때 사용:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Technical Writer",
|
||||
goal="Write clear API documentation",
|
||||
backstory="Expert technical writer",
|
||||
skills=["./skills/api-docs-style"], # 작성 가이드라인 및 템플릿
|
||||
# 도구 불필요 — 에이전트가 제공된 컨텍스트를 기반으로 작성
|
||||
)
|
||||
```
|
||||
|
||||
### 패턴 2: 도구만 (액션, 특별한 전문성 불필요)
|
||||
|
||||
에이전트가 행동을 취해야 하지만 도메인별 지침이 필요 없을 때 사용:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
|
||||
|
||||
agent = Agent(
|
||||
role="Web Researcher",
|
||||
goal="Find information about a topic",
|
||||
backstory="Skilled at finding information online",
|
||||
tools=[SerperDevTool(), ScrapeWebsiteTool()], # 검색 및 스크래핑 가능
|
||||
# 스킬 불필요 — 일반 연구에는 특별한 가이드라인이 필요 없음
|
||||
)
|
||||
```
|
||||
|
||||
### 패턴 3: 스킬 + 도구 (전문성 AND 액션)
|
||||
|
||||
가장 일반적인 실제 패턴. 스킬은 작업에 *어떻게* 접근할지를 제공하고, 도구는 에이전트가 *무엇을* 할 수 있는지를 제공합니다:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
analyst = Agent(
|
||||
role="Security Analyst",
|
||||
goal="Audit infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security and compliance",
|
||||
skills=["./skills/security-audit"], # 감사 방법론 및 체크리스트
|
||||
tools=[
|
||||
SerperDevTool(), # 알려진 취약점 조사
|
||||
FileReadTool(), # 설정 파일 읽기
|
||||
CodeInterpreterTool(), # 분석 스크립트 실행
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### 패턴 4: 스킬 + MCP
|
||||
|
||||
스킬은 도구와 마찬가지로 MCP 서버와 함께 작동합니다:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze customer data and generate reports",
|
||||
backstory="Expert data analyst with strong statistical background",
|
||||
skills=["./skills/data-analysis"], # 분석 방법론
|
||||
mcps=["https://data-warehouse.example.com/sse"], # 원격 데이터 접근
|
||||
)
|
||||
```
|
||||
|
||||
### 패턴 5: 스킬 + 앱
|
||||
|
||||
스킬은 에이전트가 플랫폼 통합을 사용하는 방법을 안내할 수 있습니다:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Customer Support Agent",
|
||||
goal="Respond to customer inquiries professionally",
|
||||
backstory="Experienced support representative",
|
||||
skills=["./skills/support-playbook"], # 응답 템플릿 및 에스컬레이션 규칙
|
||||
apps=["gmail", "zendesk"], # 이메일 전송 및 티켓 업데이트 가능
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 크루 레벨 스킬
|
||||
|
||||
스킬을 크루에 설정하여 **모든 에이전트**에 적용할 수 있습니다:
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
|
||||
crew = Crew(
|
||||
agents=[researcher, writer, reviewer],
|
||||
tasks=[research_task, write_task, review_task],
|
||||
skills=["./skills"], # 모든 에이전트가 이 스킬을 받음
|
||||
)
|
||||
```
|
||||
|
||||
에이전트 레벨 스킬이 우선합니다 — 동일한 스킬이 양쪽 레벨에서 발견되면 에이전트의 버전이 사용됩니다.
|
||||
|
||||
---
|
||||
|
||||
## SKILL.md 형식
|
||||
|
||||
@@ -34,7 +193,7 @@ compatibility: crewai>=0.1.0 # 선택
|
||||
metadata: # 선택
|
||||
author: your-name
|
||||
version: "1.0"
|
||||
allowed-tools: web-search file-read # 선택, 공백으로 구분
|
||||
allowed-tools: web-search file-read # 선택, 실험적
|
||||
---
|
||||
|
||||
에이전트를 위한 지침이 여기에 들어갑니다. 이 마크다운 본문은
|
||||
@@ -43,57 +202,46 @@ allowed-tools: web-search file-read # 선택, 공백으로 구분
|
||||
|
||||
### 프론트매터 필드
|
||||
|
||||
| 필드 | 필수 | 제약 조건 |
|
||||
| 필드 | 필수 | 설명 |
|
||||
| :-------------- | :----- | :----------------------------------------------------------------------- |
|
||||
| `name` | 예 | 1–64자. 소문자 영숫자와 하이픈. 선행/후행/연속 하이픈 불가. 디렉터리 이름과 일치 필수. |
|
||||
| `name` | 예 | 1–64자. 소문자 영숫자와 하이픈. 디렉터리 이름과 일치 필수. |
|
||||
| `description` | 예 | 1–1024자. 스킬이 무엇을 하고 언제 사용하는지 설명. |
|
||||
| `license` | 아니오 | 라이선스 이름 또는 번들된 라이선스 파일 참조. |
|
||||
| `compatibility` | 아니오 | 최대 500자. 환경 요구 사항 (제품, 패키지, 네트워크). |
|
||||
| `metadata` | 아니오 | 임의의 문자열 키-값 매핑. |
|
||||
| `allowed-tools` | 아니오 | 공백으로 구분된 사전 승인 도구 목록. 실험적. |
|
||||
|
||||
## 사용법
|
||||
---
|
||||
|
||||
### 에이전트 레벨 스킬
|
||||
## 디렉터리 구조
|
||||
|
||||
에이전트에 스킬 디렉터리 경로를 전달합니다:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
backstory="An expert researcher.",
|
||||
skills=["./skills"], # 이 디렉터리의 모든 스킬을 검색
|
||||
)
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # 필수 — 프론트매터 + 지침
|
||||
├── scripts/ # 선택 — 실행 가능한 스크립트
|
||||
├── references/ # 선택 — 참조 문서
|
||||
└── assets/ # 선택 — 정적 파일 (설정, 데이터)
|
||||
```
|
||||
|
||||
### 크루 레벨 스킬
|
||||
디렉터리 이름은 `SKILL.md`의 `name` 필드와 일치해야 합니다. `scripts/`, `references/`, `assets/` 디렉터리는 파일을 직접 참조해야 하는 에이전트를 위해 스킬의 `path`에서 사용할 수 있습니다.
|
||||
|
||||
크루의 스킬 경로는 모든 에이전트에 병합됩니다:
|
||||
---
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
## 사전 로드된 스킬
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
skills=["./skills"],
|
||||
)
|
||||
```
|
||||
|
||||
### 사전 로드된 스킬
|
||||
|
||||
`Skill` 객체를 직접 전달할 수도 있습니다:
|
||||
더 세밀한 제어를 위해 프로그래밍 방식으로 스킬을 검색하고 활성화할 수 있습니다:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from crewai.skills import discover_skills, activate_skill
|
||||
|
||||
# 디렉터리의 모든 스킬 검색
|
||||
skills = discover_skills(Path("./skills"))
|
||||
|
||||
# 활성화 (전체 SKILL.md 본문 로드)
|
||||
activated = [activate_skill(s) for s in skills]
|
||||
|
||||
# 에이전트에 전달
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
@@ -102,13 +250,57 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 스킬 로드 방식
|
||||
|
||||
스킬은 점진적으로 로드됩니다 — 각 단계에서 필요한 데이터만 읽습니다:
|
||||
스킬은 **점진적 공개**를 사용합니다 — 각 단계에서 필요한 것만 로드합니다:
|
||||
|
||||
| 단계 | 로드되는 내용 | 시점 |
|
||||
| :--------------- | :------------------------------------------------ | :----------------- |
|
||||
| 검색 | 이름, 설명, 프론트매터 필드 | `discover_skills()` |
|
||||
| 활성화 | 전체 SKILL.md 본문 텍스트 | `activate_skill()` |
|
||||
| 단계 | 로드되는 내용 | 시점 |
|
||||
| :------- | :------------------------------------ | :------------------ |
|
||||
| 검색 | 이름, 설명, 프론트매터 필드 | `discover_skills()` |
|
||||
| 활성화 | 전체 SKILL.md 본문 텍스트 | `activate_skill()` |
|
||||
|
||||
일반적인 에이전트 실행 중에 스킬은 자동으로 검색되고 활성화됩니다. `scripts/`, `references/`, `assets/` 디렉터리는 파일을 직접 참조해야 하는 에이전트를 위해 스킬의 `path`에서 사용할 수 있습니다.
|
||||
일반적인 에이전트 실행 중(`skills=["./skills"]`로 디렉터리 경로 전달 시) 스킬은 자동으로 검색되고 활성화됩니다. 점진적 로딩은 프로그래밍 API를 사용할 때만 관련됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 스킬 vs 지식
|
||||
|
||||
스킬과 지식 모두 에이전트의 프롬프트를 수정하지만, 서로 다른 목적을 가지고 있습니다:
|
||||
|
||||
| 측면 | 스킬 | 지식 |
|
||||
| :--- | :--- | :--- |
|
||||
| **제공하는 것** | 지침, 절차, 가이드라인 | 사실, 데이터, 정보 |
|
||||
| **저장 방식** | 마크다운 파일 (SKILL.md) | 벡터 스토어에 임베딩 (ChromaDB) |
|
||||
| **검색 방식** | 전체 본문이 프롬프트에 주입 | 시맨틱 검색으로 관련 청크 찾기 |
|
||||
| **적합한 용도** | 방법론, 체크리스트, 스타일 가이드 | 회사 문서, 제품 정보, 참조 데이터 |
|
||||
| **설정 방법** | `skills=["./skills"]` | `knowledge_sources=[source]` |
|
||||
|
||||
**경험 법칙:** 에이전트가 *프로세스*를 따라야 하면 스킬을 사용하세요. 에이전트가 *데이터*를 참조해야 하면 지식을 사용하세요.
|
||||
|
||||
---
|
||||
|
||||
## 자주 묻는 질문
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="스킬과 도구를 모두 설정해야 하나요?">
|
||||
사용 사례에 따라 다릅니다. 스킬과 도구는 **독립적**입니다 — 둘 중 하나, 둘 다, 또는 아무것도 사용하지 않을 수 있습니다.
|
||||
|
||||
- **스킬만**: 에이전트가 전문성은 필요하지만 외부 액션이 필요 없을 때 (예: 스타일 가이드라인으로 작성)
|
||||
- **도구만**: 에이전트가 액션은 필요하지만 특별한 방법론이 필요 없을 때 (예: 간단한 웹 검색)
|
||||
- **둘 다**: 에이전트가 전문성 AND 액션이 필요할 때 (예: 특정 체크리스트로 보안 감사 AND 코드 스캔 기능)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="스킬이 자동으로 도구를 제공하나요?">
|
||||
**아니요.** SKILL.md의 `allowed-tools` 필드는 실험적 메타데이터일 뿐 — 도구를 프로비저닝하거나 주입하지 않습니다. 항상 `tools=[]`, `mcps=[]` 또는 `apps=[]`를 통해 별도로 도구를 설정해야 합니다.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="에이전트와 크루 모두에 같은 스킬을 설정하면 어떻게 되나요?">
|
||||
에이전트 레벨 스킬이 우선합니다. 스킬은 이름으로 중복 제거됩니다 — 에이전트의 스킬이 먼저 처리되므로, 같은 스킬 이름이 양쪽 레벨에 나타나면 에이전트의 버전이 사용됩니다.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="SKILL.md 본문의 최대 크기는 얼마인가요?">
|
||||
50,000자에서 소프트 경고가 있지만 하드 리밋은 없습니다. 최상의 결과를 위해 스킬을 집중적이고 간결하게 유지하세요 — 너무 큰 프롬프트 주입은 에이전트의 주의를 분산시킬 수 있습니다.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -10,6 +10,10 @@ mode: "wide"
|
||||
CrewAI 도구는 에이전트에게 웹 검색, 데이터 분석부터 동료 간 협업 및 작업 위임에 이르기까지 다양한 기능을 제공합니다.
|
||||
이 문서에서는 CrewAI 프레임워크 내에서 이러한 도구를 생성, 통합 및 활용하는 방법과, 협업 도구에 초점을 맞춘 새로운 기능에 대해 설명합니다.
|
||||
|
||||
<Note type="info" title="도구는 다섯 가지 에이전트 기능 유형 중 하나입니다">
|
||||
도구는 에이전트에게 행동을 취할 수 있는 **호출 가능한 함수**를 제공합니다. [MCP](/ko/mcp/overview) (원격 도구 서버), [앱](/ko/concepts/agent-capabilities) (플랫폼 통합), [스킬](/ko/concepts/skills) (도메인 전문성), [지식](/ko/concepts/knowledge) (검색된 사실)과 함께 작동합니다. 각 유형을 언제 사용해야 하는지 알아보려면 [에이전트 기능](/ko/concepts/agent-capabilities) 개요를 참조하세요.
|
||||
</Note>
|
||||
|
||||
## Tool이란 무엇인가?
|
||||
|
||||
CrewAI에서 tool은 에이전트가 다양한 작업을 수행하기 위해 활용할 수 있는 기술 또는 기능입니다.
|
||||
|
||||
@@ -1,108 +1,260 @@
|
||||
---
|
||||
title: "역할 기반 접근 제어 (RBAC)"
|
||||
description: "역할과 자동화별 가시성으로 crews, 도구, 데이터 접근을 제어합니다."
|
||||
description: "역할, 범위, 세분화된 권한으로 crews, 도구, 데이터 접근을 제어합니다."
|
||||
icon: "shield"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
CrewAI AOP의 RBAC는 **조직 수준 역할**과 **자동화(Automation) 수준 가시성**을 결합하여 안전하고 확장 가능한 접근 제어를 제공합니다.
|
||||
CrewAI AMP의 RBAC는 두 가지 계층을 통해 안전하고 확장 가능한 접근 관리를 제공합니다:
|
||||
|
||||
1. **기능 권한** — 플랫폼 전반에서 각 역할이 수행할 수 있는 작업을 제어합니다 (관리, 읽기 또는 접근 불가)
|
||||
2. **엔티티 수준 권한** — 개별 자동화, 환경 변수, LLM 연결, Git 저장소에 대한 세분화된 접근 제어
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/users_and_roles.png" alt="CrewAI AMP RBAC 개요" />
|
||||
|
||||
</Frame>
|
||||
|
||||
## 사용자와 역할
|
||||
|
||||
워크스페이스의 각 구성원은 역할이 있으며, 이는 기능 접근 범위를 결정합니다.
|
||||
CrewAI 워크스페이스의 각 구성원에게는 역할이 할당되며, 이를 통해 다양한 기능에 대한 접근 범위가 결정됩니다.
|
||||
|
||||
가능한 작업:
|
||||
|
||||
- 사전 정의된 역할 사용 (Owner, Member)
|
||||
- 권한을 세분화한 커스텀 역할 생성
|
||||
- 설정 화면에서 언제든 역할 할당/변경
|
||||
- 특정 권한에 맞춘 커스텀 역할 생성
|
||||
- 설정 패널에서 언제든지 역할 할당
|
||||
|
||||
설정 위치: Settings → Roles
|
||||
|
||||
<Steps>
|
||||
<Step title="Roles 열기">
|
||||
<b>Settings → Roles</b>로 이동합니다.
|
||||
<Step title="Roles 설정 열기">
|
||||
CrewAI AMP에서 <b>Settings → Roles</b>로 이동합니다.
|
||||
</Step>
|
||||
<Step title="역할 선택">
|
||||
<b>Owner</b> 또는 <b>Member</b>를 사용하거나 <b>Create role</b>로 커스텀
|
||||
역할을 만듭니다.
|
||||
<Step title="역할 유형 선택">
|
||||
사전 정의된 역할(<b>Owner</b>, <b>Member</b>)을 사용하거나{" "}
|
||||
<b>Create role</b>을 클릭하여 커스텀 역할을 만듭니다.
|
||||
</Step>
|
||||
<Step title="멤버에 할당">
|
||||
사용자들을 선택하여 역할을 지정합니다. 언제든 변경할 수 있습니다.
|
||||
사용자를 선택하고 역할을 할당합니다. 언제든지 변경할 수 있습니다.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### 사전 정의된 역할
|
||||
|
||||
| 역할 | 설명 |
|
||||
| :--------- | :------------------------------------------------------------------- |
|
||||
| **Owner** | 모든 기능 및 설정에 대한 전체 접근 권한. 제한할 수 없습니다. |
|
||||
| **Member** | 대부분의 기능에 대한 읽기 접근, 환경 변수, LLM 연결, Studio 프로젝트에 대한 관리 접근. 조직 설정이나 기본 설정은 수정할 수 없습니다. |
|
||||
|
||||
### 구성 요약
|
||||
|
||||
| 영역 | 위치 | 옵션 |
|
||||
| 영역 | 설정 위치 | 옵션 |
|
||||
| :------------ | :--------------------------------- | :-------------------------------- |
|
||||
| 사용자 & 역할 | Settings → Roles | Owner, Member; 커스텀 역할 |
|
||||
| 사용자 & 역할 | Settings → Roles | 사전 정의: Owner, Member; 커스텀 역할 |
|
||||
| 자동화 가시성 | Automation → Settings → Visibility | Private; 사용자/역할 화이트리스트 |
|
||||
|
||||
## 자동화 수준 접근 제어
|
||||
---
|
||||
|
||||
조직 역할과 별개로, **Automations**는 사용자/역할별로 특정 자동화 접근을 제한하는 가시성 설정을 제공합니다.
|
||||
## 기능 권한 매트릭스
|
||||
|
||||
유용한 경우:
|
||||
각 역할에는 기능 영역별 권한 수준이 있습니다. 세 가지 수준은 다음과 같습니다:
|
||||
|
||||
- 민감/실험 자동화를 비공개로 유지
|
||||
- 대규모 팀/외부 협업에서 가시성 관리
|
||||
- **Manage** — 전체 읽기/쓰기 접근 (생성, 편집, 삭제)
|
||||
- **Read** — 읽기 전용 접근
|
||||
- **No access** — 기능이 숨겨지거나 접근 불가
|
||||
|
||||
| 기능 | Owner | Member (기본값) | 사용 가능한 수준 | 설명 |
|
||||
| :-------------------------- | :------ | :--------------- | :------------------------- | :------------------------------------------------------------- |
|
||||
| `usage_dashboards` | Manage | Read | Manage / Read / No access | 사용 메트릭 및 분석 보기 |
|
||||
| `crews_dashboards` | Manage | Read | Manage / Read / No access | 배포 대시보드 보기, 자동화 세부 정보 접근 |
|
||||
| `invitations` | Manage | Read | Manage / Read / No access | 조직에 새 멤버 초대 |
|
||||
| `training_ui` | Manage | Read | Manage / Read / No access | 훈련/파인튜닝 인터페이스 접근 |
|
||||
| `tools` | Manage | Read | Manage / Read / No access | 도구 생성 및 관리 |
|
||||
| `agents` | Manage | Read | Manage / Read / No access | 에이전트 생성 및 관리 |
|
||||
| `environment_variables` | Manage | Manage | Manage / No access | 환경 변수 생성 및 관리 |
|
||||
| `llm_connections` | Manage | Manage | Manage / No access | LLM 제공자 연결 구성 |
|
||||
| `default_settings` | Manage | No access | Manage / No access | 조직 전체 기본 설정 수정 |
|
||||
| `organization_settings` | Manage | No access | Manage / No access | 결제, 플랜 및 조직 구성 관리 |
|
||||
| `studio_projects` | Manage | Manage | Manage / No access | Studio에서 프로젝트 생성 및 편집 |
|
||||
|
||||
<Tip>
|
||||
커스텀 역할을 만들 때 대부분의 기능은 **Manage**, **Read** 또는 **No access**로 설정할 수 있습니다. 그러나 `environment_variables`, `llm_connections`, `default_settings`, `organization_settings`, `studio_projects`는 **Manage** 또는 **No access**만 지원합니다 — 이 기능들에는 읽기 전용 옵션이 없습니다.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## GitHub 또는 Zip에서 배포
|
||||
|
||||
가장 흔한 RBAC 질문 중 하나: _"팀원이 배포하려면 어떤 권한이 필요한가요?"_
|
||||
|
||||
### GitHub에서 배포
|
||||
|
||||
GitHub 저장소에서 자동화를 배포하려면 사용자에게 다음이 필요합니다:
|
||||
|
||||
1. **`crews_dashboards`**: 최소 `Read` — 배포가 생성되는 자동화 대시보드에 접근하는 데 필요
|
||||
2. **Git 저장소 접근** (Git 저장소에 대한 엔티티 수준 RBAC가 활성화된 경우): 사용자의 역할에 엔티티 수준 권한을 통해 특정 Git 저장소에 대한 접근이 부여되어야 함
|
||||
3. **`studio_projects`: `Manage`** — 배포 전에 Studio에서 crew를 빌드하는 경우
|
||||
|
||||
### Zip에서 배포
|
||||
|
||||
Zip 파일 업로드로 자동화를 배포하려면 사용자에게 다음이 필요합니다:
|
||||
|
||||
1. **`crews_dashboards`**: 최소 `Read` — 자동화 대시보드에 접근하는 데 필요
|
||||
2. **Zip 배포 활성화**: 조직이 조직 설정에서 Zip 배포를 비활성화하지 않아야 함
|
||||
|
||||
### 빠른 참조: 배포에 필요한 최소 권한
|
||||
|
||||
| 작업 | 필요한 기능 권한 | 추가 요구사항 |
|
||||
| :------------------- | :----------------------------------- | :----------------------------------------------- |
|
||||
| GitHub에서 배포 | `crews_dashboards: Read` | Git 저장소 엔티티 접근 (Git RBAC 활성화 시) |
|
||||
| Zip에서 배포 | `crews_dashboards: Read` | 조직 수준에서 Zip 배포가 활성화되어야 함 |
|
||||
| Studio에서 빌드 | `studio_projects: Manage` | — |
|
||||
| LLM 키 구성 | `llm_connections: Manage` | — |
|
||||
| 환경 변수 설정 | `environment_variables: Manage` | 엔티티 수준 접근 (엔티티 RBAC 활성화 시) |
|
||||
|
||||
---
|
||||
|
||||
## 자동화 수준 접근 제어 (엔티티 권한)
|
||||
|
||||
조직 전체 역할 외에도, CrewAI는 개별 리소스에 대한 접근을 제한하는 세분화된 엔티티 수준 권한을 지원합니다.
|
||||
|
||||
### 자동화 가시성
|
||||
|
||||
자동화는 사용자 또는 역할별로 접근을 제한하는 가시성 설정을 지원합니다. 다음과 같은 경우에 유용합니다:
|
||||
|
||||
- 민감하거나 실험적인 자동화를 비공개로 유지
|
||||
- 대규모 팀이나 외부 협업자의 가시성 관리
|
||||
- 격리된 컨텍스트에서 자동화 테스트
|
||||
|
||||
Private 모드에서는 화이트리스트에 포함된 사용자/역할만 다음 작업이 가능합니다:
|
||||
배포를 비공개로 구성할 수 있으며, 이 경우 화이트리스트에 포함된 사용자와 역할만 상호작용할 수 있습니다.
|
||||
|
||||
- 자동화 보기
|
||||
- 실행/API 사용
|
||||
- 로그, 메트릭, 설정 접근
|
||||
|
||||
조직 Owner는 항상 접근 가능하며, 가시성 설정에 영향을 받지 않습니다.
|
||||
|
||||
설정 위치: Automation → Settings → Visibility
|
||||
설정 위치: Automation → Settings → Visibility 탭
|
||||
|
||||
<Steps>
|
||||
<Step title="Visibility 탭 열기">
|
||||
<b>Automation → Settings → Visibility</b>로 이동합니다.
|
||||
</Step>
|
||||
<Step title="가시성 설정">
|
||||
<b>Private</b>를 선택합니다. Owner는 항상 접근 가능합니다.
|
||||
접근을 제한하려면 <b>Private</b>를 선택합니다. 조직 Owner는 항상
|
||||
접근 권한을 유지합니다.
|
||||
</Step>
|
||||
<Step title="허용 대상 추가">
|
||||
보기/실행/로그·메트릭·설정 접근이 가능한 사용자/역할을 추가합니다.
|
||||
<Step title="접근 허용 대상 추가">
|
||||
보기, 실행, 로그/메트릭/설정 접근이 허용된 특정 사용자와 역할을
|
||||
추가합니다.
|
||||
</Step>
|
||||
<Step title="저장 및 확인">
|
||||
저장 후, 목록에 없는 사용자가 보거나 실행할 수 없는지 확인합니다.
|
||||
변경 사항을 저장한 후, 화이트리스트에 없는 사용자가 자동화를 보거나 실행할 수
|
||||
없는지 확인합니다.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Private 모드 접근 결과
|
||||
### Private 가시성: 접근 결과
|
||||
|
||||
| 동작 | Owner | 화이트리스트 사용자/역할 | 비포함 |
|
||||
| :--------------- | :---- | :----------------------- | :----- |
|
||||
| 자동화 보기 | ✓ | ✓ | ✗ |
|
||||
| 실행/API | ✓ | ✓ | ✗ |
|
||||
| 로그/메트릭/설정 | ✓ | ✓ | ✗ |
|
||||
| 동작 | Owner | 화이트리스트 사용자/역할 | 비포함 |
|
||||
| :--------------------- | :---- | :----------------------- | :----- |
|
||||
| 자동화 보기 | ✓ | ✓ | ✗ |
|
||||
| 자동화/API 실행 | ✓ | ✓ | ✗ |
|
||||
| 로그/메트릭/설정 접근 | ✓ | ✓ | ✗ |
|
||||
|
||||
<Tip>
|
||||
Owner는 항상 접근 가능하며, Private 모드에서는 화이트리스트에 포함된
|
||||
사용자/역할만 권한이 부여됩니다.
|
||||
조직 Owner는 항상 접근 권한이 있습니다. Private 모드에서는 화이트리스트에 포함된
|
||||
사용자/역할만 보기, 실행, 로그/메트릭/설정에 접근할 수 있습니다.
|
||||
</Tip>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/visibility.png" alt="CrewAI AMP 가시성 설정" />
|
||||
|
||||
<img src="/images/enterprise/visibility.png" alt="CrewAI AMP 자동화 가시성 설정" />
|
||||
</Frame>
|
||||
|
||||
### 배포 권한 유형
|
||||
|
||||
특정 자동화에 엔티티 수준 접근을 부여할 때 다음 권한 유형을 할당할 수 있습니다:
|
||||
|
||||
| 권한 | 허용 범위 |
|
||||
| :------------------- | :-------------------------------------------------- |
|
||||
| `run` | 자동화 실행 및 API 사용 |
|
||||
| `traces` | 실행 트레이스 및 로그 보기 |
|
||||
| `manage_settings` | 자동화 편집, 재배포, 롤백 또는 삭제 |
|
||||
| `human_in_the_loop` | HITL(human-in-the-loop) 요청에 응답 |
|
||||
| `full_access` | 위의 모든 권한 |
|
||||
|
||||
### 기타 리소스에 대한 엔티티 수준 RBAC
|
||||
|
||||
엔티티 수준 RBAC가 활성화되면 다음 리소스에 대한 접근도 사용자 또는 역할별로 제어할 수 있습니다:
|
||||
|
||||
| 리소스 | 제어 방식 | 설명 |
|
||||
| :----------------- | :---------------------------------- | :------------------------------------------------------------ |
|
||||
| 환경 변수 | 엔티티 RBAC 기능 플래그 | 특정 환경 변수를 보거나 관리할 수 있는 역할/사용자 제한 |
|
||||
| LLM 연결 | 엔티티 RBAC 기능 플래그 | 특정 LLM 제공자 구성에 대한 접근 제한 |
|
||||
| Git 저장소 | Git 저장소 RBAC 조직 설정 | 특정 연결된 저장소에 접근할 수 있는 역할/사용자 제한 |
|
||||
|
||||
---
|
||||
|
||||
## 일반적인 역할 패턴
|
||||
|
||||
CrewAI는 Owner와 Member 역할을 기본 제공하지만, 대부분의 팀은 커스텀 역할을 만들어 활용합니다. 일반적인 패턴은 다음과 같습니다:
|
||||
|
||||
### Developer 역할
|
||||
|
||||
자동화를 빌드하고 배포하지만 조직 설정을 관리하지 않는 팀원을 위한 역할입니다.
|
||||
|
||||
| 기능 | 권한 |
|
||||
| :-------------------------- | :---------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Read |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Manage |
|
||||
| `agents` | Manage |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | Manage |
|
||||
|
||||
### Viewer / Stakeholder 역할
|
||||
|
||||
자동화를 모니터링하고 결과를 확인해야 하는 비기술 이해관계자를 위한 역할입니다.
|
||||
|
||||
| 기능 | 권한 |
|
||||
| :-------------------------- | :---------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Read |
|
||||
| `invitations` | No access |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | No access |
|
||||
| `llm_connections` | No access |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
### Ops / Platform Admin 역할
|
||||
|
||||
인프라 설정을 관리하지만 에이전트를 빌드하지 않을 수 있는 플랫폼 운영자를 위한 역할입니다.
|
||||
|
||||
| 기능 | 권한 |
|
||||
| :-------------------------- | :---------- |
|
||||
| `usage_dashboards` | Manage |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Manage |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | Manage |
|
||||
| `organization_settings` | Read |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
---
|
||||
|
||||
<Card
|
||||
title="도움이 필요하신가요?"
|
||||
icon="headset"
|
||||
href="mailto:support@crewai.com"
|
||||
>
|
||||
RBAC 구성과 점검에 대한 지원이 필요하면 연락해 주세요.
|
||||
RBAC 관련 질문은 지원팀에 문의해 주세요.
|
||||
</Card>
|
||||
|
||||
@@ -105,7 +105,7 @@ CLI는 `pyproject.toml`에서 프로젝트 유형을 자동으로 감지하고
|
||||
```
|
||||
|
||||
<Tip>
|
||||
첫 배포는 컨테이너 이미지를 빌드하므로 일반적으로 10~15분 정도 소요됩니다. 이후 배포는 훨씬 빠릅니다.
|
||||
첫 배포는 보통 약 1분 정도 소요됩니다.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
@@ -187,7 +187,7 @@ Crew를 GitHub 저장소에 푸시해야 합니다. 아직 Crew를 만들지 않
|
||||
|
||||
1. "Deploy" 버튼을 클릭하여 배포 프로세스를 시작합니다.
|
||||
2. 진행 바를 통해 진행 상황을 모니터링할 수 있습니다.
|
||||
3. 첫 번째 배포에는 일반적으로 약 10-15분 정도 소요되며, 이후 배포는 더 빠릅니다.
|
||||
3. 첫 번째 배포에는 일반적으로 약 1분 정도 소요됩니다
|
||||
|
||||
<Frame>
|
||||

|
||||
|
||||
132
docs/ko/enterprise/guides/training-crews.mdx
Normal file
132
docs/ko/enterprise/guides/training-crews.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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>
|
||||
@@ -5,6 +5,14 @@ icon: wrench
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
### 영상: 코딩 에이전트 스킬을 활용한 CrewAI Agents & Flows 구축
|
||||
|
||||
코딩 에이전트 스킬(Claude Code, Codex 등)을 설치하여 CrewAI로 코딩 에이전트를 빠르게 시작하세요.
|
||||
|
||||
`npx skills add crewaiinc/skills` 명령어로 설치할 수 있습니다
|
||||
|
||||
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
|
||||
|
||||
## 비디오 튜토리얼
|
||||
|
||||
설치 과정을 단계별로 시연하는 비디오 튜토리얼을 시청하세요:
|
||||
@@ -189,9 +197,8 @@ CrewAI는 의존성 관리와 패키지 처리를 위해 `uv`를 사용합니다
|
||||
## 다음 단계
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="첫 번째 Agent 만들기" icon="code" href="/ko/quickstart">
|
||||
빠른 시작 가이드를 따라 CrewAI 에이전트를 처음 만들어보고 직접 경험해
|
||||
보세요.
|
||||
<Card title="퀵스타트: Flow + 에이전트" icon="code" href="/ko/quickstart">
|
||||
Flow를 만들고 에이전트 한 명짜리 crew를 실행해 보고서까지 만드는 방법을 따라 해 보세요.
|
||||
</Card>
|
||||
<Card
|
||||
title="커뮤니티 참여하기"
|
||||
|
||||
@@ -16,6 +16,14 @@ mode: "wide"
|
||||
|
||||
10만 명이 넘는 개발자가 커뮤니티 과정을 통해 인증을 받았으며, CrewAI는 기업용 AI 자동화의 표준입니다.
|
||||
|
||||
### 영상: 코딩 에이전트 스킬을 활용한 CrewAI Agents & Flows 구축
|
||||
|
||||
코딩 에이전트 스킬(Claude Code, Codex 등)을 설치하여 CrewAI로 코딩 에이전트를 빠르게 시작하세요.
|
||||
|
||||
`npx skills add crewaiinc/skills` 명령어로 설치할 수 있습니다
|
||||
|
||||
<iframe src="https://www.loom.com/embed/befb9f68b81f42ad8112bfdd95a780af" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen style={{width: "100%", height: "400px"}}></iframe>
|
||||
|
||||
## CrewAI 아키텍처
|
||||
|
||||
CrewAI의 아키텍처는 자율성과 제어의 균형을 맞추도록 설계되었습니다.
|
||||
@@ -130,9 +138,9 @@ Crews의 기능:
|
||||
<Card
|
||||
title="빠른 시작"
|
||||
icon="bolt"
|
||||
href="ko/quickstart"
|
||||
href="/ko/quickstart"
|
||||
>
|
||||
빠른 시작 가이드를 따라 첫 번째 CrewAI agent를 만들고 직접 경험해 보세요.
|
||||
Flow를 만들고 에이전트 한 명 crew를 실행해 끝까지 보고서를 생성해 보세요.
|
||||
</Card>
|
||||
<Card
|
||||
title="커뮤니티 가입하기"
|
||||
|
||||
@@ -1,379 +1,278 @@
|
||||
---
|
||||
title: 퀵스타트
|
||||
description: 5분 이내에 CrewAI로 첫 번째 AI 에이전트를 구축해보세요.
|
||||
description: 몇 분 안에 첫 CrewAI Flow를 만듭니다 — 오케스트레이션, 상태, 그리고 실제 보고서를 만드는 에이전트 crew까지.
|
||||
icon: rocket
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 첫 번째 CrewAI Agent 만들기
|
||||
### 영상: 코딩 에이전트 스킬을 활용한 CrewAI Agents & Flows 구축
|
||||
|
||||
이제 주어진 주제나 항목에 대해 `최신 AI 개발 동향`을 `연구`하고 `보고`하는 간단한 crew를 만들어보겠습니다.
|
||||
코딩 에이전트 스킬(Claude Code, Codex 등)을 설치하여 CrewAI로 코딩 에이전트를 빠르게 시작하세요.
|
||||
|
||||
진행하기 전에 CrewAI 설치를 완료했는지 확인하세요.
|
||||
아직 설치하지 않았다면, [설치 가이드](/ko/installation)를 참고해 설치할 수 있습니다.
|
||||
`npx skills add crewaiinc/skills` 명령어로 설치할 수 있습니다
|
||||
|
||||
아래 단계를 따라 Crewing을 시작하세요! 🚣♂️
|
||||
<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를 아직 설치하지 않았다면 먼저 [설치 가이드](/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 만들기
|
||||
|
||||
<Steps>
|
||||
<Step title="crew 생성하기">
|
||||
터미널에서 아래 명령어를 실행하여 새로운 crew 프로젝트를 만드세요.
|
||||
이 작업은 `latest-ai-development`라는 새 디렉터리와 기본 구조를 생성합니다.
|
||||
<Step title="Flow 프로젝트 생성">
|
||||
터미널에서 Flow 프로젝트를 생성합니다(폴더 이름은 밑줄 형식입니다. 예: `latest_ai_flow`).
|
||||
|
||||
<CodeGroup>
|
||||
```shell Terminal
|
||||
crewai create crew latest-ai-development
|
||||
crewai create flow latest-ai-flow
|
||||
cd latest_ai_flow
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
이렇게 하면 `src/latest_ai_flow/` 아래에 Flow 앱이 만들어지고, 다음 단계에서 **단일 에이전트** 연구 crew로 바꿀 시작용 crew가 `crews/content_crew/`에 포함됩니다.
|
||||
</Step>
|
||||
<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>
|
||||
|
||||
<Step title="`agents.yaml`에 에이전트 하나 설정">
|
||||
`src/latest_ai_flow/crews/content_crew/config/agents.yaml` 내용을 한 명의 연구원만 남기도록 바꿉니다. `{topic}` 같은 변수는 `crew.kickoff(inputs=...)`로 채워집니다.
|
||||
|
||||
```yaml agents.yaml
|
||||
# src/latest_ai_development/config/agents.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
|
||||
researcher:
|
||||
role: >
|
||||
{topic} Senior Data Researcher
|
||||
{topic} 시니어 데이터 리서처
|
||||
goal: >
|
||||
Uncover cutting-edge developments in {topic}
|
||||
{topic} 분야의 최신 동향을 파악한다
|
||||
backstory: >
|
||||
You're a seasoned researcher with a knack for uncovering the latest
|
||||
developments in {topic}. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.
|
||||
|
||||
reporting_analyst:
|
||||
role: >
|
||||
{topic} Reporting Analyst
|
||||
goal: >
|
||||
Create detailed reports based on {topic} data analysis and research findings
|
||||
backstory: >
|
||||
You're a meticulous analyst with a keen eye for detail. You're known for
|
||||
your ability to turn complex data into clear and concise reports, making
|
||||
it easy for others to understand and act on the information you provide.
|
||||
당신은 {topic}의 최신 흐름을 찾아내는 데 능숙한 연구원입니다.
|
||||
가장 관련성 높은 정보를 찾아 명확하게 전달합니다.
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="`tasks.yaml` 파일 수정하기">
|
||||
|
||||
<Step title="`tasks.yaml`에 작업 하나 설정">
|
||||
```yaml tasks.yaml
|
||||
# src/latest_ai_development/config/tasks.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
|
||||
research_task:
|
||||
description: >
|
||||
Conduct a thorough research about {topic}
|
||||
Make sure you find any interesting and relevant information given
|
||||
the current year is 2025.
|
||||
{topic}에 대해 철저히 조사하세요. 웹 검색으로 최신이고 신뢰할 수 있는 정보를 찾으세요.
|
||||
현재 연도는 2026년입니다.
|
||||
expected_output: >
|
||||
A list with 10 bullet points of the most relevant information about {topic}
|
||||
마크다운 보고서로, 주요 트렌드·주목할 도구나 기업·시사점 등으로 섹션을 나누세요.
|
||||
분량은 약 800~1200단어. 문서 전체를 코드 펜스로 감싸지 마세요.
|
||||
agent: researcher
|
||||
|
||||
reporting_task:
|
||||
description: >
|
||||
Review the context you got and expand each topic into a full section for a report.
|
||||
Make sure the report is detailed and contains any and all relevant information.
|
||||
expected_output: >
|
||||
A fully fledge reports with the mains topics, each with a full section of information.
|
||||
Formatted as markdown without '```'
|
||||
agent: reporting_analyst
|
||||
output_file: report.md
|
||||
output_file: output/report.md
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="`crew.py` 파일 수정하기">
|
||||
```python crew.py
|
||||
# src/latest_ai_development/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai_tools import SerperDevTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
<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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
class ResearchCrew:
|
||||
"""Flow 안에서 사용하는 단일 에이전트 연구 crew."""
|
||||
|
||||
agents: List[BaseAgent]
|
||||
tasks: List[Task]
|
||||
|
||||
agents_config = "config/agents.yaml"
|
||||
tasks_config = "config/tasks.yaml"
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['researcher'], # type: ignore[index]
|
||||
config=self.agents_config["researcher"], # type: ignore[index]
|
||||
verbose=True,
|
||||
tools=[SerperDevTool()]
|
||||
)
|
||||
|
||||
@agent
|
||||
def reporting_analyst(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['reporting_analyst'], # type: ignore[index]
|
||||
verbose=True
|
||||
tools=[SerperDevTool()],
|
||||
)
|
||||
|
||||
@task
|
||||
def research_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['research_task'], # type: ignore[index]
|
||||
)
|
||||
|
||||
@task
|
||||
def reporting_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['reporting_task'], # type: ignore[index]
|
||||
output_file='output/report.md' # This is the file that will be contain the final report.
|
||||
config=self.tasks_config["research_task"], # type: ignore[index]
|
||||
)
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
"""Creates the LatestAiDevelopment crew"""
|
||||
return Crew(
|
||||
agents=self.agents, # Automatically created by the @agent decorator
|
||||
tasks=self.tasks, # Automatically created by the @task decorator
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
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
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
<Step title="`main.py`에서 Flow 정의">
|
||||
crew를 Flow에 연결합니다: `@start()` 단계에서 주제를 **상태**에 넣고, `@listen` 단계에서 crew를 실행합니다. 작업의 `output_file`은 그대로 `output/report.md`에 씁니다.
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
|
||||
# ... remaining code
|
||||
```
|
||||
|
||||
</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
|
||||
# src/latest_ai_flow/main.py
|
||||
from pydantic import BaseModel
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run the crew.
|
||||
"""
|
||||
inputs = {
|
||||
'topic': 'AI Agents'
|
||||
}
|
||||
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
|
||||
from crewai.flow import Flow, listen, start
|
||||
|
||||
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()
|
||||
```
|
||||
|
||||
</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>
|
||||
<Tip>
|
||||
패키지 이름이 `latest_ai_flow`가 아니면 `ResearchCrew` import 경로를 프로젝트 모듈 경로에 맞게 바꾸세요.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="엔터프라이즈 대안: Crew Studio에서 생성">
|
||||
CrewAI AMP 사용자는 코드를 작성하지 않고도 동일한 crew를 생성할 수 있습니다:
|
||||
<Step title="환경 변수">
|
||||
프로젝트 루트의 `.env`에 다음을 설정합니다.
|
||||
|
||||
1. CrewAI AMP 계정에 로그인하세요([app.crewai.com](https://app.crewai.com)에서 무료 계정 만들기)
|
||||
2. Crew Studio 열기
|
||||
3. 구현하려는 자동화 내용을 입력하세요
|
||||
4. 미션을 시각적으로 생성하고 순차적으로 연결하세요
|
||||
5. 입력값을 구성하고 "Download Code" 또는 "Deploy"를 클릭하세요
|
||||
|
||||

|
||||
|
||||
<Card title="CrewAI AMP 체험하기" icon="rocket" href="https://app.crewai.com">
|
||||
CrewAI AOP에서 무료 계정을 시작하세요
|
||||
</Card>
|
||||
- `SERPER_API_KEY` — [Serper.dev](https://serper.dev/)에서 발급
|
||||
- 모델 제공자 키 — [LLM 설정](/ko/concepts/llms#setting-up-your-llm) 참고
|
||||
</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
|
||||
# Comprehensive Report on the Rise and Impact of AI Agents in 2025
|
||||
# 2026년 AI 에이전트: 동향과 전망
|
||||
|
||||
## 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>
|
||||
|
||||
실제 파일은 더 길고 실시간 검색 결과를 반영합니다.
|
||||
</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 프로젝트 설정이 완료되었으며, 이제 자신만의 agentic workflow 구축을 바로 시작하실 수 있습니다!
|
||||
|
||||
에이전트 crew와 저장된 보고서까지 이어진 Flow를 완성했습니다. 이제 단계·crew·도구를 더해 확장할 수 있습니다.
|
||||
</Check>
|
||||
|
||||
### 명명 일관성에 대한 참고
|
||||
### 이름 일치
|
||||
|
||||
YAML 파일(`agents.yaml` 및 `tasks.yaml`)에서 사용하는 이름은 Python 코드의 메서드 이름과 일치해야 합니다.
|
||||
예를 들어, 특정 task에 대한 agent를 `tasks.yaml` 파일에서 참조할 수 있습니다.
|
||||
이러한 명명 일관성을 지키면 CrewAI가 설정과 코드를 자동으로 연결할 수 있습니다. 그렇지 않으면 task가 참조를 제대로 인식하지 못할 수 있습니다.
|
||||
YAML 키(`researcher`, `research_task`)는 `@CrewBase` 클래스의 메서드 이름과 같아야 합니다. 전체 데코레이터 패턴은 [Crews](/ko/concepts/crews)를 참고하세요.
|
||||
|
||||
#### 예시 참조
|
||||
## 배포
|
||||
|
||||
<Tip>
|
||||
`agents.yaml` (`email_summarizer`) 파일에서 에이전트 이름과 `crew.py`
|
||||
(`email_summarizer`) 파일에서 메서드 이름이 동일하게 사용되는 점에 주목하세요.
|
||||
</Tip>
|
||||
로컬에서 정상 실행되고 프로젝트가 **GitHub** 저장소에 있으면 Flow를 **[CrewAI AMP](https://app.crewai.com)**에 올릴 수 있습니다. 프로젝트 루트에서:
|
||||
|
||||
```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
|
||||
<CodeGroup>
|
||||
```bash 인증
|
||||
crewai login
|
||||
```
|
||||
|
||||
<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
|
||||
```bash 배포 생성
|
||||
crewai deploy create
|
||||
```
|
||||
|
||||
## Crew 배포하기
|
||||
```bash 상태 및 로그
|
||||
crewai deploy status
|
||||
crewai deploy logs
|
||||
```
|
||||
|
||||
production 환경에 crew를 배포하는 가장 쉬운 방법은 [CrewAI AMP](http://app.crewai.com)를 통해서입니다.
|
||||
```bash 코드 변경 후 반영
|
||||
crewai deploy push
|
||||
```
|
||||
|
||||
CLI를 사용하여 [CrewAI AMP](http://app.crewai.com)에 crew를 배포하는 단계별 시연은 이 영상 튜토리얼을 참고하세요.
|
||||
```bash 배포 목록 또는 삭제
|
||||
crewai deploy list
|
||||
crewai deploy remove <deployment_id>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<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>
|
||||
<Tip>
|
||||
첫 배포는 보통 **약 1분** 정도 걸립니다. 전체 사전 요건과 웹 UI 절차는 [AMP에 배포](/ko/enterprise/guides/deploy-to-amp)를 참고하세요.
|
||||
</Tip>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Enterprise에 배포" icon="rocket" href="http://app.crewai.com">
|
||||
CrewAI AOP로 시작하여 몇 번의 클릭만으로 production 환경에 crew를
|
||||
배포하세요.
|
||||
<Card title="배포 가이드" icon="book" href="/ko/enterprise/guides/deploy-to-amp">
|
||||
AMP 배포 단계별 안내(CLI 및 대시보드).
|
||||
</Card>
|
||||
<Card
|
||||
title="커뮤니티 참여하기"
|
||||
title="커뮤니티"
|
||||
icon="comments"
|
||||
href="https://community.crewai.com"
|
||||
>
|
||||
오픈 소스 커뮤니티에 참여하여 아이디어를 나누고, 프로젝트를 공유하며, 다른
|
||||
CrewAI 개발자들과 소통하세요.
|
||||
아이디어를 나누고 프로젝트를 공유하며 다른 CrewAI 개발자와 소통하세요.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -4,6 +4,192 @@ 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
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Adicionar a extensão A2UI com suporte a v0.8/v0.9, esquemas e documentação
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir prefixos de visão multimodal adicionando GPT-5 e o-series
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.13.0a6
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@alex-clawd, @greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="01 abr 2026">
|
||||
## v1.13.0a6
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a6)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Documentação
|
||||
- Corrigir níveis de permissão RBAC para corresponder às opções reais da interface do usuário (#5210)
|
||||
- Atualizar changelog e versão para v1.13.0a5 (#5200)
|
||||
|
||||
### Desempenho
|
||||
- Reduzir a sobrecarga do framework implementando um barramento de eventos preguiçoso e pulando o rastreamento quando desativado (#5187)
|
||||
|
||||
## Contributors
|
||||
|
||||
@alex-clawd, @joaomdmoura, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="31 mar 2026">
|
||||
## v1.13.0a5
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a5)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.13.0a4
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="01 abr 2026">
|
||||
## v1.13.0a4
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a4)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.13.0a3
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="01 abr 2026">
|
||||
## v1.13.0a3
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a3)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Emitir dados de uso de token no LLMCallCompletedEvent
|
||||
- Extrair e publicar metadados de ferramentas no AMP
|
||||
|
||||
### Correções de Bugs
|
||||
- Lidar com modelos GPT-5.x que não suportam o parâmetro de API `stop`
|
||||
|
||||
### Documentação
|
||||
- Corrigir imprecisões nas capacidades do agente em todas as línguas
|
||||
- Adicionar visão geral das Capacidades do Agente e melhorar a documentação de Habilidades
|
||||
- Adicionar um guia abrangente de configuração de SSO
|
||||
- Atualizar o changelog e a versão para v1.13.0rc1
|
||||
|
||||
### Refatoração
|
||||
- Converter Flow para Pydantic BaseModel
|
||||
- Converter classes LLM para Pydantic BaseModel
|
||||
- Substituir InstanceOf[T] por anotações de tipo simples
|
||||
- Remover métodos não utilizados
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@dependabot[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide, @thiagomoretto
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="27 mar 2026">
|
||||
## v1.13.0rc1
|
||||
|
||||
|
||||
147
docs/pt-BR/concepts/agent-capabilities.mdx
Normal file
147
docs/pt-BR/concepts/agent-capabilities.mdx
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: "Capacidades do Agente"
|
||||
description: "Entenda as cinco formas de estender agentes CrewAI: Ferramentas, MCPs, Apps, Skills e Knowledge."
|
||||
icon: puzzle-piece
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Agentes CrewAI podem ser estendidos com **cinco tipos distintos de capacidades**, cada um servindo a um propósito diferente. Entender quando usar cada um — e como eles funcionam juntos — é fundamental para construir agentes eficazes.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Ferramentas" icon="wrench" href="/pt-BR/concepts/tools" color="#3B82F6">
|
||||
**Funções chamáveis** — permitem que agentes tomem ações. Buscas na web, operações com arquivos, chamadas de API, execução de código.
|
||||
</Card>
|
||||
<Card title="Servidores MCP" icon="plug" href="/pt-BR/mcp/overview" color="#8B5CF6">
|
||||
**Servidores de ferramentas remotos** — conectam agentes a servidores de ferramentas externos via Model Context Protocol. Mesmo efeito de ferramentas, mas hospedados externamente.
|
||||
</Card>
|
||||
<Card title="Apps" icon="grid-2" color="#EC4899">
|
||||
**Integrações com plataformas** — conectam agentes a aplicativos SaaS (Gmail, Slack, Jira, Salesforce) via plataforma CrewAI. Executa localmente com um token de integração.
|
||||
</Card>
|
||||
<Card title="Skills" icon="bolt" href="/pt-BR/concepts/skills" color="#F59E0B">
|
||||
**Expertise de domínio** — injetam instruções, diretrizes e material de referência nos prompts dos agentes. Skills dizem aos agentes *como pensar*.
|
||||
</Card>
|
||||
<Card title="Knowledge" icon="book" href="/pt-BR/concepts/knowledge" color="#10B981">
|
||||
**Fatos recuperados** — fornecem aos agentes dados de documentos, arquivos e URLs via busca semântica (RAG). Knowledge dá aos agentes *o que saber*.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## A Distinção Fundamental
|
||||
|
||||
O mais importante a entender: **essas capacidades se dividem em duas categorias**.
|
||||
|
||||
### Capacidades de Ação (Ferramentas, MCPs, Apps)
|
||||
|
||||
Estas dão aos agentes a capacidade de **fazer coisas** — chamar APIs, ler arquivos, buscar na web, enviar emails. No momento da execução, os três tipos se resolvem no mesmo formato interno (instâncias de `BaseTool`) e aparecem em uma lista unificada de ferramentas que o agente pode chamar.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find and compile market data",
|
||||
backstory="Expert market analyst",
|
||||
tools=[SerperDevTool(), FileReadTool()], # Ferramentas locais
|
||||
mcps=["https://mcp.example.com/sse"], # Ferramentas de servidor MCP remoto
|
||||
apps=["gmail", "google_sheets"], # Integrações com plataformas
|
||||
)
|
||||
```
|
||||
|
||||
### Capacidades de Contexto (Skills, Knowledge)
|
||||
|
||||
Estas modificam o **prompt** do agente — injetando expertise, instruções ou dados recuperados antes do agente começar a raciocinar. Não dão aos agentes novas ações; elas moldam como os agentes pensam e a quais informações têm acesso.
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Security Auditor",
|
||||
goal="Audit cloud infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security with 10 years of experience",
|
||||
skills=["./skills/security-audit"], # Instruções de domínio
|
||||
knowledge_sources=[pdf_source, url_source], # Fatos recuperados
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quando Usar o Quê
|
||||
|
||||
| Você precisa... | Use | Exemplo |
|
||||
| :------------------------------------------------------- | :---------------- | :--------------------------------------- |
|
||||
| Agente buscar na web | **Ferramentas** | `tools=[SerperDevTool()]` |
|
||||
| Agente chamar uma API remota via MCP | **MCPs** | `mcps=["https://api.example.com/sse"]` |
|
||||
| Agente enviar emails pelo Gmail | **Apps** | `apps=["gmail"]` |
|
||||
| Agente seguir procedimentos específicos | **Skills** | `skills=["./skills/code-review"]` |
|
||||
| Agente consultar documentos da empresa | **Knowledge** | `knowledge_sources=[pdf_source]` |
|
||||
| Agente buscar na web E seguir diretrizes de revisão | **Ferramentas + Skills** | Use ambos juntos |
|
||||
|
||||
---
|
||||
|
||||
## Combinando Capacidades
|
||||
|
||||
Na prática, agentes frequentemente usam **múltiplos tipos de capacidades juntos**. Aqui está um exemplo realista:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
# Um agente de pesquisa totalmente equipado
|
||||
researcher = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Produce comprehensive market analysis reports",
|
||||
backstory="Expert analyst with deep industry knowledge",
|
||||
|
||||
# AÇÃO: O que o agente pode FAZER
|
||||
tools=[
|
||||
SerperDevTool(), # Buscar na web
|
||||
FileReadTool(), # Ler arquivos locais
|
||||
CodeInterpreterTool(), # Executar código Python para análise
|
||||
],
|
||||
mcps=["https://data-api.example.com/sse"], # Acessar API de dados remota
|
||||
apps=["google_sheets"], # Escrever no Google Sheets
|
||||
|
||||
# CONTEXTO: O que o agente SABE
|
||||
skills=["./skills/research-methodology"], # Como conduzir pesquisas
|
||||
knowledge_sources=[company_docs], # Dados específicos da empresa
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tabela Comparativa
|
||||
|
||||
| Característica | Ferramentas | MCPs | Apps | Skills | Knowledge |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: |
|
||||
| **Dá ações ao agente** | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| **Modifica o prompt** | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| **Requer código** | Sim | Apenas config | Apenas config | Apenas Markdown | Apenas config |
|
||||
| **Executa localmente** | Sim | Depende | Sim (com variável de ambiente) | N/A | Sim |
|
||||
| **Precisa de chaves API** | Por ferramenta | Por servidor | Token de integração | Não | Apenas embedder |
|
||||
| **Definido no Agent** | `tools=[]` | `mcps=[]` | `apps=[]` | `skills=[]` | `knowledge_sources=[]` |
|
||||
| **Definido no Crew** | ❌ | ❌ | ❌ | `skills=[]` | `knowledge_sources=[]` |
|
||||
|
||||
---
|
||||
|
||||
## Aprofundamentos
|
||||
|
||||
Pronto para aprender mais sobre cada tipo de capacidade?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Ferramentas" icon="wrench" href="/pt-BR/concepts/tools">
|
||||
Crie ferramentas personalizadas, use o catálogo OSS com 75+ opções, configure cache e execução assíncrona.
|
||||
</Card>
|
||||
<Card title="Integração MCP" icon="plug" href="/pt-BR/mcp/overview">
|
||||
Conecte-se a servidores MCP via stdio, SSE ou HTTP. Filtre ferramentas, configure autenticação.
|
||||
</Card>
|
||||
<Card title="Skills" icon="bolt" href="/pt-BR/concepts/skills">
|
||||
Construa pacotes de skills com SKILL.md, injete expertise de domínio, use divulgação progressiva.
|
||||
</Card>
|
||||
<Card title="Knowledge" icon="book" href="/pt-BR/concepts/knowledge">
|
||||
Adicione conhecimento de PDFs, CSVs, URLs e mais. Configure embedders e recuperação.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
232
docs/pt-BR/concepts/checkpointing.mdx
Normal file
232
docs/pt-BR/concepts/checkpointing.mdx
Normal file
@@ -0,0 +1,232 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,27 +1,186 @@
|
||||
---
|
||||
title: Skills
|
||||
description: Pacotes de skills baseados em sistema de arquivos que injetam contexto nos prompts dos agentes.
|
||||
description: Pacotes de skills baseados em sistema de arquivos que injetam expertise de domínio e instruções nos prompts dos agentes.
|
||||
icon: bolt
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Skills são diretórios autocontidos que fornecem aos agentes instruções, referências e assets específicos de domínio. Cada skill é definida por um arquivo `SKILL.md` com frontmatter YAML e um corpo em markdown.
|
||||
Skills são diretórios autocontidos que fornecem aos agentes **instruções, diretrizes e material de referência específicos de domínio**. Cada skill é definida por um arquivo `SKILL.md` com frontmatter YAML e um corpo em markdown.
|
||||
|
||||
Skills usam **divulgação progressiva** — metadados são carregados primeiro, instruções completas apenas quando ativadas, e catálogos de recursos apenas quando necessário.
|
||||
Quando ativada, as instruções de uma skill são injetadas diretamente no prompt da tarefa do agente — dando ao agente expertise sem exigir alterações de código.
|
||||
|
||||
## Estrutura de Diretório
|
||||
<Note type="info" title="Skills vs Ferramentas — A Distinção Fundamental">
|
||||
**Skills NÃO são ferramentas.** Este é o ponto de confusão mais comum.
|
||||
|
||||
- **Skills** injetam *instruções e contexto* no prompt do agente. Elas dizem ao agente *como pensar* sobre um problema.
|
||||
- **Ferramentas** dão ao agente *funções chamáveis* para tomar ações (buscar, ler arquivos, chamar APIs).
|
||||
|
||||
Frequentemente você precisa de **ambos**: skills para expertise, ferramentas para ação. Eles são configurados independentemente e se complementam.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Início Rápido
|
||||
|
||||
### 1. Crie um Diretório de Skill
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # Obrigatório — frontmatter + instruções
|
||||
├── scripts/ # Opcional — scripts executáveis
|
||||
├── references/ # Opcional — documentos de referência
|
||||
└── assets/ # Opcional — arquivos estáticos (configs, dados)
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md # Obrigatório — instruções
|
||||
├── references/ # Opcional — documentos de referência
|
||||
│ └── style-guide.md
|
||||
└── scripts/ # Opcional — scripts executáveis
|
||||
```
|
||||
|
||||
O nome do diretório deve corresponder ao campo `name` no `SKILL.md`.
|
||||
### 2. Escreva seu SKILL.md
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Guidelines for conducting thorough code reviews with focus on security and performance.
|
||||
metadata:
|
||||
author: your-team
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
## Diretrizes de Code Review
|
||||
|
||||
Ao revisar código, siga esta checklist:
|
||||
|
||||
1. **Segurança**: Verifique vulnerabilidades de injeção, bypasses de autenticação e exposição de dados
|
||||
2. **Performance**: Procure por queries N+1, alocações desnecessárias e chamadas bloqueantes
|
||||
3. **Legibilidade**: Garanta nomenclatura clara, comentários apropriados e estilo consistente
|
||||
4. **Testes**: Verifique cobertura adequada de testes para novas funcionalidades
|
||||
|
||||
### Níveis de Severidade
|
||||
- **Crítico**: Vulnerabilidades de segurança, riscos de perda de dados → bloquear merge
|
||||
- **Major**: Problemas de performance, erros de lógica → solicitar alterações
|
||||
- **Minor**: Questões de estilo, sugestões de nomenclatura → aprovar com comentários
|
||||
```
|
||||
|
||||
### 3. Anexe a um Agente
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
from crewai_tools import GithubSearchTool, FileReadTool
|
||||
|
||||
reviewer = Agent(
|
||||
role="Senior Code Reviewer",
|
||||
goal="Review pull requests for quality and security issues",
|
||||
backstory="Staff engineer with expertise in secure coding practices.",
|
||||
skills=["./skills"], # Injeta diretrizes de revisão
|
||||
tools=[GithubSearchTool(), FileReadTool()], # Permite ao agente ler código
|
||||
)
|
||||
```
|
||||
|
||||
O agente agora tem tanto **expertise** (da skill) quanto **capacidades** (das ferramentas).
|
||||
|
||||
---
|
||||
|
||||
## Skills + Ferramentas: Trabalhando Juntos
|
||||
|
||||
Aqui estão padrões comuns mostrando como skills e ferramentas se complementam:
|
||||
|
||||
### Padrão 1: Apenas Skills (Expertise de Domínio, Sem Ações Necessárias)
|
||||
|
||||
Use quando o agente precisa de instruções específicas mas não precisa chamar serviços externos:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Technical Writer",
|
||||
goal="Write clear API documentation",
|
||||
backstory="Expert technical writer",
|
||||
skills=["./skills/api-docs-style"], # Diretrizes e templates de escrita
|
||||
# Sem ferramentas necessárias — agente escreve baseado no contexto fornecido
|
||||
)
|
||||
```
|
||||
|
||||
### Padrão 2: Apenas Ferramentas (Ações, Sem Expertise Especial)
|
||||
|
||||
Use quando o agente precisa tomar ações mas não precisa de instruções específicas de domínio:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
|
||||
|
||||
agent = Agent(
|
||||
role="Web Researcher",
|
||||
goal="Find information about a topic",
|
||||
backstory="Skilled at finding information online",
|
||||
tools=[SerperDevTool(), ScrapeWebsiteTool()], # Pode buscar e extrair dados
|
||||
# Sem skills necessárias — pesquisa geral não precisa de diretrizes especiais
|
||||
)
|
||||
```
|
||||
|
||||
### Padrão 3: Skills + Ferramentas (Expertise E Ações)
|
||||
|
||||
O padrão mais comum no mundo real. A skill fornece *como* abordar o trabalho; ferramentas fornecem *o que* o agente pode fazer:
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
|
||||
|
||||
analyst = Agent(
|
||||
role="Security Analyst",
|
||||
goal="Audit infrastructure for vulnerabilities",
|
||||
backstory="Expert in cloud security and compliance",
|
||||
skills=["./skills/security-audit"], # Metodologia e checklists de auditoria
|
||||
tools=[
|
||||
SerperDevTool(), # Pesquisar vulnerabilidades conhecidas
|
||||
FileReadTool(), # Ler arquivos de configuração
|
||||
CodeInterpreterTool(), # Executar scripts de análise
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### Padrão 4: Skills + MCPs
|
||||
|
||||
Skills funcionam junto com servidores MCP da mesma forma que com ferramentas:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Data Analyst",
|
||||
goal="Analyze customer data and generate reports",
|
||||
backstory="Expert data analyst with strong statistical background",
|
||||
skills=["./skills/data-analysis"], # Metodologia de análise
|
||||
mcps=["https://data-warehouse.example.com/sse"], # Acesso remoto a dados
|
||||
)
|
||||
```
|
||||
|
||||
### Padrão 5: Skills + Apps
|
||||
|
||||
Skills podem guiar como um agente usa integrações de plataforma:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
role="Customer Support Agent",
|
||||
goal="Respond to customer inquiries professionally",
|
||||
backstory="Experienced support representative",
|
||||
skills=["./skills/support-playbook"], # Templates de resposta e regras de escalação
|
||||
apps=["gmail", "zendesk"], # Pode enviar emails e atualizar tickets
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skills no Nível do Crew
|
||||
|
||||
Skills podem ser definidas no crew para aplicar a **todos os agentes**:
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
|
||||
crew = Crew(
|
||||
agents=[researcher, writer, reviewer],
|
||||
tasks=[research_task, write_task, review_task],
|
||||
skills=["./skills"], # Todos os agentes recebem essas skills
|
||||
)
|
||||
```
|
||||
|
||||
Skills no nível do agente têm prioridade — se a mesma skill é descoberta em ambos os níveis, a versão do agente é usada.
|
||||
|
||||
---
|
||||
|
||||
## Formato do SKILL.md
|
||||
|
||||
@@ -34,7 +193,7 @@ compatibility: crewai>=0.1.0 # opcional
|
||||
metadata: # opcional
|
||||
author: your-name
|
||||
version: "1.0"
|
||||
allowed-tools: web-search file-read # opcional, delimitado por espaços
|
||||
allowed-tools: web-search file-read # opcional, experimental
|
||||
---
|
||||
|
||||
Instruções para o agente vão aqui. Este corpo em markdown é injetado
|
||||
@@ -43,57 +202,46 @@ no prompt do agente quando a skill é ativada.
|
||||
|
||||
### Campos do Frontmatter
|
||||
|
||||
| Campo | Obrigatório | Restrições |
|
||||
| Campo | Obrigatório | Descrição |
|
||||
| :-------------- | :---------- | :----------------------------------------------------------------------- |
|
||||
| `name` | Sim | 1–64 chars. Alfanumérico minúsculo e hifens. Sem hifens iniciais/finais/consecutivos. Deve corresponder ao nome do diretório. |
|
||||
| `name` | Sim | 1–64 chars. Alfanumérico minúsculo e hifens. Deve corresponder ao nome do diretório. |
|
||||
| `description` | Sim | 1–1024 chars. Descreve o que a skill faz e quando usá-la. |
|
||||
| `license` | Não | Nome da licença ou referência a um arquivo de licença incluído. |
|
||||
| `compatibility` | Não | Máx 500 chars. Requisitos de ambiente (produtos, pacotes, rede). |
|
||||
| `metadata` | Não | Mapeamento arbitrário de chave-valor string. |
|
||||
| `allowed-tools` | Não | Lista de ferramentas pré-aprovadas delimitada por espaços. Experimental. |
|
||||
|
||||
## Uso
|
||||
---
|
||||
|
||||
### Skills no Nível do Agente
|
||||
## Estrutura de Diretório
|
||||
|
||||
Passe caminhos de diretório de skills para um agente:
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
backstory="An expert researcher.",
|
||||
skills=["./skills"], # descobre todas as skills neste diretório
|
||||
)
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # Obrigatório — frontmatter + instruções
|
||||
├── scripts/ # Opcional — scripts executáveis
|
||||
├── references/ # Opcional — documentos de referência
|
||||
└── assets/ # Opcional — arquivos estáticos (configs, dados)
|
||||
```
|
||||
|
||||
### Skills no Nível do Crew
|
||||
O nome do diretório deve corresponder ao campo `name` no `SKILL.md`. Os diretórios `scripts/`, `references/` e `assets/` estão disponíveis no `path` da skill para agentes que precisam referenciar arquivos diretamente.
|
||||
|
||||
Caminhos de skills no crew são mesclados em todos os agentes:
|
||||
---
|
||||
|
||||
```python
|
||||
from crewai import Crew
|
||||
## Skills Pré-carregadas
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
skills=["./skills"],
|
||||
)
|
||||
```
|
||||
|
||||
### Skills Pré-carregadas
|
||||
|
||||
Você também pode passar objetos `Skill` diretamente:
|
||||
Para mais controle, você pode descobrir e ativar skills programaticamente:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from crewai.skills import discover_skills, activate_skill
|
||||
|
||||
# Descobrir todas as skills em um diretório
|
||||
skills = discover_skills(Path("./skills"))
|
||||
|
||||
# Ativá-las (carrega o corpo completo do SKILL.md)
|
||||
activated = [activate_skill(s) for s in skills]
|
||||
|
||||
# Passar para um agente
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find relevant information",
|
||||
@@ -102,13 +250,57 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Como as Skills São Carregadas
|
||||
|
||||
Skills carregam progressivamente — apenas os dados necessários em cada etapa são lidos:
|
||||
Skills usam **divulgação progressiva** — carregando apenas o necessário em cada estágio:
|
||||
|
||||
| Etapa | O que é carregado | Quando |
|
||||
| :--------------- | :------------------------------------------------ | :------------------ |
|
||||
| Descoberta | Nome, descrição, campos do frontmatter | `discover_skills()` |
|
||||
| Ativação | Texto completo do corpo do SKILL.md | `activate_skill()` |
|
||||
| Estágio | O que é carregado | Quando |
|
||||
| :--------- | :------------------------------------ | :------------------ |
|
||||
| Descoberta | Nome, descrição, campos do frontmatter | `discover_skills()` |
|
||||
| Ativação | Texto completo do corpo do SKILL.md | `activate_skill()` |
|
||||
|
||||
Durante a execução normal do agente, skills são automaticamente descobertas e ativadas. Os diretórios `scripts/`, `references/` e `assets/` estão disponíveis no `path` da skill para agentes que precisam referenciar arquivos diretamente.
|
||||
Durante a execução normal do agente (passando caminhos de diretório via `skills=["./skills"]`), skills são automaticamente descobertas e ativadas. O carregamento progressivo só importa quando usando a API programática.
|
||||
|
||||
---
|
||||
|
||||
## Skills vs Knowledge
|
||||
|
||||
Tanto skills quanto knowledge modificam o prompt do agente, mas servem propósitos diferentes:
|
||||
|
||||
| Aspecto | Skills | Knowledge |
|
||||
| :--- | :--- | :--- |
|
||||
| **O que fornece** | Instruções, procedimentos, diretrizes | Fatos, dados, informações |
|
||||
| **Como é armazenado** | Arquivos Markdown (SKILL.md) | Embarcado em banco vetorial (ChromaDB) |
|
||||
| **Como é recuperado** | Corpo inteiro injetado no prompt | Busca semântica encontra trechos relevantes |
|
||||
| **Melhor para** | Metodologia, checklists, guias de estilo | Documentos da empresa, info de produto, dados de referência |
|
||||
| **Definido via** | `skills=["./skills"]` | `knowledge_sources=[source]` |
|
||||
|
||||
**Regra prática:** Se o agente precisa seguir um *processo*, use uma skill. Se o agente precisa consultar *dados*, use knowledge.
|
||||
|
||||
---
|
||||
|
||||
## Perguntas Frequentes
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Preciso definir skills E ferramentas?">
|
||||
Depende do seu caso de uso. Skills e ferramentas são **independentes** — você pode usar qualquer um, ambos ou nenhum.
|
||||
|
||||
- **Apenas skills**: Quando o agente precisa de expertise mas não de ações externas (ex: escrever com diretrizes de estilo)
|
||||
- **Apenas ferramentas**: Quando o agente precisa de ações mas não de metodologia especial (ex: busca simples na web)
|
||||
- **Ambos**: Quando o agente precisa de expertise E ações (ex: auditoria de segurança com checklists específicas E capacidade de escanear código)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Skills fornecem ferramentas automaticamente?">
|
||||
**Não.** O campo `allowed-tools` no SKILL.md é apenas metadado experimental — ele não provisiona nem injeta nenhuma ferramenta. Você deve sempre definir ferramentas separadamente via `tools=[]`, `mcps=[]` ou `apps=[]`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="O que acontece se eu definir a mesma skill tanto no agente quanto no crew?">
|
||||
A skill no nível do agente tem prioridade. Skills são deduplicadas por nome — as skills do agente são processadas primeiro, então se o mesmo nome de skill aparece em ambos os níveis, a versão do agente é usada.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Qual o tamanho máximo do corpo do SKILL.md?">
|
||||
Há um aviso suave em 50.000 caracteres, mas sem limite rígido. Mantenha skills focadas e concisas para melhores resultados — injeções de prompt muito grandes podem diluir a atenção do agente.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -10,6 +10,10 @@ mode: "wide"
|
||||
As ferramentas do CrewAI capacitam agentes com habilidades que vão desde busca na web e análise de dados até colaboração e delegação de tarefas entre colegas de trabalho.
|
||||
Esta documentação descreve como criar, integrar e aproveitar essas ferramentas dentro do framework CrewAI, incluindo um novo foco em ferramentas de colaboração.
|
||||
|
||||
<Note type="info" title="Ferramentas são um dos cinco tipos de capacidades de agentes">
|
||||
Ferramentas dão aos agentes **funções chamáveis** para tomar ações. Elas funcionam junto com [MCPs](/pt-BR/mcp/overview) (servidores de ferramentas remotos), [Apps](/pt-BR/concepts/agent-capabilities) (integrações com plataformas), [Skills](/pt-BR/concepts/skills) (expertise de domínio) e [Knowledge](/pt-BR/concepts/knowledge) (fatos recuperados). Veja a visão geral de [Capacidades do Agente](/pt-BR/concepts/agent-capabilities) para entender quando usar cada um.
|
||||
</Note>
|
||||
|
||||
## O que é uma Ferramenta?
|
||||
|
||||
Uma ferramenta no CrewAI é uma habilidade ou função que os agentes podem utilizar para executar diversas ações.
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
---
|
||||
title: "Controle de Acesso Baseado em Funções (RBAC)"
|
||||
description: "Controle o acesso a crews, ferramentas e dados com funções e visibilidade por automação."
|
||||
description: "Controle o acesso a crews, ferramentas e dados com funções, escopos e permissões granulares."
|
||||
icon: "shield"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
O RBAC no CrewAI AMP permite gerenciar acesso de forma segura e escalável combinando **funções em nível de organização** com **controles de visibilidade em nível de automação**.
|
||||
O RBAC no CrewAI AMP permite gerenciamento de acesso seguro e escalável através de duas camadas:
|
||||
|
||||
1. **Permissões de funcionalidade** — controlam o que cada função pode fazer na plataforma (gerenciar, ler ou sem acesso)
|
||||
2. **Permissões em nível de entidade** — acesso granular em automações individuais, variáveis de ambiente, conexões LLM e repositórios Git
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/users_and_roles.png" alt="Visão geral de RBAC no CrewAI AMP" />
|
||||
|
||||
</Frame>
|
||||
|
||||
## Usuários e Funções
|
||||
|
||||
Cada membro da sua workspace possui uma função, que determina o acesso aos recursos.
|
||||
Cada membro da sua workspace CrewAI recebe uma função, que determina seu acesso aos diversos recursos.
|
||||
|
||||
Você pode:
|
||||
|
||||
@@ -31,14 +33,21 @@ A configuração de usuários e funções é feita em Settings → Roles.
|
||||
Vá em <b>Settings → Roles</b> no CrewAI AMP.
|
||||
</Step>
|
||||
<Step title="Escolher a função">
|
||||
Use <b>Owner</b> ou <b>Member</b>, ou clique em <b>Create role</b> para
|
||||
criar uma função personalizada.
|
||||
Use uma função pré-definida (<b>Owner</b>, <b>Member</b>) ou clique em{" "}
|
||||
<b>Create role</b> para criar uma personalizada.
|
||||
</Step>
|
||||
<Step title="Atribuir aos membros">
|
||||
Selecione os usuários e atribua a função. Você pode alterar depois.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Funções Pré-definidas
|
||||
|
||||
| Função | Descrição |
|
||||
| :--------- | :------------------------------------------------------------------------ |
|
||||
| **Owner** | Acesso total a todas as funcionalidades e configurações. Não pode ser restrito. |
|
||||
| **Member** | Acesso de leitura à maioria das funcionalidades, acesso de gerenciamento a variáveis de ambiente, conexões LLM e projetos Studio. Não pode modificar configurações da organização ou padrões. |
|
||||
|
||||
### Resumo de configuração
|
||||
|
||||
| Área | Onde configurar | Opções |
|
||||
@@ -46,35 +55,93 @@ A configuração de usuários e funções é feita em Settings → Roles.
|
||||
| Usuários & Funções | Settings → Roles | Pré-definidas: Owner, Member; Funções personalizadas |
|
||||
| Visibilidade da automação | Automation → Settings → Visibility | Private; Lista de usuários/funções |
|
||||
|
||||
## Controle de Acesso em Nível de Automação
|
||||
---
|
||||
|
||||
Além das funções na organização, as **Automations** suportam visibilidade refinada para restringir acesso por usuário ou função.
|
||||
## Matriz de Permissões de Funcionalidades
|
||||
|
||||
Útil para:
|
||||
Cada função possui um nível de permissão para cada área de funcionalidade. Os três níveis são:
|
||||
|
||||
- Manter automações sensíveis/experimentais privadas
|
||||
- **Manage** — acesso total de leitura/escrita (criar, editar, excluir)
|
||||
- **Read** — acesso somente leitura
|
||||
- **No access** — funcionalidade oculta/inacessível
|
||||
|
||||
| Funcionalidade | Owner | Member (padrão) | Níveis disponíveis | Descrição |
|
||||
| :------------------------ | :------ | :--------------- | :------------------------ | :-------------------------------------------------------------- |
|
||||
| `usage_dashboards` | Manage | Read | Manage / Read / No access | Visualizar métricas e análises de uso |
|
||||
| `crews_dashboards` | Manage | Read | Manage / Read / No access | Visualizar dashboards de deploy, acessar detalhes de automações |
|
||||
| `invitations` | Manage | Read | Manage / Read / No access | Convidar novos membros para a organização |
|
||||
| `training_ui` | Manage | Read | Manage / Read / No access | Acessar interfaces de treinamento/fine-tuning |
|
||||
| `tools` | Manage | Read | Manage / Read / No access | Criar e gerenciar ferramentas |
|
||||
| `agents` | Manage | Read | Manage / Read / No access | Criar e gerenciar agentes |
|
||||
| `environment_variables` | Manage | Manage | Manage / No access | Criar e gerenciar variáveis de ambiente |
|
||||
| `llm_connections` | Manage | Manage | Manage / No access | Configurar conexões de provedores LLM |
|
||||
| `default_settings` | Manage | No access | Manage / No access | Modificar configurações padrão da organização |
|
||||
| `organization_settings` | Manage | No access | Manage / No access | Gerenciar cobrança, planos e configuração da organização |
|
||||
| `studio_projects` | Manage | Manage | Manage / No access | Criar e editar projetos no Studio |
|
||||
|
||||
<Tip>
|
||||
Ao criar uma função personalizada, a maioria das funcionalidades pode ser definida como **Manage**, **Read** ou **No access**. No entanto, `environment_variables`, `llm_connections`, `default_settings`, `organization_settings` e `studio_projects` suportam apenas **Manage** ou **No access** — não há opção somente leitura para essas funcionalidades.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Deploy via GitHub ou Zip
|
||||
|
||||
Uma das perguntas mais comuns sobre RBAC é: _"Quais permissões um membro da equipe precisa para fazer deploy?"_
|
||||
|
||||
### Deploy via GitHub
|
||||
|
||||
Para fazer deploy de uma automação a partir de um repositório GitHub, o usuário precisa de:
|
||||
|
||||
1. **`crews_dashboards`**: pelo menos `Read` — necessário para acessar o dashboard de automações onde os deploys são criados
|
||||
2. **Acesso ao repositório Git** (se RBAC em nível de entidade para repositórios Git estiver habilitado): a função do usuário deve ter acesso ao repositório Git específico via permissões de entidade
|
||||
3. **`studio_projects`: `Manage`** — se estiver construindo o crew no Studio antes do deploy
|
||||
|
||||
### Deploy via Zip
|
||||
|
||||
Para fazer deploy de uma automação via upload de arquivo Zip, o usuário precisa de:
|
||||
|
||||
1. **`crews_dashboards`**: pelo menos `Read` — necessário para acessar o dashboard de automações
|
||||
2. **Deploys via Zip habilitados**: a organização não deve ter desabilitado deploys via Zip nas configurações da organização
|
||||
|
||||
### Referência Rápida: Permissões Mínimas para Deploy
|
||||
|
||||
| Ação | Permissões de funcionalidade necessárias | Requisitos adicionais |
|
||||
| :------------------------- | :--------------------------------------- | :------------------------------------------------ |
|
||||
| Deploy via GitHub | `crews_dashboards: Read` | Acesso à entidade do repositório Git (se habilitado) |
|
||||
| Deploy via Zip | `crews_dashboards: Read` | Deploys via Zip devem estar habilitados na organização |
|
||||
| Construir no Studio | `studio_projects: Manage` | — |
|
||||
| Configurar chaves LLM | `llm_connections: Manage` | — |
|
||||
| Definir variáveis de ambiente | `environment_variables: Manage` | Acesso em nível de entidade (se habilitado) |
|
||||
|
||||
---
|
||||
|
||||
## Controle de Acesso em Nível de Automação (Permissões de Entidade)
|
||||
|
||||
Além das funções em nível de organização, o CrewAI suporta permissões granulares em nível de entidade que restringem o acesso a recursos individuais.
|
||||
|
||||
### Visibilidade da Automação
|
||||
|
||||
Automações suportam configurações de visibilidade que restringem acesso por usuário ou função. Útil para:
|
||||
|
||||
- Manter automações sensíveis ou experimentais privadas
|
||||
- Gerenciar visibilidade em equipes grandes ou colaboradores externos
|
||||
- Testar automações em contexto isolado
|
||||
|
||||
Em modo privado, somente usuários/funções na whitelist poderão:
|
||||
Deploys podem ser configurados como privados, significando que apenas usuários e funções na whitelist poderão interagir com eles.
|
||||
|
||||
- Ver a automação
|
||||
- Executar/usar a API
|
||||
- Acessar logs, métricas e configurações
|
||||
|
||||
O owner da organização sempre tem acesso, independente da visibilidade.
|
||||
|
||||
Configure em Automation → Settings → Visibility.
|
||||
Configure em Automation → Settings → aba Visibility.
|
||||
|
||||
<Steps>
|
||||
<Step title="Abrir a aba Visibility">
|
||||
Acesse <b>Automation → Settings → Visibility</b>.
|
||||
</Step>
|
||||
<Step title="Definir visibilidade">
|
||||
Selecione <b>Private</b> para restringir o acesso. O owner mantém acesso.
|
||||
Selecione <b>Private</b> para restringir o acesso. O owner da organização
|
||||
mantém acesso sempre.
|
||||
</Step>
|
||||
<Step title="Permitir acesso">
|
||||
Adicione usuários e funções que poderão ver/executar e acessar
|
||||
Adicione usuários e funções que poderão ver, executar e acessar
|
||||
logs/métricas/configurações.
|
||||
</Step>
|
||||
<Step title="Salvar e verificar">
|
||||
@@ -97,9 +164,92 @@ Configure em Automation → Settings → Visibility.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/enterprise/visibility.png" alt="Configuração de visibilidade no CrewAI AMP" />
|
||||
|
||||
</Frame>
|
||||
|
||||
### Tipos de Permissão de Deploy
|
||||
|
||||
Ao conceder acesso em nível de entidade a uma automação específica, você pode atribuir estes tipos de permissão:
|
||||
|
||||
| Permissão | O que permite |
|
||||
| :------------------- | :-------------------------------------------------- |
|
||||
| `run` | Executar a automação e usar sua API |
|
||||
| `traces` | Visualizar traces de execução e logs |
|
||||
| `manage_settings` | Editar, reimplantar, reverter ou excluir a automação |
|
||||
| `human_in_the_loop` | Responder a solicitações human-in-the-loop (HITL) |
|
||||
| `full_access` | Todos os anteriores |
|
||||
|
||||
### RBAC em Nível de Entidade para Outros Recursos
|
||||
|
||||
Quando o RBAC em nível de entidade está habilitado, o acesso a estes recursos também pode ser controlado por usuário ou função:
|
||||
|
||||
| Recurso | Controlado por | Descrição |
|
||||
| :--------------------- | :------------------------------------- | :------------------------------------------------------------- |
|
||||
| Variáveis de ambiente | Flag de funcionalidade RBAC de entidade | Restringir quais funções/usuários podem ver ou gerenciar variáveis específicas |
|
||||
| Conexões LLM | Flag de funcionalidade RBAC de entidade | Restringir acesso a configurações de provedores LLM específicos |
|
||||
| Repositórios Git | Configuração RBAC de repositórios Git | Restringir quais funções/usuários podem acessar repositórios conectados específicos |
|
||||
|
||||
---
|
||||
|
||||
## Padrões Comuns de Funções
|
||||
|
||||
Embora o CrewAI venha com as funções Owner e Member, a maioria das equipes se beneficia da criação de funções personalizadas. Aqui estão os padrões comuns:
|
||||
|
||||
### Função Developer
|
||||
|
||||
Uma função para membros da equipe que constroem e fazem deploy de automações, mas não gerenciam configurações da organização.
|
||||
|
||||
| Funcionalidade | Permissão |
|
||||
| :------------------------ | :--------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Read |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Manage |
|
||||
| `agents` | Manage |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | Manage |
|
||||
|
||||
### Função Viewer / Stakeholder
|
||||
|
||||
Uma função para stakeholders não técnicos que precisam monitorar automações e visualizar resultados.
|
||||
|
||||
| Funcionalidade | Permissão |
|
||||
| :------------------------ | :--------- |
|
||||
| `usage_dashboards` | Read |
|
||||
| `crews_dashboards` | Read |
|
||||
| `invitations` | No access |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | No access |
|
||||
| `llm_connections` | No access |
|
||||
| `default_settings` | No access |
|
||||
| `organization_settings` | No access |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
### Função Ops / Platform Admin
|
||||
|
||||
Uma função para operadores de plataforma que gerenciam configurações de infraestrutura, mas podem não construir agentes.
|
||||
|
||||
| Funcionalidade | Permissão |
|
||||
| :------------------------ | :--------- |
|
||||
| `usage_dashboards` | Manage |
|
||||
| `crews_dashboards` | Manage |
|
||||
| `invitations` | Manage |
|
||||
| `training_ui` | Read |
|
||||
| `tools` | Read |
|
||||
| `agents` | Read |
|
||||
| `environment_variables` | Manage |
|
||||
| `llm_connections` | Manage |
|
||||
| `default_settings` | Manage |
|
||||
| `organization_settings` | Read |
|
||||
| `studio_projects` | No access |
|
||||
|
||||
---
|
||||
|
||||
<Card title="Precisa de Ajuda?" icon="headset" href="mailto:support@crewai.com">
|
||||
Fale com o nosso time para suporte em configuração e auditoria de RBAC.
|
||||
Fale com o nosso time para suporte em configuração de RBAC.
|
||||
</Card>
|
||||
|
||||
@@ -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 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.
|
||||
A primeira implantação normalmente leva cerca de 1 minuto.
|
||||
</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 de 10 a 15 minutos; as próximas serão mais rápidas
|
||||
3. A primeira implantação geralmente demora cerca de 1 minuto
|
||||
|
||||
<Frame>
|
||||

|
||||
|
||||
132
docs/pt-BR/enterprise/guides/training-crews.mdx
Normal file
132
docs/pt-BR/enterprise/guides/training-crews.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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>
|
||||
@@ -5,6 +5,14 @@ icon: wrench
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
### Assista: Construindo Agents e Flows CrewAI com Coding Agent Skills
|
||||
|
||||
Instale nossas coding agent skills (Claude Code, Codex, ...) para colocar seus agentes de código para funcionar rapidamente com o CrewAI.
|
||||
|
||||
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>
|
||||
|
||||
## Tutorial em Vídeo
|
||||
|
||||
Assista a este tutorial em vídeo para uma demonstração passo a passo do processo de instalação:
|
||||
@@ -192,12 +200,11 @@ Para equipes e organizações, o CrewAI oferece opções de implantação corpor
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Construa Seu Primeiro Agente"
|
||||
title="Início rápido: Flow + agente"
|
||||
icon="code"
|
||||
href="/pt-BR/quickstart"
|
||||
>
|
||||
Siga nosso guia de início rápido para criar seu primeiro agente CrewAI e
|
||||
obter experiência prática.
|
||||
Siga o guia rápido para gerar um Flow, executar um crew com um agente e produzir um relatório.
|
||||
</Card>
|
||||
<Card
|
||||
title="Junte-se à Comunidade"
|
||||
|
||||
@@ -16,6 +16,14 @@ Ele capacita desenvolvedores a construir sistemas multi-agente prontos para prod
|
||||
|
||||
Com mais de 100.000 desenvolvedores certificados em nossos cursos comunitários, o CrewAI é o padrão para automação de IA pronta para empresas.
|
||||
|
||||
### Assista: Construindo Agents e Flows CrewAI com Coding Agent Skills
|
||||
|
||||
Instale nossas coding agent skills (Claude Code, Codex, ...) para colocar seus agentes de código para funcionar rapidamente com o CrewAI.
|
||||
|
||||
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>
|
||||
|
||||
## A Arquitetura do CrewAI
|
||||
|
||||
A arquitetura do CrewAI foi projetada para equilibrar autonomia com controle.
|
||||
@@ -132,7 +140,7 @@ Para qualquer aplicação pronta para produção, **comece com um Flow**.
|
||||
icon="bolt"
|
||||
href="/pt-BR/quickstart"
|
||||
>
|
||||
Siga nosso guia rápido para criar seu primeiro agente CrewAI e colocar a mão na massa.
|
||||
Gere um Flow, execute um crew com um agente e produza um relatório ponta a ponta.
|
||||
</Card>
|
||||
<Card
|
||||
title="Junte-se à Comunidade"
|
||||
|
||||
@@ -1,374 +1,278 @@
|
||||
---
|
||||
title: Guia Rápido
|
||||
description: Construa seu primeiro agente de IA com a CrewAI em menos de 5 minutos.
|
||||
description: Crie seu primeiro Flow CrewAI em minutos — orquestração, estado e um crew com um agente que gera um relatório real.
|
||||
icon: rocket
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Construa seu primeiro Agente CrewAI
|
||||
### Assista: Construindo Agents e Flows CrewAI com Coding Agent Skills
|
||||
|
||||
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.
|
||||
Instale nossas coding agent skills (Claude Code, Codex, ...) para colocar seus agentes de código para funcionar rapidamente com o CrewAI.
|
||||
|
||||
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).
|
||||
Você pode instalar com `npx skills add crewaiinc/skills`
|
||||
|
||||
Siga os passos abaixo para começar a tripular! 🚣♂️
|
||||
<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.
|
||||
|
||||
Se ainda não instalou o CrewAI, siga primeiro o [guia de instalação](/pt-BR/installation).
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
- 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
|
||||
|
||||
<Steps>
|
||||
<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.
|
||||
<Step title="Crie um projeto Flow">
|
||||
No terminal, gere um projeto Flow (o nome da pasta usa sublinhados, ex.: `latest_ai_flow`):
|
||||
|
||||
<CodeGroup>
|
||||
```shell Terminal
|
||||
crewai create crew latest-ai-development
|
||||
crewai create flow latest-ai-flow
|
||||
cd latest_ai_flow
|
||||
```
|
||||
</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="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>
|
||||
|
||||
<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=...)`.
|
||||
|
||||
```yaml agents.yaml
|
||||
# src/latest_ai_development/config/agents.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/agents.yaml
|
||||
researcher:
|
||||
role: >
|
||||
Pesquisador Sênior de Dados em {topic}
|
||||
Pesquisador(a) Sênior de Dados em {topic}
|
||||
goal: >
|
||||
Descobrir os avanços mais recentes em {topic}
|
||||
Descobrir os desenvolvimentos mais recentes em {topic}
|
||||
backstory: >
|
||||
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.
|
||||
Você é um pesquisador experiente que descobre os últimos avanços em {topic}.
|
||||
Encontra as informações mais relevantes e apresenta tudo com clareza.
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Modifique seu arquivo `tasks.yaml`">
|
||||
|
||||
<Step title="Configure uma tarefa em `tasks.yaml`">
|
||||
```yaml tasks.yaml
|
||||
# src/latest_ai_development/config/tasks.yaml
|
||||
# src/latest_ai_flow/crews/content_crew/config/tasks.yaml
|
||||
research_task:
|
||||
description: >
|
||||
Realize uma pesquisa aprofundada sobre {topic}.
|
||||
Certifique-se de encontrar informações interessantes e relevantes considerando que o ano atual é 2025.
|
||||
Faça uma pesquisa aprofundada sobre {topic}. Use busca na web para obter
|
||||
informações atuais e confiáveis. O ano atual é 2026.
|
||||
expected_output: >
|
||||
Uma lista com 10 tópicos dos dados mais relevantes sobre {topic}
|
||||
Um relatório em markdown com seções claras: tendências principais, ferramentas
|
||||
ou empresas relevantes e implicações. Entre 800 e 1200 palavras. Sem cercas de código em volta do documento inteiro.
|
||||
agent: researcher
|
||||
|
||||
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
|
||||
output_file: output/report.md
|
||||
```
|
||||
|
||||
</Step>
|
||||
<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.project import CrewBase, agent, crew, task
|
||||
from crewai_tools import SerperDevTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
<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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
class ResearchCrew:
|
||||
"""Crew de pesquisa com um agente, usado dentro do Flow."""
|
||||
|
||||
agents: List[BaseAgent]
|
||||
tasks: List[Task]
|
||||
|
||||
agents_config = "config/agents.yaml"
|
||||
tasks_config = "config/tasks.yaml"
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['researcher'], # type: ignore[index]
|
||||
config=self.agents_config["researcher"], # type: ignore[index]
|
||||
verbose=True,
|
||||
tools=[SerperDevTool()]
|
||||
)
|
||||
|
||||
@agent
|
||||
def reporting_analyst(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['reporting_analyst'], # type: ignore[index]
|
||||
verbose=True
|
||||
tools=[SerperDevTool()],
|
||||
)
|
||||
|
||||
@task
|
||||
def research_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['research_task'], # type: ignore[index]
|
||||
)
|
||||
|
||||
@task
|
||||
def reporting_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['reporting_task'], # type: ignore[index]
|
||||
output_file='output/report.md' # Este é o arquivo que conterá o relatório final.
|
||||
config=self.tasks_config["research_task"], # type: ignore[index]
|
||||
)
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
"""Creates the LatestAiDevelopment crew"""
|
||||
return Crew(
|
||||
agents=self.agents, # Criado automaticamente pelo decorador @agent
|
||||
tasks=self.tasks, # Criado automaticamente pelo decorador @task
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
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
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
<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`.
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
|
||||
# ... remaining code
|
||||
```
|
||||
|
||||
</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
|
||||
# src/latest_ai_flow/main.py
|
||||
from pydantic import BaseModel
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run the crew.
|
||||
"""
|
||||
inputs = {
|
||||
'topic': 'AI Agents'
|
||||
}
|
||||
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
|
||||
from crewai.flow import Flow, listen, start
|
||||
|
||||
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()
|
||||
```
|
||||
|
||||
</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>
|
||||
<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="Alternativa para Empresas: Crie no Crew Studio">
|
||||
Para usuários do CrewAI AMP, você pode criar a mesma tripulação sem escrever código:
|
||||
<Step title="Variáveis de ambiente">
|
||||
Na raiz do projeto, no arquivo `.env`, defina:
|
||||
|
||||
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"
|
||||
|
||||

|
||||
|
||||
<Card title="Experimente o CrewAI AMP" icon="rocket" href="https://app.crewai.com">
|
||||
Comece sua conta gratuita no CrewAI AMP
|
||||
</Card>
|
||||
- `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)
|
||||
</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.
|
||||
|
||||
Veja um exemplo de como o relatório deve ser:
|
||||
<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):
|
||||
|
||||
<CodeGroup>
|
||||
```markdown output/report.md
|
||||
# Relatório Abrangente sobre a Ascensão e o Impacto dos Agentes de IA em 2025
|
||||
# Agentes de IA em 2026: panorama e tendências
|
||||
|
||||
## 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.
|
||||
## Resumo executivo
|
||||
…
|
||||
|
||||
## 2. Benefits of AI Agents
|
||||
AI agents bring numerous advantages that are transforming traditional work environments. Key benefits include:
|
||||
## Principais tendências
|
||||
- **Uso de ferramentas e orquestração** — …
|
||||
- **Adoção empresarial** — …
|
||||
|
||||
- **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.
|
||||
## Implicações
|
||||
…
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
O arquivo real será mais longo e refletirá resultados de busca ao vivo.
|
||||
</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>
|
||||
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!
|
||||
|
||||
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.
|
||||
</Check>
|
||||
|
||||
### Observação sobre Consistência nos Nomes
|
||||
### Consistência de nomes
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
#### Exemplos de Referências
|
||||
## Implantação
|
||||
|
||||
<Tip>
|
||||
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>
|
||||
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:
|
||||
|
||||
```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
|
||||
<CodeGroup>
|
||||
```bash Autenticar
|
||||
crewai login
|
||||
```
|
||||
|
||||
<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
|
||||
```bash Criar implantação
|
||||
crewai deploy create
|
||||
```
|
||||
|
||||
## Fazendo o Deploy da Sua Tripulação
|
||||
```bash Status e logs
|
||||
crewai deploy status
|
||||
crewai deploy logs
|
||||
```
|
||||
|
||||
A forma mais fácil de fazer deploy da sua tripulação em produção é através da [CrewAI AMP](http://app.crewai.com).
|
||||
```bash Enviar atualizações após mudanças no código
|
||||
crewai deploy push
|
||||
```
|
||||
|
||||
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.
|
||||
```bash Listar ou remover implantações
|
||||
crewai deploy list
|
||||
crewai deploy remove <deployment_id>
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<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>
|
||||
<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).
|
||||
</Tip>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<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 title="Guia de implantação" icon="book" href="/pt-BR/enterprise/guides/deploy-to-amp">
|
||||
AMP passo a passo (CLI e painel).
|
||||
</Card>
|
||||
<Card
|
||||
title="Junte-se à Comunidade"
|
||||
title="Comunidade"
|
||||
icon="comments"
|
||||
href="https://community.crewai.com"
|
||||
>
|
||||
Participe da nossa comunidade open source para discutir ideias, compartilhar
|
||||
seus projetos e conectar-se com outros desenvolvedores CrewAI.
|
||||
Troque ideias, compartilhe projetos e conecte-se com outros desenvolvedores CrewAI.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -17,6 +17,9 @@ dependencies = [
|
||||
"av~=13.0.0",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
exclude-newer = "3 days"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
@@ -152,4 +152,4 @@ __all__ = [
|
||||
"wrap_file_source",
|
||||
]
|
||||
|
||||
__version__ = "1.13.0rc1"
|
||||
__version__ = "1.14.0a3"
|
||||
|
||||
@@ -10,8 +10,7 @@ requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"pytube~=15.0.0",
|
||||
"requests~=2.32.5",
|
||||
"docker~=7.1.0",
|
||||
"crewai==1.13.0rc1",
|
||||
"crewai==1.14.0a3",
|
||||
"tiktoken~=0.8.0",
|
||||
"beautifulsoup4~=4.13.4",
|
||||
"python-docx~=1.2.0",
|
||||
@@ -142,6 +141,9 @@ contextual = [
|
||||
]
|
||||
|
||||
|
||||
[tool.uv]
|
||||
exclude-newer = "3 days"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
@@ -35,9 +35,6 @@ 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,
|
||||
@@ -225,7 +222,6 @@ __all__ = [
|
||||
"BrowserbaseLoadTool",
|
||||
"CSVSearchTool",
|
||||
"CodeDocsSearchTool",
|
||||
"CodeInterpreterTool",
|
||||
"ComposioTool",
|
||||
"ContextualAICreateAgentTool",
|
||||
"ContextualAIParseTool",
|
||||
@@ -309,4 +305,4 @@ __all__ = [
|
||||
"ZapierActionTools",
|
||||
]
|
||||
|
||||
__version__ = "1.13.0rc1"
|
||||
__version__ = "1.14.0a3"
|
||||
|
||||
@@ -24,9 +24,6 @@ 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,
|
||||
@@ -210,7 +207,6 @@ __all__ = [
|
||||
"BrowserbaseLoadTool",
|
||||
"CSVSearchTool",
|
||||
"CodeDocsSearchTool",
|
||||
"CodeInterpreterTool",
|
||||
"ComposioTool",
|
||||
"ContextualAICreateAgentTool",
|
||||
"ContextualAIParseTool",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM python:3.12-alpine
|
||||
|
||||
RUN pip install requests beautifulsoup4
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /workspace
|
||||
@@ -1,95 +0,0 @@
|
||||
# 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).
|
||||
@@ -1,424 +0,0 @@
|
||||
"""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}"
|
||||
@@ -97,6 +97,7 @@ def test_extract_init_params_schema(mock_tool_extractor):
|
||||
assert init_params_schema.keys() == {
|
||||
"$defs",
|
||||
"properties",
|
||||
"required",
|
||||
"title",
|
||||
"type",
|
||||
}
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
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
@@ -43,7 +43,8 @@ dependencies = [
|
||||
"uv~=0.9.13",
|
||||
"aiosqlite~=0.21.0",
|
||||
"pyyaml~=6.0",
|
||||
"lancedb>=0.29.2",
|
||||
"aiofiles~=24.1.0",
|
||||
"lancedb>=0.29.2,<0.30.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -54,7 +55,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = [
|
||||
"crewai-tools==1.13.0rc1",
|
||||
"crewai-tools==1.14.0a3",
|
||||
]
|
||||
embeddings = [
|
||||
"tiktoken~=0.8.0"
|
||||
@@ -83,7 +84,7 @@ voyageai = [
|
||||
"voyageai~=0.3.5",
|
||||
]
|
||||
litellm = [
|
||||
"litellm>=1.74.9,<=1.82.6",
|
||||
"litellm~=1.83.0",
|
||||
]
|
||||
bedrock = [
|
||||
"boto3~=1.40.45",
|
||||
@@ -115,6 +116,9 @@ 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"
|
||||
|
||||
@@ -4,8 +4,11 @@ from typing import Any
|
||||
import urllib.request
|
||||
import warnings
|
||||
|
||||
from pydantic import PydanticUserError
|
||||
|
||||
from crewai.agent.core import Agent
|
||||
from crewai.agent.planning_config import PlanningConfig
|
||||
from crewai.context import ExecutionContext
|
||||
from crewai.crew import Crew
|
||||
from crewai.crews.crew_output import CrewOutput
|
||||
from crewai.flow.flow import Flow
|
||||
@@ -13,6 +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.task import Task
|
||||
from crewai.tasks.llm_guardrail import LLMGuardrail
|
||||
from crewai.tasks.task_output import TaskOutput
|
||||
@@ -42,7 +46,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
|
||||
|
||||
_suppress_pydantic_deprecation_warnings()
|
||||
|
||||
__version__ = "1.13.0rc1"
|
||||
__version__ = "1.14.0a3"
|
||||
_telemetry_submitted = False
|
||||
|
||||
|
||||
@@ -93,18 +97,145 @@ def __getattr__(name: str) -> Any:
|
||||
raise AttributeError(f"module 'crewai' has no attribute {name!r}")
|
||||
|
||||
|
||||
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.tools_handler import ToolsHandler as _ToolsHandler
|
||||
from crewai.experimental.agent_executor import AgentExecutor as _AgentExecutor
|
||||
from crewai.hooks.llm_hooks import LLMCallHookContext as _LLMCallHookContext
|
||||
from crewai.tools.tool_types import ToolResult as _ToolResult
|
||||
from crewai.utilities.prompts import (
|
||||
StandardPromptResult as _StandardPromptResult,
|
||||
SystemPromptResult as _SystemPromptResult,
|
||||
)
|
||||
|
||||
_base_namespace: dict[str, type] = {
|
||||
"Agent": Agent,
|
||||
"BaseAgent": _BaseAgent,
|
||||
"Crew": Crew,
|
||||
"Flow": Flow,
|
||||
"BaseLLM": BaseLLM,
|
||||
"Task": Task,
|
||||
"BaseAgentExecutor": _BaseAgentExecutor,
|
||||
"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,
|
||||
A2AConfig as _A2AConfig,
|
||||
A2AServerConfig as _A2AServerConfig,
|
||||
)
|
||||
|
||||
_base_namespace.update(
|
||||
{
|
||||
"A2AConfig": _A2AConfig,
|
||||
"A2AClientConfig": _A2AClientConfig,
|
||||
"A2AServerConfig": _A2AServerConfig,
|
||||
}
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import sys
|
||||
|
||||
_full_namespace = {
|
||||
**_base_namespace,
|
||||
"ToolsHandler": _ToolsHandler,
|
||||
"StandardPromptResult": _StandardPromptResult,
|
||||
"SystemPromptResult": _SystemPromptResult,
|
||||
"LLMCallHookContext": _LLMCallHookContext,
|
||||
"ToolResult": _ToolResult,
|
||||
}
|
||||
|
||||
_resolve_namespace = {
|
||||
**_full_namespace,
|
||||
**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
|
||||
|
||||
Entity = Annotated[
|
||||
Flow | Crew | Agent, # type: ignore[type-arg]
|
||||
Field(discriminator="entity_type"),
|
||||
]
|
||||
|
||||
RuntimeState.model_rebuild(
|
||||
force=True,
|
||||
_types_namespace={**_full_namespace, "Entity": Entity},
|
||||
)
|
||||
|
||||
try:
|
||||
Agent.model_rebuild(force=True, _types_namespace=_full_namespace)
|
||||
except PydanticUserError:
|
||||
pass
|
||||
|
||||
except (ImportError, PydanticUserError):
|
||||
import logging as _logging
|
||||
|
||||
_logging.getLogger(__name__).warning(
|
||||
"model_rebuild() failed; forward refs may be unresolved.",
|
||||
exc_info=True,
|
||||
)
|
||||
RuntimeState = None # type: ignore[assignment,misc]
|
||||
|
||||
__all__ = [
|
||||
"LLM",
|
||||
"Agent",
|
||||
"BaseLLM",
|
||||
"Crew",
|
||||
"CrewOutput",
|
||||
"Entity",
|
||||
"ExecutionContext",
|
||||
"Flow",
|
||||
"Knowledge",
|
||||
"LLMGuardrail",
|
||||
"Memory",
|
||||
"PlanningConfig",
|
||||
"Process",
|
||||
"RuntimeState",
|
||||
"Task",
|
||||
"TaskOutput",
|
||||
"__version__",
|
||||
|
||||
148
lib/crewai/src/crewai/a2a/extensions/a2ui/__init__.py
Normal file
148
lib/crewai/src/crewai/a2a/extensions/a2ui/__init__.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""A2UI (Agent to UI) declarative UI protocol support for CrewAI."""
|
||||
|
||||
from crewai.a2a.extensions.a2ui.catalog import (
|
||||
AudioPlayer,
|
||||
Button,
|
||||
Card,
|
||||
CheckBox,
|
||||
Column,
|
||||
DateTimeInput,
|
||||
Divider,
|
||||
Icon,
|
||||
Image,
|
||||
List,
|
||||
Modal,
|
||||
MultipleChoice,
|
||||
Row,
|
||||
Slider,
|
||||
Tabs,
|
||||
Text,
|
||||
TextField,
|
||||
Video,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.client_extension import A2UIClientExtension
|
||||
from crewai.a2a.extensions.a2ui.models import (
|
||||
A2UIEvent,
|
||||
A2UIMessage,
|
||||
A2UIResponse,
|
||||
BeginRendering,
|
||||
DataModelUpdate,
|
||||
DeleteSurface,
|
||||
SurfaceUpdate,
|
||||
UserAction,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.server_extension import (
|
||||
A2UI_STANDARD_CATALOG_ID,
|
||||
A2UI_V09_BASIC_CATALOG_ID,
|
||||
A2UI_V09_EXTENSION_URI,
|
||||
A2UIServerExtension,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.v0_9 import (
|
||||
A2UIEventV09,
|
||||
A2UIMessageV09,
|
||||
ActionEvent,
|
||||
ActionV09,
|
||||
AudioPlayerV09,
|
||||
ButtonV09,
|
||||
CardV09,
|
||||
CheckBoxV09,
|
||||
ChoicePickerV09,
|
||||
ClientDataModel,
|
||||
ClientErrorV09,
|
||||
ColumnV09,
|
||||
CreateSurface,
|
||||
DateTimeInputV09,
|
||||
DeleteSurfaceV09,
|
||||
DividerV09,
|
||||
IconV09,
|
||||
ImageV09,
|
||||
ListV09,
|
||||
ModalV09,
|
||||
RowV09,
|
||||
SliderV09,
|
||||
TabsV09,
|
||||
TextFieldV09,
|
||||
TextV09,
|
||||
Theme,
|
||||
UpdateComponents,
|
||||
UpdateDataModel,
|
||||
VideoV09,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.validator import (
|
||||
validate_a2ui_event,
|
||||
validate_a2ui_event_v09,
|
||||
validate_a2ui_message,
|
||||
validate_a2ui_message_v09,
|
||||
validate_catalog_components,
|
||||
validate_catalog_components_v09,
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"A2UI_STANDARD_CATALOG_ID",
|
||||
"A2UI_V09_BASIC_CATALOG_ID",
|
||||
"A2UI_V09_EXTENSION_URI",
|
||||
"A2UIClientExtension",
|
||||
"A2UIEvent",
|
||||
"A2UIEventV09",
|
||||
"A2UIMessage",
|
||||
"A2UIMessageV09",
|
||||
"A2UIResponse",
|
||||
"A2UIServerExtension",
|
||||
"ActionEvent",
|
||||
"ActionV09",
|
||||
"AudioPlayer",
|
||||
"AudioPlayerV09",
|
||||
"BeginRendering",
|
||||
"Button",
|
||||
"ButtonV09",
|
||||
"Card",
|
||||
"CardV09",
|
||||
"CheckBox",
|
||||
"CheckBoxV09",
|
||||
"ChoicePickerV09",
|
||||
"ClientDataModel",
|
||||
"ClientErrorV09",
|
||||
"Column",
|
||||
"ColumnV09",
|
||||
"CreateSurface",
|
||||
"DataModelUpdate",
|
||||
"DateTimeInput",
|
||||
"DateTimeInputV09",
|
||||
"DeleteSurface",
|
||||
"DeleteSurfaceV09",
|
||||
"Divider",
|
||||
"DividerV09",
|
||||
"Icon",
|
||||
"IconV09",
|
||||
"Image",
|
||||
"ImageV09",
|
||||
"List",
|
||||
"ListV09",
|
||||
"Modal",
|
||||
"ModalV09",
|
||||
"MultipleChoice",
|
||||
"Row",
|
||||
"RowV09",
|
||||
"Slider",
|
||||
"SliderV09",
|
||||
"SurfaceUpdate",
|
||||
"Tabs",
|
||||
"TabsV09",
|
||||
"Text",
|
||||
"TextField",
|
||||
"TextFieldV09",
|
||||
"TextV09",
|
||||
"Theme",
|
||||
"UpdateComponents",
|
||||
"UpdateDataModel",
|
||||
"UserAction",
|
||||
"Video",
|
||||
"VideoV09",
|
||||
"validate_a2ui_event",
|
||||
"validate_a2ui_event_v09",
|
||||
"validate_a2ui_message",
|
||||
"validate_a2ui_message_v09",
|
||||
"validate_catalog_components",
|
||||
"validate_catalog_components_v09",
|
||||
]
|
||||
467
lib/crewai/src/crewai/a2a/extensions/a2ui/catalog.py
Normal file
467
lib/crewai/src/crewai/a2a/extensions/a2ui/catalog.py
Normal file
@@ -0,0 +1,467 @@
|
||||
"""Typed helpers for A2UI standard catalog components.
|
||||
|
||||
These models provide optional type safety for standard catalog components.
|
||||
Agents can also use raw dicts validated against the JSON schema.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class StringBinding(BaseModel):
|
||||
"""A string value: literal or data-model path."""
|
||||
|
||||
literal_string: str | None = Field(
|
||||
default=None, alias="literalString", description="Literal string value."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class NumberBinding(BaseModel):
|
||||
"""A numeric value: literal or data-model path."""
|
||||
|
||||
literal_number: float | None = Field(
|
||||
default=None, alias="literalNumber", description="Literal numeric value."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class BooleanBinding(BaseModel):
|
||||
"""A boolean value: literal or data-model path."""
|
||||
|
||||
literal_boolean: bool | None = Field(
|
||||
default=None, alias="literalBoolean", description="Literal boolean value."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class ArrayBinding(BaseModel):
|
||||
"""An array value: literal or data-model path."""
|
||||
|
||||
literal_array: list[str] | None = Field(
|
||||
default=None, alias="literalArray", description="Literal array of strings."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class ChildrenDef(BaseModel):
|
||||
"""Children definition for layout components."""
|
||||
|
||||
explicit_list: list[str] | None = Field(
|
||||
default=None,
|
||||
alias="explicitList",
|
||||
description="Explicit list of child component IDs.",
|
||||
)
|
||||
template: ChildTemplate | None = Field(
|
||||
default=None, description="Template for generating dynamic children."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class ChildTemplate(BaseModel):
|
||||
"""Template for generating dynamic children from a data model list."""
|
||||
|
||||
component_id: str = Field(
|
||||
alias="componentId", description="ID of the component to repeat."
|
||||
)
|
||||
data_binding: str = Field(
|
||||
alias="dataBinding", description="Data-model path to bind the template to."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class ActionContextEntry(BaseModel):
|
||||
"""A key-value pair in an action context payload."""
|
||||
|
||||
key: str = Field(description="Context entry key.")
|
||||
value: ActionBoundValue = Field(description="Context entry value.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ActionBoundValue(BaseModel):
|
||||
"""A value in an action context: literal or data-model path."""
|
||||
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
literal_string: str | None = Field(
|
||||
default=None, alias="literalString", description="Literal string value."
|
||||
)
|
||||
literal_number: float | None = Field(
|
||||
default=None, alias="literalNumber", description="Literal numeric value."
|
||||
)
|
||||
literal_boolean: bool | None = Field(
|
||||
default=None, alias="literalBoolean", description="Literal boolean value."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Action(BaseModel):
|
||||
"""Client-side action dispatched by interactive components."""
|
||||
|
||||
name: str = Field(description="Action name dispatched on interaction.")
|
||||
context: list[ActionContextEntry] | None = Field(
|
||||
default=None, description="Key-value pairs sent with the action."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class TabItem(BaseModel):
|
||||
"""A single tab definition."""
|
||||
|
||||
title: StringBinding = Field(description="Tab title text.")
|
||||
child: str = Field(description="Component ID rendered as the tab content.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class MultipleChoiceOption(BaseModel):
|
||||
"""A single option in a MultipleChoice component."""
|
||||
|
||||
label: StringBinding = Field(description="Display label for the option.")
|
||||
value: str = Field(description="Value submitted when the option is selected.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Text(BaseModel):
|
||||
"""Displays text content."""
|
||||
|
||||
text: StringBinding = Field(description="Text content to display.")
|
||||
usage_hint: Literal["h1", "h2", "h3", "h4", "h5", "caption", "body"] | None = Field(
|
||||
default=None, alias="usageHint", description="Semantic hint for text styling."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
"""Displays an image."""
|
||||
|
||||
url: StringBinding = Field(description="Image source URL.")
|
||||
fit: Literal["contain", "cover", "fill", "none", "scale-down"] | None = Field(
|
||||
default=None, description="Object-fit behavior for the image."
|
||||
)
|
||||
usage_hint: (
|
||||
Literal[
|
||||
"icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"
|
||||
]
|
||||
| None
|
||||
) = Field(
|
||||
default=None, alias="usageHint", description="Semantic hint for image sizing."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
IconName = Literal[
|
||||
"accountCircle",
|
||||
"add",
|
||||
"arrowBack",
|
||||
"arrowForward",
|
||||
"attachFile",
|
||||
"calendarToday",
|
||||
"call",
|
||||
"camera",
|
||||
"check",
|
||||
"close",
|
||||
"delete",
|
||||
"download",
|
||||
"edit",
|
||||
"event",
|
||||
"error",
|
||||
"favorite",
|
||||
"favoriteOff",
|
||||
"folder",
|
||||
"help",
|
||||
"home",
|
||||
"info",
|
||||
"locationOn",
|
||||
"lock",
|
||||
"lockOpen",
|
||||
"mail",
|
||||
"menu",
|
||||
"moreVert",
|
||||
"moreHoriz",
|
||||
"notificationsOff",
|
||||
"notifications",
|
||||
"payment",
|
||||
"person",
|
||||
"phone",
|
||||
"photo",
|
||||
"print",
|
||||
"refresh",
|
||||
"search",
|
||||
"send",
|
||||
"settings",
|
||||
"share",
|
||||
"shoppingCart",
|
||||
"star",
|
||||
"starHalf",
|
||||
"starOff",
|
||||
"upload",
|
||||
"visibility",
|
||||
"visibilityOff",
|
||||
"warning",
|
||||
]
|
||||
|
||||
|
||||
class IconBinding(BaseModel):
|
||||
"""Icon name: literal enum or data-model path."""
|
||||
|
||||
literal_string: IconName | None = Field(
|
||||
default=None, alias="literalString", description="Literal icon name."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Icon(BaseModel):
|
||||
"""Displays a named icon."""
|
||||
|
||||
name: IconBinding = Field(description="Icon name binding.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Video(BaseModel):
|
||||
"""Displays a video player."""
|
||||
|
||||
url: StringBinding = Field(description="Video source URL.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class AudioPlayer(BaseModel):
|
||||
"""Displays an audio player."""
|
||||
|
||||
url: StringBinding = Field(description="Audio source URL.")
|
||||
description: StringBinding | None = Field(
|
||||
default=None, description="Accessible description of the audio content."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Row(BaseModel):
|
||||
"""Horizontal layout container."""
|
||||
|
||||
children: ChildrenDef = Field(description="Child components in this row.")
|
||||
distribution: (
|
||||
Literal["center", "end", "spaceAround", "spaceBetween", "spaceEvenly", "start"]
|
||||
| None
|
||||
) = Field(
|
||||
default=None, description="How children are distributed along the main axis."
|
||||
)
|
||||
alignment: Literal["start", "center", "end", "stretch"] | None = Field(
|
||||
default=None, description="How children are aligned on the cross axis."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Column(BaseModel):
|
||||
"""Vertical layout container."""
|
||||
|
||||
children: ChildrenDef = Field(description="Child components in this column.")
|
||||
distribution: (
|
||||
Literal["start", "center", "end", "spaceBetween", "spaceAround", "spaceEvenly"]
|
||||
| None
|
||||
) = Field(
|
||||
default=None, description="How children are distributed along the main axis."
|
||||
)
|
||||
alignment: Literal["center", "end", "start", "stretch"] | None = Field(
|
||||
default=None, description="How children are aligned on the cross axis."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class List(BaseModel):
|
||||
"""Scrollable list container."""
|
||||
|
||||
children: ChildrenDef = Field(description="Child components in this list.")
|
||||
direction: Literal["vertical", "horizontal"] | None = Field(
|
||||
default=None, description="Scroll direction of the list."
|
||||
)
|
||||
alignment: Literal["start", "center", "end", "stretch"] | None = Field(
|
||||
default=None, description="How children are aligned on the cross axis."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Card(BaseModel):
|
||||
"""Card container wrapping a single child."""
|
||||
|
||||
child: str = Field(description="Component ID of the card content.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Tabs(BaseModel):
|
||||
"""Tabbed navigation container."""
|
||||
|
||||
tab_items: list[TabItem] = Field(
|
||||
alias="tabItems", description="List of tab definitions."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Divider(BaseModel):
|
||||
"""A visual divider line."""
|
||||
|
||||
axis: Literal["horizontal", "vertical"] | None = Field(
|
||||
default=None, description="Orientation of the divider."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class Modal(BaseModel):
|
||||
"""A modal dialog with an entry point trigger and content."""
|
||||
|
||||
entry_point_child: str = Field(
|
||||
alias="entryPointChild", description="Component ID that triggers the modal."
|
||||
)
|
||||
content_child: str = Field(
|
||||
alias="contentChild", description="Component ID rendered inside the modal."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Button(BaseModel):
|
||||
"""An interactive button with an action."""
|
||||
|
||||
child: str = Field(description="Component ID of the button label.")
|
||||
primary: bool | None = Field(
|
||||
default=None, description="Whether the button uses primary styling."
|
||||
)
|
||||
action: Action = Field(description="Action dispatched when the button is clicked.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class CheckBox(BaseModel):
|
||||
"""A checkbox input."""
|
||||
|
||||
label: StringBinding = Field(description="Label text for the checkbox.")
|
||||
value: BooleanBinding = Field(
|
||||
description="Boolean value binding for the checkbox state."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class TextField(BaseModel):
|
||||
"""A text input field."""
|
||||
|
||||
label: StringBinding = Field(description="Label text for the input.")
|
||||
text: StringBinding | None = Field(
|
||||
default=None, description="Current text value binding."
|
||||
)
|
||||
text_field_type: (
|
||||
Literal["date", "longText", "number", "shortText", "obscured"] | None
|
||||
) = Field(default=None, alias="textFieldType", description="Input type variant.")
|
||||
validation_regexp: str | None = Field(
|
||||
default=None,
|
||||
alias="validationRegexp",
|
||||
description="Regex pattern for client-side validation.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class DateTimeInput(BaseModel):
|
||||
"""A date and/or time picker."""
|
||||
|
||||
value: StringBinding = Field(description="ISO date/time string value binding.")
|
||||
enable_date: bool | None = Field(
|
||||
default=None,
|
||||
alias="enableDate",
|
||||
description="Whether the date picker is enabled.",
|
||||
)
|
||||
enable_time: bool | None = Field(
|
||||
default=None,
|
||||
alias="enableTime",
|
||||
description="Whether the time picker is enabled.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class MultipleChoice(BaseModel):
|
||||
"""A multiple-choice selection component."""
|
||||
|
||||
selections: ArrayBinding = Field(description="Array binding for selected values.")
|
||||
options: list[MultipleChoiceOption] = Field(description="Available choices.")
|
||||
max_allowed_selections: int | None = Field(
|
||||
default=None,
|
||||
alias="maxAllowedSelections",
|
||||
description="Maximum number of selections allowed.",
|
||||
)
|
||||
variant: Literal["checkbox", "chips"] | None = Field(
|
||||
default=None, description="Visual variant for the selection UI."
|
||||
)
|
||||
filterable: bool | None = Field(
|
||||
default=None, description="Whether options can be filtered by typing."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Slider(BaseModel):
|
||||
"""A numeric slider input."""
|
||||
|
||||
value: NumberBinding = Field(
|
||||
description="Numeric value binding for the slider position."
|
||||
)
|
||||
min_value: float | None = Field(
|
||||
default=None, alias="minValue", description="Minimum slider value."
|
||||
)
|
||||
max_value: float | None = Field(
|
||||
default=None, alias="maxValue", description="Maximum slider value."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
STANDARD_CATALOG_COMPONENTS: frozenset[str] = frozenset(
|
||||
{
|
||||
"Text",
|
||||
"Image",
|
||||
"Icon",
|
||||
"Video",
|
||||
"AudioPlayer",
|
||||
"Row",
|
||||
"Column",
|
||||
"List",
|
||||
"Card",
|
||||
"Tabs",
|
||||
"Divider",
|
||||
"Modal",
|
||||
"Button",
|
||||
"CheckBox",
|
||||
"TextField",
|
||||
"DateTimeInput",
|
||||
"MultipleChoice",
|
||||
"Slider",
|
||||
}
|
||||
)
|
||||
496
lib/crewai/src/crewai/a2a/extensions/a2ui/client_extension.py
Normal file
496
lib/crewai/src/crewai/a2a/extensions/a2ui/client_extension.py
Normal file
@@ -0,0 +1,496 @@
|
||||
"""A2UI client extension for the A2A protocol."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Literal, cast
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic.dataclasses import dataclass
|
||||
from typing_extensions import TypeIs, TypedDict
|
||||
|
||||
from crewai.a2a.extensions.a2ui.models import extract_a2ui_json_objects
|
||||
from crewai.a2a.extensions.a2ui.prompt import (
|
||||
build_a2ui_system_prompt,
|
||||
build_a2ui_v09_system_prompt,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.server_extension import (
|
||||
A2UI_MIME_TYPE,
|
||||
A2UI_STANDARD_CATALOG_ID,
|
||||
A2UI_V09_BASIC_CATALOG_ID,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.v0_9 import extract_a2ui_v09_json_objects
|
||||
from crewai.a2a.extensions.a2ui.validator import (
|
||||
A2UIValidationError,
|
||||
validate_a2ui_message,
|
||||
validate_a2ui_message_v09,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from a2a.types import Message
|
||||
|
||||
from crewai.agent.core import Agent
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StylesDict(TypedDict, total=False):
|
||||
"""Serialized surface styling."""
|
||||
|
||||
font: str
|
||||
primaryColor: str
|
||||
|
||||
|
||||
class ComponentEntryDict(TypedDict, total=False):
|
||||
"""Serialized component entry in a surface update."""
|
||||
|
||||
id: str
|
||||
weight: float
|
||||
component: dict[str, Any]
|
||||
|
||||
|
||||
class BeginRenderingDict(TypedDict, total=False):
|
||||
"""Serialized beginRendering payload."""
|
||||
|
||||
surfaceId: str
|
||||
root: str
|
||||
catalogId: str
|
||||
styles: StylesDict
|
||||
|
||||
|
||||
class SurfaceUpdateDict(TypedDict, total=False):
|
||||
"""Serialized surfaceUpdate payload."""
|
||||
|
||||
surfaceId: str
|
||||
components: list[ComponentEntryDict]
|
||||
|
||||
|
||||
class DataEntryDict(TypedDict, total=False):
|
||||
"""Serialized data model entry."""
|
||||
|
||||
key: str
|
||||
valueString: str
|
||||
valueNumber: float
|
||||
valueBoolean: bool
|
||||
valueMap: list[DataEntryDict]
|
||||
|
||||
|
||||
class DataModelUpdateDict(TypedDict, total=False):
|
||||
"""Serialized dataModelUpdate payload."""
|
||||
|
||||
surfaceId: str
|
||||
path: str
|
||||
contents: list[DataEntryDict]
|
||||
|
||||
|
||||
class DeleteSurfaceDict(TypedDict):
|
||||
"""Serialized deleteSurface payload."""
|
||||
|
||||
surfaceId: str
|
||||
|
||||
|
||||
class A2UIMessageDict(TypedDict, total=False):
|
||||
"""Serialized A2UI v0.8 server-to-client message with exactly one key set."""
|
||||
|
||||
beginRendering: BeginRenderingDict
|
||||
surfaceUpdate: SurfaceUpdateDict
|
||||
dataModelUpdate: DataModelUpdateDict
|
||||
deleteSurface: DeleteSurfaceDict
|
||||
|
||||
|
||||
class ThemeDict(TypedDict, total=False):
|
||||
"""Serialized v0.9 theme."""
|
||||
|
||||
primaryColor: str
|
||||
iconUrl: str
|
||||
agentDisplayName: str
|
||||
|
||||
|
||||
class CreateSurfaceDict(TypedDict, total=False):
|
||||
"""Serialized createSurface payload."""
|
||||
|
||||
surfaceId: str
|
||||
catalogId: str
|
||||
theme: ThemeDict
|
||||
sendDataModel: bool
|
||||
|
||||
|
||||
class UpdateComponentsDict(TypedDict, total=False):
|
||||
"""Serialized updateComponents payload."""
|
||||
|
||||
surfaceId: str
|
||||
components: list[dict[str, Any]]
|
||||
|
||||
|
||||
class UpdateDataModelDict(TypedDict, total=False):
|
||||
"""Serialized updateDataModel payload."""
|
||||
|
||||
surfaceId: str
|
||||
path: str
|
||||
value: Any
|
||||
|
||||
|
||||
class DeleteSurfaceV09Dict(TypedDict):
|
||||
"""Serialized v0.9 deleteSurface payload."""
|
||||
|
||||
surfaceId: str
|
||||
|
||||
|
||||
class A2UIMessageV09Dict(TypedDict, total=False):
|
||||
"""Serialized A2UI v0.9 server-to-client message with version and exactly one key set."""
|
||||
|
||||
version: Literal["v0.9"]
|
||||
createSurface: CreateSurfaceDict
|
||||
updateComponents: UpdateComponentsDict
|
||||
updateDataModel: UpdateDataModelDict
|
||||
deleteSurface: DeleteSurfaceV09Dict
|
||||
|
||||
|
||||
A2UIAnyMessageDict = A2UIMessageDict | A2UIMessageV09Dict
|
||||
|
||||
|
||||
def is_v09_message(msg: A2UIAnyMessageDict) -> TypeIs[A2UIMessageV09Dict]:
|
||||
"""Narrow a message dict to the v0.9 variant."""
|
||||
return msg.get("version") == "v0.9"
|
||||
|
||||
|
||||
def is_v08_message(msg: A2UIAnyMessageDict) -> TypeIs[A2UIMessageDict]:
|
||||
"""Narrow a message dict to the v0.8 variant."""
|
||||
return "version" not in msg
|
||||
|
||||
|
||||
@dataclass
|
||||
class A2UIConversationState:
|
||||
"""Tracks active A2UI surfaces and data models across a conversation."""
|
||||
|
||||
active_surfaces: dict[str, dict[str, Any]] = Field(default_factory=dict)
|
||||
data_models: dict[str, list[dict[str, Any]]] = Field(default_factory=dict)
|
||||
last_a2ui_messages: list[A2UIAnyMessageDict] = Field(default_factory=list)
|
||||
initialized_surfaces: set[str] = Field(default_factory=set)
|
||||
|
||||
def is_ready(self) -> bool:
|
||||
"""Return True when at least one surface has been initialized via beginRendering."""
|
||||
return bool(self.initialized_surfaces)
|
||||
|
||||
|
||||
class A2UIClientExtension:
|
||||
"""A2A client extension that adds A2UI support to agents.
|
||||
|
||||
Implements the ``A2AExtension`` protocol to inject A2UI prompt
|
||||
instructions, track UI state across conversations, and validate
|
||||
A2UI messages in responses.
|
||||
|
||||
Example::
|
||||
|
||||
A2AClientConfig(
|
||||
endpoint="...",
|
||||
extensions=["https://a2ui.org/a2a-extension/a2ui/v0.8"],
|
||||
client_extensions=[A2UIClientExtension()],
|
||||
)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
catalog_id: str | None = None,
|
||||
allowed_components: list[str] | None = None,
|
||||
version: str = "v0.8",
|
||||
) -> None:
|
||||
"""Initialize the A2UI client extension.
|
||||
|
||||
Args:
|
||||
catalog_id: Catalog identifier to use for prompt generation.
|
||||
allowed_components: Subset of component names to expose to the agent.
|
||||
version: Protocol version, ``"v0.8"`` or ``"v0.9"``.
|
||||
"""
|
||||
self._catalog_id = catalog_id
|
||||
self._allowed_components = allowed_components
|
||||
self._version = version
|
||||
|
||||
def inject_tools(self, agent: Agent) -> None:
|
||||
"""No-op — A2UI uses prompt augmentation rather than tool injection."""
|
||||
|
||||
def extract_state_from_history(
|
||||
self, conversation_history: Sequence[Message]
|
||||
) -> A2UIConversationState | None:
|
||||
"""Scan conversation history for A2UI DataParts and track surface state.
|
||||
|
||||
When ``catalog_id`` is set, only surfaces matching that catalog are tracked.
|
||||
"""
|
||||
state = A2UIConversationState()
|
||||
|
||||
for message in conversation_history:
|
||||
for part in message.parts:
|
||||
root = part.root
|
||||
if root.kind != "data":
|
||||
continue
|
||||
metadata = root.metadata or {}
|
||||
mime_type = metadata.get("mimeType", "")
|
||||
if mime_type != A2UI_MIME_TYPE:
|
||||
continue
|
||||
|
||||
data = root.data
|
||||
if not isinstance(data, dict):
|
||||
continue
|
||||
|
||||
surface_id = _get_surface_id(data)
|
||||
if not surface_id:
|
||||
continue
|
||||
|
||||
if self._catalog_id and "beginRendering" in data:
|
||||
catalog_id = data["beginRendering"].get("catalogId")
|
||||
if catalog_id and catalog_id != self._catalog_id:
|
||||
continue
|
||||
if self._catalog_id and "createSurface" in data:
|
||||
catalog_id = data["createSurface"].get("catalogId")
|
||||
if catalog_id and catalog_id != self._catalog_id:
|
||||
continue
|
||||
|
||||
if "deleteSurface" in data:
|
||||
state.active_surfaces.pop(surface_id, None)
|
||||
state.data_models.pop(surface_id, None)
|
||||
state.initialized_surfaces.discard(surface_id)
|
||||
elif "beginRendering" in data:
|
||||
state.initialized_surfaces.add(surface_id)
|
||||
state.active_surfaces[surface_id] = data["beginRendering"]
|
||||
elif "createSurface" in data:
|
||||
state.initialized_surfaces.add(surface_id)
|
||||
state.active_surfaces[surface_id] = data["createSurface"]
|
||||
elif "surfaceUpdate" in data:
|
||||
if surface_id not in state.initialized_surfaces:
|
||||
logger.warning(
|
||||
"surfaceUpdate for uninitialized surface %s",
|
||||
surface_id,
|
||||
)
|
||||
state.active_surfaces[surface_id] = data["surfaceUpdate"]
|
||||
elif "updateComponents" in data:
|
||||
if surface_id not in state.initialized_surfaces:
|
||||
logger.warning(
|
||||
"updateComponents for uninitialized surface %s",
|
||||
surface_id,
|
||||
)
|
||||
state.active_surfaces[surface_id] = data["updateComponents"]
|
||||
elif "dataModelUpdate" in data:
|
||||
contents = data["dataModelUpdate"].get("contents", [])
|
||||
state.data_models.setdefault(surface_id, []).extend(contents)
|
||||
elif "updateDataModel" in data:
|
||||
update = data["updateDataModel"]
|
||||
state.data_models.setdefault(surface_id, []).append(update)
|
||||
|
||||
if not state.active_surfaces and not state.data_models:
|
||||
return None
|
||||
return state
|
||||
|
||||
def augment_prompt(
|
||||
self,
|
||||
base_prompt: str,
|
||||
_conversation_state: A2UIConversationState | None,
|
||||
) -> str:
|
||||
"""Append A2UI system prompt instructions to the base prompt."""
|
||||
if self._version == "v0.9":
|
||||
a2ui_prompt = build_a2ui_v09_system_prompt(
|
||||
catalog_id=self._catalog_id,
|
||||
allowed_components=self._allowed_components,
|
||||
)
|
||||
else:
|
||||
a2ui_prompt = build_a2ui_system_prompt(
|
||||
catalog_id=self._catalog_id,
|
||||
allowed_components=self._allowed_components,
|
||||
)
|
||||
return f"{base_prompt}\n\n{a2ui_prompt}"
|
||||
|
||||
def process_response(
|
||||
self,
|
||||
agent_response: Any,
|
||||
conversation_state: A2UIConversationState | None,
|
||||
) -> Any:
|
||||
"""Extract and validate A2UI JSON from agent output.
|
||||
|
||||
When ``allowed_components`` is set, components not in the allowlist are
|
||||
logged and stripped from surface updates. Stores extracted A2UI messages
|
||||
on the conversation state and returns the original response unchanged.
|
||||
"""
|
||||
text = (
|
||||
agent_response if isinstance(agent_response, str) else str(agent_response)
|
||||
)
|
||||
results: list[A2UIAnyMessageDict]
|
||||
if self._version == "v0.9":
|
||||
results = list(_extract_and_validate_v09(text))
|
||||
if self._allowed_components:
|
||||
allowed = set(self._allowed_components)
|
||||
results = [
|
||||
_filter_components_v09(m, allowed)
|
||||
for m in results
|
||||
if is_v09_message(m)
|
||||
]
|
||||
else:
|
||||
results = list(_extract_and_validate(text))
|
||||
if self._allowed_components:
|
||||
allowed = set(self._allowed_components)
|
||||
results = [
|
||||
_filter_components(msg, allowed)
|
||||
for msg in results
|
||||
if is_v08_message(msg)
|
||||
]
|
||||
|
||||
if results and conversation_state is not None:
|
||||
conversation_state.last_a2ui_messages = results
|
||||
|
||||
return agent_response
|
||||
|
||||
def prepare_message_metadata(
|
||||
self,
|
||||
_conversation_state: A2UIConversationState | None,
|
||||
) -> dict[str, Any]:
|
||||
"""Inject a2uiClientCapabilities into outbound A2A message metadata.
|
||||
|
||||
Per the A2UI extension spec, clients must declare supported catalog
|
||||
IDs in every outbound message's metadata. v0.9 nests capabilities
|
||||
under a ``"v0.9"`` key per ``client_capabilities.json``.
|
||||
"""
|
||||
if self._version == "v0.9":
|
||||
default_catalog = A2UI_V09_BASIC_CATALOG_ID
|
||||
catalog_ids = [default_catalog]
|
||||
if self._catalog_id and self._catalog_id != default_catalog:
|
||||
catalog_ids.append(self._catalog_id)
|
||||
return {
|
||||
"a2uiClientCapabilities": {
|
||||
"v0.9": {
|
||||
"supportedCatalogIds": catalog_ids,
|
||||
},
|
||||
},
|
||||
}
|
||||
catalog_ids = [A2UI_STANDARD_CATALOG_ID]
|
||||
if self._catalog_id and self._catalog_id != A2UI_STANDARD_CATALOG_ID:
|
||||
catalog_ids.append(self._catalog_id)
|
||||
return {
|
||||
"a2uiClientCapabilities": {
|
||||
"supportedCatalogIds": catalog_ids,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
_ALL_SURFACE_ID_KEYS = (
|
||||
"beginRendering",
|
||||
"surfaceUpdate",
|
||||
"dataModelUpdate",
|
||||
"deleteSurface",
|
||||
"createSurface",
|
||||
"updateComponents",
|
||||
"updateDataModel",
|
||||
)
|
||||
|
||||
|
||||
def _get_surface_id(data: dict[str, Any]) -> str | None:
|
||||
"""Extract surfaceId from any A2UI v0.8 or v0.9 message type."""
|
||||
for key in _ALL_SURFACE_ID_KEYS:
|
||||
inner = data.get(key)
|
||||
if isinstance(inner, dict):
|
||||
sid = inner.get("surfaceId")
|
||||
if isinstance(sid, str):
|
||||
return sid
|
||||
return None
|
||||
|
||||
|
||||
def _filter_components(msg: A2UIMessageDict, allowed: set[str]) -> A2UIMessageDict:
|
||||
"""Strip components whose type is not in *allowed* from a surfaceUpdate."""
|
||||
surface_update = msg.get("surfaceUpdate")
|
||||
if not isinstance(surface_update, dict):
|
||||
return msg
|
||||
|
||||
components = surface_update.get("components")
|
||||
if not isinstance(components, list):
|
||||
return msg
|
||||
|
||||
filtered = []
|
||||
for entry in components:
|
||||
component = entry.get("component", {})
|
||||
component_types = set(component.keys())
|
||||
disallowed = component_types - allowed
|
||||
if disallowed:
|
||||
logger.debug(
|
||||
"Stripping disallowed component type(s) %s from surface update",
|
||||
disallowed,
|
||||
)
|
||||
continue
|
||||
filtered.append(entry)
|
||||
|
||||
if len(filtered) == len(components):
|
||||
return msg
|
||||
|
||||
return {**msg, "surfaceUpdate": {**surface_update, "components": filtered}}
|
||||
|
||||
|
||||
def _filter_components_v09(
|
||||
msg: A2UIMessageV09Dict, allowed: set[str]
|
||||
) -> A2UIMessageV09Dict:
|
||||
"""Strip v0.9 components whose type is not in *allowed* from updateComponents.
|
||||
|
||||
v0.9 components use a flat structure where ``component`` is a type-name string.
|
||||
"""
|
||||
update = msg.get("updateComponents")
|
||||
if not isinstance(update, dict):
|
||||
return msg
|
||||
|
||||
components = update.get("components")
|
||||
if not isinstance(components, list):
|
||||
return msg
|
||||
|
||||
filtered = []
|
||||
for entry in components:
|
||||
comp_type = entry.get("component") if isinstance(entry, dict) else None
|
||||
if isinstance(comp_type, str) and comp_type not in allowed:
|
||||
logger.debug("Stripping disallowed v0.9 component type %s", comp_type)
|
||||
continue
|
||||
filtered.append(entry)
|
||||
|
||||
if len(filtered) == len(components):
|
||||
return msg
|
||||
|
||||
return {**msg, "updateComponents": {**update, "components": filtered}}
|
||||
|
||||
|
||||
def _extract_and_validate(text: str) -> list[A2UIMessageDict]:
|
||||
"""Extract A2UI v0.8 JSON objects from text and validate them."""
|
||||
return [
|
||||
dumped
|
||||
for candidate in extract_a2ui_json_objects(text)
|
||||
if (dumped := _try_validate(candidate)) is not None
|
||||
]
|
||||
|
||||
|
||||
def _try_validate(candidate: dict[str, Any]) -> A2UIMessageDict | None:
|
||||
"""Validate a single v0.8 A2UI candidate, returning None on failure."""
|
||||
try:
|
||||
msg = validate_a2ui_message(candidate)
|
||||
except A2UIValidationError:
|
||||
logger.debug(
|
||||
"Skipping invalid A2UI candidate in agent output",
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
return cast(A2UIMessageDict, msg.model_dump(by_alias=True, exclude_none=True))
|
||||
|
||||
|
||||
def _extract_and_validate_v09(text: str) -> list[A2UIMessageV09Dict]:
|
||||
"""Extract and validate v0.9 A2UI JSON objects from text."""
|
||||
return [
|
||||
dumped
|
||||
for candidate in extract_a2ui_v09_json_objects(text)
|
||||
if (dumped := _try_validate_v09(candidate)) is not None
|
||||
]
|
||||
|
||||
|
||||
def _try_validate_v09(candidate: dict[str, Any]) -> A2UIMessageV09Dict | None:
|
||||
"""Validate a single v0.9 A2UI candidate, returning None on failure."""
|
||||
try:
|
||||
msg = validate_a2ui_message_v09(candidate)
|
||||
except A2UIValidationError:
|
||||
logger.debug(
|
||||
"Skipping invalid A2UI v0.9 candidate in agent output",
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
return cast(A2UIMessageV09Dict, msg.model_dump(by_alias=True, exclude_none=True))
|
||||
277
lib/crewai/src/crewai/a2a/extensions/a2ui/models.py
Normal file
277
lib/crewai/src/crewai/a2a/extensions/a2ui/models.py
Normal file
@@ -0,0 +1,277 @@
|
||||
"""Pydantic models for A2UI server-to-client messages and client-to-server events."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
class BoundValue(BaseModel):
|
||||
"""A value that can be a literal or a data-model path reference."""
|
||||
|
||||
literal_string: str | None = Field(
|
||||
default=None, alias="literalString", description="Literal string value."
|
||||
)
|
||||
literal_number: float | None = Field(
|
||||
default=None, alias="literalNumber", description="Literal numeric value."
|
||||
)
|
||||
literal_boolean: bool | None = Field(
|
||||
default=None, alias="literalBoolean", description="Literal boolean value."
|
||||
)
|
||||
literal_array: list[str] | None = Field(
|
||||
default=None, alias="literalArray", description="Literal array of strings."
|
||||
)
|
||||
path: str | None = Field(default=None, description="Data-model path reference.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class MapEntry(BaseModel):
|
||||
"""A single entry in a valueMap adjacency list, supporting recursive nesting."""
|
||||
|
||||
key: str = Field(description="Entry key.")
|
||||
value_string: str | None = Field(
|
||||
default=None, alias="valueString", description="String value."
|
||||
)
|
||||
value_number: float | None = Field(
|
||||
default=None, alias="valueNumber", description="Numeric value."
|
||||
)
|
||||
value_boolean: bool | None = Field(
|
||||
default=None, alias="valueBoolean", description="Boolean value."
|
||||
)
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class DataEntry(BaseModel):
|
||||
"""A data model entry with a key and exactly one typed value."""
|
||||
|
||||
key: str = Field(description="Entry key.")
|
||||
value_string: str | None = Field(
|
||||
default=None, alias="valueString", description="String value."
|
||||
)
|
||||
value_number: float | None = Field(
|
||||
default=None, alias="valueNumber", description="Numeric value."
|
||||
)
|
||||
value_boolean: bool | None = Field(
|
||||
default=None, alias="valueBoolean", description="Boolean value."
|
||||
)
|
||||
value_map: list[MapEntry] | None = Field(
|
||||
default=None, alias="valueMap", description="Nested map entries."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
_HEX_COLOR_PATTERN: re.Pattern[str] = re.compile(r"^#[0-9a-fA-F]{6}$")
|
||||
|
||||
|
||||
class Styles(BaseModel):
|
||||
"""Surface styling information."""
|
||||
|
||||
font: str | None = Field(default=None, description="Font family name.")
|
||||
primary_color: str | None = Field(
|
||||
default=None,
|
||||
alias="primaryColor",
|
||||
pattern=_HEX_COLOR_PATTERN.pattern,
|
||||
description="Primary color as a hex string.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class ComponentEntry(BaseModel):
|
||||
"""A single component in a UI widget tree.
|
||||
|
||||
The ``component`` dict must contain exactly one key — the component type
|
||||
name (e.g. ``"Text"``, ``"Button"``) — whose value holds the component
|
||||
properties. Component internals are left as ``dict[str, Any]`` because
|
||||
they are catalog-dependent; use the typed helpers in ``catalog.py`` for
|
||||
the standard catalog.
|
||||
"""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
weight: float | None = Field(
|
||||
default=None, description="Flex weight for layout distribution."
|
||||
)
|
||||
component: dict[str, Any] = Field(
|
||||
description="Component type name mapped to its properties."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class BeginRendering(BaseModel):
|
||||
"""Signals the client to begin rendering a surface."""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Unique surface identifier.")
|
||||
root: str = Field(description="Component ID of the root element.")
|
||||
catalog_id: str | None = Field(
|
||||
default=None,
|
||||
alias="catalogId",
|
||||
description="Catalog identifier for the surface.",
|
||||
)
|
||||
styles: Styles | None = Field(
|
||||
default=None, description="Surface styling overrides."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class SurfaceUpdate(BaseModel):
|
||||
"""Updates a surface with a new set of components."""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Target surface identifier.")
|
||||
components: list[ComponentEntry] = Field(
|
||||
min_length=1, description="Components to render on the surface."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class DataModelUpdate(BaseModel):
|
||||
"""Updates the data model for a surface."""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Target surface identifier.")
|
||||
path: str | None = Field(
|
||||
default=None, description="Data-model path prefix for the update."
|
||||
)
|
||||
contents: list[DataEntry] = Field(
|
||||
description="Data entries to merge into the model."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class DeleteSurface(BaseModel):
|
||||
"""Signals the client to delete a surface."""
|
||||
|
||||
surface_id: str = Field(
|
||||
alias="surfaceId", description="Surface identifier to delete."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class A2UIMessage(BaseModel):
|
||||
"""Union wrapper for the four server-to-client A2UI message types.
|
||||
|
||||
Exactly one of the fields must be set.
|
||||
"""
|
||||
|
||||
begin_rendering: BeginRendering | None = Field(
|
||||
default=None,
|
||||
alias="beginRendering",
|
||||
description="Begin rendering a new surface.",
|
||||
)
|
||||
surface_update: SurfaceUpdate | None = Field(
|
||||
default=None,
|
||||
alias="surfaceUpdate",
|
||||
description="Update components on a surface.",
|
||||
)
|
||||
data_model_update: DataModelUpdate | None = Field(
|
||||
default=None,
|
||||
alias="dataModelUpdate",
|
||||
description="Update the surface data model.",
|
||||
)
|
||||
delete_surface: DeleteSurface | None = Field(
|
||||
default=None, alias="deleteSurface", description="Delete an existing surface."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_exactly_one(self) -> A2UIMessage:
|
||||
"""Enforce the spec's exactly-one-of constraint."""
|
||||
fields = [
|
||||
self.begin_rendering,
|
||||
self.surface_update,
|
||||
self.data_model_update,
|
||||
self.delete_surface,
|
||||
]
|
||||
count = sum(f is not None for f in fields)
|
||||
if count != 1:
|
||||
raise ValueError(f"Exactly one A2UI message type must be set, got {count}")
|
||||
return self
|
||||
|
||||
|
||||
class UserAction(BaseModel):
|
||||
"""Reports a user-initiated action from a component."""
|
||||
|
||||
name: str = Field(description="Action name.")
|
||||
surface_id: str = Field(alias="surfaceId", description="Source surface identifier.")
|
||||
source_component_id: str = Field(
|
||||
alias="sourceComponentId", description="Component that triggered the action."
|
||||
)
|
||||
timestamp: str = Field(description="ISO 8601 timestamp of the action.")
|
||||
context: dict[str, Any] = Field(description="Action context payload.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
|
||||
class ClientError(BaseModel):
|
||||
"""Reports a client-side error."""
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
class A2UIEvent(BaseModel):
|
||||
"""Union wrapper for client-to-server events."""
|
||||
|
||||
user_action: UserAction | None = Field(
|
||||
default=None, alias="userAction", description="User-initiated action event."
|
||||
)
|
||||
error: ClientError | None = Field(
|
||||
default=None, description="Client-side error report."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_exactly_one(self) -> A2UIEvent:
|
||||
"""Enforce the spec's exactly-one-of constraint."""
|
||||
fields = [self.user_action, self.error]
|
||||
count = sum(f is not None for f in fields)
|
||||
if count != 1:
|
||||
raise ValueError(f"Exactly one A2UI event type must be set, got {count}")
|
||||
return self
|
||||
|
||||
|
||||
class A2UIResponse(BaseModel):
|
||||
"""Typed wrapper for responses containing A2UI messages."""
|
||||
|
||||
text: str = Field(description="Raw text content of the response.")
|
||||
a2ui_parts: list[dict[str, Any]] = Field(
|
||||
default_factory=list, description="A2UI DataParts extracted from the response."
|
||||
)
|
||||
a2ui_messages: list[dict[str, Any]] = Field(
|
||||
default_factory=list, description="Validated A2UI message dicts."
|
||||
)
|
||||
|
||||
|
||||
_A2UI_KEYS = {"beginRendering", "surfaceUpdate", "dataModelUpdate", "deleteSurface"}
|
||||
|
||||
|
||||
def extract_a2ui_json_objects(text: str) -> list[dict[str, Any]]:
|
||||
"""Extract JSON objects containing A2UI keys from text.
|
||||
|
||||
Uses ``json.JSONDecoder.raw_decode`` for robust parsing that correctly
|
||||
handles braces inside string literals.
|
||||
"""
|
||||
decoder = json.JSONDecoder()
|
||||
results: list[dict[str, Any]] = []
|
||||
idx = 0
|
||||
while idx < len(text):
|
||||
idx = text.find("{", idx)
|
||||
if idx == -1:
|
||||
break
|
||||
try:
|
||||
obj, end_idx = decoder.raw_decode(text, idx)
|
||||
if isinstance(obj, dict) and _A2UI_KEYS & obj.keys():
|
||||
results.append(obj)
|
||||
idx = end_idx
|
||||
except json.JSONDecodeError:
|
||||
idx += 1
|
||||
return results
|
||||
150
lib/crewai/src/crewai/a2a/extensions/a2ui/prompt.py
Normal file
150
lib/crewai/src/crewai/a2a/extensions/a2ui/prompt.py
Normal file
@@ -0,0 +1,150 @@
|
||||
"""System prompt generation for A2UI-capable agents."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from crewai.a2a.extensions.a2ui.catalog import STANDARD_CATALOG_COMPONENTS
|
||||
from crewai.a2a.extensions.a2ui.schema import load_schema
|
||||
from crewai.a2a.extensions.a2ui.server_extension import (
|
||||
A2UI_EXTENSION_URI,
|
||||
A2UI_V09_BASIC_CATALOG_ID,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.v0_9 import (
|
||||
BASIC_CATALOG_COMPONENTS as V09_CATALOG_COMPONENTS,
|
||||
BASIC_CATALOG_FUNCTIONS,
|
||||
)
|
||||
|
||||
|
||||
def build_a2ui_system_prompt(
|
||||
catalog_id: str | None = None,
|
||||
allowed_components: list[str] | None = None,
|
||||
) -> str:
|
||||
"""Build a v0.8 system prompt fragment instructing the LLM to produce A2UI output.
|
||||
|
||||
Args:
|
||||
catalog_id: Catalog identifier to reference. Defaults to the
|
||||
standard catalog version derived from ``A2UI_EXTENSION_URI``.
|
||||
allowed_components: Subset of component names to expose. When
|
||||
``None``, all standard catalog components are available.
|
||||
|
||||
Returns:
|
||||
A system prompt string to append to the agent's instructions.
|
||||
"""
|
||||
components = sorted(
|
||||
allowed_components
|
||||
if allowed_components is not None
|
||||
else STANDARD_CATALOG_COMPONENTS
|
||||
)
|
||||
|
||||
catalog_label = catalog_id or f"standard ({A2UI_EXTENSION_URI.rsplit('/', 1)[-1]})"
|
||||
|
||||
resolved_schema = load_schema(
|
||||
"server_to_client_with_standard_catalog", version="v0.8"
|
||||
)
|
||||
schema_json = json.dumps(resolved_schema, indent=2)
|
||||
|
||||
return f"""\
|
||||
<A2UI_INSTRUCTIONS>
|
||||
You can generate rich, declarative UI by emitting A2UI JSON messages.
|
||||
|
||||
CATALOG: {catalog_label}
|
||||
AVAILABLE COMPONENTS: {", ".join(components)}
|
||||
|
||||
MESSAGE TYPES (emit exactly ONE per message):
|
||||
- beginRendering: Initialize a new surface with a root component and optional styles.
|
||||
- surfaceUpdate: Send/update components for a surface. Each component has a unique id \
|
||||
and a "component" wrapper containing exactly one component-type key.
|
||||
- dataModelUpdate: Update the data model for a surface. Data entries have a key and \
|
||||
one typed value (valueString, valueNumber, valueBoolean, valueMap).
|
||||
- deleteSurface: Remove a surface.
|
||||
|
||||
DATA BINDING:
|
||||
- Use {{"literalString": "..."}} for inline string values.
|
||||
- Use {{"literalNumber": ...}} for inline numeric values.
|
||||
- Use {{"literalBoolean": ...}} for inline boolean values.
|
||||
- Use {{"literalArray": ["...", "..."]}} for inline array values.
|
||||
- Use {{"path": "/data/model/path"}} to bind to data model values.
|
||||
|
||||
ACTIONS:
|
||||
- Interactive components (Button, etc.) have an "action" with a "name" and optional \
|
||||
"context" array of key/value pairs.
|
||||
- Values in action context can use data binding (path or literal).
|
||||
|
||||
OUTPUT FORMAT:
|
||||
Emit each A2UI message as a valid JSON object. When generating UI, produce a \
|
||||
beginRendering message first, then surfaceUpdate messages with components, and \
|
||||
optionally dataModelUpdate messages to populate data-bound values.
|
||||
|
||||
SCHEMA:
|
||||
{schema_json}
|
||||
</A2UI_INSTRUCTIONS>"""
|
||||
|
||||
|
||||
def build_a2ui_v09_system_prompt(
|
||||
catalog_id: str | None = None,
|
||||
allowed_components: list[str] | None = None,
|
||||
) -> str:
|
||||
"""Build a v0.9 system prompt fragment instructing the LLM to produce A2UI output.
|
||||
|
||||
Args:
|
||||
catalog_id: Catalog identifier to reference. Defaults to the
|
||||
v0.9 basic catalog.
|
||||
allowed_components: Subset of component names to expose. When
|
||||
``None``, all basic catalog components are available.
|
||||
|
||||
Returns:
|
||||
A system prompt string to append to the agent's instructions.
|
||||
"""
|
||||
components = sorted(
|
||||
allowed_components if allowed_components is not None else V09_CATALOG_COMPONENTS
|
||||
)
|
||||
|
||||
catalog_label = catalog_id or A2UI_V09_BASIC_CATALOG_ID
|
||||
functions = sorted(BASIC_CATALOG_FUNCTIONS)
|
||||
|
||||
envelope_schema = load_schema("server_to_client", version="v0.9")
|
||||
schema_json = json.dumps(envelope_schema, indent=2)
|
||||
|
||||
return f"""\
|
||||
<A2UI_INSTRUCTIONS>
|
||||
You can generate rich, declarative UI by emitting A2UI v0.9 JSON messages.
|
||||
Every message MUST include "version": "v0.9".
|
||||
|
||||
CATALOG: {catalog_label}
|
||||
AVAILABLE COMPONENTS: {", ".join(components)}
|
||||
AVAILABLE FUNCTIONS: {", ".join(functions)}
|
||||
|
||||
MESSAGE TYPES (emit exactly ONE per message alongside "version": "v0.9"):
|
||||
- createSurface: Create a new surface. Requires surfaceId and catalogId. \
|
||||
Optionally includes theme (primaryColor, iconUrl, agentDisplayName) and \
|
||||
sendDataModel (boolean).
|
||||
- updateComponents: Send/update components for a surface. Each component is a flat \
|
||||
object with "id", "component" (type name string), and type-specific properties at the \
|
||||
top level. One component MUST have id "root".
|
||||
- updateDataModel: Update the data model. Uses "path" (JSON Pointer) and "value" \
|
||||
(any JSON type). Omit "value" to delete the key at path.
|
||||
- deleteSurface: Remove a surface by surfaceId.
|
||||
|
||||
COMPONENT FORMAT (flat, NOT nested):
|
||||
{{"id": "myText", "component": "Text", "text": "Hello world", "variant": "h1"}}
|
||||
{{"id": "myBtn", "component": "Button", "child": "myText", "action": {{"event": \
|
||||
{{"name": "click"}}}}}}
|
||||
|
||||
DATA BINDING:
|
||||
- Use plain values for literals: "text": "Hello" or "value": 42
|
||||
- Use {{"path": "/data/model/path"}} to bind to data model values.
|
||||
- Use {{"call": "functionName", "args": {{...}}}} for client-side functions.
|
||||
|
||||
ACTIONS:
|
||||
- Server event: {{"event": {{"name": "actionName", "context": {{"key": "value"}}}}}}
|
||||
- Local function: {{"functionCall": {{"call": "openUrl", "args": {{"url": "..."}}}}}}
|
||||
|
||||
OUTPUT FORMAT:
|
||||
Emit each A2UI message as a valid JSON object. When generating UI, first emit a \
|
||||
createSurface message with the catalogId, then updateComponents messages with \
|
||||
components (one must have id "root"), and optionally updateDataModel messages.
|
||||
|
||||
ENVELOPE SCHEMA:
|
||||
{schema_json}
|
||||
</A2UI_INSTRUCTIONS>"""
|
||||
74
lib/crewai/src/crewai/a2a/extensions/a2ui/schema/__init__.py
Normal file
74
lib/crewai/src/crewai/a2a/extensions/a2ui/schema/__init__.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""Schema loading utilities for vendored A2UI JSON schemas."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
_V08_DIR = Path(__file__).parent / "v0_8"
|
||||
_V09_DIR = Path(__file__).parent / "v0_9"
|
||||
|
||||
_SCHEMA_CACHE: dict[str, dict[str, Any]] = {}
|
||||
|
||||
SCHEMA_NAMES: frozenset[str] = frozenset(
|
||||
{
|
||||
"server_to_client",
|
||||
"client_to_server",
|
||||
"standard_catalog_definition",
|
||||
"server_to_client_with_standard_catalog",
|
||||
}
|
||||
)
|
||||
|
||||
V09_SCHEMA_NAMES: frozenset[str] = frozenset(
|
||||
{
|
||||
"server_to_client",
|
||||
"client_to_server",
|
||||
"common_types",
|
||||
"basic_catalog",
|
||||
"client_capabilities",
|
||||
"server_capabilities",
|
||||
"client_data_model",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def load_schema(name: str, *, version: str = "v0.8") -> dict[str, Any]:
|
||||
"""Load a vendored A2UI JSON schema by name and version.
|
||||
|
||||
Args:
|
||||
name: Schema name without extension, e.g. ``"server_to_client"``.
|
||||
version: Protocol version, ``"v0.8"`` or ``"v0.9"``.
|
||||
|
||||
Returns:
|
||||
Parsed JSON schema dict.
|
||||
|
||||
Raises:
|
||||
ValueError: If the schema name or version is not recognized.
|
||||
FileNotFoundError: If the schema file is missing from the package.
|
||||
"""
|
||||
if version == "v0.8":
|
||||
valid_names = SCHEMA_NAMES
|
||||
schema_dir = _V08_DIR
|
||||
elif version == "v0.9":
|
||||
valid_names = V09_SCHEMA_NAMES
|
||||
schema_dir = _V09_DIR
|
||||
else:
|
||||
raise ValueError(f"Unknown version {version!r}. Available: v0.8, v0.9")
|
||||
|
||||
if name not in valid_names:
|
||||
raise ValueError(
|
||||
f"Unknown schema {name!r} for {version}. Available: {sorted(valid_names)}"
|
||||
)
|
||||
|
||||
cache_key = f"{version}/{name}"
|
||||
if cache_key in _SCHEMA_CACHE:
|
||||
return _SCHEMA_CACHE[cache_key]
|
||||
|
||||
path = schema_dir / f"{name}.json"
|
||||
with path.open() as f:
|
||||
schema: dict[str, Any] = json.load(f)
|
||||
|
||||
_SCHEMA_CACHE[cache_key] = schema
|
||||
return schema
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"title": "A2UI (Agent to UI) Client-to-Server Event Schema",
|
||||
"description": "Describes a JSON payload for a client-to-server event message.",
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"properties": {
|
||||
"userAction": {
|
||||
"type": "object",
|
||||
"description": "Reports a user-initiated action from a component.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the action, taken from the component's action.name property."
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The id of the surface where the event originated."
|
||||
},
|
||||
"sourceComponentId": {
|
||||
"type": "string",
|
||||
"description": "The id of the component that triggered the event."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "An ISO 8601 timestamp of when the event occurred."
|
||||
},
|
||||
"context": {
|
||||
"type": "object",
|
||||
"description": "A JSON object containing the key-value pairs from the component's action.context, after resolving all data bindings.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"surfaceId",
|
||||
"sourceComponentId",
|
||||
"timestamp",
|
||||
"context"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"description": "Reports a client-side error. The content is flexible.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{ "required": ["userAction"] },
|
||||
{ "required": ["error"] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"title": "A2UI Message Schema",
|
||||
"description": "Describes a JSON payload for an A2UI (Agent to UI) message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'deleteSurface'.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"beginRendering": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to begin rendering a surface with a root component and specific styles.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be rendered."
|
||||
},
|
||||
"catalogId": {
|
||||
"type": "string",
|
||||
"description": "The identifier of the component catalog to use for this surface. If omitted, the client MUST default to the standard catalog for this A2UI version (https://a2ui.org/specification/v0_8/standard_catalog_definition.json)."
|
||||
},
|
||||
"root": {
|
||||
"type": "string",
|
||||
"description": "The ID of the root component to render."
|
||||
},
|
||||
"styles": {
|
||||
"type": "object",
|
||||
"description": "Styling information for the UI.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": ["root", "surfaceId"]
|
||||
},
|
||||
"surfaceUpdate": {
|
||||
"type": "object",
|
||||
"description": "Updates a surface with a new set of components.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be updated. If you are adding a new surface this *must* be a new, unique identified that has never been used for any existing surfaces shown."
|
||||
},
|
||||
"components": {
|
||||
"type": "array",
|
||||
"description": "A list containing all UI components for the surface.",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "Represents a *single* component in a UI widget tree. This component could be one of many supported types.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for this component."
|
||||
},
|
||||
"weight": {
|
||||
"type": "number",
|
||||
"description": "The relative weight of this component within a Row or Column. This corresponds to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column."
|
||||
},
|
||||
"component": {
|
||||
"type": "object",
|
||||
"description": "A wrapper object that MUST contain exactly one key, which is the name of the component type. The value is an object containing the properties for that specific component.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": ["id", "component"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId", "components"]
|
||||
},
|
||||
"dataModelUpdate": {
|
||||
"type": "object",
|
||||
"description": "Updates the data model for a surface.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface this data model update applies to."
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set to '/', the entire data model will be replaced."
|
||||
},
|
||||
"contents": {
|
||||
"type": "array",
|
||||
"description": "An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value*' property.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A single data entry. Exactly one 'value*' property should be provided alongside the key.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "The key for this data entry."
|
||||
},
|
||||
"valueString": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"valueBoolean": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"valueMap": {
|
||||
"description": "Represents a map as an adjacency list.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "One entry in the map. Exactly one 'value*' property should be provided alongside the key.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueString": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"valueBoolean": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["contents", "surfaceId"]
|
||||
},
|
||||
"deleteSurface": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to delete the surface identified by 'surfaceId'.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be deleted."
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,832 @@
|
||||
{
|
||||
"title": "A2UI Message Schema",
|
||||
"description": "Describes a JSON payload for an A2UI (Agent to UI) message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'deleteSurface'.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"beginRendering": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to begin rendering a surface with a root component and specific styles.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be rendered."
|
||||
},
|
||||
"root": {
|
||||
"type": "string",
|
||||
"description": "The ID of the root component to render."
|
||||
},
|
||||
"styles": {
|
||||
"type": "object",
|
||||
"description": "Styling information for the UI.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"font": {
|
||||
"type": "string",
|
||||
"description": "The primary font for the UI."
|
||||
},
|
||||
"primaryColor": {
|
||||
"type": "string",
|
||||
"description": "The primary UI color as a hexadecimal code (e.g., '#00BFFF').",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["root", "surfaceId"]
|
||||
},
|
||||
"surfaceUpdate": {
|
||||
"type": "object",
|
||||
"description": "Updates a surface with a new set of components.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be updated. If you are adding a new surface this *must* be a new, unique identified that has never been used for any existing surfaces shown."
|
||||
},
|
||||
"components": {
|
||||
"type": "array",
|
||||
"description": "A list containing all UI components for the surface.",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "Represents a *single* component in a UI widget tree. This component could be one of many supported types.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for this component."
|
||||
},
|
||||
"weight": {
|
||||
"type": "number",
|
||||
"description": "The relative weight of this component within a Row or Column. This corresponds to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column."
|
||||
},
|
||||
"component": {
|
||||
"type": "object",
|
||||
"description": "A wrapper object that MUST contain exactly one key, which is the name of the component type (e.g., 'Heading'). The value is an object containing the properties for that specific component.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"Text": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "object",
|
||||
"description": "The text content to display. This can be a literal string or a reference to a value in the data model ('path', e.g., '/doc/title'). While simple Markdown formatting is supported (i.e. without HTML, images, or links), utilizing dedicated UI components is generally preferred for a richer and more structured presentation.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"usageHint": {
|
||||
"type": "string",
|
||||
"description": "A hint for the base text style. One of:\n- `h1`: Largest heading.\n- `h2`: Second largest heading.\n- `h3`: Third largest heading.\n- `h4`: Fourth largest heading.\n- `h5`: Fifth largest heading.\n- `caption`: Small text for captions.\n- `body`: Standard body text.",
|
||||
"enum": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"caption",
|
||||
"body"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["text"]
|
||||
},
|
||||
"Image": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the image to display. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. '/thumbnail/url').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fit": {
|
||||
"type": "string",
|
||||
"description": "Specifies how the image should be resized to fit its container. This corresponds to the CSS 'object-fit' property.",
|
||||
"enum": [
|
||||
"contain",
|
||||
"cover",
|
||||
"fill",
|
||||
"none",
|
||||
"scale-down"
|
||||
]
|
||||
},
|
||||
"usageHint": {
|
||||
"type": "string",
|
||||
"description": "A hint for the image size and style. One of:\n- `icon`: Small square icon.\n- `avatar`: Circular avatar image.\n- `smallFeature`: Small feature image.\n- `mediumFeature`: Medium feature image.\n- `largeFeature`: Large feature image.\n- `header`: Full-width, full bleed, header image.",
|
||||
"enum": [
|
||||
"icon",
|
||||
"avatar",
|
||||
"smallFeature",
|
||||
"mediumFeature",
|
||||
"largeFeature",
|
||||
"header"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"Icon": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "object",
|
||||
"description": "The name of the icon to display. This can be a literal string or a reference to a value in the data model ('path', e.g. '/form/submit').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"accountCircle",
|
||||
"add",
|
||||
"arrowBack",
|
||||
"arrowForward",
|
||||
"attachFile",
|
||||
"calendarToday",
|
||||
"call",
|
||||
"camera",
|
||||
"check",
|
||||
"close",
|
||||
"delete",
|
||||
"download",
|
||||
"edit",
|
||||
"event",
|
||||
"error",
|
||||
"favorite",
|
||||
"favoriteOff",
|
||||
"folder",
|
||||
"help",
|
||||
"home",
|
||||
"info",
|
||||
"locationOn",
|
||||
"lock",
|
||||
"lockOpen",
|
||||
"mail",
|
||||
"menu",
|
||||
"moreVert",
|
||||
"moreHoriz",
|
||||
"notificationsOff",
|
||||
"notifications",
|
||||
"payment",
|
||||
"person",
|
||||
"phone",
|
||||
"photo",
|
||||
"print",
|
||||
"refresh",
|
||||
"search",
|
||||
"send",
|
||||
"settings",
|
||||
"share",
|
||||
"shoppingCart",
|
||||
"star",
|
||||
"starHalf",
|
||||
"starOff",
|
||||
"upload",
|
||||
"visibility",
|
||||
"visibilityOff",
|
||||
"warning"
|
||||
]
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
},
|
||||
"Video": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the video to display. This can be a literal string or a reference to a value in the data model ('path', e.g. '/video/url').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"AudioPlayer": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the audio to be played. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. '/song/url').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the audio, such as a title or summary. This can be a literal string or a reference to a value in the data model ('path', e.g. '/song/title').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"Row": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"type": "object",
|
||||
"description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"dataBinding": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"type": "string",
|
||||
"description": "Defines the arrangement of children along the main axis (horizontally). This corresponds to the CSS 'justify-content' property.",
|
||||
"enum": [
|
||||
"center",
|
||||
"end",
|
||||
"spaceAround",
|
||||
"spaceBetween",
|
||||
"spaceEvenly",
|
||||
"start"
|
||||
]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"description": "Defines the alignment of children along the cross axis (vertically). This corresponds to the CSS 'align-items' property.",
|
||||
"enum": ["start", "center", "end", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"Column": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"type": "object",
|
||||
"description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"dataBinding": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"type": "string",
|
||||
"description": "Defines the arrangement of children along the main axis (vertically). This corresponds to the CSS 'justify-content' property.",
|
||||
"enum": [
|
||||
"start",
|
||||
"center",
|
||||
"end",
|
||||
"spaceBetween",
|
||||
"spaceAround",
|
||||
"spaceEvenly"
|
||||
]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"description": "Defines the alignment of children along the cross axis (horizontally). This corresponds to the CSS 'align-items' property.",
|
||||
"enum": ["center", "end", "start", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"List": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"type": "object",
|
||||
"description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"type": "string"
|
||||
},
|
||||
"dataBinding": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"direction": {
|
||||
"type": "string",
|
||||
"description": "The direction in which the list items are laid out.",
|
||||
"enum": ["vertical", "horizontal"]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"description": "Defines the alignment of children along the cross axis.",
|
||||
"enum": ["start", "center", "end", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"Card": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"child": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to be rendered inside the card."
|
||||
}
|
||||
},
|
||||
"required": ["child"]
|
||||
},
|
||||
"Tabs": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tabItems": {
|
||||
"type": "array",
|
||||
"description": "An array of objects, where each object defines a tab with a title and a child component.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "object",
|
||||
"description": "The tab title. Defines the value as either a literal value or a path to data model value (e.g. '/options/title').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"child": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "child"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["tabItems"]
|
||||
},
|
||||
"Divider": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"axis": {
|
||||
"type": "string",
|
||||
"description": "The orientation of the divider.",
|
||||
"enum": ["horizontal", "vertical"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Modal": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"entryPointChild": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component that opens the modal when interacted with (e.g., a button)."
|
||||
},
|
||||
"contentChild": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to be displayed inside the modal."
|
||||
}
|
||||
},
|
||||
"required": ["entryPointChild", "contentChild"]
|
||||
},
|
||||
"Button": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"child": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to display in the button, typically a Text component."
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates if this button should be styled as the primary action."
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"description": "The client-side action to be dispatched when the button is clicked. It includes the action's name and an optional context payload.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"context": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "Defines the value to be included in the context as either a literal value or a path to a data model value (e.g. '/user/name').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"literalNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"literalBoolean": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["key", "value"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"required": ["child", "action"]
|
||||
},
|
||||
"CheckBox": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"description": "The text to display next to the checkbox. Defines the value as either a literal value or a path to data model ('path', e.g. '/option/label').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "The current state of the checkbox (true for checked, false for unchecked). This can be a literal boolean ('literalBoolean') or a reference to a value in the data model ('path', e.g. '/filter/open').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalBoolean": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["label", "value"]
|
||||
},
|
||||
"TextField": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"description": "The text label for the input field. This can be a literal string or a reference to a value in the data model ('path, e.g. '/user/name').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "object",
|
||||
"description": "The value of the text field. This can be a literal string or a reference to a value in the data model ('path', e.g. '/user/name').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"textFieldType": {
|
||||
"type": "string",
|
||||
"description": "The type of input field to display.",
|
||||
"enum": [
|
||||
"date",
|
||||
"longText",
|
||||
"number",
|
||||
"shortText",
|
||||
"obscured"
|
||||
]
|
||||
},
|
||||
"validationRegexp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used for client-side validation of the input."
|
||||
}
|
||||
},
|
||||
"required": ["label"]
|
||||
},
|
||||
"DateTimeInput": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "The selected date and/or time value in ISO 8601 format. This can be a literal string ('literalString') or a reference to a value in the data model ('path', e.g. '/user/dob').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableDate": {
|
||||
"type": "boolean",
|
||||
"description": "If true, allows the user to select a date."
|
||||
},
|
||||
"enableTime": {
|
||||
"type": "boolean",
|
||||
"description": "If true, allows the user to select a time."
|
||||
}
|
||||
},
|
||||
"required": ["value"]
|
||||
},
|
||||
"MultipleChoice": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"selections": {
|
||||
"type": "object",
|
||||
"description": "The currently selected values for the component. This can be a literal array of strings or a path to an array in the data model('path', e.g. '/hotel/options').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalArray": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"description": "An array of available options for the user to choose from.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"description": "The text to display for this option. This can be a literal string or a reference to a value in the data model (e.g. '/option/label').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "The value to be associated with this option when selected."
|
||||
}
|
||||
},
|
||||
"required": ["label", "value"]
|
||||
}
|
||||
},
|
||||
"maxAllowedSelections": {
|
||||
"type": "integer",
|
||||
"description": "The maximum number of options that the user is allowed to select."
|
||||
},
|
||||
"variant": {
|
||||
"type": "string",
|
||||
"enum": ["checkbox", "chips"],
|
||||
"description": "The visual variant for the selection UI."
|
||||
},
|
||||
"filterable": {
|
||||
"type": "boolean",
|
||||
"description": "Whether options can be filtered by typing."
|
||||
}
|
||||
},
|
||||
"required": ["selections", "options"]
|
||||
},
|
||||
"Slider": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "The current value of the slider. This can be a literal number ('literalNumber') or a reference to a value in the data model ('path', e.g. '/restaurant/cost').",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minValue": {
|
||||
"type": "number",
|
||||
"description": "The minimum value of the slider."
|
||||
},
|
||||
"maxValue": {
|
||||
"type": "number",
|
||||
"description": "The maximum value of the slider."
|
||||
}
|
||||
},
|
||||
"required": ["value"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "component"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId", "components"]
|
||||
},
|
||||
"dataModelUpdate": {
|
||||
"type": "object",
|
||||
"description": "Updates the data model for a surface.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface this data model update applies to."
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set to '/', the entire data model will be replaced."
|
||||
},
|
||||
"contents": {
|
||||
"type": "array",
|
||||
"description": "An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value*' property.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A single data entry. Exactly one 'value*' property should be provided alongside the key.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "The key for this data entry."
|
||||
},
|
||||
"valueString": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"valueBoolean": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"valueMap": {
|
||||
"description": "Represents a map as an adjacency list.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "One entry in the map. Exactly one 'value*' property should be provided alongside the key.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueString": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"valueBoolean": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["contents", "surfaceId"]
|
||||
},
|
||||
"deleteSurface": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to delete the surface identified by 'surfaceId'.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be deleted."
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"components": {
|
||||
"Text": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "object",
|
||||
"description": "The text content to display. This can be a literal string or a reference to a value in the data model ('path', e.g., '/doc/title'). While simple Markdown formatting is supported (i.e. without HTML, images, or links), utilizing dedicated UI components is generally preferred for a richer and more structured presentation.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"usageHint": {
|
||||
"type": "string",
|
||||
"description": "A hint for the base text style.",
|
||||
"enum": ["h1", "h2", "h3", "h4", "h5", "caption", "body"]
|
||||
}
|
||||
},
|
||||
"required": ["text"]
|
||||
},
|
||||
"Image": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the image to display.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"fit": {
|
||||
"type": "string",
|
||||
"description": "Specifies how the image should be resized to fit its container.",
|
||||
"enum": ["contain", "cover", "fill", "none", "scale-down"]
|
||||
},
|
||||
"usageHint": {
|
||||
"type": "string",
|
||||
"description": "A hint for the image size and style.",
|
||||
"enum": ["icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"]
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"Icon": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "object",
|
||||
"description": "The name of the icon to display.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"accountCircle", "add", "arrowBack", "arrowForward", "attachFile",
|
||||
"calendarToday", "call", "camera", "check", "close", "delete",
|
||||
"download", "edit", "event", "error", "favorite", "favoriteOff",
|
||||
"folder", "help", "home", "info", "locationOn", "lock", "lockOpen",
|
||||
"mail", "menu", "moreVert", "moreHoriz", "notificationsOff",
|
||||
"notifications", "payment", "person", "phone", "photo", "print",
|
||||
"refresh", "search", "send", "settings", "share", "shoppingCart",
|
||||
"star", "starHalf", "starOff", "upload", "visibility",
|
||||
"visibilityOff", "warning"
|
||||
]
|
||||
},
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
},
|
||||
"Video": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the video to display.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"AudioPlayer": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "object",
|
||||
"description": "The URL of the audio to be played.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the audio, such as a title or summary.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
},
|
||||
"Row": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": { "type": "array", "items": { "type": "string" } },
|
||||
"template": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": { "type": "string" },
|
||||
"dataBinding": { "type": "string" }
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"type": "string",
|
||||
"enum": ["center", "end", "spaceAround", "spaceBetween", "spaceEvenly", "start"]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"enum": ["start", "center", "end", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"Column": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": { "type": "array", "items": { "type": "string" } },
|
||||
"template": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": { "type": "string" },
|
||||
"dataBinding": { "type": "string" }
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"type": "string",
|
||||
"enum": ["start", "center", "end", "spaceBetween", "spaceAround", "spaceEvenly"]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"enum": ["center", "end", "start", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"List": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "object",
|
||||
"description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"explicitList": { "type": "array", "items": { "type": "string" } },
|
||||
"template": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"componentId": { "type": "string" },
|
||||
"dataBinding": { "type": "string" }
|
||||
},
|
||||
"required": ["componentId", "dataBinding"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"direction": {
|
||||
"type": "string",
|
||||
"enum": ["vertical", "horizontal"]
|
||||
},
|
||||
"alignment": {
|
||||
"type": "string",
|
||||
"enum": ["start", "center", "end", "stretch"]
|
||||
}
|
||||
},
|
||||
"required": ["children"]
|
||||
},
|
||||
"Card": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"child": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to be rendered inside the card."
|
||||
}
|
||||
},
|
||||
"required": ["child"]
|
||||
},
|
||||
"Tabs": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tabItems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"child": { "type": "string" }
|
||||
},
|
||||
"required": ["title", "child"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["tabItems"]
|
||||
},
|
||||
"Divider": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"axis": {
|
||||
"type": "string",
|
||||
"enum": ["horizontal", "vertical"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Modal": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"entryPointChild": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component that opens the modal when interacted with."
|
||||
},
|
||||
"contentChild": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to be displayed inside the modal."
|
||||
}
|
||||
},
|
||||
"required": ["entryPointChild", "contentChild"]
|
||||
},
|
||||
"Button": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"child": {
|
||||
"type": "string",
|
||||
"description": "The ID of the component to display in the button."
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates if this button should be styled as the primary action."
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"context": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"key": { "type": "string" },
|
||||
"value": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"path": { "type": "string" },
|
||||
"literalString": { "type": "string" },
|
||||
"literalNumber": { "type": "number" },
|
||||
"literalBoolean": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["key", "value"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"required": ["child", "action"]
|
||||
},
|
||||
"CheckBox": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalBoolean": { "type": "boolean" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["label", "value"]
|
||||
},
|
||||
"TextField": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"textFieldType": {
|
||||
"type": "string",
|
||||
"enum": ["date", "longText", "number", "shortText", "obscured"]
|
||||
},
|
||||
"validationRegexp": { "type": "string" }
|
||||
},
|
||||
"required": ["label"]
|
||||
},
|
||||
"DateTimeInput": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"enableDate": { "type": "boolean" },
|
||||
"enableTime": { "type": "boolean" }
|
||||
},
|
||||
"required": ["value"]
|
||||
},
|
||||
"MultipleChoice": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"selections": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalArray": { "type": "array", "items": { "type": "string" } },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalString": { "type": "string" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"value": { "type": "string" }
|
||||
},
|
||||
"required": ["label", "value"]
|
||||
}
|
||||
},
|
||||
"maxAllowedSelections": { "type": "integer" },
|
||||
"variant": {
|
||||
"type": "string",
|
||||
"enum": ["checkbox", "chips"]
|
||||
},
|
||||
"filterable": { "type": "boolean" }
|
||||
},
|
||||
"required": ["selections", "options"]
|
||||
},
|
||||
"Slider": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"literalNumber": { "type": "number" },
|
||||
"path": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"minValue": { "type": "number" },
|
||||
"maxValue": { "type": "number" }
|
||||
},
|
||||
"required": ["value"]
|
||||
}
|
||||
},
|
||||
"styles": {
|
||||
"font": {
|
||||
"type": "string",
|
||||
"description": "The primary font for the UI."
|
||||
},
|
||||
"primaryColor": {
|
||||
"type": "string",
|
||||
"description": "The primary UI color as a hexadecimal code (e.g., '#00BFFF').",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://a2ui.org/specification/v0_9/client_capabilities.json",
|
||||
"title": "A2UI Client Capabilities Schema",
|
||||
"description": "A schema for the a2uiClientCapabilities object, which is sent from the client to the server as part of the A2A metadata to describe the client's UI rendering capabilities.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v0.9": {
|
||||
"type": "object",
|
||||
"description": "The capabilities structure for version 0.9 of the A2UI protocol.",
|
||||
"properties": {
|
||||
"supportedCatalogIds": {
|
||||
"type": "array",
|
||||
"description": "The URI of each of the component and function catalogs that is supported by the client.",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"inlineCatalogs": {
|
||||
"type": "array",
|
||||
"description": "An array of inline catalog definitions, which can contain both components and functions. This should only be provided if the agent declares 'acceptsInlineCatalogs: true' in its capabilities.",
|
||||
"items": { "$ref": "#/$defs/Catalog" }
|
||||
}
|
||||
},
|
||||
"required": ["supportedCatalogIds"]
|
||||
}
|
||||
},
|
||||
"required": ["v0.9"],
|
||||
"$defs": {
|
||||
"FunctionDefinition": {
|
||||
"type": "object",
|
||||
"description": "Describes a function's interface.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The unique name of the function."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A human-readable description of what the function does and how to use it."
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"description": "A JSON Schema describing the expected arguments (args) for this function.",
|
||||
"$ref": "https://json-schema.org/draft/2020-12/schema"
|
||||
},
|
||||
"returnType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"array",
|
||||
"object",
|
||||
"any",
|
||||
"void"
|
||||
],
|
||||
"description": "The type of value this function returns."
|
||||
}
|
||||
},
|
||||
"required": ["name", "parameters", "returnType"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Catalog": {
|
||||
"type": "object",
|
||||
"description": "A collection of component and function definitions.",
|
||||
"properties": {
|
||||
"catalogId": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for this catalog."
|
||||
},
|
||||
"components": {
|
||||
"type": "object",
|
||||
"description": "Definitions for UI components supported by this catalog.",
|
||||
"additionalProperties": {
|
||||
"$ref": "https://json-schema.org/draft/2020-12/schema"
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
"type": "array",
|
||||
"description": "Definitions for functions supported by this catalog.",
|
||||
"items": {
|
||||
"$ref": "#/$defs/FunctionDefinition"
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"title": "A2UI Theme",
|
||||
"description": "A schema that defines a catalog of A2UI theme properties. Each key is a theme property name (e.g. 'primaryColor'), and each value is the JSON schema for that property.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "https://json-schema.org/draft/2020-12/schema"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["catalogId"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://a2ui.org/specification/v0_9/client_data_model.json",
|
||||
"title": "A2UI Client Data Model Schema",
|
||||
"description": "Schema for attaching the client data model to A2A message metadata. This object should be placed in the `a2uiClientDataModel` field of the metadata.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"surfaces": {
|
||||
"type": "object",
|
||||
"description": "A map of surface IDs to their current data models.",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"description": "The current data model for the surface, as a standard JSON object."
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["version", "surfaces"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"title": "A2UI (Agent to UI) Client-to-Server Event Schema",
|
||||
"description": "Describes a JSON payload for a client-to-server event message.",
|
||||
"type": "object",
|
||||
"minProperties": 2,
|
||||
"maxProperties": 2,
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"description": "Reports a user-initiated action from a component.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the action, taken from the component's action.event.name property."
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The id of the surface where the event originated."
|
||||
},
|
||||
"sourceComponentId": {
|
||||
"type": "string",
|
||||
"description": "The id of the component that triggered the event."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "An ISO 8601 timestamp of when the event occurred."
|
||||
},
|
||||
"context": {
|
||||
"type": "object",
|
||||
"description": "A JSON object containing the key-value pairs from the component's action.event.context, after resolving all data bindings.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"surfaceId",
|
||||
"sourceComponentId",
|
||||
"timestamp",
|
||||
"context"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"description": "Reports a client-side error.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Validation Failed Error",
|
||||
"properties": {
|
||||
"code": {
|
||||
"const": "VALIDATION_FAILED"
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The id of the surface where the error occurred."
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "The JSON pointer to the field that failed validation (e.g. '/components/0/text')."
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "A short one or two sentence description of why validation failed."
|
||||
}
|
||||
},
|
||||
"required": ["code", "path", "message", "surfaceId"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Generic Error",
|
||||
"properties": {
|
||||
"code": {
|
||||
"not": {
|
||||
"const": "VALIDATION_FAILED"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "A short one or two sentence description of why the error occurred."
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The id of the surface where the error occurred."
|
||||
}
|
||||
},
|
||||
"required": ["code", "surfaceId", "message"],
|
||||
"additionalProperties": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"required": ["action", "version"]
|
||||
},
|
||||
{
|
||||
"required": ["error", "version"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://a2ui.org/specification/v0_9/common_types.json",
|
||||
"title": "A2UI Common Types",
|
||||
"description": "Common type definitions used across A2UI schemas.",
|
||||
"$defs": {
|
||||
"ComponentId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for a component, used for both definitions and references within the same surface."
|
||||
},
|
||||
"AccessibilityAttributes": {
|
||||
"type": "object",
|
||||
"description": "Attributes to enhance accessibility when using assistive technologies like screen readers.",
|
||||
"properties": {
|
||||
"label": {
|
||||
"$ref": "#/$defs/DynamicString",
|
||||
"description": "A short string, typically 1 to 3 words, used by assistive technologies to convey the purpose or intent of an element. For example, an input field might have an accessible label of 'User ID' or a button might be labeled 'Submit'."
|
||||
},
|
||||
"description": {
|
||||
"$ref": "#/$defs/DynamicString",
|
||||
"description": "Additional information provided by assistive technologies about an element such as instructions, format requirements, or result of an action. For example, a mute button might have a label of 'Mute' and a description of 'Silences notifications about this conversation'."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ComponentCommon": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/$defs/ComponentId"
|
||||
},
|
||||
"accessibility": {
|
||||
"$ref": "#/$defs/AccessibilityAttributes"
|
||||
}
|
||||
},
|
||||
"required": ["id"]
|
||||
},
|
||||
"ChildList": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/ComponentId"
|
||||
},
|
||||
"description": "A static list of child component IDs."
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "A template for generating a dynamic list of children from a data model list. The `componentId` is the component to use as a template.",
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"$ref": "#/$defs/ComponentId"
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "The path to the list of component property objects in the data model."
|
||||
}
|
||||
},
|
||||
"required": ["componentId", "path"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"DataBinding": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "A JSON Pointer path to a value in the data model."
|
||||
}
|
||||
},
|
||||
"required": ["path"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DynamicValue": {
|
||||
"description": "A value that can be a literal, a path, or a function call returning any type.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DataBinding"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicString": {
|
||||
"description": "Represents a string",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DataBinding"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"returnType": {
|
||||
"const": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicNumber": {
|
||||
"description": "Represents a value that can be either a literal number, a path to a number in the data model, or a function call returning a number.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DataBinding"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"returnType": {
|
||||
"const": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicBoolean": {
|
||||
"description": "A boolean value that can be a literal, a path, or a function call returning a boolean.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DataBinding"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"returnType": {
|
||||
"const": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicStringList": {
|
||||
"description": "Represents a value that can be either a literal array of strings, a path to a string array in the data model, or a function call returning a string array.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DataBinding"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"returnType": {
|
||||
"const": "array"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"FunctionCall": {
|
||||
"type": "object",
|
||||
"description": "Invokes a named function on the client.",
|
||||
"properties": {
|
||||
"call": {
|
||||
"type": "string",
|
||||
"description": "The name of the function to call."
|
||||
},
|
||||
"args": {
|
||||
"type": "object",
|
||||
"description": "Arguments passed to the function.",
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/DynamicValue"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "A literal object argument (e.g. configuration)."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"returnType": {
|
||||
"type": "string",
|
||||
"description": "The expected return type of the function call.",
|
||||
"enum": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"array",
|
||||
"object",
|
||||
"any",
|
||||
"void"
|
||||
],
|
||||
"default": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["call"],
|
||||
"oneOf": [
|
||||
{ "$ref": "basic_catalog.json#/$defs/anyFunction" }
|
||||
]
|
||||
},
|
||||
"CheckRule": {
|
||||
"type": "object",
|
||||
"description": "A single validation rule applied to an input component.",
|
||||
"properties": {
|
||||
"condition": {
|
||||
"$ref": "#/$defs/DynamicBoolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "The error message to display if the check fails."
|
||||
}
|
||||
},
|
||||
"required": ["condition", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Checkable": {
|
||||
"description": "Properties for components that support client-side checks.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "array",
|
||||
"description": "A list of checks to perform. These are function calls that must return a boolean indicating validity.",
|
||||
"items": {
|
||||
"$ref": "#/$defs/CheckRule"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Action": {
|
||||
"description": "Defines an interaction handler that can either trigger a server-side event or execute a local client-side function.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Triggers a server-side event.",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "object",
|
||||
"description": "The event to dispatch to the server.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the action to be dispatched to the server."
|
||||
},
|
||||
"context": {
|
||||
"type": "object",
|
||||
"description": "A JSON object containing the key-value pairs for the action context. Values can be literals or paths. Use literal values unless the value must be dynamically bound to the data model. Do NOT use paths for static IDs.",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/DynamicValue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["event"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Executes a local client-side function.",
|
||||
"properties": {
|
||||
"functionCall": {
|
||||
"$ref": "#/$defs/FunctionCall"
|
||||
}
|
||||
},
|
||||
"required": ["functionCall"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://a2ui.org/specification/v0_9/server_capabilities.json",
|
||||
"title": "A2UI Server Capabilities Schema",
|
||||
"description": "A schema for the server capabilities object, which is used by an A2UI server (or Agent) to advertise its supported UI features to clients. This can be embedded in an Agent Card for A2A or used in other transport protocols like MCP.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v0.9": {
|
||||
"type": "object",
|
||||
"description": "The server capabilities structure for version 0.9 of the A2UI protocol.",
|
||||
"properties": {
|
||||
"supportedCatalogIds": {
|
||||
"type": "array",
|
||||
"description": "An array of strings, where each string is an ID identifying a Catalog Definition Schema that the server can generate. This is not necessarily a resolvable URI.",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"acceptsInlineCatalogs": {
|
||||
"type": "boolean",
|
||||
"description": "A boolean indicating if the server can accept an 'inlineCatalogs' array in the client's a2uiClientCapabilities. If omitted, this defaults to false.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["v0.9"]
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://a2ui.org/specification/v0_9/server_to_client.json",
|
||||
"title": "A2UI Message Schema",
|
||||
"description": "Describes a JSON payload for an A2UI (Agent to UI) message, which is used to dynamically construct and update user interfaces.",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/$defs/CreateSurfaceMessage" },
|
||||
{ "$ref": "#/$defs/UpdateComponentsMessage" },
|
||||
{ "$ref": "#/$defs/UpdateDataModelMessage" },
|
||||
{ "$ref": "#/$defs/DeleteSurfaceMessage" }
|
||||
],
|
||||
"$defs": {
|
||||
"CreateSurfaceMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"createSurface": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to create a new surface and begin rendering it. When this message is sent, the client will expect 'updateComponents' and/or 'updateDataModel' messages for the same surfaceId that define the component tree.",
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be rendered."
|
||||
},
|
||||
"catalogId": {
|
||||
"description": "A string that uniquely identifies this catalog. It is recommended to prefix this with an internet domain that you own, to avoid conflicts e.g. mycompany.com:somecatalog'.",
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"$ref": "basic_catalog.json#/$defs/theme",
|
||||
"description": "Theme parameters for the surface (e.g., {'primaryColor': '#FF0000'}). These must validate against the 'theme' schema defined in the catalog."
|
||||
},
|
||||
"sendDataModel": {
|
||||
"type": "boolean",
|
||||
"description": "If true, the client will send the full data model of this surface in the metadata of every A2A message sent to the server that created the surface. Defaults to false."
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId", "catalogId"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["createSurface", "version"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"UpdateComponentsMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"updateComponents": {
|
||||
"type": "object",
|
||||
"description": "Updates a surface with a new set of components. This message can be sent multiple times to update the component tree of an existing surface. One of the components in one of the components lists MUST have an 'id' of 'root' to serve as the root of the component tree. The createSurface message MUST have been previously sent with the 'catalogId' that is in this message.",
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be updated."
|
||||
},
|
||||
|
||||
"components": {
|
||||
"type": "array",
|
||||
"description": "A list containing all UI components for the surface.",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "basic_catalog.json#/$defs/anyComponent"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId", "components"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["updateComponents", "version"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"UpdateDataModelMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"updateDataModel": {
|
||||
"type": "object",
|
||||
"description": "Updates the data model for an existing surface. This message can be sent multiple times to update the data model. The createSurface message MUST have been previously sent with the 'catalogId' that is in this message.",
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface this data model update applies to."
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set to '/', refers to the entire data model."
|
||||
},
|
||||
"value": {
|
||||
"description": "The data to be updated in the data model. If present, the value at 'path' is replaced (or created). If omitted, the key at 'path' is removed.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["updateDataModel", "version"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DeleteSurfaceMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"const": "v0.9"
|
||||
},
|
||||
"deleteSurface": {
|
||||
"type": "object",
|
||||
"description": "Signals the client to delete the surface identified by 'surfaceId'. The createSurface message MUST have been previously sent with the 'catalogId' that is in this message.",
|
||||
"properties": {
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the UI surface to be deleted."
|
||||
}
|
||||
},
|
||||
"required": ["surfaceId"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["deleteSurface", "version"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
160
lib/crewai/src/crewai/a2a/extensions/a2ui/server_extension.py
Normal file
160
lib/crewai/src/crewai/a2a/extensions/a2ui/server_extension.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""A2UI server extension for the A2A protocol."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from crewai.a2a.extensions.a2ui.models import A2UIResponse, extract_a2ui_json_objects
|
||||
from crewai.a2a.extensions.a2ui.v0_9 import (
|
||||
extract_a2ui_v09_json_objects,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.validator import (
|
||||
A2UIValidationError,
|
||||
validate_a2ui_message,
|
||||
validate_a2ui_message_v09,
|
||||
)
|
||||
from crewai.a2a.extensions.server import ExtensionContext, ServerExtension
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
A2UI_MIME_TYPE = "application/json+a2ui"
|
||||
A2UI_EXTENSION_URI = "https://a2ui.org/a2a-extension/a2ui/v0.8"
|
||||
A2UI_STANDARD_CATALOG_ID = (
|
||||
"https://a2ui.org/specification/v0_8/standard_catalog_definition.json"
|
||||
)
|
||||
A2UI_V09_EXTENSION_URI = "https://a2ui.org/a2a-extension/a2ui/v0.9"
|
||||
A2UI_V09_BASIC_CATALOG_ID = "https://a2ui.org/specification/v0_9/basic_catalog.json"
|
||||
|
||||
|
||||
class A2UIServerExtension(ServerExtension):
|
||||
"""A2A server extension that enables A2UI declarative UI generation.
|
||||
|
||||
Supports both v0.8 and v0.9 of the A2UI protocol via the ``version``
|
||||
parameter. When activated by a client, this extension:
|
||||
|
||||
* Negotiates catalog preferences during ``on_request``.
|
||||
* Wraps A2UI messages in the agent response as A2A DataParts with
|
||||
``application/json+a2ui`` MIME type during ``on_response``.
|
||||
|
||||
Example::
|
||||
|
||||
A2AServerConfig
|
||||
server_extensions=[A2UIServerExtension],
|
||||
default_output_modes=["text/plain", "application/json+a2ui"],
|
||||
"""
|
||||
|
||||
uri: str = A2UI_EXTENSION_URI
|
||||
required: bool = False
|
||||
description: str = "A2UI declarative UI generation"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
catalog_ids: list[str] | None = None,
|
||||
accept_inline_catalogs: bool = False,
|
||||
version: str = "v0.8",
|
||||
) -> None:
|
||||
"""Initialize the A2UI server extension.
|
||||
|
||||
Args:
|
||||
catalog_ids: Catalog identifiers this server supports.
|
||||
accept_inline_catalogs: Whether inline catalog definitions are accepted.
|
||||
version: Protocol version, ``"v0.8"`` or ``"v0.9"``.
|
||||
"""
|
||||
self._catalog_ids = catalog_ids or []
|
||||
self._accept_inline_catalogs = accept_inline_catalogs
|
||||
self._version = version
|
||||
if version == "v0.9":
|
||||
self.uri = A2UI_V09_EXTENSION_URI
|
||||
|
||||
@property
|
||||
def params(self) -> dict[str, Any]:
|
||||
"""Extension parameters advertised in the AgentCard."""
|
||||
result: dict[str, Any] = {}
|
||||
if self._catalog_ids:
|
||||
result["supportedCatalogIds"] = self._catalog_ids
|
||||
result["acceptsInlineCatalogs"] = self._accept_inline_catalogs
|
||||
return result
|
||||
|
||||
async def on_request(self, context: ExtensionContext) -> None:
|
||||
"""Extract A2UI catalog preferences from the client request.
|
||||
|
||||
Stores the negotiated catalog in ``context.state`` under
|
||||
``"a2ui_catalog_id"`` for downstream use.
|
||||
"""
|
||||
if not self.is_active(context):
|
||||
return
|
||||
|
||||
catalog_id = context.get_extension_metadata(self.uri, "catalogId")
|
||||
if isinstance(catalog_id, str):
|
||||
context.state["a2ui_catalog_id"] = catalog_id
|
||||
elif self._catalog_ids:
|
||||
context.state["a2ui_catalog_id"] = self._catalog_ids[0]
|
||||
|
||||
context.state["a2ui_active"] = True
|
||||
|
||||
async def on_response(self, context: ExtensionContext, result: Any) -> Any:
|
||||
"""Wrap A2UI messages in the result as A2A DataParts.
|
||||
|
||||
Scans the result for A2UI JSON payloads and converts them into
|
||||
DataParts with ``application/json+a2ui`` MIME type and A2UI metadata.
|
||||
Dispatches to the correct extractor and validator based on version.
|
||||
"""
|
||||
if not context.state.get("a2ui_active"):
|
||||
return result
|
||||
|
||||
if not isinstance(result, str):
|
||||
return result
|
||||
|
||||
if self._version == "v0.9":
|
||||
a2ui_messages = extract_a2ui_v09_json_objects(result)
|
||||
else:
|
||||
a2ui_messages = extract_a2ui_json_objects(result)
|
||||
|
||||
if not a2ui_messages:
|
||||
return result
|
||||
|
||||
build_fn = _build_data_part_v09 if self._version == "v0.9" else _build_data_part
|
||||
data_parts = [
|
||||
part
|
||||
for part in (build_fn(msg_data) for msg_data in a2ui_messages)
|
||||
if part is not None
|
||||
]
|
||||
|
||||
if not data_parts:
|
||||
return result
|
||||
|
||||
return A2UIResponse(text=result, a2ui_parts=data_parts)
|
||||
|
||||
|
||||
def _build_data_part(msg_data: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""Validate a v0.8 A2UI message and wrap it as a DataPart dict."""
|
||||
try:
|
||||
validated = validate_a2ui_message(msg_data)
|
||||
except A2UIValidationError:
|
||||
logger.warning("Skipping invalid A2UI message in response", exc_info=True)
|
||||
return None
|
||||
return {
|
||||
"kind": "data",
|
||||
"data": validated.model_dump(by_alias=True, exclude_none=True),
|
||||
"metadata": {
|
||||
"mimeType": A2UI_MIME_TYPE,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _build_data_part_v09(msg_data: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""Validate a v0.9 A2UI message and wrap it as a DataPart dict."""
|
||||
try:
|
||||
validated = validate_a2ui_message_v09(msg_data)
|
||||
except A2UIValidationError:
|
||||
logger.warning("Skipping invalid A2UI v0.9 message in response", exc_info=True)
|
||||
return None
|
||||
return {
|
||||
"kind": "data",
|
||||
"data": validated.model_dump(by_alias=True, exclude_none=True),
|
||||
"metadata": {
|
||||
"mimeType": A2UI_MIME_TYPE,
|
||||
},
|
||||
}
|
||||
831
lib/crewai/src/crewai/a2a/extensions/a2ui/v0_9.py
Normal file
831
lib/crewai/src/crewai/a2a/extensions/a2ui/v0_9.py
Normal file
@@ -0,0 +1,831 @@
|
||||
"""Pydantic models for A2UI v0.9 protocol messages and types.
|
||||
|
||||
This module provides v0.9 counterparts to the v0.8 models in ``models.py``.
|
||||
Key differences from v0.8:
|
||||
|
||||
* ``beginRendering`` → ``createSurface`` — adds ``theme``, ``sendDataModel``,
|
||||
requires ``catalogId``.
|
||||
* ``surfaceUpdate`` → ``updateComponents`` — component structure is flat:
|
||||
``component`` is a type-name string, properties live at the top level.
|
||||
* ``dataModelUpdate`` → ``updateDataModel`` — ``contents`` adjacency list
|
||||
replaced by a single ``value`` of any JSON type; ``path`` uses JSON Pointers.
|
||||
* All messages carry a ``version: "v0.9"`` discriminator.
|
||||
* Data binding uses plain JSON values, ``DataBinding`` objects, or
|
||||
``FunctionCall`` objects instead of ``literalString`` / ``path`` wrappers.
|
||||
* ``MultipleChoice`` is replaced by ``ChoicePicker``.
|
||||
* ``Styles`` is replaced by ``Theme`` — adds ``iconUrl``, ``agentDisplayName``.
|
||||
* Client-to-server ``userAction`` is renamed to ``action``; ``error`` gains
|
||||
structured ``code`` / ``path`` fields.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any, Literal, get_args
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
ComponentName = Literal[
|
||||
"Text",
|
||||
"Image",
|
||||
"Icon",
|
||||
"Video",
|
||||
"AudioPlayer",
|
||||
"Row",
|
||||
"Column",
|
||||
"List",
|
||||
"Card",
|
||||
"Tabs",
|
||||
"Modal",
|
||||
"Divider",
|
||||
"Button",
|
||||
"TextField",
|
||||
"CheckBox",
|
||||
"ChoicePicker",
|
||||
"Slider",
|
||||
"DateTimeInput",
|
||||
]
|
||||
|
||||
BASIC_CATALOG_COMPONENTS: frozenset[ComponentName] = frozenset(get_args(ComponentName))
|
||||
|
||||
FunctionName = Literal[
|
||||
"required",
|
||||
"regex",
|
||||
"length",
|
||||
"numeric",
|
||||
"email",
|
||||
"formatString",
|
||||
"formatNumber",
|
||||
"formatCurrency",
|
||||
"formatDate",
|
||||
"pluralize",
|
||||
"openUrl",
|
||||
"and",
|
||||
"or",
|
||||
"not",
|
||||
]
|
||||
|
||||
BASIC_CATALOG_FUNCTIONS: frozenset[FunctionName] = frozenset(get_args(FunctionName))
|
||||
|
||||
IconNameV09 = Literal[
|
||||
"accountCircle",
|
||||
"add",
|
||||
"arrowBack",
|
||||
"arrowForward",
|
||||
"attachFile",
|
||||
"calendarToday",
|
||||
"call",
|
||||
"camera",
|
||||
"check",
|
||||
"close",
|
||||
"delete",
|
||||
"download",
|
||||
"edit",
|
||||
"event",
|
||||
"error",
|
||||
"fastForward",
|
||||
"favorite",
|
||||
"favoriteOff",
|
||||
"folder",
|
||||
"help",
|
||||
"home",
|
||||
"info",
|
||||
"locationOn",
|
||||
"lock",
|
||||
"lockOpen",
|
||||
"mail",
|
||||
"menu",
|
||||
"moreVert",
|
||||
"moreHoriz",
|
||||
"notificationsOff",
|
||||
"notifications",
|
||||
"pause",
|
||||
"payment",
|
||||
"person",
|
||||
"phone",
|
||||
"photo",
|
||||
"play",
|
||||
"print",
|
||||
"refresh",
|
||||
"rewind",
|
||||
"search",
|
||||
"send",
|
||||
"settings",
|
||||
"share",
|
||||
"shoppingCart",
|
||||
"skipNext",
|
||||
"skipPrevious",
|
||||
"star",
|
||||
"starHalf",
|
||||
"starOff",
|
||||
"stop",
|
||||
"upload",
|
||||
"visibility",
|
||||
"visibilityOff",
|
||||
"volumeDown",
|
||||
"volumeMute",
|
||||
"volumeOff",
|
||||
"volumeUp",
|
||||
"warning",
|
||||
]
|
||||
|
||||
V09_ICON_NAMES: frozenset[IconNameV09] = frozenset(get_args(IconNameV09))
|
||||
|
||||
|
||||
class DataBinding(BaseModel):
|
||||
"""JSON Pointer path reference to the data model."""
|
||||
|
||||
path: str = Field(description="A JSON Pointer path to a value in the data model.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class FunctionCall(BaseModel):
|
||||
"""Client-side function invocation."""
|
||||
|
||||
call: str = Field(description="The name of the function to call.")
|
||||
args: dict[str, DynamicValue] | None = Field(
|
||||
default=None, description="Arguments passed to the function."
|
||||
)
|
||||
return_type: (
|
||||
Literal["string", "number", "boolean", "array", "object", "any", "void"] | None
|
||||
) = Field(
|
||||
default=None,
|
||||
alias="returnType",
|
||||
description="Expected return type of the function call.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
DynamicValue = str | float | int | bool | list[Any] | DataBinding | FunctionCall
|
||||
DynamicString = str | DataBinding | FunctionCall
|
||||
DynamicNumber = float | int | DataBinding | FunctionCall
|
||||
DynamicBoolean = bool | DataBinding | FunctionCall
|
||||
DynamicStringList = list[str] | DataBinding | FunctionCall
|
||||
|
||||
|
||||
class CheckRule(BaseModel):
|
||||
"""A single validation rule for an input component."""
|
||||
|
||||
condition: DynamicBoolean = Field(
|
||||
description="Condition that must evaluate to true for the check to pass."
|
||||
)
|
||||
message: str = Field(description="Error message displayed if the check fails.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class AccessibilityAttributes(BaseModel):
|
||||
"""Accessibility attributes for assistive technologies."""
|
||||
|
||||
label: DynamicString | None = Field(
|
||||
default=None, description="Short label for screen readers."
|
||||
)
|
||||
description: DynamicString | None = Field(
|
||||
default=None, description="Extended description for screen readers."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChildTemplate(BaseModel):
|
||||
"""Template for generating dynamic children from a data model list."""
|
||||
|
||||
component_id: str = Field(
|
||||
alias="componentId", description="Component to repeat per list item."
|
||||
)
|
||||
path: str = Field(description="Data model path to the list of items.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
ChildListV09 = list[str] | ChildTemplate
|
||||
|
||||
|
||||
class EventAction(BaseModel):
|
||||
"""Server-side event triggered by a component interaction."""
|
||||
|
||||
name: str = Field(description="Action name dispatched to the server.")
|
||||
context: dict[str, DynamicValue] | None = Field(
|
||||
default=None, description="Key-value pairs sent with the event."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ActionV09(BaseModel):
|
||||
"""Interaction handler: server event or local function call.
|
||||
|
||||
Exactly one of ``event`` or ``function_call`` must be set.
|
||||
"""
|
||||
|
||||
event: EventAction | None = Field(
|
||||
default=None, description="Triggers a server-side event."
|
||||
)
|
||||
function_call: FunctionCall | None = Field(
|
||||
default=None,
|
||||
alias="functionCall",
|
||||
description="Executes a local client-side function.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_exactly_one(self) -> ActionV09:
|
||||
"""Enforce exactly one of event or functionCall."""
|
||||
count = sum(f is not None for f in (self.event, self.function_call))
|
||||
if count != 1:
|
||||
raise ValueError(
|
||||
f"Exactly one of event or functionCall must be set, got {count}"
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class TextV09(BaseModel):
|
||||
"""Displays text content."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Text"] = "Text"
|
||||
text: DynamicString = Field(description="Text content to display.")
|
||||
variant: Literal["h1", "h2", "h3", "h4", "h5", "caption", "body"] | None = Field(
|
||||
default=None, description="Semantic text style hint."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ImageV09(BaseModel):
|
||||
"""Displays an image."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Image"] = "Image"
|
||||
url: DynamicString = Field(description="Image source URL.")
|
||||
description: DynamicString | None = Field(
|
||||
default=None, description="Accessibility text."
|
||||
)
|
||||
fit: Literal["contain", "cover", "fill", "none", "scaleDown"] | None = Field(
|
||||
default=None, description="Object-fit behavior."
|
||||
)
|
||||
variant: (
|
||||
Literal[
|
||||
"icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"
|
||||
]
|
||||
| None
|
||||
) = Field(default=None, description="Image size hint.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class IconV09(BaseModel):
|
||||
"""Displays a named icon."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Icon"] = "Icon"
|
||||
name: IconNameV09 | DataBinding = Field(description="Icon name or data binding.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class VideoV09(BaseModel):
|
||||
"""Displays a video player."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Video"] = "Video"
|
||||
url: DynamicString = Field(description="Video source URL.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class AudioPlayerV09(BaseModel):
|
||||
"""Displays an audio player."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["AudioPlayer"] = "AudioPlayer"
|
||||
url: DynamicString = Field(description="Audio source URL.")
|
||||
description: DynamicString | None = Field(
|
||||
default=None, description="Audio content description."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class RowV09(BaseModel):
|
||||
"""Horizontal layout container."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Row"] = "Row"
|
||||
children: ChildListV09 = Field(description="Child components.")
|
||||
justify: (
|
||||
Literal[
|
||||
"center",
|
||||
"end",
|
||||
"spaceAround",
|
||||
"spaceBetween",
|
||||
"spaceEvenly",
|
||||
"start",
|
||||
"stretch",
|
||||
]
|
||||
| None
|
||||
) = Field(default=None, description="Main-axis distribution.")
|
||||
align: Literal["start", "center", "end", "stretch"] | None = Field(
|
||||
default=None, description="Cross-axis alignment."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ColumnV09(BaseModel):
|
||||
"""Vertical layout container."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Column"] = "Column"
|
||||
children: ChildListV09 = Field(description="Child components.")
|
||||
justify: (
|
||||
Literal[
|
||||
"start",
|
||||
"center",
|
||||
"end",
|
||||
"spaceBetween",
|
||||
"spaceAround",
|
||||
"spaceEvenly",
|
||||
"stretch",
|
||||
]
|
||||
| None
|
||||
) = Field(default=None, description="Main-axis distribution.")
|
||||
align: Literal["center", "end", "start", "stretch"] | None = Field(
|
||||
default=None, description="Cross-axis alignment."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ListV09(BaseModel):
|
||||
"""Scrollable list container."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["List"] = "List"
|
||||
children: ChildListV09 = Field(description="Child components.")
|
||||
direction: Literal["vertical", "horizontal"] | None = Field(
|
||||
default=None, description="Scroll direction."
|
||||
)
|
||||
align: Literal["start", "center", "end", "stretch"] | None = Field(
|
||||
default=None, description="Cross-axis alignment."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class CardV09(BaseModel):
|
||||
"""Card container wrapping a single child."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Card"] = "Card"
|
||||
child: str = Field(description="ID of the child component.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class TabItemV09(BaseModel):
|
||||
"""A single tab definition."""
|
||||
|
||||
title: DynamicString = Field(description="Tab title.")
|
||||
child: str = Field(description="ID of the tab content component.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class TabsV09(BaseModel):
|
||||
"""Tabbed navigation container."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Tabs"] = "Tabs"
|
||||
tabs: list[TabItemV09] = Field(min_length=1, description="Tab definitions.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ModalV09(BaseModel):
|
||||
"""Modal dialog with a trigger and content."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Modal"] = "Modal"
|
||||
trigger: str = Field(description="ID of the component that opens the modal.")
|
||||
content: str = Field(description="ID of the component inside the modal.")
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class DividerV09(BaseModel):
|
||||
"""Visual divider line."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Divider"] = "Divider"
|
||||
axis: Literal["horizontal", "vertical"] | None = Field(
|
||||
default=None, description="Divider orientation."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ButtonV09(BaseModel):
|
||||
"""Interactive button."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Button"] = "Button"
|
||||
child: str = Field(description="ID of the button label component.")
|
||||
action: ActionV09 = Field(description="Action dispatched on click.")
|
||||
variant: Literal["default", "primary", "borderless"] | None = Field(
|
||||
default=None, description="Button style variant."
|
||||
)
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class TextFieldV09(BaseModel):
|
||||
"""Text input field."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["TextField"] = "TextField"
|
||||
label: DynamicString = Field(description="Input label.")
|
||||
value: DynamicString | None = Field(default=None, description="Current text value.")
|
||||
variant: Literal["longText", "number", "shortText", "obscured"] | None = Field(
|
||||
default=None, description="Input type variant."
|
||||
)
|
||||
validation_regexp: str | None = Field(
|
||||
default=None,
|
||||
alias="validationRegexp",
|
||||
description="Regex for client-side validation.",
|
||||
)
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class CheckBoxV09(BaseModel):
|
||||
"""Checkbox input."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["CheckBox"] = "CheckBox"
|
||||
label: DynamicString = Field(description="Checkbox label.")
|
||||
value: DynamicBoolean = Field(description="Checked state.")
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChoicePickerOption(BaseModel):
|
||||
"""A single option in a ChoicePicker."""
|
||||
|
||||
label: DynamicString = Field(description="Display label.")
|
||||
value: str = Field(description="Value when selected.")
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChoicePickerV09(BaseModel):
|
||||
"""Selection component replacing v0.8 MultipleChoice."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["ChoicePicker"] = "ChoicePicker"
|
||||
options: list[ChoicePickerOption] = Field(description="Available choices.")
|
||||
value: DynamicStringList = Field(description="Currently selected values.")
|
||||
label: DynamicString | None = Field(default=None, description="Group label.")
|
||||
variant: Literal["multipleSelection", "mutuallyExclusive"] | None = Field(
|
||||
default=None, description="Selection behavior."
|
||||
)
|
||||
display_style: Literal["checkbox", "chips"] | None = Field(
|
||||
default=None, alias="displayStyle", description="Visual display style."
|
||||
)
|
||||
filterable: bool | None = Field(
|
||||
default=None, description="Whether options can be filtered."
|
||||
)
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class SliderV09(BaseModel):
|
||||
"""Numeric slider input."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["Slider"] = "Slider"
|
||||
value: DynamicNumber = Field(description="Current slider value.")
|
||||
max: float = Field(description="Maximum slider value.")
|
||||
min: float | None = Field(default=None, description="Minimum slider value.")
|
||||
label: DynamicString | None = Field(default=None, description="Slider label.")
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class DateTimeInputV09(BaseModel):
|
||||
"""Date and/or time picker."""
|
||||
|
||||
id: str = Field(description="Unique component identifier.")
|
||||
component: Literal["DateTimeInput"] = "DateTimeInput"
|
||||
value: DynamicString = Field(description="ISO 8601 date/time value.")
|
||||
enable_date: bool | None = Field(
|
||||
default=None, alias="enableDate", description="Enable date selection."
|
||||
)
|
||||
enable_time: bool | None = Field(
|
||||
default=None, alias="enableTime", description="Enable time selection."
|
||||
)
|
||||
min: DynamicString | None = Field(
|
||||
default=None, description="Minimum allowed date/time."
|
||||
)
|
||||
max: DynamicString | None = Field(
|
||||
default=None, description="Maximum allowed date/time."
|
||||
)
|
||||
label: DynamicString | None = Field(default=None, description="Input label.")
|
||||
checks: list[CheckRule] | None = Field(
|
||||
default=None, description="Validation rules."
|
||||
)
|
||||
weight: float | None = Field(default=None, description="Flex weight.")
|
||||
accessibility: AccessibilityAttributes | None = None
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class Theme(BaseModel):
|
||||
"""Surface theme configuration for v0.9.
|
||||
|
||||
Replaces v0.8 ``Styles``. Adds ``iconUrl`` and ``agentDisplayName``
|
||||
for agent attribution; drops ``font``.
|
||||
"""
|
||||
|
||||
primary_color: str | None = Field(
|
||||
default=None,
|
||||
alias="primaryColor",
|
||||
pattern=r"^#[0-9a-fA-F]{6}$",
|
||||
description="Primary brand color as a hex string.",
|
||||
)
|
||||
icon_url: str | None = Field(
|
||||
default=None,
|
||||
alias="iconUrl",
|
||||
description="URL for an image identifying the agent or tool.",
|
||||
)
|
||||
agent_display_name: str | None = Field(
|
||||
default=None,
|
||||
alias="agentDisplayName",
|
||||
description="Text label identifying the agent or tool.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||||
|
||||
|
||||
class CreateSurface(BaseModel):
|
||||
"""Signals the client to create a new surface and begin rendering.
|
||||
|
||||
Replaces v0.8 ``BeginRendering``. ``catalogId`` is now required and
|
||||
``theme`` / ``sendDataModel`` are new.
|
||||
"""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Unique surface identifier.")
|
||||
catalog_id: str = Field(
|
||||
alias="catalogId", description="Catalog identifier for this surface."
|
||||
)
|
||||
theme: Theme | None = Field(default=None, description="Theme parameters.")
|
||||
send_data_model: bool | None = Field(
|
||||
default=None,
|
||||
alias="sendDataModel",
|
||||
description="If true, client sends data model in action metadata.",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class UpdateComponents(BaseModel):
|
||||
"""Updates a surface with a new set of components.
|
||||
|
||||
Replaces v0.8 ``SurfaceUpdate``. Components use a flat structure where
|
||||
``component`` is a type-name string and properties sit at the top level.
|
||||
"""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Target surface identifier.")
|
||||
components: list[dict[str, Any]] = Field(
|
||||
min_length=1, description="Components to render on the surface."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class UpdateDataModel(BaseModel):
|
||||
"""Updates the data model for a surface.
|
||||
|
||||
Replaces v0.8 ``DataModelUpdate``. The ``contents`` adjacency list is
|
||||
replaced by a single ``value`` of any JSON type. ``path`` uses JSON
|
||||
Pointer syntax — e.g. ``/user/name``.
|
||||
"""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Target surface identifier.")
|
||||
path: str | None = Field(
|
||||
default=None, description="JSON Pointer path for the update."
|
||||
)
|
||||
value: Any = Field(
|
||||
default=None, description="Value to set. Omit to delete the key."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class DeleteSurfaceV09(BaseModel):
|
||||
"""Signals the client to delete a surface."""
|
||||
|
||||
surface_id: str = Field(alias="surfaceId", description="Surface to delete.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
|
||||
class A2UIMessageV09(BaseModel):
|
||||
"""Union wrapper for v0.9 server-to-client message types.
|
||||
|
||||
Exactly one message field must be set alongside the ``version`` field.
|
||||
"""
|
||||
|
||||
version: Literal["v0.9"] = "v0.9"
|
||||
create_surface: CreateSurface | None = Field(
|
||||
default=None, alias="createSurface", description="Create a new surface."
|
||||
)
|
||||
update_components: UpdateComponents | None = Field(
|
||||
default=None,
|
||||
alias="updateComponents",
|
||||
description="Update components on a surface.",
|
||||
)
|
||||
update_data_model: UpdateDataModel | None = Field(
|
||||
default=None,
|
||||
alias="updateDataModel",
|
||||
description="Update the surface data model.",
|
||||
)
|
||||
delete_surface: DeleteSurfaceV09 | None = Field(
|
||||
default=None, alias="deleteSurface", description="Delete a surface."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_exactly_one(self) -> A2UIMessageV09:
|
||||
"""Enforce the spec's exactly-one-of constraint."""
|
||||
fields = [
|
||||
self.create_surface,
|
||||
self.update_components,
|
||||
self.update_data_model,
|
||||
self.delete_surface,
|
||||
]
|
||||
count = sum(f is not None for f in fields)
|
||||
if count != 1:
|
||||
raise ValueError(
|
||||
f"Exactly one A2UI v0.9 message type must be set, got {count}"
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class ActionEvent(BaseModel):
|
||||
"""User-initiated action from a component.
|
||||
|
||||
Replaces v0.8 ``UserAction``. The event field is renamed from
|
||||
``userAction`` to ``action``.
|
||||
"""
|
||||
|
||||
name: str = Field(description="Action name.")
|
||||
surface_id: str = Field(alias="surfaceId", description="Source surface identifier.")
|
||||
source_component_id: str = Field(
|
||||
alias="sourceComponentId",
|
||||
description="Component that triggered the action.",
|
||||
)
|
||||
timestamp: str = Field(description="ISO 8601 timestamp of the action.")
|
||||
context: dict[str, Any] = Field(description="Resolved action context payload.")
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
|
||||
class ClientErrorV09(BaseModel):
|
||||
"""Structured client-side error report.
|
||||
|
||||
Replaces v0.8's flexible ``ClientError`` with required ``code``,
|
||||
``surfaceId``, and ``message`` fields.
|
||||
"""
|
||||
|
||||
code: str = Field(description="Error code (e.g. VALIDATION_FAILED).")
|
||||
surface_id: str = Field(
|
||||
alias="surfaceId", description="Surface where the error occurred."
|
||||
)
|
||||
message: str = Field(description="Human-readable error description.")
|
||||
path: str | None = Field(
|
||||
default=None, description="JSON Pointer to the failing field."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||||
|
||||
|
||||
class A2UIEventV09(BaseModel):
|
||||
"""Union wrapper for v0.9 client-to-server events."""
|
||||
|
||||
version: Literal["v0.9"] = "v0.9"
|
||||
action: ActionEvent | None = Field(
|
||||
default=None, description="User-initiated action event."
|
||||
)
|
||||
error: ClientErrorV09 | None = Field(
|
||||
default=None, description="Client-side error report."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_exactly_one(self) -> A2UIEventV09:
|
||||
"""Enforce the spec's exactly-one-of constraint."""
|
||||
fields = [self.action, self.error]
|
||||
count = sum(f is not None for f in fields)
|
||||
if count != 1:
|
||||
raise ValueError(
|
||||
f"Exactly one A2UI v0.9 event type must be set, got {count}"
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class ClientDataModel(BaseModel):
|
||||
"""Client data model payload for A2A message metadata.
|
||||
|
||||
When ``sendDataModel`` is ``true`` on ``createSurface``, the client
|
||||
attaches this object to every outbound A2A message as
|
||||
``a2uiClientDataModel`` in the metadata.
|
||||
"""
|
||||
|
||||
version: Literal["v0.9"] = "v0.9"
|
||||
surfaces: dict[str, dict[str, Any]] = Field(
|
||||
description="Map of surface IDs to their current data models."
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
_V09_KEYS = {"createSurface", "updateComponents", "updateDataModel", "deleteSurface"}
|
||||
|
||||
|
||||
def extract_a2ui_v09_json_objects(text: str) -> list[dict[str, Any]]:
|
||||
"""Extract JSON objects containing A2UI v0.9 keys from text.
|
||||
|
||||
Uses ``json.JSONDecoder.raw_decode`` for robust parsing that correctly
|
||||
handles braces inside string literals.
|
||||
"""
|
||||
decoder = json.JSONDecoder()
|
||||
results: list[dict[str, Any]] = []
|
||||
idx = 0
|
||||
while idx < len(text):
|
||||
idx = text.find("{", idx)
|
||||
if idx == -1:
|
||||
break
|
||||
try:
|
||||
obj, end_idx = decoder.raw_decode(text, idx)
|
||||
if isinstance(obj, dict) and _V09_KEYS & obj.keys():
|
||||
results.append(obj)
|
||||
idx = end_idx
|
||||
except json.JSONDecodeError:
|
||||
idx += 1
|
||||
return results
|
||||
285
lib/crewai/src/crewai/a2a/extensions/a2ui/validator.py
Normal file
285
lib/crewai/src/crewai/a2a/extensions/a2ui/validator.py
Normal file
@@ -0,0 +1,285 @@
|
||||
"""Validate A2UI message dicts via Pydantic models."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
from crewai.a2a.extensions.a2ui.catalog import (
|
||||
AudioPlayer,
|
||||
Button,
|
||||
Card,
|
||||
CheckBox,
|
||||
Column,
|
||||
DateTimeInput,
|
||||
Divider,
|
||||
Icon,
|
||||
Image,
|
||||
List,
|
||||
Modal,
|
||||
MultipleChoice,
|
||||
Row,
|
||||
Slider,
|
||||
Tabs,
|
||||
Text,
|
||||
TextField,
|
||||
Video,
|
||||
)
|
||||
from crewai.a2a.extensions.a2ui.models import A2UIEvent, A2UIMessage
|
||||
from crewai.a2a.extensions.a2ui.v0_9 import (
|
||||
A2UIEventV09,
|
||||
A2UIMessageV09,
|
||||
AudioPlayerV09,
|
||||
ButtonV09,
|
||||
CardV09,
|
||||
CheckBoxV09,
|
||||
ChoicePickerV09,
|
||||
ColumnV09,
|
||||
DateTimeInputV09,
|
||||
DividerV09,
|
||||
IconV09,
|
||||
ImageV09,
|
||||
ListV09,
|
||||
ModalV09,
|
||||
RowV09,
|
||||
SliderV09,
|
||||
TabsV09,
|
||||
TextFieldV09,
|
||||
TextV09,
|
||||
VideoV09,
|
||||
)
|
||||
|
||||
|
||||
_STANDARD_CATALOG_MODELS: dict[str, type[BaseModel]] = {
|
||||
"AudioPlayer": AudioPlayer,
|
||||
"Button": Button,
|
||||
"Card": Card,
|
||||
"CheckBox": CheckBox,
|
||||
"Column": Column,
|
||||
"DateTimeInput": DateTimeInput,
|
||||
"Divider": Divider,
|
||||
"Icon": Icon,
|
||||
"Image": Image,
|
||||
"List": List,
|
||||
"Modal": Modal,
|
||||
"MultipleChoice": MultipleChoice,
|
||||
"Row": Row,
|
||||
"Slider": Slider,
|
||||
"Tabs": Tabs,
|
||||
"Text": Text,
|
||||
"TextField": TextField,
|
||||
"Video": Video,
|
||||
}
|
||||
|
||||
|
||||
class A2UIValidationError(Exception):
|
||||
"""Raised when an A2UI message fails validation."""
|
||||
|
||||
def __init__(self, message: str, errors: list[Any] | None = None) -> None:
|
||||
super().__init__(message)
|
||||
self.errors = errors or []
|
||||
|
||||
|
||||
def validate_a2ui_message(
|
||||
data: dict[str, Any],
|
||||
*,
|
||||
validate_catalog: bool = False,
|
||||
) -> A2UIMessage:
|
||||
"""Parse and validate an A2UI server-to-client message.
|
||||
|
||||
Args:
|
||||
data: Raw JSON-decoded message dict.
|
||||
validate_catalog: If True, also validate component properties
|
||||
against the standard catalog.
|
||||
|
||||
Returns:
|
||||
Validated ``A2UIMessage`` instance.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If the data does not conform to the A2UI schema.
|
||||
"""
|
||||
try:
|
||||
message = A2UIMessage.model_validate(data)
|
||||
except ValidationError as exc:
|
||||
raise A2UIValidationError(
|
||||
f"Invalid A2UI message: {exc.error_count()} validation error(s)",
|
||||
errors=exc.errors(),
|
||||
) from exc
|
||||
|
||||
if validate_catalog:
|
||||
validate_catalog_components(message)
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def validate_a2ui_event(data: dict[str, Any]) -> A2UIEvent:
|
||||
"""Parse and validate an A2UI client-to-server event.
|
||||
|
||||
Args:
|
||||
data: Raw JSON-decoded event dict.
|
||||
|
||||
Returns:
|
||||
Validated ``A2UIEvent`` instance.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If the data does not conform to the A2UI event schema.
|
||||
"""
|
||||
try:
|
||||
return A2UIEvent.model_validate(data)
|
||||
except ValidationError as exc:
|
||||
raise A2UIValidationError(
|
||||
f"Invalid A2UI event: {exc.error_count()} validation error(s)",
|
||||
errors=exc.errors(),
|
||||
) from exc
|
||||
|
||||
|
||||
def validate_a2ui_message_v09(data: dict[str, Any]) -> A2UIMessageV09:
|
||||
"""Parse and validate an A2UI v0.9 server-to-client message.
|
||||
|
||||
Args:
|
||||
data: Raw JSON-decoded message dict.
|
||||
|
||||
Returns:
|
||||
Validated ``A2UIMessageV09`` instance.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If the data does not conform to the v0.9 schema.
|
||||
"""
|
||||
try:
|
||||
return A2UIMessageV09.model_validate(data)
|
||||
except ValidationError as exc:
|
||||
raise A2UIValidationError(
|
||||
f"Invalid A2UI v0.9 message: {exc.error_count()} validation error(s)",
|
||||
errors=exc.errors(),
|
||||
) from exc
|
||||
|
||||
|
||||
def validate_a2ui_event_v09(data: dict[str, Any]) -> A2UIEventV09:
|
||||
"""Parse and validate an A2UI v0.9 client-to-server event.
|
||||
|
||||
Args:
|
||||
data: Raw JSON-decoded event dict.
|
||||
|
||||
Returns:
|
||||
Validated ``A2UIEventV09`` instance.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If the data does not conform to the v0.9 schema.
|
||||
"""
|
||||
try:
|
||||
return A2UIEventV09.model_validate(data)
|
||||
except ValidationError as exc:
|
||||
raise A2UIValidationError(
|
||||
f"Invalid A2UI v0.9 event: {exc.error_count()} validation error(s)",
|
||||
errors=exc.errors(),
|
||||
) from exc
|
||||
|
||||
|
||||
def validate_catalog_components(message: A2UIMessage) -> None:
|
||||
"""Validate component properties in a surfaceUpdate against the standard catalog.
|
||||
|
||||
Only applies to surfaceUpdate messages. Components whose type is not
|
||||
in the standard catalog are skipped without error.
|
||||
|
||||
Args:
|
||||
message: A validated A2UIMessage.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If any component fails catalog validation.
|
||||
"""
|
||||
if message.surface_update is None:
|
||||
return
|
||||
|
||||
errors: list[Any] = []
|
||||
for entry in message.surface_update.components:
|
||||
for type_name, props in entry.component.items():
|
||||
model = _STANDARD_CATALOG_MODELS.get(type_name)
|
||||
if model is None:
|
||||
continue
|
||||
try:
|
||||
model.model_validate(props)
|
||||
except ValidationError as exc:
|
||||
errors.extend(
|
||||
{
|
||||
"component_id": entry.id,
|
||||
"component_type": type_name,
|
||||
**err,
|
||||
}
|
||||
for err in exc.errors()
|
||||
)
|
||||
|
||||
if errors:
|
||||
raise A2UIValidationError(
|
||||
f"Catalog validation failed: {len(errors)} error(s)",
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
_V09_BASIC_CATALOG_MODELS: dict[str, type[BaseModel]] = {
|
||||
"AudioPlayer": AudioPlayerV09,
|
||||
"Button": ButtonV09,
|
||||
"Card": CardV09,
|
||||
"CheckBox": CheckBoxV09,
|
||||
"ChoicePicker": ChoicePickerV09,
|
||||
"Column": ColumnV09,
|
||||
"DateTimeInput": DateTimeInputV09,
|
||||
"Divider": DividerV09,
|
||||
"Icon": IconV09,
|
||||
"Image": ImageV09,
|
||||
"List": ListV09,
|
||||
"Modal": ModalV09,
|
||||
"Row": RowV09,
|
||||
"Slider": SliderV09,
|
||||
"Tabs": TabsV09,
|
||||
"Text": TextV09,
|
||||
"TextField": TextFieldV09,
|
||||
"Video": VideoV09,
|
||||
}
|
||||
|
||||
|
||||
def validate_catalog_components_v09(message: A2UIMessageV09) -> None:
|
||||
"""Validate component properties in an updateComponents against the basic catalog.
|
||||
|
||||
v0.9 components use a flat structure where ``component`` is a type-name
|
||||
string and properties sit at the top level of the component dict.
|
||||
|
||||
Only applies to updateComponents messages. Components whose type is not
|
||||
in the basic catalog are skipped without error.
|
||||
|
||||
Args:
|
||||
message: A validated A2UIMessageV09.
|
||||
|
||||
Raises:
|
||||
A2UIValidationError: If any component fails catalog validation.
|
||||
"""
|
||||
if message.update_components is None:
|
||||
return
|
||||
|
||||
errors: list[Any] = []
|
||||
for entry in message.update_components.components:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
type_name = entry.get("component")
|
||||
if not isinstance(type_name, str):
|
||||
continue
|
||||
model = _V09_BASIC_CATALOG_MODELS.get(type_name)
|
||||
if model is None:
|
||||
continue
|
||||
try:
|
||||
model.model_validate(entry)
|
||||
except ValidationError as exc:
|
||||
errors.extend(
|
||||
{
|
||||
"component_id": entry.get("id", "<unknown>"),
|
||||
"component_type": type_name,
|
||||
**err,
|
||||
}
|
||||
for err in exc.errors()
|
||||
)
|
||||
|
||||
if errors:
|
||||
raise A2UIValidationError(
|
||||
f"v0.9 catalog validation failed: {len(errors)} error(s)",
|
||||
errors=errors,
|
||||
)
|
||||
@@ -150,6 +150,23 @@ class A2AExtension(Protocol):
|
||||
"""
|
||||
...
|
||||
|
||||
def prepare_message_metadata(
|
||||
self,
|
||||
conversation_state: ConversationState | None,
|
||||
) -> dict[str, Any]:
|
||||
"""Prepare extension-specific metadata for outbound A2A messages.
|
||||
|
||||
Called when constructing A2A messages to inject extension-specific
|
||||
metadata such as client capabilities declarations.
|
||||
|
||||
Args:
|
||||
conversation_state: Extension-specific state from extract_state_from_history.
|
||||
|
||||
Returns:
|
||||
Dict of metadata key-value pairs to merge into the message metadata.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class ExtensionRegistry:
|
||||
"""Registry for managing A2A extensions.
|
||||
@@ -236,3 +253,21 @@ class ExtensionRegistry:
|
||||
state = extension_states.get(type(extension))
|
||||
processed = extension.process_response(processed, state)
|
||||
return processed
|
||||
|
||||
def prepare_all_metadata(
|
||||
self,
|
||||
extension_states: dict[type[A2AExtension], ConversationState],
|
||||
) -> dict[str, Any]:
|
||||
"""Collect metadata from all registered extensions for outbound messages.
|
||||
|
||||
Args:
|
||||
extension_states: Mapping of extension types to conversation states.
|
||||
|
||||
Returns:
|
||||
Merged metadata dict from all extensions.
|
||||
"""
|
||||
metadata: dict[str, Any] = {}
|
||||
for extension in self._extensions:
|
||||
state = extension_states.get(type(extension))
|
||||
metadata.update(extension.prepare_message_metadata(state))
|
||||
return metadata
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import TYPE_CHECKING, Any, TypedDict
|
||||
from typing import TYPE_CHECKING, Any
|
||||
import uuid
|
||||
|
||||
from a2a.client.errors import A2AClientHTTPError
|
||||
@@ -18,7 +18,7 @@ from a2a.types import (
|
||||
TaskStatusUpdateEvent,
|
||||
TextPart,
|
||||
)
|
||||
from typing_extensions import NotRequired
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
from crewai.events.event_bus import crewai_event_bus
|
||||
from crewai.events.types.a2a_events import (
|
||||
|
||||
@@ -7,12 +7,11 @@ from typing import (
|
||||
Any,
|
||||
Literal,
|
||||
Protocol,
|
||||
TypedDict,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
from pydantic import BeforeValidator, HttpUrl, TypeAdapter
|
||||
from typing_extensions import NotRequired
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
|
||||
try:
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, NamedTuple, Protocol, TypedDict
|
||||
from typing import TYPE_CHECKING, Any, NamedTuple, Protocol
|
||||
|
||||
from pydantic import GetCoreSchemaHandler
|
||||
from pydantic_core import CoreSchema, core_schema
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
||||
class CommonParams(NamedTuple):
|
||||
|
||||
@@ -28,6 +28,7 @@ APPLICATION_PDF: Literal["application/pdf"] = "application/pdf"
|
||||
APPLICATION_OCTET_STREAM: Literal["application/octet-stream"] = (
|
||||
"application/octet-stream"
|
||||
)
|
||||
APPLICATION_A2UI_JSON: Literal["application/json+a2ui"] = "application/json+a2ui"
|
||||
|
||||
DEFAULT_CLIENT_INPUT_MODES: Final[list[Literal["text/plain", "application/json"]]] = [
|
||||
TEXT_PLAIN,
|
||||
@@ -311,6 +312,10 @@ def get_part_content_type(part: Part) -> str:
|
||||
if root.kind == "text":
|
||||
return TEXT_PLAIN
|
||||
if root.kind == "data":
|
||||
metadata = root.metadata or {}
|
||||
mime = metadata.get("mimeType", "")
|
||||
if mime == APPLICATION_A2UI_JSON:
|
||||
return APPLICATION_A2UI_JSON
|
||||
return APPLICATION_JSON
|
||||
if root.kind == "file":
|
||||
return root.file.mime_type or APPLICATION_OCTET_STREAM
|
||||
|
||||
@@ -10,7 +10,7 @@ from functools import wraps
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, TypedDict, cast
|
||||
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from a2a.server.agent_execution import RequestContext
|
||||
@@ -38,6 +38,7 @@ from a2a.utils import (
|
||||
from a2a.utils.errors import ServerError
|
||||
from aiocache import SimpleMemoryCache, caches # type: ignore[import-untyped]
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from crewai.a2a.utils.agent_card import _get_server_config
|
||||
from crewai.a2a.utils.content_type import validate_message_parts
|
||||
|
||||
@@ -1273,6 +1273,15 @@ def _delegate_to_a2a(
|
||||
for turn_num in range(ctx.max_turns):
|
||||
agent_branch, accepted_output_modes = _get_turn_context(ctx.agent_config)
|
||||
|
||||
merged_metadata = dict(ctx.metadata) if ctx.metadata else {}
|
||||
if _extension_registry and conversation_history:
|
||||
_ext_states = _extension_registry.extract_all_states(
|
||||
conversation_history
|
||||
)
|
||||
merged_metadata.update(
|
||||
_extension_registry.prepare_all_metadata(_ext_states)
|
||||
)
|
||||
|
||||
a2a_result = execute_a2a_delegation(
|
||||
endpoint=ctx.agent_config.endpoint,
|
||||
auth=ctx.agent_config.auth,
|
||||
@@ -1281,7 +1290,7 @@ def _delegate_to_a2a(
|
||||
context_id=context_id,
|
||||
task_id=task_id,
|
||||
reference_task_ids=reference_task_ids,
|
||||
metadata=ctx.metadata,
|
||||
metadata=merged_metadata or None,
|
||||
extensions=ctx.extensions,
|
||||
conversation_history=conversation_history,
|
||||
agent_id=ctx.agent_id,
|
||||
@@ -1619,6 +1628,15 @@ async def _adelegate_to_a2a(
|
||||
for turn_num in range(ctx.max_turns):
|
||||
agent_branch, accepted_output_modes = _get_turn_context(ctx.agent_config)
|
||||
|
||||
merged_metadata = dict(ctx.metadata) if ctx.metadata else {}
|
||||
if _extension_registry and conversation_history:
|
||||
_ext_states = _extension_registry.extract_all_states(
|
||||
conversation_history
|
||||
)
|
||||
merged_metadata.update(
|
||||
_extension_registry.prepare_all_metadata(_ext_states)
|
||||
)
|
||||
|
||||
a2a_result = await aexecute_a2a_delegation(
|
||||
endpoint=ctx.agent_config.endpoint,
|
||||
auth=ctx.agent_config.auth,
|
||||
@@ -1627,7 +1645,7 @@ async def _adelegate_to_a2a(
|
||||
context_id=context_id,
|
||||
task_id=task_id,
|
||||
reference_task_ids=reference_task_ids,
|
||||
metadata=ctx.metadata,
|
||||
metadata=merged_metadata or None,
|
||||
extensions=ctx.extensions,
|
||||
conversation_history=conversation_history,
|
||||
agent_id=ctx.agent_id,
|
||||
|
||||
@@ -9,11 +9,10 @@ import contextvars
|
||||
from datetime import datetime
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Annotated,
|
||||
Any,
|
||||
Literal,
|
||||
NoReturn,
|
||||
@@ -23,12 +22,13 @@ import warnings
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
BeforeValidator,
|
||||
ConfigDict,
|
||||
Field,
|
||||
InstanceOf,
|
||||
PrivateAttr,
|
||||
model_validator,
|
||||
)
|
||||
from pydantic.functional_serializers import PlainSerializer
|
||||
from typing_extensions import Self
|
||||
|
||||
from crewai.agent.planning_config import PlanningConfig
|
||||
@@ -46,7 +46,11 @@ from crewai.agent.utils import (
|
||||
save_last_messages,
|
||||
validate_max_execution_time,
|
||||
)
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.agents.agent_builder.base_agent import (
|
||||
BaseAgent,
|
||||
_serialize_llm_ref,
|
||||
_validate_llm_ref,
|
||||
)
|
||||
from crewai.agents.cache.cache_handler import CacheHandler
|
||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||
from crewai.events.event_bus import crewai_event_bus
|
||||
@@ -110,7 +114,6 @@ 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
|
||||
@@ -122,6 +125,24 @@ if TYPE_CHECKING:
|
||||
|
||||
_passthrough_exceptions: tuple[type[Exception], ...] = ()
|
||||
|
||||
_EXECUTOR_CLASS_MAP: dict[str, type] = {
|
||||
"CrewAgentExecutor": CrewAgentExecutor,
|
||||
"AgentExecutor": AgentExecutor,
|
||||
}
|
||||
|
||||
|
||||
def _validate_executor_class(value: Any) -> Any:
|
||||
if isinstance(value, str):
|
||||
cls = _EXECUTOR_CLASS_MAP.get(value)
|
||||
if cls is None:
|
||||
raise ValueError(f"Unknown executor class: {value}")
|
||||
return cls
|
||||
return value
|
||||
|
||||
|
||||
def _serialize_executor_class(value: Any) -> str:
|
||||
return value.__name__ if isinstance(value, type) else str(value)
|
||||
|
||||
|
||||
class Agent(BaseAgent):
|
||||
"""Represents an agent in a system.
|
||||
@@ -167,12 +188,16 @@ class Agent(BaseAgent):
|
||||
default=True,
|
||||
description="Use system prompt for the agent.",
|
||||
)
|
||||
llm: str | InstanceOf[BaseLLM] | None = Field(
|
||||
description="Language model that will run the agent.", default=None
|
||||
)
|
||||
function_calling_llm: str | InstanceOf[BaseLLM] | None = Field(
|
||||
description="Language model that will run the agent.", default=None
|
||||
)
|
||||
llm: Annotated[
|
||||
str | BaseLLM | None,
|
||||
BeforeValidator(_validate_llm_ref),
|
||||
PlainSerializer(_serialize_llm_ref, return_type=dict | 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"),
|
||||
] = Field(description="Language model that will run the agent.", default=None)
|
||||
system_template: str | None = Field(
|
||||
default=None, description="System format for the agent."
|
||||
)
|
||||
@@ -183,7 +208,9 @@ class Agent(BaseAgent):
|
||||
default=None, description="Response format for the agent."
|
||||
)
|
||||
allow_code_execution: bool | None = Field(
|
||||
default=False, description="Enable code execution for the agent."
|
||||
default=False,
|
||||
deprecated=True,
|
||||
description="Deprecated. CodeInterpreterTool is no longer available. Use dedicated sandbox services instead.",
|
||||
)
|
||||
respect_context_window: bool = Field(
|
||||
default=True,
|
||||
@@ -208,7 +235,8 @@ class Agent(BaseAgent):
|
||||
)
|
||||
code_execution_mode: Literal["safe", "unsafe"] = Field(
|
||||
default="safe",
|
||||
description="Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution).",
|
||||
deprecated=True,
|
||||
description="Deprecated. CodeInterpreterTool is no longer available. Use dedicated sandbox services instead.",
|
||||
)
|
||||
planning_config: PlanningConfig | None = Field(
|
||||
default=None,
|
||||
@@ -268,7 +296,14 @@ class Agent(BaseAgent):
|
||||
Can be a single A2AConfig/A2AClientConfig/A2AServerConfig, or a list of any number of A2AConfig/A2AClientConfig with a single A2AServerConfig.
|
||||
""",
|
||||
)
|
||||
executor_class: type[CrewAgentExecutor] | type[AgentExecutor] = Field(
|
||||
agent_executor: CrewAgentExecutor | AgentExecutor | None = Field(
|
||||
default=None, description="An instance of the CrewAgentExecutor class."
|
||||
)
|
||||
executor_class: Annotated[
|
||||
type[CrewAgentExecutor] | type[AgentExecutor],
|
||||
BeforeValidator(_validate_executor_class),
|
||||
PlainSerializer(_serialize_executor_class, return_type=str, when_used="json"),
|
||||
] = Field(
|
||||
default=CrewAgentExecutor,
|
||||
description="Class to use for the agent executor. Defaults to CrewAgentExecutor, can optionally use AgentExecutor.",
|
||||
)
|
||||
@@ -294,7 +329,13 @@ class Agent(BaseAgent):
|
||||
self._setup_agent_executor()
|
||||
|
||||
if self.allow_code_execution:
|
||||
self._validate_docker_installation()
|
||||
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.set_skills()
|
||||
|
||||
@@ -691,7 +732,9 @@ class Agent(BaseAgent):
|
||||
task_prompt,
|
||||
knowledge_config,
|
||||
self.knowledge.query if self.knowledge else lambda *a, **k: None,
|
||||
self.crew.query_knowledge if self.crew else lambda *a, **k: None,
|
||||
self.crew.query_knowledge
|
||||
if self.crew and not isinstance(self.crew, str)
|
||||
else lambda *a, **k: None,
|
||||
)
|
||||
|
||||
task_prompt = self._finalize_task_prompt(task_prompt, tools, task)
|
||||
@@ -778,14 +821,18 @@ class Agent(BaseAgent):
|
||||
if not self.agent_executor:
|
||||
raise RuntimeError("Agent executor is not initialized.")
|
||||
|
||||
return self.agent_executor.invoke(
|
||||
{
|
||||
"input": task_prompt,
|
||||
"tool_names": self.agent_executor.tools_names,
|
||||
"tools": self.agent_executor.tools_description,
|
||||
"ask_for_human_input": task.human_input,
|
||||
}
|
||||
)["output"]
|
||||
result = cast(
|
||||
dict[str, Any],
|
||||
self.agent_executor.invoke(
|
||||
{
|
||||
"input": task_prompt,
|
||||
"tool_names": self.agent_executor.tools_names,
|
||||
"tools": self.agent_executor.tools_description,
|
||||
"ask_for_human_input": task.human_input,
|
||||
}
|
||||
),
|
||||
)
|
||||
return result["output"]
|
||||
|
||||
async def aexecute_task(
|
||||
self,
|
||||
@@ -956,16 +1003,20 @@ class Agent(BaseAgent):
|
||||
if self.agent_executor is not None:
|
||||
self._update_executor_parameters(
|
||||
task=task,
|
||||
tools=parsed_tools, # type: ignore[arg-type]
|
||||
tools=parsed_tools,
|
||||
raw_tools=raw_tools,
|
||||
prompt=prompt,
|
||||
stop_words=stop_words,
|
||||
rpm_limit_fn=rpm_limit_fn,
|
||||
)
|
||||
else:
|
||||
if not isinstance(self.llm, BaseLLM):
|
||||
raise RuntimeError(
|
||||
"LLM must be resolved before creating agent executor."
|
||||
)
|
||||
self.agent_executor = self.executor_class(
|
||||
llm=cast(BaseLLM, self.llm),
|
||||
task=task, # type: ignore[arg-type]
|
||||
llm=self.llm,
|
||||
task=task,
|
||||
i18n=self.i18n,
|
||||
agent=self,
|
||||
crew=self.crew,
|
||||
@@ -992,7 +1043,7 @@ class Agent(BaseAgent):
|
||||
def _update_executor_parameters(
|
||||
self,
|
||||
task: Task | None,
|
||||
tools: list[BaseTool],
|
||||
tools: list[CrewStructuredTool],
|
||||
raw_tools: list[BaseTool],
|
||||
prompt: SystemPromptResult | StandardPromptResult,
|
||||
stop_words: list[str],
|
||||
@@ -1008,11 +1059,18 @@ class Agent(BaseAgent):
|
||||
stop_words: Stop words list.
|
||||
rpm_limit_fn: RPM limit callback function.
|
||||
"""
|
||||
self.agent_executor.task = task
|
||||
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.tools = tools
|
||||
self.agent_executor.original_tools = raw_tools
|
||||
self.agent_executor.prompt = prompt
|
||||
self.agent_executor.stop = stop_words
|
||||
if isinstance(self.agent_executor, AgentExecutor):
|
||||
self.agent_executor.stop_words = stop_words
|
||||
else:
|
||||
self.agent_executor.stop = stop_words
|
||||
self.agent_executor.tools_names = get_tool_names(tools)
|
||||
self.agent_executor.tools_description = render_text_description_and_args(tools)
|
||||
self.agent_executor.response_model = (
|
||||
@@ -1024,7 +1082,7 @@ class Agent(BaseAgent):
|
||||
self.agent_executor.tools_handler = self.tools_handler
|
||||
self.agent_executor.request_within_rpm_limit = rpm_limit_fn
|
||||
|
||||
if self.agent_executor.llm:
|
||||
if isinstance(self.agent_executor.llm, BaseLLM):
|
||||
existing_stop = getattr(self.agent_executor.llm, "stop", [])
|
||||
self.agent_executor.llm.stop = list(
|
||||
set(
|
||||
@@ -1034,7 +1092,7 @@ class Agent(BaseAgent):
|
||||
)
|
||||
)
|
||||
|
||||
def get_delegation_tools(self, agents: list[BaseAgent]) -> list[BaseTool]:
|
||||
def get_delegation_tools(self, agents: Sequence[BaseAgent]) -> list[BaseTool]:
|
||||
agent_tools = AgentTools(agents=agents)
|
||||
return agent_tools.tools()
|
||||
|
||||
@@ -1071,20 +1129,15 @@ class Agent(BaseAgent):
|
||||
|
||||
return [AddImageTool()]
|
||||
|
||||
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 []
|
||||
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 []
|
||||
|
||||
@staticmethod
|
||||
def get_output_converter(
|
||||
@@ -1164,28 +1217,14 @@ class Agent(BaseAgent):
|
||||
self._logger.log("warning", f"Failed to inject date: {e!s}")
|
||||
|
||||
def _validate_docker_installation(self) -> None:
|
||||
"""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
|
||||
"""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
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
|
||||
@@ -1788,21 +1827,3 @@ class Agent(BaseAgent):
|
||||
LiteAgentOutput: The result of the agent execution.
|
||||
"""
|
||||
return await self.kickoff_async(messages, response_format, input_files)
|
||||
|
||||
|
||||
try:
|
||||
from crewai.a2a.config import (
|
||||
A2AClientConfig as _A2AClientConfig,
|
||||
A2AConfig as _A2AConfig,
|
||||
A2AServerConfig as _A2AServerConfig,
|
||||
)
|
||||
|
||||
Agent.model_rebuild(
|
||||
_types_namespace={
|
||||
"A2AConfig": _A2AConfig,
|
||||
"A2AClientConfig": _A2AClientConfig,
|
||||
"A2AServerConfig": _A2AServerConfig,
|
||||
}
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
@@ -137,7 +137,8 @@ def handle_knowledge_retrieval(
|
||||
Returns:
|
||||
The task prompt potentially augmented with knowledge context.
|
||||
"""
|
||||
if not (agent.knowledge or (agent.crew and agent.crew.knowledge)):
|
||||
_crew = agent.crew if not isinstance(agent.crew, str) else None
|
||||
if not (agent.knowledge or (_crew and _crew.knowledge)):
|
||||
return task_prompt
|
||||
|
||||
crewai_event_bus.emit(
|
||||
@@ -244,7 +245,7 @@ def apply_training_data(agent: Agent, task_prompt: str) -> str:
|
||||
Returns:
|
||||
The task prompt with training data applied.
|
||||
"""
|
||||
if agent.crew and agent.crew._train:
|
||||
if agent.crew and not isinstance(agent.crew, str) and agent.crew._train:
|
||||
return agent._training_handler(task_prompt=task_prompt)
|
||||
return agent._use_trained_data(task_prompt=task_prompt)
|
||||
|
||||
@@ -355,7 +356,8 @@ async def ahandle_knowledge_retrieval(
|
||||
Returns:
|
||||
The task prompt potentially augmented with knowledge context.
|
||||
"""
|
||||
if not (agent.knowledge or (agent.crew and agent.crew.knowledge)):
|
||||
_crew = agent.crew if not isinstance(agent.crew, str) else None
|
||||
if not (agent.knowledge or (_crew and _crew.knowledge)):
|
||||
return task_prompt
|
||||
|
||||
crewai_event_bus.emit(
|
||||
@@ -381,15 +383,16 @@ async def ahandle_knowledge_retrieval(
|
||||
if agent.agent_knowledge_context:
|
||||
task_prompt += agent.agent_knowledge_context
|
||||
|
||||
knowledge_snippets = await agent.crew.aquery_knowledge(
|
||||
[agent.knowledge_search_query], **knowledge_config
|
||||
)
|
||||
if knowledge_snippets:
|
||||
agent.crew_knowledge_context = extract_knowledge_context(
|
||||
knowledge_snippets
|
||||
if _crew:
|
||||
knowledge_snippets = await _crew.aquery_knowledge(
|
||||
[agent.knowledge_search_query], **knowledge_config
|
||||
)
|
||||
if agent.crew_knowledge_context:
|
||||
task_prompt += agent.crew_knowledge_context
|
||||
if knowledge_snippets:
|
||||
agent.crew_knowledge_context = extract_knowledge_context(
|
||||
knowledge_snippets
|
||||
)
|
||||
if agent.crew_knowledge_context:
|
||||
task_prompt += agent.crew_knowledge_context
|
||||
|
||||
crewai_event_bus.emit(
|
||||
agent,
|
||||
|
||||
@@ -5,7 +5,7 @@ with CrewAI's agent system. Provides memory persistence, tool integration, and s
|
||||
output functionality.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import Any, cast
|
||||
|
||||
from pydantic import ConfigDict, Field, PrivateAttr
|
||||
@@ -30,6 +30,7 @@ from crewai.events.types.agent_events import (
|
||||
)
|
||||
from crewai.tools.agent_tools.agent_tools import AgentTools
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
from crewai.types.callback import SerializableCallable
|
||||
from crewai.utilities import Logger
|
||||
from crewai.utilities.converter import Converter
|
||||
from crewai.utilities.import_utils import require
|
||||
@@ -50,7 +51,7 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
|
||||
_memory: Any = PrivateAttr(default=None)
|
||||
_max_iterations: int = PrivateAttr(default=10)
|
||||
function_calling_llm: Any = Field(default=None)
|
||||
step_callback: Callable[..., Any] | None = Field(default=None)
|
||||
step_callback: SerializableCallable | None = Field(default=None)
|
||||
|
||||
model: str = Field(default="gpt-4o")
|
||||
verbose: bool = Field(default=False)
|
||||
@@ -272,7 +273,7 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
|
||||
available_tools: list[Any] = self._tool_adapter.tools()
|
||||
self._graph.tools = available_tools
|
||||
|
||||
def get_delegation_tools(self, agents: list[BaseAgent]) -> list[BaseTool]:
|
||||
def get_delegation_tools(self, agents: Sequence[BaseAgent]) -> list[BaseTool]:
|
||||
"""Implement delegation tools support for LangGraph.
|
||||
|
||||
Creates delegation tools that allow this agent to delegate tasks to other agents.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user