mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-11 05:39:39 +00:00
Compare commits
1 Commits
clear-cont
...
codex/llm-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5007af866e |
3
.github/CONTRIBUTING.md
vendored
3
.github/CONTRIBUTING.md
vendored
@@ -103,8 +103,7 @@ chore(deps): bump pydantic to 2.11
|
||||
- Keep PRs focused — avoid bundling unrelated changes
|
||||
- PRs over 500 lines are labeled `size/XL` automatically
|
||||
- Title must follow the same conventional commit format
|
||||
- Link related issues where applicable (`#123`, `Fixes #123`, or the issue URL)
|
||||
- First-time contributors must open or pick an existing **open** issue first, then mention it in the PR title or body (for example `#123`). PRs without a linked open issue are closed automatically.
|
||||
- Link related issues where applicable
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
6
.github/codeql/codeql-config.yml
vendored
6
.github/codeql/codeql-config.yml
vendored
@@ -2,7 +2,7 @@ name: "CodeQL Config"
|
||||
|
||||
paths-ignore:
|
||||
# Ignore template files - these are boilerplate code that shouldn't be analyzed
|
||||
- "lib/cli/src/crewai_cli/templates/**"
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
# Ignore test cassettes - these are test fixtures/recordings
|
||||
- "lib/crewai/tests/cassettes/**"
|
||||
- "lib/crewai-tools/tests/cassettes/**"
|
||||
@@ -18,16 +18,12 @@ paths:
|
||||
- ".github/workflows/**"
|
||||
- ".github/actions/**"
|
||||
# Include all Python source code from workspace packages
|
||||
- "lib/cli/src/**"
|
||||
- "lib/crewai/src/**"
|
||||
- "lib/crewai-core/src/**"
|
||||
- "lib/crewai-tools/src/**"
|
||||
- "lib/crewai-files/src/**"
|
||||
- "lib/devtools/src/**"
|
||||
# Include tests (but exclude cassettes via paths-ignore)
|
||||
- "lib/cli/tests/**"
|
||||
- "lib/crewai/tests/**"
|
||||
- "lib/crewai-core/tests/**"
|
||||
- "lib/crewai-tools/tests/**"
|
||||
- "lib/crewai-files/tests/**"
|
||||
- "lib/devtools/tests/**"
|
||||
|
||||
18
.github/dependabot.yml
vendored
18
.github/dependabot.yml
vendored
@@ -1,3 +1,6 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
@@ -5,22 +8,9 @@ updates:
|
||||
- package-ecosystem: uv
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
open-pull-requests-limit: 10
|
||||
interval: "weekly"
|
||||
groups:
|
||||
security-updates:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "*"
|
||||
patch-minor-updates:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- patch
|
||||
- minor
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
|
||||
23
.github/pull_request_template.md
vendored
23
.github/pull_request_template.md
vendored
@@ -1,23 +0,0 @@
|
||||
## Related issue
|
||||
|
||||
Fixes #
|
||||
|
||||
<!--
|
||||
First-time contributors must mention an existing open issue in this repo
|
||||
(for example #123). PRs without a linked open issue are closed automatically.
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- Explain the solution and why. -->
|
||||
|
||||
## Verification
|
||||
|
||||
<!-- List the automated and manual checks used to verify the change. -->
|
||||
|
||||
- [ ] Tests added or updated for the changed behavior
|
||||
- [ ] Relevant tests and quality checks pass locally
|
||||
|
||||
## Additional context
|
||||
|
||||
<!-- Include screenshots, compatibility notes, follow-up work, or "None". -->
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -15,11 +15,11 @@ on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- "lib/cli/src/crewai_cli/templates/**"
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- "lib/cli/src/crewai_cli/templates/**"
|
||||
- "lib/crewai/src/crewai/cli/templates/**"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
||||
121
.github/workflows/ftc-require-issue.yml
vendored
121
.github/workflows/ftc-require-issue.yml
vendored
@@ -1,121 +0,0 @@
|
||||
name: First-time contributor issue required
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: read
|
||||
|
||||
concurrency:
|
||||
group: ftc-require-issue-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
require-issue:
|
||||
# Allow-list returning contributors. FIRST_TIMER / FIRST_TIME_CONTRIBUTOR
|
||||
# are often NONE on pull_request_target at opened time, which skipped the
|
||||
# previous deny-list and left first-timer PRs open.
|
||||
if: >
|
||||
github.event.pull_request.user.type != 'Bot' &&
|
||||
!contains(fromJSON('["MEMBER","OWNER","COLLABORATOR","CONTRIBUTOR"]'),
|
||||
github.event.pull_request.author_association)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Require an open issue
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
|
||||
run: |
|
||||
python3 << 'PY'
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
repo = os.environ["REPO"]
|
||||
pr_number = os.environ["PR_NUMBER"]
|
||||
owner, name = repo.split("/", 1)
|
||||
print(
|
||||
"author_association=",
|
||||
os.environ.get("AUTHOR_ASSOCIATION", ""),
|
||||
sep="",
|
||||
)
|
||||
|
||||
patterns = (
|
||||
re.compile(r"(?<![\w./-])#(\d+)\b"),
|
||||
re.compile(rf"{re.escape(owner)}/{re.escape(name)}#(\d+)\b"),
|
||||
re.compile(
|
||||
rf"https://github\.com/{re.escape(owner)}/{re.escape(name)}/issues/(\d+)\b"
|
||||
),
|
||||
)
|
||||
|
||||
def gh_json(*args: str) -> dict:
|
||||
return json.loads(
|
||||
subprocess.check_output(["gh", *args], text=True)
|
||||
)
|
||||
|
||||
def is_open_repo_issue(number: int) -> bool:
|
||||
result = subprocess.run(
|
||||
["gh", "api", f"repos/{repo}/issues/{number}"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
stderr = result.stderr or ""
|
||||
if "404" in stderr or "Not Found" in stderr:
|
||||
return False
|
||||
raise RuntimeError(
|
||||
f"GitHub API error looking up #{number}: {stderr}"
|
||||
)
|
||||
payload = json.loads(result.stdout)
|
||||
if "pull_request" in payload:
|
||||
return False
|
||||
return (payload.get("state") or "").lower() == "open"
|
||||
|
||||
pr = gh_json(
|
||||
"pr", "view", pr_number, "--repo", repo, "--json", "title,body,state"
|
||||
)
|
||||
text = f"{pr.get('title') or ''}\n{pr.get('body') or ''}"
|
||||
candidates = {
|
||||
int(match)
|
||||
for pattern in patterns
|
||||
for match in pattern.findall(text)
|
||||
}
|
||||
if any(is_open_repo_issue(number) for number in sorted(candidates)):
|
||||
sys.exit(0)
|
||||
|
||||
if (pr.get("state") or "").upper() == "CLOSED":
|
||||
sys.exit(0)
|
||||
|
||||
comment = f"""Thanks for the pull request.
|
||||
|
||||
First-time contributors need an associated open issue before we can review a PR.
|
||||
|
||||
1. Open an issue with a [template](https://github.com/{repo}/issues/new/choose), or pick an existing open one.
|
||||
2. Open a new PR (or reopen this one) whose title or body mentions that issue, for example `#123`.
|
||||
|
||||
See the [contributing guide](https://github.com/{repo}/blob/main/.github/CONTRIBUTING.md).
|
||||
"""
|
||||
subprocess.run(
|
||||
[
|
||||
"gh",
|
||||
"pr",
|
||||
"comment",
|
||||
pr_number,
|
||||
"--repo",
|
||||
repo,
|
||||
"--body",
|
||||
comment,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["gh", "pr", "close", pr_number, "--repo", repo],
|
||||
check=True,
|
||||
)
|
||||
PY
|
||||
5
.github/workflows/linter.yml
vendored
5
.github/workflows/linter.yml
vendored
@@ -18,14 +18,13 @@ jobs:
|
||||
with:
|
||||
# Exclusion-only patterns match every non-excluded file under the
|
||||
# default "some" quantifier. Require all patterns (including "**")
|
||||
# so docs/markdown/Actions-only PRs correctly set code=false.
|
||||
# so docs-only / markdown-only PRs correctly set code=false.
|
||||
predicate-quantifier: every
|
||||
filters: |
|
||||
code:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
- '!.github/**'
|
||||
|
||||
lint-run:
|
||||
needs: changes
|
||||
@@ -82,7 +81,7 @@ jobs:
|
||||
- name: Check results
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Non-code change, skipping lint"
|
||||
echo "Docs-only change, skipping lint"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.lint-run.result }}" == "success" ]; then
|
||||
|
||||
21
.github/workflows/tests.yml
vendored
21
.github/workflows/tests.yml
vendored
@@ -18,14 +18,13 @@ jobs:
|
||||
with:
|
||||
# Exclusion-only patterns match every non-excluded file under the
|
||||
# default "some" quantifier. Require all patterns (including "**")
|
||||
# so docs/markdown/Actions-only PRs correctly set code=false.
|
||||
# so docs-only / markdown-only PRs correctly set code=false.
|
||||
predicate-quantifier: every
|
||||
filters: |
|
||||
code:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
- '!.github/**'
|
||||
|
||||
tests-matrix:
|
||||
name: tests (${{ matrix.python-version }})
|
||||
@@ -122,31 +121,17 @@ jobs:
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
|
||||
# Report the required check names (tests 3.10–3.13) when the matrix is skipped.
|
||||
# Branch protection expects these names; a skipped matrix never reports them.
|
||||
tests-skip:
|
||||
name: tests (${{ matrix.python-version }})
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||
steps:
|
||||
- name: Skip non-code change
|
||||
run: echo "Non-code change, skipping tests"
|
||||
|
||||
# Summary job to provide single status for branch protection
|
||||
tests:
|
||||
name: tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, tests-matrix, tests-skip]
|
||||
needs: [changes, tests-matrix]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check results
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Non-code change, skipping tests"
|
||||
echo "Docs-only change, skipping tests"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.tests-matrix.result }}" == "success" ]; then
|
||||
|
||||
21
.github/workflows/type-checker.yml
vendored
21
.github/workflows/type-checker.yml
vendored
@@ -18,14 +18,13 @@ jobs:
|
||||
with:
|
||||
# Exclusion-only patterns match every non-excluded file under the
|
||||
# default "some" quantifier. Require all patterns (including "**")
|
||||
# so docs/markdown/Actions-only PRs correctly set code=false.
|
||||
# so docs-only / markdown-only PRs correctly set code=false.
|
||||
predicate-quantifier: every
|
||||
filters: |
|
||||
code:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
- '!.github/**'
|
||||
|
||||
type-checker-matrix:
|
||||
name: type-checker (${{ matrix.python-version }})
|
||||
@@ -76,31 +75,17 @@ jobs:
|
||||
.venv
|
||||
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
|
||||
|
||||
# Report the required check names when the matrix is skipped.
|
||||
# Branch protection expects these names; a skipped matrix never reports them.
|
||||
type-checker-skip:
|
||||
name: type-checker (${{ matrix.python-version }})
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
steps:
|
||||
- name: Skip non-code change
|
||||
run: echo "Non-code change, skipping type checks"
|
||||
|
||||
# Summary job to provide single status for branch protection
|
||||
type-checker:
|
||||
name: type-checker
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, type-checker-matrix, type-checker-skip]
|
||||
needs: [changes, type-checker-matrix]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check results
|
||||
run: |
|
||||
if [ "${{ needs.changes.outputs.code }}" != "true" ]; then
|
||||
echo "Non-code change, skipping type checks"
|
||||
echo "Docs-only change, skipping type checks"
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ needs.type-checker-matrix.result }}" == "success" ]; then
|
||||
|
||||
19
.github/workflows/vulnerability-scan.yml
vendored
19
.github/workflows/vulnerability-scan.yml
vendored
@@ -26,14 +26,13 @@ jobs:
|
||||
with:
|
||||
# Exclusion-only patterns match every non-excluded file under the
|
||||
# default "some" quantifier. Require all patterns (including "**")
|
||||
# so docs/markdown/Actions-only PRs correctly set code=false.
|
||||
# so docs-only / markdown-only PRs correctly set code=false.
|
||||
predicate-quantifier: every
|
||||
filters: |
|
||||
code:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!**/*.md'
|
||||
- '!.github/**'
|
||||
- name: Set code output
|
||||
id: set
|
||||
run: |
|
||||
@@ -86,20 +85,8 @@ jobs:
|
||||
--skip-editable
|
||||
--format json
|
||||
--output pip-audit-report.json
|
||||
# chromadb <=1.5.9: Python HTTP server issues. No PyPI release beyond
|
||||
# 1.5.9 yet. CrewAI only uses PersistentClient (embedded), not the
|
||||
# HTTP server.
|
||||
# GHSA-f4j7-r4q5-qw2c (CVE-2026-45829): pre-auth RCE. Fix merged in
|
||||
# chroma-core/chroma#7237.
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c
|
||||
# GHSA-2wm9-hf6c-p5cr (CVE-2026-45830): authenticated cross-tenant IDOR.
|
||||
--ignore-vuln GHSA-2wm9-hf6c-p5cr
|
||||
# GHSA-36p7-vc44-83pf (CVE-2026-45833): authenticated trust_remote_code
|
||||
# injection on the collection-update endpoint.
|
||||
--ignore-vuln GHSA-36p7-vc44-83pf
|
||||
# GHSA-xph7-9rjv-w5fr (CVE-2026-45831): SimpleRBACAuthorizationProvider
|
||||
# ignores tenant/database/collection scope.
|
||||
--ignore-vuln GHSA-xph7-9rjv-w5fr
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47 # torch 2.12.0 (CVE-2025-3000): local-only memory corruption in torch.jit.script; no fix available.
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c # chromadb 1.1.1 (CVE-2026-45829): pre-auth RCE in the HTTP server; no fix available.
|
||||
)
|
||||
uv run pip-audit "${pip_audit_args[@]}"
|
||||
continue-on-error: true
|
||||
|
||||
@@ -48,6 +48,7 @@ repos:
|
||||
--ignore-vuln PYSEC-2025-197
|
||||
--ignore-vuln PYSEC-2025-210
|
||||
--ignore-vuln PYSEC-2026-139
|
||||
--ignore-vuln GHSA-rrmf-rvhw-rf47
|
||||
--ignore-vuln PYSEC-2025-211
|
||||
--ignore-vuln PYSEC-2025-212
|
||||
--ignore-vuln PYSEC-2025-213
|
||||
@@ -56,10 +57,7 @@ repos:
|
||||
--ignore-vuln PYSEC-2025-216
|
||||
--ignore-vuln PYSEC-2025-217
|
||||
--ignore-vuln PYSEC-2025-218
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c
|
||||
--ignore-vuln GHSA-2wm9-hf6c-p5cr
|
||||
--ignore-vuln GHSA-36p7-vc44-83pf
|
||||
--ignore-vuln GHSA-xph7-9rjv-w5fr' --
|
||||
--ignore-vuln GHSA-f4j7-r4q5-qw2c' --
|
||||
language: system
|
||||
pass_filenames: false
|
||||
stages: [pre-push, manual]
|
||||
|
||||
19
AGENTS.md
19
AGENTS.md
@@ -14,23 +14,6 @@ Follow these guidelines when contributing:
|
||||
6. Follow software principles such as DRY and YAGNI.
|
||||
7. Keep diffs as minimal as possible.
|
||||
|
||||
## Message Content
|
||||
|
||||
`LLMMessage.content` is `str | list[dict[str, Any]] | None`; the list form is
|
||||
multimodal content parts. Never `str()` it — that puts a Python repr
|
||||
(`[{'type': 'text', 'text': 'hi'}]`) in front of the model and into memory.
|
||||
Collapse a message to text with the helper instead:
|
||||
|
||||
```python
|
||||
from crewai.utilities.agent_utils import message_content_text
|
||||
|
||||
text = message_content_text(msg) # "" for None; joined text for a parts list
|
||||
```
|
||||
|
||||
Parts arrive from a model and are typed `dict[str, Any]`, so a `text` key that
|
||||
is not a string is possible. `_content_parts_text` skips those blocks rather
|
||||
than raising, and names a list with no usable text `[multimodal content]`.
|
||||
|
||||
## Changing Docs
|
||||
|
||||
1. Edit MDX under `docs/edge/en/*` and reference it from `docs/docs.json` if
|
||||
@@ -41,5 +24,3 @@ than raising, and names a list with no usable text `[multimodal content]`.
|
||||
may reference them.
|
||||
4. If you want to preview your changes locally, use `cd docs && mintlify dev`.
|
||||
To check for broken links, run `cd docs && mintlify broken-links`.
|
||||
5. After editing English docs, sync translations to `ar`, `ko`, and `pt-BR`
|
||||
before finishing the task. Follow [DOCS_TRANSLATIONS.md](DOCS_TRANSLATIONS.md).
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
# Sync Docs Translations
|
||||
|
||||
After English documentation changes, sync the same updates to Arabic (`ar`),
|
||||
Korean (`ko`), and Brazilian Portuguese (`pt-BR`).
|
||||
|
||||
Supported locales: `ar`, `ko`, `pt-BR`.
|
||||
|
||||
## Step 1 — Find changed English files with git
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
# Uncommitted changes (staged or unstaged)
|
||||
git diff --name-only HEAD -- docs/edge/en/
|
||||
|
||||
# All changes on this branch vs main
|
||||
git diff --name-only main...HEAD -- docs/edge/en/
|
||||
|
||||
# Newly added files
|
||||
git status --porcelain docs/edge/en/
|
||||
```
|
||||
|
||||
Only process `*.mdx` under `docs/edge/en/`. Do not edit `docs/v*/` snapshots.
|
||||
|
||||
## Step 2 — Map each file to locale targets
|
||||
|
||||
For `docs/edge/en/<path>.mdx`, update or create:
|
||||
|
||||
- `docs/edge/ar/<path>.mdx`
|
||||
- `docs/edge/ko/<path>.mdx`
|
||||
- `docs/edge/pt-BR/<path>.mdx`
|
||||
|
||||
If English is a **new page**, also add matching entries in `docs/docs.json`
|
||||
navigation for each locale.
|
||||
|
||||
## Step 3 — Translate
|
||||
|
||||
Use the updated English file as source of truth. When locale files already
|
||||
exist, apply the same semantic change — do not rewrite unrelated sections.
|
||||
|
||||
Rules:
|
||||
|
||||
- Translate prose and frontmatter values (`title`, `description`, `sidebarTitle`)
|
||||
- Keep MDX/JSX tags, code blocks, URLs, and identifiers unchanged
|
||||
- Keep terms like Agent, Crew, Task, Flow, LLM, API, CLI, MCP in English where
|
||||
appropriate
|
||||
- Rewrite internal links: `/en/` → `/{lang}/` (`/ar/`, `/ko/`, `/pt-BR/`)
|
||||
- Do not add translator notes
|
||||
|
||||
## Step 4 — Verify (optional)
|
||||
|
||||
```bash
|
||||
cd docs && mintlify broken-links
|
||||
```
|
||||
|
||||
Commit English and locale files together.
|
||||
|
||||
## Checklist
|
||||
|
||||
```markdown
|
||||
- [ ] Git: listed changed docs/edge/en/*.mdx files
|
||||
- [ ] ar: updated/created matching files
|
||||
- [ ] ko: updated/created matching files
|
||||
- [ ] pt-BR: updated/created matching files
|
||||
- [ ] Links use /{lang}/ prefix; code blocks unchanged
|
||||
- [ ] docs/docs.json updated if new English page added
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
`git diff --name-only HEAD -- docs/edge/en/` returns:
|
||||
|
||||
```text
|
||||
docs/edge/en/concepts/llms.mdx
|
||||
```
|
||||
|
||||
Update:
|
||||
|
||||
- `docs/edge/ar/concepts/llms.mdx`
|
||||
- `docs/edge/ko/concepts/llms.mdx`
|
||||
- `docs/edge/pt-BR/concepts/llms.mdx`
|
||||
411
README.md
411
README.md
@@ -66,7 +66,7 @@ standard for production-ready agentic automation.
|
||||
|
||||
# CrewAI AMP Suite
|
||||
|
||||
For organizations that need a commercial control plane around CrewAI, [CrewAI AMP Suite](https://crewai.com/amp) adds managed deployment, observability, governance, security, and enterprise support.
|
||||
For organizations that need a commercial control plane around CrewAI, [CrewAI AMP Suite](https://www.crewai.com/enterprise) adds managed deployment, observability, governance, security, and enterprise support.
|
||||
|
||||
You can try one part of the suite, the [Crew Control Plane, for free](https://app.crewai.com).
|
||||
|
||||
@@ -88,12 +88,8 @@ intelligent automations.
|
||||
- [Build with AI](#build-with-ai)
|
||||
- [Why CrewAI?](#why-crewai)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Learning Resources](#learning-resources)
|
||||
- [Understanding Flows and Crews](#understanding-flows-and-crews)
|
||||
- [Installation](#1-installation)
|
||||
- [Setting Up Your Crew](#2-setting-up-your-crew)
|
||||
- [Running Your Crew](#3-running-your-crew)
|
||||
- [Key Features](#key-features)
|
||||
- [Understanding Flows and Crews](#understanding-flows-and-crews)
|
||||
- [Examples](#examples)
|
||||
- [Quick Tutorial](#quick-tutorial)
|
||||
- [Write Job Descriptions](#write-job-descriptions)
|
||||
@@ -121,7 +117,7 @@ Four skills that activate automatically when you ask relevant CrewAI questions:
|
||||
|
||||
| Skill | When it runs |
|
||||
|-------|--------------|
|
||||
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.jsonc` / `main.py` |
|
||||
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.py` / `main.py` |
|
||||
| `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails |
|
||||
| `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review |
|
||||
| `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details |
|
||||
@@ -155,7 +151,9 @@ Setup and run your first CrewAI agents by following this tutorial.
|
||||
|
||||
[](https://www.youtube.com/watch?v=-kSOTtYzgEw "CrewAI Getting Started Tutorial")
|
||||
|
||||
### Learning Resources
|
||||
###
|
||||
|
||||
Learning Resources
|
||||
|
||||
Learn CrewAI through our comprehensive courses:
|
||||
|
||||
@@ -189,76 +187,47 @@ The true power of CrewAI emerges when combining Crews and Flows. This synergy al
|
||||
|
||||
### Getting Started with Installation
|
||||
|
||||
To get started with CrewAI, follow these simple steps. The full walkthrough lives in the [installation guide](https://docs.crewai.com/en/installation).
|
||||
To get started with CrewAI, follow these simple steps:
|
||||
|
||||
### 1. Installation
|
||||
|
||||
CrewAI requires `Python >=3.10 and <3.14`. Check your version with:
|
||||
Ensure you have Python >=3.10 <3.14 installed on your system. CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
|
||||
|
||||
```bash
|
||||
python3 --version
|
||||
```
|
||||
|
||||
CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling. If you haven't installed `uv` yet, install it first.
|
||||
|
||||
**macOS/Linux:**
|
||||
First, install CrewAI:
|
||||
|
||||
```shell
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv pip install crewai
|
||||
```
|
||||
|
||||
If your system doesn't have `curl`, you can use `wget`:
|
||||
If you want to install the 'crewai' package along with its optional features that include additional tools for agents, you can do so by using the following command:
|
||||
|
||||
```shell
|
||||
wget -qO- https://astral.sh/uv/install.sh | sh
|
||||
uv pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
The command above installs the basic package and also adds extra components which require more dependencies to function.
|
||||
|
||||
```shell
|
||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
```
|
||||
### Troubleshooting Dependencies
|
||||
|
||||
If you run into any issues, refer to [UV's installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
If you encounter issues during installation or usage, here are some common solutions:
|
||||
|
||||
Then install the CrewAI CLI:
|
||||
#### Common Issues
|
||||
|
||||
```shell
|
||||
uv tool install crewai
|
||||
```
|
||||
1. **ModuleNotFoundError: No module named 'tiktoken'**
|
||||
|
||||
If you encounter a `PATH` warning, run:
|
||||
- Install tiktoken explicitly: `uv pip install 'crewai[embeddings]'`
|
||||
- If using embedchain or other tools: `uv pip install 'crewai[tools]'`
|
||||
|
||||
```shell
|
||||
uv tool update-shell
|
||||
```
|
||||
2. **Failed building wheel for tiktoken**
|
||||
|
||||
If you encounter the `chroma-hnswlib==0.7.6` build error (`fatal error C1083: Cannot open include file: 'float.h'`) on Windows, install [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/) with *Desktop development with C++*.
|
||||
- Ensure Rust compiler is installed (see installation steps above)
|
||||
- For Windows: Verify Visual C++ Build Tools are installed
|
||||
- Try upgrading pip: `uv pip install --upgrade pip`
|
||||
- If issues persist, use a pre-built wheel: `uv pip install tiktoken --prefer-binary`
|
||||
|
||||
Verify the install:
|
||||
### 2. Setting Up Your Crew with the YAML Configuration
|
||||
|
||||
```shell
|
||||
uv tool list
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```shell
|
||||
crewai v0.102.0
|
||||
- crewai
|
||||
```
|
||||
|
||||
To upgrade the global CLI later:
|
||||
|
||||
```shell
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
|
||||
This upgrades the **global `crewai` CLI tool** only. To upgrade the `crewai` version inside a project's virtual environment, see [Upgrading CrewAI in a project](https://docs.crewai.com/en/guides/migration/upgrading-crewai).
|
||||
|
||||
### 2. Setting Up Your Crew
|
||||
|
||||
`crewai create crew` creates a JSON-first crew project. Agents live in `agents/*.jsonc`, tasks and crew-level settings live in `crew.jsonc`, and `crewai run` loads that JSON definition directly.
|
||||
To create a new CrewAI project, run the following CLI (Command Line Interface) command:
|
||||
|
||||
```shell
|
||||
crewai create crew <project_name>
|
||||
@@ -269,125 +238,199 @@ This command creates a new project folder with the following structure:
|
||||
```
|
||||
my_project/
|
||||
├── .gitignore
|
||||
├── .env
|
||||
├── agents/
|
||||
│ └── researcher.jsonc
|
||||
├── crew.jsonc
|
||||
├── knowledge/
|
||||
├── pyproject.toml
|
||||
├── README.md
|
||||
├── skills/
|
||||
└── tools/
|
||||
├── .env
|
||||
└── src/
|
||||
└── my_project/
|
||||
├── __init__.py
|
||||
├── main.py
|
||||
├── crew.py
|
||||
├── tools/
|
||||
│ ├── custom_tool.py
|
||||
│ └── __init__.py
|
||||
└── config/
|
||||
├── agents.yaml
|
||||
└── tasks.yaml
|
||||
```
|
||||
|
||||
If you need the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`, run:
|
||||
|
||||
```shell
|
||||
crewai create crew <project_name> --classic
|
||||
```
|
||||
|
||||
See [Using Annotations](https://docs.crewai.com/en/learn/using-annotations) for the classic pattern.
|
||||
You can now start developing your crew by editing the files in the `src/my_project` folder. The `main.py` file is the entry point of the project, the `crew.py` file is where you define your crew, the `agents.yaml` file is where you define your agents, and the `tasks.yaml` file is where you define your tasks.
|
||||
|
||||
#### To customize your project, you can:
|
||||
|
||||
- Modify `agents/*.jsonc` to define each agent's role, goal, backstory, LLM, tools, and behavior.
|
||||
- Modify `crew.jsonc` to define tasks, process, and input defaults.
|
||||
- Add custom tools in `tools/` and reference them as `"custom:<name>"`.
|
||||
- Add optional knowledge files in `knowledge/` and skill files in `skills/`.
|
||||
- Modify `src/my_project/config/agents.yaml` to define your agents.
|
||||
- Modify `src/my_project/config/tasks.yaml` to define your tasks.
|
||||
- Modify `src/my_project/crew.py` to add your own logic, tools, and specific arguments.
|
||||
- Modify `src/my_project/main.py` to add custom inputs for your agents and tasks.
|
||||
- Add your environment variables into the `.env` file.
|
||||
|
||||
Use `{placeholder}` values in agent and task text, then set defaults in `crew.jsonc` under `inputs`. When you run `crewai run`, the CLI prompts for any missing values.
|
||||
|
||||
#### Example of a simple crew with a sequential process:
|
||||
|
||||
Instantiate your crew:
|
||||
|
||||
```shell
|
||||
crewai create crew latest-ai-development
|
||||
cd latest_ai_development
|
||||
```
|
||||
|
||||
Then edit the generated files:
|
||||
Modify the files as needed to fit your use case:
|
||||
|
||||
**agents/researcher.jsonc**
|
||||
**agents.yaml**
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"role": "{topic} Senior Data Researcher",
|
||||
"goal": "Uncover cutting-edge developments in {topic}",
|
||||
"backstory": "You're a seasoned researcher who finds relevant information and presents it clearly.",
|
||||
"llm": "openai/gpt-4o",
|
||||
"tools": ["SerperDevTool"],
|
||||
"settings": {
|
||||
"verbose": true
|
||||
}
|
||||
}
|
||||
```yaml
|
||||
# src/my_project/config/agents.yaml
|
||||
researcher:
|
||||
role: >
|
||||
{topic} Senior Data Researcher
|
||||
goal: >
|
||||
Uncover cutting-edge developments in {topic}
|
||||
backstory: >
|
||||
You're a seasoned researcher with a knack for uncovering the latest
|
||||
developments in {topic}. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.
|
||||
|
||||
reporting_analyst:
|
||||
role: >
|
||||
{topic} Reporting Analyst
|
||||
goal: >
|
||||
Create detailed reports based on {topic} data analysis and research findings
|
||||
backstory: >
|
||||
You're a meticulous analyst with a keen eye for detail. You're known for
|
||||
your ability to turn complex data into clear and concise reports, making
|
||||
it easy for others to understand and act on the information you provide.
|
||||
```
|
||||
|
||||
**agents/reporting_analyst.jsonc**
|
||||
**tasks.yaml**
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"role": "{topic} Reporting Analyst",
|
||||
"goal": "Create detailed reports based on {topic} data analysis and research findings",
|
||||
"backstory": "You're a meticulous analyst who turns complex data into clear, concise reports.",
|
||||
"llm": "openai/gpt-4o",
|
||||
"settings": {
|
||||
"verbose": true
|
||||
}
|
||||
}
|
||||
````yaml
|
||||
# src/my_project/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.
|
||||
expected_output: >
|
||||
A list with 10 bullet points of the most relevant information about {topic}
|
||||
agent: researcher
|
||||
|
||||
reporting_task:
|
||||
description: >
|
||||
Review the context you got and expand each topic into a full section for a report.
|
||||
Make sure the report is detailed and contains any and all relevant information.
|
||||
expected_output: >
|
||||
A fully fledge reports with the mains topics, each with a full section of information.
|
||||
Formatted as markdown without '```'
|
||||
agent: reporting_analyst
|
||||
output_file: report.md
|
||||
````
|
||||
|
||||
**crew.py**
|
||||
|
||||
```python
|
||||
# src/my_project/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai_tools import SerperDevTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from typing import List
|
||||
|
||||
@CrewBase
|
||||
class LatestAiDevelopmentCrew():
|
||||
"""LatestAiDevelopment crew"""
|
||||
agents: List[BaseAgent]
|
||||
tasks: List[Task]
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['researcher'],
|
||||
verbose=True,
|
||||
tools=[SerperDevTool()]
|
||||
)
|
||||
|
||||
@agent
|
||||
def reporting_analyst(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config['reporting_analyst'],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
@task
|
||||
def research_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['research_task'],
|
||||
)
|
||||
|
||||
@task
|
||||
def reporting_task(self) -> Task:
|
||||
return Task(
|
||||
config=self.tasks_config['reporting_task'],
|
||||
output_file='report.md'
|
||||
)
|
||||
|
||||
@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
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
)
|
||||
```
|
||||
|
||||
**crew.jsonc**
|
||||
**main.py**
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "Latest AI Development",
|
||||
"agents": ["researcher", "reporting_analyst"],
|
||||
"tasks": [
|
||||
{
|
||||
"name": "research_task",
|
||||
"description": "Conduct thorough research about {topic}. Find recent, relevant information.",
|
||||
"expected_output": "A list with 10 bullet points of the most relevant information about {topic}.",
|
||||
"agent": "researcher"
|
||||
},
|
||||
{
|
||||
"name": "reporting_task",
|
||||
"description": "Review the research and expand each topic into a full section for a report.",
|
||||
"expected_output": "A markdown report with the main topics, each with a full section of information. No fenced code blocks around the whole document.",
|
||||
"agent": "reporting_analyst",
|
||||
"context": ["research_task"],
|
||||
"output_file": "output/report.md",
|
||||
"markdown": true
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# src/my_project/main.py
|
||||
import sys
|
||||
from latest_ai_development.crew import LatestAiDevelopmentCrew
|
||||
|
||||
def run():
|
||||
"""
|
||||
Run the crew.
|
||||
"""
|
||||
inputs = {
|
||||
'topic': 'AI Agents'
|
||||
}
|
||||
],
|
||||
"process": "sequential",
|
||||
"verbose": true,
|
||||
"inputs": {
|
||||
"topic": "AI Agents"
|
||||
}
|
||||
}
|
||||
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
|
||||
```
|
||||
|
||||
### 3. Running Your Crew
|
||||
|
||||
Before running your crew, set the required keys in your `.env` file:
|
||||
Before running your crew, make sure you have the following keys set as environment variables in your `.env` file:
|
||||
|
||||
- Your model provider API key — see [LLM setup](https://docs.crewai.com/en/concepts/llms#setting-up-your-llm)
|
||||
- A [Serper.dev](https://serper.dev/) API key if you use web search: `SERPER_API_KEY=YOUR_KEY_HERE`
|
||||
- An [OpenAI API key](https://platform.openai.com/account/api-keys) (or other LLM API key): `OPENAI_API_KEY=sk-...`
|
||||
- A [Serper.dev](https://serper.dev/) API key: `SERPER_API_KEY=YOUR_KEY_HERE`
|
||||
|
||||
Then install dependencies and run from the project directory:
|
||||
Lock the dependencies and install them by using the CLI command but first, navigate to your project directory:
|
||||
|
||||
```shell
|
||||
crewai install
|
||||
cd my_project
|
||||
crewai install (Optional)
|
||||
```
|
||||
|
||||
To run your crew, execute the following command in the root of your project:
|
||||
|
||||
```bash
|
||||
crewai run
|
||||
```
|
||||
|
||||
If you need additional packages, use `uv add <package-name>`.
|
||||
or
|
||||
|
||||
You should see the output in the console, and `output/report.md` should be created in the project root.
|
||||
```bash
|
||||
python src/my_project/main.py
|
||||
```
|
||||
|
||||
In addition to the sequential process, you can use the hierarchical process, which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. [See more about the processes here](https://docs.crewai.com/en/concepts/processes).
|
||||
If an error happens due to the usage of poetry, please run the following command to update your crewai package:
|
||||
|
||||
For a Flow-first walkthrough, see the [Quickstart](https://docs.crewai.com/en/quickstart).
|
||||
```bash
|
||||
crewai update
|
||||
```
|
||||
|
||||
You should see the output in the console and the `report.md` file should be created in the root of your project with the full final report.
|
||||
|
||||
In addition to the sequential process, you can use the hierarchical process, which automatically assigns a manager to the defined crew to properly coordinate the planning and execution of tasks through delegation and validation of results. [See more about the processes here](https://docs.crewai.com/core-concepts/Processes/).
|
||||
|
||||
## Key Features
|
||||
|
||||
@@ -408,7 +451,7 @@ Choose CrewAI to build powerful, adaptable, and production-ready AI automations.
|
||||
You can test different real life examples of AI crews in the [CrewAI-examples repo](https://github.com/crewAIInc/crewAI-examples?tab=readme-ov-file):
|
||||
|
||||
- [Landing Page Generator](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/landing_page_generator)
|
||||
- [Having Human input on the execution](https://docs.crewai.com/en/learn/human-input-on-execution)
|
||||
- [Having Human input on the execution](https://docs.crewai.com/how-to/Human-Input-on-Execution)
|
||||
- [Trip Planner](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/trip_planner)
|
||||
- [Stock Analysis](https://github.com/crewAIInc/crewAI-examples/tree/main/crews/stock_analysis)
|
||||
|
||||
@@ -440,7 +483,7 @@ CrewAI's power truly shines when combining Crews with Flows to create sophistica
|
||||
CrewAI flows support logical operators like `or_` and `and_` to combine multiple conditions. This can be used with `@start`, `@listen`, or `@router` decorators to create complex triggering conditions.
|
||||
|
||||
- `or_`: Triggers when any of the specified conditions are met.
|
||||
- `and_`: Triggers when all of the specified conditions are met.
|
||||
- `and_`Triggers when all of the specified conditions are met.
|
||||
|
||||
Here's how you can orchestrate multiple Crews within a Flow:
|
||||
|
||||
@@ -537,7 +580,7 @@ This example demonstrates how to:
|
||||
|
||||
CrewAI supports using various LLMs through a variety of connection options. By default your agents will use the OpenAI API when querying the model. However, there are several other ways to allow your agents to connect to models. For example, you can configure your agents to use a local model via the Ollama tool.
|
||||
|
||||
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/en/learn/llm-connections) page for details on configuring your agents' connections to models.
|
||||
Please refer to the [Connect CrewAI to LLMs](https://docs.crewai.com/how-to/LLM-Connections/) page for details on configuring your agents' connections to models.
|
||||
|
||||
## When to Use CrewAI
|
||||
|
||||
@@ -553,26 +596,13 @@ CrewAI is especially useful when you want to:
|
||||
|
||||
## Contribution
|
||||
|
||||
CrewAI is open-source and we welcome contributions. See
|
||||
[`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for the full setup guide,
|
||||
branching conventions, and PR checklist.
|
||||
CrewAI is open-source and we welcome contributions. If you're looking to contribute, please:
|
||||
|
||||
Quick start:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/crewAIInc/crewAI.git
|
||||
cd crewAI
|
||||
uv sync --all-groups --all-extras
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
```bash
|
||||
# Tests
|
||||
uv run pytest lib/crewai/tests/ -x -q
|
||||
|
||||
# Type checks
|
||||
uv run mypy lib/
|
||||
```
|
||||
- Fork the repository.
|
||||
- Create a new branch for your feature.
|
||||
- Add your feature or improvement.
|
||||
- Send a pull request.
|
||||
- We appreciate your input!
|
||||
|
||||
### Contributing to the docs
|
||||
|
||||
@@ -584,8 +614,51 @@ immediately and are frozen into a new versioned snapshot under
|
||||
`docs/v<X.Y.Z>/` at the next release cut. Frozen snapshots are immutable — CI
|
||||
rejects PRs that modify them without a `[docs-freeze]` title prefix. The
|
||||
release CLI (`devtools release`) handles the freeze automatically; see
|
||||
[`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for contributor guidance and
|
||||
[`lib/devtools/README.md`](lib/devtools/README.md) for release tooling.
|
||||
[`AGENTS.md`](AGENTS.md) for the full contributor guide and
|
||||
[`RELEASING.md`](RELEASING.md) for the release-cut runbook.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
```bash
|
||||
uv lock
|
||||
uv sync
|
||||
```
|
||||
|
||||
### Virtual Env
|
||||
|
||||
```bash
|
||||
uv venv
|
||||
```
|
||||
|
||||
### Pre-commit hooks
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
uv run pytest .
|
||||
```
|
||||
|
||||
### Running static type checks
|
||||
|
||||
```bash
|
||||
uvx mypy src
|
||||
```
|
||||
|
||||
### Packaging
|
||||
|
||||
```bash
|
||||
uv build
|
||||
```
|
||||
|
||||
### Installing Locally
|
||||
|
||||
```bash
|
||||
uv pip install dist/*.tar.gz
|
||||
```
|
||||
|
||||
## Telemetry
|
||||
|
||||
@@ -656,13 +729,17 @@ A: CrewAI is a lean, fast Python framework built specifically for orchestrating
|
||||
|
||||
### Q: How do I install CrewAI?
|
||||
|
||||
A: Install the CrewAI CLI with [UV](https://docs.astral.sh/uv/):
|
||||
A: Install CrewAI using pip:
|
||||
|
||||
```shell
|
||||
uv tool install crewai
|
||||
uv pip install crewai
|
||||
```
|
||||
|
||||
Then create a project with `crewai create crew <project_name>`, run `crewai install`, and start it with `crewai run`. See the [installation guide](https://docs.crewai.com/en/installation) for details.
|
||||
For additional tools, use:
|
||||
|
||||
```shell
|
||||
uv pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
### Q: Is CrewAI a standalone framework?
|
||||
|
||||
@@ -674,7 +751,7 @@ A: Yes. CrewAI excels at both simple and highly complex real-world scenarios, of
|
||||
|
||||
### Q: Can I use CrewAI with local AI models?
|
||||
|
||||
A: Absolutely! CrewAI supports various language models, including local ones. Tools like Ollama and LM Studio allow seamless integration. Check the [LLM Connections documentation](https://docs.crewai.com/en/learn/llm-connections) for more details.
|
||||
A: Absolutely! CrewAI supports various language models, including local ones. Tools like Ollama and LM Studio allow seamless integration. Check the [LLM Connections documentation](https://docs.crewai.com/how-to/LLM-Connections/) for more details.
|
||||
|
||||
### Q: What makes Crews different from Flows?
|
||||
|
||||
@@ -694,7 +771,7 @@ A: Check out practical examples in the [CrewAI-examples repository](https://gith
|
||||
|
||||
### Q: How can I contribute to CrewAI?
|
||||
|
||||
A: Contributions are warmly welcomed! Fork the repository, create your branch, implement your changes, and submit a pull request. See [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for detailed guidelines.
|
||||
A: Contributions are warmly welcomed! Fork the repository, create your branch, implement your changes, and submit a pull request. See the Contribution section of the README for detailed guidelines.
|
||||
|
||||
### Q: What additional features does CrewAI AMP offer?
|
||||
|
||||
|
||||
12033
docs/docs.json
12033
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -4,232 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="27 أغسطس 2026">
|
||||
## v1.15.18
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.18)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- ترقية تدفقات المحادثة إلى حالة مستقرة
|
||||
- تسجيل نشر تم إنشاؤه مع UUID المعطى
|
||||
- تحسين وثائق تدفقات المحادثة وواجهات برمجة التطبيقات
|
||||
- السماح لإعلان بتسمية تنسيق استجابة الموجه
|
||||
- السماح لتدفق الدردشة بإعلان شكل حالته الخاصة
|
||||
- قبول إعدادات LLM على نمط الطاقم في إعلان المحادثة
|
||||
- الإبلاغ عن إنشاء المشروع مع المعرف المُصنّع
|
||||
- تسجيل ما إذا كانت العملية تحتوي على مدخلات، دون تسجيل المدخلات
|
||||
- ملء معرف المشروع من كل أمر مشروع يتم استدعاؤه بواسطة المستخدم
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- الحفاظ على نتائج الأداة عندما تكون الإجابة النهائية فارغة
|
||||
- ربط Claude Sonnet 4.6 الافتراضي بنافذة السياق 1M الخاصة به
|
||||
- رفع الحد الأقصى الافتراضي لـ max_tokens من Anthropic لاستدعاءات الأدوات الكبيرة
|
||||
- عرض أجزاء محتوى الرسالة كنص، وليس كتمثيل بايثون
|
||||
- الاحتفاظ بأدوار الرسائل عندما يحصل Agent.kickoff على محادثة
|
||||
- تخطي روابط الاعتراض على تدفقات crewai-internal
|
||||
- تسجيل فشل المهام كفشلات، وليس نجاحات
|
||||
- إصدار دورة حياة التدفق عند استئناف مكتوم
|
||||
- فتح واجهة المستخدم النصية للمحادثة لتدفق دردشة إعلاني
|
||||
- تسجيل crew_memory كسلسلة نصية، وليس كقيمة منطقية
|
||||
- إصدار project_id دائمًا حتى تظل القيم الغائبة والفارغة متميزة
|
||||
|
||||
### الوثائق
|
||||
- توضيح وثائق المراقبة لـ Arize Phoenix
|
||||
|
||||
## المساهمون
|
||||
|
||||
@Vidit-Ostwal, @arizedatngo, @joaomdmoura, @lorenzejay, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="19 أغسطس 2026">
|
||||
## v1.15.17
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.17)
|
||||
|
||||
## ما الذي تغيّر
|
||||
|
||||
### الميزات
|
||||
- إضافة وثائق تدفقات المحادثة التصريحية
|
||||
- توليف طرق المحادثة المدمجة للتصريحات
|
||||
- تمكين التصريحات من قيادة وضع المحادثة
|
||||
- جعل خيار الانضمام إلى المحادثة لا لبس فيه
|
||||
- حمل شريحة AMP على الأدوات المستخرجة من مرجع الشريحة
|
||||
- التعامل مع الرسائل الفردية الكبيرة أثناء تقسيمها
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح استخدام اسم المضيف URL كاسم خادم MCP HTTP و SSE
|
||||
- إغلاق نطاق الوكيل في كل محاولة فاشلة
|
||||
- نسب أخطاء الأدوات إلى الأداة التي فشلت
|
||||
- تثبيت فحوصات SSRF على كل خطوة إعادة توجيه وعنوان IP النظير
|
||||
- حل المشكلات المتعلقة بالاستدعاءات الأصلية للأدوات المعطلة عبر واجهة برمجة تطبيقات استجابات OpenAI
|
||||
|
||||
### الوثائق
|
||||
- تحديث الوثائق مع لقطة وتغيير السجل للإصدار v1.15.16
|
||||
|
||||
## المساهمون
|
||||
|
||||
@Copilot, @Vidit-Ostwal, @github-code-quality[bot], @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="13 أغسطس 2026">
|
||||
## v1.15.16
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.16)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- تقديم إدارة سياق التنفيذ مع دعم UUID
|
||||
- تسجيل نوع الاستثناء الذي أنهى تدفق العمل
|
||||
- تسجيل متى تم مشاركة دفعة تتبع مع AMP
|
||||
- عد عمليات النشر من أي مصدر وتسجيل مكان بدايتها
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- تسجيل الإصدار الجاري على كل نطاق تم إصداره
|
||||
- إصلاح التحقق من صحة اسم جدول البحث في MySQL
|
||||
- منع فشل دورة من تحديد الدورة التالية على أنها فاشلة
|
||||
|
||||
### الوثائق
|
||||
- إضافة أدلة الواجهة الأمامية لـ CopilotKit و AG-UI
|
||||
|
||||
## المساهمون
|
||||
|
||||
@joaomdmoura, @lorenzejay, @ranst91, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="11 أغسطس 2026">
|
||||
## v1.15.15
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.15)
|
||||
|
||||
## ما الذي تغيّر
|
||||
|
||||
### الميزات
|
||||
- الإبلاغ عن نتيجة التدفق، والمدة، وإشارات الإنسان في الحلقة.
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصدار FlowStartedEvent عندما يقوم خطاف الحدود بإلغاء التدفق.
|
||||
- تحديد نطاق تصدير النطاق لمزود المتعقب الخاص بنا.
|
||||
- ترقية torch إلى الإصدار 2.13.0 لمعالجة ثغرة أمنية.
|
||||
- ترقية gitpython إلى الإصدار 3.1.58 في crewai-tools[github].
|
||||
|
||||
### إعادة الهيكلة
|
||||
- تحديث وظيفة حقن التاريخ في الوكلاء.
|
||||
- توحيد علامات CLI إلى صيغة kebab-case.
|
||||
|
||||
### الوثائق
|
||||
- لقطة وتغيير السجل للإصدار v1.15.14.
|
||||
|
||||
## المساهمون
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="8 أغسطس 2026">
|
||||
## v1.15.14
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.14)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- فصل سياق وقت التشغيل عن وكيل الترميز وإضافة معرف المشروع
|
||||
|
||||
### الوثائق
|
||||
- تحديث اللقطة وسجل التغييرات للإصدار v1.15.13
|
||||
|
||||
## المساهمون
|
||||
|
||||
@joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="7 أغسطس 2026">
|
||||
## v1.15.13
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.13)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح الحفاظ على مزود النماذج الموجهة بواسطة LiteLLM.
|
||||
- تعزيز نماذج حدث LLM الهشة.
|
||||
- إصلاح التقارير الناقصة لاستخدام رموز التخزين المؤقت من Anthropic.
|
||||
- ترقية h2 إلى الإصدار 4.4.1 لمعالجة ثغرة الأمان GHSA-6hr6-w5qg-qmwg.
|
||||
|
||||
### الوثائق
|
||||
- إضافة سير العمل DOCS_TRANSLATIONS لمزامنة المواقع.
|
||||
- إصلاح الروابط المعطلة في README، وفهرس المحتويات، وإرشادات المساهمة.
|
||||
- لقطة وتغيير سجل الإصدار 1.15.12.
|
||||
|
||||
## المساهمون
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="5 أغسطس 2026">
|
||||
## v1.15.12
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.12)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- زيادة إصدار Flow canary عند الإصدار
|
||||
- إضافة URLReadTool لقراءة عناوين URL العشوائية
|
||||
- إضافة بيانات التعريف الخاصة بالتطبيق إلى أدوات إجراءات المنصة
|
||||
- توحيد الهيكل تحت `crewai create <resource>`
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- توضيح أخطاء تصادم أسماء المسارات/المعالجين في المحادثات
|
||||
|
||||
### الوثائق
|
||||
- تحديث ملف AGENTS.md للهيكل الموحد لأداة سطر الأوامر لإنشاء
|
||||
|
||||
### تغييرات كبيرة
|
||||
- لا شيء
|
||||
|
||||
## المساهمون
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="4 أغسطس 2026">
|
||||
## v1.15.11
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.11)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- تتبع عمليات إرسال خطاف الاعتراض في التلميتري
|
||||
- إضافة project_id لربط استخدام OSS بحساب المؤسسة
|
||||
- عرض AMP في AGENTS.md واكتشاف وكلاء الترميز في التلميتري
|
||||
- إضافة أداة بحث IBM Db2
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- مسح تنبيهات تطهير جزء URL غير المكتمل في CodeQL
|
||||
- تحديث aiohttp وcryptography لمسح ستة تحذيرات GHSA
|
||||
- الإبلاغ عن خطأ CEL الحقيقي للفشل داخل الأدبيات الخرائطية
|
||||
- تخطي CI الكود بشكل صحيح لطلبات السحب الخاصة بالوثائق فقط
|
||||
|
||||
### الوثائق
|
||||
- لقطة وتغيير السجل للإصدار v1.15.10
|
||||
|
||||
## المساهمون
|
||||
|
||||
@PawanThakurIBM, @Vidit-Ostwal, @gabemilani, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="31 يوليو 2026">
|
||||
## v1.15.10
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ mode: "wide"
|
||||
| **احترام نافذة السياق** _(اختياري)_ | `respect_context_window` | `bool` | إبقاء الرسائل تحت حجم نافذة السياق عبر التلخيص. الافتراضي True. |
|
||||
| **وضع تنفيذ الكود** _(اختياري)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | وضع تنفيذ الكود: 'safe' (باستخدام Docker) أو 'unsafe' (مباشر). الافتراضي 'safe'. |
|
||||
| **متعدد الوسائط** _(اختياري)_ | `multimodal` | `bool` | ما إذا كان الوكيل يدعم القدرات متعددة الوسائط. الافتراضي False. |
|
||||
| **حقن التاريخ** _(اختياري)_ | `inject_date` | `bool` | ما إذا كان يتم حقن التاريخ الحالي تلقائيًا في أمر الوكيل. الافتراضي False. |
|
||||
| **حقن التاريخ** _(اختياري)_ | `inject_date` | `bool` | ما إذا كان يتم حقن التاريخ الحالي تلقائيًا في المهام. الافتراضي False. |
|
||||
| **تنسيق التاريخ** _(اختياري)_ | `date_format` | `str` | سلسلة تنسيق التاريخ عند تفعيل inject_date. الافتراضي "%Y-%m-%d" (تنسيق ISO). |
|
||||
| **الاستدلال** _(اختياري)_ | `reasoning` | `bool` | ما إذا كان يجب على الوكيل التأمل وإنشاء خطة قبل تنفيذ المهمة. الافتراضي False. |
|
||||
| **الحد الأقصى لمحاولات الاستدلال** _(اختياري)_ | `max_reasoning_attempts` | `Optional[int]` | الحد الأقصى لمحاولات الاستدلال قبل تنفيذ المهمة. إذا None، سيحاول حتى الاستعداد. |
|
||||
@@ -287,7 +287,7 @@ analysis_agent = Agent(
|
||||
|
||||
- `multimodal`: تفعيل القدرات متعددة الوسائط لمعالجة النص والمحتوى المرئي
|
||||
- `reasoning`: تمكين الوكيل من التأمل وإنشاء خطط قبل تنفيذ المهام
|
||||
- `inject_date`: حقن التاريخ الحالي تلقائيًا في أمر الوكيل
|
||||
- `inject_date`: حقن التاريخ الحالي تلقائيًا في أوصاف المهام
|
||||
|
||||
#### القوالب
|
||||
|
||||
|
||||
@@ -54,16 +54,6 @@ crewai create flow my_new_flow
|
||||
|
||||
افتراضيًا، ينشئ `crewai create crew` مشروعًا JSON-first يحتوي على `crew.jsonc` و `agents/*.jsonc`. استخدم `crewai create crew my_new_crew --classic` فقط إذا أردت البنية القديمة Python/YAML مع `crew.py` و `config/agents.yaml` و `config/tasks.yaml`.
|
||||
|
||||
#### أسماء مستعار قديمة للأعلام (مهملة)
|
||||
|
||||
لا تزال أعلام snake_case القديمة تعمل، لكنها مخفية من `--help`. يُفضّل استخدام صيغ kebab-case الموثّقة في أقسام الأوامر أدناه.
|
||||
|
||||
| مهمل | استخدم بدلاً منه |
|
||||
| :--- | :--- |
|
||||
| `--skip_provider` (في `crewai create crew`) | `--skip-provider` |
|
||||
| `--n_iterations` (في `crewai train`، `crewai test`) | `--n-iterations` |
|
||||
| `--task_id` (في `crewai replay`) | `--task-id` |
|
||||
|
||||
### 2. الإصدار
|
||||
|
||||
عرض الإصدار المثبت من CrewAI.
|
||||
@@ -82,7 +72,7 @@ crewai version [OPTIONS]
|
||||
crewai train [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: عدد تكرارات التدريب (افتراضي: 5)
|
||||
- `-n, --n_iterations INTEGER`: عدد تكرارات التدريب (افتراضي: 5)
|
||||
- `-f, --filename TEXT`: مسار ملف مخصص للتدريب (افتراضي: "trained_agents_data.pkl")
|
||||
|
||||
### 4. الإعادة
|
||||
@@ -93,7 +83,7 @@ crewai train [OPTIONS]
|
||||
crewai replay [OPTIONS]
|
||||
```
|
||||
|
||||
- `-t, --task-id TEXT`: إعادة تنفيذ الطاقم من معرّف المهمة هذا، بما في ذلك جميع المهام اللاحقة
|
||||
- `-t, --task_id TEXT`: إعادة تنفيذ الطاقم من معرّف المهمة هذا، بما في ذلك جميع المهام اللاحقة
|
||||
|
||||
### 5. سجل مخرجات المهام
|
||||
|
||||
@@ -127,7 +117,7 @@ crewai reset-memories [OPTIONS]
|
||||
crewai test [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: عدد تكرارات الاختبار (افتراضي: 3)
|
||||
- `-n, --n_iterations INTEGER`: عدد تكرارات الاختبار (افتراضي: 3)
|
||||
- `-m, --model TEXT`: نموذج LLM لتشغيل الاختبارات (افتراضي: "gpt-4o-mini")
|
||||
|
||||
### 8. التشغيل
|
||||
|
||||
@@ -172,8 +172,6 @@ class YourCrewName:
|
||||
|
||||
بعد تنفيذ الطاقم، يمكنك الوصول إلى خاصية `usage_metrics` لعرض مقاييس استخدام نموذج اللغة (LLM) لجميع المهام المنفذة.
|
||||
|
||||
`total_tokens` هو الإجمالي المفوتر (`prompt_tokens + completion_tokens`). حقول التفصيل مثل `cached_prompt_tokens` و`cache_creation_tokens` تصف أجزاءً مُدرجة بالفعل ضمن تلك الإجماليات ولا تُضاف مرة أخرى إلى `total_tokens`. راجع قسم **UsageMetrics field semantics** في توثيق مفهوم Flows للحصول على العقد الكامل.
|
||||
|
||||
```python Code
|
||||
crew = Crew(agents=[agent1, agent2], tasks=[task1, task2])
|
||||
crew.kickoff()
|
||||
|
||||
@@ -266,24 +266,6 @@ print(flow.usage_metrics)
|
||||
كلما احتجت إلى الإجمالي **الكامل** للتوكنات لتنفيذ التدفق.
|
||||
</Note>
|
||||
|
||||
### دلالات حقول UsageMetrics
|
||||
|
||||
يستخدم كائن [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) المُعاد عقدًا محايدًا للمزود:
|
||||
|
||||
| الحقل | المعنى |
|
||||
| --- | --- |
|
||||
| `total_tokens` | الإجمالي المفوتر: `prompt_tokens + completion_tokens` |
|
||||
| `prompt_tokens` | إجمالي رموز الإدخال/المطالبة المفوترة للطلب |
|
||||
| `completion_tokens` | رموز الإخراج/الإكمال المفوترة للطلب |
|
||||
| `cached_prompt_tokens` | جزء قراءة الذاكرة المؤقتة من رموز المطالبة (تفصيل فقط) |
|
||||
| `cache_creation_tokens` | جزء كتابة الذاكرة المؤقتة من رموز المطالبة (تفصيل فقط، Anthropic) |
|
||||
| `reasoning_tokens` | جزء التفكير/الاستدلال حيث يبلّغ المزود عنه بشكل منفصل (تفصيل فقط) |
|
||||
| `successful_requests` | عدد استدعاءات LLM المُجمّعة |
|
||||
|
||||
حقول التفصيل مثل `cached_prompt_tokens` و`cache_creation_tokens` و`reasoning_tokens` **لا تُضاف** فوق `total_tokens` — بل تصف أجزاءً مُدرجة بالفعل ضمن `prompt_tokens` أو `completion_tokens`.
|
||||
|
||||
بالنسبة إلى Anthropic، تُدمج عدادات قراءة وكتابة الذاكرة المؤقتة ضمن `prompt_tokens`، لذا تنعكس أعباء العمل المخزنة مؤقتًا بالكامل في `total_tokens`. يُدرج مزودو OpenAI الرموز المخزنة مؤقتًا بالفعل داخل `prompt_tokens`؛ يعرض CrewAI الجزء المخزن مؤقتًا بشكل منفصل للوضوح.
|
||||
|
||||
كل حقل في [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) المُعاد هو مجموع جميع استدعاءات نموذج اللغة التي حدثت خلال استدعاء واحد لـ `flow.kickoff()`. تتم إعادة تعيين العدادات عند الاستدعاء التالي لـ `kickoff()` (وفي كل تكرار من `kickoff_for_each`)، لذلك لن تتكرر العدّات عبر التشغيلات المتتالية. يمكن قراءة هذه الخاصية بأمان في أي وقت بعد اكتمال `kickoff()`؛ قراءتها أثناء التنفيذ تُرجع المجموع الجزئي المتراكم حتى تلك اللحظة.
|
||||
|
||||
## إدارة حالة التدفق
|
||||
|
||||
@@ -392,22 +392,6 @@ mode: "wide"
|
||||
- تتبع استخدام الرموز
|
||||
- محادثات استخدام أدوات متعددة الأدوار
|
||||
|
||||
**استخدام الرموز والتخزين المؤقت للمطالبة:**
|
||||
|
||||
يُبلّغ Anthropic عن الإدخال المفوتر في عدادات منفصلة — `input_tokens` (غير المخزن مؤقتًا)، و`cache_read_input_tokens`، و`cache_creation_input_tokens`. يدمج CrewAI الثلاثة ضمن `prompt_tokens` (و`input_tokens` الأصلي في استجابات المزود) بحيث يعكس `total_tokens` الاستخدام المفوتر الكامل على أعباء العمل المخزنة مؤقتًا.
|
||||
|
||||
يسجّل `cached_prompt_tokens` جزء قراءة الذاكرة المؤقتة كتفصيل فقط؛ وهو مُدرج بالفعل ضمن `prompt_tokens` ولا يجب إضافته مرة أخرى إلى `total_tokens`. يسجّل `cache_creation_tokens` عمليات الكتابة في الذاكرة المؤقتة بنفس الطريقة.
|
||||
|
||||
```python Code
|
||||
usage = llm.get_token_usage_summary()
|
||||
# total_tokens == prompt_tokens + completion_tokens
|
||||
# prompt_tokens includes cache read + cache write for Anthropic
|
||||
```
|
||||
|
||||
راجع قسم **UsageMetrics field semantics** في توثيق مفهوم Flows
|
||||
للحصول على العقد المحايد للمزود المستخدم في `crew.usage_metrics`
|
||||
و`flow.usage_metrics`.
|
||||
|
||||
**ملاحظات مهمة:**
|
||||
- `max_tokens` معامل **مطلوب** لجميع نماذج Anthropic
|
||||
- يستخدم Claude `stop_sequences` بدلاً من `stop`
|
||||
|
||||
@@ -736,7 +736,7 @@ memory = Memory(llm="anthropic/claude-3-haiku-20240307")
|
||||
memory = Memory(llm="ollama/llama3.2")
|
||||
|
||||
# Use Google Gemini
|
||||
memory = Memory(llm="gemini/gemini-3.7-flash")
|
||||
memory = Memory(llm="gemini/gemini-2.0-flash")
|
||||
|
||||
# Pass a pre-configured LLM instance with custom settings
|
||||
llm = LLM(model="gpt-4o", temperature=0)
|
||||
|
||||
@@ -20,7 +20,7 @@ crewai test
|
||||
إذا أردت تشغيل المزيد من التكرارات أو استخدام نموذج مختلف، يمكنك تحديد المعاملات هكذا:
|
||||
|
||||
```bash
|
||||
crewai test --n-iterations 5 --model gpt-4o
|
||||
crewai test --n_iterations 5 --model gpt-4o
|
||||
```
|
||||
|
||||
أو باستخدام الصيغة المختصرة:
|
||||
@@ -29,11 +29,6 @@ crewai test --n-iterations 5 --model gpt-4o
|
||||
crewai test -n 5 -m gpt-4o
|
||||
```
|
||||
|
||||
<Note>
|
||||
العلم القديم `--n_iterations` لا يزال يعمل، لكنه مهمل ومخفي من `--help`.
|
||||
استخدم `--n-iterations` (أو `-n`) بدلاً من ذلك.
|
||||
</Note>
|
||||
|
||||
عند تشغيل أمر `crewai test`، سيتم تنفيذ الطاقم للعدد المحدد من التكرارات، وستُعرض مقاييس الأداء في نهاية التشغيل.
|
||||
|
||||
سيظهر جدول الدرجات في النهاية لعرض أداء الطاقم من حيث المقاييس التالية:
|
||||
|
||||
@@ -75,7 +75,7 @@ research_crew/
|
||||
}
|
||||
```
|
||||
|
||||
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `anthropic/claude-sonnet-4-6` أو `gemini/gemini-3.7-flash`.
|
||||
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `anthropic/claude-sonnet-4-6` أو `gemini/gemini-2.0-flash-001`.
|
||||
|
||||
## الخطوة 3: تعريف المهام وإعدادات الـ Crew
|
||||
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
---
|
||||
title: تدفقات المحادثة
|
||||
description: أنشئ تطبيقات دردشة متعددة الجولات باستخدام handle_turn لكل جولة، وسجل الرسائل، وتوجيه النية، والتتبع، والبث المنظّم.
|
||||
description: أنشئ تطبيقات دردشة متعددة الجولات مع kickoff لكل جولة وسجل الرسائل وتوجيه النية والتتبع وجسور WebSocket.
|
||||
icon: comments
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
تعامل التطبيقات المحادثية مع كل سطر من المستخدم كـ **تشغيل flow جديد** بنفس **معرّف الجلسة**. توفر CrewAI مساعدات لسجل الرسائل، وتوجيه النية الاختياري، وتأجيل التتبع، والبث المنظّم للجولات، إضافة إلى REPL محلي عبر `flow.chat()`.
|
||||
تعامل التطبيقات المحادثية مع كل سطر من المستخدم كـ **تشغيل flow جديد** بنفس **معرّف الجلسة**. توفر CrewAI مساعدات لسجل الرسائل وتصنيف النية الاختياري وتأجيل التتبع وجسور الواجهة، إضافة إلى REPL محلي `flow.chat()` للتدفقات المحادثية.
|
||||
|
||||
| المفهوم | التنفيذ |
|
||||
|---------|---------|
|
||||
| معرّف الجلسة | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
|
||||
| سطر المستخدم | `handle_turn(message)` يضيف الرسالة إلى `state.messages` قبل تشغيل الرسم |
|
||||
| اكتمال الجولة | `conversation_turn_completed`؛ ومع تأجيل التتبع الافتراضي ينتظر `FlowFinished` استدعاء `finalize_session_traces()` |
|
||||
| تتبع الجلسة الكامل | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
|
||||
| اكتمال الجولة | `FlowFinished` لهذا **التشغيل** فقط؛ تستمر المحادثة في `handle_turn` التالي |
|
||||
| تتبع الجلسة | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
|
||||
|
||||
## واجهات الجولات
|
||||
|
||||
استخدم **`flow.handle_turn(message, session_id=...)`** لكل رسالة مستخدم من REST أو WebSocket أو الاختبارات أو الواجهات المخصصة. استخدم **`flow.chat()`** عندما تريد حلقة دردشة محلية في الطرفية لـ `Flow` محادثي.
|
||||
|
||||
لا يقبل `Flow.kickoff()` الوسيطين `user_message=` أو `session_id=`. في التدفقات المحادثية، يخزن `handle_turn()` الرسالة المعلقة ويستدعي داخلياً `kickoff(inputs={"id": session_id})` بعد إعادة ضبط حالة التنفيذ الخاصة بالجولة.
|
||||
لا يقبل `Flow.kickoff()` الوسيطين `user_message=` أو `session_id=`. في التدفقات المحادثية، يخزن `handle_turn()` الرسالة المعلقة ويستدعي داخلياً `kickoff(inputs={"id": session_id})`.
|
||||
|
||||
| API | الاستخدام |
|
||||
|-----|-----------|
|
||||
| `handle_turn(message, session_id=...)` | غلاف مريح لجولة واحدة في `Flow` محادثي |
|
||||
| `stream_turn(message, session_id=...)` | بث جولة محادثية واحدة كإطارات runtime مرتبة |
|
||||
| `chat()` | REPL محلي في الطرفية لـ `Flow` محادثي |
|
||||
| `kickoff(inputs={...})` | تشغيل متقدم للـ flow بدون معالجة جولة محادثية |
|
||||
| `ask()` | مطالبة حاجزة **داخل** خطوة واحدة (معالج إرشادي أو طلب توضيح) |
|
||||
| `ask()` | مطالبة حاجزة **داخل** خطوة واحدة |
|
||||
| `@human_feedback` | الموافقة/الرفض على **مخرجات خطوة** — وليس السطر التالي |
|
||||
|
||||
ترفع `handle_turn()` و`stream_turn()` و`chat()` الخطأ `ValueError` ما لم يكن الوضع المحادثاتي مفعّلاً. يؤدي تطبيق `@ConversationConfig(...)` إلى تفعيله تلقائياً؛ وإلا فعيّن `conversational = True`.
|
||||
| `ChatSession.handle_turn(...)` | طبقة نقل فوق `handle_turn` |
|
||||
|
||||
## بداية سريعة
|
||||
|
||||
@@ -40,7 +38,7 @@ from uuid import uuid4
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
)
|
||||
@@ -48,29 +46,31 @@ from crewai.flow import (
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
def route_turn(self, context):
|
||||
message = (self.state.current_user_message or "").lower()
|
||||
if "order" in message:
|
||||
if "طلب" in message or "order" in message:
|
||||
return "order"
|
||||
if "bye" in message or "goodbye" in message:
|
||||
if "وداع" in message or "goodbye" in message:
|
||||
return "goodbye"
|
||||
return "help"
|
||||
|
||||
@listen("order")
|
||||
def handle_order(self):
|
||||
reply = "Your order is on the way."
|
||||
reply = "طلبك في الطريق."
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
@listen("help")
|
||||
def handle_help(self):
|
||||
reply = "How can I help?"
|
||||
reply = "كيف يمكنني المساعدة؟"
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
@listen("goodbye")
|
||||
def handle_goodbye(self):
|
||||
reply = "Goodbye!"
|
||||
reply = "وداعاً!"
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
@@ -79,141 +79,130 @@ session_id = str(uuid4())
|
||||
flow = SupportFlow()
|
||||
|
||||
try:
|
||||
flow.handle_turn("Where is my order?", session_id=session_id)
|
||||
flow.handle_turn("What about returns?", session_id=session_id)
|
||||
flow.handle_turn("أين طلبي؟", session_id=session_id)
|
||||
flow.handle_turn("وماذا عن الإرجاع؟", session_id=session_id)
|
||||
finally:
|
||||
flow.finalize_session_traces() # one trace link for the whole chat
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
|
||||
## بث جولة
|
||||
|
||||
استخدم `stream_turn()` عندما تحتاج واجهة مستخدم أو بيئة تشغيل إلى أحداث منظّمة لجولة دردشة واحدة. يعيد جلسة بث تحتوي على إطارات مرتبة لتوجيه Flow، وأجزاء LLM، ونشاط الأدوات، ورسائل المحادثة.
|
||||
|
||||
```python
|
||||
stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
|
||||
راجع [عقد بيئة البث](/edge/ar/learn/streaming-runtime-contract) للاطلاع على عقد الإطارات الكامل وقائمة القنوات.
|
||||
|
||||
## دورة حياة الجولة
|
||||
|
||||
يشغّل كل `handle_turn` المسار التالي:
|
||||
كل `handle_turn` يشغّل:
|
||||
|
||||
1. **إعداد الجولة** — يخزن رسالة المستخدم المعلقة، ويحل معرّف الجلسة، ويعيد ضبط تعقّب التنفيذ الخاص بالجولة، ثم يستدعي `kickoff(inputs={"id": session_id})`.
|
||||
2. **استعادة الحالة** — إذا وُجد `inputs["id"]` وكان `@persist` مهيّأً، تُحمّل أحدث لقطة.
|
||||
1. **`_configure_conversational_kickoff`** — دمج `session_id` / `user_message` في `inputs` وتطبيق `ConversationalConfig`.
|
||||
2. **استعادة الحالة** — عند وجود `inputs["id"]` و`@persist`.
|
||||
3. **`FlowStarted`** — في أول جولة للجلسة المؤجلة فقط.
|
||||
4. **ترطيب الجولة المعلقة** — تُضاف رسالة المستخدم إلى `state.messages`، وتُضبط `current_user_message` / `last_user_message`، ويُجرى التصنيف اختيارياً عند ضبط `intents` / `default_intents` مع `intent_llm`.
|
||||
5. **تنفيذ الرسم** — طرق `@start` التي يعرّفها المستخدم (إن وجدت) → `route_conversation` (نقطة البدء/الموجّه المدمجة) → معالج `@listen` المختار. تستدعي `route_conversation` أيضاً المساعد القابل للتجاوز `conversation_start()`.
|
||||
6. **نهاية التشغيل** — يُتخطى `flow_finished` لكل جولة وإنهاء التتبع عند تفعيل التأجيل؛ كما لا تغلق استدعاءات `Agent.kickoff()` المتداخلة أو crews دفعة الأب.
|
||||
4. **`prepare_conversational_turn`** — إضافة رسالة المستخدم و`last_user_message` وتصنيف اختياري.
|
||||
5. **تنفيذ الرسم** — `@start` → `@router` → معالجات `@listen`.
|
||||
6. **نهاية التشغيل** — يُتخطى `flow_finished` والتتبع لكل جولة عند التأجيل؛ `Agent.kickoff()` / crews لا تغلق دفعة الأب.
|
||||
|
||||
استدعِ **`append_assistant_message(reply)`** عندما لا تطابق الرد الظاهر قيمة الإرجاع، أو عند قصّ التاريخ. تُسجَّل أيضاً سلسلة الإرجاع العامة كمساعد وتُضمَّن في لقطة `@persist`، فتستعيدها نسخة Flow جديدة. سطر المستخدم محفوظ عبر `handle_turn` — لا تُضفه مرة أخرى.
|
||||
استدعِ **`append_assistant_message(reply)`** في المعالجات. سطر المستخدم محفوظ عبر `handle_turn` — لا تُضفه مرة أخرى.
|
||||
|
||||
## نظرة عامة على الإعداد
|
||||
## `ConversationalConfig` (افتراضيات على مستوى الصنف)
|
||||
|
||||
يؤدي تزيين صنف فرعي من `Flow` بـ `ConversationConfig` إلى إرفاق افتراضيات الدردشة وتفعيل الوضع المحادثاتي معاً. راجع [مرجع الحقول الكامل](#conversationconfig) أدناه. ويمكنك تجاوز التصنيف المسبق لكل جولة عبر `handle_turn(..., intents=..., intent_llm=...)`.
|
||||
عيّن على صنف `Flow` كـ `conversational_config: ClassVar[ConversationalConfig | None]`.
|
||||
|
||||
## مساعدات `ChatState` منخفضة المستوى
|
||||
| الحقل | الافتراضي | الغرض |
|
||||
|-------|-----------|--------|
|
||||
| `default_intents` | `None` | تسميات outcome للتصنيف التلقائي قبل kickoff |
|
||||
| `intent_llm` | `None` | نموذج التصنيف (مطلوب عند وجود intents) |
|
||||
| `interactive_prompt` | `"You: "` | مطالبة `kickoff(interactive=True)` |
|
||||
| `interactive_timeout` | `None` | مهلة لكل سطر في الوضع التفاعلي |
|
||||
| `exit_commands` | `exit`, `quit` | كلمات إنهاء الوضع التفاعلي |
|
||||
| `defer_trace_finalization` | `True` | إبقاء دفعة trace واحدة مفتوحة بين الجولات |
|
||||
|
||||
تظل `ChatState` و`ConversationalConfig` القديمة ومساعدات `crewai.flow.conversation` قابلة للاستيراد للتنسيق المتقدم أو الاختبارات أو الأغلفة المخصصة. وهي منفصلة عن واجهتي `ConversationState` / `ConversationConfig`، ولا تضيف وسيطي `user_message=` أو `session_id=` إلى `Flow.kickoff()`.
|
||||
يمكن التجاوز لكل kickoff عبر `intents=` و`intent_llm=`.
|
||||
|
||||
## `ChatState` (شكل الحالة الموصى به للحفظ)
|
||||
|
||||
```python
|
||||
from crewai.flow import ChatState
|
||||
|
||||
|
||||
class MyChatState(ChatState):
|
||||
# Inherited: id, messages, last_user_message, last_intent, session_ready
|
||||
# موروث: id, messages, last_user_message, last_intent, session_ready
|
||||
research_turn_count: int = 0
|
||||
custom_flag: bool = False
|
||||
```
|
||||
|
||||
| الحقل | الدور |
|
||||
|-------|------|
|
||||
| `id` | UUID الجلسة (نفس `inputs["id"]`) |
|
||||
| `messages` | `list` من `{role, content}` لسجل LLM |
|
||||
| `id` | UUID الجلسة (مثل `session_id` / `inputs["id"]`) |
|
||||
| `messages` | قائمة `{role, content}` لسجل LLM |
|
||||
| `last_user_message` | آخر سطر مستخدم في هذه الجولة |
|
||||
| `last_intent` | تسمية المسار بعد التصنيف (إن وُجد) |
|
||||
| `session_ready` | علم bootstrap لمرة واحدة (الصلاحيات، وذاكرات التخزين المؤقت، وغيرها) |
|
||||
| `session_ready` | علم bootstrap لمرة واحدة |
|
||||
|
||||
`ConversationalInputs` هو `TypedDict` لمفاتيح `kickoff(inputs={...})` الاصطلاحية: `id` و`user_message` و`last_intent`.
|
||||
|
||||
تخزن `ConversationState` رسائل `messages` ككائنات `ConversationMessage`، وتوفر أيضاً `current_user_message` و`ended` و`events` و`agent_threads`. استخدم `conversation_messages` عند تمرير سجلها القانوني إلى LLM.
|
||||
`ConversationalInputs` هو `TypedDict` لـ `kickoff(inputs={...})`: `id`, `user_message`, `last_intent`.
|
||||
|
||||
## API المحادثة على `Flow`
|
||||
|
||||
### معاملات `handle_turn`
|
||||
### معاملات `kickoff` / `kickoff_async`
|
||||
|
||||
| المعامل | الغرض |
|
||||
|---------|--------|
|
||||
| `message` | نص هذه الجولة |
|
||||
| `user_message` | نص هذه الجولة (أو `{"role": "user", "content": "..."}`) |
|
||||
| `session_id` | UUID المحادثة → `inputs["id"]` / `state.id` |
|
||||
| `intents` | تسميات النتائج لـ `classify_intent` قبل kickoff |
|
||||
| `intents` | تسميات outcome لـ `classify_intent` قبل kickoff |
|
||||
| `intent_llm` | LLM للتصنيف (مطلوب مع `intents`) |
|
||||
| `**kickoff_kwargs` | تُمرر إلى `kickoff()` لخيارات مثل `input_files` و`from_checkpoint` و`restore_from_state_id` |
|
||||
|
||||
### معاملات `kickoff`
|
||||
|
||||
يقبل `Flow.kickoff()` كلاً من `inputs` و`input_files` و`from_checkpoint` و`restore_from_state_id`. مرر `inputs={"id": session_id}` عندما تحتاج إلى تنفيذ flow خام، لكن استخدم `handle_turn()` عندما يمثل الاستدعاء رسالة دردشة.
|
||||
| `interactive` | حلقة CLI عبر `ask()` (للعروض المحلية فقط) |
|
||||
| `interactive_prompt` | مطالبة الوضع التفاعلي |
|
||||
| `interactive_timeout` | مهلة `ask()` لكل سطر |
|
||||
| `exit_commands` | كلمات إنهاء الوضع التفاعلي |
|
||||
| `inputs` | حقول حالة إضافية |
|
||||
| `restore_from_state_id` | استنساخ من flow محفوظ آخر |
|
||||
|
||||
### سمات المثيل
|
||||
|
||||
| السمة | الغرض |
|
||||
|-------|--------|
|
||||
| `conversational` | عيّنه على `True` لتفعيل الرسم المحادثاتي و`handle_turn()` |
|
||||
| `defer_trace_finalization` | تجاوز اختياري على مستوى المثيل. وإلا تقرأ `_should_defer_trace_finalization()` القيمة `ConversationConfig.defer_trace_finalization`. |
|
||||
| `suppress_flow_events` | يخفي لوحات flow في الطرفية ويمنع أحداث تنفيذ الطرق؛ وتظل أحداث بدء/انتهاء flow تصدر |
|
||||
| `stream` | علم البث العام لـ Flow. استخدم `stream_turn()` للجولات المحادثية بدلاً من جمع هذا العلم مع `handle_turn()`. |
|
||||
| `conversational_config` | افتراضيات `ConversationalConfig` على مستوى الصنف |
|
||||
| `defer_trace_finalization` | علم المثيل؛ يُضبط تلقائياً من config عند kickoff |
|
||||
| `suppress_flow_events` | يخفي لوحات console؛ **التتبع يُسجّل** |
|
||||
| `stream` | بث؛ مع `ChatSession.handle_turn(..., stream=True)` |
|
||||
|
||||
### طرق وخصائص
|
||||
|
||||
| الاسم | الوصف |
|
||||
|------|--------|
|
||||
| `append_assistant_message(content)` | إضافة رد مساعد مرئي للمستخدم إلى `state.messages` |
|
||||
| `append_message(role, content, **extra)` | إضافة إلى `state.messages` |
|
||||
| `conversation_messages` | سجل للقراءة فقط لاستدعاءات LLM |
|
||||
| `classify_intent(text, outcomes, *, llm, context=None)` | تعيين النص إلى نتيجة واحدة (بنفس منطق الاختزال المستخدم في `@human_feedback`) |
|
||||
| `receive_user_message(text, *, outcomes=None, llm=None)` | إضافة رسالة مستخدم، وضبط `last_intent` اختيارياً |
|
||||
| `classify_intent(text, outcomes, *, llm, context=None)` | تعيين outcome |
|
||||
| `receive_user_message(text, *, outcomes=None, llm=None)` | إضافة رسالة مستخدم؛ `last_intent` اختياري |
|
||||
| `finalize_session_traces()` | إصدار `flow_finished` المؤجل وإنهاء دفعة trace |
|
||||
| `_should_defer_trace_finalization()` | hook متقدم/داخلي يحسم ما إذا كان إنهاء trace لكل جولة مؤجلاً |
|
||||
| `_should_defer_trace_finalization()` | هل يُؤجل إنهاء trace لكل جولة |
|
||||
| `input_history` | سجل تدقيق مطالبات وردود `ask()` |
|
||||
|
||||
### مساعدات الوحدة (`crewai.flow.conversation`)
|
||||
|
||||
يمكن استيرادها من `crewai.flow.conversation` للاختبارات أو التنسيق المخصص. تستخدم هذه المساعدات بنية `ConversationalConfig` القديمة؛ كما تمسح `prepare_conversational_turn()` قيمة `last_intent`، بخلاف `handle_turn()` التي تحتفظ بها كسياق للموجّه.
|
||||
|
||||
| الدالة | الوصف |
|
||||
|--------|--------|
|
||||
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)` | دمج وسائط المحادثة في `inputs` |
|
||||
| `normalize_kickoff_inputs(...)` | دمج kwargs المحادثة في `inputs` |
|
||||
| `get_conversation_messages(flow)` | قراءة الرسائل من الحالة أو المخزن |
|
||||
| `append_message(flow, role, content, **extra)` | مثل طريقة المثيل |
|
||||
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | ترطيب الجولة منخفض المستوى للأغلفة المخصصة |
|
||||
| `receive_user_message(flow, text, ...)` | مثل طريقة المثيل |
|
||||
| `append_message(flow, ...)` | مثل طريقة المثيل |
|
||||
| `prepare_conversational_turn(flow, ...)` | تهيئة الجولة (عادةً kickoff يستدعيها) |
|
||||
| `receive_user_message(flow, ...)` | مثل طريقة المثيل |
|
||||
| `set_state_field(flow, name, value)` | تعيين حقل dict أو Pydantic |
|
||||
| `get_conversational_config(flow)` | قراءة `conversational_config` |
|
||||
| `input_history_to_messages(entries)` | تحويل `input_history` لصيغة رسائل LLM |
|
||||
|
||||
## أنماط توجيه النية
|
||||
|
||||
### أ. تصنيف مسبق عبر `ConversationConfig` (الأبسط)
|
||||
### أ. تصنيف مسبق عبر `ConversationalConfig` (الأبسط)
|
||||
|
||||
عيّن `default_intents` و`intent_llm`. يصنّف كل `handle_turn()` الرسالة الحالية مسبقاً. تكون الأولوية لنتيجة غير فارغة يعيدها `route_turn()` مخصص؛ وإلا تستخدم `route_conversation` النية المصنّفة للجولة الحالية.
|
||||
عيّن `default_intents` و`intent_llm`. كل kickoff يصنّف قبل `@router`؛ اقرأ `self.state.last_intent` في `route()`.
|
||||
|
||||
### ب. تصنيف داخل `route_turn` (مطالبات أغنى)
|
||||
### ب. تصنيف داخل `@router` (مطالبات أغنى)
|
||||
|
||||
عيّن `default_intents=None` كي يضيف `handle_turn()` رسالة المستخدم فقط. داخل `route_turn()`، استدعِ `classify_intent` بمطالبة أو أوصاف مخصصة:
|
||||
عيّن `default_intents=None` ليضيف kickoff الرسالة فقط. في `route()` استدعِ `classify_intent`:
|
||||
|
||||
```python
|
||||
def route_turn(self, context):
|
||||
@router(bootstrap)
|
||||
def route(self):
|
||||
intent = self.classify_intent(
|
||||
self._routing_prompt(self.state.current_user_message),
|
||||
self._routing_prompt(self.state.last_user_message),
|
||||
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
|
||||
llm="gpt-4o-mini",
|
||||
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
|
||||
)
|
||||
self.state.last_intent = intent
|
||||
return intent
|
||||
@@ -223,59 +212,70 @@ def route_turn(self, context):
|
||||
|
||||
## عندما ينتهي الـ flow ويستمر المستخدم
|
||||
|
||||
يُكمل كل `handle_turn()` تشغيل رسم واحد، وتستمر المحادثة عبر `handle_turn()` آخر يستخدم `session_id` نفسه. مع دورة حياة التتبع المؤجلة افتراضياً، يصدر ذلك التشغيل `conversation_turn_completed`، بينما يصدر `FlowFinished` مرة واحدة عندما تغلق `finalize_session_traces()` الجلسة. ويستعيد `@persist` الرسائل والأعلام والسياق.
|
||||
`FlowFinished` يعني أن **تنفيذ الرسم هذا** اكتمل. تستمر المحادثة بـ `kickoff` آخر ونفس `session_id`. `@persist` يستعيد `messages` والأعلام والسياق.
|
||||
|
||||
**نمط الحفظ:** يُفضّل `@persist` على **خطوة نهائية واحدة** (مثل `finalize`) وليس على صنف `Flow` بالكامل. يحفظ الاستمرار على مستوى الصنف بعد كل طريقة؛ وتستخدم `load_state` أحدث صف، وقد يكون لقطة في منتصف التشغيل (مثلاً بعد `bootstrap` مباشرة) لا تتضمن تحديثات المعالج من الجولة نفسها.
|
||||
**نمط الحفظ:** يُفضّل `@persist` على **خطوة نهائية واحدة** (مثل `finalize`) وليس على صنف `Flow` بالكامل. الحفظ على مستوى الصنف بعد كل method قد يفقد تحديثات المعالجات في نفس الجولة.
|
||||
|
||||
لا تستخدم `@human_feedback` لأسطر المتابعة في الدردشة إلا عند الحاجة لموافقة بشرية على مخرجات خطوة محددة.
|
||||
|
||||
## `Flow` المحادثاتي
|
||||
## `Flow` المحادثاتي (تجريبي)
|
||||
|
||||
اشترك في رسم الدردشة المحادثاتي بتعيين `conversational = True` على صنف فرعي من `Flow` أو بتطبيق `@ConversationConfig(...)`. يوفر `Flow` الأساسي عندئذٍ `route_conversation` كنقطة البدء/الموجّه المدمجة، إضافة إلى مستمعي `converse_turn` و`end_conversation`. يظل المستمع المهمل `answer_from_history_turn` متاحاً للتوافق. يدير الإطار `state.messages`، ويمكنه تشغيل LLM للموجّه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب **المسارات المخصصة**؛ والإطار يتولى الباقي.
|
||||
<Warning>
|
||||
**ميزة تجريبية.** سطح `Flow` المحادثاتي (`conversational = True`،
|
||||
`handle_turn`، `ConversationConfig`، `RouterConfig`،
|
||||
`ConversationState`، الرسم البياني المدمج والمساعدات) يقع تحت
|
||||
`crewai.experimental` وقد يتغير شكله قبل التخرج. ثبّت إصدار CrewAI إذا
|
||||
كنت تعتمد على سلوك محدد، وراقب changelog للتحديثات الكاسرة. الملاحظات
|
||||
والمشاكل مرحب بها.
|
||||
</Warning>
|
||||
|
||||
فعّل الرسم المحادثاتي بتعيين `conversational = True` على صنف فرعي من `Flow`. عندئذٍ يُظهر `Flow` الأساسي رسم `@start` / `@router` / `converse_turn` / `end_conversation` مدمجاً، ويدير `state.messages`، ويُشغّل LLM التوجيه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب **المسارات المخصصة** فقط؛ والإطار يتولى الباقي.
|
||||
|
||||
استخدمه عندما تريد دردشة متعددة الجولات مع موجّه قائم على LLM ومعالجات لكل مسار دون توصيل دورة الحياة يدوياً. استخدم `Flow[ChatState]` (النمط الأدنى مستوى في الأعلى) عندما تحتاج تحكماً كاملاً.
|
||||
|
||||
### مثال سريع
|
||||
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai import LLM, Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
RouterConfig,
|
||||
)
|
||||
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
ROUTER_LLM = LLM(model="gpt-4o-mini")
|
||||
|
||||
|
||||
@ConversationConfig(
|
||||
system_prompt="A multi-agent assistant for ordinary chat and tool-backed tasks.",
|
||||
llm=ROUTER_LLM,
|
||||
router=RouterConfig(), # المسارات + الأوصاف تُكتشف تلقائياً من معالجات @listen
|
||||
)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
def route_turn(self, context: dict) -> str | None:
|
||||
message = (self.state.current_user_message or "").lower()
|
||||
if "search" in message or "news" in message:
|
||||
return "INTERNET_SEARCH"
|
||||
if "docs" in message or "crewai" in message:
|
||||
return "CREWAI_DOCS"
|
||||
return "converse"
|
||||
conversational = True
|
||||
|
||||
@listen("INTERNET_SEARCH")
|
||||
def handle_internet_search(self) -> str:
|
||||
"""Fresh web research, current news, real-time lookups."""
|
||||
reply = "I would run the web research route here."
|
||||
...
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
@listen("CREWAI_DOCS")
|
||||
def handle_crewai_docs(self) -> str:
|
||||
"""Look up the CrewAI documentation for framework/API questions."""
|
||||
reply = "I would look up the CrewAI docs here."
|
||||
...
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
|
||||
flow = SupportFlow()
|
||||
try:
|
||||
flow.handle_turn("What can you do?") # routes to converse
|
||||
flow.handle_turn("Search the web for AI news.") # routes to INTERNET_SEARCH
|
||||
flow.handle_turn("Check the CrewAI docs.") # routes to CREWAI_DOCS
|
||||
flow.handle_turn("ماذا يمكنك أن تفعل؟") # يوجَّه إلى converse (مدمج)
|
||||
flow.handle_turn("ابحث في الويب عن أخبار الذكاء الاصطناعي.") # يوجَّه إلى INTERNET_SEARCH
|
||||
flow.handle_turn("لخص النتيجة الأولى.") # يعود إلى converse
|
||||
finally:
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
@@ -297,54 +297,27 @@ def kickoff() -> None:
|
||||
|-------|-----------|-------|
|
||||
| `system_prompt` | `slices.conversational_system_prompt` من i18n | رسالة system يستخدمها `converse_turn` المدمج. مرر `""` للتعطيل التام. |
|
||||
| `llm` | `None` | LLM المحادثة (يستخدمه `converse_turn` وكاحتياطي للموجّه). |
|
||||
| `router` | `None` | تجاوزات `RouterConfig` اختيارية. مع وجود مستمعين مخصصين وLLM قابل للحل، يُفعّل التوجيه تلقائياً حتى عند إغفال هذا الحقل. |
|
||||
| `answer_from_history_prompt` | افتراضي الإطار | **مهمل.** استخدم system prompt الخاص بـ `converse` أو تجاوز `converse_turn()`. |
|
||||
| `answer_from_history_llm` | `None` | **مهمل.** استخدم `llm`؛ إذ يتلقى `converse` السجل القانوني بالفعل. |
|
||||
| `router` | `None` | `RouterConfig` للتوجيه عبر LLM. بدونه، يسقط الـ flow دائماً إلى `converse`. |
|
||||
| `answer_from_history_prompt` | افتراضي الإطار | رسالة system للمسار الاختياري `answer_from_history`. |
|
||||
| `answer_from_history_llm` | `None` | يُفعّل الاختصار `answer_from_history` عند تعيينه. |
|
||||
| `intent_llm` | `None` | LLM لمسار التصنيف المسبق القديم `intents=`/`default_intents`. |
|
||||
| `default_intents` | `None` | تسميات النتائج للتصنيف المسبق القديم. |
|
||||
| `visible_agent_outputs` | `None` | `"all"` أو قائمة بأسماء الـ agents الذين تُرفع مخرجاتهم من `append_agent_result()` إلى رسائل عامة. |
|
||||
| `defer_trace_finalization` | `True` | يبقي دفعة trace واحدة مفتوحة عبر استدعاءات `handle_turn()`. |
|
||||
|
||||
<Warning>
|
||||
تم إهمال `answer_from_history_prompt` و`answer_from_history_llm` ومسار
|
||||
`answer_from_history`، وستُزال في إصدار مستقبلي. فهي تكرر `converse`، الذي
|
||||
يتولى بالفعل السجل القانوني، وتضيف استدعاء LLM للتحقق من أهلية الإجابة،
|
||||
ويجري تجاوزها عندما يعيد الموجّه التلقائي المعتاد مساراً. تظل الإعدادات
|
||||
الحالية تعمل وتُصدر `DeprecationWarning`.
|
||||
</Warning>
|
||||
|
||||
عند عدم وجود مسارات مخصصة، تسقط الجولات إلى `converse`. ومع وجود مسارات مخصصة وLLM للمحادثة/الموجّه، ينشئ الإطار `RouterConfig` افتراضية؛ لا توفر واحدة صراحةً إلا لتخصيص المطالبة أو قائمة المسارات أو الأوصاف أو سلوك fallback. أما ضبط `default_intents` فيستخدم مسار التصنيف المسبق القديم.
|
||||
|
||||
إذا لم يُهيأ LLM للمحادثة، يعيد `converse_turn` المدمج عنصراً نائباً للإعداد بدلاً من توليد إجابة.
|
||||
|
||||
### `RouterConfig` وفهرس المسارات المُولَّد تلقائياً
|
||||
|
||||
```python
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai import LLM
|
||||
from crewai.flow import RouterConfig
|
||||
|
||||
|
||||
class MyRoute(BaseModel):
|
||||
intent: Literal["INTERNET_SEARCH", "CREWAI_DOCS", "converse"]
|
||||
|
||||
|
||||
ROUTER_LLM = LLM(model="gpt-4o-mini")
|
||||
|
||||
|
||||
router_config = RouterConfig(
|
||||
prompt="Optional domain framing (policy, voice, persona).",
|
||||
response_format=MyRoute, # optional; auto-generated otherwise
|
||||
llm=ROUTER_LLM, # falls back to ConversationConfig.llm
|
||||
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # optional; inferred from listeners
|
||||
RouterConfig(
|
||||
prompt="تأطير اختياري للنطاق (سياسة، صوت، شخصية).",
|
||||
response_format=MyRoute, # اختياري؛ يُولَّد تلقائياً عند الإغفال
|
||||
llm=ROUTER_LLM, # يسقط إلى ConversationConfig.llm
|
||||
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # اختياري؛ يُستنتج من المستمعين
|
||||
route_descriptions={
|
||||
"INTERNET_SEARCH": "Override the docstring for this one route.",
|
||||
"INTERNET_SEARCH": "تجاوز الـ docstring لهذا المسار فقط.",
|
||||
},
|
||||
default_intent="converse", # used when LLM call fails or no LLM available
|
||||
fallback_intent="converse", # used when LLM returns an invalid route
|
||||
default_intent="converse", # يُستخدم عند فشل LLM أو غيابه
|
||||
fallback_intent="converse", # يُستخدم عندما يعيد LLM مساراً غير صالح
|
||||
intent_field="intent",
|
||||
)
|
||||
```
|
||||
@@ -352,17 +325,13 @@ router_config = RouterConfig(
|
||||
تُبنى رسالة الموجّه إلى LLM تلقائياً. لكل مسار يختار الإطار وصفاً بهذا الترتيب من الأولوية:
|
||||
|
||||
1. `RouterConfig.route_descriptions[label]` — تجاوز صريح.
|
||||
2. `Flow.builtin_route_descriptions[label]` — نص جاهز من الإطار لـ `converse` و`end` ولمسار التوافق المهمل `answer_from_history` (مصاغ لـ LLM التوجيه).
|
||||
3. قيمة `description` المعلنة للطريقة (تستخدمها التدفقات التعريفية وإسقاطات DSL).
|
||||
4. أول سطر غير فارغ من docstring معالج `@listen(label)`.
|
||||
5. فارغ (المسار يظهر في الفهرس بلا وصف).
|
||||
2. `Flow.builtin_route_descriptions[label]` — نص جاهز من الإطار لـ `converse` و`end` و`answer_from_history` (مصاغ لـ LLM التوجيه).
|
||||
3. أول سطر غير فارغ من docstring معالج `@listen(label)`.
|
||||
4. فارغ (المسار يظهر في الفهرس بلا وصف).
|
||||
|
||||
عملياً، **إضافة مسار جديد = `@listen("X")` + docstring من سطر واحد**:
|
||||
|
||||
```python
|
||||
from crewai.flow import listen
|
||||
|
||||
|
||||
@listen("INTERNET_SEARCH")
|
||||
def handle_internet_search(self) -> str:
|
||||
"""Fresh web research, current news, real-time lookups."""
|
||||
@@ -381,34 +350,13 @@ Routes:
|
||||
|
||||
`RouterConfig.prompt` مخصص لـ **تأطير النطاق** (شخصية المساعد، قواعد العمل، النبرة). فهرس المسارات يُبنى تلقائياً — لا تُدرج المسارات في `prompt`؛ سيختل التزامن لحظة إضافة معالج جديد.
|
||||
|
||||
### تسمية المعالجات
|
||||
|
||||
السلسلة النصية في `@listen("…")` هي **تسمية مسار للموجّه** (اسم حدث)، وليست اسم طريقة Python. تتشارك تسميات المسارات وأحداث اكتمال الطرق مساحة مشغلات واحدة، ولذلك تؤدي تسمية المعالج باسم مساره نفسه إلى إعادة تشغيل المعالج في حلقة.
|
||||
|
||||
استخدم اسماً مختلفاً للطريقة — تستخدم أمثلة التوثيق بادئة `handle_*`:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def handle_create_video(self) -> str:
|
||||
"""User wants a new video."""
|
||||
...
|
||||
```
|
||||
|
||||
لا تكرر تسمية المسار في اسم الطريقة:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def create_video(self) -> str: # rejected at flow instantiation
|
||||
...
|
||||
```
|
||||
|
||||
### المسارات المدمجة
|
||||
|
||||
| المسار | المعالج | الغرض |
|
||||
|--------|---------|-------|
|
||||
| `converse` | `converse_turn` | معالج الدردشة الافتراضي. يستدعي `ConversationConfig.llm` بـ system prompt + التاريخ القانوني للرسائل. |
|
||||
| `end` | `end_conversation` | يضبط `state.ended = True` ويُصدر رد إنهاء. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | **مسار توافق مهمل.** استخدم `converse`، الذي يتلقى السجل القانوني بالفعل. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | اختياري. يُوجَّه إليه عندما يكون `ConversationConfig.answer_from_history_llm` مُعيَّناً ويمكن الإجابة على الرسالة من التاريخ فقط. |
|
||||
|
||||
يمكنك تجاوز أي من هذه بتعريف معالج بنفس الاسم في الصنف الفرعي.
|
||||
|
||||
@@ -418,9 +366,9 @@ def create_video(self) -> str: # rejected at flow instantiation
|
||||
|
||||
1. يعيد ضبط تعقّب التنفيذ لكل جولة (`_completed_methods`, `_method_outputs`) ليُعاد تشغيل الرسم — بدون ذلك، استدعاءات `kickoff` المتكررة على نفس النسخة ستُحدث دائرة قصر من الجولة الثانية لأن `Flow.kickoff_async` يعتبر `inputs={"id": ...}` استعادة من نقطة تفتيش.
|
||||
2. يُلحق رسالة المستخدم بـ `state.messages` ويضبط `current_user_message` / `last_user_message`. يُحافَظ على `last_intent` **من الجولة السابقة** كي يستخدمها LLM التوجيه كإشارة.
|
||||
3. يُشغّل طرق `@start` التي يعرّفها المستخدم (إن وجدت)، ثم `route_conversation` كنقطة البدء/الموجّه المدمجة، ثم معالج `@listen` المختار. وتستدعي `route_conversation` المساعد القابل للتجاوز `conversation_start()`.
|
||||
3. يُشغّل `conversation_start` → `route_conversation` → معالج `@listen` المختار.
|
||||
4. يخزّن الموجّه قراره في `state.last_intent` (يكون مرئياً لسياق التوجيه في الجولة التالية).
|
||||
5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ `append_assistant_message`، فإن `handle_turn` يُلحقها نيابةً عنك ويحفظ `state.messages` المحدَّث حتى تشمل استعادة `@persist` جولة المساعد.
|
||||
5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ `append_assistant_message`، فإن `handle_turn` يُلحقها نيابةً عنك.
|
||||
|
||||
استدعِ `handle_turn()` لرسائل الدردشة. استدعاء `kickoff(inputs={"id": ...})` مباشرةً يشغل الرسم بدون غلاف الجولة المحادثية.
|
||||
|
||||
@@ -441,8 +389,6 @@ flow.chat()
|
||||
4. يطبع نتيجة المساعد.
|
||||
5. ينهي traces الجلسة المؤجلة داخل كتلة `finally`.
|
||||
|
||||
يُفعّل `chat(defer_trace_finalization=True)` مؤقتاً علم التأجيل على مستوى المثيل للـ REPL، ثم يعيد قيمته السابقة عند الخروج.
|
||||
|
||||
خصص سلوك الطرفية عبر I/O قابل للحقن:
|
||||
|
||||
```python
|
||||
@@ -461,12 +407,6 @@ flow.chat(
|
||||
لتشغيل آثار جانبية (إعداد ناقل أحداث، قياس عن بُعد) في كل قرار توجيه، تجاوز `route_turn`:
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationState
|
||||
|
||||
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
@@ -475,7 +415,7 @@ class SupportFlow(Flow[ConversationState]):
|
||||
return super().route_turn(context)
|
||||
```
|
||||
|
||||
لتجاوز موجّه LLM بالكامل واختيار مسار برمجياً، أعد سلسلة نصية غير فارغة من `route_turn`. لا يؤدي إرجاع قيمة falsy من التجاوز إلى استدعاء `_route_with_config()`؛ بل يسقط التوجيه إلى النية المصنّفة مسبقاً لهذه الجولة، ثم إلى مسار التوافق المهمل `answer_from_history` عند إعداده، وأخيراً إلى `converse`. تكون `last_intent` من الجولة السابقة متاحة في سياق الموجّه، لكنها لا تُعاد أبداً كـ fallback.
|
||||
لتجاوز موجّه LLM واختيار مسار برمجياً، أعد سلسلة نصية من `route_turn`؛ إعادة `None` تسقط إلى `_route_with_config(...)`.
|
||||
|
||||
### `append_assistant_message` و`append_agent_result`
|
||||
|
||||
@@ -486,76 +426,9 @@ class SupportFlow(Flow[ConversationState]):
|
||||
|
||||
يمكن لـ `ConversationConfig.visible_agent_outputs` رفع النتائج الخاصة لـ agents محددين إلى عامة عالمياً (`"all"` أو قائمة بالأسماء).
|
||||
|
||||
## تعريف تدفق محادثاتي بصيغة JSON/YAML
|
||||
|
||||
يمكن لـ [التدفق التعريفي](/edge/ar/concepts/cli) أن يكون محادثاتيًا أيضًا. أضف كتلة `conversational` في المستوى الأعلى وعرّف مساراتك الخاصة كطرق تستمع (`listen`) إلى تسمية مسار:
|
||||
|
||||
```yaml
|
||||
schema: crewai.flow/v1
|
||||
name: SupportFlow
|
||||
|
||||
conversational:
|
||||
system_prompt: You are a terse support assistant.
|
||||
llm: gpt-4o-mini
|
||||
router:
|
||||
llm: gpt-4o-mini
|
||||
|
||||
methods:
|
||||
handle_order:
|
||||
description: Order status, shipping and delivery questions.
|
||||
listen: order
|
||||
do:
|
||||
call: agent
|
||||
with:
|
||||
role: Support specialist
|
||||
goal: Answer order questions accurately
|
||||
backstory: Knows the fulfilment pipeline.
|
||||
input: "${state.current_user_message}"
|
||||
```
|
||||
|
||||
تعريف الكتلة هو الاشتراك نفسه — القيمة الافتراضية لـ `enabled` هي `true`. اضبطها على `enabled: false` للاحتفاظ بالإعدادات مع إيقاف المحادثة. يؤدي ذلك أيضاً إلى تعطيل إنشاء الطرق المدمجة، ولذلك يجب أن توفر التعريفة رسماً عادياً غير محادثاتي.
|
||||
|
||||
تُوفَّر لك ثلاثة أشياء:
|
||||
|
||||
| المُوفَّر | التفاصيل |
|
||||
|----------|--------|
|
||||
| الرسم البياني المدمج | تُضاف `route_conversation` و`converse_turn` و`end_conversation` تلقائيًا. يُحتفظ بـ `answer_from_history_turn` المهملة للتوافق. عرّف طريقة بأحد هذه الأسماء لتجاوزها. |
|
||||
| حالة المحادثة | تُستخدم `ConversationState` عند عدم وجود كتلة `state`. وتُركّب حالة Pydantic ذات `ref` أو `json_schema` تلقائياً مع الحقول المحادثية؛ ولا يلزم أن ترث من `ConversationState`. |
|
||||
| كتالوج المسارات | يُستنتج من الطرق غير الموجّهة التي تحمل تسميات `listen`، مع استبعاد المسارات الداخلية. تتبع الأوصاف ترتيب الأولوية أعلاه، ويمكن لـ `router.routes` الصريحة تقييد الخيارات. |
|
||||
|
||||
تقبل حقول `llm` و`router.llm` و`intent_llm` التعريفية إما معرّف نموذج أو خريطة إعدادات مثل `{model: openai/gpt-4o-mini, max_tokens: 512}`. وتدعم كتلة `conversational` أيضاً `default_intents` و`visible_agent_outputs` و`defer_trace_finalization` وحقول `RouterConfig` الموضحة أعلاه. تظل تعريفات `answer_from_history_prompt` / `answer_from_history_llm` المهملة مقبولة للتوافق.
|
||||
|
||||
شغّله من Python بنفس واجهات الجولة المستخدمة مع تدفق محادثاتي معرّف بصنف:
|
||||
|
||||
```python
|
||||
from crewai.flow import Flow
|
||||
|
||||
flow = Flow.from_declaration(path="flow.yaml")
|
||||
|
||||
try:
|
||||
flow.handle_turn("Where is my order?", session_id="session-1")
|
||||
finally:
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
|
||||
### تسمية المسارات
|
||||
|
||||
تتشارك تسميات المسارات وأسماء الطرق مساحة اسم واحدة للمشغّلات، لذا يجب ألا يحمل المعالج اسم المسار الذي يستمع إليه — يُرفض `create_video` الذي يستمع إلى `create_video` عند بناء التدفق. استخدم بادئة `handle_*`.
|
||||
|
||||
### ما لا يمكن للتعريفة التعبير عنه
|
||||
|
||||
| غير قابل للتعبير | استخدم بدلًا منه |
|
||||
|-----------------|-------------|
|
||||
| مثيل `LLM` حي أو `BaseLLM` مخصص | سلسلة معرّف نموذج أو خريطة إعدادات ثابتة |
|
||||
| `router.response_format` كصنف نموذج حيّ | سمِّ الصنف بمرجع python: `response_format: {python: my_project.schemas.ConversationRoute}`. احذفه ويولّد الإطار واحدًا |
|
||||
| تجاوز `route_turn()` | اكتب Flow بلغة Python، أو استبدل طريقة `route_conversation` التعريفية بإجراء `call: code` / expression |
|
||||
| تجاوز `can_answer_from_history()` | مهمل. استخدم `converse` أو تجاوز `converse_turn()` في Python. |
|
||||
|
||||
يفتح `crewai run` واجهة المحادثة النصية للتدفق المحادثاتي التعريفي — نفس الواجهة التي يحصل عليها Flow محادثاتي مكتوب بلغة Python. تحتاج حلقة المحادثة إلى طرفية، ولذلك يخرج التشغيل بدون طرفية برمز غير صفري مع إرشادات بدلاً من تنفيذ جولة واحدة؛ شغّله من Python هناك عبر `handle_turn()` أو `stream_turn()`. وتعمل الطريقة التعريفية ذات كتلة `human_feedback:` (وفي Python: `@human_feedback`) على REPL طرفي، لأن runtime يجمع الملاحظات بمطالبة حاجزة لا تستطيع TUI خدمتها. لا يُقبل `--inputs` مع Flow محادثاتي — فمدخل كل جولة هو الرسالة التي تكتبها — واستئناف جلسة حسب المعرّف غير موصول بواجهة CLI بعد؛ استخدم `flow.handle_turn(message, session_id=...)` من Python لذلك.
|
||||
|
||||
## التتبع عبر الجولات
|
||||
|
||||
مع `defer_trace_finalization=True` (افتراضي في `ConversationConfig`):
|
||||
مع `defer_trace_finalization=True` (افتراضي في `ConversationalConfig`):
|
||||
|
||||
- **دفعة trace واحدة** لجلسة الدردشة.
|
||||
- **`flow_started`** في الجولة الأولى فقط؛ **`flow_finished`** مرة في `finalize_session_traces()`.
|
||||
@@ -566,30 +439,17 @@ finally:
|
||||
flow.chat(session_id=session_id)
|
||||
```
|
||||
|
||||
`flow.chat()` يستدعي `finalize_session_traces()` نيابةً عنك. عندما تملك الحلقة عبر `handle_turn()`، استدعِ `finalize_session_traces()` عند انتهاء الجلسة.
|
||||
`flow.chat()` يستدعي `finalize_session_traces()` نيابةً عنك. عندما تملك الحلقة عبر `handle_turn()` أو `kickoff(...)`، استدعِ `finalize_session_traces()` عند انتهاء الجلسة.
|
||||
|
||||
يخفي `suppress_flow_events=True` لوحات Rich ويمنع أحداث تنفيذ الطرق. وتظل أحداث بدء/انتهاء Flow تصدر، فيبقى بالإمكان تتبع دورة حياة Flow الخارجية، بينما تُحذف spans الطرق الفردية.
|
||||
`suppress_flow_events=True` يخفي لوحات Rich فقط؛ أحداث trace والـ methods تُصدر.
|
||||
|
||||
### دورة حياة trace لـ `Flow` المحادثاتي
|
||||
|
||||
يستخدم [`Flow` المحادثاتي](#flow-المحادثاتي) دورة حياة التتبع نفسها: القيمة الافتراضية لـ `defer_trace_finalization` هي `True`، ولذلك يبقي كل `handle_turn()` trace الجلسة مفتوحاً. تمنع الجولات المؤجلة أيضاً إصدار `flow_failed` لكل جولة؛ وعند حدوث خطأ في جولة أو إلغاء الجلسة، أنهِ الجلسة صراحةً. يغلق ذلك الدفعة بحدث `FlowFinished` على مستوى الجلسة بدلاً من حدث `FlowFailed` لكل جولة. لُف REPL/الحلقة دائماً بـ `try/finally` واستدعِ `flow.finalize_session_traces()` عند الخروج. بدون ذلك، تبقى دفعة trace مفتوحة وقد لا تُصدَّر المحادثة النهائية أبداً.
|
||||
يستخدم [`Flow` المحادثاتي](#flow-المحادثاتي-تجريبي) التجريبي نفس دورة حياة tracing: `defer_trace_finalization` افتراضياً `True`، فيبقي كل `handle_turn()` أثر الجلسة مفتوحاً. أنهِ دوماً عند نهاية الجلسة — لُف حلقتك بـ `try/finally` واستدعِ `flow.finalize_session_traces()` عند الخروج. بدون ذلك، تبقى الدفعة مفتوحة وقد لا تُصدَّر آخر محادثة أبداً.
|
||||
|
||||
## البث
|
||||
|
||||
استخدم `stream_turn()` للواجهات المحادثية، وكرّر عبر كائنات `StreamFrame` المرتبة التي يعيدها:
|
||||
|
||||
```python
|
||||
stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
reply = stream.result
|
||||
```
|
||||
|
||||
بالنسبة إلى Flow غير محادثاتي، يؤدي ضبط `stream = True` إلى جعل `kickoff()` يعيد `StreamSession`. لا تضبط `flow.stream = True` عند استخدام `handle_turn()`؛ إذ تملك `stream_turn()` دورة حياة البث المحادثاتي.
|
||||
اضبط `stream = True` على صنف `Flow`. عندئذٍ يُصدر `kickoff(...)` أحداث `assistant_delta` (وما يرتبط بها) عبر ناقل الأحداث القياسي.
|
||||
|
||||
## الاستيراد
|
||||
|
||||
@@ -604,15 +464,10 @@ from crewai.flow import (
|
||||
router,
|
||||
start,
|
||||
)
|
||||
from crewai.flow.conversation import prepare_conversational_turn
|
||||
from crewai.flow import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
RouterConfig,
|
||||
)
|
||||
```
|
||||
|
||||
## مراجع
|
||||
|
||||
- [إتقان إدارة حالة Flow](/ar/guides/flows/mastering-flow-state)
|
||||
- [أنشئ أول Flow](/ar/guides/flows/first-flow)
|
||||
- Demo: `lib/crewai/runner_conversational_flow_simple.py` — REPL بسيط مع `RESEARCH` ووكيل Exa
|
||||
|
||||
@@ -104,7 +104,7 @@ crewai flow add-crew content-crew
|
||||
}
|
||||
```
|
||||
|
||||
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `gemini/gemini-3.7-flash` أو `anthropic/claude-sonnet-4-6`.
|
||||
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `gemini/gemini-2.0-flash-001` أو `anthropic/claude-sonnet-4-6`.
|
||||
|
||||
3. أنشئ `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
|
||||
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
title: القنوات
|
||||
description: شغّل نفس وكيل CrewAI كروبوت على Slack أو Teams باستخدام CopilotKit Channels SDK ومنصة Intelligence المُدارة.
|
||||
icon: messages
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## قابل مستخدميك حيث هم بالفعل
|
||||
|
||||
وكيل CrewAI الذي بنيته في [النظرة العامة](/edge/ar/guides/frontend/overview) لا يجب أن يعيش خلف تطبيق ويب فقط. يمكن لنفس الـ Crew أو الـ Flow أن يعمل كروبوت داخل منصة مراسلة. لا حاجة لإعادة البناء ولا لنسخة ثانية من منطق وكيلك: يبقى الوكيل مكشوفًا عبر [بروتوكول AG-UI](https://docs.ag-ui.com)، وتقوم **قناة** بتشغيله من Slack أو Microsoft Teams.
|
||||
|
||||
يوفّر [Channels SDK](https://docs.copilotkit.ai/slack) من CopilotKit تلك القناة. تُعرّف `createChannel` في وقت تشغيل صغير، وتوجّهه إلى وكيل CrewAI الخاص بك، وتتولى منصة **Intelligence** المُدارة من CopilotKit التوسّط في الاتصال مع مزوّد المراسلة.
|
||||
|
||||
<Note>
|
||||
على خلاف بقية هذا القسم، فإن Channels **ليست ذاتية الاستضافة**. تعمل من خلال **CopilotKit Intelligence** — وهي سطح مطلوب لـ Channels، بحكم التصميم (تتوفر طبقة مجانية). تحتفظ Intelligence باتصال المنصة وبيانات الاعتماد، وتستقبل كل حدث من المنصة، وتسلّم الدور إلى عملية قناتك؛ تشغّل عمليتك الوكيل وتبثّ الرد مرة أخرى. تقوم بإعداد Slack مرة واحدة في لوحة تحكم Intelligence، ولا تدخل بيانات اعتماد المنصة عمليتك أبدًا. يبقى وكيلك وأدواتك وحالتك ملكًا لك.
|
||||
</Note>
|
||||
|
||||
## كيف تتكامل الأجزاء معًا
|
||||
|
||||
لا يتغير أي شيء بخصوص خادم وكيل CrewAI الخاص بك. يستمر في تقديم الـ Crew أو الـ Flow عبر AG-UI تمامًا كما في النظرة العامة. ما تضيفه هو عملية Node منفصلة طويلة الأمد مبنية باستخدام `@copilotkit/channels`: تسجّل قناة على `CopilotRuntime`، وتتصل بـ Intelligence، وتشغّل وكيلك كلما وصلت رسالة.
|
||||
|
||||
```
|
||||
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
تحتفظ عملية القناة باتصال دائم مع بوابة Intelligence، لذا فهي تحتاج إلى مضيف طويل الأمد — لا يمكن لمعالج طلبات بلا خادم (serverless) أن يملك ذلك الاتصال. يمكن لخادم CrewAI الخاص بك أن يستمر في تقديم واجهة الويب الأمامية من النظرة العامة في الوقت نفسه: تطبيق الويب والقناة ما هما إلا عميلان لنقطة نهاية AG-UI واحدة.
|
||||
|
||||
## دليل التكامل
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="ثبّت حزم Channels">
|
||||
|
||||
يأتي Channels SDK مكتمل العناصر — كل منصة تُشحن في الحزمة الواحدة، بلا محوّل خاص بكل منصة لتثبيته. أضفه إلى جانب وقت التشغيل الذي يستضيف القناة وعميل CrewAI AG-UI:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="أنشئ قناة في Intelligence">
|
||||
|
||||
في [لوحة تحكم CopilotKit](https://docs.copilotkit.ai/slack)، أنشئ قناة واربط Slack — ترشدك Intelligence خلال إنشاء تطبيق Slack وتحتفظ ببيانات اعتماده. يترك ذلك متغيّري بيئة لعمليتك، كلاهما من لوحة التحكم:
|
||||
|
||||
```bash
|
||||
export INTELLIGENCE_API_KEY=... # authenticates the runtime with Intelligence (free tier available)
|
||||
export INTELLIGENCE_CHANNEL_ID=... # the Channel ID, matched by createChannel({ name })
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="عرّف القناة">
|
||||
|
||||
تُعرّف `createChannel` القناة وتربط وكيلك بها. ابنِ الوكيل كمصنع لكل خيط (thread) بحيث تحصل كل محادثة على جلستها الخاصة، مستخدمًا نفس `CrewAIAgent` الذي تستخدمه النظرة العامة في وقت تشغيل الويب، موجّهًا إلى نقطة نهاية AG-UI الخاصة بك. تتيح `identifyUser: "platform"` لـ Intelligence ربط كل مستخدم من المنصة بهوية ثابتة.
|
||||
|
||||
```ts
|
||||
// channel.ts
|
||||
import { createChannel } from "@copilotkit/channels";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
|
||||
const channel = createChannel({
|
||||
name: process.env.INTELLIGENCE_CHANNEL_ID!, // must match the Channel ID in Intelligence
|
||||
identifyUser: "platform",
|
||||
// A fresh agent per conversation, pointed at your CrewAI AG-UI endpoint.
|
||||
agent: (threadId) => {
|
||||
const agent = new CrewAIAgent({ url: "http://localhost:8000/recipe" });
|
||||
agent.threadId = threadId;
|
||||
return agent;
|
||||
},
|
||||
});
|
||||
|
||||
// A mention subscribes the thread and runs the agent; afterwards every message
|
||||
// in a subscribed thread runs it without needing another mention.
|
||||
channel.onMention(async ({ thread }) => {
|
||||
await thread.subscribe();
|
||||
await thread.runAgent();
|
||||
});
|
||||
|
||||
channel.onMessage(async ({ thread }) => {
|
||||
if (await thread.isSubscribed()) await thread.runAgent();
|
||||
});
|
||||
|
||||
export { channel };
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="سجّل القناة على وقت التشغيل">
|
||||
|
||||
أنشئ `CopilotRuntime` مع بوابة Intelligence وقناتك، ثم قدّمه باستخدام `createCopilotNodeListener`. تبقى خريطة `agents` فارغة — القناة توفّر وكيلها الخاص. انتظر حتى تكون القناة جاهزة كي يفشل بدء التشغيل بصوت عالٍ عند وجود إعداد معطوب.
|
||||
|
||||
```ts
|
||||
// server.ts
|
||||
import { createServer } from "node:http";
|
||||
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
|
||||
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
|
||||
import { channel } from "./channel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {}, // the channel supplies its own agent; no web-facing agents needed
|
||||
intelligence: new CopilotKitIntelligence({
|
||||
apiKey: process.env.INTELLIGENCE_API_KEY!, // free tier available
|
||||
}),
|
||||
channels: [channel],
|
||||
});
|
||||
|
||||
const listener = createCopilotNodeListener({ runtime });
|
||||
await listener.channels?.ready({ timeoutMs: 15_000 });
|
||||
|
||||
createServer(listener).listen(3123, () => {
|
||||
console.log("Channels runtime listening on port 3123");
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="شغّل وقت تشغيل القناة">
|
||||
|
||||
ابدأه إلى جانب خادم وكيل CrewAI الخاص بك:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1 — CrewAI agent server
|
||||
npx tsx server.ts # terminal 2 — Channels runtime
|
||||
```
|
||||
|
||||
اذكر الروبوت في Slack أو Teams فيشغّل الـ Crew أو الـ Flow الخاص بك، ويبثّ الرد مرة أخرى داخل الخيط. يبقى الخيط مشتركًا، لذا تعمل رسائل المتابعة دون الحاجة إلى ذكر آخر.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## نموذج الأحداث
|
||||
|
||||
تتفاعل القناة مع أحداث المنصة عبر معالِجات، ويستقبل كل معالِج خيطًا (`thread`) تديره بعدد قليل من الدوال:
|
||||
|
||||
- **`channel.onMention`** يُطلَق عندما يذكر مستخدم الروبوت بـ @. استدعِ `thread.subscribe()` للانضمام إلى الخيط، ثم `thread.runAgent()` لتشغيل وكيل CrewAI الخاص بك عند الذكر.
|
||||
- **`channel.onMessage`** يُطلَق عند كل رسالة في خيط يمكن للروبوت رؤيته. قيّده بـ `thread.isSubscribed()` كي لا يستجيب الوكيل إلا حيث انضمّ، ثم `thread.runAgent()`.
|
||||
- **`thread.runAgent()`** يشغّل وكيل CrewAI المرفق للدور الحالي ويبثّ مخرجاته مرة أخرى داخل القناة. مرّر `{ prompt }` لتجاوز النص الذي يعمل عليه الوكيل.
|
||||
|
||||
يستقبل وكيلك `RunAgentInput` عاديًا من AG-UI ويصدر أحداث AG-UI عادية؛ تبقى آليات المنصة خلف القناة، لذا يعمل نفس الـ Crew أو الـ Flow دون تغيير عبر كل منصة. تكشف القناة أيضًا معالِجات للترحيبات والمقاطعات والأوامر والتفاعلات والنوافذ (modals) — راجع [مرجع `Channel`](https://docs.copilotkit.ai/reference/channels/classes/Channel) للاطلاع على السطح الكامل.
|
||||
|
||||
## دعم المنصات
|
||||
|
||||
يغطي مسار Intelligence المُدار **Slack** و**Microsoft Teams** اليوم — يعمل نفس كود القناة على أيٍّ منهما، وتفيد `message.platform` / `thread.platform` بالأصل الأصلي. تُبلَغ المنصات الأخرى (Discord وTelegram وWhatsApp) عبر **محوّلات مباشرة** يشغّلها المطوّر بدلًا من المسار المُدار — تملك عمليتك الخاصة بيانات اعتماد المنصة والنقل. راجع [توثيق CopilotKit Channels](https://docs.copilotkit.ai/slack) للاطلاع على قائمة المنصات الحالية والإعداد الخاص بكل منصة.
|
||||
|
||||
## ذات صلة
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="النظرة العامة على الواجهة الأمامية" icon="browser" href="/edge/ar/guides/frontend/overview">
|
||||
قدّم الـ Crew أو الـ Flow الخاص بك عبر AG-UI — الأساس الذي تُبنى عليه كل قناة.
|
||||
</Card>
|
||||
<Card title="التدخل البشري (Human-in-the-Loop)" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
أوقف الوكيل مؤقتًا لجمع موافقة المستخدم أو مدخلاته في منتصف التشغيل.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
title: النظرة العامة على الواجهة الأمامية
|
||||
description: ابنِ واجهات مستخدم تفاعلية لوكلاء CrewAI الخاصين بك باستخدام CopilotKit وبروتوكول AG-UI.
|
||||
icon: browser
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## امنح وكلاءك واجهة مستخدم
|
||||
|
||||
يشغّل CrewAI وكلاءك. ويمنحهم [CopilotKit](https://copilotkit.ai) واجهة أمامية. معًا يتيحان لك بناء تطبيقات يحادث فيها المستخدمون Crew أو Flow، ويشاهدونه يعمل في الوقت الفعلي، ويوافقون على قراراته، ويرون مخرجاته معروضة كواجهة حيّة بدلًا من جدران من النص.
|
||||
|
||||
يتصل الاثنان عبر [بروتوكول AG-UI](https://docs.ag-ui.com). تكشف حزمة `ag-ui-crewai` أي Crew أو Flow كنقطة نهاية AG-UI. وتستهلك خطافات (hooks) ومكوّنات React من CopilotKit تلك النقطة. يفتح ذلك تجارب تتجاوز بكثير صندوق المحادثة:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="واجهة المستخدم التوليدية (Generative UI)" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
اعرض استدعاءات أدوات الوكيل وحالته كمكوّنات React خاصة بك.
|
||||
</Card>
|
||||
<Card title="التدخل البشري (Human-in-the-Loop)" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
أوقف الوكيل مؤقتًا لجمع موافقة المستخدم أو مدخلاته في منتصف التشغيل.
|
||||
</Card>
|
||||
<Card title="الحالة المشتركة (Shared State)" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
أبقِ حالة الوكيل وواجهة تطبيقك متزامنتين في الاتجاهين.
|
||||
</Card>
|
||||
<Card title="القنوات (Channels)" icon="messages" href="/edge/ar/guides/frontend/channels">
|
||||
شغّل نفس الوكيل كروبوت على Slack أو Discord أو Teams.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
يجعل هذا الدليل Crew أو Flow يتحدث مع واجهة أمامية بـ Next.js من البداية إلى النهاية. تبني بقية القسم على التطبيق الذي تعدّه هنا.
|
||||
|
||||
## البنية
|
||||
|
||||
هناك ثلاثة أجزاء:
|
||||
|
||||
1. **خادم وكيل CrewAI** — عملية Python تقدّم الـ Crew أو الـ Flow الخاص بك عبر AG-UI (FastAPI + `ag-ui-crewai`).
|
||||
2. **وقت تشغيل CopilotKit** — مسار Next.js يسجّل وكيلك ويوكّل الطلبات إليه.
|
||||
3. **الواجهة الأمامية بـ React** — مزوّد `<CopilotKit>` إلى جانب مكوّنات المحادثة والواجهة التوليدية.
|
||||
|
||||
```
|
||||
React app ──► CopilotKit runtime (/api/copilotkit) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل المسار **الذاتي الاستضافة**: تشغّل خادم وكيل CrewAI بنفسك باستخدام `ag-ui-crewai`، ويعمل محليًا دون أي خدمة مُدارة. يقدّم CopilotKit أيضًا مسارًا **مُدارًا** (CopilotKit Cloud / Enterprise Intelligence) بخيوط مستضافة وأداة فحص — راجع [دليل البدء السريع لـ CopilotKit مع CrewAI](https://docs.copilotkit.ai/crewai-crews/quickstart) إن أردت ذلك بدلًا منه. كود الواجهة الأمامية في هذا القسم هو نفسه في الحالتين؛ الاختلاف فقط في كيفية استضافة الوكيل وتسجيله.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
يعمل CrewAI خلف AG-UI بثلاثة أشكال: الـ **Flows** العادية (المستخدمة في هذه الأدلة)، و**[الـ Flows المحادثية (Conversational Flows)](/edge/en/guides/frontend/conversational-flows)** (أصلية، مدركة للجلسة، قائمة على الأدوار، بتكافؤ كامل في الميزات)، والـ **Crews** (محادثة أساسية). الواجهة الأمامية في هذا القسم متطابقة عبرها جميعًا — الاختلاف فقط في تأليف الخلفية وتسجيلها.
|
||||
</Note>
|
||||
|
||||
## دليل التكامل
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="قدّم وكيلك عبر AG-UI">
|
||||
|
||||
ثبّت حزمة التكامل في مشروع CrewAI الخاص بك:
|
||||
|
||||
```bash
|
||||
pip install ag-ui-crewai
|
||||
```
|
||||
|
||||
اكشف وكيلك من تطبيق FastAPI. تستخدم الـ Flows دالة `add_crewai_flow_fastapi_endpoint`؛ وتستخدم الـ Crews دالة `add_crewai_crew_fastapi_endpoint`. يمكنك تسجيل ما تشاء منها، كلٌّ على مساره الخاص.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Flow
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from my_agents.recipe_flow import RecipeFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=RecipeFlow(),
|
||||
path="/recipe",
|
||||
)
|
||||
```
|
||||
|
||||
```python Crew
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_crew_fastapi_endpoint
|
||||
from my_agents.research_crew import ResearchCrew
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_crew_fastapi_endpoint(
|
||||
app=app,
|
||||
crew=ResearchCrew().crew(),
|
||||
path="/research",
|
||||
)
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
شغّله:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
<Note>
|
||||
اضبط متغيّرات البيئة الخاصة بمزوّد الـ LLM الخاص بك (على سبيل المثال `OPENAI_API_KEY`) قبل بدء الخادم.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="أنشئ تطبيق Next.js">
|
||||
|
||||
إن لم تكن لديك واجهة أمامية بعد، أنشئ هيكلًا:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest my-app
|
||||
cd my-app
|
||||
```
|
||||
|
||||
ثبّت CopilotKit وعميل CrewAI AG-UI:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="أضف وقت تشغيل CopilotKit">
|
||||
|
||||
أنشئ مسارًا يسجّل وكيل (أو وكلاء) CrewAI مع وقت تشغيل CopilotKit. يشير كل وكيل إلى مسار على خادم Python الخاص بك عبر `CrewAIAgent`.
|
||||
|
||||
```ts
|
||||
// app/api/copilotkit/route.ts
|
||||
import {
|
||||
CopilotRuntime,
|
||||
InMemoryAgentRunner,
|
||||
createCopilotEndpoint,
|
||||
} from "@copilotkit/runtime/v2";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
import { handle } from "hono/vercel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {
|
||||
recipe: new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
|
||||
},
|
||||
runner: new InMemoryAgentRunner(),
|
||||
});
|
||||
|
||||
const app = createCopilotEndpoint({
|
||||
runtime,
|
||||
basePath: "/api/copilotkit",
|
||||
});
|
||||
|
||||
const handler = handle(app);
|
||||
export const GET = handler;
|
||||
export const POST = handler;
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="غلّف تطبيقك بالمزوّد">
|
||||
|
||||
وجّه `<CopilotKit>` إلى مسار وقت التشغيل واذكر اسم الوكيل الذي سجّلته.
|
||||
|
||||
```tsx
|
||||
// app/page.tsx
|
||||
"use client";
|
||||
import { CopilotKit } from "@copilotkit/react-core";
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
import "@copilotkit/react-core/v2/styles.css";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="recipe">
|
||||
<YourApp />
|
||||
<CopilotSidebar agentId="recipe" labels={{ modalHeaderTitle: "Assistant" }} />
|
||||
</CopilotKit>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="شغّله">
|
||||
|
||||
ابدأ العمليتين وافتح التطبيق. تشغّل المحادثة في الشريط الجانبي الآن الـ Crew أو الـ Flow الخاص بك.
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1
|
||||
npm run dev # terminal 2
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## خيارات واجهة المحادثة
|
||||
|
||||
يشحن CopilotKit ثلاثة أسطح محادثة قابلة للتبديل. بدّل المكوّن؛ يبقى التوصيل متطابقًا.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```tsx Sidebar
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotSidebar agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Popup
|
||||
import { CopilotPopup } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotPopup agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Inline
|
||||
import { CopilotChat } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotChat agentId="recipe" />
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## إلى أين تذهب بعد ذلك
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="واجهة المستخدم التوليدية (Generative UI)" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
اعرض استدعاءات الأدوات وحالة الوكيل كمكوّنات مخصّصة.
|
||||
</Card>
|
||||
<Card title="إجراءات الواجهة الأمامية (Frontend Actions)" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
|
||||
دع الوكيل يستدعي دوالًا تعمل في المتصفح.
|
||||
</Card>
|
||||
<Card title="التدخل البشري (Human-in-the-Loop)" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
قيّد إجراءات الوكيل خلف موافقة المستخدم.
|
||||
</Card>
|
||||
<Card title="الحالة التنبؤية (Predictive State)" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
ابثّ الحالة قيد التنفيذ إلى الواجهة أثناء عمل الوكيل.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,200 +0,0 @@
|
||||
---
|
||||
title: خطافات حدود التنفيذ
|
||||
description: اعتراض بداية تنفيذ الـ Crew والـ Flow ومدخلاته ومخرجاته ونهايته باستخدام المزخرف @on
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
تعترض خطافات حدود التنفيذ الأطراف الخارجية للتشغيل — قبل بدء أي عمل، وعند
|
||||
حسم المدخلات، وعند جاهزية النتيجة النهائية، وعند انتهاء التنفيذ. وهي تعمل مع
|
||||
الـ Crew والـ Flow على حد سواء، وتُعد المكان المناسب لفحوصات السياسة على
|
||||
مستوى التشغيل وإعادة كتابة المدخلات وتنقية المخرجات.
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
أربع نقاط اعتراض تغطي الحدود:
|
||||
|
||||
| النقطة | التوقيت | `ctx.payload` |
|
||||
|--------|---------|---------------|
|
||||
| `EXECUTION_START` | Crew أو Flow على وشك البدء | `dict` المدخلات |
|
||||
| `INPUT` | المدخلات المحسومة للتنفيذ | `dict` المدخلات |
|
||||
| `OUTPUT` | النتيجة النهائية جاهزة | كائن المخرجات |
|
||||
| `EXECUTION_END` | انتهى التنفيذ (نجاحًا أو فشلًا) | كائن المخرجات، أو `None` عند الفشل |
|
||||
|
||||
بالنسبة إلى الـ Crew، يكون payload المخرجات `CrewOutput`. أما في الـ Flow فهو
|
||||
النتيجة النهائية لدالة الـ Flow.
|
||||
|
||||
## توقيع الخطاف
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def boundary_hook(ctx) -> Any | None:
|
||||
# Mutate ctx.payload in place, or
|
||||
# return a non-None value to replace it, or
|
||||
# raise HookAborted(reason, source) to stop the run
|
||||
return None
|
||||
```
|
||||
|
||||
تتبع خطافات الحدود العقد القياسي: المتابعة (`return None`)، أو التعديل في
|
||||
المكان، أو الاستبدال بإرجاع قيمة، أو الإجهاض برفع `HookAborted`. أي إجهاض
|
||||
عند أي حد ينتشر خارج `kickoff()` مع سببه.
|
||||
|
||||
## مخطط السياق
|
||||
|
||||
تتلقى كل نقطة سياقًا منمّطًا. تشترك جميع السياقات في الحقول الأساسية:
|
||||
|
||||
```python
|
||||
class InterceptionContext:
|
||||
payload: Any # The interceptable value (see table above)
|
||||
agent: Any = None # Not populated at execution boundaries
|
||||
agent_role: str | None # Not populated at execution boundaries
|
||||
task: Any = None # Not populated at execution boundaries
|
||||
crew: Any = None # The Crew instance (crew runs only)
|
||||
flow: Any = None # The Flow instance (flow runs only)
|
||||
```
|
||||
|
||||
تضيف سياقات كل نقطة اسمًا بديلًا للـ payload:
|
||||
|
||||
```python
|
||||
class ExecutionStartContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class InputContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class OutputContext(InterceptionContext):
|
||||
output: Any # The output object
|
||||
|
||||
class ExecutionEndContext(InterceptionContext):
|
||||
output: Any # The output object (None when status == "failed")
|
||||
status: str # "completed" or "failed"
|
||||
error: BaseException | None # The exception when status == "failed"
|
||||
```
|
||||
|
||||
<Note>
|
||||
`ctx.inputs` هو اسم بديل لقاموس المدخلات **الأصلي**، لذا فإن التعديلات في
|
||||
المكان عبر أي من الاسمين متكافئة. إذا *استبدل* خطاف سابق الـ payload بإرجاع
|
||||
dict جديد، فإن `ctx.payload` وحده يُعاد ربطه — اقرأ واكتب دائمًا عبر
|
||||
`ctx.payload` عندما يمكن أن تتسلسل الخطافات.
|
||||
</Note>
|
||||
|
||||
## تشغيلات الـ Crew مقابل تشغيلات الـ Flow
|
||||
|
||||
تعمل خطافات الحدود على كلا وقتي التشغيل، وتنفيذ الـ Crew يجري داخليًا فوق وقت
|
||||
تشغيل Flow. لذلك أثناء `crew.kickoff()` يُطلق الخطاف الحدودي العام لحدّ الـ
|
||||
Crew (`ctx.crew` مضبوط و`ctx.flow` يساوي `None`) **و** للـ Flow الداخلي
|
||||
(`ctx.flow` مضبوط و`ctx.crew` يساوي `None`). ميّز حسب وقت التشغيل:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def crew_output_only(ctx):
|
||||
if ctx.crew is None:
|
||||
return None # Skip the internal flow (or a bare flow)
|
||||
ctx.payload.raw = ctx.payload.raw.strip()
|
||||
```
|
||||
|
||||
## حالات استخدام شائعة
|
||||
|
||||
### فحص السياسة عند البدء
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def enforce_policy(ctx):
|
||||
if ctx.crew is not None and not ctx.payload.get("authorized"):
|
||||
raise HookAborted(reason="unauthorized execution", source="access-control")
|
||||
```
|
||||
|
||||
### إعادة كتابة المدخلات
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.INPUT)
|
||||
def add_defaults(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.setdefault("locale", "en-US")
|
||||
ctx.payload["topic"] = ctx.payload["topic"].strip().lower()
|
||||
```
|
||||
|
||||
تتدفق المدخلات المعاد كتابتها إلى استيفاء الـ Task، فيتصرف التشغيل كما لو
|
||||
بدأ بالقاموس المعدل.
|
||||
|
||||
فضّل `INPUT` لإعادة الكتابة وعامل `EXECUTION_START` كبوابة سماح/منع. إعادة
|
||||
الكتابة عند `EXECUTION_START` تظل مُحترمة — في الـ Crew تغذي أيضًا استدعاءات
|
||||
`before_kickoff`؛ وفي الـ Flow تُطبق تمامًا كإعادة كتابة `INPUT`.
|
||||
|
||||
### تنقية المخرجات
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def redact_emails(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.raw = re.sub(
|
||||
r"\b[\w.+-]+@[\w-]+\.[\w.]+\b", "[EMAIL-REDACTED]", ctx.payload.raw
|
||||
)
|
||||
```
|
||||
|
||||
يعمل `OUTPUT` قبل `EXECUTION_END`، وكلاهما يرى الـ payload (الذي ربما
|
||||
استُبدل) من الخطافات السابقة؛ والقيمة النهائية المعاد كتابتها هي ما يعيده
|
||||
`kickoff()`.
|
||||
|
||||
### مراقبة الإخفاقات
|
||||
|
||||
يُطلق `EXECUTION_END` مرة واحدة بالضبط لكل تنفيذ، عند النجاح والفشل على حد
|
||||
سواء. عندما يرفع التشغيل استثناءً — خطأ في Task، أو استثناء في دالة Flow، أو
|
||||
`HookAborted` من نقطة سابقة — يتلقى الخطاف `status="failed"` مع الاستثناء في
|
||||
`ctx.error`، ويظل الاستثناء الأصلي ينتشر خارج `kickoff()` دون تغيير:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_END)
|
||||
def report_outcome(ctx):
|
||||
if ctx.status == "failed":
|
||||
notify_policy_engine(status="failed", error=repr(ctx.error))
|
||||
else:
|
||||
notify_policy_engine(status="completed")
|
||||
```
|
||||
|
||||
تنبيهان: لا يُطلق `EXECUTION_END` عندما لا يكون `EXECUTION_START` قد أُرسل
|
||||
أصلًا (الإجهاض عند البدء يعني أن الحد لم يُفتح قط، فلا توجد نهاية تقابله)،
|
||||
ورفع `HookAborted` من إرسال `EXECUTION_END` في مسار الفشل يُتجاهل — لم يعد
|
||||
هناك ما يُجهض، والخطأ الأصلي هو الغالب.
|
||||
|
||||
## الترتيب
|
||||
|
||||
لتشغيل Crew يكون ترتيب الحدود:
|
||||
|
||||
```
|
||||
EXECUTION_START → before_kickoff callbacks → INPUT → tasks execute → OUTPUT → EXECUTION_END
|
||||
```
|
||||
|
||||
لتشغيل Flow، تحسم خطافات الحدود المدخلات قبل أن تبدأ أحداث دورة الحياة:
|
||||
|
||||
```
|
||||
EXECUTION_START → INPUT → FlowStartedEvent → flow methods execute → OUTPUT → EXECUTION_END → FlowFinishedEvent
|
||||
```
|
||||
|
||||
يحمل `FlowStartedEvent` المدخلات كما حسمتها الخطافات، وإعادة كتابة
|
||||
`inputs["id"]` داخل خطاف حدودي تعيد توجيه استعادة الحالة. يظهر الإجهاض عند
|
||||
`EXECUTION_START` مع ذلك كحدث `FlowStartedEvent` يتبعه `FlowFailedEvent`،
|
||||
ويُبثان عند الإجهاض مع الحمولة كما حسمتها الخطافات التي عملت قبله.
|
||||
|
||||
تعمل الخطافات في النقطة نفسها حسب ترتيب التسجيل، الخطافات العامة أولًا ثم
|
||||
الخطافات المحدودة بالـ Crew. تُبث القياسات (`HookDispatchedEvent`) مع كل
|
||||
إرسال.
|
||||
|
||||
## إدارة الخطافات في الاختبارات
|
||||
|
||||
```python
|
||||
from crewai.hooks import clear_all_hooks
|
||||
|
||||
clear_all_hooks() # Clears every point, including boundaries
|
||||
```
|
||||
|
||||
## وثائق ذات صلة
|
||||
|
||||
- [نظرة عامة على خطافات التنفيذ →](/edge/ar/learn/execution-hooks)
|
||||
- [خطافات استدعاء LLM →](/edge/ar/learn/llm-hooks)
|
||||
- [خطافات استدعاء الأدوات →](/edge/ar/learn/tool-hooks)
|
||||
@@ -176,7 +176,7 @@ grep -r "llm:" --include="*.yaml" .
|
||||
# llm = LLM(model="mistral/mistral-large-latest")
|
||||
|
||||
# After (Native):
|
||||
llm = LLM(model="gemini/gemini-3.7-flash")
|
||||
llm = LLM(model="gemini/gemini-2.0-flash")
|
||||
```
|
||||
|
||||
```bash
|
||||
@@ -312,7 +312,7 @@ llm = LLM(model="anthropic/claude-haiku-3-5") # Fast & affordable
|
||||
# Together AI → OpenAI or Gemini
|
||||
# llm = LLM(model="together_ai/meta-llama/Meta-Llama-3.1-70B")
|
||||
llm = LLM(model="openai/gpt-4o") # High quality
|
||||
llm = LLM(model="gemini/gemini-3.7-flash") # Fast & capable
|
||||
llm = LLM(model="gemini/gemini-2.0-flash") # Fast & capable
|
||||
|
||||
# Mistral → Anthropic or OpenAI
|
||||
# llm = LLM(model="mistral/mistral-large-latest")
|
||||
|
||||
@@ -141,7 +141,7 @@ mode: "wide"
|
||||
# Example using Gemini's OpenAI-compatible API.
|
||||
os.environ["OPENAI_API_KEY"] = "your-gemini-key" # Should start with AIza...
|
||||
os.environ["OPENAI_API_BASE"] = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-flash" # Add your Gemini model here, under openai/
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-2.0-flash" # Add your Gemini model here, under openai/
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
@@ -159,7 +159,7 @@ mode: "wide"
|
||||
```python Google
|
||||
# Example using Gemini's OpenAI-compatible API
|
||||
llm = LLM(
|
||||
model="openai/gemini-3.7-flash",
|
||||
model="openai/gemini-2.0-flash",
|
||||
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
||||
api_key="your-gemini-key", # Should start with AIza...
|
||||
)
|
||||
|
||||
@@ -144,7 +144,7 @@ Planning agents benefit from reasoning models that can handle complex strategic
|
||||
from crewai import Agent, Task, Crew, LLM
|
||||
|
||||
# High-capability reasoning model for strategic planning
|
||||
manager_llm = LLM(model="gemini/gemini-3.7-flash", temperature=0.1)
|
||||
manager_llm = LLM(model="gemini-2.5-flash-preview-05-20", temperature=0.1)
|
||||
|
||||
# Creative model for content generation
|
||||
content_llm = LLM(model="claude-3-5-sonnet-20241022", temperature=0.7)
|
||||
@@ -409,7 +409,7 @@ Rather than repeating the strategic framework, here's a tactical checklist for i
|
||||
# Manager or coordination agents
|
||||
manager_agent = Agent(
|
||||
role="Project Manager",
|
||||
llm=LLM(model="gemini/gemini-3.7-flash"), # Premium for coordination
|
||||
llm=LLM(model="gemini-2.5-flash-preview-05-20"), # Premium for coordination
|
||||
# ... rest of config
|
||||
)
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ result = stream.result
|
||||
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationConfig, ConversationState
|
||||
from crewai.experimental.conversational import ConversationConfig, ConversationState
|
||||
|
||||
|
||||
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)
|
||||
|
||||
@@ -7,9 +7,9 @@ mode: "wide"
|
||||
|
||||
# تكامل Arize Phoenix
|
||||
|
||||
يوضح هذا الدليل كيفية دمج **Arize Phoenix** مع **CrewAI** باستخدام OpenTelemetry عبر حزمة [OpenInference](https://github.com/openinference/openinference) SDK. بنهاية هذا الدليل، ستتمكن من تتبع وكلاء CrewAI وتصحيح سلوك الوكلاء.
|
||||
يوضح هذا الدليل كيفية دمج **Arize Phoenix** مع **CrewAI** باستخدام OpenTelemetry عبر حزمة [OpenInference](https://github.com/openinference/openinference) SDK. بنهاية هذا الدليل، ستتمكن من تتبع وكلاء CrewAI وتصحيح أخطاء وكلائك بسهولة.
|
||||
|
||||
> **ما هو Arize Phoenix؟** [Arize Phoenix](https://arize.com/phoenix/) هو خيار المراقبة والتقييم مفتوح المصدر من [Arize AI](https://arize.com/?utm_source=crewai-docs&utm_medium=partner&utm_campaign=partner-docs&utm_content=observability-arize-phoenix). استخدم Phoenix عندما تريد التشغيل محلياً أو الاستضافة الذاتية. استخدم [Arize AX](https://arize.com/products/ax/) لمنصة سحابية مُدارة أو ذاتية الاستضافة للمؤسسات لأنظمة الذكاء الاصطناعي في الإنتاج.
|
||||
> **ما هو Arize Phoenix؟** [Arize Phoenix](https://phoenix.arize.com) هو منصة مراقبة LLM توفر التتبع والتقييم لتطبيقات الذكاء الاصطناعي.
|
||||
|
||||
[](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
|
||||
|
||||
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
|
||||
|
||||
### الخطوة 2: إعداد متغيرات البيئة
|
||||
|
||||
قم بإعداد مفتاح API الخاص بـ Phoenix ونقطة نهاية OpenTelemetry لإرسال التتبعات إلى Phoenix. يعمل الإعداد نفسه مع نقطة نهاية Phoenix محلية أو ذاتية الاستضافة عن طريق تغيير عنوان المجمع.
|
||||
قم بإعداد مفاتيح API لـ Phoenix Cloud وإعداد OpenTelemetry لإرسال التتبعات إلى Phoenix. Phoenix Cloud هو إصدار مستضاف من Arize Phoenix، لكنه ليس مطلوباً لاستخدام هذا التكامل.
|
||||
|
||||
يمكنك الحصول على مفتاح Serper API المجاني [هنا](https://serper.dev/).
|
||||
|
||||
@@ -35,8 +35,8 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
|
||||
import os
|
||||
from getpass import getpass
|
||||
|
||||
# Get your Phoenix API key
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix API key: ")
|
||||
# Get your Phoenix Cloud credentials
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
|
||||
|
||||
# Get API keys for services
|
||||
OPENAI_API_KEY = getpass("🔑 Enter your OpenAI API key: ")
|
||||
@@ -44,7 +44,7 @@ SERPER_API_KEY = getpass("🔑 Enter your Serper API key: ")
|
||||
|
||||
# Set environment variables
|
||||
os.environ["PHOENIX_CLIENT_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
|
||||
os.environ["SERPER_API_KEY"] = SERPER_API_KEY
|
||||
```
|
||||
@@ -131,7 +131,7 @@ print(result)
|
||||
|
||||
بعد تشغيل الوكيل، يمكنك عرض التتبعات المولدة من تطبيق CrewAI في Phoenix. سترى خطوات مفصلة لتفاعلات الوكلاء واستدعاءات LLM، مما يساعدك في التصحيح والتحسين.
|
||||
|
||||
افتح مشروع Phoenix وانتقل إلى المشروع الذي حددته في معامل `project_name`. سترى عرض زمني للتتبع مع جميع تفاعلات الوكلاء واستخدامات الأدوات واستدعاءات LLM.
|
||||
سجل الدخول إلى حساب Phoenix Cloud الخاص بك وانتقل إلى المشروع الذي حددته في معامل `project_name`. سترى عرض زمني للتتبع مع جميع تفاعلات الوكلاء واستخدامات الأدوات واستدعاءات LLM.
|
||||
|
||||

|
||||
|
||||
@@ -145,9 +145,6 @@ print(result)
|
||||
|
||||
### المراجع
|
||||
- [وثائق Phoenix](https://docs.arize.com/phoenix/) - نظرة عامة على منصة Phoenix.
|
||||
- [Arize AX](https://arize.com/products/ax/) - مراقبة وتقييم مُداران سحابياً أو ذاتيا الاستضافة للمؤسسات.
|
||||
- [دليل Arize لتقييم الوكلاء](https://arize.com/guides/ai-agent-handbook/agent-evaluation/) - سير عمل إنتاجي لتقييم سلوك الوكلاء من التتبعات.
|
||||
- [دليل Arize لتقييم LLM](https://arize.com/resources/llm-evaluation/) - طرق ومقاييس لتقييم تطبيقات LLM.
|
||||
- [وثائق CrewAI](https://docs.crewai.com/) - نظرة عامة على إطار عمل CrewAI.
|
||||
- [وثائق OpenTelemetry](https://opentelemetry.io/docs/) - دليل OpenTelemetry
|
||||
- [OpenInference GitHub](https://github.com/openinference/openinference) - الكود المصدري لـ OpenInference SDK.
|
||||
|
||||
@@ -34,36 +34,18 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
|
||||
os.environ['OTEL_SDK_DISABLED'] = 'true'
|
||||
```
|
||||
|
||||
### العزل عن إعداد OpenTelemetry الخاص بك
|
||||
|
||||
يعمل القياس عن بُعد الخاص بـ CrewAI على `TracerProvider` خاص به ولا يسجل نفسه
|
||||
أبدًا كمزوّد عام. هذا يفصل الاتجاهين:
|
||||
|
||||
- لا تُرسَل أبدًا إلى CrewAI الامتدادات (spans) الصادرة عن المكتبات الأخرى
|
||||
المزوّدة بأدوات القياس في عمليتك — أطر الويب، وعملاء قواعد البيانات،
|
||||
وعملاء HTTP.
|
||||
- لا تُرسَل امتدادات القياس عن بُعد الخاصة بـ CrewAI إلى نظام المراقبة لديك، لذا
|
||||
لن تظهر في Langfuse أو Braintrust أو Phoenix أو أي مُجمِّع آخر تقوم بإعداده.
|
||||
|
||||
لا تتأثر تكاملات المراقبة: فهي تقيس CrewAI عبر مزوّد التتبع الخاص بها، وهو
|
||||
مستقل عن المزوّد الموصوف هنا.
|
||||
|
||||
### شرح البيانات:
|
||||
| افتراضي | البيانات | السبب والتفاصيل |
|
||||
|:----------|:------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------|
|
||||
| نعم | إصدار CrewAI وPython | تتبع إصدارات البرمجيات. مثال: CrewAI v1.2.3، Python 3.8.10. لا بيانات شخصية. |
|
||||
| نعم | بيانات وصفية للطاقم | تشمل: مفتاح ومعرّف مُولّد عشوائياً، نوع العملية (مثل 'sequential'، 'parallel')، علم منطقي لاستخدام الذاكرة (true/false)، علم منطقي يوضح ما إذا تم تمرير أي مدخلات للتشغيل (true/false — وليس مفاتيح المدخلات أو قيمها، والتي لا تُجمع إلا عند تمكين `share_crew`)، عدد المهام، عدد الوكلاء. كلها غير شخصية. |
|
||||
| نعم | بيانات وصفية للطاقم | تشمل: مفتاح ومعرّف مُولّد عشوائياً، نوع العملية (مثل 'sequential'، 'parallel')، علم منطقي لاستخدام الذاكرة (true/false)، عدد المهام، عدد الوكلاء. كلها غير شخصية. |
|
||||
| نعم | بيانات الوكيل | تشمل: مفتاح ومعرّف مُولّد عشوائياً، اسم الدور (يجب ألا يتضمن معلومات شخصية)، إعدادات منطقية (verbose، التفويض مُفعّل، تنفيذ الكود مسموح)، أقصى عدد تكرارات، أقصى RPM، أقصى حد لإعادة المحاولة، معلومات LLM (انظر سمات LLM)، قائمة أسماء الأدوات (يجب ألا تتضمن معلومات شخصية). لا بيانات شخصية. |
|
||||
| نعم | بيانات وصفية للمهمة | تشمل: مفتاح ومعرّف مُولّد عشوائياً، إعدادات تنفيذ منطقية (async_execution، human_input)، دور ومفتاح الوكيل المرتبط، قائمة أسماء الأدوات. كلها غير شخصية. |
|
||||
| نعم | إحصائيات استخدام الأدوات | تشمل: اسم الأداة (يجب ألا يتضمن معلومات شخصية)، عدد محاولات الاستخدام (عدد صحيح)، سمات LLM المستخدمة. لا بيانات شخصية. |
|
||||
| نعم | بيانات تنفيذ الاختبار | تشمل: مفتاح ومعرّف الطاقم المُولّد عشوائياً، عدد التكرارات، اسم النموذج المستخدم، درجة الجودة (عدد عشري)، وقت التنفيذ (بالثواني). كلها غير شخصية. |
|
||||
| نعم | بيانات دورة حياة المهمة | تشمل: أوقات الإنشاء وبدء/انتهاء التنفيذ، معرّفات الطاقم والمهمة، وما إذا نجحت المهمة أو فشلت. وعند فشل المهمة، يُسجَّل **اسم صنف** الاستثناء (مثل `TimeoutError`) بحيث يمكن عدّ حالات الفشل وتشخيصها — وليس رسالة الخطأ أبدًا، فهي قد تحتوي على مطالبات أو مخرجات نموذج أو مسارات ملفات أو بيانات اعتماد. مخزنة كنطاقات مع طوابع زمنية. لا بيانات شخصية. |
|
||||
| نعم | بيانات دورة حياة المهمة | تشمل: أوقات الإنشاء وبدء/انتهاء التنفيذ، معرّفات الطاقم والمهمة. مخزنة كنطاقات مع طوابع زمنية. لا بيانات شخصية. |
|
||||
| نعم | سمات LLM | تشمل: الاسم، model_name، model، top_k، temperature، واسم فئة LLM. كلها بيانات تقنية غير شخصية. |
|
||||
| نعم | إنشاء مشروع باستخدام CLI الخاص بـ CrewAI | تشمل: أن مشروعًا جديدًا أُنشئ عبر `crewai create`، ونوعه (`crew` أو `json_crew` أو `flow`)، ومعرّف المشروع الذي تم توليده لهذا المشروع الجديد وكُتب في ملف `pyproject.toml` الخاص به. وهو معرّف المشروع الجديد نفسه، ويُسجَّل بشكل منفصل عن `project_id` الخاص بالمجلد الذي شُغّل منه الأمر — وقد يختلفان. لا اسم مشروع، ولا محتويات ملفات، ولا شيفرة. لا بيانات شخصية. |
|
||||
| نعم | محاولة نشر الطاقم باستخدام CLI الخاص بـ CrewAI | تشمل: حقيقة إجراء النشر ومعرّف الطاقم، وما إذا كان يحاول سحب السجلات، وما إذا بدأ النشر من أمر CLI أو من واجهة التشغيل TUI. لا تُسجَّل محتويات المشروع أو الطاقم. لا توجد بيانات شخصية. |
|
||||
| نعم | بيئة التنفيذ | تشمل: مساعد البرمجة بالذكاء الاصطناعي الذي يشغّل العملية إن وُجد (واحد من قائمة ثابتة مثل `claude_code` أو `codex` أو `cursor` أو `unknown`)، ومكان تشغيل العملية (واحد من قائمة ثابتة مثل `ci` أو `container` أو `serverless` أو `interactive`)، و`project_id` من ملف `pyproject.toml` عند ضبطه، ونطاقًا تقريبيًا لحجم الجهاز (واحد من `1-2` أو `3-4` أو `5-8` أو `9-16` أو `17-32` أو `33+` أو `unknown`). النطاق مجال وليس العدد الدقيق للأنوية أبدًا — العدد الدقيق اختياري فقط، ضمن «معلومات البيئة» أدناه. تأتي فئة الحجم من عدد أنوية المضيف؛ ويتحقق اكتشاف المساعد وموقع التشغيل فقط مما إذا كانت متغيرات البيئة المعروفة مضبوطة، ولا يقرأ قيمها أبدًا. لا بيانات شخصية. |
|
||||
| نعم | إشارات دورة حياة التدفق | تشمل: بدء التدفق، وما إذا اكتمل أو فشل، وما إذا فشلت إحدى طرقه، وما إذا توقف مؤقتًا لانتظار إدخال أو ملاحظات بشرية، وما إذا كان البدء تشغيلًا مستأنفًا، وما إذا فشل دور محادثة، ومدة تشغيل التدفق، وما إذا كان التدفق مما تشغّله CrewAI داخليًا أو مما كتبته أنت. ويُسجَّل اسم التدفق، كما هو الحال بالفعل لإنشاء التدفق وتنفيذه. وعند فشل تدفق أو إحدى طرقه، يُسجَّل **اسم فئة** الاستثناء (مثل `TimeoutError`) لتشخيص الأعطال — ولا تُسجَّل أبدًا رسالة الخطأ، التي قد تحتوي على مطالبات أو مخرجات النموذج أو مسارات ملفات أو بيانات اعتماد. ولا تُسجَّل أبدًا أسماء الطرق أو حالة التدفق. لا توجد بيانات شخصية. |
|
||||
| نعم | إشارة مشاركة التتبع | تشمل: نجاح مشاركة دفعة من عمليات التتبع مع CrewAI AMP، وما إذا تمت المشاركة بشكل مجهول (قبل إنشاء حساب) أو مرتبطة بحسابك. ومثل كل span، تحمل أيضًا سمات بيئة التنفيذ الموضحة أعلاه (`project_id` عند تكوينه، ومساعد البرمجة، وبيئة التشغيل). يصف هذا الصف بيانات القياس عن بُعد الخاصة بالمشاركة فقط — وليس محتويات التتبع أو الوصول الذي تمنحه روابط التتبع المشتركة. لا تُسجَّل محتويات التتبع أو المدخلات أو المخرجات في هذه الإشارة. قبل مشاركة التتبعات، راجع الأسرار والبيانات الشخصية وإعدادات التنقيح والاحتفاظ في AMP. |
|
||||
| نعم | محاولة نشر الطاقم باستخدام CLI الخاص بـ CrewAI | تشمل: حقيقة إجراء النشر ومعرّف الطاقم، وما إذا كان يحاول سحب السجلات، لا بيانات أخرى. |
|
||||
| لا | بيانات الوكيل الموسّعة | تشمل: وصف الهدف، نص الخلفية، معرّف ملف موجهات i18n. يجب على المستخدمين التأكد من عدم تضمين معلومات شخصية في حقول النص. |
|
||||
| لا | معلومات المهمة التفصيلية | تشمل: وصف المهمة، وصف المخرجات المتوقعة، مراجع السياق. يجب على المستخدمين التأكد من عدم تضمين معلومات شخصية في هذه الحقول. |
|
||||
| لا | معلومات البيئة | تشمل: المنصة، الإصدار، النظام، الإصدار، وعدد وحدات المعالجة المركزية. مثال: 'Windows 10'، 'x86_64'. لا بيانات شخصية. |
|
||||
|
||||
@@ -9,7 +9,7 @@ mode: "wide"
|
||||
|
||||
## الوصف
|
||||
|
||||
أداة `ScrapeElementFromWebsiteTool` مصممة لاستخراج عناصر محددة من المواقع باستخدام محددات CSS. تسمح هذه الأداة لوكلاء CrewAI باستخراج محتوى مستهدف من صفحات الويب، مما يجعلها مفيدة لمهام استخراج البيانات حيث تكون أجزاء محددة فقط من صفحة الويب مطلوبة. تمر الطلبات عبر مساعد HTTP الآمن ضد SSRF في CrewAI: يتم فحص عنوان URL المطلوب وكل قفزة إعادة توجيه مقابل النطاقات الخاصة والمحجوزة (بما في ذلك بيانات تعريف السحابة)، ويُثبَّت اتصال TCP على عنوان IP الذي اجتاز هذا الفحص.
|
||||
أداة `ScrapeElementFromWebsiteTool` مصممة لاستخراج عناصر محددة من المواقع باستخدام محددات CSS. تسمح هذه الأداة لوكلاء CrewAI باستخراج محتوى مستهدف من صفحات الويب، مما يجعلها مفيدة لمهام استخراج البيانات حيث تكون أجزاء محددة فقط من صفحة الويب مطلوبة.
|
||||
|
||||
## التثبيت
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ mode: "wide"
|
||||
أداة مصممة لاستخراج وقراءة محتوى موقع محدد. قادرة على التعامل مع أنواع مختلفة من صفحات الويب عن طريق إجراء طلبات HTTP وتحليل محتوى HTML المستلم.
|
||||
يمكن أن تكون هذه الأداة مفيدة بشكل خاص لمهام استخراج البيانات من الويب وجمع البيانات أو استخراج معلومات محددة من المواقع.
|
||||
|
||||
تمر الطلبات عبر مساعد HTTP الآمن ضد SSRF في CrewAI: يتم فحص عنوان URL المطلوب وكل قفزة إعادة توجيه مقابل النطاقات الخاصة والمحجوزة (بما في ذلك بيانات تعريف السحابة)، ويُثبَّت اتصال TCP على عنوان IP الذي اجتاز هذا الفحص.
|
||||
|
||||
## التثبيت
|
||||
|
||||
ثبّت حزمة crewai_tools
|
||||
|
||||
@@ -4,232 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Aug 27, 2026">
|
||||
## v1.15.18
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.18)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Promote conversational flows to stable
|
||||
- Record a created deployment with the given UUID
|
||||
- Enhance conversational flow documentation and APIs
|
||||
- Let a declaration name the router's response format
|
||||
- Let a chat flow declare its own state shape
|
||||
- Accept crew-style LLM config in a conversational declaration
|
||||
- Report project creation with the minted ID
|
||||
- Record whether a run had inputs, without recording the inputs
|
||||
- Backfill project ID from every user-invoked project command
|
||||
|
||||
### Bug Fixes
|
||||
- Preserve tool results when the final answer is empty
|
||||
- Map default Claude Sonnet 4.6 to its 1M context window
|
||||
- Raise Anthropic default max_tokens for large tool calls
|
||||
- Render message content parts as text, not as a Python repr
|
||||
- Keep message roles when Agent.kickoff gets a conversation
|
||||
- Skip interception hooks on crewai-internal flows
|
||||
- Record task failures as failures, not successes
|
||||
- Emit the flow lifecycle on a suppressed resume
|
||||
- Open the conversational TUI for a declarative chat flow
|
||||
- Record crew_memory as a string, not a bool
|
||||
- Always emit project_id so absent and empty stay distinct
|
||||
|
||||
### Documentation
|
||||
- Clarify Arize Phoenix observability docs
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @arizedatngo, @joaomdmoura, @lorenzejay, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 19, 2026">
|
||||
## v1.15.17
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.17)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Add declarative conversational flows documentation
|
||||
- Synthesize built-in conversational methods for declarations
|
||||
- Enable declarations to drive conversational mode
|
||||
- Make conversational opt-in unmistakable
|
||||
- Carry the AMP slug on tools resolved from a slug reference
|
||||
- Handle oversized single messages during chunking
|
||||
|
||||
### Bug Fixes
|
||||
- Fix usage of the URL hostname as MCP HTTP and SSE server_name
|
||||
- Close the agent scope on every failed attempt
|
||||
- Attribute tool errors to the tool that failed
|
||||
- Pin SSRF checks to each redirect hop and peer IP
|
||||
- Resolve issues with native tool calls broken over OpenAI Responses API
|
||||
|
||||
### Documentation
|
||||
- Update documentation with a snapshot and changelog for v1.15.16
|
||||
|
||||
## Contributors
|
||||
|
||||
@Copilot, @Vidit-Ostwal, @github-code-quality[bot], @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 13, 2026">
|
||||
## v1.15.16
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.16)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Introduce execution context management with UUID support
|
||||
- Record what kind of exception ended a flow
|
||||
- Record when a trace batch is shared with AMP
|
||||
- Count deployments from any origin and record where they started
|
||||
|
||||
### Bug Fixes
|
||||
- Record the running release on every emitted span
|
||||
- Fix MySQL search table name validation
|
||||
- Stop a failed turn from marking the next one as failed
|
||||
|
||||
### Documentation
|
||||
- Add Frontend guides for CopilotKit and AG-UI
|
||||
|
||||
## Contributors
|
||||
|
||||
@joaomdmoura, @lorenzejay, @ranst91, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 11, 2026">
|
||||
## v1.15.15
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.15)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Report flow outcome, duration, and human-in-the-loop signals.
|
||||
|
||||
### Bug Fixes
|
||||
- Emit FlowStartedEvent when a boundary hook aborts the flow.
|
||||
- Scope span export to our own tracer provider.
|
||||
- Bump torch to version 2.13.0 to address security vulnerability.
|
||||
- Bump gitpython to version 3.1.58 in crewai-tools[github].
|
||||
|
||||
### Refactoring
|
||||
- Update date injection functionality in agents.
|
||||
- Standardize CLI flags to kebab-case.
|
||||
|
||||
### Documentation
|
||||
- Snapshot and changelog for v1.15.14.
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 08, 2026">
|
||||
## v1.15.14
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.14)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Split runtime context from coding agent and add project ID
|
||||
|
||||
### Documentation
|
||||
- Update snapshot and changelog for v1.15.13
|
||||
|
||||
## Contributors
|
||||
|
||||
@joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 07, 2026">
|
||||
## v1.15.13
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.13)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Bug Fixes
|
||||
- Fix preservation of provider on LiteLLM-routed models.
|
||||
- Harden brittle LLM event-bus mocks.
|
||||
- Fix underreporting of Anthropic cache token usage.
|
||||
- Bump h2 to version 4.4.1 to address security vulnerability GHSA-6hr6-w5qg-qmwg.
|
||||
|
||||
### Documentation
|
||||
- Add DOCS_TRANSLATIONS workflow for locale synchronization.
|
||||
- Fix broken README links, table of contents, and contribution guidance.
|
||||
- Snapshot and changelog for version 1.15.12.
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 05, 2026">
|
||||
## v1.15.12
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.12)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Bump Flow canary on release
|
||||
- Add URLReadTool for reading arbitrary URLs
|
||||
- Add app metadata to platform action tools
|
||||
- Unify scaffolding under `crewai create <resource>`
|
||||
|
||||
### Bug Fixes
|
||||
- Clarify conversational route/handler name collision errors
|
||||
|
||||
### Documentation
|
||||
- Update scaffold AGENTS.md for unified create CLI
|
||||
|
||||
### Breaking Changes
|
||||
- None
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Aug 04, 2026">
|
||||
## v1.15.11
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.11)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Track interception-hook dispatches in telemetry
|
||||
- Add project_id to link OSS usage to an enterprise account
|
||||
- Surface AMP in AGENTS.md and detect coding agents in telemetry
|
||||
- Add IBM Db2 search tool
|
||||
|
||||
### Bug Fixes
|
||||
- Clear CodeQL incomplete URL substring sanitization alerts
|
||||
- Bump aiohttp and cryptography to clear six GHSA advisories
|
||||
- Report the real CEL error for failures inside map literals
|
||||
- Correctly skip code CI for docs-only PRs
|
||||
|
||||
### Documentation
|
||||
- Snapshot and changelog for v1.15.10
|
||||
|
||||
## Contributors
|
||||
|
||||
@PawanThakurIBM, @Vidit-Ostwal, @gabemilani, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jul 31, 2026">
|
||||
## v1.15.10
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ The Visual Agent Builder enables:
|
||||
| **Respect Context Window** _(optional)_ | `respect_context_window` | `bool` | Keep messages under context window size by summarizing. Default is True. |
|
||||
| **Code Execution Mode** _(optional)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct). Default is 'safe'. |
|
||||
| **Multimodal** _(optional)_ | `multimodal` | `bool` | Whether the agent supports multimodal capabilities. Default is False. |
|
||||
| **Inject Date** _(optional)_ | `inject_date` | `bool` | Whether to automatically inject the current date into the agent's prompt. Default is False. |
|
||||
| **Inject Date** _(optional)_ | `inject_date` | `bool` | Whether to automatically inject the current date into tasks. Default is False. |
|
||||
| **Date Format** _(optional)_ | `date_format` | `str` | Format string for date when inject_date is enabled. Default is "%Y-%m-%d" (ISO format). |
|
||||
| **Reasoning** _(optional)_ | `reasoning` | `bool` | Whether the agent should reflect and create a plan before executing a task. Default is False. |
|
||||
| **Max Reasoning Attempts** _(optional)_ | `max_reasoning_attempts` | `Optional[int]` | Maximum number of reasoning attempts before executing the task. If None, will try until ready. |
|
||||
@@ -236,7 +236,7 @@ strategic_agent = Agent(
|
||||
role="Market Analyst",
|
||||
goal="Track market movements with precise date references and strategic planning",
|
||||
backstory="Expert in time-sensitive financial analysis and strategic reporting",
|
||||
inject_date=True, # Automatically inject current date into the prompt
|
||||
inject_date=True, # Automatically inject current date into tasks
|
||||
date_format="%B %d, %Y", # Format as "May 21, 2025"
|
||||
reasoning=True, # Enable strategic planning
|
||||
max_reasoning_attempts=2, # Limit planning iterations
|
||||
@@ -303,7 +303,7 @@ multimodal_agent = Agent(
|
||||
|
||||
- `multimodal`: Enable multimodal capabilities for processing text and visual content
|
||||
- `reasoning`: Enable agent to reflect and create plans before executing tasks
|
||||
- `inject_date`: Automatically inject current date into the agents prompt
|
||||
- `inject_date`: Automatically inject current date into task descriptions
|
||||
|
||||
#### Templates
|
||||
|
||||
@@ -623,12 +623,6 @@ messages = [
|
||||
result = researcher.kickoff(messages)
|
||||
```
|
||||
|
||||
The last `user` message is the request the agent answers. Every other message
|
||||
keeps its role and its position around that request, so a conversation that
|
||||
ends in assistant or tool messages still asks the user's question and still
|
||||
delivers those trailing turns after it. With no `user` message at all, the last
|
||||
message is treated as the request.
|
||||
|
||||
### Async Support
|
||||
|
||||
An asynchronous version is available via `kickoff_async()` with the same parameters:
|
||||
|
||||
@@ -36,83 +36,24 @@ crewai [COMMAND] [OPTIONS] [ARGUMENTS]
|
||||
|
||||
### 1. Create
|
||||
|
||||
Create a new crew, flow, tool, skill, or template project.
|
||||
Create a new crew or flow.
|
||||
|
||||
```shell Terminal
|
||||
crewai create [OPTIONS] TYPE NAME
|
||||
```
|
||||
|
||||
- `TYPE`: `crew`, `flow`, `tool`, `skill`, or `template`
|
||||
- `NAME`: Name of the project, tool handle, skill, or template
|
||||
- `TYPE`: Choose between "crew" or "flow"
|
||||
- `NAME`: Name of the crew or flow
|
||||
|
||||
#### Crew
|
||||
Example:
|
||||
|
||||
```shell Terminal
|
||||
crewai create crew my_new_crew
|
||||
crewai create crew my_new_crew --classic
|
||||
crewai create flow my_new_flow
|
||||
```
|
||||
|
||||
By default, `crewai create crew` creates a JSON-first crew project with `crew.jsonc` and `agents/*.jsonc`. Use `crewai create crew my_new_crew --classic` only when you want the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`.
|
||||
|
||||
#### Flow
|
||||
|
||||
```shell Terminal
|
||||
crewai create flow my_new_flow
|
||||
crewai create flow my_new_flow --declarative
|
||||
```
|
||||
|
||||
#### Tool
|
||||
|
||||
Scaffold a custom tool repository:
|
||||
|
||||
```shell Terminal
|
||||
crewai create tool my_tool
|
||||
```
|
||||
|
||||
#### Skill
|
||||
|
||||
Scaffold an agent skill. Inside a crew project (where `pyproject.toml` exists), the skill is created under `./skills/`:
|
||||
|
||||
```shell Terminal
|
||||
crewai create skill my-skill
|
||||
crewai create skill my-skill --no-project
|
||||
```
|
||||
|
||||
Use `--no-project` to create the skill in the current directory instead of `./skills/`.
|
||||
|
||||
#### Template
|
||||
|
||||
Add a remote project template to the current directory:
|
||||
|
||||
```shell Terminal
|
||||
crewai create template my-template
|
||||
crewai create template my-template --output-dir custom_dir
|
||||
```
|
||||
|
||||
Use `--output-dir` to override the output folder name (defaults to the template name).
|
||||
|
||||
#### Deprecated create aliases
|
||||
|
||||
These older commands still work but print a yellow deprecation warning. Prefer the `crewai create <type>` forms above.
|
||||
|
||||
| Deprecated | Use instead |
|
||||
| :--- | :--- |
|
||||
| `crewai tool create <handle>` | `crewai create tool <handle>` |
|
||||
| `crewai skill create <name>` | `crewai create skill <name>` |
|
||||
| `crewai template add <name>` | `crewai create template <name>` |
|
||||
|
||||
Lifecycle commands are unchanged — for example `crewai tool install`, `crewai skill publish`, and `crewai template list` stay under their resource groups.
|
||||
|
||||
#### Deprecated flag aliases
|
||||
|
||||
These older snake_case flags still work but are hidden from `--help`. Prefer the kebab-case forms documented in each command section below.
|
||||
|
||||
| Deprecated | Use instead |
|
||||
| :--- | :--- |
|
||||
| `--skip_provider` (on `crewai create crew`) | `--skip-provider` |
|
||||
| `--n_iterations` (on `crewai train`, `crewai test`) | `--n-iterations` |
|
||||
| `--task_id` (on `crewai replay`) | `--task-id` |
|
||||
|
||||
### 2. Version
|
||||
|
||||
Show the installed version of CrewAI.
|
||||
@@ -138,7 +79,7 @@ Train the crew for a specified number of iterations.
|
||||
crewai train [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: Number of iterations to train the crew (default: 5)
|
||||
- `-n, --n_iterations INTEGER`: Number of iterations to train the crew (default: 5)
|
||||
- `-f, --filename TEXT`: Path to a custom file for training (default: "trained_agents_data.pkl")
|
||||
|
||||
Example:
|
||||
@@ -155,7 +96,7 @@ Replay the crew execution from a specific task.
|
||||
crewai replay [OPTIONS]
|
||||
```
|
||||
|
||||
- `-t, --task-id TEXT`: Replay the crew from this task ID, including all subsequent tasks
|
||||
- `-t, --task_id TEXT`: Replay the crew from this task ID, including all subsequent tasks
|
||||
|
||||
Example:
|
||||
|
||||
@@ -202,7 +143,7 @@ Test the crew and evaluate the results.
|
||||
crewai test [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: Number of iterations to test the crew (default: 3)
|
||||
- `-n, --n_iterations INTEGER`: Number of iterations to test the crew (default: 3)
|
||||
- `-m, --model TEXT`: LLM Model to run the tests on the Crew (default: "gpt-4o-mini")
|
||||
|
||||
Example:
|
||||
|
||||
@@ -322,8 +322,6 @@ Caches can be employed to store the results of tools' execution, making the proc
|
||||
|
||||
After the crew execution, you can access the `usage_metrics` attribute to view the language model (LLM) usage metrics for all tasks executed by the crew. This provides insights into operational efficiency and areas for improvement.
|
||||
|
||||
`total_tokens` is the billed total (`prompt_tokens + completion_tokens`). Breakdown fields such as `cached_prompt_tokens` and `cache_creation_tokens` describe subsets already included in those totals and are not added on top of `total_tokens`. See the **UsageMetrics field semantics** section in the Flows concept documentation for the full contract.
|
||||
|
||||
```python Code
|
||||
# Access the crew's usage metrics
|
||||
crew = Crew(agents=[agent1, agent2], tasks=[task1, task2])
|
||||
|
||||
@@ -267,27 +267,7 @@ print(flow.usage_metrics)
|
||||
execution.
|
||||
</Note>
|
||||
|
||||
### UsageMetrics field semantics
|
||||
|
||||
The returned [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) object uses a provider-neutral contract:
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `total_tokens` | Billed total: `prompt_tokens + completion_tokens` |
|
||||
| `prompt_tokens` | Full input/prompt tokens billed for the request |
|
||||
| `completion_tokens` | Output/completion tokens billed for the request |
|
||||
| `cached_prompt_tokens` | Cache-read subset of prompt tokens (breakdown only) |
|
||||
| `cache_creation_tokens` | Cache-write subset of prompt tokens (breakdown only, Anthropic) |
|
||||
| `reasoning_tokens` | Reasoning/thinking subset where the provider reports it separately (breakdown only) |
|
||||
| `successful_requests` | Number of LLM calls aggregated |
|
||||
|
||||
Breakdown fields such as `cached_prompt_tokens`, `cache_creation_tokens`, and
|
||||
`reasoning_tokens` are **not** added on top of `total_tokens` — they describe
|
||||
portions already included in `prompt_tokens` or `completion_tokens`.
|
||||
|
||||
For Anthropic, cache read and cache write counters are folded into `prompt_tokens`, so cached workloads are fully reflected in `total_tokens`. OpenAI-style providers already include cached input inside `prompt_tokens`; CrewAI surfaces the cached portion separately for visibility.
|
||||
|
||||
Each entry in the returned `UsageMetrics` is the sum across all LLM calls made within a single `flow.kickoff()` invocation. Counters reset on the next `kickoff()` call (or on each iteration of `kickoff_for_each`), so successive runs don't double-count. The property is safe to read at any point after `kickoff()` completes; reading it during execution returns the partial total accumulated so far.
|
||||
Each entry in the returned [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) is the sum across all LLM calls made within a single `flow.kickoff()` invocation. Counters reset on the next `kickoff()` call (or on each iteration of `kickoff_for_each`), so successive runs don't double-count. The property is safe to read at any point after `kickoff()` completes; reading it during execution returns the partial total accumulated so far.
|
||||
|
||||
## Flow State Management
|
||||
|
||||
|
||||
@@ -418,27 +418,6 @@ In this section, you'll find detailed examples that help you select, configure,
|
||||
- Token usage tracking
|
||||
- Multi-turn tool use conversations
|
||||
|
||||
**Token usage and prompt caching:**
|
||||
|
||||
Anthropic reports billed input in separate counters — `input_tokens` (uncached),
|
||||
`cache_read_input_tokens`, and `cache_creation_input_tokens`. CrewAI folds all
|
||||
three into `prompt_tokens` (and native `input_tokens` in provider responses) so
|
||||
`total_tokens` reflects full billed usage on cached workloads.
|
||||
|
||||
`cached_prompt_tokens` records the cache-read portion as a breakdown only; it is
|
||||
already included in `prompt_tokens` and must not be added again to
|
||||
`total_tokens`. `cache_creation_tokens` records cache writes the same way.
|
||||
|
||||
```python Code
|
||||
usage = llm.get_token_usage_summary()
|
||||
# total_tokens == prompt_tokens + completion_tokens
|
||||
# prompt_tokens includes cache read + cache write for Anthropic
|
||||
```
|
||||
|
||||
See the **UsageMetrics field semantics** section in the Flows concept
|
||||
documentation for the provider-neutral contract used by `crew.usage_metrics`
|
||||
and `flow.usage_metrics`.
|
||||
|
||||
**Important Notes:**
|
||||
- `max_tokens` is a **required** parameter for all Anthropic models
|
||||
- Claude uses `stop_sequences` instead of `stop`
|
||||
|
||||
@@ -740,7 +740,7 @@ memory = Memory(llm="anthropic/claude-3-haiku-20240307")
|
||||
memory = Memory(llm="ollama/llama3.2")
|
||||
|
||||
# Use Google Gemini
|
||||
memory = Memory(llm="gemini/gemini-3.7-flash")
|
||||
memory = Memory(llm="gemini/gemini-2.0-flash")
|
||||
|
||||
# Pass a pre-configured LLM instance with custom settings
|
||||
llm = LLM(model="gpt-4o", temperature=0)
|
||||
|
||||
@@ -32,10 +32,10 @@ You often need **both**: skills for expertise, tools for action. They are config
|
||||
The CLI is the supported way to create a skill — it scaffolds the directory layout and a valid `SKILL.md` for you:
|
||||
|
||||
```shell Terminal
|
||||
crewai create skill code-review
|
||||
crewai skill create code-review
|
||||
```
|
||||
|
||||
Inside a crew project (where `pyproject.toml` lives) this creates `./skills/code-review/`; outside a project it creates `./code-review/` in the current directory (you can force that behavior with `--no-project` on `crewai create skill`):
|
||||
Inside a crew project (where `pyproject.toml` lives) this creates `./skills/code-review/`; outside a project it creates `./code-review/` in the current directory (you can force that behavior with `--no-project`):
|
||||
|
||||
```
|
||||
skills/
|
||||
@@ -178,16 +178,12 @@ agent = Agent(
|
||||
|
||||
## Creating, Publishing, and Installing Skills
|
||||
|
||||
Skills have a full lifecycle managed by the CLI: **create them with `crewai create skill`, publish them with `crewai skill publish`** — hand-rolling directories works for local experiments, but the CLI is the intended workflow and keeps your skill layout and frontmatter valid.
|
||||
|
||||
<Note>
|
||||
`crewai skill create` is deprecated and still works with a warning. Use `crewai create skill` instead.
|
||||
</Note>
|
||||
Skills have a full lifecycle managed by the CLI: **create them with `crewai skill create`, publish them with `crewai skill publish`** — hand-rolling directories works for local experiments, but the CLI is the intended workflow and keeps your skill layout and frontmatter valid.
|
||||
|
||||
### Create
|
||||
|
||||
```shell Terminal
|
||||
crewai create skill my-skill
|
||||
crewai skill create my-skill
|
||||
```
|
||||
|
||||
Scaffolds the directory (into `./skills/` inside a crew project) with a template `SKILL.md`, plus empty `scripts/`, `references/`, and `assets/` directories. Edit `SKILL.md` to define the instructions.
|
||||
|
||||
@@ -20,7 +20,7 @@ crewai test
|
||||
If you want to run more iterations or use a different model, you can specify the parameters like this:
|
||||
|
||||
```bash
|
||||
crewai test --n-iterations 5 --model gpt-4o
|
||||
crewai test --n_iterations 5 --model gpt-4o
|
||||
```
|
||||
|
||||
or using the short forms:
|
||||
@@ -29,11 +29,6 @@ or using the short forms:
|
||||
crewai test -n 5 -m gpt-4o
|
||||
```
|
||||
|
||||
<Note>
|
||||
The older `--n_iterations` flag still works but is deprecated and hidden from
|
||||
`--help`. Use `--n-iterations` (or `-n`) instead.
|
||||
</Note>
|
||||
|
||||
When you run the `crewai test` command, the crew will be executed for the specified number of iterations, and the performance metrics will be displayed at the end of the run.
|
||||
|
||||
A table of scores at the end will show the performance of the crew in terms of the following metrics:
|
||||
|
||||
@@ -21,13 +21,9 @@ crewai create crew my_crew
|
||||
crewai create flow my_flow
|
||||
|
||||
# Tool repository
|
||||
crewai create tool my_tool
|
||||
crewai tool create my_tool
|
||||
```
|
||||
|
||||
<Note>
|
||||
`crewai tool create` is deprecated and still works with a warning. Use `crewai create tool` instead.
|
||||
</Note>
|
||||
|
||||
## Tool Setup: Point Assistants to AGENTS.md
|
||||
|
||||
### Codex
|
||||
|
||||
@@ -77,7 +77,7 @@ Replace the generated `agents/researcher.jsonc` file and add `agents/analyst.jso
|
||||
}
|
||||
```
|
||||
|
||||
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, or `gemini/gemini-3.7-flash`.
|
||||
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, or `gemini/gemini-2.0-flash-001`.
|
||||
|
||||
## Step 3: Define Tasks and Crew Settings
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Conversational Flows
|
||||
description: Build multi-turn chat apps with handle_turn per turn, message history, intent routing, tracing, and structured streaming.
|
||||
description: Build multi-turn chat apps with handle_turn per turn, message history, intent routing, tracing, and WebSocket bridges.
|
||||
icon: comments
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Conversational apps treat each user line as a **new flow run** with the **same session id**. CrewAI adds helpers for message history, optional intent routing, deferred tracing, structured turn streaming, and a local `flow.chat()` REPL.
|
||||
Conversational apps treat each user line as a **new flow run** with the **same session id**. CrewAI adds helpers for message history, optional intent routing, deferred tracing, UI bridges, and a local `flow.chat()` REPL for conversational flows.
|
||||
|
||||
| Concept | Implementation |
|
||||
|---------|----------------|
|
||||
| Session id | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
|
||||
| User line | `handle_turn(message)` appends to `state.messages` before the graph runs |
|
||||
| Turn complete | `conversation_turn_completed`; with default trace deferral, `FlowFinished` waits for `finalize_session_traces()` |
|
||||
| Turn complete | `FlowFinished` for **this run** only; chat continues on the next `handle_turn` |
|
||||
| Full-session trace | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
|
||||
|
||||
## Turn APIs
|
||||
@@ -30,8 +30,7 @@ Use **`flow.handle_turn(message, session_id=...)`** for every user message from
|
||||
| `kickoff(inputs={...})` | Advanced flow execution without conversational turn handling |
|
||||
| `ask()` | Blocking prompt **inside** one step (wizard, clarification) |
|
||||
| `@human_feedback` | Approve/reject **a step output** — not the next chat line |
|
||||
|
||||
`handle_turn()`, `stream_turn()`, and `chat()` raise `ValueError` unless conversational mode is enabled. Applying `@ConversationConfig(...)` enables it automatically; otherwise set `conversational = True`.
|
||||
| `ChatSession.handle_turn(...)` | Transport layer over `handle_turn` (SSE / WebSocket) |
|
||||
|
||||
## Quick start
|
||||
|
||||
@@ -40,7 +39,7 @@ from uuid import uuid4
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
)
|
||||
@@ -48,6 +47,8 @@ from crewai.flow import (
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
def route_turn(self, context):
|
||||
message = (self.state.current_user_message or "").lower()
|
||||
if "order" in message:
|
||||
@@ -95,12 +96,12 @@ stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
print(frame.data.get("chunk", ""), end="", flush=True)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
|
||||
For the full frame contract and channel list, see [Streaming Runtime Contract](/edge/en/learn/streaming-runtime-contract).
|
||||
For the full frame contract, channel list, and async API, see [Streaming Runtime Contract](/edge/en/learn/streaming-runtime-contract).
|
||||
|
||||
## Turn lifecycle
|
||||
|
||||
@@ -110,18 +111,29 @@ Each `handle_turn` runs this pipeline:
|
||||
2. **State restore** — if `inputs["id"]` exists and `@persist` is configured, loads the latest snapshot.
|
||||
3. **`FlowStarted`** — emitted on the first deferred session turn only.
|
||||
4. **Pending turn hydration** — appends the user message to `state.messages`, sets `current_user_message` / `last_user_message`, and optionally classifies when `intents` / `default_intents` + `intent_llm` are set.
|
||||
5. **Graph execution** — user-defined `@start` methods (if any) → `route_conversation` (the built-in start/router) → the selected `@listen` handler. `route_conversation` also calls the overridable `conversation_start()` helper.
|
||||
5. **Graph execution** — `conversation_start` → `route_conversation` → the selected `@listen` handler.
|
||||
6. **End of run** — per-turn `flow_finished` and trace finalization are **skipped** when deferral is enabled; nested `Agent.kickoff()` / crews do not close the parent batch either.
|
||||
|
||||
Handlers should call **`append_assistant_message(reply)`** when the visible reply is not the return value, or when you trim history. A public string return is also recorded as assistant and included in the `@persist` snapshot, so a fresh Flow instance restores it. The user line is already stored by `handle_turn` — do not append it again in handlers.
|
||||
Handlers should call **`append_assistant_message(reply)`** so the next turn’s `conversation_messages` includes assistant text. The user line is already stored by `handle_turn` — do not append it again in handlers.
|
||||
|
||||
## Configuration overview
|
||||
## `ConversationConfig` (class-level defaults)
|
||||
|
||||
Decorating a `Flow` subclass with `ConversationConfig` both attaches the chat defaults and enables conversational mode. See the [full field reference](#conversationconfig) below. Override pre-classification per turn with `handle_turn(..., intents=..., intent_llm=...)`.
|
||||
Decorate your conversational `Flow` subclass with `ConversationConfig`.
|
||||
|
||||
| Field | Default | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `system_prompt` | Framework default | System message used by the built-in `converse_turn`. |
|
||||
| `llm` | `None` | Conversation LLM used by `converse_turn` and as router fallback. |
|
||||
| `router` | `None` | `RouterConfig` for LLM-driven routing. |
|
||||
| `intent_llm` | `None` | LLM for `intents=` / `default_intents` pre-classification. |
|
||||
| `default_intents` | `None` | Outcome labels for pre-classification. |
|
||||
| `defer_trace_finalization` | `True` | Keep one trace batch open across `handle_turn()` calls. |
|
||||
|
||||
Override pre-classification per turn with `handle_turn(..., intents=..., intent_llm=...)`.
|
||||
|
||||
## Lower-level `ChatState` helpers
|
||||
|
||||
`ChatState`, the legacy `ConversationalConfig`, and `crewai.flow.conversation` helpers are still importable for advanced orchestration, tests, or custom wrappers. They are separate from the `ConversationState` / `ConversationConfig` API and do not add `user_message=` or `session_id=` keyword arguments to `Flow.kickoff()`.
|
||||
`ChatState`, `ConversationalConfig`, and `crewai.flow.conversation` helpers are still importable for advanced orchestration, tests, or custom wrappers. They do not add `user_message=` or `session_id=` keyword arguments to `Flow.kickoff()`.
|
||||
|
||||
```python
|
||||
from crewai.flow import ChatState
|
||||
@@ -143,8 +155,6 @@ class MyChatState(ChatState):
|
||||
|
||||
`ConversationalInputs` is a `TypedDict` for conventional `kickoff(inputs={...})` keys: `id`, `user_message`, `last_intent`.
|
||||
|
||||
`ConversationState` stores `messages` as `ConversationMessage` objects and additionally provides `current_user_message`, `ended`, `events`, and `agent_threads`. Use `conversation_messages` when passing its canonical history to an LLM.
|
||||
|
||||
## `Flow` conversational API
|
||||
|
||||
### `handle_turn` parameters
|
||||
@@ -166,9 +176,9 @@ class MyChatState(ChatState):
|
||||
| Attribute | Purpose |
|
||||
|-----------|---------|
|
||||
| `conversational` | Set to `True` to enable the conversational graph and `handle_turn()` |
|
||||
| `defer_trace_finalization` | Optional instance override. Otherwise `_should_defer_trace_finalization()` reads `ConversationConfig.defer_trace_finalization`. |
|
||||
| `suppress_flow_events` | Hides console flow panels and suppresses method execution events; flow start/finish events still emit |
|
||||
| `stream` | Generic Flow streaming flag. For conversational turns, use `stream_turn()` instead of combining this flag with `handle_turn()`. |
|
||||
| `defer_trace_finalization` | Instance flag; set automatically from config on `handle_turn()` |
|
||||
| `suppress_flow_events` | Hides console flow panels; **tracing still records** method/flow events |
|
||||
| `stream` | Enable streaming; use with `ChatSession.handle_turn(..., stream=True)` |
|
||||
|
||||
### Methods and properties
|
||||
|
||||
@@ -180,12 +190,12 @@ class MyChatState(ChatState):
|
||||
| `classify_intent(text, outcomes, *, llm, context=None)` | Map text to one outcome (same collapse logic as `@human_feedback`) |
|
||||
| `receive_user_message(text, *, outcomes=None, llm=None)` | Append user message; optionally set `last_intent` |
|
||||
| `finalize_session_traces()` | Emit deferred `flow_finished` and finalize the session trace batch |
|
||||
| `_should_defer_trace_finalization()` | Advanced/internal hook that resolves whether per-turn trace finalization is deferred |
|
||||
| `_should_defer_trace_finalization()` | Whether this flow defers per-turn trace finalization |
|
||||
| `input_history` | Audit trail of `ask()` prompts and responses |
|
||||
|
||||
### Module helpers (`crewai.flow.conversation`)
|
||||
|
||||
Importable from `crewai.flow.conversation` for tests or custom orchestration. These helpers use the legacy `ConversationalConfig` shape; `prepare_conversational_turn()` also clears `last_intent`, unlike `handle_turn()`, which preserves it as router context.
|
||||
Importable for tests or custom orchestration:
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
@@ -202,7 +212,7 @@ Importable from `crewai.flow.conversation` for tests or custom orchestration. Th
|
||||
|
||||
### A. Pre-classify via `ConversationConfig` (simplest)
|
||||
|
||||
Set `default_intents` and `intent_llm`. Each `handle_turn()` pre-classifies the current message. A non-empty result returned by a custom `route_turn()` takes precedence; otherwise `route_conversation` uses the current turn's classified intent.
|
||||
Set `default_intents` and `intent_llm`. Each `handle_turn()` runs classification before routing; read `self.state.last_intent` in `route_turn()`.
|
||||
|
||||
### B. Classify inside `route_turn` (richer prompts)
|
||||
|
||||
@@ -223,15 +233,24 @@ Use **`@listen("RESEARCH")`** (or similar) for steps that run `Agent.kickoff()`
|
||||
|
||||
## When the flow finishes but the user keeps chatting
|
||||
|
||||
Each `handle_turn()` completes one graph run, and the conversation continues with another `handle_turn()` using the same `session_id`. With the default deferred trace lifecycle, that run emits `conversation_turn_completed`, while `FlowFinished` is emitted once when `finalize_session_traces()` closes the session. `@persist` restores `messages`, flags, and context.
|
||||
`FlowFinished` means **this graph run** completed. The conversation continues with another `handle_turn()` and the same `session_id`. `@persist` restores `messages`, flags, and context.
|
||||
|
||||
**Persist pattern:** prefer `@persist` on a **single terminal step** (for example `finalize`) rather than on the whole `Flow` class. Class-level persist saves after every method; `load_state` uses the latest row, which may be a mid-run snapshot (for example right after `bootstrap`) and miss handler updates from the same turn.
|
||||
|
||||
Do **not** use `@human_feedback` for follow-up chat lines unless a human must approve a specific step output before it is shown.
|
||||
|
||||
## Conversational `Flow`
|
||||
## Conversational `Flow` (experimental)
|
||||
|
||||
Opt into the conversational chat graph by setting `conversational = True` on a `Flow` subclass or applying `@ConversationConfig(...)`. The base `Flow` then supplies `route_conversation` as the built-in start/router plus the `converse_turn` and `end_conversation` listeners. The deprecated `answer_from_history_turn` listener remains available for compatibility. The framework manages `state.messages`, can drive a router LLM, and keeps the trace batch open across turns. You write the **custom routes**; the framework owns the rest.
|
||||
<Warning>
|
||||
**This is an experimental feature.** The conversational `Flow` surface
|
||||
(`conversational = True`, `handle_turn`, `ConversationConfig`,
|
||||
`RouterConfig`, `ConversationState`, the built-in graph + helpers) lives
|
||||
under `crewai.experimental` and may change shape before it graduates.
|
||||
Pin your CrewAI version if you depend on specific behavior, and watch the
|
||||
changelog for breaking updates. Open issues / feedback welcome.
|
||||
</Warning>
|
||||
|
||||
Opt into the conversational chat graph by setting `conversational = True` on a `Flow` subclass. The base `Flow` then ships a built-in `@start` / `@router` / `converse_turn` / `end_conversation` graph, manages `state.messages`, can drive a router LLM, and keeps the trace batch open across turns. You write the **custom routes**; the framework owns the rest.
|
||||
|
||||
Use this when you want a multi-turn chat with a router and per-route handlers without wiring the lifecycle yourself. Use `Flow[ChatState]` (the lower-level pattern above) when you need full control.
|
||||
|
||||
@@ -240,7 +259,7 @@ Use this when you want a multi-turn chat with a router and per-route handlers wi
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
)
|
||||
@@ -248,6 +267,8 @@ from crewai.flow import (
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
def route_turn(self, context: dict) -> str | None:
|
||||
message = (self.state.current_user_message or "").lower()
|
||||
if "search" in message or "news" in message:
|
||||
@@ -297,26 +318,14 @@ Class decorator that attaches per-class chat defaults.
|
||||
|-------|---------|---------|
|
||||
| `system_prompt` | `slices.conversational_system_prompt` from i18n | System message used by the built-in `converse_turn`. Pass `""` to opt out entirely. |
|
||||
| `llm` | `None` | Conversation LLM (used by `converse_turn` and as router fallback). |
|
||||
| `router` | `None` | Optional `RouterConfig` overrides. With custom listeners and a resolvable LLM, routing auto-enables even when this is omitted. |
|
||||
| `answer_from_history_prompt` | Framework default | **Deprecated.** Use the `converse` system prompt or override `converse_turn()`. |
|
||||
| `answer_from_history_llm` | `None` | **Deprecated.** Use `llm`; `converse` already receives canonical history. |
|
||||
| `router` | `None` | `RouterConfig` for LLM-driven routing. Without it, the flow always falls through to `converse`. |
|
||||
| `answer_from_history_prompt` | Framework default | System message for the optional `answer_from_history` route. |
|
||||
| `answer_from_history_llm` | `None` | Enables the `answer_from_history` short-circuit when set. |
|
||||
| `intent_llm` | `None` | LLM for legacy `intents=`/`default_intents` pre-classification. |
|
||||
| `default_intents` | `None` | Outcome labels for legacy pre-classification. |
|
||||
| `visible_agent_outputs` | `None` | `"all"`, or a list of agent names whose `append_agent_result()` calls should be promoted to public assistant messages. |
|
||||
| `defer_trace_finalization` | `True` | Keep one trace batch open across `handle_turn()` calls. |
|
||||
|
||||
<Warning>
|
||||
`answer_from_history_prompt`, `answer_from_history_llm`, and the
|
||||
`answer_from_history` route are deprecated and will be removed in a future
|
||||
release. They duplicate `converse`, add an eligibility LLM call, and are
|
||||
bypassed when the normal auto-router returns a route. Existing configurations
|
||||
continue to work and emit `DeprecationWarning`.
|
||||
</Warning>
|
||||
|
||||
With no custom routes, turns fall through to `converse`. With custom routes and a conversation/router LLM, the framework synthesizes a default `RouterConfig`; provide one explicitly only to customize its prompt, route list, descriptions, or fallback behavior. Setting `default_intents` uses the legacy pre-classification path instead.
|
||||
|
||||
If no conversation LLM is configured, the built-in `converse_turn` returns a configuration placeholder rather than generating an answer.
|
||||
|
||||
### `RouterConfig` and the auto-built route catalog
|
||||
|
||||
```python
|
||||
@@ -325,7 +334,7 @@ from typing import Literal
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai import LLM
|
||||
from crewai.flow import RouterConfig
|
||||
from crewai.experimental.conversational import RouterConfig
|
||||
|
||||
|
||||
class MyRoute(BaseModel):
|
||||
@@ -351,10 +360,9 @@ router_config = RouterConfig(
|
||||
The router prompt that gets sent to the LLM is built automatically. For each route the framework picks a description with this precedence:
|
||||
|
||||
1. `RouterConfig.route_descriptions[label]` — explicit override.
|
||||
2. `Flow.builtin_route_descriptions[label]` — framework-canned text for `converse`, `end`, and the deprecated `answer_from_history` compatibility route (phrased for the router LLM).
|
||||
3. The method's declared `description` (used by declarative flows and DSL projections).
|
||||
4. First non-empty line of the `@listen(label)` handler's docstring.
|
||||
5. Empty (the route is listed without a description).
|
||||
2. `Flow.builtin_route_descriptions[label]` — framework-canned text for `converse`, `end`, `answer_from_history` (phrased for the router LLM).
|
||||
3. First non-empty line of the `@listen(label)` handler's docstring.
|
||||
4. Empty (the route is listed without a description).
|
||||
|
||||
So in practice, **adding a new route is `@listen("X")` + a one-line docstring**:
|
||||
|
||||
@@ -368,27 +376,6 @@ def handle_internet_search(self) -> str:
|
||||
...
|
||||
```
|
||||
|
||||
### Naming handlers
|
||||
|
||||
The string in `@listen("…")` is a **router route label** (an event name), not the Python method name. Route labels and method completion events share one trigger namespace, so naming a handler the same as its route causes the handler to re-trigger itself in a loop.
|
||||
|
||||
Use a different method name — the docs examples use a `handle_*` prefix:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def handle_create_video(self) -> str:
|
||||
"""User wants a new video."""
|
||||
...
|
||||
```
|
||||
|
||||
Do **not** mirror the route label on the method:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def create_video(self) -> str: # rejected at flow instantiation
|
||||
...
|
||||
```
|
||||
|
||||
…and the router LLM sees:
|
||||
|
||||
```
|
||||
@@ -407,7 +394,7 @@ Routes:
|
||||
|-------|---------|---------|
|
||||
| `converse` | `converse_turn` | Default chat handler. Calls `ConversationConfig.llm` with the system prompt + canonical message history. |
|
||||
| `end` | `end_conversation` | Sets `state.ended = True` and emits a terminator reply. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | **Deprecated compatibility route.** Use `converse`, which already receives canonical history. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | Optional. Routes here when `ConversationConfig.answer_from_history_llm` is set and the message can be answered from existing history. |
|
||||
|
||||
You can override any of these by defining a same-named handler in your subclass.
|
||||
|
||||
@@ -417,9 +404,9 @@ You can override any of these by defining a same-named handler in your subclass.
|
||||
|
||||
1. Resets per-execution tracking (`_completed_methods`, `_method_outputs`) so the graph re-runs — without this, repeated `kickoff` calls on the same flow instance would short-circuit on turn 2+ because `Flow.kickoff_async` treats `inputs={"id": ...}` as a checkpoint restore.
|
||||
2. Appends the user message to `state.messages`, sets `current_user_message` / `last_user_message`. `last_intent` is **preserved from the prior turn** so the router LLM can use it as a signal.
|
||||
3. Runs user-defined `@start` methods (if any), then `route_conversation` as the built-in start/router, then the chosen `@listen` handler. `route_conversation` invokes the overridable `conversation_start()` helper.
|
||||
3. Runs `conversation_start` → `route_conversation` → the chosen `@listen` handler.
|
||||
4. The router stores its decision in `state.last_intent` (visible to the next turn's router context).
|
||||
5. If your handler returned a string and didn't already call `append_assistant_message`, `handle_turn` appends it for you and persists the updated `state.messages` so `@persist` restore includes the assistant turn.
|
||||
5. If your handler returned a string and didn't already call `append_assistant_message`, `handle_turn` appends it for you.
|
||||
|
||||
Call `handle_turn()` for chat messages. Calling `kickoff(inputs={"id": ...})` directly runs the flow graph without applying the conversational turn wrapper.
|
||||
|
||||
@@ -440,8 +427,6 @@ It handles the common local loop:
|
||||
4. Prints the assistant result.
|
||||
5. Finalizes deferred session traces in a `finally` block.
|
||||
|
||||
`chat(defer_trace_finalization=True)` temporarily enables the instance deferral flag for the REPL and restores its prior value on exit.
|
||||
|
||||
Customize the terminal behavior with injectable I/O:
|
||||
|
||||
```python
|
||||
@@ -463,7 +448,7 @@ To run side effects (event bus setup, telemetry) on every routing decision, over
|
||||
from typing import Any
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationState
|
||||
from crewai.experimental.conversational import ConversationState
|
||||
|
||||
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
@@ -474,7 +459,7 @@ class SupportFlow(Flow[ConversationState]):
|
||||
return super().route_turn(context)
|
||||
```
|
||||
|
||||
To bypass the LLM router entirely and pick a route programmatically, return a non-empty string from `route_turn`. A falsy return does **not** invoke `_route_with_config()` from your override; routing falls through to this turn's pre-classified intent, then the deprecated `answer_from_history` compatibility path when configured, and finally `converse`. A previous turn's `last_intent` is available in router context but is never replayed as a fallback.
|
||||
To bypass the LLM router entirely and pick a route programmatically, return a string from `route_turn`; returning `None` falls back to `_route_with_config(...)`.
|
||||
|
||||
### `append_assistant_message` and `append_agent_result`
|
||||
|
||||
@@ -485,73 +470,6 @@ Inside a `@listen(label)` handler, choose:
|
||||
|
||||
`ConversationConfig.visible_agent_outputs` can promote specific agents' private results to public globally (`"all"`, or a list of agent names).
|
||||
|
||||
## Declaring a conversational flow in JSON/YAML
|
||||
|
||||
A [declarative Flow](/edge/en/concepts/cli) can be conversational too. Add a top-level `conversational` block and declare your own routes as methods that `listen` to a route label:
|
||||
|
||||
```yaml
|
||||
schema: crewai.flow/v1
|
||||
name: SupportFlow
|
||||
|
||||
conversational:
|
||||
system_prompt: You are a terse support assistant.
|
||||
llm: gpt-4o-mini
|
||||
router:
|
||||
llm: gpt-4o-mini
|
||||
|
||||
methods:
|
||||
handle_order:
|
||||
description: Order status, shipping and delivery questions.
|
||||
listen: order
|
||||
do:
|
||||
call: agent
|
||||
with:
|
||||
role: Support specialist
|
||||
goal: Answer order questions accurately
|
||||
backstory: Knows the fulfilment pipeline.
|
||||
input: "${state.current_user_message}"
|
||||
```
|
||||
|
||||
Declaring the block is the opt-in — `enabled` defaults to `true`. Set `enabled: false` to keep the configuration while turning chat off. This also disables built-in method synthesis, so the declaration must provide a normal non-conversational graph.
|
||||
|
||||
Three things are supplied for you:
|
||||
|
||||
| Supplied | Detail |
|
||||
|----------|--------|
|
||||
| The built-in graph | `route_conversation`, `converse_turn`, and `end_conversation` are added automatically. Deprecated `answer_from_history_turn` is retained for compatibility. Declare a method under one of those names to override it. |
|
||||
| Conversation state | `ConversationState` is used when there is no `state` block. A Pydantic `ref` or `json_schema` state is automatically composed with the conversational fields; it does not need to extend `ConversationState`. |
|
||||
| The route catalog | Inferred from non-router methods with `listen` labels, excluding internal routes. Descriptions follow the precedence above, and explicit `router.routes` can limit the choices. |
|
||||
|
||||
Declarative `llm`, `router.llm`, and `intent_llm` fields accept either a model id or a configuration mapping such as `{model: openai/gpt-4o-mini, max_tokens: 512}`. The `conversational` block also supports `default_intents`, `visible_agent_outputs`, `defer_trace_finalization`, and the `RouterConfig` fields shown above. Deprecated `answer_from_history_prompt` / `answer_from_history_llm` declarations remain accepted for compatibility.
|
||||
|
||||
Run it from Python with the same turn APIs as a class-based conversational Flow:
|
||||
|
||||
```python
|
||||
from crewai.flow import Flow
|
||||
|
||||
flow = Flow.from_declaration(path="flow.yaml")
|
||||
|
||||
try:
|
||||
flow.handle_turn("Where is my order?", session_id="session-1")
|
||||
finally:
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
|
||||
### Naming routes
|
||||
|
||||
Route labels and method names share one trigger namespace, so a handler must not be named after the route it listens to — `create_video` listening to `create_video` is rejected when the flow is built. Use a `handle_*` prefix.
|
||||
|
||||
### What a declaration cannot express
|
||||
|
||||
| Not expressible | Use instead |
|
||||
|-----------------|-------------|
|
||||
| A live `LLM` instance or a custom `BaseLLM` | A model id string or static configuration mapping |
|
||||
| `router.response_format` as a live model class | Name the class with a python ref: `response_format: {python: my_project.schemas.ConversationRoute}`. Omit it and the framework synthesizes one |
|
||||
| A `route_turn()` override | Author the Flow in Python, or replace the declarative `route_conversation` method with a `call: code` / expression action |
|
||||
| A `can_answer_from_history()` override | Deprecated. Use `converse` or override `converse_turn()` in Python. |
|
||||
|
||||
`crewai run` opens the chat TUI for a declarative conversational flow — the same one a Python conversational Flow gets. A chat loop needs a terminal, so a headless run exits non-zero with guidance instead of running a single turn; drive it from Python there with `handle_turn()` or `stream_turn()`. A declarative method with a `human_feedback:` block (Python: `@human_feedback`) runs on a terminal REPL, because the runtime collects feedback with a blocking prompt the TUI cannot service. `--inputs` is not accepted for a conversational flow — each turn's input is the message you type — and resuming a session by id is not wired into the CLI yet; use `flow.handle_turn(message, session_id=...)` from Python for that.
|
||||
|
||||
## Tracing across turns
|
||||
|
||||
With `defer_trace_finalization=True` (default in `ConversationConfig`):
|
||||
@@ -569,28 +487,15 @@ flow.chat(session_id=session_id)
|
||||
with `handle_turn()`, call `finalize_session_traces()` when
|
||||
the session ends.
|
||||
|
||||
`suppress_flow_events=True` hides Rich console panels and suppresses method execution events. Flow start/finish events still emit, so the outer Flow lifecycle remains traceable, but individual method spans are omitted.
|
||||
`suppress_flow_events=True` only hides Rich console panels; trace and method events still emit for observability.
|
||||
|
||||
### Conversational `Flow` trace lifecycle
|
||||
|
||||
The [conversational `Flow`](#conversational-flow) uses the same tracing lifecycle: `defer_trace_finalization` defaults to `True`, so each `handle_turn()` keeps the session trace open. Deferred turns also suppress per-turn `flow_failed`; on a turn error or session abort, finalize the session explicitly. This closes the batch with the session-level `FlowFinished` event rather than a per-turn `FlowFailed` event. Always wrap your REPL/loop in `try/finally` and call `flow.finalize_session_traces()` on exit. Without it, the trace batch stays open and the final conversation may never export.
|
||||
The experimental [conversational `Flow`](#conversational-flow-experimental) uses the same tracing lifecycle: `defer_trace_finalization` defaults to `True`, so each `handle_turn()` keeps the session trace open. Always finalize at the end of the session — wrap your REPL/loop in `try/finally` and call `flow.finalize_session_traces()` on exit. Without it, the trace batch stays open and the final conversation may never export.
|
||||
|
||||
## Streaming
|
||||
|
||||
For conversational UIs, use `stream_turn()` and iterate its ordered `StreamFrame` objects:
|
||||
|
||||
```python
|
||||
stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
reply = stream.result
|
||||
```
|
||||
|
||||
For a non-conversational Flow, setting `stream = True` makes `kickoff()` return a `StreamSession`. Do not set `flow.stream = True` when using `handle_turn()`; `stream_turn()` owns the conversational streaming lifecycle.
|
||||
Set `stream = True` on the `Flow` class. `kickoff(...)` will then emit `assistant_delta` (and related) events through the standard event bus.
|
||||
|
||||
## Imports
|
||||
|
||||
@@ -605,15 +510,10 @@ from crewai.flow import (
|
||||
router,
|
||||
start,
|
||||
)
|
||||
from crewai.flow.conversation import prepare_conversational_turn
|
||||
from crewai.flow import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
RouterConfig,
|
||||
)
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Mastering Flow State Management](/en/guides/flows/mastering-flow-state) — persistence, Pydantic state, `@persist`
|
||||
- [Build Your First Flow](/en/guides/flows/first-flow) — flow basics
|
||||
- Demo: `lib/crewai/runner_conversational_flow_simple.py` — minimal REPL with `RESEARCH` + Exa agent
|
||||
|
||||
@@ -136,7 +136,7 @@ Now, let's configure the content writer crew with JSONC. We'll set up two specia
|
||||
}
|
||||
```
|
||||
|
||||
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `gemini/gemini-3.7-flash`, or `anthropic/claude-sonnet-4-6`.
|
||||
Replace `provider/model-id` with the model you use, for example `openai/gpt-4o`, `gemini/gemini-2.0-flash-001`, or `anthropic/claude-sonnet-4-6`.
|
||||
|
||||
3. Create `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
|
||||
|
||||
@@ -483,7 +483,7 @@ Flows allow you to make direct calls to language models when you need simple, st
|
||||
|
||||
```python
|
||||
llm = LLM(
|
||||
model="model-id-here", # gpt-4o, gemini/gemini-3.7-flash, anthropic/claude...
|
||||
model="model-id-here", # gpt-4o, gemini-2.0-flash, anthropic/claude...
|
||||
response_format=GuideOutline
|
||||
)
|
||||
response = llm.call(messages=messages)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
title: A2UI
|
||||
description: The declarative tier of generative UI — the agent assembles a surface from a catalog of components you own.
|
||||
icon: table-cells
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## The agent assembles the UI
|
||||
|
||||
[Tool-based rendering](/edge/en/guides/frontend/tool-based-generative-ui) maps one tool to one component: the agent picks a component, you draw it. A2UI is the **declarative** tier of the [generative-UI spectrum](/edge/en/guides/frontend/generative-ui#declarative) — instead of picking a single component, the agent **assembles a surface** by combining building blocks from a catalog you define.
|
||||
|
||||
You still own the components. The agent can only use what is in your catalog, so it can never render something you did not ship. What the agent decides is the **layout and the data** — how those building blocks come together into a panel, and what goes in them.
|
||||
|
||||
<Note>
|
||||
A2UI works with [Flows](/en/concepts/flows). Both modes below — dynamic and fixed-schema — run as Flows served over AG-UI, exactly like the rest of this section.
|
||||
</Note>
|
||||
|
||||
## The catalog (same for every mode)
|
||||
|
||||
The frontend wiring is identical no matter which backend mode you use: you register a **catalog** on the `<CopilotKit>` provider with the `a2ui` prop.
|
||||
|
||||
```tsx
|
||||
import { CopilotKit } from "@copilotkit/react-core";
|
||||
import { catalog } from "@/a2ui-catalog";
|
||||
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="assistant" a2ui={{ catalog }}>
|
||||
{/* ... */}
|
||||
</CopilotKit>
|
||||
```
|
||||
|
||||
The catalog is your set of React components keyed by a catalog id — a `FlightCard`, a `HotelCard`, a `Chart`, whatever your app needs. The agent references catalog ids; CopilotKit paints your components with the data the agent supplies.
|
||||
|
||||
<Note>
|
||||
Authoring the catalog itself — the id schema, prop mapping, and composition rules — is deeper than this page covers. See the [CopilotKit A2UI docs](https://docs.copilotkit.ai) for the full authoring reference. Here we focus on the two backend modes and when to reach for each.
|
||||
</Note>
|
||||
|
||||
## Two backend modes
|
||||
|
||||
A2UI backends come in two shapes. In **dynamic** mode the agent designs the surface; in **fixed-schema** mode you pre-author the layout and the agent only fills in data.
|
||||
|
||||
| Mode | Who designs the layout | Backend | Predictability |
|
||||
| --- | --- | --- | --- |
|
||||
| **[Dynamic](#dynamic)** | The agent, from the conversation | No A2UI tool — auto-injected | Novel layouts, LLM layout step |
|
||||
| **[Fixed-schema](#fixed-schema)** | You, up front | Backend tools return an envelope | Deterministic, no layout step |
|
||||
|
||||
### Dynamic
|
||||
|
||||
The Flow wires **no** A2UI tool. Enable A2UI on the runtime for this agent and it gains a `generate_a2ui` tool automatically. A sub-agent designs a surface from the conversation against your catalog, streams it to the frontend progressively, and self-heals invalid output through a validate-then-retry recovery pass. You write a normal agentic-chat Flow; the tool is injected for you.
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Register the catalog on the provider">
|
||||
|
||||
Same as above — pass your catalog through the `a2ui` prop:
|
||||
|
||||
```tsx
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="assistant" a2ui={{ catalog }}>
|
||||
{/* ... */}
|
||||
</CopilotKit>
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve a normal Flow">
|
||||
|
||||
Your backend is a plain agentic-chat Flow. You do not define an A2UI tool — the runtime injects `generate_a2ui` when A2UI is enabled for the agent, and the sub-agent invents the layout from the conversation.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Let the agent compose">
|
||||
|
||||
When a turn calls for UI, the agent assembles a surface from your catalog, streams the components in as it designs them, and repairs any invalid output before it reaches the screen. Your registered components render in the layout the agent chose.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
### Fixed-schema
|
||||
|
||||
When you already know the layout and only the data changes per call, pre-author the surface and let the agent fill it. The Flow wires backend tools (for example `search_flights`, `search_hotels`). Each tool returns an **A2UI operations envelope** as its result — `createSurface` -> `updateComponents` -> `updateDataModel` — which the frontend paints. There is no sub-agent, no generation, and no recovery pass: the layout JSON is authored by you, and only the data varies.
|
||||
|
||||
Install the toolkit that provides the envelope helpers:
|
||||
|
||||
```bash
|
||||
pip install ag-ui-a2ui-toolkit
|
||||
```
|
||||
|
||||
Build the envelope with the toolkit helpers and emit it as the tool result:
|
||||
|
||||
```python
|
||||
from ag_ui_a2ui_toolkit import (
|
||||
A2UI_OPERATIONS_KEY,
|
||||
create_surface,
|
||||
update_components,
|
||||
update_data_model,
|
||||
)
|
||||
from ag_ui_crewai.sdk import copilotkit_emit_tool_result, copilotkit_stream
|
||||
```
|
||||
|
||||
The tool assembles the `createSurface` -> `updateComponents` -> `updateDataModel` operations into an envelope keyed by `A2UI_OPERATIONS_KEY`, then hands it back with `copilotkit_emit_tool_result(...)`. Because the layout is fixed, the same tool always produces the same shape — only the values differ from call to call.
|
||||
|
||||
## When to use which
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Dynamic" icon="wand-magic-sparkles">
|
||||
The layout is not known ahead of time and you want the agent to compose novel surfaces from your primitives. You gain flexibility and pay for an LLM layout step.
|
||||
</Card>
|
||||
<Card title="Fixed-schema" icon="table-cells">
|
||||
The layout is known and only the data varies. More predictable and deterministic — no generation, no recovery, no LLM in the layout path.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Both modes share the same frontend: one catalog, registered once on the provider. Start with fixed-schema when your surfaces are stable, and reach for dynamic when you want the agent to design layouts you did not anticipate.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
The full spectrum — A2UI is its declarative tier.
|
||||
</Card>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map one tool to one component (controlled).
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state (controlled).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,208 +0,0 @@
|
||||
---
|
||||
title: Agentic Generative UI
|
||||
description: Render your CrewAI Flow's live state as UI that updates as the agent works through multi-step tasks.
|
||||
icon: list-check
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Render the agent's live state
|
||||
|
||||
Some work does not fit into a single tool call. A research task, a multi-step plan, a long-running job: the interesting thing to show the user is not one result, but *progress*. Agentic generative UI renders the agent's **state** and re-renders it every time that state changes.
|
||||
|
||||
The pattern has two halves:
|
||||
|
||||
1. Your Flow writes progress into its own state as it works.
|
||||
2. Your frontend reads that state with `useAgent` and paints it, re-rendering as the state streams in.
|
||||
|
||||
The Flow's state reaches the frontend over AG-UI without you wiring up any transport. A state snapshot is emitted automatically at each step (method) boundary of the Flow, and you can push intermediate updates during a long-running step by calling `copilotkit_emit_state` explicitly. You subclass the state to add your own fields, update them in the Flow, and read them in React.
|
||||
|
||||
<Note>
|
||||
State-driven rendering requires a **Flow** with custom state (`Flow[AgentState]`). Crews are chat-oriented and do not expose custom state this way, so with a Crew use [tool rendering](/edge/en/guides/frontend/tool-based-generative-ui) instead.
|
||||
</Note>
|
||||
|
||||
## Build a live task planner
|
||||
|
||||
This example builds a planner that breaks a request into about ten steps and streams them to the UI as a checklist. It assumes you already have a CrewAI server and a CopilotKit frontend wired up. If you do not, start with the [Frontend Overview](/edge/en/guides/frontend/overview).
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Add your own fields to the agent state">
|
||||
|
||||
Subclass `CopilotKitState` to declare the state your UI needs. `CopilotKitState` already carries the conversation (`messages`); you add whatever else you want to render, here a list of task steps.
|
||||
|
||||
```python
|
||||
from typing import List, Literal
|
||||
from pydantic import BaseModel, Field
|
||||
from ag_ui_crewai.sdk import CopilotKitState
|
||||
|
||||
|
||||
class TaskStep(BaseModel):
|
||||
description: str
|
||||
status: Literal["enabled", "disabled"]
|
||||
|
||||
|
||||
class AgentState(CopilotKitState):
|
||||
steps: List[TaskStep] = Field(default_factory=list)
|
||||
```
|
||||
|
||||
Everything on `AgentState` is included in the state snapshot the frontend receives. A snapshot is emitted automatically at each step boundary, so writing to `self.state` is enough for the UI to pick it up between steps. To update the UI *during* a long step, emit explicitly (shown below).
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Write progress into state from the Flow">
|
||||
|
||||
Type your Flow with the custom state (`Flow[AgentState]`) and let the model fill it in. Here the LLM calls a `generate_task_steps` tool; the streamed tool call lands in the conversation and the steps become visible in state.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream
|
||||
|
||||
GENERATE_TASK_STEPS_TOOL = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "generate_task_steps",
|
||||
"description": "Break a task into about 10 short imperative steps.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {"type": "string"},
|
||||
"status": {"type": "string", "enum": ["enabled"]},
|
||||
},
|
||||
"required": ["description", "status"],
|
||||
},
|
||||
},
|
||||
},
|
||||
"required": ["steps"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TaskPlannerFlow(Flow[AgentState]):
|
||||
@start()
|
||||
async def chat(self):
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "Plan the task the user asks for."},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[GENERATE_TASK_STEPS_TOOL],
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
```
|
||||
|
||||
Wrapping the LLM call in `copilotkit_stream` streams the assistant's tokens and tool call to the frontend as they are produced. The `steps` you write to `self.state` are sent in the state snapshot emitted at the end of this step.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Stream progress during a long step (optional)">
|
||||
|
||||
The automatic snapshot fires at step boundaries. If a single step does substantial work and you want the checklist to fill in *as it happens*, emit intermediate state yourself with `copilotkit_emit_state`. Each call pushes the current state to the frontend immediately.
|
||||
|
||||
```python
|
||||
from ag_ui_crewai.sdk import copilotkit_emit_state
|
||||
|
||||
class TaskPlannerFlow(Flow[AgentState]):
|
||||
@start()
|
||||
async def execute(self):
|
||||
for step in self.state.steps:
|
||||
step.status = "disabled" # mark done as you go
|
||||
await copilotkit_emit_state(self.state) # push update now
|
||||
await do_work(step)
|
||||
```
|
||||
|
||||
Import `copilotkit_emit_state` from `ag_ui_crewai.sdk`. It requires the CopilotKit SDK (`pip install "copilotkit[crewai]"`). Reach for it only when a step is long enough that waiting for its boundary snapshot would feel unresponsive.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve the Flow over AG-UI">
|
||||
|
||||
Register the Flow exactly as any other, on its own path:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from my_agents.task_planner import TaskPlannerFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=TaskPlannerFlow(),
|
||||
path="/task_planner",
|
||||
)
|
||||
```
|
||||
|
||||
See the [Frontend Overview](/edge/en/guides/frontend/overview) for the full server, runtime, and provider setup, and remember to register the agent (here `task_planner`) in your CopilotKit runtime route.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Read the live state in React">
|
||||
|
||||
On the frontend, `useAgent` gives you the agent's live state. Subscribe to state changes so your component re-renders every time the Flow writes an update.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useAgent, UseAgentUpdate } from "@copilotkit/react-core/v2";
|
||||
|
||||
function TaskPlan() {
|
||||
const { agent } = useAgent({
|
||||
agentId: "task_planner",
|
||||
updates: [UseAgentUpdate.OnStateChanged],
|
||||
});
|
||||
|
||||
const steps = agent?.state?.steps ?? [];
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{steps.map((s, i) => (
|
||||
<li key={i}>{s.description}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`useAgent` returns `{ agent }`. A few things to know:
|
||||
|
||||
- `agent.state` is the live Flow state. Its shape matches the fields you added to `AgentState`, so `agent.state.steps` is your list of task steps.
|
||||
- `agent.isRunning` tells you when the agent is actively working, useful for showing a spinner or disabling input.
|
||||
- `updates: [UseAgentUpdate.OnStateChanged]` re-renders the component whenever state changes, so the checklist fills in as the Flow streams its steps.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Where this goes next
|
||||
|
||||
Reading state is the foundation. Two guides build directly on it:
|
||||
|
||||
- [Shared State](/edge/en/guides/frontend/shared-state) adds the other direction: editing the agent's state from the UI and having the Flow pick up the change.
|
||||
- [Predictive State](/edge/en/guides/frontend/predictive-state-updates) streams a tool's in-progress arguments into state so the UI reflects work before it is committed.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Sync agent state and app UI in both directions.
|
||||
</Card>
|
||||
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
Stream in-progress tool arguments into state.
|
||||
</Card>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map agent tool calls to components.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
title: Channels
|
||||
description: Run the same CrewAI agent as a Slack or Teams bot with the CopilotKit Channels SDK and managed Intelligence platform.
|
||||
icon: messages
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Meet your users where they already are
|
||||
|
||||
The CrewAI agent you built in the [Overview](/edge/en/guides/frontend/overview) does not have to live behind a web app. The same Crew or Flow can run as a bot inside a messaging platform. No rebuild, no second copy of your agent logic: the agent stays exposed over the [AG-UI protocol](https://docs.ag-ui.com), and a **channel** drives it from Slack or Microsoft Teams.
|
||||
|
||||
CopilotKit's [Channels SDK](https://docs.copilotkit.ai/slack) provides that channel. You declare a `createChannel` in a small runtime, point it at your CrewAI agent, and CopilotKit's managed **Intelligence** platform brokers the connection to the messaging provider.
|
||||
|
||||
<Note>
|
||||
Unlike the rest of this section, Channels is **not self-hosted**. It runs through **CopilotKit Intelligence** — a required surface for Channels, by design (a free tier is available). Intelligence holds the platform connection and credentials, receives each platform event, and delivers the turn to your channel process; your process runs the agent and streams the reply back. You configure Slack once in the Intelligence dashboard, and platform credentials never enter your process. Your agent, tools, and state stay yours.
|
||||
</Note>
|
||||
|
||||
## How it fits together
|
||||
|
||||
Nothing about your CrewAI agent server changes. It keeps serving your Crew or Flow over AG-UI exactly as in the Overview. What you add is a separate long-running Node process built with `@copilotkit/channels`: it registers a channel on the `CopilotRuntime`, connects to Intelligence, and runs your agent whenever a message arrives.
|
||||
|
||||
```
|
||||
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
The channel process holds a persistent connection to the Intelligence gateway, so it needs a long-running host — a serverless request handler cannot own that connection. Your CrewAI server can keep serving the web frontend from the Overview at the same time: the web app and the channel are just two clients of one AG-UI endpoint.
|
||||
|
||||
## Integration guide
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Install the Channels packages">
|
||||
|
||||
The Channels SDK is batteries-included — every platform ships in the one package, with no per-platform adapter to install. Add it alongside the runtime that hosts the channel and the CrewAI AG-UI client:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create a Channel in Intelligence">
|
||||
|
||||
In the [CopilotKit dashboard](https://docs.copilotkit.ai/slack), create a Channel and connect Slack — Intelligence walks you through creating the Slack app and holds its credentials. That leaves two environment variables for your process, both from the dashboard:
|
||||
|
||||
```bash
|
||||
export INTELLIGENCE_API_KEY=... # authenticates the runtime with Intelligence (free tier available)
|
||||
export INTELLIGENCE_CHANNEL_ID=... # the Channel ID, matched by createChannel({ name })
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Define the channel">
|
||||
|
||||
`createChannel` declares the channel and attaches your agent. Build the agent as a per-thread factory so each conversation gets its own session, using the same `CrewAIAgent` the Overview uses in the web runtime, pointed at your AG-UI endpoint. `identifyUser: "platform"` lets Intelligence map each platform user to a stable identity.
|
||||
|
||||
```ts
|
||||
// channel.ts
|
||||
import { createChannel } from "@copilotkit/channels";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
|
||||
const channel = createChannel({
|
||||
name: process.env.INTELLIGENCE_CHANNEL_ID!, // must match the Channel ID in Intelligence
|
||||
identifyUser: "platform",
|
||||
// A fresh agent per conversation, pointed at your CrewAI AG-UI endpoint.
|
||||
agent: (threadId) => {
|
||||
const agent = new CrewAIAgent({ url: "http://localhost:8000/recipe" });
|
||||
agent.threadId = threadId;
|
||||
return agent;
|
||||
},
|
||||
});
|
||||
|
||||
// A mention subscribes the thread and runs the agent; afterwards every message
|
||||
// in a subscribed thread runs it without needing another mention.
|
||||
channel.onMention(async ({ thread }) => {
|
||||
await thread.subscribe();
|
||||
await thread.runAgent();
|
||||
});
|
||||
|
||||
channel.onMessage(async ({ thread }) => {
|
||||
if (await thread.isSubscribed()) await thread.runAgent();
|
||||
});
|
||||
|
||||
export { channel };
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Register the channel on the runtime">
|
||||
|
||||
Create a `CopilotRuntime` with the Intelligence gateway and your channel, then serve it with `createCopilotNodeListener`. The `agents` map stays empty — the channel supplies its own agent. Wait for the channel to be ready so a broken config fails startup loudly.
|
||||
|
||||
```ts
|
||||
// server.ts
|
||||
import { createServer } from "node:http";
|
||||
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
|
||||
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
|
||||
import { channel } from "./channel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {}, // the channel supplies its own agent; no web-facing agents needed
|
||||
intelligence: new CopilotKitIntelligence({
|
||||
apiKey: process.env.INTELLIGENCE_API_KEY!, // free tier available
|
||||
}),
|
||||
channels: [channel],
|
||||
});
|
||||
|
||||
const listener = createCopilotNodeListener({ runtime });
|
||||
await listener.channels?.ready({ timeoutMs: 15_000 });
|
||||
|
||||
createServer(listener).listen(3123, () => {
|
||||
console.log("Channels runtime listening on port 3123");
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run the channel runtime">
|
||||
|
||||
Start it alongside your CrewAI agent server:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1 — CrewAI agent server
|
||||
npx tsx server.ts # terminal 2 — Channels runtime
|
||||
```
|
||||
|
||||
Mention the bot in Slack or Teams and it runs your Crew or Flow, streaming the reply back into the thread. The thread stays subscribed, so follow-up messages run without another mention.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## The event model
|
||||
|
||||
A channel reacts to platform events with handlers, and each handler receives a `thread` you drive with a few methods:
|
||||
|
||||
- **`channel.onMention`** fires when a user @-mentions the bot. Call `thread.subscribe()` to join the thread, then `thread.runAgent()` to run your CrewAI agent on the mention.
|
||||
- **`channel.onMessage`** fires on every message in a thread the bot can see. Gate it with `thread.isSubscribed()` so the agent only responds where it has joined, then `thread.runAgent()`.
|
||||
- **`thread.runAgent()`** runs the attached CrewAI agent for the current turn and streams its output back into the channel. Pass `{ prompt }` to override the text the agent runs on.
|
||||
|
||||
Your agent receives an ordinary AG-UI `RunAgentInput` and emits ordinary AG-UI events; the platform mechanics stay behind the channel, so the same Crew or Flow runs unchanged across every platform. The channel also exposes handlers for welcomes, interrupts, commands, reactions, and modals — see the [`Channel` reference](https://docs.copilotkit.ai/reference/channels/classes/Channel) for the full surface.
|
||||
|
||||
## Platform support
|
||||
|
||||
The managed Intelligence path covers **Slack** and **Microsoft Teams** today — the same channel code runs on either, and `message.platform` / `thread.platform` report the native origin. Other platforms (Discord, Telegram, WhatsApp) are reached through developer-operated **direct adapters** rather than the managed path — your own process holds the platform credentials and transport. Check the [CopilotKit Channels documentation](https://docs.copilotkit.ai/slack) for the current platform list and per-platform setup.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend Overview" icon="browser" href="/edge/en/guides/frontend/overview">
|
||||
Serve your Crew or Flow over AG-UI — the foundation every channel builds on.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Pause the agent to collect user approval or input mid-run.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
title: Conversational Flows
|
||||
description: Serve native, session-aware CrewAI Flows over AG-UI with managed conversation state and full frontend parity.
|
||||
icon: comments
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Three execution shapes, one bridge
|
||||
|
||||
Behind the AG-UI bridge, a CrewAI backend can take one of three shapes. Knowing which one you are serving decides how you author the backend, not how you build the frontend.
|
||||
|
||||
| Shape | What it is | How it is entered |
|
||||
| --- | --- | --- |
|
||||
| **Regular Flows** | Author-controlled `@start`/`@listen`/`@router` graphs. The default used throughout these guides. | `kickoff` / `astream` |
|
||||
| **Conversational Flows** | Native, session-aware, turn-based Flows with managed conversation state. | `stream_turn(message, session_id=...)` |
|
||||
| **Crews** | Closed autonomous task/agent loops. Basic chat only, a separate compatibility path. | Not the focus here. |
|
||||
|
||||
Conversational Flows are a newer CrewAI capability, and an important thing to be clear about up front: **they are Flows, not Crews.** They now run at full regular-Flow feature parity. This page introduces them and shows how they fit the rest of the frontend guides.
|
||||
|
||||
<Note>
|
||||
Reach for a Conversational Flow when you want native multi-turn conversation with CrewAI managing session state and history for you, rather than wiring turn and state handling into a regular Flow yourself. If you are new here, start with the [Frontend Overview](/edge/en/guides/frontend/overview) for the base server, runtime, and provider setup.
|
||||
</Note>
|
||||
|
||||
## Register a Conversational Flow
|
||||
|
||||
You register a Conversational Flow through the same endpoint helper as any other Flow, with one extra argument: `conversational=True`.
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app,
|
||||
flow,
|
||||
"/conversation",
|
||||
conversational=True,
|
||||
)
|
||||
```
|
||||
|
||||
Two requirements must hold for this to work:
|
||||
|
||||
- The Flow instance declares `conversational = True`.
|
||||
- The Flow exposes CrewAI's public, callable `stream_turn(message, session_id=...)`.
|
||||
|
||||
Detection is capability-based, not version-gated: the bridge checks that the Flow actually offers turn-based conversation, rather than keying off a version number.
|
||||
|
||||
<Warning>
|
||||
If those requirements are not met, the request fails loudly with a `RUN_ERROR` (code `AGUI_CREWAI_CONVERSATIONAL_FLOW_UNSUPPORTED`). It never silently falls back to regular kickoff semantics, so you always know exactly which path you are on.
|
||||
</Warning>
|
||||
|
||||
Authoring the Flow itself, including how you implement `stream_turn`, belongs to CrewAI's Conversational Flows documentation. This page stays at the registration and integration boundary.
|
||||
|
||||
## Session and state
|
||||
|
||||
Conversational Flows manage session state and history for you across turns. You do not re-thread history manually.
|
||||
|
||||
- The AG-UI `threadId` **is** the CrewAI conversation `session_id`. The same thread is the same conversation.
|
||||
- Before each turn the bridge hydrates the Flow's state and conversation history, then calls `stream_turn`. CrewAI restores the stored session state, and a per-request overlay reapplies the incoming AG-UI state and history so the browser's latest edits win over stale storage.
|
||||
|
||||
The result: from the backend author's side, each turn arrives already carrying the conversation's state, and CrewAI persists what you write for the next turn.
|
||||
|
||||
## Frontend parity
|
||||
|
||||
This is the point to hold onto: **Conversational Flows run through the same event pipeline as regular Flows, so the frontend code is identical.**
|
||||
|
||||
There is no Conversational-Flow-specific frontend API. Every feature in these guides works exactly the same way with a Conversational Flow as it does with a regular Flow, using the same hooks and components:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map agent tool calls to your React components.
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render the Flow's live state as it works.
|
||||
</Card>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Keep agent state and app UI in two-way sync.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Pause the agent for user approval or input mid-turn.
|
||||
</Card>
|
||||
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
Stream in-progress tool arguments into state.
|
||||
</Card>
|
||||
<Card title="Reasoning" icon="brain" href="/edge/en/guides/frontend/reasoning">
|
||||
Show the model's thinking in the chat.
|
||||
</Card>
|
||||
<Card title="A2UI" icon="table-cells" href="/edge/en/guides/frontend/a2ui">
|
||||
Render agent-authored UI from a component catalog.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
The only difference is on the backend: how you author the Flow (turn-based `stream_turn` with managed session state) and the `conversational=True` registration. Once the endpoint is up, everything you already know about building the frontend applies unchanged.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend Overview" icon="browser" href="/edge/en/guides/frontend/overview">
|
||||
Wire a Crew or Flow to a Next.js frontend end to end.
|
||||
</Card>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
Render tool calls and agent state as custom components.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Gate agent actions behind user approval.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,121 +0,0 @@
|
||||
---
|
||||
title: Frontend Actions
|
||||
description: Let your CrewAI agent call functions that run in the user's browser, from switching themes to navigating your app.
|
||||
icon: bolt
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Let the agent act on the app
|
||||
|
||||
A frontend action is a tool the agent calls that runs code in the browser instead of on the server. The model decides to invoke it; your handler switches the theme, navigates, highlights an element, or updates your app data; and the result flows back to the agent.
|
||||
|
||||
It uses the same hook as tool-based generative UI, `useFrontendTool`. The difference is what you give it: a `handler` that runs code, instead of (or alongside) a `render` that draws UI.
|
||||
|
||||
<Note>
|
||||
Frontend actions work with both Crews and Flows. Any agent that binds `copilotkit.actions` into its LLM call can invoke them.
|
||||
</Note>
|
||||
|
||||
## Build a frontend action
|
||||
|
||||
The example below lets the agent switch the app into dark mode on request.
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Register the action on the frontend">
|
||||
|
||||
Call `useFrontendTool` with a `handler`. The handler runs in the browser when the agent invokes the tool, and the string it returns is fed back to the agent.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useFrontendTool } from "@copilotkit/react-core/v2";
|
||||
import { z } from "zod";
|
||||
|
||||
useFrontendTool({
|
||||
agentId: "assistant",
|
||||
name: "set_theme",
|
||||
description: "Switch the app between light and dark mode.",
|
||||
parameters: z.object({
|
||||
theme: z.enum(["light", "dark"]),
|
||||
}),
|
||||
followUp: false,
|
||||
handler: async ({ theme }) => {
|
||||
document.documentElement.dataset.theme = theme; // runs in the browser
|
||||
return `Theme set to ${theme}.`;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The arguments:
|
||||
|
||||
- **`name`** — the tool name the model calls (`set_theme`).
|
||||
- **`description`** — a short explanation of what the tool does. The model reads it to decide *when* to call the tool, so make it specific. Omitting it leaves the model guessing from the name alone.
|
||||
- **`parameters`** — a [zod](https://zod.dev) schema describing the arguments the model must supply. CopilotKit turns this into the tool's JSON schema and validates the incoming call.
|
||||
- **`handler(args)`** — runs in the browser with the parsed arguments. Do your side effect here (set the theme, navigate, update state). The string you return is handed back to the agent as the tool result.
|
||||
- **`followUp: false`** — stops the agent from taking another turn after the action runs. Leave it out (or set `true`) when you want the agent to respond after acting.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Bind the frontend tools on the backend">
|
||||
|
||||
The agent can only call a tool it has been given. In your Flow, pass the frontend-registered tools into the LLM `tools` list with `*self.state.copilotkit.actions`.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, start
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, CopilotKitState
|
||||
|
||||
class AssistantFlow(Flow[CopilotKitState]):
|
||||
@start()
|
||||
async def chat(self):
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "Help the user. Use the tools available to control the app."},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[*self.state.copilotkit.actions], # tools the frontend registered
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
```
|
||||
|
||||
`self.state.copilotkit.actions` holds the tool definitions for every frontend action registered with `useFrontendTool`. Spreading them into the LLM `tools` list is what makes the agent able to invoke browser-side actions. `copilotkit_stream` streams the response, including the tool call, back to the frontend, where CopilotKit runs the matching handler.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve the Flow">
|
||||
|
||||
Expose the Flow over AG-UI with `add_crewai_flow_fastapi_endpoint(...)` and register it in the CopilotKit runtime, exactly as in the [Frontend Overview](/edge/en/guides/frontend/overview). Once both are running, asking the assistant to "switch to dark mode" triggers `set_theme`, and the page flips.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Actions vs. generative UI
|
||||
|
||||
`useFrontendTool` covers both ends of a spectrum, and you pick per tool:
|
||||
|
||||
| You provide | What it does |
|
||||
| --- | --- |
|
||||
| **`handler`** | Runs code in the browser (a frontend action) |
|
||||
| **`render`** | Draws UI for the tool call (generative UI) |
|
||||
|
||||
You can supply either one, or both. A `handler` with a `render` alongside it performs the action and draws UI while it runs. For render-only tools that just display the result of an agent action, see [Tool-Based Generative UI](/edge/en/guides/frontend/tool-based-generative-ui).
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map agent tool calls to React components.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Gate agent actions behind user approval.
|
||||
</Card>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Keep agent state and your app UI in two-way sync.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
title: Generative UI
|
||||
description: Render your CrewAI agent's work as live React components, across the full spectrum from author-controlled to agent-invented UI.
|
||||
icon: wand-magic-sparkles
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Beyond the chat bubble
|
||||
|
||||
Generative UI means the agent's work shows up as real interface, not just text. When your Crew or Flow calls a tool, updates its state, or reasons about a problem, you decide what the user sees: a progress checklist, a recipe card, a chart, a whole assembled panel.
|
||||
|
||||
CopilotKit renders generative UI along a **spectrum**, from fully author-controlled (you decide every pixel) to agent-invented (the agent assembles the surface):
|
||||
|
||||
| Tier | Who decides the UI | CrewAI mechanism |
|
||||
| --- | --- | --- |
|
||||
| **[Controlled](#controlled)** | You — a fixed set of components the agent picks from | `useRenderTool`, `useAgent`, reasoning |
|
||||
| **[Declarative](#declarative)** | The agent — assembles a surface from *your* component catalog | [A2UI](/edge/en/guides/frontend/a2ui) |
|
||||
| **[Open-ended](#open-ended)** | An external tool/server invents the surface | MCP tools |
|
||||
|
||||
The tiers compose freely; a single app usually mixes them.
|
||||
|
||||
## Controlled
|
||||
|
||||
You own the components. The agent chooses which to show and with what data. This is the most predictable tier and where most apps start.
|
||||
|
||||
### Tool rendering
|
||||
|
||||
The agent calls a tool on the backend. You register a matching component on the frontend with `useRenderTool`, and CopilotKit renders it, streaming the arguments in as they arrive.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useRenderTool } from "@copilotkit/react-core/v2";
|
||||
import { z } from "zod";
|
||||
|
||||
useRenderTool({
|
||||
name: "generate_recipe",
|
||||
parameters: z.object({
|
||||
title: z.string(),
|
||||
ingredients: z.array(z.string()),
|
||||
}),
|
||||
render: ({ args }) => <RecipeCard title={args.title} ingredients={args.ingredients} />,
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
`useRenderTool` renders a tool call. When a tool also needs to *run* code in the browser, use [`useFrontendTool`](/edge/en/guides/frontend/frontend-actions) (a `handler`, with optional `render`).
|
||||
</Note>
|
||||
|
||||
See [Tool-Based Generative UI](/edge/en/guides/frontend/tool-based-generative-ui) for the full walkthrough, including progressive rendering as arguments stream, and [Backend Tool Rendering](/edge/en/guides/frontend/tool-based-generative-ui#backend-tools) for tools your Crew or Flow executes server-side.
|
||||
|
||||
### State rendering
|
||||
|
||||
Instead of reacting to a single tool call, render the agent's **state** as it changes. This is the right pattern for multi-step work: read the agent's working state with `useAgent` and paint it however you like.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useAgent } from "@copilotkit/react-core/v2";
|
||||
|
||||
function TaskProgress() {
|
||||
const { agent } = useAgent({ agentId: "task_runner" });
|
||||
const steps = agent?.state?.steps ?? [];
|
||||
return <StepList steps={steps} />;
|
||||
}
|
||||
```
|
||||
|
||||
See [Agentic Generative UI](/edge/en/guides/frontend/agentic-generative-ui) for streaming state from a Flow, and [Shared State](/edge/en/guides/frontend/shared-state) for editing that state from the UI.
|
||||
|
||||
### Reasoning
|
||||
|
||||
When the model reasons before answering, that thinking renders in the chat automatically. No component to write. See [Reasoning](/edge/en/guides/frontend/reasoning).
|
||||
|
||||
## Declarative
|
||||
|
||||
The agent goes beyond picking a component: it **assembles a surface** by combining building blocks from a catalog *you* define. You still own the components (the agent can only use what is in your catalog), but the layout is the agent's.
|
||||
|
||||
This is [A2UI](/edge/en/guides/frontend/a2ui). You register a catalog on the provider:
|
||||
|
||||
```tsx
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="assistant" a2ui={{ catalog }}>
|
||||
{/* ... */}
|
||||
</CopilotKit>
|
||||
```
|
||||
|
||||
The agent then builds surfaces from that catalog — either dynamically (it designs the layout from the conversation) or from a fixed schema your backend fills with data. See [A2UI](/edge/en/guides/frontend/a2ui) for both modes and error recovery.
|
||||
|
||||
## Open-ended
|
||||
|
||||
At the far end, the surface is invented outside your app entirely. For CrewAI this comes through **MCP**: tools served by an MCP server the agent connects to render as tool calls in the chat, the same way backend tools do. This is the least constrained and the least predictable tier.
|
||||
|
||||
MCP tool calls surface as standard tool-call UI — render them with `useRenderTool` like any other tool. Full agent-invented "MCP App" surfaces are an emerging capability; see the [CopilotKit docs](https://docs.copilotkit.ai) for the current state.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map agent tool calls to components (controlled).
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state (controlled).
|
||||
</Card>
|
||||
<Card title="A2UI" icon="table-cells" href="/edge/en/guides/frontend/a2ui">
|
||||
Let the agent assemble surfaces from your catalog (declarative).
|
||||
</Card>
|
||||
<Card title="Reasoning" icon="brain" href="/edge/en/guides/frontend/reasoning">
|
||||
Render the agent's thinking.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,191 +0,0 @@
|
||||
---
|
||||
title: Human-in-the-Loop
|
||||
description: Pause your CrewAI agent mid-run to collect a user decision, then resume the agent with their answer.
|
||||
icon: user-check
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Put the user in the loop
|
||||
|
||||
Some steps should not happen without a human saying yes. Human-in-the-loop pauses the agent mid-run, renders an interactive component in the frontend, and waits. The user makes a choice; the agent resumes with that choice and continues.
|
||||
|
||||
The mechanism is a tool the frontend registers. When the model calls it, the run halts at that tool call until the user responds. Nothing happens automatically: the agent stays parked until `respond()` hands control back.
|
||||
|
||||
In the example below, the agent proposes a list of task steps. The user enables or disables each step and confirms. The agent then continues, respecting exactly what the user approved.
|
||||
|
||||
<Note>
|
||||
This pattern works with Flows. It relies on the Flow's chat loop re-entering after `respond()`: the returned value comes back as a tool result, and the agent's next turn acts on it.
|
||||
</Note>
|
||||
|
||||
## Build it
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Bind the frontend actions into the model's tools">
|
||||
|
||||
In your Flow, add the frontend-registered actions to the model's tool list with `*self.state.copilotkit.actions`. Those actions are the tools your frontend registered (via `useHumanInTheLoop`). Binding them lets the model call them; the run pauses at that tool call until the user responds.
|
||||
|
||||
```python
|
||||
# human_in_the_loop_flow.py
|
||||
from crewai.flow.flow import Flow, start, router, listen
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, CopilotKitState
|
||||
|
||||
|
||||
class HumanInTheLoopFlow(Flow[CopilotKitState]):
|
||||
@start()
|
||||
@listen("route_follow_up")
|
||||
async def start_flow(self):
|
||||
pass
|
||||
|
||||
@router(start_flow)
|
||||
async def chat(self):
|
||||
system_prompt = (
|
||||
"You perform tasks for the user. When asked to do a task, call the "
|
||||
"tool the frontend provides so the user can approve or adjust the steps "
|
||||
"before you continue."
|
||||
)
|
||||
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[*self.state.copilotkit.actions], # tools registered by the frontend
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
return "route_end"
|
||||
|
||||
@listen("route_end")
|
||||
async def end(self):
|
||||
pass
|
||||
```
|
||||
|
||||
`CopilotKitState` carries the frontend-registered actions on `self.state.copilotkit.actions`. When the model calls one, the run pauses there. After the user responds, the returned value lands in `self.state.messages` as the tool result, and the Flow loops back through `chat` so the model can act on the decision.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve the Flow over AG-UI">
|
||||
|
||||
Expose the Flow from your FastAPI server with `add_crewai_flow_fastapi_endpoint`, the same way as every other agent. See [Frontend Overview](/edge/en/guides/frontend/overview) for the full server, runtime, and provider setup.
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from my_agents.human_in_the_loop_flow import HumanInTheLoopFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=HumanInTheLoopFlow(),
|
||||
path="/human_in_the_loop",
|
||||
)
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Register the interactive tool on the frontend">
|
||||
|
||||
`useHumanInTheLoop` registers the tool the agent pauses on and gives you a `render` function to draw the interactive UI. When the agent calls the tool, your component appears; when the user acts, you call `respond()` to resume the agent.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useHumanInTheLoop } from "@copilotkit/react-core/v2";
|
||||
import { z } from "zod";
|
||||
|
||||
useHumanInTheLoop({
|
||||
agentId: "human_in_the_loop",
|
||||
name: "generate_task_steps",
|
||||
parameters: z.object({
|
||||
steps: z.array(
|
||||
z.object({
|
||||
description: z.string(),
|
||||
status: z.enum(["enabled", "disabled", "executing"]),
|
||||
})
|
||||
),
|
||||
}),
|
||||
render: ({ args, respond, status }) => (
|
||||
<StepReview
|
||||
steps={args.steps ?? []}
|
||||
// `status === "executing"` means the agent is waiting for the user
|
||||
waiting={status === "executing"}
|
||||
onConfirm={(chosen) => respond?.(chosen)}
|
||||
/>
|
||||
),
|
||||
});
|
||||
```
|
||||
|
||||
The `render` function receives:
|
||||
|
||||
- **`args`** — the tool arguments the model produced (here, the proposed `steps`). These stream in as the model generates them.
|
||||
- **`status`** — the tool call's lifecycle. While it is `"executing"`, the agent is paused and waiting on the human.
|
||||
- **`respond(value)`** — resumes the agent with the user's decision. The agent's next turn sees the returned value and acts on it.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Let the user decide, then respond">
|
||||
|
||||
Your component reads `args.steps`, lets the user toggle each one, and calls `respond()` with the final selection. That value is what the agent continues with.
|
||||
|
||||
```tsx
|
||||
function StepReview({ steps, waiting, onConfirm }) {
|
||||
const [choices, setChoices] = useState(steps);
|
||||
|
||||
const toggle = (i) =>
|
||||
setChoices((prev) =>
|
||||
prev.map((s, idx) =>
|
||||
idx === i
|
||||
? { ...s, status: s.status === "enabled" ? "disabled" : "enabled" }
|
||||
: s
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{choices.map((step, i) => (
|
||||
<label key={i}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={step.status === "enabled"}
|
||||
disabled={!waiting}
|
||||
onChange={() => toggle(i)}
|
||||
/>
|
||||
{step.description}
|
||||
</label>
|
||||
))}
|
||||
<button disabled={!waiting} onClick={() => onConfirm(choices)}>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Once the user clicks Confirm, `respond()` fires, the run resumes, and the Flow's `chat` step runs again with the user's choices in the message history.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend Actions" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
|
||||
Let the agent call functions that run in the browser.
|
||||
</Card>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Keep agent state and your app UI in two-way sync.
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state as custom components.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
title: Frontend Overview
|
||||
description: Build interactive user interfaces for your CrewAI agents with CopilotKit and the AG-UI protocol.
|
||||
icon: browser
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Give your agents a user interface
|
||||
|
||||
CrewAI runs your agents. [CopilotKit](https://copilotkit.ai) gives them a frontend. Together they let you build applications where users chat with a Crew or Flow, watch it work in real time, approve its decisions, and see its output rendered as live UI instead of walls of text.
|
||||
|
||||
The two connect through the [AG-UI protocol](https://docs.ag-ui.com). The `ag-ui-crewai` package exposes any Crew or Flow as an AG-UI endpoint. CopilotKit's React hooks and components consume that endpoint. This unlocks experiences that go well beyond a chat box:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
Render agent tool calls and state as your own React components.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Pause the agent to collect user approval or input mid-run.
|
||||
</Card>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Keep agent state and your app UI in two-way sync.
|
||||
</Card>
|
||||
<Card title="Channels" icon="messages" href="/edge/en/guides/frontend/channels">
|
||||
Run the same agent as a Slack, Discord, or Teams bot.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
This guide gets a Crew or Flow talking to a Next.js frontend end to end. The rest of the section builds on the app you set up here.
|
||||
|
||||
## Architecture
|
||||
|
||||
There are three pieces:
|
||||
|
||||
1. **CrewAI agent server** — a Python process that serves your Crew or Flow over AG-UI (FastAPI + `ag-ui-crewai`).
|
||||
2. **CopilotKit runtime** — a Next.js route that registers your agent and proxies requests to it.
|
||||
3. **React frontend** — the `<CopilotKit>` provider plus chat and generative-UI components.
|
||||
|
||||
```
|
||||
React app ──► CopilotKit runtime (/api/copilotkit) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
<Note>
|
||||
This guide covers the **self-hosted** path: you run the CrewAI agent server yourself with `ag-ui-crewai`, and it works locally with no managed service. CopilotKit also offers a **managed** path (CopilotKit Cloud / Enterprise Intelligence) with hosted threads and an inspector — see the [CopilotKit CrewAI quickstart](https://docs.copilotkit.ai/crewai-crews/quickstart) if you want that instead. The frontend code in this section is the same either way; only how the agent is hosted and registered differs.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
CrewAI runs behind AG-UI in three shapes: regular **Flows** (used throughout these guides), **[Conversational Flows](/edge/en/guides/frontend/conversational-flows)** (native, session-aware, turn-based, at full feature parity), and **Crews** (basic chat). The frontend in this section is identical across them — only the backend authoring and registration differ.
|
||||
</Note>
|
||||
|
||||
## Integration guide
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Serve your agent over AG-UI">
|
||||
|
||||
Install the integration package into your CrewAI project:
|
||||
|
||||
```bash
|
||||
pip install ag-ui-crewai
|
||||
```
|
||||
|
||||
Expose your agent from a FastAPI app. Flows use `add_crewai_flow_fastapi_endpoint`; Crews use `add_crewai_crew_fastapi_endpoint`. You can register as many as you want, each on its own path.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Flow
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from my_agents.recipe_flow import RecipeFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=RecipeFlow(),
|
||||
path="/recipe",
|
||||
)
|
||||
```
|
||||
|
||||
```python Crew
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_crew_fastapi_endpoint
|
||||
from my_agents.research_crew import ResearchCrew
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_crew_fastapi_endpoint(
|
||||
app=app,
|
||||
crew=ResearchCrew().crew(),
|
||||
path="/research",
|
||||
)
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Run it:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
<Note>
|
||||
Set the environment variables for your LLM provider (for example `OPENAI_API_KEY`) before starting the server.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create a Next.js app">
|
||||
|
||||
If you do not have a frontend yet, scaffold one:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest my-app
|
||||
cd my-app
|
||||
```
|
||||
|
||||
Install CopilotKit and the CrewAI AG-UI client:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Add the CopilotKit runtime">
|
||||
|
||||
Create a route that registers your CrewAI agent(s) with the CopilotKit runtime. Each agent points at a path on your Python server via `CrewAIAgent`.
|
||||
|
||||
```ts
|
||||
// app/api/copilotkit/route.ts
|
||||
import {
|
||||
CopilotRuntime,
|
||||
InMemoryAgentRunner,
|
||||
createCopilotEndpoint,
|
||||
} from "@copilotkit/runtime/v2";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
import { handle } from "hono/vercel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {
|
||||
recipe: new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
|
||||
},
|
||||
runner: new InMemoryAgentRunner(),
|
||||
});
|
||||
|
||||
const app = createCopilotEndpoint({
|
||||
runtime,
|
||||
basePath: "/api/copilotkit",
|
||||
});
|
||||
|
||||
const handler = handle(app);
|
||||
export const GET = handler;
|
||||
export const POST = handler;
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Wrap your app with the provider">
|
||||
|
||||
Point `<CopilotKit>` at the runtime route and name the agent you registered.
|
||||
|
||||
```tsx
|
||||
// app/page.tsx
|
||||
"use client";
|
||||
import { CopilotKit } from "@copilotkit/react-core";
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
import "@copilotkit/react-core/v2/styles.css";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="recipe">
|
||||
<YourApp />
|
||||
<CopilotSidebar agentId="recipe" labels={{ modalHeaderTitle: "Assistant" }} />
|
||||
</CopilotKit>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run it">
|
||||
|
||||
Start both processes and open the app. Chatting in the sidebar now runs your Crew or Flow.
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1
|
||||
npm run dev # terminal 2
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Chat UI options
|
||||
|
||||
CopilotKit ships three interchangeable chat surfaces. Swap the component; the wiring is identical.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```tsx Sidebar
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotSidebar agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Popup
|
||||
import { CopilotPopup } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotPopup agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Inline
|
||||
import { CopilotChat } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotChat agentId="recipe" />
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Where to go next
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
Render tool calls and agent state as custom components.
|
||||
</Card>
|
||||
<Card title="Frontend Actions" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
|
||||
Let the agent call functions that run in the browser.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Gate agent actions behind user approval.
|
||||
</Card>
|
||||
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
Stream in-progress state to the UI as the agent works.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
title: Predictive State Updates
|
||||
description: Stream an in-progress tool call's arguments into agent state so the UI updates optimistically while the agent is still generating.
|
||||
icon: gauge-high
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Show the work as it happens
|
||||
|
||||
Normally a tool call is atomic from the UI's point of view: the agent decides what to write, and your interface only sees the result once the call finishes. For a tool that produces a large document that means a long pause followed by everything snapping into place at once.
|
||||
|
||||
Predictive state updates remove the wait. You project a streaming tool argument onto a field of the agent's state, so as the model generates the argument token by token, that state field fills in live. A document the agent is writing appears in the editor as it is typed, not after.
|
||||
|
||||
<Note>
|
||||
Predictive state relies on a Flow with custom state (`Flow[AgentState]`). It projects a streaming tool argument onto a state field, so there is no equivalent for a bare Crew.
|
||||
</Note>
|
||||
|
||||
## How it compares to Shared State
|
||||
|
||||
Both patterns read the agent's state from the frontend, but they solve different problems:
|
||||
|
||||
| Pattern | What it does |
|
||||
| --- | --- |
|
||||
| **Predictive state** | One-way. Streams an in-progress tool argument into a state field so the UI updates *during* generation, before the call completes. |
|
||||
| **[Shared State](/edge/en/guides/frontend/shared-state)** | Two-way. The UI reads *and writes* the agent's committed state, keeping app and agent in sync across turns. |
|
||||
|
||||
Reach for predictive state when you want an optimistic, in-flight preview of what the agent is producing. Reach for [Shared State](/edge/en/guides/frontend/shared-state) when the user needs to edit that state back.
|
||||
|
||||
## Walkthrough
|
||||
|
||||
This assumes you already have a Crew or Flow served over AG-UI and a CopilotKit frontend wired up. If not, start with the [Frontend Overview](/edge/en/guides/frontend/overview).
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Define a Flow with custom state">
|
||||
|
||||
Predictive state projects a tool argument onto a state field, so your Flow needs a typed state field to receive it. Add the field you want to stream into to your `CopilotKitState` subclass.
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from crewai.flow.flow import Flow, start, router, listen
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, copilotkit_predict_state, CopilotKitState
|
||||
|
||||
WRITE_DOCUMENT_TOOL = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "write_document",
|
||||
"description": "Write the full document in markdown.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"document": {"type": "string", "description": "The document to write"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
class AgentState(CopilotKitState):
|
||||
document: Optional[str] = None
|
||||
|
||||
class DocumentFlow(Flow[AgentState]):
|
||||
@start()
|
||||
@listen("route_follow_up")
|
||||
async def start_flow(self):
|
||||
pass
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Map a state field to a tool argument">
|
||||
|
||||
Call `copilotkit_predict_state` **before** you start streaming the completion. It tells the runtime to project the named tool argument onto the named state field: as the `write_document` call streams its `document` argument, the `document` state field updates live.
|
||||
|
||||
```python
|
||||
@router(start_flow)
|
||||
async def chat(self):
|
||||
# Map the `document` state field to the `document` argument of write_document.
|
||||
# As the tool call streams, the state field updates live.
|
||||
await copilotkit_predict_state({
|
||||
"document": {"tool_name": "write_document", "tool_argument": "document"},
|
||||
})
|
||||
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "Write and edit the document with write_document."},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[*self.state.copilotkit.actions, WRITE_DOCUMENT_TOOL],
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
```
|
||||
|
||||
The key is `copilotkit_predict_state({ "<state_field>": {"tool_name": ..., "tool_argument": ...} })`. Without it, the frontend would only see `document` once the tool call completed. With it, the partial argument streams onto the field while the agent is still generating.
|
||||
|
||||
Serve the Flow with `add_crewai_flow_fastapi_endpoint(...)` as shown in the [Frontend Overview](/edge/en/guides/frontend/overview).
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Read the predicted state on the frontend">
|
||||
|
||||
On the frontend, read the field with `useAgent` and subscribe to state changes. Because the backend is projecting the streaming argument onto `document`, this component re-renders as the agent types.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useAgent, UseAgentUpdate } from "@copilotkit/react-core/v2";
|
||||
|
||||
function DocumentView() {
|
||||
const { agent } = useAgent({
|
||||
agentId: "document",
|
||||
updates: [UseAgentUpdate.OnStateChanged],
|
||||
});
|
||||
const document = (agent?.state as { document?: string })?.document ?? "";
|
||||
return <article>{document}</article>; // updates as the agent types
|
||||
}
|
||||
```
|
||||
|
||||
The `document` field fills in progressively as the agent generates the `write_document` call, so the editor updates in real time rather than snapping in at the end.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
Read and write the agent's state two-way.
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state as it changes.
|
||||
</Card>
|
||||
<Card title="Tool-Based Generative UI" icon="puzzle-piece" href="/edge/en/guides/frontend/tool-based-generative-ui">
|
||||
Map agent tool calls to components.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
title: Reasoning
|
||||
description: Show the model's thinking in the chat automatically, with no component to build.
|
||||
icon: brain
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Thinking, rendered for free
|
||||
|
||||
When a reasoning-capable model thinks before it answers, CopilotKit renders that thinking right in the chat. This is the simplest generative-UI pattern in the whole section: there is nothing to build. No hook, no component, no props. Use a reasoning-capable model, keep the streaming wrapper your Flows already have, and the chat surface from the [Overview](/edge/en/guides/frontend/overview) does the rest.
|
||||
|
||||
## Use a reasoning-capable model
|
||||
|
||||
Reasoning is surfaced automatically by `copilotkit_stream`, which every Flow example already wraps the model call in. The bridge reads the model's reasoning deltas and emits them to the frontend. It is provider-agnostic and works over both of CrewAI's streaming transports, so the only thing you change is the model.
|
||||
|
||||
```python
|
||||
# recipe_flow.py
|
||||
from crewai.flow.flow import Flow, start
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, CopilotKitState
|
||||
from litellm import acompletion
|
||||
|
||||
|
||||
class RecipeFlow(Flow[CopilotKitState]):
|
||||
@start()
|
||||
async def chat(self):
|
||||
response = await copilotkit_stream(
|
||||
acompletion(
|
||||
# any reasoning-capable model, e.g. deepseek-reasoner
|
||||
model="deepseek/deepseek-reasoner",
|
||||
messages=self.state.messages,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
```
|
||||
|
||||
Models that emit reasoning over the standard channel include DeepSeek `deepseek-reasoner`, Anthropic extended thinking (Claude), and Gemini thinking, among others. Swap the `model` for one of these and its thinking starts streaming through.
|
||||
|
||||
This works the same for both Crews and Flows, since both run their model calls through `copilotkit_stream`.
|
||||
|
||||
## Render it
|
||||
|
||||
There is no frontend step. The `CopilotChat`, `CopilotSidebar`, or `CopilotPopup` surface you already mounted shows the reasoning as it streams, above the answer it produced.
|
||||
|
||||
```tsx
|
||||
import { CopilotChat } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotChat agentId="recipe" />
|
||||
```
|
||||
|
||||
<Note>
|
||||
There is no `useReasoning` hook and no reasoning component to write. Reasoning is not something you wire up on the frontend; it renders automatically as long as the model emits it.
|
||||
</Note>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
The full spectrum, from author-controlled to agent-invented UI.
|
||||
</Card>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state as the Flow works.
|
||||
</Card>
|
||||
<Card title="Frontend Overview" icon="browser" href="/edge/en/guides/frontend/overview">
|
||||
Set up the chat surface and runtime.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,210 +0,0 @@
|
||||
---
|
||||
title: Shared State
|
||||
description: Keep your CrewAI agent's state and your app's UI in two-way sync, so edits on either side flow to the other.
|
||||
icon: arrows-rotate
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## One state, both directions
|
||||
|
||||
Shared state is a single state object that the agent and the UI both read and write. The agent updates it as it works and your React components render it live. When the user edits that same state in the UI, the change flows back so the agent sees it on its next turn.
|
||||
|
||||
The classic example is a recipe: the agent drafts it, the user tweaks an ingredient or an instruction, and the agent picks up from the edited version. Neither side owns the state; they share it.
|
||||
|
||||
<Note>
|
||||
Shared state relies on a Flow with custom state. Define an `AgentState` that subclasses `CopilotKitState` and type your Flow as `Flow[AgentState]`. Crews do not carry custom state, so this pattern is Flow-only.
|
||||
</Note>
|
||||
|
||||
## How it works
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Define the shared state on your Flow">
|
||||
|
||||
Subclass `CopilotKitState` so the agent keeps CopilotKit's message plumbing, then add your own fields. Here the shared field is `recipe`.
|
||||
|
||||
```python
|
||||
# recipe_flow.py
|
||||
import json
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from crewai.flow.flow import Flow, start, router, listen
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, CopilotKitState
|
||||
|
||||
|
||||
class Ingredient(BaseModel):
|
||||
name: str
|
||||
amount: str
|
||||
|
||||
|
||||
class Recipe(BaseModel):
|
||||
title: str
|
||||
ingredients: List[Ingredient] = Field(default_factory=list)
|
||||
instructions: List[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class AgentState(CopilotKitState):
|
||||
recipe: Optional[Recipe] = None
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Read and write the state from the agent">
|
||||
|
||||
The agent reads the current state by dumping it into the system prompt, and writes it back by assigning to `self.state.recipe`. A `generate_recipe` tool lets the model return the updated recipe as structured arguments.
|
||||
|
||||
```python
|
||||
GENERATE_RECIPE_TOOL = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "generate_recipe",
|
||||
"description": "Generate or modify the recipe.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"recipe": {"type": "object"}},
|
||||
"required": ["recipe"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class SharedStateFlow(Flow[AgentState]):
|
||||
@start()
|
||||
@listen("route_follow_up")
|
||||
async def start_flow(self):
|
||||
pass
|
||||
|
||||
@router(start_flow)
|
||||
async def chat(self):
|
||||
# The current shared state is visible to the model.
|
||||
system_prompt = f"""You help the user build a recipe.
|
||||
Current recipe: {self.state.model_dump_json(indent=2)}
|
||||
Modify it by calling generate_recipe."""
|
||||
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[*self.state.copilotkit.actions, GENERATE_RECIPE_TOOL],
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
|
||||
if message.tool_calls:
|
||||
call = message.tool_calls[0]
|
||||
if call.function.name == "generate_recipe":
|
||||
args = json.loads(call.function.arguments)
|
||||
self.state.recipe = Recipe(**args["recipe"]) # write to shared state
|
||||
self.state.messages.append({
|
||||
"role": "tool",
|
||||
"content": "Recipe updated.",
|
||||
"tool_call_id": call.id,
|
||||
})
|
||||
return "route_follow_up"
|
||||
return "route_end"
|
||||
|
||||
@listen("route_end")
|
||||
async def end(self):
|
||||
pass
|
||||
```
|
||||
|
||||
Two things make this shared rather than one-way: dumping `self.state` into the prompt means the agent always works from the latest recipe (including edits the user made in the UI), and assigning `self.state.recipe` puts the new value into the state snapshot sent to connected clients at the end of the step. For updates during a long step, emit explicitly with `copilotkit_emit_state` (see [Agentic Generative UI](/edge/en/guides/frontend/agentic-generative-ui)).
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve the Flow over AG-UI">
|
||||
|
||||
Expose the Flow from your FastAPI app with `add_crewai_flow_fastapi_endpoint`, then register it in the CopilotKit runtime. See the [Frontend Overview](/edge/en/guides/frontend/overview) for the full server and runtime setup.
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from recipe_flow import SharedStateFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=SharedStateFlow(),
|
||||
path="/shared_state",
|
||||
)
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Read and write the state from the UI">
|
||||
|
||||
`useAgent` gives you both directions in one hook. Read the shared state off `agent.state`, and write it back with `agent.setState(...)`. Subscribe to `OnStateChanged` so your component re-renders whenever the agent updates the state.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useAgent, UseAgentUpdate } from "@copilotkit/react-core/v2";
|
||||
|
||||
function RecipeEditor() {
|
||||
const { agent } = useAgent({
|
||||
agentId: "shared_state",
|
||||
updates: [UseAgentUpdate.OnStateChanged],
|
||||
});
|
||||
|
||||
const state = agent?.state as { recipe?: Recipe } | undefined;
|
||||
const isLoading = agent?.isRunning;
|
||||
|
||||
const recipe = state?.recipe;
|
||||
|
||||
// setState replaces the whole state object, so spread the current
|
||||
// state and override only the field you changed. Passing just
|
||||
// `{ recipe }` would drop messages and other runtime fields.
|
||||
const updateRecipe = (patch: Partial<Recipe>) =>
|
||||
agent?.setState({ ...(agent.state ?? {}), recipe: { ...(recipe ?? {}), ...patch } });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={recipe?.title ?? ""}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => updateRecipe({ title: e.target.value })}
|
||||
/>
|
||||
{/* render inputs for ingredients and instructions the same way */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`agent.state` reads the shared state, `agent.setState(...)` writes it back so the agent sees the change on its next turn, and `agent.isRunning` reflects whether the agent is currently working.
|
||||
|
||||
<Note>
|
||||
`setState` **replaces** the entire state object rather than merging. Always spread the current state (`{ ...agent.state, ... }`) and override only the fields you are changing, or you will drop the conversation and other runtime fields the agent depends on.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## The two-way loop
|
||||
|
||||
Putting the pieces together, a single recipe object is kept in sync in both directions:
|
||||
|
||||
- **Agent edits, UI updates.** The Flow assigns `self.state.recipe`, the new value ships in the step's state snapshot, and `OnStateChanged` re-renders your inputs.
|
||||
- **User edits, agent sees it.** A change in the UI calls `agent.setState(...)`, and because the Flow dumps `self.state` into its prompt, the agent works from the edited recipe on its next turn.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state as it changes.
|
||||
</Card>
|
||||
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
Stream in-progress state to the UI as the agent works.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Pause the agent to collect user approval or input mid-run.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,235 +0,0 @@
|
||||
---
|
||||
title: Tool-Based Generative UI
|
||||
description: Map a CrewAI agent's tool calls to React components and stream the arguments in as they arrive.
|
||||
icon: puzzle-piece
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## Render tool calls as components
|
||||
|
||||
When your Crew or Flow calls a tool, you rarely want the raw arguments dumped into the chat. Tool-based generative UI maps each tool the agent calls to a React component you own. The agent decides *when* to call the tool; you decide what the user sees.
|
||||
|
||||
Because CopilotKit streams the tool call to the frontend as the model generates it, the arguments fill in progressively. Your component can paint the moment the first field arrives and update as the rest stream in.
|
||||
|
||||
This guide builds a haiku generator: the agent calls a `generate_haiku` tool, and the frontend renders each haiku as a card. It assumes you already have a Crew or Flow talking to a Next.js app. If not, start with the [Frontend Overview](/edge/en/guides/frontend/overview) for the full server, runtime, and provider setup.
|
||||
|
||||
<Note>
|
||||
Tool rendering works with both Crews and Flows. The example below uses a Flow, but the frontend wiring is identical either way.
|
||||
</Note>
|
||||
|
||||
## Walkthrough
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Define the tool on the backend">
|
||||
|
||||
Declare the tool with a JSON schema and pass it to the model. The `copilotkit_stream` wrapper together with `stream=True` is what streams the tool call to the frontend as it is generated, one argument chunk at a time.
|
||||
|
||||
```python
|
||||
# haiku_flow.py
|
||||
from crewai.flow.flow import Flow, start
|
||||
from litellm import acompletion
|
||||
from ag_ui_crewai.sdk import copilotkit_stream, CopilotKitState
|
||||
|
||||
GENERATE_HAIKU_TOOL = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "generate_haiku",
|
||||
"description": "Generate a haiku in Japanese and its English translation",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"japanese": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Three lines in Japanese",
|
||||
},
|
||||
"english": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Three lines in English",
|
||||
},
|
||||
},
|
||||
"required": ["japanese", "english"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class HaikuFlow(Flow[CopilotKitState]):
|
||||
@start()
|
||||
async def chat(self):
|
||||
system_prompt = "You help the user write haikus. Use the generate_haiku tool."
|
||||
|
||||
response = await copilotkit_stream(
|
||||
await acompletion(
|
||||
model="openai/gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
*self.state.messages,
|
||||
],
|
||||
tools=[GENERATE_HAIKU_TOOL],
|
||||
parallel_tool_calls=False,
|
||||
stream=True,
|
||||
)
|
||||
)
|
||||
|
||||
message = response.choices[0].message
|
||||
self.state.messages.append(message)
|
||||
|
||||
if message.tool_calls:
|
||||
self.state.messages.append({
|
||||
"tool_call_id": message.tool_calls[0].id,
|
||||
"role": "tool",
|
||||
"content": "Haiku generated.",
|
||||
})
|
||||
```
|
||||
|
||||
The tool has no Python implementation. It exists only so the model emits a structured call the frontend can render. After the call, append a short tool result so the conversation stays well-formed for the next turn.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Serve the Flow over AG-UI">
|
||||
|
||||
Expose the Flow from your FastAPI app on its own path:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from haiku_flow import HaikuFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=HaikuFlow(),
|
||||
path="/haiku",
|
||||
)
|
||||
```
|
||||
|
||||
Register the agent with the CopilotKit runtime and point `<CopilotKit>` at it exactly as shown in the [Frontend Overview](/edge/en/guides/frontend/overview). The rest of this guide assumes the agent is registered under the id `haiku`.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Register the rendering component">
|
||||
|
||||
On the frontend, call `useRenderTool` with the same `name` the backend declared. `useRenderTool` is the hook for *rendering* a tool call: it takes a `render` function and nothing to execute, because this tool is pure display.
|
||||
|
||||
<Note>
|
||||
Use `useRenderTool` when the tool only draws UI. If the tool also needs to *run* something in the browser, use [`useFrontendTool`](/edge/en/guides/frontend/frontend-actions) instead, which pairs a `handler` with an optional `render`.
|
||||
</Note>
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
import { useRenderTool } from "@copilotkit/react-core/v2";
|
||||
import { z } from "zod";
|
||||
|
||||
useRenderTool({
|
||||
name: "generate_haiku",
|
||||
parameters: z.object({
|
||||
japanese: z.array(z.string()),
|
||||
english: z.array(z.string()),
|
||||
}),
|
||||
render: ({ args, status }) => {
|
||||
if (!args.japanese) return <></>; // still streaming
|
||||
return <HaikuCard japanese={args.japanese} english={args.english} />;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The tool is scoped to the active agent by the `<CopilotKit agent="haiku">` provider, so no `agentId` is needed here. A few things to note:
|
||||
|
||||
- **`name` must match the backend tool name** exactly (`generate_haiku`). That match is how CopilotKit routes the call to this component.
|
||||
- **`render` receives `{ args, status }`.** `args` fills in progressively as the model streams the call; early on it may be empty or partial. `status` moves through `"inProgress"` / `"executing"` to `"complete"` if you want to show a loading state while arguments stream.
|
||||
- **Guard against partial args.** Return an empty fragment until the fields you need exist. Here we wait for `args.japanese` before rendering the card.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Render the haiku">
|
||||
|
||||
The `render` function delegates to an ordinary React component. Nothing about it is CopilotKit-specific: it takes props and returns markup.
|
||||
|
||||
```tsx
|
||||
function HaikuCard({
|
||||
japanese,
|
||||
english,
|
||||
}: {
|
||||
japanese: string[];
|
||||
english: string[];
|
||||
}) {
|
||||
return (
|
||||
<div className="haiku-card">
|
||||
{japanese.map((line, i) => (
|
||||
<div key={i} className="haiku-line">
|
||||
<span className="jp">{line}</span>
|
||||
<span className="en">{english?.[i]}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Because `english` streams in alongside `japanese`, use optional access (`english?.[i]`) so the card renders cleanly while the translation is still arriving.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run it">
|
||||
|
||||
Start both processes and ask the assistant for a haiku. The card renders as the arguments stream in, filling out line by line.
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1
|
||||
npm run dev # terminal 2
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## How progressive rendering works
|
||||
|
||||
The model does not emit the tool call all at once. It streams tokens, and CopilotKit re-invokes your `render` function every time a new chunk of arguments arrives:
|
||||
|
||||
1. The call begins. `args` is empty, so your guard returns an empty fragment.
|
||||
2. `args.japanese` fills in line by line. The card appears and grows.
|
||||
3. `args.english` fills in. Translations slot into place.
|
||||
4. The call completes. `args` holds the final, fully-validated object.
|
||||
|
||||
This is why the partial-args guard matters: `render` runs against incomplete data by design. Read only the fields you have, and let the rest paint as they arrive.
|
||||
|
||||
## Backend tools
|
||||
|
||||
The `generate_haiku` tool above has no Python implementation — it exists only so the model emits a structured call the frontend renders. But a **real tool your Crew or Flow runs server-side** renders the same way.
|
||||
|
||||
When an Agent or Crew executes a tool during its run, the bridge surfaces that tool call along with its **result**. Register a `useRenderTool` for the tool's name and read `result` in the render:
|
||||
|
||||
```tsx
|
||||
useRenderTool({
|
||||
name: "get_weather",
|
||||
parameters: z.object({ location: z.string() }),
|
||||
render: ({ args, result, status }) => {
|
||||
if (status !== "complete") return <WeatherSkeleton location={args.location} />;
|
||||
return <WeatherCard data={JSON.parse(result)} />;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
A backend tool must return a **JSON string**, not a Python dict. The bridge stringifies tool output, so a raw dict arrives as a Python repr the browser cannot `JSON.parse`. Return `json.dumps(...)` from the tool.
|
||||
</Note>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Agentic Generative UI" icon="list-check" href="/edge/en/guides/frontend/agentic-generative-ui">
|
||||
Render live agent state as it changes across a multi-step run.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
Pause the agent to collect user approval or input mid-run.
|
||||
</Card>
|
||||
<Card title="Frontend Actions" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
|
||||
Let the agent call functions that run in the browser.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -120,11 +120,6 @@ def add_defaults(ctx):
|
||||
Rewritten inputs flow into task interpolation, so the run behaves as if it was
|
||||
kicked off with the modified dict.
|
||||
|
||||
Prefer `INPUT` for rewriting and treat `EXECUTION_START` as the allow/deny
|
||||
gate. Rewrites at `EXECUTION_START` are still honored — on crews they also
|
||||
feed the `before_kickoff` callbacks; on flows they land exactly like an
|
||||
`INPUT` rewrite.
|
||||
|
||||
### Output Sanitization
|
||||
|
||||
```python
|
||||
@@ -161,10 +156,9 @@ def report_outcome(ctx):
|
||||
```
|
||||
|
||||
Two caveats: `EXECUTION_END` does not fire when `EXECUTION_START` never
|
||||
dispatched (an abort at start means the boundary never opened, so there is no
|
||||
end to pair), and raising `HookAborted` from a failure-path `EXECUTION_END`
|
||||
dispatch is ignored — there is nothing left to abort, and the original error
|
||||
wins.
|
||||
dispatched (an abort at start counts as the execution never beginning), and
|
||||
raising `HookAborted` from a failure-path `EXECUTION_END` dispatch is ignored —
|
||||
there is nothing left to abort, and the original error wins.
|
||||
|
||||
## Ordering
|
||||
|
||||
@@ -174,19 +168,6 @@ For a crew run the boundary order is:
|
||||
EXECUTION_START → before_kickoff callbacks → INPUT → tasks execute → OUTPUT → EXECUTION_END
|
||||
```
|
||||
|
||||
For a flow run, the boundary hooks resolve the inputs before the lifecycle
|
||||
events begin:
|
||||
|
||||
```
|
||||
EXECUTION_START → INPUT → FlowStartedEvent → flow methods execute → OUTPUT → EXECUTION_END → FlowFinishedEvent
|
||||
```
|
||||
|
||||
`FlowStartedEvent` carries the hook-resolved inputs, and rewriting
|
||||
`inputs["id"]` in a boundary hook redirects state restoration. An abort at
|
||||
`EXECUTION_START` still surfaces as `FlowStartedEvent` followed by
|
||||
`FlowFailedEvent`, emitted at the abort with the payload as resolved by the
|
||||
hooks that ran before it.
|
||||
|
||||
Hooks at the same point run in registration order, global hooks first, then
|
||||
crew-scoped hooks. Telemetry (`HookDispatchedEvent`) is emitted per dispatch.
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ grep -r "llm:" --include="*.yaml" .
|
||||
# llm = LLM(model="mistral/mistral-large-latest")
|
||||
|
||||
# After (Native):
|
||||
llm = LLM(model="gemini/gemini-3.7-flash")
|
||||
llm = LLM(model="gemini/gemini-2.0-flash")
|
||||
```
|
||||
|
||||
```bash
|
||||
@@ -399,7 +399,7 @@ llm = LLM(model="anthropic/claude-haiku-3-5") # Fast & affordable
|
||||
# Together AI → OpenAI or Gemini
|
||||
# llm = LLM(model="together_ai/meta-llama/Meta-Llama-3.1-70B")
|
||||
llm = LLM(model="openai/gpt-4o") # High quality
|
||||
llm = LLM(model="gemini/gemini-3.7-flash") # Fast & capable
|
||||
llm = LLM(model="gemini/gemini-2.0-flash") # Fast & capable
|
||||
|
||||
# Mistral → Anthropic or OpenAI
|
||||
# llm = LLM(model="mistral/mistral-large-latest")
|
||||
|
||||
@@ -141,7 +141,7 @@ You can connect to OpenAI-compatible LLMs using either environment variables or
|
||||
# Example using Gemini's OpenAI-compatible API.
|
||||
os.environ["OPENAI_API_KEY"] = "your-gemini-key" # Should start with AIza...
|
||||
os.environ["OPENAI_API_BASE"] = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-flash" # Add your Gemini model here, under openai/
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-2.0-flash" # Add your Gemini model here, under openai/
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
@@ -159,7 +159,7 @@ You can connect to OpenAI-compatible LLMs using either environment variables or
|
||||
```python Google
|
||||
# Example using Gemini's OpenAI-compatible API
|
||||
llm = LLM(
|
||||
model="openai/gemini-3.7-flash",
|
||||
model="openai/gemini-2.0-flash",
|
||||
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
||||
api_key="your-gemini-key", # Should start with AIza...
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ Planning agents benefit from reasoning models that can handle complex strategic
|
||||
from crewai import Agent, Task, Crew, LLM
|
||||
|
||||
# High-capability reasoning model for strategic planning
|
||||
manager_llm = LLM(model="gemini/gemini-3.7-flash", temperature=0.1)
|
||||
manager_llm = LLM(model="gemini-2.5-flash-preview-05-20", temperature=0.1)
|
||||
|
||||
# Creative model for content generation
|
||||
content_llm = LLM(model="claude-3-5-sonnet-20241022", temperature=0.7)
|
||||
@@ -412,7 +412,7 @@ Rather than repeating the strategic framework, here's a tactical checklist for i
|
||||
# Manager or coordination agents
|
||||
manager_agent = Agent(
|
||||
role="Project Manager",
|
||||
llm=LLM(model="gemini/gemini-3.7-flash"), # Premium for coordination
|
||||
llm=LLM(model="gemini-2.5-flash-preview-05-20"), # Premium for coordination
|
||||
# ... rest of config
|
||||
)
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ Conversational Flows can stream one user turn with `stream_turn()`:
|
||||
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationConfig, ConversationState
|
||||
from crewai.experimental.conversational import ConversationConfig, ConversationState
|
||||
|
||||
|
||||
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)
|
||||
|
||||
@@ -7,9 +7,9 @@ mode: "wide"
|
||||
|
||||
# Arize Phoenix Integration
|
||||
|
||||
This guide demonstrates how to integrate **Arize Phoenix** with **CrewAI** using OpenTelemetry via the [OpenInference](https://github.com/openinference/openinference) SDK. By the end of this guide, you will be able to trace your CrewAI agents and debug agent behavior.
|
||||
This guide demonstrates how to integrate **Arize Phoenix** with **CrewAI** using OpenTelemetry via the [OpenInference](https://github.com/openinference/openinference) SDK. By the end of this guide, you will be able to trace your CrewAI agents and easily debug your agents.
|
||||
|
||||
> **What is Arize Phoenix?** [Arize Phoenix](https://arize.com/phoenix/) is the open-source observability and evaluation option from [Arize AI](https://arize.com/?utm_source=crewai-docs&utm_medium=partner&utm_campaign=partner-docs&utm_content=observability-arize-phoenix). Use Phoenix when you want to run locally or self-host. Use [Arize AX](https://arize.com/products/ax/) for a managed cloud or enterprise self-hosted platform for production AI systems.
|
||||
> **What is Arize Phoenix?** [Arize Phoenix](https://phoenix.arize.com) is an LLM observability platform that provides tracing and evaluation for AI applications.
|
||||
|
||||
[](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
|
||||
|
||||
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
|
||||
|
||||
### Step 2: Set Up Environment Variables
|
||||
|
||||
Configure your Phoenix API key and OpenTelemetry endpoint to send traces to Phoenix. The same setup works with a local or self-hosted Phoenix endpoint by changing the collector URL.
|
||||
Setup Phoenix Cloud API keys and configure OpenTelemetry to send traces to Phoenix. Phoenix Cloud is a hosted version of Arize Phoenix, but it is not required to use this integration.
|
||||
|
||||
You can get your free Serper API key [here](https://serper.dev/).
|
||||
|
||||
@@ -35,8 +35,8 @@ You can get your free Serper API key [here](https://serper.dev/).
|
||||
import os
|
||||
from getpass import getpass
|
||||
|
||||
# Get your Phoenix API key
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix API key: ")
|
||||
# Get your Phoenix Cloud credentials
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
|
||||
|
||||
# Get API keys for services
|
||||
OPENAI_API_KEY = getpass("🔑 Enter your OpenAI API key: ")
|
||||
@@ -44,7 +44,7 @@ SERPER_API_KEY = getpass("🔑 Enter your Serper API key: ")
|
||||
|
||||
# Set environment variables
|
||||
os.environ["PHOENIX_CLIENT_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
|
||||
os.environ["SERPER_API_KEY"] = SERPER_API_KEY
|
||||
```
|
||||
@@ -133,7 +133,7 @@ print(result)
|
||||
|
||||
After running the agent, you can view the traces generated by your CrewAI application in Phoenix. You should see detailed steps of the agent interactions and LLM calls, which can help you debug and optimize your AI agents.
|
||||
|
||||
Open your Phoenix project and navigate to the project you specified in the `project_name` parameter. You'll see a timeline view of your trace with all the agent interactions, tool usages, and LLM calls.
|
||||
Log into your Phoenix Cloud account and navigate to the project you specified in the `project_name` parameter. You'll see a timeline view of your trace with all the agent interactions, tool usages, and LLM calls.
|
||||
|
||||

|
||||
|
||||
@@ -147,9 +147,6 @@ Open your Phoenix project and navigate to the project you specified in the `proj
|
||||
|
||||
### References
|
||||
- [Phoenix Documentation](https://docs.arize.com/phoenix/) - Overview of the Phoenix platform.
|
||||
- [Arize AX](https://arize.com/products/ax/) - Managed cloud and enterprise self-hosted observability and evaluation.
|
||||
- [Arize agent evaluation guide](https://arize.com/guides/ai-agent-handbook/agent-evaluation/) - Production workflow for evaluating agent behavior from traces.
|
||||
- [Arize LLM evaluation guide](https://arize.com/resources/llm-evaluation/) - Methods and metrics for evaluating LLM applications.
|
||||
- [CrewAI Documentation](https://docs.crewai.com/) - Overview of the CrewAI framework.
|
||||
- [OpenTelemetry Docs](https://opentelemetry.io/docs/) - OpenTelemetry guide
|
||||
- [OpenInference GitHub](https://github.com/openinference/openinference) - Source code for OpenInference SDK.
|
||||
|
||||
@@ -34,36 +34,18 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
|
||||
os.environ['OTEL_SDK_DISABLED'] = 'true'
|
||||
```
|
||||
|
||||
### Isolation from your own OpenTelemetry setup
|
||||
|
||||
CrewAI's telemetry runs on its own private `TracerProvider` and never registers
|
||||
itself as the global one. This keeps the two directions separate:
|
||||
|
||||
- Spans from other instrumented libraries in your process — web frameworks,
|
||||
database clients, HTTP clients — are never sent to CrewAI.
|
||||
- CrewAI's telemetry spans are never sent to your observability backend, so they
|
||||
will not appear in Langfuse, Braintrust, Phoenix, or any other collector you
|
||||
configure.
|
||||
|
||||
Observability integrations are unaffected: they instrument CrewAI through their
|
||||
own tracer provider, which is independent of the one described here.
|
||||
|
||||
### Data Explanation:
|
||||
| Defaulted | Data | Reason and Specifics |
|
||||
|:----------|:------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------|
|
||||
| Yes | CrewAI and Python Version | Tracks software versions. Example: CrewAI v1.2.3, Python 3.8.10. No personal data. |
|
||||
| Yes | Crew Metadata | Includes: randomly generated key and ID, process type (e.g., 'sequential', 'parallel'), boolean flag for memory usage (true/false), a boolean flag for whether any inputs were passed to the run (true/false — never the input keys or values, which are only collected when `share_crew` is enabled), count of tasks, count of agents. All non-personal. |
|
||||
| Yes | Crew Metadata | Includes: randomly generated key and ID, process type (e.g., 'sequential', 'parallel'), boolean flag for memory usage (true/false), count of tasks, count of agents. All non-personal. |
|
||||
| Yes | Agent Data | Includes: randomly generated key and ID, role name (should not include personal info), boolean settings (verbose, delegation enabled, code execution allowed), max iterations, max RPM, max retry limit, LLM info (see LLM Attributes), list of tool names (should not include personal info). No personal data. |
|
||||
| Yes | Task Metadata | Includes: randomly generated key and ID, boolean execution settings (async_execution, human_input), associated agent's role and key, list of tool names. All non-personal. |
|
||||
| Yes | Tool Usage Statistics | Includes: tool name (should not include personal info), number of usage attempts (integer), LLM attributes used. No personal data. |
|
||||
| Yes | Test Execution Data | Includes: crew's randomly generated key and ID, number of iterations, model name used, quality score (float), execution time (in seconds). All non-personal. |
|
||||
| Yes | Task Lifecycle Data | Includes: creation and execution start/end times, crew and task identifiers, and whether the task succeeded or failed. When a task fails, the **class name** of the exception is recorded (for example `TimeoutError`) so failures can be counted and diagnosed — never the error message, which can contain prompts, model output, file paths or credentials. Stored as spans with timestamps. No personal data. |
|
||||
| Yes | Task Lifecycle Data | Includes: creation and execution start/end times, crew and task identifiers. Stored as spans with timestamps. No personal data. |
|
||||
| Yes | LLM Attributes | Includes: name, model_name, model, top_k, temperature, and class name of the LLM. All technical, non-personal data. |
|
||||
| Yes | Project Creation using crewAI CLI | Includes: that a new project was scaffolded by `crewai create`, which kind it was (`crew`, `json_crew` or `flow`), and the project ID minted for that new project and written into its own `pyproject.toml`. That is the new project's own ID, recorded separately from the `project_id` of the directory the command was run from — the two can differ. No project name, no file contents, no code. No personal data. |
|
||||
| Yes | Crew Deployment attempt using crewAI CLI | Includes: The fact a deploy is being made and crew id, whether it's trying to pull logs, and whether the deploy was started from a CLI command or from the run TUI. No project or crew contents. No personal data. |
|
||||
| Yes | Execution Environment | Includes: which AI coding assistant is running the process, if any (one of a fixed list such as `claude_code`, `codex`, `cursor`, or `unknown`), where the process runs (one of a fixed list such as `ci`, `container`, `serverless`, `interactive`), the `project_id` from your `pyproject.toml` when one is configured, and a coarse size band for the machine (one of `1-2`, `3-4`, `5-8`, `9-16`, `17-32`, `33+`, or `unknown`). The band is a range, never the exact core count — the exact count is opt-in only, under Environment Information below. The size band comes from the host CPU count; assistant and location detection reads only whether known environment variables are set, never their values. No personal data. |
|
||||
| Yes | Flow Lifecycle Signals | Includes: that a flow started, whether it completed or failed, whether one of its methods failed, whether it paused for human input or feedback, whether the start was a resumed run, whether a conversation turn failed, how long the flow ran, and whether the flow is one CrewAI runs internally or one you wrote. The flow name is recorded, as it already is for flow creation and execution. When a flow or one of its methods fails, the **class name** of the exception is recorded (for example `TimeoutError`) so that failures can be diagnosed — never the error message, which can contain prompts, model output, file paths or credentials. Method names and flow state are never recorded. No personal data. |
|
||||
| Yes | Trace Sharing Signal | Includes: that a batch of traces was successfully shared with CrewAI AMP, and whether it was shared anonymously (before you have an account) or linked to your account. Like every span, it also carries the Execution Environment attributes described above (`project_id` when configured, the coding assistant, and the runtime). This row describes sharing telemetry only — not the trace contents or access granted by shared trace links. Trace contents, inputs, and outputs are never recorded on this signal. Before sharing traces, review secrets, personal data, and AMP redaction and retention settings. |
|
||||
| Yes | Crew Deployment attempt using crewAI CLI | Includes: The fact a deploy is being made and crew id, and if it's trying to pull logs, no other data. |
|
||||
| No | Agent's Expanded Data | Includes: goal description, backstory text, i18n prompt file identifier. Users should ensure no personal info is included in text fields. |
|
||||
| No | Detailed Task Information | Includes: task description, expected output description, context references. Users should ensure no personal info is included in these fields. |
|
||||
| No | Environment Information | Includes: platform, release, system, version, and CPU count. Example: 'Windows 10', 'x86_64'. No personal data. |
|
||||
|
||||
@@ -77,4 +77,4 @@ To let an agent read a directory tree outside the working directory, point `base
|
||||
file_read_tool = FileReadTool(base_dir='/data')
|
||||
```
|
||||
|
||||
As a last resort, setting `CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true` disables path validation. This applies process-wide to every crewai-tools tool, including the SSRF protections on URL-fetching tools, so prefer `base_dir`. Managed workers should set `CREWAI_TOOLS_FORCE_SAFE_PATHS=true` so a tenant cannot disable those checks by exporting the escape hatch.
|
||||
As a last resort, setting `CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true` disables path validation. This applies process-wide to every crewai-tools tool, including the SSRF protections on URL-fetching tools, so prefer `base_dir`.
|
||||
|
||||
@@ -9,7 +9,7 @@ mode: "wide"
|
||||
|
||||
## Description
|
||||
|
||||
The `ScrapeElementFromWebsiteTool` is designed to extract specific elements from websites using CSS selectors. This tool allows CrewAI agents to scrape targeted content from web pages, making it useful for data extraction tasks where only specific parts of a webpage are needed. Fetches go through CrewAI's SSRF-safe HTTP helper: the requested URL and every redirect hop are checked against private and reserved ranges (including cloud metadata), and the TCP connection is pinned to an IP that passed that check.
|
||||
The `ScrapeElementFromWebsiteTool` is designed to extract specific elements from websites using CSS selectors. This tool allows CrewAI agents to scrape targeted content from web pages, making it useful for data extraction tasks where only specific parts of a webpage are needed.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ mode: "wide"
|
||||
A tool designed to extract and read the content of a specified website. It is capable of handling various types of web pages by making HTTP requests and parsing the received HTML content.
|
||||
This tool can be particularly useful for web scraping tasks, data collection, or extracting specific information from websites.
|
||||
|
||||
Fetches go through CrewAI's SSRF-safe HTTP helper: the requested URL and every redirect hop are checked against private and reserved ranges (including cloud metadata), and the TCP connection is pinned to an IP that passed that check.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the crewai_tools package
|
||||
|
||||
@@ -4,232 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 8월 27일">
|
||||
## v1.15.18
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.18)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 대화 흐름을 안정 상태로 승격
|
||||
- 주어진 UUID로 생성된 배포를 기록
|
||||
- 대화 흐름 문서 및 API 개선
|
||||
- 선언이 라우터의 응답 형식을 지정하도록 허용
|
||||
- 채팅 흐름이 자체 상태 형태를 선언하도록 허용
|
||||
- 대화 선언에서 크루 스타일 LLM 구성 수용
|
||||
- 발급된 ID로 프로젝트 생성 보고
|
||||
- 실행에 입력이 있었는지 여부를 기록하되 입력은 기록하지 않음
|
||||
- 모든 사용자 호출 프로젝트 명령에서 프로젝트 ID를 백필
|
||||
|
||||
### 버그 수정
|
||||
- 최종 답변이 비어 있을 때 도구 결과 보존
|
||||
- 기본 Claude Sonnet 4.6을 1M 컨텍스트 윈도우에 매핑
|
||||
- 대형 도구 호출을 위한 Anthropic 기본 max_tokens 증가
|
||||
- 메시지 내용 부분을 텍스트로 렌더링, Python repr로 렌더링하지 않음
|
||||
- Agent.kickoff가 대화를 받을 때 메시지 역할 유지
|
||||
- crewai 내부 흐름에서 가로채기 후크 건너뛰기
|
||||
- 작업 실패를 실패로 기록하고 성공으로 기록하지 않음
|
||||
- 억제된 재개에서 흐름 생명 주기 방출
|
||||
- 선언적 채팅 흐름을 위한 대화형 TUI 열기
|
||||
- crew_memory를 문자열로 기록하고 불리언으로 기록하지 않음
|
||||
- 항상 project_id를 방출하여 부재 및 비어 있는 상태를 구분
|
||||
|
||||
### 문서
|
||||
- Arize Phoenix 가시성 문서 명확화
|
||||
|
||||
## 기여자
|
||||
|
||||
@Vidit-Ostwal, @arizedatngo, @joaomdmoura, @lorenzejay, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 19일">
|
||||
## v1.15.17
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.17)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 선언적 대화 흐름 문서 추가
|
||||
- 선언을 위한 내장 대화 방법 합성
|
||||
- 선언이 대화 모드를 주도할 수 있도록 활성화
|
||||
- 대화 선택 참여를 명확하게 표시
|
||||
- 슬러그 참조에서 해결된 도구에 AMP 슬러그 전달
|
||||
- 청크 처리 중 과도한 단일 메시지 처리
|
||||
|
||||
### 버그 수정
|
||||
- MCP HTTP 및 SSE server_name으로 URL 호스트 이름 사용 수정
|
||||
- 모든 실패한 시도에서 에이전트 범위 닫기
|
||||
- 도구 오류를 실패한 도구에 귀속
|
||||
- 각 리디렉션 홉 및 피어 IP에 SSRF 검사 고정
|
||||
- OpenAI Responses API를 통해 깨진 네이티브 도구 호출 문제 해결
|
||||
|
||||
### 문서
|
||||
- v1.15.16에 대한 스냅샷 및 변경 로그로 문서 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@Copilot, @Vidit-Ostwal, @github-code-quality[bot], @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 13일">
|
||||
## v1.15.16
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.16)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- UUID 지원을 통한 실행 컨텍스트 관리 도입
|
||||
- 흐름을 종료한 예외의 종류 기록
|
||||
- 트레이스 배치가 AMP와 공유된 시점 기록
|
||||
- 모든 출처에서 배포를 카운트하고 시작 위치 기록
|
||||
|
||||
### 버그 수정
|
||||
- 모든 생성된 스팬에서 실행 중인 릴리스를 기록
|
||||
- MySQL 검색 테이블 이름 유효성 검사 수정
|
||||
- 실패한 턴이 다음 턴을 실패로 표시하지 않도록 중지
|
||||
|
||||
### 문서
|
||||
- CopilotKit 및 AG-UI에 대한 프론트엔드 가이드 추가
|
||||
|
||||
## 기여자
|
||||
|
||||
@joaomdmoura, @lorenzejay, @ranst91, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 11일">
|
||||
## v1.15.15
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.15)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 보고서 흐름 결과, 지속 시간 및 인간 개입 신호를 보고합니다.
|
||||
|
||||
### 버그 수정
|
||||
- 경계 후크가 흐름을 중단할 때 FlowStartedEvent를 발생시킵니다.
|
||||
- 범위 스팬 내보내기를 우리 고유의 트레이서 제공자로 제한합니다.
|
||||
- 보안 취약점을 해결하기 위해 torch를 2.13.0 버전으로 업데이트합니다.
|
||||
- crewai-tools[github]에서 gitpython을 3.1.58 버전으로 업데이트합니다.
|
||||
|
||||
### 리팩토링
|
||||
- 에이전트의 날짜 주입 기능을 업데이트합니다.
|
||||
- CLI 플래그를 케밥 케이스로 표준화합니다.
|
||||
|
||||
### 문서
|
||||
- v1.15.14에 대한 스냅샷 및 변경 로그.
|
||||
|
||||
## 기여자
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 8일">
|
||||
## v1.15.14
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.14)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 코딩 에이전트와 런타임 컨텍스트 분리 및 프로젝트 ID 추가
|
||||
|
||||
### 문서
|
||||
- v1.15.13에 대한 스냅샷 및 변경 로그 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 7일">
|
||||
## v1.15.13
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.13)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 버그 수정
|
||||
- LiteLLM 라우팅 모델에서 제공자의 보존 문제 수정.
|
||||
- 취약한 LLM 이벤트 버스 모의 객체 강화.
|
||||
- Anthropic 캐시 토큰 사용량의 과소 보고 문제 수정.
|
||||
- 보안 취약점 GHSA-6hr6-w5qg-qmwg를 해결하기 위해 h2를 버전 4.4.1로 업데이트.
|
||||
|
||||
### 문서
|
||||
- 로케일 동기화를 위한 DOCS_TRANSLATIONS 워크플로 추가.
|
||||
- 깨진 README 링크, 목차 및 기여 가이드 수정.
|
||||
- 버전 1.15.12에 대한 스냅샷 및 변경 로그.
|
||||
|
||||
## 기여자
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 5일">
|
||||
## v1.15.12
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.12)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 릴리스 시 Flow 카나리 버전 증가
|
||||
- 임의의 URL을 읽기 위한 URLReadTool 추가
|
||||
- 플랫폼 액션 도구에 앱 메타데이터 추가
|
||||
- `crewai create <resource>` 아래에서 스캐폴딩 통합
|
||||
|
||||
### 버그 수정
|
||||
- 대화형 경로/핸들러 이름 충돌 오류 명확화
|
||||
|
||||
### 문서
|
||||
- 통합된 생성 CLI를 위한 AGENTS.md 스캐폴드 업데이트
|
||||
|
||||
### 주요 변경 사항
|
||||
- 없음
|
||||
|
||||
## 기여자
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 8월 4일">
|
||||
## v1.15.11
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.15.11)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 텔레메트리에서 인터셉션 훅 디스패치를 추적합니다.
|
||||
- OSS 사용을 기업 계정에 연결하기 위해 project_id를 추가합니다.
|
||||
- AGENTS.md에서 AMP를 표시하고 텔레메트리에서 코딩 에이전트를 감지합니다.
|
||||
- IBM Db2 검색 도구를 추가합니다.
|
||||
|
||||
### 버그 수정
|
||||
- CodeQL의 불완전한 URL 하위 문자열 정화 경고를 지웁니다.
|
||||
- 여섯 개의 GHSA 권고 사항을 해결하기 위해 aiohttp와 cryptography를 업데이트합니다.
|
||||
- 맵 리터럴 내 실패에 대한 실제 CEL 오류를 보고합니다.
|
||||
- 문서 전용 PR에 대해 코드 CI를 올바르게 건너뜁니다.
|
||||
|
||||
### 문서
|
||||
- v1.15.10에 대한 스냅샷 및 변경 로그
|
||||
|
||||
## 기여자
|
||||
|
||||
@PawanThakurIBM, @Vidit-Ostwal, @gabemilani, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 7월 31일">
|
||||
## v1.15.10
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ CrewAI AOP에는 코드를 작성하지 않고도 에이전트 생성 및 구성
|
||||
| **컨텍스트 윈도우 준수** _(옵션)_ | `respect_context_window` | `bool` | 메시지를 컨텍스트 윈도우 크기 내로 유지하기 위하여 요약 기능을 사용합니다. 기본값은 True입니다. |
|
||||
| **코드 실행 모드** _(옵션)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | 코드 실행 모드: 'safe'(Docker 사용) 또는 'unsafe'(직접 실행). 기본값은 'safe'입니다. |
|
||||
| **멀티모달** _(옵션)_ | `multimodal` | `bool` | 에이전트가 멀티모달 기능을 지원하는지 여부입니다. 기본값은 False입니다. |
|
||||
| **날짜 자동 삽입** _(옵션)_ | `inject_date` | `bool` | 에이전트 프롬프트에 현재 날짜를 자동으로 삽입할지 여부입니다. 기본값은 False입니다. |
|
||||
| **날짜 자동 삽입** _(옵션)_ | `inject_date` | `bool` | 작업에 현재 날짜를 자동으로 삽입할지 여부입니다. 기본값은 False입니다. |
|
||||
| **날짜 형식** _(옵션)_ | `date_format` | `str` | inject_date 활성화 시 날짜 표시 형식 문자열입니다. 기본값은 "%Y-%m-%d"(ISO 포맷)입니다. |
|
||||
| **추론** _(옵션)_ | `reasoning` | `bool` | 에이전트가 작업을 실행하기 전에 반영 및 플랜을 생성할지 여부입니다. 기본값은 False입니다. |
|
||||
| **최대 추론 시도 수** _(옵션)_ | `max_reasoning_attempts` | `Optional[int]` | 작업 실행 전 최대 추론 시도 횟수입니다. 설정하지 않으면 준비될 때까지 시도합니다. |
|
||||
@@ -267,7 +267,7 @@ strategic_agent = Agent(
|
||||
role="Market Analyst",
|
||||
goal="Track market movements with precise date references and strategic planning",
|
||||
backstory="Expert in time-sensitive financial analysis and strategic reporting",
|
||||
inject_date=True, # Automatically inject current date into the prompt
|
||||
inject_date=True, # Automatically inject current date into tasks
|
||||
date_format="%B %d, %Y", # Format as "May 21, 2025"
|
||||
reasoning=True, # Enable strategic planning
|
||||
max_reasoning_attempts=2, # Limit planning iterations
|
||||
@@ -328,7 +328,7 @@ multimodal_agent = Agent(
|
||||
#### 고급 기능
|
||||
- `multimodal`: 텍스트와 시각적 콘텐츠 처리를 위한 멀티모달 기능 활성화
|
||||
- `reasoning`: 에이전트가 작업을 수행하기 전에 반영하고 계획을 작성할 수 있도록 활성화
|
||||
- `inject_date`: 현재 날짜를 에이전트 프롬프트에 자동으로 삽입
|
||||
- `inject_date`: 현재 날짜를 작업 설명에 자동으로 삽입
|
||||
|
||||
#### 템플릿
|
||||
- `system_template`: 에이전트의 핵심 동작을 정의합니다
|
||||
@@ -630,11 +630,6 @@ messages = [
|
||||
result = researcher.kickoff(messages)
|
||||
```
|
||||
|
||||
마지막 `user` 메시지가 에이전트가 답변할 요청입니다. 나머지 메시지는 각자의 역할과
|
||||
그 요청을 기준으로 한 위치를 그대로 유지하므로, assistant 또는 tool 메시지로 끝나는
|
||||
대화도 사용자의 질문을 그대로 전달하며 뒤따르는 턴도 요청 뒤에 그대로 전달됩니다.
|
||||
`user` 메시지가 전혀 없으면 마지막 메시지를 요청으로 처리합니다.
|
||||
|
||||
### 비동기 지원
|
||||
|
||||
동일한 매개변수를 사용하는 비동기 버전은 `kickoff_async()`를 통해 사용할 수 있습니다:
|
||||
|
||||
@@ -54,16 +54,6 @@ crewai create flow my_new_flow
|
||||
|
||||
기본적으로 `crewai create crew`는 `crew.jsonc`와 `agents/*.jsonc`가 있는 JSON-first 프로젝트를 만듭니다. `crew.py`, `config/agents.yaml`, `config/tasks.yaml`을 사용하는 기존 Python/YAML 스캐폴드가 필요할 때만 `crewai create crew my_new_crew --classic`을 사용하세요.
|
||||
|
||||
#### 사용 중단된 플래그 별칭
|
||||
|
||||
이전 snake_case 플래그는 여전히 동작하지만 `--help`에는 표시되지 않습니다. 아래 각 명령 섹션에 문서화된 kebab-case 형식을 사용하세요.
|
||||
|
||||
| 사용 중단 | 대신 사용 |
|
||||
| :--- | :--- |
|
||||
| `--skip_provider` (`crewai create crew`) | `--skip-provider` |
|
||||
| `--n_iterations` (`crewai train`, `crewai test`) | `--n-iterations` |
|
||||
| `--task_id` (`crewai replay`) | `--task-id` |
|
||||
|
||||
### 2. 버전
|
||||
|
||||
설치된 CrewAI의 버전을 표시합니다.
|
||||
@@ -89,7 +79,7 @@ crewai version --tools
|
||||
crewai train [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: crew를 훈련할 반복 횟수 (기본값: 5)
|
||||
- `-n, --n_iterations INTEGER`: crew를 훈련할 반복 횟수 (기본값: 5)
|
||||
- `-f, --filename TEXT`: 훈련에 사용할 커스텀 파일의 경로 (기본값: "trained_agents_data.pkl")
|
||||
|
||||
예시:
|
||||
@@ -106,7 +96,7 @@ crewai train -n 10 -f my_training_data.pkl
|
||||
crewai replay [OPTIONS]
|
||||
```
|
||||
|
||||
- `-t, --task-id TEXT`: 이 task ID에서부터 crew를 다시 재생하며, 이후의 모든 task를 포함합니다.
|
||||
- `-t, --task_id TEXT`: 이 task ID에서부터 crew를 다시 재생하며, 이후의 모든 task를 포함합니다.
|
||||
|
||||
예시:
|
||||
|
||||
@@ -153,7 +143,7 @@ crew를 테스트하고 결과를 평가합니다.
|
||||
crewai test [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: crew를 테스트할 반복 횟수 (기본값: 3)
|
||||
- `-n, --n_iterations INTEGER`: crew를 테스트할 반복 횟수 (기본값: 3)
|
||||
- `-m, --model TEXT`: Crew에서 테스트를 실행할 LLM 모델 (기본값: "gpt-4o-mini")
|
||||
|
||||
예시:
|
||||
|
||||
@@ -324,8 +324,6 @@ crew는 메모리(단기, 장기 및 엔티티 메모리)를 활용하여 시간
|
||||
|
||||
crew 실행 후, `usage_metrics` 속성에 접근하여 crew가 실행한 모든 작업에 대한 언어 모델(LLM) 사용 메트릭을 확인할 수 있습니다. 이를 통해 운영 효율성과 개선이 필요한 영역에 대한 인사이트를 얻을 수 있습니다.
|
||||
|
||||
`total_tokens`는 청구된 총합(`prompt_tokens + completion_tokens`)입니다. `cached_prompt_tokens` 및 `cache_creation_tokens`와 같은 breakdown 필드는 이미 해당 총합에 포함된 부분 집합을 설명하며 `total_tokens` 위에 다시 더하지 않습니다. 전체 계약은 Flows 개념 문서의 **UsageMetrics field semantics** 섹션을 참조하세요.
|
||||
|
||||
```python Code
|
||||
# Access the crew's usage metrics
|
||||
crew = Crew(agents=[agent1, agent2], tasks=[task1, task2])
|
||||
|
||||
@@ -261,24 +261,6 @@ print(flow.usage_metrics)
|
||||
**전체** 토큰 집계가 필요할 때는 항상 `flow.usage_metrics`를 사용하십시오.
|
||||
</Note>
|
||||
|
||||
### UsageMetrics 필드 의미
|
||||
|
||||
반환되는 [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) 객체는 제공자 중립 계약을 사용합니다:
|
||||
|
||||
| 필드 | 의미 |
|
||||
| --- | --- |
|
||||
| `total_tokens` | 청구된 총합: `prompt_tokens + completion_tokens` |
|
||||
| `prompt_tokens` | 요청에 대해 청구된 전체 입력/프롬프트 토큰 |
|
||||
| `completion_tokens` | 요청에 대해 청구된 출력/완료 토큰 |
|
||||
| `cached_prompt_tokens` | 프롬프트 토큰 중 캐시 읽기 부분 집합 (breakdown 전용) |
|
||||
| `cache_creation_tokens` | 프롬프트 토큰 중 캐시 쓰기 부분 집합 (breakdown 전용, Anthropic) |
|
||||
| `reasoning_tokens` | 제공자가 별도로 보고하는 추론/사고 부분 집합 (breakdown 전용) |
|
||||
| `successful_requests` | 집계된 LLM 호출 수 |
|
||||
|
||||
`cached_prompt_tokens`, `cache_creation_tokens`, `reasoning_tokens`와 같은 breakdown 필드는 `total_tokens` **위에 추가되지 않습니다** — 이미 `prompt_tokens` 또는 `completion_tokens`에 포함된 부분을 설명합니다.
|
||||
|
||||
Anthropic의 경우 캐시 읽기 및 쓰기 카운터가 `prompt_tokens`에 포함되므로, 캐시된 워크로드가 `total_tokens`에 완전히 반영됩니다. OpenAI 스타일 제공자는 캐시된 입력을 이미 `prompt_tokens`에 포함합니다. CrewAI는 가시성을 위해 캐시된 부분을 별도로 표시합니다.
|
||||
|
||||
반환되는 [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py)의 각 항목은 단일 `flow.kickoff()` 실행 동안 발생한 모든 LLM 호출의 합계입니다. 다음 `kickoff()` 호출(및 `kickoff_for_each`의 각 반복)에서 카운터가 초기화되므로 연속 실행이 이중으로 집계되지 않습니다. 이 속성은 `kickoff()` 완료 후 언제든지 안전하게 읽을 수 있으며, 실행 중에 읽으면 그 시점까지 누적된 부분 합계를 반환합니다.
|
||||
|
||||
## 플로우 상태 관리
|
||||
|
||||
@@ -270,21 +270,6 @@ CrewAI는 고유한 기능, 인증 방법, 모델 역량을 제공하는 다양
|
||||
)
|
||||
```
|
||||
|
||||
**토큰 사용량 및 프롬프트 캐싱:**
|
||||
|
||||
Anthropic은 청구된 입력을 별도 카운터로 보고합니다 — `input_tokens`(캐시되지 않은 입력), `cache_read_input_tokens`, `cache_creation_input_tokens`. CrewAI는 세 값을 모두 `prompt_tokens`(및 제공자 응답의 네이티브 `input_tokens`)에 포함시켜 캐시된 워크로드에서 `total_tokens`가 전체 청구 사용량을 반영하도록 합니다.
|
||||
|
||||
`cached_prompt_tokens`는 캐시 읽기 부분을 breakdown으로만 기록합니다. 이미 `prompt_tokens`에 포함되어 있으므로 `total_tokens`에 다시 더하면 안 됩니다. `cache_creation_tokens`도 캐시 쓰기를 같은 방식으로 기록합니다.
|
||||
|
||||
```python Code
|
||||
usage = llm.get_token_usage_summary()
|
||||
# total_tokens == prompt_tokens + completion_tokens
|
||||
# prompt_tokens includes cache read + cache write for Anthropic
|
||||
```
|
||||
|
||||
`crew.usage_metrics` 및 `flow.usage_metrics`에 사용되는 제공자 중립 계약은
|
||||
Flows 개념 문서의 **UsageMetrics field semantics** 섹션을 참조하세요.
|
||||
|
||||
현재 모델 ID와 기능은 Anthropic의 [모델 개요](https://platform.claude.com/docs/en/about-claude/models/overview)를 확인하고, 프로덕션에서 모델을 고정하기 전에 [모델 지원 중단 표](https://platform.claude.com/docs/en/about-claude/model-deprecations)를 검토하세요.
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -736,7 +736,7 @@ memory = Memory(llm="anthropic/claude-3-haiku-20240307")
|
||||
memory = Memory(llm="ollama/llama3.2")
|
||||
|
||||
# Google Gemini 사용
|
||||
memory = Memory(llm="gemini/gemini-3.7-flash")
|
||||
memory = Memory(llm="gemini/gemini-2.0-flash")
|
||||
|
||||
# 사용자 정의 설정이 있는 사전 구성된 LLM 인스턴스 전달
|
||||
llm = LLM(model="gpt-4o", temperature=0)
|
||||
|
||||
@@ -20,7 +20,7 @@ crewai test
|
||||
더 많은 반복 횟수로 실행하거나 다른 모델을 사용하려면 다음과 같이 매개변수를 지정할 수 있습니다:
|
||||
|
||||
```bash
|
||||
crewai test --n-iterations 5 --model gpt-4o
|
||||
crewai test --n_iterations 5 --model gpt-4o
|
||||
```
|
||||
|
||||
또는 축약형을 사용할 수 있습니다:
|
||||
@@ -29,11 +29,6 @@ crewai test --n-iterations 5 --model gpt-4o
|
||||
crewai test -n 5 -m gpt-4o
|
||||
```
|
||||
|
||||
<Note>
|
||||
이전 `--n_iterations` 플래그는 여전히 동작하지만 사용 중단되었으며 `--help`에는
|
||||
표시되지 않습니다. 대신 `--n-iterations`(또는 `-n`)를 사용하세요.
|
||||
</Note>
|
||||
|
||||
`crewai test` 명령어를 실행하면 crew가 지정한 횟수만큼 실행되고, 수행이 끝나면 성능 지표가 표시됩니다.
|
||||
|
||||
실행 마지막에 표시되는 점수 표는 다음과 같은 지표로 crew의 성능을 보여줍니다:
|
||||
|
||||
@@ -75,7 +75,7 @@ research_crew/
|
||||
}
|
||||
```
|
||||
|
||||
`provider/model-id`를 `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, `gemini/gemini-3.7-flash` 같은 모델로 바꾸세요.
|
||||
`provider/model-id`를 `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, `gemini/gemini-2.0-flash-001` 같은 모델로 바꾸세요.
|
||||
|
||||
## 3단계: 태스크와 Crew 설정
|
||||
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
---
|
||||
title: 대화형 Flow
|
||||
description: 턴별 handle_turn, 메시지 기록, 의도 라우팅, 트레이싱, 구조화된 스트리밍으로 멀티턴 채팅 앱을 만듭니다.
|
||||
description: 턴마다 kickoff, 메시지 기록, 의도 라우팅, 트레이싱, WebSocket 브리지로 멀티턴 채팅 앱을 만듭니다.
|
||||
icon: comments
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
대화형 앱은 각 사용자 입력을 **동일한 세션 id**로 **새 flow 실행**으로 처리합니다. CrewAI는 메시지 기록, 선택적 의도 라우팅, 지연 트레이싱, 구조화된 턴 스트리밍, 로컬 `flow.chat()` REPL을 위한 헬퍼를 제공합니다.
|
||||
대화형 앱은 각 사용자 입력을 **동일한 세션 id**로 **새 flow 실행**으로 처리합니다. CrewAI는 메시지 기록, 선택적 의도 분류, 지연 트레이싱, UI 브리지, 그리고 대화형 flow용 로컬 `flow.chat()` REPL을 제공합니다.
|
||||
|
||||
| 개념 | 구현 |
|
||||
|------|------|
|
||||
| 세션 id | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
|
||||
| 사용자 입력 | `handle_turn(message)`가 그래프 실행 전 `state.messages`에 추가 |
|
||||
| 턴 완료 | `conversation_turn_completed`; 기본 trace 지연을 사용하면 `FlowFinished`는 `finalize_session_traces()`까지 대기 |
|
||||
| 턴 완료 | `FlowFinished`는 **이번 실행**만 의미; 다음 `handle_turn`로 대화 계속 |
|
||||
| 세션 전체 트레이스 | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
|
||||
|
||||
## 턴 API
|
||||
|
||||
REST, WebSocket, 테스트, 커스텀 UI에서 오는 모든 사용자 메시지에는 **`flow.handle_turn(message, session_id=...)`**를 사용하세요. 대화형 `Flow`를 로컬 터미널 채팅 루프로 실행하고 싶을 때는 **`flow.chat()`**을 사용하세요.
|
||||
|
||||
`Flow.kickoff()`는 `user_message=` 또는 `session_id=` 키워드 인자를 받지 않습니다. 대화형 flow에서는 `handle_turn()`이 보류 중인 메시지를 저장하고 턴별 실행 상태를 초기화한 뒤 내부적으로 `kickoff(inputs={"id": session_id})`를 호출합니다.
|
||||
`Flow.kickoff()`는 `user_message=` 또는 `session_id=` 키워드 인자를 받지 않습니다. 대화형 flow에서는 `handle_turn()`이 보류 중인 메시지를 저장하고 내부적으로 `kickoff(inputs={"id": session_id})`를 호출합니다.
|
||||
|
||||
| API | 용도 |
|
||||
|-----|------|
|
||||
| `handle_turn(message, session_id=...)` | 대화형 `Flow`용 한 턴 편의 래퍼 |
|
||||
| `stream_turn(message, session_id=...)` | 대화형 한 턴을 순서가 보장된 런타임 frame으로 스트리밍 |
|
||||
| `chat()` | 대화형 `Flow`용 로컬 터미널 REPL |
|
||||
| `kickoff(inputs={...})` | 대화형 턴 처리 없이 flow를 직접 실행하는 고급 용도 |
|
||||
| `kickoff(inputs={...})` | 대화형 턴 처리 없이 flow를 직접 실행 |
|
||||
| `ask()` | 한 스텝 **내부** 블로킹 프롬프트 (마법사, 확인) |
|
||||
| `@human_feedback` | **스텝 출력** 승인/거부 — 다음 채팅 줄이 아님 |
|
||||
|
||||
대화형 모드가 활성화되지 않으면 `handle_turn()`, `stream_turn()`, `chat()`은 `ValueError`를 발생시킵니다. `@ConversationConfig(...)`를 적용하면 자동으로 활성화되며, 그렇지 않으면 `conversational = True`로 설정하세요.
|
||||
| `ChatSession.handle_turn(...)` | `handle_turn` 위의 전송 계층 (SSE / WebSocket) |
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
@@ -40,7 +38,7 @@ from uuid import uuid4
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
)
|
||||
@@ -48,6 +46,8 @@ from crewai.flow import (
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
def route_turn(self, context):
|
||||
message = self.state.current_user_message or ""
|
||||
if "주문" in message or "order" in message.lower():
|
||||
@@ -85,43 +85,35 @@ finally:
|
||||
flow.finalize_session_traces() # 전체 대화에 대한 단일 trace 링크
|
||||
```
|
||||
|
||||
## 턴 스트리밍
|
||||
|
||||
UI나 런타임에서 한 채팅 턴의 구조화된 이벤트가 필요하면 `stream_turn()`을 사용하세요. Flow 라우팅, LLM chunk, tool 활동, 대화 메시지를 순서가 보장된 frame으로 제공하는 stream session을 반환합니다.
|
||||
|
||||
```python
|
||||
stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
result = stream.result
|
||||
```
|
||||
|
||||
전체 frame 계약과 channel 목록은 [스트리밍 런타임 계약](/edge/ko/learn/streaming-runtime-contract)을 참고하세요.
|
||||
|
||||
## 턴 생명주기
|
||||
|
||||
각 `handle_turn`은 다음 파이프라인을 실행합니다:
|
||||
|
||||
1. **턴 설정** — 보류 중인 사용자 메시지를 저장하고 세션 id를 결정하며 턴별 실행 추적을 초기화한 뒤 `kickoff(inputs={"id": session_id})`를 호출.
|
||||
1. **`_configure_conversational_kickoff`** — `session_id` / `user_message`를 `inputs`에 병합, `ConversationalConfig` 적용, 설정 시 지연 트레이싱 활성화.
|
||||
2. **상태 복원** — `inputs["id"]`가 있고 `@persist`가 설정되면 최신 스냅샷 로드.
|
||||
3. **`FlowStarted`** — 지연 세션의 첫 턴에서만 발생.
|
||||
4. **보류 중인 턴 수화** — 사용자 메시지를 `state.messages`에 추가하고 `current_user_message` / `last_user_message`를 설정하며, `intents` / `default_intents` + `intent_llm` 설정 시 선택적으로 분류.
|
||||
5. **그래프 실행** — 사용자 정의 `@start` 메서드(있는 경우) → `route_conversation`(내장 start/router) → 선택된 `@listen` 핸들러. `route_conversation`은 재정의 가능한 `conversation_start()` 헬퍼도 호출합니다.
|
||||
4. **`prepare_conversational_turn`** — 사용자 메시지를 `state.messages`에 추가, `last_user_message` 설정, `last_intent` 초기화, `intents` / `default_intents` + `intent_llm` 설정 시 분류.
|
||||
5. **그래프 실행** — `@start` → `@router` → `@listen` 핸들러.
|
||||
6. **실행 종료** — 지연 활성화 시 턴별 `flow_finished` 및 trace 종료 **건너뜀**; 중첩 `Agent.kickoff()` / crew도 부모 batch를 닫지 않음.
|
||||
|
||||
핸들러는 보이는 응답이 반환값과 다를 때, 또는 히스토리를 자를 때 **`append_assistant_message(reply)`**를 호출하세요. public 문자열 반환값도 assistant로 기록되며 `@persist` 스냅샷에 포함되므로, 새 Flow 인스턴스에서도 복원됩니다. 사용자 입력은 `handle_turn`이 이미 저장합니다 — 핸들러에서 다시 추가하지 마세요.
|
||||
핸들러는 **`append_assistant_message(reply)`**를 호출해 다음 턴의 `conversation_messages`에 어시스턴트 응답이 포함되게 하세요. 사용자 입력은 `handle_turn`이 이미 저장합니다 — 핸들러에서 다시 추가하지 마세요.
|
||||
|
||||
## 설정 개요
|
||||
## `ConversationalConfig` (클래스 수준 기본값)
|
||||
|
||||
`Flow` 서브클래스에 `ConversationConfig`를 데코레이터로 적용하면 채팅 기본값이 부착되고 대화형 모드도 활성화됩니다. 아래의 [전체 필드 레퍼런스](#conversationconfig)를 참고하세요. 턴마다 `handle_turn(..., intents=..., intent_llm=...)`로 사전 분류 설정을 재정의할 수 있습니다.
|
||||
`Flow` 서브클래스에 `conversational_config: ClassVar[ConversationalConfig | None]`로 설정합니다.
|
||||
|
||||
## 하위 수준 `ChatState` 헬퍼
|
||||
| 필드 | 기본값 | 목적 |
|
||||
|------|--------|------|
|
||||
| `default_intents` | `None` | kickoff 전 자동 분류용 outcome 라벨 |
|
||||
| `intent_llm` | `None` | 분류용 모델 (intent 사용 시 필수) |
|
||||
| `interactive_prompt` | `"You: "` | `kickoff(interactive=True)` 프롬프트 |
|
||||
| `interactive_timeout` | `None` | 대화형 모드 줄 단위 타임아웃 |
|
||||
| `exit_commands` | `exit`, `quit` | 대화형 모드 종료 단어 |
|
||||
| `defer_trace_finalization` | `True` | 턴 간 하나의 trace batch 유지 |
|
||||
|
||||
`ChatState`, 레거시 `ConversationalConfig`, `crewai.flow.conversation` 헬퍼는 고급 오케스트레이션, 테스트, 커스텀 래퍼에서 계속 import할 수 있습니다. 이들은 `ConversationState` / `ConversationConfig` API와 별개이며 `Flow.kickoff()`에 `user_message=` 또는 `session_id=` 키워드 인자를 추가하지 않습니다.
|
||||
`intents=` 및 `intent_llm=` 키워드로 kickoff마다 재정의할 수 있습니다.
|
||||
|
||||
## `ChatState` (권장 persist 형태)
|
||||
|
||||
```python
|
||||
from crewai.flow import ChatState
|
||||
@@ -135,7 +127,7 @@ class MyChatState(ChatState):
|
||||
|
||||
| 필드 | 역할 |
|
||||
|------|------|
|
||||
| `id` | 세션 UUID (`inputs["id"]`와 동일) |
|
||||
| `id` | 세션 UUID (`session_id` / `inputs["id"]`와 동일) |
|
||||
| `messages` | LLM 기록용 `{role, content}` 리스트 |
|
||||
| `last_user_message` | 이번 턴의 최신 사용자 입력 |
|
||||
| `last_intent` | 분류 후 라우트 라벨 (사용 시) |
|
||||
@@ -143,77 +135,76 @@ class MyChatState(ChatState):
|
||||
|
||||
`ConversationalInputs`는 `kickoff(inputs={...})`용 `TypedDict`: `id`, `user_message`, `last_intent`.
|
||||
|
||||
`ConversationState`는 `messages`를 `ConversationMessage` 객체로 저장하며 `current_user_message`, `ended`, `events`, `agent_threads`도 제공합니다. 정식 기록을 LLM에 전달할 때는 `conversation_messages`를 사용하세요.
|
||||
|
||||
## `Flow` 대화 API
|
||||
|
||||
### `handle_turn` 파라미터
|
||||
### `kickoff` / `kickoff_async` 파라미터
|
||||
|
||||
| 파라미터 | 목적 |
|
||||
|----------|------|
|
||||
| `message` | 이번 턴의 텍스트 |
|
||||
| `user_message` | 이번 턴 텍스트 (또는 `{"role": "user", "content": "..."}`) |
|
||||
| `session_id` | 대화 UUID → `inputs["id"]` / `state.id` |
|
||||
| `intents` | kickoff 전 `classify_intent`용 결과 라벨 |
|
||||
| `intents` | kickoff 전 `classify_intent`용 outcome 라벨 |
|
||||
| `intent_llm` | 분류 LLM (`intents`와 함께 필수) |
|
||||
| `**kickoff_kwargs` | `input_files`, `from_checkpoint`, `restore_from_state_id` 같은 옵션을 `kickoff()`로 전달 |
|
||||
|
||||
### `kickoff` 파라미터
|
||||
|
||||
`Flow.kickoff()`는 `inputs`, `input_files`, `from_checkpoint`, `restore_from_state_id`를 받습니다. 원시 flow 실행이 필요하면 `inputs={"id": session_id}`를 전달할 수 있지만, 채팅 메시지를 나타내는 호출에는 `handle_turn()`을 사용하세요.
|
||||
| `interactive` | `ask()` CLI 루프 (로컬 데모 전용) |
|
||||
| `interactive_prompt` | 대화형 모드 프롬프트 |
|
||||
| `interactive_timeout` | 줄 단위 `ask()` 타임아웃 |
|
||||
| `exit_commands` | 대화형 모드 종료 단어 |
|
||||
| `inputs` | 추가 상태 필드 |
|
||||
| `restore_from_state_id` | 다른 persist flow에서 fork 복원 |
|
||||
|
||||
### 인스턴스 속성
|
||||
|
||||
| 속성 | 목적 |
|
||||
|------|------|
|
||||
| `conversational` | 대화형 그래프와 `handle_turn()`을 활성화하려면 `True`로 설정 |
|
||||
| `defer_trace_finalization` | 선택적 인스턴스 재정의. 없으면 `_should_defer_trace_finalization()`이 `ConversationConfig.defer_trace_finalization`을 읽음 |
|
||||
| `suppress_flow_events` | 콘솔 flow 패널과 메서드 실행 이벤트를 숨김. flow start/finish 이벤트는 계속 발생 |
|
||||
| `stream` | 일반 Flow 스트리밍 플래그. 대화형 턴에서는 이 플래그와 `handle_turn()`을 함께 쓰지 말고 `stream_turn()` 사용 |
|
||||
| `conversational_config` | 클래스 수준 `ConversationalConfig` |
|
||||
| `defer_trace_finalization` | 인스턴스 플래그; kickoff 시 config에서 자동 설정 |
|
||||
| `suppress_flow_events` | 콘솔 flow 패널 숨김; **트레이싱은 계속 기록** |
|
||||
| `stream` | 스트리밍; `ChatSession.handle_turn(..., stream=True)`와 함께 |
|
||||
|
||||
### 메서드 및 프로퍼티
|
||||
|
||||
| 이름 | 설명 |
|
||||
|------|------|
|
||||
| `append_assistant_message(content)` | 사용자에게 보이는 어시스턴트 응답을 `state.messages`에 추가 |
|
||||
| `append_message(role, content, **extra)` | `state.messages`에 추가 |
|
||||
| `conversation_messages` | LLM 호출용 읽기 전용 기록 |
|
||||
| `classify_intent(text, outcomes, *, llm, context=None)` | outcome 매핑 (`@human_feedback`와 동일 collapse) |
|
||||
| `receive_user_message(text, *, outcomes=None, llm=None)` | 사용자 메시지 추가; 선택적 `last_intent` |
|
||||
| `finalize_session_traces()` | 지연 `flow_finished` 발생 및 세션 trace batch 종료 |
|
||||
| `_should_defer_trace_finalization()` | 턴별 trace 종료 지연 여부를 결정하는 고급/내부 hook |
|
||||
| `_should_defer_trace_finalization()` | 턴별 trace 종료 지연 여부 |
|
||||
| `input_history` | `ask()` 프롬프트/응답 감사 기록 |
|
||||
|
||||
### 모듈 헬퍼 (`crewai.flow.conversation`)
|
||||
|
||||
테스트 또는 커스텀 오케스트레이션을 위해 `crewai.flow.conversation`에서 import할 수 있습니다. 이 헬퍼들은 레거시 `ConversationalConfig` 형태를 사용합니다. 또한 `prepare_conversational_turn()`은 `last_intent`를 지우지만, `handle_turn()`은 router 컨텍스트로 보존합니다.
|
||||
테스트 또는 커스텀 오케스트레이션용:
|
||||
|
||||
| 함수 | 설명 |
|
||||
|------|------|
|
||||
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)` | 대화 kwargs를 `inputs`에 병합 |
|
||||
| `normalize_kickoff_inputs(...)` | 대화 kwargs를 `inputs`에 병합 |
|
||||
| `get_conversation_messages(flow)` | 상태 또는 내부 버퍼에서 메시지 읽기 |
|
||||
| `append_message(flow, role, content, **extra)` | 인스턴스 메서드와 동일 |
|
||||
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | 커스텀 래퍼용 하위 수준 턴 수화 |
|
||||
| `receive_user_message(flow, text, ...)` | 인스턴스 메서드와 동일 |
|
||||
| `append_message(flow, ...)` | 인스턴스 메서드와 동일 |
|
||||
| `prepare_conversational_turn(flow, ...)` | 턴 수화 (보통 kickoff가 호출) |
|
||||
| `receive_user_message(flow, ...)` | 인스턴스 메서드와 동일 |
|
||||
| `set_state_field(flow, name, value)` | dict 또는 Pydantic 상태 필드 설정 |
|
||||
| `get_conversational_config(flow)` | 클래스 `conversational_config` 읽기 |
|
||||
| `input_history_to_messages(entries)` | `input_history`를 LLM 메시지 형식으로 |
|
||||
|
||||
## 의도 라우팅 패턴
|
||||
|
||||
### A. `ConversationConfig`로 사전 분류 (가장 단순)
|
||||
### A. `ConversationalConfig`로 사전 분류 (가장 단순)
|
||||
|
||||
`default_intents`와 `intent_llm`을 설정하세요. 각 `handle_turn()`이 현재 메시지를 사전 분류합니다. 커스텀 `route_turn()`이 반환한 비어 있지 않은 결과가 우선하며, 그렇지 않으면 `route_conversation`이 현재 턴의 분류된 intent를 사용합니다.
|
||||
`default_intents`와 `intent_llm` 설정. 각 kickoff가 `@router` 전에 분류; `route()`에서 `self.state.last_intent` 읽기.
|
||||
|
||||
### B. `route_turn` 내부에서 분류 (풍부한 프롬프트)
|
||||
### B. `@router` 내부에서 분류 (풍부한 프롬프트)
|
||||
|
||||
`default_intents=None`으로 설정하면 `handle_turn()`은 사용자 메시지만 추가합니다. `route_turn()`에서 커스텀 프롬프트나 설명과 함께 `classify_intent`를 호출하세요:
|
||||
`default_intents=None`으로 kickoff는 메시지만 추가. `route()`에서 커스텀 프롬프트로 `classify_intent` 호출:
|
||||
|
||||
```python
|
||||
def route_turn(self, context):
|
||||
@router(bootstrap)
|
||||
def route(self):
|
||||
intent = self.classify_intent(
|
||||
self._routing_prompt(self.state.current_user_message),
|
||||
self._routing_prompt(self.state.last_user_message),
|
||||
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
|
||||
llm="gpt-4o-mini",
|
||||
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
|
||||
)
|
||||
self.state.last_intent = intent
|
||||
return intent
|
||||
@@ -223,59 +214,69 @@ def route_turn(self, context):
|
||||
|
||||
## flow가 끝났지만 사용자는 계속 대화할 때
|
||||
|
||||
각 `handle_turn()`은 하나의 그래프 실행을 완료하며, 같은 `session_id`로 다음 `handle_turn()`을 호출해 대화를 이어갑니다. 기본 지연 trace 수명 주기에서는 해당 실행이 `conversation_turn_completed`를 발생시키고, `finalize_session_traces()`가 세션을 닫을 때 `FlowFinished`가 한 번 발생합니다. `@persist`는 `messages`, 플래그, 컨텍스트를 복원합니다.
|
||||
`FlowFinished`는 **이번 그래프 실행**이 완료됨을 의미합니다. 같은 `session_id`로 또 다른 `kickoff`로 대화가 이어집니다. `@persist`가 `messages`, 플래그, 컨텍스트를 복원합니다.
|
||||
|
||||
**Persist 패턴:** 전체 `Flow` 클래스보다 **단일 종료 스텝**(예: `finalize`)에 `@persist`를 두는 것이 좋습니다. 클래스 수준 persist는 매 메서드 후 저장하며, `load_state`는 최신 행을 사용해 같은 턴의 핸들러 업데이트를 놓칠 수 있습니다.
|
||||
|
||||
후속 채팅 줄에 `@human_feedback`를 쓰지 마세요. 특정 스텝 출력을 사람이 승인해야 할 때만 사용하세요.
|
||||
|
||||
## 대화형 `Flow`
|
||||
## 대화형 `Flow` (실험적)
|
||||
|
||||
`Flow` 서브클래스에 `conversational = True`를 지정하거나 `@ConversationConfig(...)`를 적용하면 대화형 채팅 그래프가 활성화됩니다. 베이스 `Flow`는 내장 start/router인 `route_conversation`과 `converse_turn`, `end_conversation` 리스너를 제공합니다. 사용 중단된 `answer_from_history_turn` 리스너는 호환성을 위해 계속 제공됩니다. 또한 `state.messages`를 관리하고 router LLM을 구동할 수 있으며 턴 간 trace batch를 열린 상태로 유지합니다. 여러분은 **커스텀 라우트**를 작성하고 나머지는 프레임워크에 맡기면 됩니다.
|
||||
<Warning>
|
||||
**실험적 기능입니다.** 대화형 `Flow`의 API 표면(`conversational = True`,
|
||||
`handle_turn`, `ConversationConfig`, `RouterConfig`, `ConversationState`,
|
||||
내장 그래프와 헬퍼)은 `crewai.experimental` 하위에 있으며 정식 출시
|
||||
전까지 변경될 수 있습니다. 특정 동작에 의존한다면 CrewAI 버전을 고정하고
|
||||
변경 사항이 있는지 changelog를 확인하세요. 피드백과 이슈 환영합니다.
|
||||
</Warning>
|
||||
|
||||
`Flow` 서브클래스에 `conversational = True`를 지정하면 대화형 챗 그래프가 활성화됩니다. 베이스 `Flow`가 `@start` / `@router` / `converse_turn` / `end_conversation` 그래프를 노출하고, `state.messages`를 관리하며, router LLM을 구동하고, 턴 간 trace 배치를 열린 상태로 유지합니다. 여러분은 **커스텀 라우트**만 작성하면 되고, 나머지는 프레임워크가 담당합니다.
|
||||
|
||||
LLM 기반 라우터와 라우트별 핸들러로 멀티턴 챗을 만들고 싶지만 라이프사이클을 직접 배선하고 싶지 않을 때 사용하세요. 완전한 제어가 필요하면 위의 `Flow[ChatState]`로 내려가세요.
|
||||
|
||||
### 빠른 예제
|
||||
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai import LLM, Flow
|
||||
from crewai.flow import listen
|
||||
from crewai.flow import (
|
||||
from crewai.experimental.conversational import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
RouterConfig,
|
||||
)
|
||||
|
||||
|
||||
@ConversationConfig(defer_trace_finalization=True)
|
||||
ROUTER_LLM = LLM(model="gpt-4o-mini")
|
||||
|
||||
|
||||
@ConversationConfig(
|
||||
system_prompt="A multi-agent assistant for ordinary chat and tool-backed tasks.",
|
||||
llm=ROUTER_LLM,
|
||||
router=RouterConfig(), # 라우트 + 설명은 @listen 핸들러에서 자동 발견
|
||||
)
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
def route_turn(self, context: dict) -> str | None:
|
||||
message = (self.state.current_user_message or "").lower()
|
||||
if "search" in message or "news" in message:
|
||||
return "INTERNET_SEARCH"
|
||||
if "docs" in message or "crewai" in message:
|
||||
return "CREWAI_DOCS"
|
||||
return "converse"
|
||||
conversational = True
|
||||
|
||||
@listen("INTERNET_SEARCH")
|
||||
def handle_internet_search(self) -> str:
|
||||
"""Fresh web research, current news, real-time lookups."""
|
||||
reply = "I would run the web research route here."
|
||||
...
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
@listen("CREWAI_DOCS")
|
||||
def handle_crewai_docs(self) -> str:
|
||||
"""Look up the CrewAI documentation for framework/API questions."""
|
||||
reply = "I would look up the CrewAI docs here."
|
||||
...
|
||||
self.append_assistant_message(reply)
|
||||
return reply
|
||||
|
||||
|
||||
flow = SupportFlow()
|
||||
try:
|
||||
flow.handle_turn("What can you do?") # routes to converse
|
||||
flow.handle_turn("Search the web for AI news.") # routes to INTERNET_SEARCH
|
||||
flow.handle_turn("Check the CrewAI docs.") # routes to CREWAI_DOCS
|
||||
flow.handle_turn("뭘 할 수 있어?") # converse(빌트인)로 라우팅
|
||||
flow.handle_turn("AI 뉴스를 웹에서 찾아줘.") # INTERNET_SEARCH로 라우팅
|
||||
flow.handle_turn("첫 번째 결과를 요약해줘.") # 다시 converse로 라우팅
|
||||
finally:
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
@@ -297,53 +298,27 @@ def kickoff() -> None:
|
||||
|------|--------|------|
|
||||
| `system_prompt` | i18n `slices.conversational_system_prompt` | 빌트인 `converse_turn`이 사용하는 system 메시지. 빈 문자열(`""`)을 전달하면 system 메시지를 끕니다. |
|
||||
| `llm` | `None` | 대화용 LLM (빌트인 `converse_turn`이 사용하고 router 폴백도 됨). |
|
||||
| `router` | `None` | 선택적 `RouterConfig` 재정의. 커스텀 listener와 결정 가능한 LLM이 있으면 생략해도 라우팅이 자동 활성화됩니다. |
|
||||
| `answer_from_history_prompt` | 프레임워크 기본값 | **사용 중단됨.** `converse` system prompt를 사용하거나 `converse_turn()`을 재정의하세요. |
|
||||
| `answer_from_history_llm` | `None` | **사용 중단됨.** `llm`을 사용하세요. `converse`는 이미 정식 기록을 전달받습니다. |
|
||||
| `router` | `None` | LLM 기반 라우팅을 위한 `RouterConfig`. 없으면 항상 `converse`로 떨어집니다. |
|
||||
| `answer_from_history_prompt` | 프레임워크 기본값 | 선택적인 `answer_from_history` 라우트용 system 메시지. |
|
||||
| `answer_from_history_llm` | `None` | 설정되면 `answer_from_history` 단축 경로가 활성화됩니다. |
|
||||
| `intent_llm` | `None` | 레거시 `intents=`/`default_intents` 사전 분류용 LLM. |
|
||||
| `default_intents` | `None` | 레거시 사전 분류용 outcome 레이블. |
|
||||
| `visible_agent_outputs` | `None` | `"all"` 또는 `append_agent_result()` 결과를 사용자에게 공개로 승격할 에이전트 이름 목록. |
|
||||
| `defer_trace_finalization` | `True` | `handle_turn()` 호출들 사이에서 하나의 trace 배치를 열어 둡니다. |
|
||||
|
||||
<Warning>
|
||||
`answer_from_history_prompt`, `answer_from_history_llm`, `answer_from_history`
|
||||
라우트는 사용 중단되었으며 향후 릴리스에서 제거될 예정입니다. 이들은 이미
|
||||
정식 기록을 처리하는 `converse`와 기능이 중복되고, 답변 가능 여부를 판단하는
|
||||
LLM 호출을 추가하며, 일반 auto-router가 라우트를 반환하면 우회됩니다. 기존
|
||||
설정은 계속 작동하며 `DeprecationWarning`을 발생시킵니다.
|
||||
</Warning>
|
||||
|
||||
커스텀 라우트가 없으면 턴은 `converse`로 이어집니다. 커스텀 라우트와 대화/router LLM이 있으면 프레임워크가 기본 `RouterConfig`를 합성합니다. prompt, 라우트 목록, 설명, fallback 동작을 바꿔야 할 때만 명시적으로 제공하세요. `default_intents`를 설정하면 레거시 사전 분류 경로를 사용합니다.
|
||||
|
||||
대화 LLM을 설정하지 않으면 내장 `converse_turn`은 답변을 생성하는 대신 설정 안내 placeholder를 반환합니다.
|
||||
|
||||
### `RouterConfig`와 자동 생성되는 라우트 카탈로그
|
||||
|
||||
```python
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai import LLM
|
||||
from crewai.flow import RouterConfig
|
||||
|
||||
|
||||
class MyRoute(BaseModel):
|
||||
intent: Literal["INTERNET_SEARCH", "CREWAI_DOCS", "converse"]
|
||||
|
||||
|
||||
ROUTER_LLM = LLM(model="gpt-4o-mini")
|
||||
|
||||
router_config = RouterConfig(
|
||||
prompt="Optional domain framing (policy, voice, persona).",
|
||||
response_format=MyRoute, # optional; auto-generated otherwise
|
||||
llm=ROUTER_LLM, # falls back to ConversationConfig.llm
|
||||
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # optional; inferred from listeners
|
||||
RouterConfig(
|
||||
prompt="선택적인 도메인 프레이밍 (정책, 톤, 페르소나).",
|
||||
response_format=MyRoute, # 선택; 없으면 자동 생성
|
||||
llm=ROUTER_LLM, # ConversationConfig.llm으로 폴백
|
||||
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # 선택; 리스너에서 추론
|
||||
route_descriptions={
|
||||
"INTERNET_SEARCH": "Override the docstring for this one route.",
|
||||
"INTERNET_SEARCH": "이 라우트만 docstring 대신 사용할 설명.",
|
||||
},
|
||||
default_intent="converse", # used when LLM call fails or no LLM available
|
||||
fallback_intent="converse", # used when LLM returns an invalid route
|
||||
default_intent="converse", # LLM 호출 실패 또는 LLM 없음일 때 사용
|
||||
fallback_intent="converse", # LLM이 잘못된 라우트를 반환할 때 사용
|
||||
intent_field="intent",
|
||||
)
|
||||
```
|
||||
@@ -351,10 +326,9 @@ router_config = RouterConfig(
|
||||
router에 전달되는 프롬프트는 자동으로 만들어집니다. 각 라우트의 설명은 다음 우선순위로 결정됩니다:
|
||||
|
||||
1. `RouterConfig.route_descriptions[label]` — 명시적 오버라이드.
|
||||
2. `Flow.builtin_route_descriptions[label]` — `converse`, `end`, 사용 중단된 `answer_from_history` 호환 라우트용 프레임워크 기본 텍스트 (router LLM용으로 다듬어진 문구).
|
||||
3. 메서드에 선언된 `description` — 선언적 flow와 DSL projection에서 사용.
|
||||
4. `@listen(label)` 핸들러 docstring의 첫 번째 비어 있지 않은 줄.
|
||||
5. 빈 문자열 — 설명 없이 라우트만 표시.
|
||||
2. `Flow.builtin_route_descriptions[label]` — `converse`, `end`, `answer_from_history`용 프레임워크 캐닝 텍스트 (router LLM용으로 다듬어진 문구).
|
||||
3. `@listen(label)` 핸들러 docstring의 첫 줄(비어있지 않은 줄).
|
||||
4. 빈 문자열 (라우트만 카탈로그에 등장하고 설명은 없음).
|
||||
|
||||
실제 사용에서 **새 라우트를 추가하는 방법은 `@listen("X")` + 한 줄짜리 docstring**입니다:
|
||||
|
||||
@@ -365,27 +339,6 @@ def handle_internet_search(self) -> str:
|
||||
...
|
||||
```
|
||||
|
||||
### 핸들러 이름 짓기
|
||||
|
||||
`@listen("…")`의 문자열은 Python 메서드 이름이 아니라 **router 라우트 레이블**(이벤트 이름)입니다. 라우트 레이블과 메서드 완료 이벤트는 하나의 트리거 namespace를 공유하므로, 핸들러 이름을 라우트와 같게 지정하면 핸들러가 자기 자신을 반복해서 다시 실행합니다.
|
||||
|
||||
서로 다른 메서드 이름을 사용하세요. 문서 예제에서는 `handle_*` 접두사를 사용합니다:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def handle_create_video(self) -> str:
|
||||
"""User wants a new video."""
|
||||
...
|
||||
```
|
||||
|
||||
메서드 이름을 라우트 레이블과 같게 만들지 마세요:
|
||||
|
||||
```python
|
||||
@listen("create_video")
|
||||
def create_video(self) -> str: # rejected at flow instantiation
|
||||
...
|
||||
```
|
||||
|
||||
…그러면 router LLM은 다음을 봅니다:
|
||||
|
||||
```
|
||||
@@ -404,7 +357,7 @@ Routes:
|
||||
|--------|--------|------|
|
||||
| `converse` | `converse_turn` | 기본 챗 핸들러. system prompt + 정식 메시지 히스토리와 함께 `ConversationConfig.llm`을 호출합니다. |
|
||||
| `end` | `end_conversation` | `state.ended = True`로 설정하고 종료 응답을 보냅니다. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | **사용 중단된 호환 라우트.** 이미 정식 기록을 전달받는 `converse`를 사용하세요. |
|
||||
| `answer_from_history` | `answer_from_history_turn` | 선택적. `ConversationConfig.answer_from_history_llm`이 설정되어 있고 메시지를 히스토리만으로 답할 수 있을 때 라우팅됩니다. |
|
||||
|
||||
서브클래스에 같은 이름의 핸들러를 정의하면 어떤 것이든 오버라이드할 수 있습니다.
|
||||
|
||||
@@ -414,9 +367,9 @@ Routes:
|
||||
|
||||
1. 그래프가 다시 실행되도록 턴 단위 실행 추적(`_completed_methods`, `_method_outputs`)을 초기화합니다 — 이게 없으면 동일 인스턴스에서 반복 `kickoff` 호출 시 `Flow.kickoff_async`가 `inputs={"id": ...}`를 체크포인트 복원으로 간주해 2번째 턴부터 단락 회로가 발생합니다.
|
||||
2. 사용자 메시지를 `state.messages`에 추가하고 `current_user_message` / `last_user_message`를 설정합니다. `last_intent`는 **이전 턴 값이 유지**되어 router LLM이 신호로 활용할 수 있습니다.
|
||||
3. 사용자 정의 `@start` 메서드(있는 경우)를 실행한 다음 내장 start/router인 `route_conversation`을 거쳐 선택된 `@listen` 핸들러를 실행합니다. `route_conversation`은 재정의 가능한 `conversation_start()` 헬퍼를 호출합니다.
|
||||
3. `conversation_start` → `route_conversation` → 선택된 `@listen` 핸들러 순으로 실행됩니다.
|
||||
4. router는 결정을 `state.last_intent`에 저장합니다 (다음 턴의 router 컨텍스트에서 보입니다).
|
||||
5. 핸들러가 문자열을 반환했지만 `append_assistant_message`를 직접 호출하지 않았다면, `handle_turn`이 대신 추가한 뒤 갱신된 `state.messages`를 persist합니다. `@persist` 복원 시 assistant 턴이 포함됩니다.
|
||||
5. 핸들러가 문자열을 반환했지만 `append_assistant_message`를 직접 호출하지 않았다면, `handle_turn`이 대신 추가해 줍니다.
|
||||
|
||||
채팅 메시지에는 `handle_turn()`을 호출하세요. `kickoff(inputs={"id": ...})`를 직접 호출하면 대화형 턴 래퍼 없이 flow 그래프가 실행됩니다.
|
||||
|
||||
@@ -437,8 +390,6 @@ flow.chat()
|
||||
4. 어시스턴트 결과를 출력합니다.
|
||||
5. `finally` 블록에서 지연된 세션 trace를 finalize합니다.
|
||||
|
||||
`chat(defer_trace_finalization=True)`는 REPL 동안 인스턴스의 지연 플래그를 임시로 활성화하고 종료할 때 이전 값으로 복원합니다.
|
||||
|
||||
주입 가능한 I/O로 터미널 동작을 커스터마이즈할 수 있습니다:
|
||||
|
||||
```python
|
||||
@@ -457,12 +408,6 @@ flow.chat(
|
||||
매 라우팅 결정마다 사이드 이펙트(이벤트 버스 셋업, 텔레메트리)를 실행하려면 `route_turn`을 오버라이드하세요:
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationState
|
||||
|
||||
|
||||
class SupportFlow(Flow[ConversationState]):
|
||||
conversational = True
|
||||
|
||||
@@ -471,7 +416,7 @@ class SupportFlow(Flow[ConversationState]):
|
||||
return super().route_turn(context)
|
||||
```
|
||||
|
||||
LLM router를 완전히 우회하고 프로그램 방식으로 라우트를 선택하려면 `route_turn`에서 비어 있지 않은 문자열을 반환하세요. falsy 값을 반환해도 오버라이드에서 `_route_with_config()`가 호출되지는 않습니다. 대신 현재 턴의 사전 분류된 intent, 설정된 경우 사용 중단된 `answer_from_history` 호환 경로, 마지막으로 `converse` 순으로 fallback합니다. 이전 턴의 `last_intent`는 router 컨텍스트에서 사용할 수 있지만 fallback으로 다시 실행되지는 않습니다.
|
||||
LLM router를 우회해 프로그램적으로 라우트를 선택하려면 `route_turn`에서 문자열을 반환하세요. `None`을 반환하면 `_route_with_config(...)`로 떨어집니다.
|
||||
|
||||
### `append_assistant_message`와 `append_agent_result`
|
||||
|
||||
@@ -482,76 +427,9 @@ LLM router를 완전히 우회하고 프로그램 방식으로 라우트를 선
|
||||
|
||||
`ConversationConfig.visible_agent_outputs`로 특정 에이전트의 private 결과를 전역적으로 public으로 승격할 수 있습니다 (`"all"` 또는 이름 리스트).
|
||||
|
||||
## JSON/YAML로 대화형 플로우 선언하기
|
||||
|
||||
[선언적 Flow](/edge/ko/concepts/cli)도 대화형으로 만들 수 있습니다. 최상위 `conversational` 블록을 추가하고 라우트 레이블을 `listen`하는 메서드로 자체 라우트를 선언하세요:
|
||||
|
||||
```yaml
|
||||
schema: crewai.flow/v1
|
||||
name: SupportFlow
|
||||
|
||||
conversational:
|
||||
system_prompt: You are a terse support assistant.
|
||||
llm: gpt-4o-mini
|
||||
router:
|
||||
llm: gpt-4o-mini
|
||||
|
||||
methods:
|
||||
handle_order:
|
||||
description: Order status, shipping and delivery questions.
|
||||
listen: order
|
||||
do:
|
||||
call: agent
|
||||
with:
|
||||
role: Support specialist
|
||||
goal: Answer order questions accurately
|
||||
backstory: Knows the fulfilment pipeline.
|
||||
input: "${state.current_user_message}"
|
||||
```
|
||||
|
||||
블록 선언 자체가 opt-in이며 `enabled`의 기본값은 `true`입니다. 설정은 유지하면서 채팅을 끄려면 `enabled: false`로 지정하세요. 이 경우 내장 메서드 합성도 비활성화되므로 선언에 일반 비대화형 그래프를 제공해야 합니다.
|
||||
|
||||
세 가지가 자동으로 제공됩니다:
|
||||
|
||||
| 제공 항목 | 설명 |
|
||||
|----------|--------|
|
||||
| 내장 그래프 | `route_conversation`, `converse_turn`, `end_conversation`이 자동으로 추가됩니다. 사용 중단된 `answer_from_history_turn`은 호환성을 위해 유지됩니다. 같은 이름 중 하나로 메서드를 선언하면 재정의됩니다. |
|
||||
| 대화 상태 | `state` 블록이 없으면 `ConversationState`가 사용됩니다. Pydantic `ref` 또는 `json_schema` state는 대화형 필드와 자동으로 합성되며 `ConversationState`를 상속할 필요가 없습니다. |
|
||||
| 라우트 카탈로그 | 내부 라우트를 제외하고 `listen` 레이블이 있는 비-router 메서드에서 추론됩니다. 설명에는 위 우선순위가 적용되며 명시적인 `router.routes`로 선택지를 제한할 수 있습니다. |
|
||||
|
||||
선언적 `llm`, `router.llm`, `intent_llm` 필드는 모델 id 또는 `{model: openai/gpt-4o-mini, max_tokens: 512}` 같은 설정 mapping을 받습니다. `conversational` 블록은 `default_intents`, `visible_agent_outputs`, `defer_trace_finalization`과 위에 나온 `RouterConfig` 필드도 지원합니다. 사용 중단된 `answer_from_history_prompt` / `answer_from_history_llm` 선언은 호환성을 위해 계속 허용됩니다.
|
||||
|
||||
클래스 기반 대화형 플로우와 동일한 턴 API로 Python에서 실행합니다:
|
||||
|
||||
```python
|
||||
from crewai.flow import Flow
|
||||
|
||||
flow = Flow.from_declaration(path="flow.yaml")
|
||||
|
||||
try:
|
||||
flow.handle_turn("Where is my order?", session_id="session-1")
|
||||
finally:
|
||||
flow.finalize_session_traces()
|
||||
```
|
||||
|
||||
### 라우트 이름 짓기
|
||||
|
||||
라우트 레이블과 메서드 이름은 하나의 트리거 네임스페이스를 공유하므로, 핸들러 이름이 자신이 listen하는 라우트와 같으면 안 됩니다 — `create_video`가 `create_video`를 listen하면 플로우 생성 시 거부됩니다. `handle_*` 접두사를 사용하세요.
|
||||
|
||||
### 선언으로 표현할 수 없는 것
|
||||
|
||||
| 표현 불가 | 대신 사용 |
|
||||
|-----------------|-------------|
|
||||
| 살아 있는 `LLM` 인스턴스나 커스텀 `BaseLLM` | 모델 id 문자열 또는 정적 설정 mapping |
|
||||
| 살아 있는 모델 클래스로서의 `router.response_format` | python ref로 클래스를 지정하세요: `response_format: {python: my_project.schemas.ConversationRoute}`. 생략하면 프레임워크가 생성합니다 |
|
||||
| `route_turn()` 재정의 | Flow를 Python으로 작성하거나 선언적 `route_conversation` 메서드를 `call: code` / expression action으로 교체 |
|
||||
| `can_answer_from_history()` 재정의 | 사용 중단됨. `converse`를 사용하거나 Python에서 `converse_turn()`을 재정의하세요. |
|
||||
|
||||
`crewai run`은 선언적 대화형 Flow에 대해 Python 대화형 Flow와 같은 채팅 TUI를 엽니다. 채팅 루프에는 터미널이 필요하므로 headless 실행은 단일 턴을 실행하는 대신 안내와 함께 0이 아닌 코드로 종료됩니다. 이런 환경에서는 Python의 `handle_turn()` 또는 `stream_turn()`으로 실행하세요. `human_feedback:` 블록이 있는 선언적 메서드(Python: `@human_feedback`)는 터미널 REPL에서 실행됩니다. 런타임이 TUI가 처리할 수 없는 블로킹 prompt로 feedback을 수집하기 때문입니다. 대화형 Flow에서는 `--inputs`를 받지 않습니다. 각 턴의 입력은 사용자가 입력하는 메시지이며 id로 세션을 재개하는 기능은 아직 CLI에 연결되지 않았습니다. 필요하면 Python에서 `flow.handle_turn(message, session_id=...)`을 사용하세요.
|
||||
|
||||
## 턴 간 트레이싱
|
||||
|
||||
`defer_trace_finalization=True` (`ConversationConfig` 기본값):
|
||||
`defer_trace_finalization=True` (`ConversationalConfig` 기본값):
|
||||
|
||||
- 채팅 세션 전체에 **하나의 trace batch**.
|
||||
- 첫 턴에만 **`flow_started`**; `finalize_session_traces()`에서 **`flow_finished`** 한 번.
|
||||
@@ -562,30 +440,17 @@ finally:
|
||||
flow.chat(session_id=session_id)
|
||||
```
|
||||
|
||||
`flow.chat()`이 `finalize_session_traces()`를 대신 호출합니다. `handle_turn()`으로 직접 루프를 소유하는 경우 세션이 끝날 때 `finalize_session_traces()`를 호출하세요.
|
||||
`flow.chat()`이 `finalize_session_traces()`를 대신 호출합니다. `handle_turn()`이나 `kickoff(...)`로 직접 루프를 소유하는 경우, 세션이 끝날 때 `finalize_session_traces()`를 호출하세요.
|
||||
|
||||
`suppress_flow_events=True`는 Rich 콘솔 패널을 숨기고 메서드 실행 이벤트를 억제합니다. Flow start/finish 이벤트는 계속 발생하므로 바깥쪽 Flow 수명 주기는 추적할 수 있지만 개별 메서드 span은 생략됩니다.
|
||||
`suppress_flow_events=True`는 Rich 콘솔 패널만 숨깁니다. trace 및 method 이벤트는 계속 발생합니다.
|
||||
|
||||
### 대화형 `Flow` trace 수명 주기
|
||||
|
||||
[대화형 `Flow`](#대화형-flow)는 동일한 tracing 수명 주기를 따릅니다. `defer_trace_finalization` 기본값이 `True`이므로 각 `handle_turn()`은 세션 trace를 열린 상태로 유지합니다. 지연된 턴은 턴별 `flow_failed`도 억제합니다. 턴 오류나 세션 중단이 발생하면 세션을 명시적으로 finalize하세요. 그러면 턴별 `FlowFailed` 이벤트 대신 세션 수준 `FlowFinished` 이벤트로 batch가 닫힙니다. REPL/루프는 항상 `try/finally`로 감싸고 종료 시 `flow.finalize_session_traces()`를 호출하세요. 호출하지 않으면 trace batch가 열린 채 남아 최종 대화가 export되지 않을 수 있습니다.
|
||||
실험적 [대화형 `Flow`](#대화형-flow-실험적)는 동일한 tracing 수명 주기를 따릅니다. `defer_trace_finalization` 기본값이 `True`이므로 각 `handle_turn()`이 세션 trace를 열어 둡니다. 세션 끝에서 항상 finalize하세요 — REPL/루프를 `try/finally`로 감싸고 종료 시 `flow.finalize_session_traces()`를 호출하세요. 호출하지 않으면 batch가 열린 채 남아 마지막 대화가 export되지 않을 수 있습니다.
|
||||
|
||||
## 스트리밍
|
||||
|
||||
대화형 UI에서는 `stream_turn()`을 사용하고 순서가 보장된 `StreamFrame` 객체를 순회하세요:
|
||||
|
||||
```python
|
||||
stream = flow.stream_turn("Where is my order?", session_id=session_id)
|
||||
|
||||
with stream:
|
||||
for frame in stream.events:
|
||||
if frame.channel == "llm" and frame.type == "llm_stream_chunk":
|
||||
print(frame.content, end="", flush=True)
|
||||
|
||||
reply = stream.result
|
||||
```
|
||||
|
||||
비대화형 Flow에서는 `stream = True`로 설정하면 `kickoff()`가 `StreamSession`을 반환합니다. `handle_turn()`을 사용할 때 `flow.stream = True`로 설정하지 마세요. 대화형 스트리밍 수명 주기는 `stream_turn()`이 관리합니다.
|
||||
`Flow` 클래스에 `stream = True`. `kickoff(...)`가 표준 이벤트 버스를 통해 `assistant_delta` 등 이벤트를 발생시킵니다.
|
||||
|
||||
## import
|
||||
|
||||
@@ -600,15 +465,10 @@ from crewai.flow import (
|
||||
router,
|
||||
start,
|
||||
)
|
||||
from crewai.flow.conversation import prepare_conversational_turn
|
||||
from crewai.flow import (
|
||||
ConversationConfig,
|
||||
ConversationState,
|
||||
RouterConfig,
|
||||
)
|
||||
```
|
||||
|
||||
## 참고
|
||||
|
||||
- [Flow 상태 관리 마스터하기](/ko/guides/flows/mastering-flow-state)
|
||||
- [첫 Flow 만들기](/ko/guides/flows/first-flow)
|
||||
- 데모: `lib/crewai/runner_conversational_flow_simple.py`
|
||||
|
||||
@@ -135,7 +135,7 @@ crewai flow add-crew content-crew
|
||||
}
|
||||
```
|
||||
|
||||
`provider/model-id`를 사용하는 모델로 바꾸세요. 예: `openai/gpt-4o`, `gemini/gemini-3.7-flash`, `anthropic/claude-sonnet-4-6`.
|
||||
`provider/model-id`를 사용하는 모델로 바꾸세요. 예: `openai/gpt-4o`, `gemini/gemini-2.0-flash-001`, `anthropic/claude-sonnet-4-6`.
|
||||
|
||||
3. `src/guide_creator_flow/crews/content_crew/crew.jsonc`를 만듭니다:
|
||||
|
||||
@@ -481,7 +481,7 @@ Flow를 사용하면 간단하고 구조화된 응답이 필요할 때 언어
|
||||
|
||||
```python
|
||||
llm = LLM(
|
||||
model="model-id-here", # gpt-4o, gemini/gemini-3.7-flash, anthropic/claude...
|
||||
model="model-id-here", # gpt-4o, gemini-2.0-flash, anthropic/claude...
|
||||
response_format=GuideOutline
|
||||
)
|
||||
response = llm.call(messages=messages)
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
title: Channels
|
||||
description: CopilotKit Channels SDK와 관리형 Intelligence 플랫폼으로 동일한 CrewAI 에이전트를 Slack 또는 Teams 봇으로 실행하세요.
|
||||
icon: messages
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 사용자가 이미 있는 곳에서 만나세요
|
||||
|
||||
[Overview](/edge/ko/guides/frontend/overview)에서 만든 CrewAI 에이전트는 반드시 웹 앱 뒤에서만 동작할 필요가 없습니다. 동일한 Crew 또는 Flow를 메시징 플랫폼 안에서 봇으로 실행할 수 있습니다. 다시 빌드할 필요도, 에이전트 로직을 두 번 복사할 필요도 없습니다. 에이전트는 [AG-UI 프로토콜](https://docs.ag-ui.com)을 통해 그대로 노출되고, **channel**이 Slack 또는 Microsoft Teams에서 이를 구동합니다.
|
||||
|
||||
CopilotKit의 [Channels SDK](https://docs.copilotkit.ai/slack)가 그 channel을 제공합니다. 작은 런타임에 `createChannel`을 선언하고 이를 CrewAI 에이전트에 연결하면, CopilotKit의 관리형 **Intelligence** 플랫폼이 메시징 제공자와의 연결을 중개합니다.
|
||||
|
||||
<Note>
|
||||
이 섹션의 나머지 내용과 달리 Channels는 **셀프 호스팅되지 않습니다**. Channels는 **CopilotKit Intelligence**를 통해 실행되며, 이는 설계상 Channels에 필수적인 서비스입니다(무료 티어 제공). Intelligence는 플랫폼 연결과 자격 증명을 보관하고, 각 플랫폼 이벤트를 수신하며, 해당 턴을 여러분의 channel 프로세스로 전달합니다. 여러분의 프로세스는 에이전트를 실행하고 응답을 다시 스트리밍합니다. Slack은 Intelligence 대시보드에서 한 번만 구성하면 되며, 플랫폼 자격 증명은 결코 여러분의 프로세스로 들어오지 않습니다. 에이전트, 도구, 상태는 온전히 여러분의 것으로 유지됩니다.
|
||||
</Note>
|
||||
|
||||
## 어떻게 맞물리는가
|
||||
|
||||
CrewAI 에이전트 서버에 관한 것은 아무것도 바뀌지 않습니다. Overview에서와 똑같이 AG-UI를 통해 Crew 또는 Flow를 계속 제공합니다. 여러분이 추가하는 것은 `@copilotkit/channels`로 빌드된 별도의 장시간 실행 Node 프로세스입니다. 이 프로세스는 `CopilotRuntime`에 channel을 등록하고, Intelligence에 연결하며, 메시지가 도착할 때마다 에이전트를 실행합니다.
|
||||
|
||||
```
|
||||
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
channel 프로세스는 Intelligence 게이트웨이에 대한 지속적인 연결을 유지하므로, 장시간 실행되는 호스트가 필요합니다. 서버리스 요청 핸들러는 그 연결을 소유할 수 없습니다. CrewAI 서버는 동시에 Overview의 웹 프론트엔드를 계속 제공할 수 있습니다. 웹 앱과 channel은 하나의 AG-UI 엔드포인트에 연결된 두 개의 클라이언트일 뿐입니다.
|
||||
|
||||
## 통합 가이드
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Channels 패키지 설치">
|
||||
|
||||
Channels SDK는 모든 것이 포함되어 있습니다. 모든 플랫폼이 하나의 패키지로 제공되며, 플랫폼별로 설치할 어댑터가 없습니다. channel을 호스팅하는 런타임 및 CrewAI AG-UI 클라이언트와 함께 다음을 추가하세요:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Intelligence에서 Channel 생성">
|
||||
|
||||
[CopilotKit 대시보드](https://docs.copilotkit.ai/slack)에서 Channel을 생성하고 Slack을 연결하세요. Intelligence가 Slack 앱 생성 과정을 안내하고 그 자격 증명을 보관합니다. 그러면 여러분의 프로세스를 위한 두 개의 환경 변수가 남으며, 둘 다 대시보드에서 얻습니다:
|
||||
|
||||
```bash
|
||||
export INTELLIGENCE_API_KEY=... # authenticates the runtime with Intelligence (free tier available)
|
||||
export INTELLIGENCE_CHANNEL_ID=... # the Channel ID, matched by createChannel({ name })
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="channel 정의">
|
||||
|
||||
`createChannel`은 channel을 선언하고 에이전트를 연결합니다. 각 대화가 자신만의 세션을 갖도록 에이전트를 스레드별 팩토리로 빌드하되, Overview가 웹 런타임에서 사용하는 것과 동일한 `CrewAIAgent`를 여러분의 AG-UI 엔드포인트를 가리키도록 설정하세요. `identifyUser: "platform"`은 Intelligence가 각 플랫폼 사용자를 안정적인 신원에 매핑하도록 합니다.
|
||||
|
||||
```ts
|
||||
// channel.ts
|
||||
import { createChannel } from "@copilotkit/channels";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
|
||||
const channel = createChannel({
|
||||
name: process.env.INTELLIGENCE_CHANNEL_ID!, // must match the Channel ID in Intelligence
|
||||
identifyUser: "platform",
|
||||
// A fresh agent per conversation, pointed at your CrewAI AG-UI endpoint.
|
||||
agent: (threadId) => {
|
||||
const agent = new CrewAIAgent({ url: "http://localhost:8000/recipe" });
|
||||
agent.threadId = threadId;
|
||||
return agent;
|
||||
},
|
||||
});
|
||||
|
||||
// A mention subscribes the thread and runs the agent; afterwards every message
|
||||
// in a subscribed thread runs it without needing another mention.
|
||||
channel.onMention(async ({ thread }) => {
|
||||
await thread.subscribe();
|
||||
await thread.runAgent();
|
||||
});
|
||||
|
||||
channel.onMessage(async ({ thread }) => {
|
||||
if (await thread.isSubscribed()) await thread.runAgent();
|
||||
});
|
||||
|
||||
export { channel };
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="런타임에 channel 등록">
|
||||
|
||||
Intelligence 게이트웨이와 여러분의 channel로 `CopilotRuntime`을 생성한 다음, `createCopilotNodeListener`로 이를 제공하세요. `agents` 맵은 비어 있는 상태로 둡니다. channel이 자신의 에이전트를 제공하기 때문입니다. 잘못된 구성이 시작 시 명확하게 실패하도록 channel이 준비될 때까지 기다리세요.
|
||||
|
||||
```ts
|
||||
// server.ts
|
||||
import { createServer } from "node:http";
|
||||
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
|
||||
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
|
||||
import { channel } from "./channel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {}, // the channel supplies its own agent; no web-facing agents needed
|
||||
intelligence: new CopilotKitIntelligence({
|
||||
apiKey: process.env.INTELLIGENCE_API_KEY!, // free tier available
|
||||
}),
|
||||
channels: [channel],
|
||||
});
|
||||
|
||||
const listener = createCopilotNodeListener({ runtime });
|
||||
await listener.channels?.ready({ timeoutMs: 15_000 });
|
||||
|
||||
createServer(listener).listen(3123, () => {
|
||||
console.log("Channels runtime listening on port 3123");
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="channel 런타임 실행">
|
||||
|
||||
CrewAI 에이전트 서버와 함께 시작하세요:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1 — CrewAI agent server
|
||||
npx tsx server.ts # terminal 2 — Channels runtime
|
||||
```
|
||||
|
||||
Slack 또는 Teams에서 봇을 멘션하면 Crew 또는 Flow를 실행하고 응답을 스레드로 다시 스트리밍합니다. 스레드는 구독된 상태로 유지되므로 후속 메시지는 또다시 멘션할 필요 없이 실행됩니다.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## 이벤트 모델
|
||||
|
||||
channel은 핸들러로 플랫폼 이벤트에 반응하며, 각 핸들러는 몇 가지 메서드로 구동하는 `thread`를 받습니다:
|
||||
|
||||
- **`channel.onMention`**은 사용자가 봇을 @-멘션할 때 발생합니다. `thread.subscribe()`를 호출해 스레드에 참여한 다음, `thread.runAgent()`로 멘션에 대해 CrewAI 에이전트를 실행하세요.
|
||||
- **`channel.onMessage`**는 봇이 볼 수 있는 스레드의 모든 메시지에서 발생합니다. `thread.isSubscribed()`로 게이트를 걸어 에이전트가 참여한 곳에서만 응답하도록 한 다음, `thread.runAgent()`를 호출하세요.
|
||||
- **`thread.runAgent()`**는 현재 턴에 대해 연결된 CrewAI 에이전트를 실행하고 그 출력을 channel로 다시 스트리밍합니다. 에이전트가 실행할 텍스트를 재정의하려면 `{ prompt }`를 전달하세요.
|
||||
|
||||
여러분의 에이전트는 일반적인 AG-UI `RunAgentInput`을 받고 일반적인 AG-UI 이벤트를 방출합니다. 플랫폼 메커니즘은 channel 뒤에 머무르므로, 동일한 Crew 또는 Flow가 모든 플랫폼에서 변경 없이 실행됩니다. channel은 환영 인사, 인터럽트, 명령, 반응, 모달을 위한 핸들러도 노출합니다. 전체 표면은 [`Channel` 레퍼런스](https://docs.copilotkit.ai/reference/channels/classes/Channel)를 참조하세요.
|
||||
|
||||
## 플랫폼 지원
|
||||
|
||||
관리형 Intelligence 경로는 현재 **Slack**과 **Microsoft Teams**를 지원합니다. 동일한 channel 코드가 양쪽에서 실행되며, `message.platform` / `thread.platform`이 원래의 출처를 보고합니다. 다른 플랫폼(Discord, Telegram, WhatsApp)은 관리형 경로가 아니라 개발자가 운영하는 **direct adapters**를 통해 연결됩니다. 여러분 자신의 프로세스가 플랫폼 자격 증명과 전송을 보유합니다. 현재 지원 플랫폼 목록과 플랫폼별 설정은 [CopilotKit Channels 문서](https://docs.copilotkit.ai/slack)를 확인하세요.
|
||||
|
||||
## 관련 항목
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend Overview" icon="browser" href="/edge/ko/guides/frontend/overview">
|
||||
Crew 또는 Flow를 AG-UI를 통해 제공하세요. 모든 channel이 그 위에 세워지는 토대입니다.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
실행 도중 사용자 승인이나 입력을 수집하기 위해 에이전트를 일시 중지하세요.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
title: Frontend Overview
|
||||
description: CopilotKit과 AG-UI 프로토콜로 CrewAI 에이전트를 위한 인터랙티브 사용자 인터페이스를 구축하세요.
|
||||
icon: browser
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
## 에이전트에 사용자 인터페이스를 부여하세요
|
||||
|
||||
CrewAI는 여러분의 에이전트를 실행합니다. [CopilotKit](https://copilotkit.ai)은 그 에이전트에 프론트엔드를 제공합니다. 이 둘을 함께 사용하면 사용자가 Crew 또는 Flow와 대화하고, 실시간으로 작동하는 모습을 지켜보고, 그 결정을 승인하며, 출력을 장황한 텍스트 대신 살아 있는 UI로 렌더링하여 볼 수 있는 애플리케이션을 구축할 수 있습니다.
|
||||
|
||||
이 둘은 [AG-UI 프로토콜](https://docs.ag-ui.com)을 통해 연결됩니다. `ag-ui-crewai` 패키지는 어떤 Crew나 Flow든 AG-UI 엔드포인트로 노출합니다. CopilotKit의 React 훅과 컴포넌트가 그 엔드포인트를 소비합니다. 이를 통해 채팅 상자를 훨씬 뛰어넘는 경험이 열립니다:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
에이전트 도구 호출과 상태를 여러분만의 React 컴포넌트로 렌더링하세요.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
실행 도중 사용자 승인이나 입력을 수집하기 위해 에이전트를 일시 중지하세요.
|
||||
</Card>
|
||||
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
|
||||
에이전트 상태와 앱 UI를 양방향으로 동기화하세요.
|
||||
</Card>
|
||||
<Card title="Channels" icon="messages" href="/edge/ko/guides/frontend/channels">
|
||||
동일한 에이전트를 Slack, Discord 또는 Teams 봇으로 실행하세요.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
이 가이드는 Crew 또는 Flow를 Next.js 프론트엔드와 처음부터 끝까지 연동시킵니다. 이 섹션의 나머지 내용은 여기서 설정한 앱을 기반으로 합니다.
|
||||
|
||||
## 아키텍처
|
||||
|
||||
세 가지 구성 요소가 있습니다:
|
||||
|
||||
1. **CrewAI 에이전트 서버** — AG-UI를 통해 Crew 또는 Flow를 제공하는 Python 프로세스(FastAPI + `ag-ui-crewai`).
|
||||
2. **CopilotKit 런타임** — 에이전트를 등록하고 요청을 프록시하는 Next.js 라우트.
|
||||
3. **React 프론트엔드** — `<CopilotKit>` 프로바이더와 채팅 및 generative-UI 컴포넌트.
|
||||
|
||||
```
|
||||
React app ──► CopilotKit runtime (/api/copilotkit) ──► CrewAI server (AG-UI) ──► Crew / Flow
|
||||
```
|
||||
|
||||
<Note>
|
||||
이 가이드는 **셀프 호스팅** 경로를 다룹니다. `ag-ui-crewai`로 CrewAI 에이전트 서버를 직접 실행하며, 관리형 서비스 없이 로컬에서 동작합니다. CopilotKit은 호스팅된 스레드와 인스펙터를 갖춘 **관리형** 경로(CopilotKit Cloud / Enterprise Intelligence)도 제공합니다. 그 방식을 원한다면 [CopilotKit CrewAI 퀵스타트](https://docs.copilotkit.ai/crewai-crews/quickstart)를 참조하세요. 이 섹션의 프론트엔드 코드는 어느 쪽이든 동일합니다. 에이전트를 호스팅하고 등록하는 방식만 다릅니다.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
CrewAI는 AG-UI 뒤에서 세 가지 형태로 실행됩니다: 일반 **Flows**(이 가이드 전반에서 사용), **[Conversational Flows](/edge/en/guides/frontend/conversational-flows)**(네이티브, 세션 인식, 턴 기반, 완전한 기능 동등성), 그리고 **Crews**(기본 채팅). 이 섹션의 프론트엔드는 이들 전반에서 동일합니다. 백엔드 작성과 등록만 다릅니다.
|
||||
</Note>
|
||||
|
||||
## 통합 가이드
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="AG-UI를 통해 에이전트 제공">
|
||||
|
||||
통합 패키지를 CrewAI 프로젝트에 설치하세요:
|
||||
|
||||
```bash
|
||||
pip install ag-ui-crewai
|
||||
```
|
||||
|
||||
FastAPI 앱에서 에이전트를 노출하세요. Flows는 `add_crewai_flow_fastapi_endpoint`를, Crews는 `add_crewai_crew_fastapi_endpoint`를 사용합니다. 원하는 만큼 등록할 수 있으며, 각각 자신의 경로에 배치됩니다.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Flow
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint
|
||||
from my_agents.recipe_flow import RecipeFlow
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_flow_fastapi_endpoint(
|
||||
app=app,
|
||||
flow=RecipeFlow(),
|
||||
path="/recipe",
|
||||
)
|
||||
```
|
||||
|
||||
```python Crew
|
||||
# server.py
|
||||
from fastapi import FastAPI
|
||||
from ag_ui_crewai.endpoint import add_crewai_crew_fastapi_endpoint
|
||||
from my_agents.research_crew import ResearchCrew
|
||||
|
||||
app = FastAPI(title="CrewAI Agent Server")
|
||||
|
||||
add_crewai_crew_fastapi_endpoint(
|
||||
app=app,
|
||||
crew=ResearchCrew().crew(),
|
||||
path="/research",
|
||||
)
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
실행하세요:
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
<Note>
|
||||
서버를 시작하기 전에 LLM 제공자를 위한 환경 변수(예: `OPENAI_API_KEY`)를 설정하세요.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Next.js 앱 생성">
|
||||
|
||||
아직 프론트엔드가 없다면 하나를 스캐폴딩하세요:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest my-app
|
||||
cd my-app
|
||||
```
|
||||
|
||||
CopilotKit과 CrewAI AG-UI 클라이언트를 설치하세요:
|
||||
|
||||
```bash
|
||||
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @ag-ui/crewai
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="CopilotKit 런타임 추가">
|
||||
|
||||
CrewAI 에이전트를 CopilotKit 런타임에 등록하는 라우트를 생성하세요. 각 에이전트는 `CrewAIAgent`를 통해 Python 서버의 경로를 가리킵니다.
|
||||
|
||||
```ts
|
||||
// app/api/copilotkit/route.ts
|
||||
import {
|
||||
CopilotRuntime,
|
||||
InMemoryAgentRunner,
|
||||
createCopilotEndpoint,
|
||||
} from "@copilotkit/runtime/v2";
|
||||
import { CrewAIAgent } from "@ag-ui/crewai";
|
||||
import { handle } from "hono/vercel";
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
agents: {
|
||||
recipe: new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
|
||||
},
|
||||
runner: new InMemoryAgentRunner(),
|
||||
});
|
||||
|
||||
const app = createCopilotEndpoint({
|
||||
runtime,
|
||||
basePath: "/api/copilotkit",
|
||||
});
|
||||
|
||||
const handler = handle(app);
|
||||
export const GET = handler;
|
||||
export const POST = handler;
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="프로바이더로 앱 감싸기">
|
||||
|
||||
`<CopilotKit>`을 런타임 라우트로 가리키고 등록한 에이전트의 이름을 지정하세요.
|
||||
|
||||
```tsx
|
||||
// app/page.tsx
|
||||
"use client";
|
||||
import { CopilotKit } from "@copilotkit/react-core";
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
import "@copilotkit/react-core/v2/styles.css";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="recipe">
|
||||
<YourApp />
|
||||
<CopilotSidebar agentId="recipe" labels={{ modalHeaderTitle: "Assistant" }} />
|
||||
</CopilotKit>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="실행">
|
||||
|
||||
두 프로세스를 모두 시작하고 앱을 여세요. 이제 사이드바에서 채팅하면 Crew 또는 Flow가 실행됩니다.
|
||||
|
||||
```bash
|
||||
uvicorn server:app --port 8000 # terminal 1
|
||||
npm run dev # terminal 2
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## 채팅 UI 옵션
|
||||
|
||||
CopilotKit은 서로 교체 가능한 세 가지 채팅 표면을 제공합니다. 컴포넌트만 바꾸면 되며, 연결 방식은 동일합니다.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```tsx Sidebar
|
||||
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotSidebar agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Popup
|
||||
import { CopilotPopup } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotPopup agentId="recipe" />
|
||||
```
|
||||
|
||||
```tsx Inline
|
||||
import { CopilotChat } from "@copilotkit/react-core/v2";
|
||||
|
||||
<CopilotChat agentId="recipe" />
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 다음으로 갈 곳
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
|
||||
도구 호출과 에이전트 상태를 커스텀 컴포넌트로 렌더링하세요.
|
||||
</Card>
|
||||
<Card title="Frontend Actions" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
|
||||
에이전트가 브라우저에서 실행되는 함수를 호출하도록 하세요.
|
||||
</Card>
|
||||
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
|
||||
에이전트 동작을 사용자 승인 뒤에 두세요.
|
||||
</Card>
|
||||
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
|
||||
에이전트가 작동하는 동안 진행 중인 상태를 UI로 스트리밍하세요.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,204 +0,0 @@
|
||||
---
|
||||
title: 실행 경계 훅
|
||||
description: "@on 데코레이터로 crew와 flow 실행의 시작, 입력, 출력, 종료를 가로채기"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
실행 경계 훅은 실행의 가장 바깥쪽 경계를 가로챕니다 — 작업이 시작되기 전,
|
||||
입력이 확정될 때, 최종 결과가 준비될 때, 그리고 실행이 끝날 때입니다. 크루와
|
||||
플로우 모두에서 발생하며, 실행 수준의 정책 검사, 입력 재작성, 출력 정제에
|
||||
적합한 위치입니다.
|
||||
|
||||
## 개요
|
||||
|
||||
네 가지 인터셉션 포인트가 경계를 담당합니다:
|
||||
|
||||
| 포인트 | 시점 | `ctx.payload` |
|
||||
|--------|------|---------------|
|
||||
| `EXECUTION_START` | 크루 또는 플로우가 막 시작되려는 시점 | 입력 `dict` |
|
||||
| `INPUT` | 실행을 위한 입력이 확정된 시점 | 입력 `dict` |
|
||||
| `OUTPUT` | 최종 결과가 준비된 시점 | 출력 객체 |
|
||||
| `EXECUTION_END` | 실행이 끝난 시점(성공 또는 실패) | 출력 객체, 실패 시 `None` |
|
||||
|
||||
크루의 경우 출력 payload는 `CrewOutput`입니다. 플로우의 경우 최종 플로우
|
||||
메서드의 결과입니다.
|
||||
|
||||
## 훅 시그니처
|
||||
|
||||
```python
|
||||
from crewai.hooks import on, HookAborted, InterceptionPoint
|
||||
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def boundary_hook(ctx) -> Any | None:
|
||||
# Mutate ctx.payload in place, or
|
||||
# return a non-None value to replace it, or
|
||||
# raise HookAborted(reason, source) to stop the run
|
||||
return None
|
||||
```
|
||||
|
||||
경계 훅은 표준 계약을 따릅니다: 진행(`return None`), 제자리(in-place) 수정,
|
||||
값을 반환하여 교체, 또는 `HookAborted`를 발생시켜 중단합니다. 어떤
|
||||
경계에서든 중단(abort)은 그 사유와 함께 `kickoff()` 밖으로 전파됩니다.
|
||||
|
||||
## 컨텍스트 스키마
|
||||
|
||||
각 포인트는 타입이 지정된 컨텍스트를 받습니다. 모든 컨텍스트는 공통 기본
|
||||
필드를 공유합니다:
|
||||
|
||||
```python
|
||||
class InterceptionContext:
|
||||
payload: Any # The interceptable value (see table above)
|
||||
agent: Any = None # Not populated at execution boundaries
|
||||
agent_role: str | None # Not populated at execution boundaries
|
||||
task: Any = None # Not populated at execution boundaries
|
||||
crew: Any = None # The Crew instance (crew runs only)
|
||||
flow: Any = None # The Flow instance (flow runs only)
|
||||
```
|
||||
|
||||
포인트별 컨텍스트는 payload에 대한 이름 있는 별칭을 추가합니다:
|
||||
|
||||
```python
|
||||
class ExecutionStartContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class InputContext(InterceptionContext):
|
||||
inputs: dict # Same dict as payload
|
||||
|
||||
class OutputContext(InterceptionContext):
|
||||
output: Any # The output object
|
||||
|
||||
class ExecutionEndContext(InterceptionContext):
|
||||
output: Any # The output object (None when status == "failed")
|
||||
status: str # "completed" or "failed"
|
||||
error: BaseException | None # The exception when status == "failed"
|
||||
```
|
||||
|
||||
<Note>
|
||||
`ctx.inputs`는 **원본** 입력 dict의 별칭이므로, 어느 이름으로든 제자리
|
||||
수정은 동일하게 동작합니다. 이전 훅이 새 dict를 반환하여 payload를
|
||||
*교체*했다면 `ctx.payload`만 다시 바인딩됩니다 — 훅이 연쇄될 수 있는 경우
|
||||
항상 `ctx.payload`를 읽고 쓰세요.
|
||||
</Note>
|
||||
|
||||
## 크루 실행 vs. 플로우 실행
|
||||
|
||||
경계 훅은 두 런타임 모두에서 발생하며, 크루 실행은 내부적으로 플로우 런타임
|
||||
위에서 동작합니다. 따라서 `crew.kickoff()` 중에는 전역 경계 훅이 크루
|
||||
경계(`ctx.crew` 설정, `ctx.flow`는 `None`)**와** 내부 플로우(`ctx.flow`
|
||||
설정, `ctx.crew`는 `None`) 모두에서 발생합니다. 런타임으로 구분하세요:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def crew_output_only(ctx):
|
||||
if ctx.crew is None:
|
||||
return None # Skip the internal flow (or a bare flow)
|
||||
ctx.payload.raw = ctx.payload.raw.strip()
|
||||
```
|
||||
|
||||
## 일반적인 사용 사례
|
||||
|
||||
### 시작 시 정책 검사
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_START)
|
||||
def enforce_policy(ctx):
|
||||
if ctx.crew is not None and not ctx.payload.get("authorized"):
|
||||
raise HookAborted(reason="unauthorized execution", source="access-control")
|
||||
```
|
||||
|
||||
### 입력 재작성
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.INPUT)
|
||||
def add_defaults(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.setdefault("locale", "en-US")
|
||||
ctx.payload["topic"] = ctx.payload["topic"].strip().lower()
|
||||
```
|
||||
|
||||
재작성된 입력은 태스크 보간(interpolation)으로 흘러가므로, 실행은 수정된
|
||||
dict로 시작된 것처럼 동작합니다.
|
||||
|
||||
재작성에는 `INPUT`을 사용하고, `EXECUTION_START`는 허용/거부 게이트로
|
||||
취급하세요. `EXECUTION_START`에서의 재작성도 여전히 반영됩니다 — 크루에서는
|
||||
`before_kickoff` 콜백에도 전달되고, 플로우에서는 `INPUT` 재작성과 동일하게
|
||||
적용됩니다.
|
||||
|
||||
### 출력 정제
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
@on(InterceptionPoint.OUTPUT)
|
||||
def redact_emails(ctx):
|
||||
if ctx.crew is None:
|
||||
return None
|
||||
ctx.payload.raw = re.sub(
|
||||
r"\b[\w.+-]+@[\w-]+\.[\w.]+\b", "[EMAIL-REDACTED]", ctx.payload.raw
|
||||
)
|
||||
```
|
||||
|
||||
`OUTPUT`은 `EXECUTION_END`보다 먼저 실행되며, 둘 다 이전 훅에서 (교체되었을
|
||||
수 있는) payload를 봅니다. 최종적으로 재작성된 값이 `kickoff()`가 반환하는
|
||||
값입니다.
|
||||
|
||||
### 실패 관찰
|
||||
|
||||
`EXECUTION_END`는 성공이든 실패든 실행마다 정확히 한 번 발생합니다. 실행이
|
||||
예외를 던지면 — 태스크 오류, 플로우 메서드 예외, 또는 이전 포인트의
|
||||
`HookAborted` — 훅은 `ctx.error`에 예외가 담긴 `status="failed"`를 받으며,
|
||||
원래 예외는 변경 없이 `kickoff()` 밖으로 전파됩니다:
|
||||
|
||||
```python
|
||||
@on(InterceptionPoint.EXECUTION_END)
|
||||
def report_outcome(ctx):
|
||||
if ctx.status == "failed":
|
||||
notify_policy_engine(status="failed", error=repr(ctx.error))
|
||||
else:
|
||||
notify_policy_engine(status="completed")
|
||||
```
|
||||
|
||||
두 가지 주의 사항: `EXECUTION_START`가 디스패치되지 않았다면
|
||||
`EXECUTION_END`는 발생하지 않습니다(시작 시점의 중단은 경계가 열리지
|
||||
않았다는 뜻이므로 짝을 이룰 종료가 없습니다). 또한 실패 경로의
|
||||
`EXECUTION_END` 디스패치에서 `HookAborted`를 발생시키는 것은 무시됩니다 —
|
||||
더 이상 중단할 것이 없고, 원래 오류가 우선합니다.
|
||||
|
||||
## 순서
|
||||
|
||||
크루 실행의 경계 순서는 다음과 같습니다:
|
||||
|
||||
```
|
||||
EXECUTION_START → before_kickoff callbacks → INPUT → tasks execute → OUTPUT → EXECUTION_END
|
||||
```
|
||||
|
||||
플로우 실행에서는 라이프사이클 이벤트가 시작되기 전에 경계 훅이 입력을
|
||||
확정합니다:
|
||||
|
||||
```
|
||||
EXECUTION_START → INPUT → FlowStartedEvent → flow methods execute → OUTPUT → EXECUTION_END → FlowFinishedEvent
|
||||
```
|
||||
|
||||
`FlowStartedEvent`는 훅이 확정한 입력을 담으며, 경계 훅에서 `inputs["id"]`를
|
||||
재작성하면 상태 복원 대상이 바뀝니다. `EXECUTION_START`에서의 중단은 여전히
|
||||
`FlowStartedEvent` 다음에 `FlowFailedEvent`가 오는 형태로 나타나며, 중단
|
||||
시점에 그때까지 실행된 훅이 확정한 페이로드와 함께 발생합니다.
|
||||
|
||||
같은 포인트의 훅은 등록 순서대로 실행되며, 전역 훅이 먼저, 그다음 크루 범위
|
||||
훅이 실행됩니다. 텔레메트리(`HookDispatchedEvent`)는 디스패치마다
|
||||
발생합니다.
|
||||
|
||||
## 테스트에서 훅 관리
|
||||
|
||||
```python
|
||||
from crewai.hooks import clear_all_hooks
|
||||
|
||||
clear_all_hooks() # Clears every point, including boundaries
|
||||
```
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- [실행 훅 개요 →](/edge/ko/learn/execution-hooks)
|
||||
- [LLM 호출 훅 →](/edge/ko/learn/llm-hooks)
|
||||
- [도구 호출 훅 →](/edge/ko/learn/tool-hooks)
|
||||
@@ -141,7 +141,7 @@ OpenAI 호환 LLM에 연결하려면 환경 변수를 사용하거나 LLM 클래
|
||||
# Gemini의 OpenAI 호환 API 예시입니다.
|
||||
os.environ["OPENAI_API_KEY"] = "your-gemini-key" # AIza...로 시작해야 합니다.
|
||||
os.environ["OPENAI_API_BASE"] = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-flash" # Gemini 모델을 여기에 추가하세요. openai/ 하위에 위치.
|
||||
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-2.0-flash" # Gemini 모델을 여기에 추가하세요. openai/ 하위에 위치.
|
||||
```
|
||||
</CodeGroup>
|
||||
</Tab>
|
||||
@@ -159,7 +159,7 @@ OpenAI 호환 LLM에 연결하려면 환경 변수를 사용하거나 LLM 클래
|
||||
```python Google
|
||||
# Gemini의 OpenAI 호환 API 예시
|
||||
llm = LLM(
|
||||
model="openai/gemini-3.7-flash",
|
||||
model="openai/gemini-2.0-flash",
|
||||
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
||||
api_key="your-gemini-key", # AIza...로 시작해야 합니다.
|
||||
)
|
||||
|
||||
@@ -145,7 +145,7 @@ planning agent는 복잡한 전략적 사고와 다단계 분석을 처리할
|
||||
from crewai import Agent, Task, Crew, LLM
|
||||
|
||||
# High-capability reasoning model for strategic planning
|
||||
manager_llm = LLM(model="gemini/gemini-3.7-flash", temperature=0.1)
|
||||
manager_llm = LLM(model="gemini-2.5-flash-preview-05-20", temperature=0.1)
|
||||
|
||||
# Creative model for content generation
|
||||
content_llm = LLM(model="claude-3-5-sonnet-20241022", temperature=0.7)
|
||||
@@ -411,7 +411,7 @@ tech_writer = Agent(
|
||||
# Manager 또는 coordination agent
|
||||
manager_agent = Agent(
|
||||
role="Project Manager",
|
||||
llm=LLM(model="gemini/gemini-3.7-flash"), # 조율을 위한 프리미엄
|
||||
llm=LLM(model="gemini-2.5-flash-preview-05-20"), # 조율을 위한 프리미엄
|
||||
# ... 나머지 설정
|
||||
)
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ result = stream.result
|
||||
|
||||
```python
|
||||
from crewai import Flow
|
||||
from crewai.flow import ConversationConfig, ConversationState
|
||||
from crewai.experimental.conversational import ConversationConfig, ConversationState
|
||||
|
||||
|
||||
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)
|
||||
|
||||
@@ -7,9 +7,9 @@ mode: "wide"
|
||||
|
||||
# Arize Phoenix 통합
|
||||
|
||||
이 가이드는 [OpenInference](https://github.com/openinference/openinference) SDK를 통해 OpenTelemetry를 사용하여 **Arize Phoenix**를 **CrewAI**와 통합하는 방법을 보여줍니다. 이 가이드를 완료하면 CrewAI agent를 추적하고 agent 동작을 디버그할 수 있습니다.
|
||||
이 가이드는 [OpenInference](https://github.com/openinference/openinference) SDK를 통해 OpenTelemetry를 사용하여 **Arize Phoenix**를 **CrewAI**와 통합하는 방법을 보여줍니다. 이 가이드를 완료하면 CrewAI agent를 추적하고 agent를 쉽게 디버그할 수 있습니다.
|
||||
|
||||
> **Arize Phoenix란?** [Arize Phoenix](https://arize.com/phoenix/)는 [Arize AI](https://arize.com/?utm_source=crewai-docs&utm_medium=partner&utm_campaign=partner-docs&utm_content=observability-arize-phoenix)의 오픈소스 observability 및 evaluation 옵션입니다. 로컬에서 실행하거나 self-host하려는 경우 Phoenix를 사용하세요. 프로덕션 AI 시스템을 위한 managed cloud 또는 enterprise self-hosted 플랫폼이 필요하면 [Arize AX](https://arize.com/products/ax/)를 사용하세요.
|
||||
> **Arize Phoenix란?** [Arize Phoenix](https://phoenix.arize.com)는 AI 애플리케이션을 위한 추적 및 평가 기능을 제공하는 LLM 가시성(observability) 플랫폼입니다.
|
||||
|
||||
[](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
|
||||
|
||||
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
|
||||
|
||||
### 2단계: 환경 변수 설정
|
||||
|
||||
Phoenix API 키와 OpenTelemetry endpoint를 구성하여 추적 정보를 Phoenix로 전송합니다. collector URL을 변경하면 동일한 설정을 로컬 또는 self-hosted Phoenix endpoint와 함께 사용할 수 있습니다.
|
||||
Phoenix Cloud API 키를 설정하고 OpenTelemetry를 구성하여 추적 정보를 Phoenix로 전송합니다. Phoenix Cloud는 Arize Phoenix의 호스팅 버전이지만, 이 통합을 사용하는 데 필수는 아닙니다.
|
||||
|
||||
무료 Serper API 키는 [여기](https://serper.dev/)에서 받을 수 있습니다.
|
||||
|
||||
@@ -35,8 +35,8 @@ Phoenix API 키와 OpenTelemetry endpoint를 구성하여 추적 정보를 Phoen
|
||||
import os
|
||||
from getpass import getpass
|
||||
|
||||
# Get your Phoenix API key
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix API key: ")
|
||||
# Get your Phoenix Cloud credentials
|
||||
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
|
||||
|
||||
# Get API keys for services
|
||||
OPENAI_API_KEY = getpass("🔑 Enter your OpenAI API key: ")
|
||||
@@ -44,7 +44,7 @@ SERPER_API_KEY = getpass("🔑 Enter your Serper API key: ")
|
||||
|
||||
# Set environment variables
|
||||
os.environ["PHOENIX_CLIENT_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
|
||||
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
|
||||
os.environ["SERPER_API_KEY"] = SERPER_API_KEY
|
||||
```
|
||||
@@ -133,7 +133,7 @@ print(result)
|
||||
|
||||
에이전트를 실행한 후, Phoenix에서 CrewAI 애플리케이션에 의해 생성된 트레이스를 볼 수 있습니다. 에이전트 상호작용과 LLM 호출의 상세한 단계가 표시되어 AI 에이전트를 디버깅하고 최적화하는 데 도움이 됩니다.
|
||||
|
||||
Phoenix 프로젝트를 열고 `project_name` 파라미터에서 지정한 프로젝트로 이동하세요. 모든 에이전트 상호작용, 도구 사용 및 LLM 호출이 포함된 트레이스의 타임라인 보기를 확인할 수 있습니다.
|
||||
Phoenix Cloud 계정에 로그인한 다음 `project_name` 파라미터에서 지정한 프로젝트로 이동하세요. 모든 에이전트 상호작용, 도구 사용 및 LLM 호출이 포함된 트레이스의 타임라인 보기를 확인할 수 있습니다.
|
||||
|
||||

|
||||
|
||||
@@ -145,9 +145,6 @@ Phoenix 프로젝트를 열고 `project_name` 파라미터에서 지정한 프
|
||||
|
||||
### 참고 자료
|
||||
- [Phoenix 문서](https://docs.arize.com/phoenix/) - Phoenix 플랫폼 개요.
|
||||
- [Arize AX](https://arize.com/products/ax/) - Managed cloud 및 enterprise self-hosted observability와 evaluation.
|
||||
- [Arize agent evaluation guide](https://arize.com/guides/ai-agent-handbook/agent-evaluation/) - 트레이스에서 agent 동작을 평가하는 프로덕션 워크플로.
|
||||
- [Arize LLM evaluation guide](https://arize.com/resources/llm-evaluation/) - LLM 애플리케이션 평가를 위한 방법과 메트릭.
|
||||
- [CrewAI 문서](https://docs.crewai.com/) - CrewAI 프레임워크 개요.
|
||||
- [OpenTelemetry 문서](https://opentelemetry.io/docs/) - OpenTelemetry 가이드
|
||||
- [OpenInference GitHub](https://github.com/openinference/openinference) - OpenInference SDK 소스 코드.
|
||||
- [OpenInference GitHub](https://github.com/openinference/openinference) - OpenInference SDK 소스 코드.
|
||||
@@ -33,35 +33,18 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
|
||||
os.environ['OTEL_SDK_DISABLED'] = 'true'
|
||||
```
|
||||
|
||||
### 사용자 OpenTelemetry 설정과의 격리
|
||||
|
||||
CrewAI의 telemetry는 자체 전용 `TracerProvider`에서 실행되며 자신을 전역
|
||||
provider로 등록하지 않습니다. 이를 통해 양방향이 분리됩니다:
|
||||
|
||||
- 프로세스 내 다른 계측된 라이브러리(웹 프레임워크, 데이터베이스 클라이언트,
|
||||
HTTP 클라이언트)의 span은 CrewAI로 전송되지 않습니다.
|
||||
- CrewAI의 telemetry span은 사용자의 관측 가능성 백엔드로 전송되지 않으므로
|
||||
Langfuse, Braintrust, Phoenix 또는 구성한 다른 수집기에 나타나지 않습니다.
|
||||
|
||||
관측 가능성 통합은 영향을 받지 않습니다: 해당 통합은 여기서 설명한 provider와
|
||||
독립적인 자체 tracer provider를 통해 CrewAI를 계측합니다.
|
||||
|
||||
### 데이터 설명:
|
||||
| 기본값 | 데이터 | 사유 및 세부 사항 |
|
||||
|:--------|:-------------------------------------------|:----------------------------------------------------------------------------------------------------------------------|
|
||||
| 예 | CrewAI 및 Python 버전 | 소프트웨어 버전을 추적합니다. 예: CrewAI v1.2.3, Python 3.8.10. 개인 정보 없음. |
|
||||
| 예 | Crew 메타데이터 | 랜덤으로 생성된 키 및 ID, 프로세스 유형(예: 'sequential', 'parallel'), 메모리 사용 플래그(boolean, true/false), 실행에 입력이 전달되었는지를 나타내는 플래그(boolean, true/false — 입력 키나 값 자체는 포함되지 않으며, 이는 `share_crew`가 활성화된 경우에만 수집됩니다), 작업 수, 에이전트 수가 포함됩니다. 모두 비개인 정보입니다. |
|
||||
| 예 | Crew 메타데이터 | 랜덤으로 생성된 키 및 ID, 프로세스 유형(예: 'sequential', 'parallel'), 메모리 사용 플래그(boolean, true/false), 작업 수, 에이전트 수가 포함됩니다. 모두 비개인 정보입니다. |
|
||||
| 예 | 에이전트 데이터 | 랜덤으로 생성된 키 및 ID, 역할 이름(개인 정보 포함 불가), boolean 설정(상세 출력, 위임 가능, 코드 실행 허용), 최대 반복 횟수, 최대 RPM, 최대 재시도 제한, LLM 정보(LLM 속성 참조), 도구 이름 목록(개인 정보 포함 불가) 포함. 개인 정보 없음. |
|
||||
| 예 | 작업 메타데이터 | 랜덤으로 생성된 키 및 ID, boolean 실행 설정(async_execution, human_input), 관련 에이전트 역할 및 키, 도구 이름 목록이 포함됩니다. 모두 비개인 정보입니다. |
|
||||
| 예 | 도구 사용 통계 | 도구 이름(개인 정보 포함 불가), 사용 시도 횟수(정수), 사용된 LLM 속성이 포함됩니다. 개인 정보 없음. |
|
||||
| 예 | 테스트 실행 데이터 | crew의 랜덤 생성 키와 ID, 반복 횟수, 사용된 모델명, 품질 점수(실수), 실행 시간(초 단위)이 포함됩니다. 모두 비개인 정보입니다. |
|
||||
| 예 | 작업 라이프사이클 데이터 | 생성 및 실행 시작/종료 시각, crew 및 작업 식별자, 그리고 작업의 성공 또는 실패 여부가 포함됩니다. 작업이 실패하면 실패를 집계하고 진단할 수 있도록 예외의 **클래스 이름**(예: `TimeoutError`)이 기록되며, 프롬프트·모델 출력·파일 경로·자격 증명이 포함될 수 있는 오류 메시지는 결코 기록되지 않습니다. 타임스탬프를 포함한 span으로 저장됩니다. 개인 정보 없음. |
|
||||
| 예 | 작업 라이프사이클 데이터 | 생성 및 실행 시작/종료 시각, crew 및 작업 식별자가 포함됩니다. 타임스탬프를 포함한 span으로 저장됩니다. 개인 정보 없음. |
|
||||
| 예 | LLM 속성 | LLM의 이름, model_name, 모델, top_k, temperature 및 클래스명이 포함됩니다. 모두 기술적이고 비개인 정보입니다. |
|
||||
| 예 | crewAI CLI를 통한 프로젝트 생성 | 포함 항목: `crewai create`로 새 프로젝트가 생성되었다는 사실, 그 종류(`crew`, `json_crew` 또는 `flow`), 그리고 그 새 프로젝트에 발급되어 해당 프로젝트의 `pyproject.toml`에 기록된 프로젝트 ID. 이는 새 프로젝트 자체의 ID이며, 명령을 실행한 디렉터리의 `project_id`와는 별개로 기록됩니다 — 두 값은 다를 수 있습니다. 프로젝트 이름, 파일 내용, 코드는 기록되지 않습니다. 개인 정보 없음. |
|
||||
| 예 | crewAI CLI를 통한 Crew 배포 시도 | 포함 항목: 배포가 시도되고 있다는 사실과 crew id, 로그를 가져오려고 하는지 여부, 그리고 배포가 CLI 명령에서 시작되었는지 실행 TUI에서 시작되었는지 여부. 프로젝트나 crew의 내용은 기록되지 않습니다. 개인 정보 없음. |
|
||||
| 예 | 실행 환경 | 포함: 프로세스를 실행 중인 AI 코딩 어시스턴트(있는 경우, `claude_code`, `codex`, `cursor`, `unknown` 등 고정 목록 중 하나), 프로세스가 실행되는 위치(`ci`, `container`, `serverless`, `interactive` 등 고정 목록 중 하나), `pyproject.toml`에 설정된 경우 `project_id`, 그리고 머신 크기의 대략적인 구간(`1-2`, `3-4`, `5-8`, `9-16`, `17-32`, `33+`, `unknown` 중 하나). 구간은 범위이며 정확한 코어 수는 절대 포함하지 않습니다 — 정확한 코어 수는 아래 환경 정보에서 옵트인한 경우에만 수집됩니다. 크기 구간은 호스트 CPU 수에서 가져오며, 어시스턴트와 실행 위치 감지는 알려진 환경 변수의 설정 여부만 확인하고 값은 읽지 않음. 개인 데이터 없음. |
|
||||
| 예 | Flow 라이프사이클 신호 | 포함 항목: flow의 시작, 완료 또는 실패 여부, 해당 메서드의 실패 여부, 사람의 입력이나 피드백을 위해 일시 중지되었는지 여부, 해당 시작이 재개된 실행이었는지 여부, 대화 턴의 실패 여부, flow 실행 시간, 그리고 해당 flow가 CrewAI가 내부적으로 실행하는 것인지 사용자가 작성한 것인지 여부. flow 이름은 flow 생성 및 실행에서와 마찬가지로 기록됩니다. flow 또는 해당 메서드가 실패하면 장애 진단을 위해 예외의 **클래스 이름**(예: `TimeoutError`)이 기록되며, 프롬프트·모델 출력·파일 경로·자격 증명이 포함될 수 있는 오류 메시지는 절대 기록되지 않습니다. 메서드 이름과 flow 상태는 절대 기록되지 않습니다. 개인 정보 없음. |
|
||||
| 예 | 트레이스 공유 신호 | 포함 항목: 트레이스 배치가 CrewAI AMP에 성공적으로 공유되었는지 여부와, 익명으로(계정 생성 전) 공유되었는지 또는 계정에 연결되어 공유되었는지 여부. 모든 span과 마찬가지로 위에서 설명한 실행 환경 속성(구성된 경우 `project_id`, 코딩 어시스턴트, 런타임)도 함께 기록됩니다. 이 행은 공유 텔레메트리만 설명하며 — 트레이스 내용이나 공유된 트레이스 링크로 부여되는 접근 권한은 설명하지 않습니다. 트레이스 내용, 입력, 출력은 이 신호에는 기록되지 않습니다. 트레이스를 공유하기 전에 비밀 정보, 개인 데이터, AMP 편집 및 보존 설정을 검토하세요. |
|
||||
| 예 | crewAI CLI를 통한 Crew 배포 시도 | 배포가 시도되고 있고 crew id가 포함되며, 로그를 가져오려고 하는 경우에만 해당. 다른 데이터 없음. |
|
||||
| 아니오 | 에이전트 확장 데이터 | 목표 설명, 배경 이야기 텍스트, i18n 프롬프트 파일 식별자가 포함됩니다. 사용자들은 텍스트 필드에 개인 정보가 포함되지 않도록 해야 합니다. |
|
||||
| 아니오 | 상세 작업 정보 | 작업 설명, 예상 출력 설명, 컨텍스트 참조가 포함됩니다. 사용자들은 이러한 필드에 개인 정보가 포함되지 않도록 해야 합니다. |
|
||||
| 아니오 | 환경 정보 | 플랫폼, 릴리즈, 시스템, 버전, CPU 개수가 포함됩니다. 예: 'Windows 10', 'x86_64'. 개인 정보 없음. |
|
||||
|
||||
@@ -9,7 +9,7 @@ mode: "wide"
|
||||
|
||||
## 설명
|
||||
|
||||
`ScrapeElementFromWebsiteTool`은 CSS 선택자를 사용하여 웹사이트에서 특정 요소를 추출하도록 설계되었습니다. 이 도구는 CrewAI 에이전트가 웹 페이지에서 타겟이 되는 콘텐츠를 스크래핑할 수 있게 하여, 웹페이지의 특정 부분만이 필요한 데이터 추출 작업에 유용합니다. 가져오기는 CrewAI의 SSRF 안전 HTTP 헬퍼를 거칩니다. 요청된 URL과 모든 리다이렉트 홉이 사설 및 예약 대역(클라우드 메타데이터 포함)에 대해 검사되며, TCP 연결은 그 검사를 통과한 IP에 고정됩니다.
|
||||
`ScrapeElementFromWebsiteTool`은 CSS 선택자를 사용하여 웹사이트에서 특정 요소를 추출하도록 설계되었습니다. 이 도구는 CrewAI 에이전트가 웹 페이지에서 타겟이 되는 콘텐츠를 스크래핑할 수 있게 하여, 웹페이지의 특정 부분만이 필요한 데이터 추출 작업에 유용합니다.
|
||||
|
||||
## 설치
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ mode: "wide"
|
||||
지정된 웹사이트의 내용을 추출하고 읽을 수 있도록 설계된 도구입니다. 이 도구는 HTTP 요청을 보내고 수신된 HTML 콘텐츠를 파싱함으로써 다양한 유형의 웹 페이지를 처리할 수 있습니다.
|
||||
이 도구는 웹 스크래핑 작업, 데이터 수집 또는 웹사이트에서 특정 정보를 추출하는 데 특히 유용할 수 있습니다.
|
||||
|
||||
가져오기는 CrewAI의 SSRF 안전 HTTP 헬퍼를 거칩니다. 요청된 URL과 모든 리다이렉트 홉이 사설 및 예약 대역(클라우드 메타데이터 포함)에 대해 검사되며, TCP 연결은 그 검사를 통과한 IP에 고정됩니다.
|
||||
|
||||
## 설치
|
||||
|
||||
crewai_tools 패키지를 설치하세요
|
||||
|
||||
@@ -4,232 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="27 ago 2026">
|
||||
## v1.15.18
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.18)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Promover fluxos de conversa para estáveis
|
||||
- Registrar uma implantação criada com o UUID fornecido
|
||||
- Melhorar a documentação e as APIs dos fluxos de conversa
|
||||
- Permitir que uma declaração nomeie o formato de resposta do roteador
|
||||
- Permitir que um fluxo de chat declare sua própria forma de estado
|
||||
- Aceitar configuração de LLM estilo crew em uma declaração de conversa
|
||||
- Relatar a criação do projeto com o ID gerado
|
||||
- Registrar se uma execução teve entradas, sem registrar as entradas
|
||||
- Preencher o ID do projeto a partir de cada comando de projeto invocado pelo usuário
|
||||
|
||||
### Correções de Bugs
|
||||
- Preservar resultados de ferramentas quando a resposta final estiver vazia
|
||||
- Mapear o Claude Sonnet 4.6 padrão para sua janela de contexto de 1M
|
||||
- Aumentar o max_tokens padrão da Anthropic para chamadas de ferramentas grandes
|
||||
- Renderizar partes do conteúdo da mensagem como texto, não como uma representação Python
|
||||
- Manter os papéis das mensagens quando Agent.kickoff recebe uma conversa
|
||||
- Ignorar ganchos de interceptação em fluxos internos do crewai
|
||||
- Registrar falhas de tarefas como falhas, não como sucessos
|
||||
- Emitir o ciclo de vida do fluxo em uma retomada suprimida
|
||||
- Abrir o TUI de conversa para um fluxo de chat declarativo
|
||||
- Registrar crew_memory como uma string, não como um bool
|
||||
- Sempre emitir project_id para que ausente e vazio permaneçam distintos
|
||||
|
||||
### Documentação
|
||||
- Esclarecer a documentação de observabilidade do Arize Phoenix
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@Vidit-Ostwal, @arizedatngo, @joaomdmoura, @lorenzejay, @lucasgomide
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="19 ago 2026">
|
||||
## v1.15.17
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.17)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Adicionar documentação de fluxos de conversa declarativos
|
||||
- Sintetizar métodos de conversa embutidos para declarações
|
||||
- Permitir que declarações conduzam o modo de conversa
|
||||
- Tornar a opção de conversa inconfundível
|
||||
- Carregar o slug AMP em ferramentas resolvidas a partir de uma referência de slug
|
||||
- Lidar com mensagens únicas excessivamente grandes durante a fragmentação
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir o uso do nome do host da URL como server_name do MCP HTTP e SSE
|
||||
- Fechar o escopo do agente em cada tentativa falhada
|
||||
- Atribuir erros de ferramenta à ferramenta que falhou
|
||||
- Fixar verificações de SSRF em cada redirecionamento e IP de par
|
||||
- Resolver problemas com chamadas de ferramentas nativas quebradas na API de Respostas do OpenAI
|
||||
|
||||
### Documentação
|
||||
- Atualizar a documentação com um instantâneo e registro de alterações para v1.15.16
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@Copilot, @Vidit-Ostwal, @github-code-quality[bot], @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="13 ago 2026">
|
||||
## v1.15.16
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.16)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Introduzir gerenciamento de contexto de execução com suporte a UUID
|
||||
- Registrar que tipo de exceção finalizou um fluxo
|
||||
- Registrar quando um lote de rastreamento é compartilhado com AMP
|
||||
- Contar implantações de qualquer origem e registrar onde elas começaram
|
||||
|
||||
### Correções de Bugs
|
||||
- Registrar a versão em execução em cada span emitido
|
||||
- Corrigir a validação do nome da tabela de busca do MySQL
|
||||
- Impedir que uma tentativa falhada marque a próxima como falhada
|
||||
|
||||
### Documentação
|
||||
- Adicionar guias de Frontend para CopilotKit e AG-UI
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@joaomdmoura, @lorenzejay, @ranst91, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="11 ago 2026">
|
||||
## v1.15.15
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.15)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Relatar resultado do fluxo, duração e sinais de interação humana.
|
||||
|
||||
### Correções de Bugs
|
||||
- Emitir FlowStartedEvent quando um gancho de limite aborta o fluxo.
|
||||
- Escopar a exportação de span para nosso próprio provedor de rastreamento.
|
||||
- Atualizar o torch para a versão 2.13.0 para resolver vulnerabilidade de segurança.
|
||||
- Atualizar o gitpython para a versão 3.1.58 em crewai-tools[github].
|
||||
|
||||
### Refatoração
|
||||
- Atualizar a funcionalidade de injeção de data em agentes.
|
||||
- Padronizar as flags da CLI para kebab-case.
|
||||
|
||||
### Documentação
|
||||
- Snapshot e changelog para v1.15.14.
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="08 ago 2026">
|
||||
## v1.15.14
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.14)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Separar o contexto de execução do agente de codificação e adicionar ID do projeto
|
||||
|
||||
### Documentação
|
||||
- Atualizar snapshot e changelog para v1.15.13
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@joaomdmoura
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="07 ago 2026">
|
||||
## v1.15.13
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.13)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir a preservação do provedor em modelos roteados por LiteLLM.
|
||||
- Fortalecer os mocks do barramento de eventos LLM que são frágeis.
|
||||
- Corrigir a subnotificação do uso de tokens de cache da Anthropic.
|
||||
- Atualizar o h2 para a versão 4.4.1 para resolver a vulnerabilidade de segurança GHSA-6hr6-w5qg-qmwg.
|
||||
|
||||
### Documentação
|
||||
- Adicionar o fluxo de trabalho DOCS_TRANSLATIONS para sincronização de locais.
|
||||
- Corrigir links quebrados no README, tabela de conteúdos e orientações de contribuição.
|
||||
- Snapshot e changelog para a versão 1.15.12.
|
||||
|
||||
## Contributors
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="05 ago 2026">
|
||||
## v1.15.12
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.12)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Aumentar o canário do Flow na versão
|
||||
- Adicionar URLReadTool para leitura de URLs arbitrárias
|
||||
- Adicionar metadados do aplicativo às ferramentas de ação da plataforma
|
||||
- Unificar a estrutura sob `crewai create <resource>`
|
||||
|
||||
### Correções de Bugs
|
||||
- Esclarecer erros de colisão de nomes de rota/manipulador de conversa
|
||||
|
||||
### Documentação
|
||||
- Atualizar o AGENTS.md da estrutura para CLI de criação unificada
|
||||
|
||||
### Mudanças Quebradoras
|
||||
- Nenhuma
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@Vidit-Ostwal, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="04 ago 2026">
|
||||
## v1.15.11
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.15.11)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Rastrear dispatches de hooks de interceptação na telemetria
|
||||
- Adicionar project_id para vincular o uso do OSS a uma conta empresarial
|
||||
- Exibir AMP em AGENTS.md e detectar agentes de codificação na telemetria
|
||||
- Adicionar ferramenta de busca IBM Db2
|
||||
|
||||
### Correções de Bugs
|
||||
- Limpar alertas de sanitização de substring de URL incompleta do CodeQL
|
||||
- Atualizar aiohttp e cryptography para eliminar seis avisos GHSA
|
||||
- Reportar o erro CEL real para falhas dentro de literais de mapa
|
||||
- Pular corretamente a CI de código para PRs apenas de documentação
|
||||
|
||||
### Documentação
|
||||
- Snapshot e changelog para v1.15.10
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@PawanThakurIBM, @Vidit-Ostwal, @gabemilani, @joaomdmoura, @lorenzejay, @lucasgomide, @theCyberTech
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="31 jul 2026">
|
||||
## v1.15.10
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ O Construtor Visual de Agentes permite:
|
||||
| **Respect Context Window** _(opcional)_ | `respect_context_window` | `bool` | Mantém as mensagens dentro do tamanho da janela de contexto, resumindo quando necessário. Padrão: True. |
|
||||
| **Code Execution Mode** _(opcional)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | Modo de execução de código: 'safe' (usando Docker) ou 'unsafe' (direto). Padrão: 'safe'. |
|
||||
| **Multimodal** _(opcional)_ | `multimodal` | `bool` | Se o agente suporta capacidades multimodais. Padrão: False. |
|
||||
| **Inject Date** _(opcional)_ | `inject_date` | `bool` | Se deve injetar automaticamente a data atual no prompt do agente. Padrão: False. |
|
||||
| **Inject Date** _(opcional)_ | `inject_date` | `bool` | Se deve injetar automaticamente a data atual nas tarefas. Padrão: False. |
|
||||
| **Date Format** _(opcional)_ | `date_format` | `str` | Formato de data utilizado quando `inject_date` está ativo. Padrão: "%Y-%m-%d" (formato ISO). |
|
||||
| **Reasoning** _(opcional)_ | `reasoning` | `bool` | Se o agente deve refletir e criar um plano antes de executar uma tarefa. Padrão: False. |
|
||||
| **Max Reasoning Attempts** _(opcional)_ | `max_reasoning_attempts` | `Optional[int]` | Número máximo de tentativas de raciocínio antes de executar a tarefa. Se None, tentará até estar pronto. |
|
||||
@@ -274,7 +274,7 @@ strategic_agent = Agent(
|
||||
role="Analista de Mercado",
|
||||
goal="Acompanhar movimentos do mercado com referências de datas precisas e planejamento estratégico",
|
||||
backstory="Especialista em análise financeira sensível ao tempo e relatórios estratégicos",
|
||||
inject_date=True, # Injeta automaticamente a data atual no prompt
|
||||
inject_date=True, # Injeta automaticamente a data atual nas tarefas
|
||||
date_format="%d de %B de %Y", # Exemplo: "21 de maio de 2025"
|
||||
reasoning=True, # Ativa planejamento estratégico
|
||||
max_reasoning_attempts=2, # Limite de iterações de planejamento
|
||||
@@ -341,7 +341,7 @@ multimodal_agent = Agent(
|
||||
|
||||
- `multimodal`: Habilita capacidades multimodais para processar texto e conteúdo visual
|
||||
- `reasoning`: Permite que o agente reflita e crie planos antes de executar tarefas
|
||||
- `inject_date`: Injeta a data atual automaticamente no prompt do agente
|
||||
- `inject_date`: Injeta a data atual automaticamente nas descrições das tarefas
|
||||
|
||||
#### Templates
|
||||
|
||||
|
||||
@@ -55,16 +55,6 @@ crewai create flow my_new_flow
|
||||
|
||||
Por padrão, `crewai create crew` cria um projeto JSON-first com `crew.jsonc` e `agents/*.jsonc`. Use `crewai create crew my_new_crew --classic` somente quando quiser o scaffold antigo em Python/YAML com `crew.py`, `config/agents.yaml` e `config/tasks.yaml`.
|
||||
|
||||
#### Aliases de flags obsoletas
|
||||
|
||||
As flags antigas em snake_case ainda funcionam, mas ficam ocultas no `--help`. Prefira as formas em kebab-case documentadas em cada seção de comando abaixo.
|
||||
|
||||
| Obsoleto | Use em vez disso |
|
||||
| :--- | :--- |
|
||||
| `--skip_provider` (em `crewai create crew`) | `--skip-provider` |
|
||||
| `--n_iterations` (em `crewai train`, `crewai test`) | `--n-iterations` |
|
||||
| `--task_id` (em `crewai replay`) | `--task-id` |
|
||||
|
||||
### 2. Version
|
||||
|
||||
Mostre a versão instalada do CrewAI.
|
||||
@@ -90,7 +80,7 @@ Treine o crew por um número específico de iterações.
|
||||
crewai train [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: Número de iterações para treinar o crew (padrão: 5)
|
||||
- `-n, --n_iterations INTEGER`: Número de iterações para treinar o crew (padrão: 5)
|
||||
- `-f, --filename TEXT`: Caminho para um arquivo customizado para treinamento (padrão: "trained_agents_data.pkl")
|
||||
|
||||
Exemplo:
|
||||
@@ -123,7 +113,7 @@ Reexecute a execução do crew a partir de uma tarefa específica.
|
||||
crewai replay [OPTIONS]
|
||||
```
|
||||
|
||||
- `-t, --task-id TEXT`: Reexecuta o crew a partir deste task ID, incluindo todas as tarefas subsequentes
|
||||
- `-t, --task_id TEXT`: Reexecuta o crew a partir deste task ID, incluindo todas as tarefas subsequentes
|
||||
|
||||
Exemplo:
|
||||
|
||||
@@ -170,7 +160,7 @@ Teste o crew e avalie os resultados.
|
||||
crewai test [OPTIONS]
|
||||
```
|
||||
|
||||
- `-n, --n-iterations INTEGER`: Número de iterações para testar o crew (padrão: 3)
|
||||
- `-n, --n_iterations INTEGER`: Número de iterações para testar o crew (padrão: 3)
|
||||
- `-m, --model TEXT`: Modelo LLM para executar os testes no Crew (padrão: "gpt-4o-mini")
|
||||
|
||||
Exemplo:
|
||||
|
||||
@@ -322,8 +322,6 @@ Caches podem ser utilizados para armazenar resultados de execuções de ferramen
|
||||
|
||||
Após a execução da crew, você pode acessar o atributo `usage_metrics` para visualizar as métricas de uso do modelo de linguagem (LLM) para todas as tasks executadas pela crew. Isso fornece insights sobre eficiência operacional e oportunidades de melhoria.
|
||||
|
||||
`total_tokens` é o total faturado (`prompt_tokens + completion_tokens`). Campos de breakdown como `cached_prompt_tokens` e `cache_creation_tokens` descrevem subconjuntos já incluídos nesses totais e não são somados novamente a `total_tokens`. Consulte a seção **UsageMetrics field semantics** na documentação do conceito Flows para o contrato completo.
|
||||
|
||||
```python Code
|
||||
# Acessar as métricas de uso da crew
|
||||
crew = Crew(agents=[agent1, agent2], tasks=[task1, task2])
|
||||
|
||||
@@ -260,24 +260,6 @@ print(flow.usage_metrics)
|
||||
rollup **completo** de tokens da execução do Flow.
|
||||
</Note>
|
||||
|
||||
### Semântica dos campos UsageMetrics
|
||||
|
||||
O objeto [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) retornado usa um contrato neutro em relação ao provedor:
|
||||
|
||||
| Campo | Significado |
|
||||
| --- | --- |
|
||||
| `total_tokens` | Total faturado: `prompt_tokens + completion_tokens` |
|
||||
| `prompt_tokens` | Total de tokens de entrada/prompt faturados para a requisição |
|
||||
| `completion_tokens` | Tokens de saída/conclusão faturados para a requisição |
|
||||
| `cached_prompt_tokens` | Subconjunto de leitura de cache dos tokens de prompt (apenas breakdown) |
|
||||
| `cache_creation_tokens` | Subconjunto de escrita de cache dos tokens de prompt (apenas breakdown, Anthropic) |
|
||||
| `reasoning_tokens` | Subconjunto de raciocínio/pensamento quando o provedor reporta separadamente (apenas breakdown) |
|
||||
| `successful_requests` | Número de chamadas LLM agregadas |
|
||||
|
||||
Campos de breakdown como `cached_prompt_tokens`, `cache_creation_tokens` e `reasoning_tokens` **não** são somados sobre `total_tokens` — eles descrevem porções já incluídas em `prompt_tokens` ou `completion_tokens`.
|
||||
|
||||
Para Anthropic, os contadores de leitura e escrita de cache são incorporados em `prompt_tokens`, de modo que workloads em cache são totalmente refletidos em `total_tokens`. Provedores no estilo OpenAI já incluem a entrada em cache dentro de `prompt_tokens`; o CrewAI expõe a porção em cache separadamente para visibilidade.
|
||||
|
||||
Cada campo do [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/types/usage_metrics.py) retornado representa a soma de todas as chamadas de LLM feitas em uma única invocação de `flow.kickoff()`. Os contadores são resetados a cada novo `kickoff()` (e em cada iteração de `kickoff_for_each`), de modo que execuções sucessivas não duplicam o total. A propriedade é segura para ser lida em qualquer momento após o `kickoff()`; lê-la durante a execução retorna o total parcial acumulado até aquele instante.
|
||||
|
||||
## Gerenciamento de Estado em Flows
|
||||
|
||||
@@ -270,22 +270,6 @@ Nesta seção, você encontrará exemplos detalhados que ajudam a selecionar, co
|
||||
)
|
||||
```
|
||||
|
||||
**Uso de tokens e prompt caching:**
|
||||
|
||||
A Anthropic reporta a entrada faturada em contadores separados — `input_tokens` (não em cache), `cache_read_input_tokens` e `cache_creation_input_tokens`. O CrewAI incorpora os três em `prompt_tokens` (e no `input_tokens` nativo nas respostas do provedor) para que `total_tokens` reflita o uso faturado completo em workloads em cache.
|
||||
|
||||
`cached_prompt_tokens` registra a porção de leitura de cache apenas como breakdown; ela já está incluída em `prompt_tokens` e não deve ser somada novamente a `total_tokens`. `cache_creation_tokens` registra escritas de cache da mesma forma.
|
||||
|
||||
```python Code
|
||||
usage = llm.get_token_usage_summary()
|
||||
# total_tokens == prompt_tokens + completion_tokens
|
||||
# prompt_tokens includes cache read + cache write for Anthropic
|
||||
```
|
||||
|
||||
Consulte a seção **UsageMetrics field semantics** na documentação do
|
||||
conceito Flows para o contrato neutro em relação ao provedor usado por
|
||||
`crew.usage_metrics` e `flow.usage_metrics`.
|
||||
|
||||
Consulte a [visão geral dos modelos](https://platform.claude.com/docs/en/about-claude/models/overview) da Anthropic para obter IDs e capacidades atuais e revise a [tabela de descontinuação](https://platform.claude.com/docs/en/about-claude/model-deprecations) antes de fixar um modelo em produção.
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -736,7 +736,7 @@ memory = Memory(llm="anthropic/claude-3-haiku-20240307")
|
||||
memory = Memory(llm="ollama/llama3.2")
|
||||
|
||||
# Usar Google Gemini
|
||||
memory = Memory(llm="gemini/gemini-3.7-flash")
|
||||
memory = Memory(llm="gemini/gemini-2.0-flash")
|
||||
|
||||
# Passar uma instância LLM pré-configurada com configurações customizadas
|
||||
llm = LLM(model="gpt-4o", temperature=0)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user