Compare commits

...

58 Commits

Author SHA1 Message Date
Iris Clawd
8cc6e5d225 fix(deps): bump gitpython to >=3.1.60 for PYSEC-2026-3785–3788
Raise gitpython floor from >=3.1.58 to >=3.1.60 in both the workspace
override and crewai-tools[github] to clear pip-audit findings on
PYSEC-2026-3785 through PYSEC-2026-3788 (.gitmodules include disclosure,
config-injection RCE, --separate-git-dir clone, Repo.blame file read).

Drop the gitpython exclude-newer-package cutoff (3.1.60+ is older than
the global 3-day window). Lock resolves to 3.1.61.

Co-authored-by: Vidit Ostwal <viditostwal@gmail.com>
2026-09-04 08:12:43 +00:00
Havel Cyrus
92eb5f9183 fix: append trailing user turn in native Gemini provider (#6973)
* fix: append trailing user turn in native Gemini provider

GeminiCompletion._format_messages_for_gemini maps assistant messages
to Gemini's 'model' role but never guards against the resulting
contents list ending on a model turn. CrewAI's own agent loop (max
iterations, guardrail retries) can produce exactly that history, and
Gemini's generateContent API rejects it with 400 'Requests ending
with a model turn are not supported'.

Mirrors the existing Mistral/Ollama guard in
LLM._format_messages_for_provider, which never applies to Gemini
since gemini/google model strings resolve to this native provider
instead of the LiteLLM fallback path.

Fixes #6972

* fix: append trailing user turn for Gemini on the LiteLLM fallback path

LLM._format_messages_for_provider already guards Mistral/Ollama
against a trailing assistant turn, but Gemini models routed through
the LiteLLM fallback (no google-genai installed, or a model name not
recognized as native) had no equivalent guard. litellm's own
Vertex/Gemini transformation doesn't handle this either, so the
request reaches Gemini's generateContent API unguarded and 400s.

Complements the native-provider fix in GeminiCompletion, covering
both dispatch paths.

* fix: don't append text turn after unresolved Gemini function call

Address CodeRabbit review on #6973: appending a plain 'Please
continue.' user turn after a trailing model turn that contains an
unresolved function_call violates Gemini's function-calling protocol
-- it requires a matching functionResponse, not free text. Raise a
targeted error instead so the caller notices rather than silently
sending a malformed follow-up.

Also strengthens the native-provider formatting tests to assert exact
role sequence and text content (not just the last role), per review,
and adds a regression test for the unresolved-function-call case.

* fix: guard None parts when checking Gemini history for unresolved function call

contents[-1].parts is typed list[Part] | None; iterating it directly
failed mypy (union-attr) on 3.10-3.13. Narrow to [] before the any()
check and document the ValueError in the docstring.

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-03 16:26:44 +05:30
Parthiban Sivakumar
c90337ba5a fix(llms): normalize scheme and port in Ollama base URL (#7206)
* fix(llms): normalize scheme and port in Ollama base URL

OLLAMA_HOST follows Ollama's own convention and may be a bare host
("0.0.0.0") or a host:port pair ("127.0.0.1:11434") rather than a full
URL. _normalize_ollama_base_url only appended "/v1", so those values
produced invalid base URLs such as "0.0.0.0/v1", and every request
failed with the misleading error "Failed to connect to OpenAI API:
Connection error." - confusing, since no OpenAI model was requested.

Fill in the missing parts the way Ollama's own client does: prepend
http:// when no scheme is present, append the default port 11434 when
none is present and the scheme is http (https implies 443), then append
the /v1 suffix the OpenAI-compatible endpoint requires.

Six of nine realistic OLLAMA_HOST forms were affected, including
127.0.0.1:11434, which is Ollama's documented default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(llms): strip only the parsed path when normalizing Ollama base URL

Stripping trailing slashes from the whole URL before parsing corrupted
inputs that carry a query or fragment. "http://ollama/?tenant=acme" kept
a "/" path and produced a doubled "//v1", and a query or fragment ending
in "/" silently lost that character.

Parse first, then rstrip only parts.path. Adds regression tests for a
root path alongside a query and for a query value ending in "/".

Reported by CodeRabbit on #7206.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-03 16:05:56 +05:30
Vidit Ostwal
3d72c707d5 chore(ci): ignore unpatched nltk GHSA-8mgp-746c-j5xp (#7215)
* chore(ci): ignore unpatched nltk GHSA-8mgp-746c-j5xp

No patched PyPI release exists beyond 3.10.3. nltk is transitive via
crewai-tools[xml] -> unstructured; CrewAI does not call the vulnerable
model-artifact APIs.

Co-authored-by: Vidit Ostwal <Vidit-Ostwal@users.noreply.github.com>

* chore(ci): note dropping nltk GHSA ignore on the next bump

Leave an explicit TODO beside the ignore so GHSA-8mgp-746c-j5xp is
removed when nltk moves past the unpatched 3.10.3 floor.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Vidit Ostwal <Vidit-Ostwal@users.noreply.github.com>
2026-09-02 10:04:20 -07:00
Zhewen Tan
98799a3b09 fix(memory): preserve reusable scope configs (#7068)
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-02 19:35:30 +05:30
Vidit Ostwal
1cef70de52 fix: bump pypdf to 6.16.2 for GHSA-jp53-mhqp-8xcg (#7200)
* fix: bump pypdf to 6.16.2 for GHSA-jp53-mhqp-8xcg

pypdf 6.15.0 fails pip-audit on three moderate DoS advisories; 6.16.1+ patches them.

* chore: keep existing uv.lock environment markers

A full uv lock refresh rewrote unrelated dependency markers; restore them so the pypdf bump stays isolated.

* chore: drop unused pypdf exclude-newer-package in crewai-files

~=6.16.1 plus the global 3-day cutoff already admits 6.16.2.

* chore: drop pypdf from exclude-newer-package

6.16.2 is already older than the global 3-day cutoff; the version floor is enough.
2026-09-02 10:38:33 -03:00
Fang Kaiqi
b608a3595c docs: remove CodeInterpreterTool from AI/ML overview examples (#7100)
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-02 10:31:18 +05:30
Fang Kaiqi
f5db5a1788 docs: point prompt-template link at its current path (#7101)
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-02 10:21:40 +05:30
Vinicius Brasil
968c3065d3 Add Clipper integrations client (#7196)
* Add Clipper integrations client

Implement the internal Clipper discovery and execution contract with
deployment authentication and normalized results. Keep
CrewAIPlatformTools on LegacyClient until the new path is ready for
selection.

* Allow Clipper requests without deployment instances

Local and self-hosted CrewAI executions can have a valid integration
token without a deployment instance UUID. Send the deployment header
when it is available and allow Clipper to attribute other executions to
the organization.

* fixup! Allow Clipper requests without deployment instances

* fixup! Add Clipper integrations client
2026-09-01 14:11:43 -07:00
Vidit Ostwal
818f2624e8 [OSS-149] Accept 1/yes/on on telemetry disable flags (#7185)
* fix(telemetry): accept 1/yes/on on disable flags

CREWAI_DISABLE_TELEMETRY=1 was ignored because the gate only matched true, so telemetry stayed on with no warning.

* fix(telemetry): warn once on unrecognized disable values

Stop repeating the same invalid-flag warning on every telemetry check, and drop the undocumented CREWAI_DISABLE_TRACKING alias from docs.

---------

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-09-01 11:41:20 -07:00
Vidit Ostwal
8e46205619 [OSS-151] Fail closed when human-feedback emit cannot classify (#7188)
* fix(flow): resolve @human_feedback emit LLM from the project model

Omitting llm= no longer hardcodes OpenAI. Collapse and learn resolve through create_llm so MODEL / MODEL_NAME / OPENAI_MODEL_NAME win, then DEFAULT_LLM_MODEL.

* fix(flow): fail closed when human-feedback collapse cannot classify

Stop routing to emit[0] when the collapse LLM cannot be called or its response does not match an outcome. Empty skip still uses default_outcome.

* refactor(flow): extract human-feedback collapse matching helpers

Move match/require outcome helpers out of _collapse_to_outcome so the classify path stays flat.

* refactor(flow): catch only LLM call failures in collapse

Keep HumanFeedbackCollapseError from matching outside the call try so it is raised once and does not trigger a second prompt.

* fix(flow): treat non-object JSON as raw collapse text

Avoid AttributeError when the collapse LLM returns JSON that is not an object.
2026-09-01 17:25:07 +00:00
Thiago Moretto
1bc2e0722d feat(flows): add now() to the CEL expression environment (#7194)
* feat(flows): add now() to the CEL expression environment

CEL expressions in flow definitions had no way to produce the current
date: the environment was built bare, so date-dependent flows failed at
runtime. Register a now() function that returns the current UTC time as
a CEL timestamp. The value is frozen once per kickoff so every
expression in a run sees the same instant, even across midnight.

Standard CEL covers formatting from there: string(now()),
now().getFullYear(), now() - duration('24h').

* chore(flows): drop redundant comment on _cel_now

* refactor(flows): derive CEL env and functions from one registry

A function now lives in one _CelFunctionSpec entry: its annotation for
compile and its implementation factory for evaluate, so the two cannot
drift. Run-scoped values move into _CelRunContext; adding one is a
field, not a new parameter through every helper signature.

* chore(flows): drop _CelRunContext docstring

* fix(flows): freeze a fresh cel now() on human-feedback resume

resume_async never passes through kickoff_async, so a flow restored
with from_pending() had no frozen instant and now() fell back to live
wall-clock per expression. Freeze a fresh instant at resume instead of
persisting the kickoff one: a flow can pause on feedback for days, and
expressions after resume must see today.
2026-09-01 17:05:47 +00:00
Vinicius Brasil
48cc5d4e5e Decouple platform tools from the integrations API (#7180)
* Decouple platform tools from the integrations API

Define normalized selector and tool data so platform tool creation does
not depend on the legacy API response shape. This contract makes the
legacy client easier to replace later.

- Move action discovery and response normalization into LegacyClient.
- Pass ToolInfo from discovery through tool creation and execution.
- Replace the builder flow with direct factory orchestration.
- Preserve app, action, and connection data in immutable models.
- Build sanitized tool names from the full tool identity.
- Preserve legacy request, SSL, and failure behavior with contract tests.

* fixup! Decouple platform tools from the integrations API

* fixup! Decouple platform tools from the integrations API

* fixup! Decouple platform tools from the integrations API

* fixup! Decouple platform tools from the integrations API
2026-09-01 16:07:08 +00:00
Lorenze Jay
917b9df6d7 Validate JSON crews in project environments (#7171)
* Validate JSON crews in project environments

* fix(cli): address standalone deploy review feedback

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-09-01 16:00:59 +00:00
João Moura
ec53d6f534 fix(llms): native structured outputs for current claude models, and snowflake CVE floor (#7182)
* fix(llms): let current claude models use native structured outputs

NATIVE_STRUCTURED_OUTPUT_MODELS only listed 4.5-era prefixes, so Opus 5,
Sonnet 5, Fable 5 and Opus 4.8 fell through to the forced-tool-call
fallback. That path also overwrites params["tools"], so a call combining
tools with a response_model silently lost the caller's tools.

_infer_provider_from_model documented a pattern-matching fallback it never
performed, so a Claude release newer than the constants list resolved to
"openai". Bedrock ('.' in model) and Azure (every OpenAI prefix) are left
out of that fallback because they would capture gpt-* models.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(llms): route bedrock-namespaced anthropic ids to bedrock

"anthropic.claude-*" is Bedrock's namespace, not the Anthropic API, and it
satisfies the anthropic prefix pattern. Settle it before the pattern loop so
an unlisted Bedrock id picks BedrockCompletion. The region-prefixed form
("us.anthropic.claude-*") was resolving to openai, so this repairs that too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(deps): raise snowflake-connector-python floor for CVE-2026-15925

GHSA-5cc2-282f-jjq2 (CRITICAL): the connector does not verify TLS hostnames,
so a network attacker can impersonate the Snowflake endpoint. Fixed in 4.7.1.

crewai-tools[snowflake] declares "snowflake-connector-python>=3.12.4", which
the lock had resolved to 4.6.0. Following the existing convention, the security
floor goes in [tool.uv] override-dependencies rather than the source
declaration, matching how cryptography is handled.

Relocking also refreshes numpy/humanfriendly/nvidia environment markers, which
re-resolution under the relative exclude-newer window produces regardless of
this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 12:01:22 +05:30
Vinicius Brasil
381fef73be Add injectable client for CrewAI platform tools (#7177)
* Add injectable client for CrewAI platform tools

Define an integrations client contract for action discovery and
execution. Keep the existing platform API as the default client to
preserve current behavior.

Allow callers to provide a custom client through CrewaiPlatformTools.

* Fix platform action tool failure tests

* Remove redundant protocol placeholders
2026-08-31 16:01:54 -07:00
Vidit Ostwal
bf56bb13bd ci: require an open issue for first-time contributor PRs (#7169)
* ci: require an open issue for first-time contributor PRs

Gate anyone who is not a returning contributor, and allow the PR only when a closing keyword points at an open issue in this repo.

* ci: accept any open issue mention for first-timer PRs

Drop the closing-keyword regex so #123, owner/repo#N, or an issue URL is enough when that issue is open.

* ci: ignore foreign owner/repo#N in first-timer issue gate

Bare #123 no longer matches the suffix of other/repo#123, so an open local issue cannot keep that PR open.
2026-08-31 22:48:14 +05:30
Vidit Ostwal
614efcdd30 ci: close first-time contributor PRs that lack a linked issue (#7164)
* ci: close first-time contributor PRs that lack a linked issue

* ci: indent FTC close comment so the workflow YAML parses
2026-08-31 21:29:59 +05:30
Vidit Ostwal
0e7625813b fix: bump nltk to 3.10.3 for PYSEC-2026-3726 (#7162)
Force the xml extra and workspace override onto the patched release so pip-audit stops failing on the 3.10.0 symlink file-read advisory.
2026-08-31 21:26:41 +05:30
Ran Shemtov
a35fbc864d docs: update channels guide to current copilotkit channels api (#7016)
* docs: update channels guide to current copilotkit channels api

* docs: translate channels and frontend overview guides to ar, ko, pt-BR

---------

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-08-31 15:47:58 +00:00
chenshiyang
265697b6f8 docs: refresh retired Gemini model ids (#7003)
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-31 10:43:31 -03:00
Vinicius Brasil
da4daadba0 Parse platform application selectors (#7148)
Stores application, action, and connection values in an internal
selector. Validates the selector with clearer error messages. Keeps
existing application syntax and legacy API requests unchanged.
2026-08-28 15:36:29 -07:00
Lucas Gomide
e9d4c57b2a fix: run model call hooks on every path and propagate a deny (#7111)
* fix: let a hook deny reach the caller as a deny

A hook that raised `HookAborted` on `pre_model_call` never reached the code
making the call: the LLM layer caught it and returned `False`, which providers
translated into `ValueError("LLM call blocked by before_llm_call hook")`,
dropping the reason and the source and making a policy decision
indistinguishable from a provider outage. Every internal model call then
absorbed that error through the `except Exception` that keeps a provider hiccup
from failing a run, so memory analysis fell back to defaults and the converter
and reasoning handler retried the call that was just denied. The abort now
propagates out of the LLM layer while the boolean convention keeps its
documented `ValueError` via `LegacyHookBlocked`, and the fail-open handlers
around internal model calls re-raise it instead of degrading.

* fix: dispatch model call hooks on the paths that skipped them

A model call was only checked when the executor loop drove it: the
`from_agent is not None` short-circuit in `base_llm` silenced the hooks
for agent planning and step observation, no provider `acall` dispatched
them at all, and `InternalInstructor` bypassed `llm.call` entirely. This
replaces that short-circuit with an explicit
`model_call_hooks_already_dispatched` window so the enclosing caller
claims the dispatch, adds the pre-call dispatch to every provider's
`acall`, and runs the hooks around the Instructor client call. A denial
now emits a denied event instead of being logged and reported as a
provider failure.

* fix: report a boolean-convention deny as a deny, not an outage

A `before_llm_call` hook that blocks by returning `False` reached the five
native providers as a plain `ValueError`, which fell through to their generic
`except Exception` and was logged and emitted as `OpenAI API call failed: ...`
— the same deny raised as `HookAborted` was already labelled correctly, so the
two dialects disagreed on whether a policy decision was a provider outage. The
LLM layer now converts it into `LLMCallBlockedError`, still a `ValueError` so
the fail-open handlers around internal model calls keep absorbing it, but its
own type so a provider can report the decision it is. Since a block is raised
rather than returned, the thirteen callers that turned the return flag into a
raise by hand drop that line, and `_prepare_llm_call` raises the same type.

* fix: keep a denied plan from letting the agent run unplanned

`AgentExecutor.generate_plan` wraps `handle_agent_reasoning()` in a bare
`except Exception`, so guarding the reasoning handler alone still left the
deny absorbed one frame up: the executor logged "Error during planning" and
the agent proceeded with no plan. It now re-raises `HookAborted` like the
other planning boundaries, and the accompanying test also covers the
boolean convention still degrading at a fail-open site.

* fix: stop a denied knowledge query from running the task without knowledge

`handle_knowledge_retrieval` and its async twin wrap the query rewrite in
their own `except Exception`, so guarding `_get_knowledge_search_query`
alone still let `execute_task` continue on the unaugmented prompt after a
deny. Both now emit the terminal `KnowledgeSearchQueryFailedEvent` and
re-raise `HookAborted`, matching the second-frame guard already added to
`AgentExecutor.generate_plan`. Also documents the abort contract on
`PlannerObserver.observe`.

* fix: stop nine callers from re-swallowing a model call deny

CodeRabbit caught the replan path re-swallowing a deny, so an AST sweep of
every caller of a guarded function found the same defeat in nine places:
classic and replan planning, memory recall and memory save on both `Agent`
and `LiteAgent`, the base executor's save, and `LLMGuardrail.__call__`,
which turned a refused call into validation feedback. Each now re-raises
`HookAborted` after emitting whatever terminal event it owes, while every
other failure keeps degrading as before — the knowledge guards move to that
same idiom instead of duplicating their emit.

* fix: pair a denied guardrail with the event it started

Re-raising from `LLMGuardrail` left `process_guardrail` between its started
and completed events, so a denied validation read as one still in flight
rather than a policy decision. It now emits `LLMGuardrailCompletedEvent`
with the deny reason before the abort leaves, matching what every other
guarded site in this change already does.

* fix: stop retrying a task after a hook denied its model call

`Agent.execute_task` funnels every exception into `_handle_execution_error`,
which re-runs the whole task up to `max_retry_limit` times, so a policy deny
read as a transient blip: a crew whose first model call was denied retried and
returned a normal answer. `HookAborted` now joins `_passthrough_exceptions`,
the tuple already reserved for deliberate stops. The new boundary tests drive
the public entry points instead of the frame that makes the call, and count
model calls so a deny that gets retried fails the assertion — ten of the twelve
fail against `main`.

* fix: stop a denied plan step from being reported as a failed step

Making model call hooks reachable on agent-bearing calls put a deny inside
`StepExecutor.execute`, whose broad `except Exception` turned it into
`StepResult(success=False)` and let the plan carry on; `HookAborted` now
joins `ToolExecutionFailedError` in the passthrough handlers there, and
`execute_todos_parallel` re-raises a deny that `return_exceptions=True`
would otherwise record as one failed todo. `_emit_call_denied_event` also
renders the source through the now-public `source_name`, so a hook that
names itself with a callable reads as its name instead of a repr.

---------

Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-28 12:32:09 -04:00
João Moura
cba6c03646 feat(events): record how a crew run ended, for every user (#7118)
* feat(events): record how a crew run ended, for every user

Crew was the one level with no ungated terminal record. `Crew Execution` and
`end_crew` are both behind `share_crew`, which defaults False, so for
essentially every run there is no end-of-crew span at all - not one with fields
missing. Task outcomes ship ungated, flow outcomes ship ungated; crew being the
exception looks like an accident of history rather than a decision.

Adds `Crew Completed` carrying `outcome` and an explicit `duration_ms`, keyed by
crew_key/crew_id so it joins the existing ungated `Crew Created`. Modelled
directly on `flow_completed_span`, including its reasoning: a separate span
rather than holding `Crew Execution` open, because that span is emitted and
closed at start, so holding it would drop every run that is killed or crashes.

`on_crew_failed` called no telemetry at all before this, so a failed crew
produced nothing. It deliberately does not call `end_crew`, which writes onto
the gated execution span a failed run may never have opened.

Deliberately NOT included, each for a reason:

- Tokens. `crew.token_usage` sums per-agent LLM counters, and two agents sharing
  one LLM object share one counter, so the total double-counts today. Putting it
  on a span would propagate a known-wrong number into a metric. The dedup keys on
  `id(llm._token_usage)`, not `id(llm)` - `Agent.copy()` shallow-copies the LLM -
  and it changes the value of public `Crew.calculate_usage_metrics`, so it earns
  its own change.
- Models. Already on the ungated `Crew Created` span at 99.86% coverage; this
  joins to them by crew_id rather than duplicating.
- Tool counts. The ungated `Tool Usage` span covers only the ReAct path, the
  plan/step path double-emits, and nested crews share one RuntimeState - the
  count needs a design decision on cache hits before it is worth emitting.
- error_type. Needs 4.1's exception-class field factored out of task_events so
  both events share it, rather than duplicated hours after that merged.

Tests use the exporter pattern this suite already uses rather than mocking
`EventListener._telemetry`: EventListener is a singleton, so swapping that leaks
a MagicMock into every later test. The listener tests assert on the stamp
lifecycle instead. Verified order-independent over five randomized runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(telemetry): docstring the crew-completed helpers

Eight of the thirteen functions in this file carried a docstring and five
did not, which is an inconsistency inside the file this PR adds rather
than anything inherited. Documents the two fixtures, the span lookup, the
event-bus runner and the two test methods that were missing one.

No behaviour change: 9 passed, and re-run under random ordering on two
seeds to confirm order-independence.

Deliberately not addressed: the reviewer's 52.17% docstring-coverage
figure is dominated by event_listener.py, where 84 functions - nearly
every pre-existing on_* handler - carry no docstring. That is the file's
convention, and documenting them here would be an unrelated refactor.
The public API this PR adds, Telemetry.crew_completed_span, is documented.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 13:06:51 -03:00
João Moura
1f6e327b3c feat(events): report machine size as a coarse band, not a core count (#7117)
* feat(events): report machine size as a coarse band, not a core count

`runtime_context` says where a process runs but carries no capacity axis, and
its largest bucket is a catch-all: a gunicorn worker on a VM and
`python main.py > out.log` on a MacBook both report `non_interactive`. Docker
Desktop on a laptop reports `container` via /.dockerenv, and a Remote-SSH shell
on a server reports `vscode_terminal`. So "server or laptop" is not answerable
from it today.

Adds `cpu_band` to the common span attributes, so it rides every span the way
`runtime_context` does rather than sitting on `Crew Created` alone - which would
answer nothing for Flow-only, CLI-only or standalone-agent runs.

Six bands, powers of two, top one open-ended: 1-2, 3-4, 5-8, 9-16, 17-32, 33+.
Open-ended because the exact count is the fingerprint - the observed fleet
maximum is 512, and a span reporting 512 identifies one machine. The vocabulary
is closed and asserted, like KNOWN_CODING_AGENTS and KNOWN_RUNTIME_CONTEXTS.

The share_crew-gated exact `cpus` attribute and the four platform* attributes
are untouched. That gating was a deliberate 2024 classification of machine
fingerprint as shareable content (44e38b1d5), and this does not reverse it: a
band is a range, the gated attribute remains the precise value.

Documents a trap in the docstring rather than leaving it to be rediscovered:
os.cpu_count() reports HOST cores, not the cgroup quota, so a 1-vCPU pod on a
96-core node lands in 33+. Right for "what kind of machine", wrong for "what did
this run get" - os.process_cpu_count() gives the latter but needs 3.13, above
this package's floor.

Docs updated in en/ar/ko/pt-BR, in the default-on Execution Environment row,
stating explicitly that the band is a range and the exact count stays opt-in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(telemetry): say where the cpu band comes from

The Execution Environment row ended "Detection reads only whether known
environment variables are set, never their values". True for the assistant
and runtime-context fields, and wrong for the band this PR adds:
detect_cpu_band() reads os.cpu_count() (runtime_env.py:306) and touches no
environment variable. In a privacy disclosure table that is the kind of
inaccuracy worth a line.

Names the source explicitly and scopes the env-var sentence to the two
fields it actually describes. All four locales at parity.

pt-BR also takes the reviewer's wording fix: "um de uma lista fixa" ->
"um valor de uma lista fixa", and the missing comma before o `project_id`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:50:10 -07:00
Lorenze Jay
4bc5d29242 [docs-freeze] docs: snapshot and changelog for v1.15.18 (#7138) 2026-08-27 18:07:05 +00:00
Lorenze Jay
5fd7c47669 feat: bump versions to 1.15.18 (#7136) 2026-08-27 18:00:28 +00:00
Lorenze Jay
f90d37b4ac feat(flow): promote conversational flows to stable (#7107)
Move the canonical API into crewai.flow while preserving experimental imports and declarative references through compatibility aliases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 23:06:57 +05:30
Lorenze Jay
5139078c6a fix(agents): preserve tool results when final answer is empty (#7133)
* fix(agents): preserve tool results when final answer is empty

Updated the StepExecutor to ensure that if the final answer from a tool call is empty, the last valid tool result is returned instead. This change enhances the reliability of the output in scenarios where the final answer may not provide useful information. Added tests to verify that both text and native steps correctly preserve tool results under these conditions.

* raise on empty string path
2026-08-27 22:10:11 +05:30
João Moura
fcdeb3d98d feat(events): record a created deployment with the uuid it was given (#7115)
`Create Crew Deployment` fires before the API call that creates the
deployment, so it counts creation ATTEMPTS and cannot carry the uuid - the
call that creates the deployment is the call that returns it. Live effect:
`create_deployment` reads 76,015 events with 0 carrying a uuid (0.000%), so
deployments cannot be joined to anything.

Moving the existing span after the response would fix the uuid and silently
redefine the metric, turning attempts into successes; the deployment churn
figures are built on attempts. So this adds a second span rather than moving
the first.

`Crew Deployment Created` fires after `_validate_response`, which raises
SystemExit on failure - a failed create therefore still counts as an attempt
and reports no creation. Both creation paths, git remote and zip upload,
converge on that line and both return the uuid.

Emits no `deploy:created` feature count: the attempt span already does, and a
second emit would double the deployment count that origin-independent
aggregation depends on.

Tests cover the emitter (uuid carried, distinct span name, no second feature
count, absent-vs-empty uuid) and the call site across both creation paths plus
the failure path.

The warehouse consumer must be widened BEFORE this merges or it delivers
nothing: `mv_span_fanout_forward` filters on a hard-coded 13-name allowlist,
and `mv_fanout_deployment_spans`'s `multiIf` ends in a catch-all `remove_crew`
arm that would mislabel the new span. Runbook prepared separately.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:13:17 -07:00
Vidit Ostwal
704db1d66f fix(llms): map default Claude Sonnet 4.6 to its 1M context window (#7125)
* fix(llms): map default Claude Sonnet 4.6 to its 1M context window

Native AnthropicCompletion fell back to 200k for claude-sonnet-4-6, so the default instance understated the documented limit.

* fix(llms): align Anthropic context windows with active Claude models

Retired Claude 3/2/Instant IDs no longer have dedicated entries; the map now covers the current Claude API lineup and their documented 1M vs 200k windows.

* fix(llms): map Claude Mythos 5 to its documented 1M context window

Native AnthropicCompletion fell back to 200k for claude-mythos-5 even though Anthropic lists a 1M-token window.
2026-08-26 14:09:35 -07:00
Vidit Ostwal
039f6ff5f6 fix(llms): raise Anthropic default max_tokens so large tool calls survive (#7077)
* fix(llms): raise Anthropic default max_tokens so large tool calls survive

* fix(llms): default Anthropic to sonnet-4-6 and drop retired models

---------

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-08-26 19:01:31 +00:00
Vidit Ostwal
56e0e85a81 [OSS-115] Persist custom conversational replies after fallback append (#7026)
* fix(flows): persist custom conversational replies after fallback append

Custom @listen routes that only return a public string were appended after kickoff, so @persist snapshots missed the assistant turn. Snapshot again from the same persist path once the fallback writes state.messages.

* test(flows): cover @persist restore of custom conversational replies

Add regression coverage for custom @listen returns across fresh Flow instances, no double-append on built-in converse, and a single user/assistant message-added event.

* docs: note that public listen returns persist as assistant replies
2026-08-26 09:41:28 -07:00
Vidit Ostwal
871c9c5131 chore(telemetry): remove unused _safe_telemetry_operation from crewai_core (#6977) 2026-08-25 20:29:02 +00:00
João Moura
6ad3bf9390 fix(agents): render message content parts as text, not a python repr (#7109)
* fix(agents): render message content parts as text, not a Python repr

A message whose `content` is a multimodal parts list collapsed to
`str(content)` wherever a message had to become a string, so the model
saw `[{'type': 'text', 'text': 'hello'}]` in Current Task, and memory
stored and searched that same repr.

Four sites flattened it that way: the turn promoted into the executor
prompt, the memory recall query, what `_save_kickoff_to_memory` writes,
and `_message_content_text` (token estimation and oversized-message
splitting).

The extraction already existed, inline in `_format_messages_for_summary`
-- text blocks joined, or `[multimodal content]` when a list carries
none. This lifts it to `_content_parts_text` and routes all five callers
through it, so summary, prompt, memory and token counting agree.

`_message_content_text` becomes `message_content_text`: it now has a
caller outside its module, and `agent/core.py` imports only public names
from `agent_utils`. It is not re-exported from any `__init__`, so no
public import path changes.

`test_list_content_uses_str` pinned the repr, so it is intentionally
rewritten to pin the text. Every other existing caller is unchanged:
30 failures on main, 30 on this branch, identical names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

* fix(agents): skip a content part whose text is not a string

`_content_parts_text` joined `block["text"]` straight into a string, and
content blocks are `dict[str, Any]` arriving from a model, so a `text`
key holding an int, dict or None raised `TypeError`. That was contained
to summarization before; routing the prompt, memory and token-estimation
paths through the same helper widened it to `Agent.kickoff`, where the
old `str()` had merely produced an ugly string.

Such a block carries no usable text, so it is skipped. A list left with
nothing usable still falls back to `[multimodal content]`.

Writes the convention down in AGENTS.md rather than leaving it in a
review thread: never `str()` a message's content, use
`message_content_text`. Four sites had independently reached for
`str()`, which is what this whole change is undoing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 19:34:05 +00:00
Vidit Ostwal
7c23857aed [OSS-137] Report MCP HTTP auth failures instead of cancelled connections (#7067)
* feat(mcp): add shared error classifier for HTTP auth failures

When an MCP server refuses a streamable-HTTP connection, the HTTP status is
observed by the client but often buried inside anyio teardown. Add typed
connection exceptions and helpers to recover the status from exception groups,
CancelledError context chains, and httpx errors so later call sites can report
authentication failures instead of guessing.

Groundwork only; no call sites wired yet.

* feat(mcp): raise typed errors from HTTPTransport.connect on HTTP status

When streamable-HTTP connect fails with an httpx HTTPStatusError, classify
the status via the shared MCP exception helpers and raise MCPAuthenticationError
for 401/403 or MCPHTTPError for other refused statuses instead of a generic
ConnectionError that hides the credential problem.

* refactor(mcp): centralize raise_connection_failure and simplify connect

Move connection failure classification into exceptions.py so transports and
clients share one helper. Flatten HTTPTransport.connect to a single except
path that cleans up once and raises, avoiding the outer handler re-classifying
errors the inner handler already typed.

* fix(mcp): classify auth failures in MCPClient.connect before reporting cancelled

When a streamable-HTTP server refuses the connection, the awaiting coroutine
often sees only CancelledError while the HTTP status surfaces during transport
unwind. Inspect cleanup for the status before emitting error_type=cancelled,
and fix HTTPTransport.disconnect so it raises typed errors instead of
suppressing exception groups that carry the refusal.

* fix(mcp): replace speculative tool resolver errors with classifier

Use raise_connection_failure for native MCP discovery instead of hedged
cancel-scope wording, preserve typed MCPConnectionError from setup, and
detect event-loop presence explicitly so ConnectionError is not mistaken
for a missing running loop. Update HTTPS discovery to classify HTTP status
codes via find_http_status.

* refactor(mcp): collapse native tool resolver failure handlers

CancelledError is not an Exception subclass, so handle it alongside
Exception in one except clause and delegate to a shared helper.

* refactor(mcp): call raise_connection_failure directly in tool resolver

* fix(mcp): classify tool execution auth failures in events

Add tool_execution_error_type so call_tool_result emits authentication
instead of server_error for MCPAuthenticationError and HTTP 401/403.
Preserve typed MCPConnectionError in _retry_operation instead of flattening
them into a generic ConnectionError first.

* feat(mcp): add status_code to MCPConnectionFailedEvent

Surface the HTTP status observed during connection failures on the event
payload and in verbose console output, so executions and checkpoints record
401/403 alongside error_type=authentication instead of only the message text.

* fix(mcp): handle cancellation and exception groups in auth paths

Ensure discovery cleanup runs on CancelledError, classify mixed
BaseExceptionGroups during HTTP connect, and fix ExceptionGroup imports
on Python 3.10 with regression tests.

* fix(mcp): preserve auth errors from discovery disconnect cleanup

Re-raise MCPConnectionError from disconnect during cancellation cleanup
instead of logging and swallowing it, with a regression test.

* fix(mcp): unwind transport context to recover auth on cancel

Always exit pending streamable-HTTP contexts before classifying failures,
handle CancelledError during client cleanup, propagate typed HTTPS discovery
errors, and add regression tests for the teardown recovery path.

* fix(mcp): classify auth from groups and timeout teardown

Handle BaseExceptionGroup in HTTPS discovery and recover HTTP 401 from
streamable-HTTP context exit after connect timeouts, with regression tests.

* refactor(mcp): consolidate client connection failure reporting

Extract _report_connection_failure and delegate _http_failure and
_connection_failure to it without changing connect error behavior.

* refactor(mcp): drop redundant client failure helper wrappers

Call _report_connection_failure directly from connect() instead of
_http_failure and _connection_failure delegators.

* fix(mcp): propagate CancelledError after HTTP transport teardown

Re-raise cancellation from disconnect when no HTTP auth status is
recovered during context unwind, with a regression test.

* fix(mcp): preserve typed errors from MCPClient.disconnect

Re-raise MCPConnectionError and CancelledError from exit-stack teardown
instead of wrapping auth failures in RuntimeError, with a regression test.
2026-08-25 16:47:31 +00:00
Lucas Gomide
3df34d9169 fix: skip interception hooks on crewai-internal flows (#7079)
* fix: skip interception hooks on crewai-internal flows

The `AgentExecutor` and the memory encoding/recall flows are `Flow`
subclasses CrewAI runs for its own bookkeeping, and they were dispatching
interception points as if their methods were the caller's steps — a hook
saw machinery no user wrote, and a policy could deny a run over it.
`Flow._skip_interception` now suppresses every point on a flow marked
`is_crewai_internal`, except the execution boundary on machinery that is
itself the run the caller asked for. A standalone `Agent.kickoff()` keeps
its boundary and stays blockable, while the same executor bound to a crew
or nested in a caller's flow stays silent, so boundaries only ever fire
at the root.

* docs: note that the resume match id feeds the boundary check

`from_pending` seeds `_flow_match_id` from `instance.flow_id` for the usage
listener's filter, and `resume_async` forces `current_flow_id` to it for the
duration of the resume. `AgentExecutor._owns_execution_boundary` compares the
two, so seeding the original persisted id instead would make a resumed
standalone agent disown a boundary its kickoff already opened.
2026-08-25 09:54:17 -04:00
João Moura
4e0b2e2b15 fix(events): record task failures as failures, not as successes (#7073)
* fix(telemetry): record task failures as failures, not as successes

close_span() sets StatusCode.OK unconditionally, and TaskFailedEvent was routed
to Telemetry.task_ended, which calls it. Every failed task was therefore
exported as OK, which is why error_count downstream is not merely low but
exactly zero: 240.0M task executions across 13 months in
crew_task_executions_daily_target, error_count = 0 in every one of them.

The same line had a second defect. The span was only ended when
source.agent.crew was present, so a task failing without one was popped from the
span map and then never closed - never ended, never exported, invisible rather
than mislabelled. task_failed takes no crew (it reads nothing off one), so that
condition disappears rather than being widened.

Only the exception class name is recorded, never the message, which routinely
contains prompts, model output, file paths and credentials.
close_span_with_error drops any value failing str.isidentifier(), so a message
cannot be recorded even if one is passed by mistake.

This is the task half of closed PR #6781, re-cut onto main as that PR asked for.
The crew half is deliberately left out: crew_execution_span() returns None unless
share_crew=True, so crew._execution_span is None for nearly every user and a
crew-failure handler would exit immediately for the default population.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* fix(telemetry): take the exception class for error_type, not a free-form string

cursor and CodeRabbit both flagged the sanitization, and they were right: the
package already had a stronger convention and this change had not used it.
Telemetry._safe_error_type takes the exception *class*, and its docstring says
why in as many words - "a single-word message such as 'secret_token' is itself a
valid identifier", so filtering a string with isidentifier() is not enough. The
ported code predates that helper and reinvented the weaker check.

TaskFailedEvent.error_type is now type[BaseException] | None, so pydantic itself
rejects a message before any of our code runs, and task_failed routes it through
_safe_error_type. The identifier check in close_span_with_error stays as the
second gate on the derived name, which is the role _safe_error_type's docstring
already describes.

Also adds producer-level tests, which CodeRabbit correctly identified as missing:
every earlier test constructed TaskFailedEvent directly, so a regression in the
two emit sites this change touches in task.py would have passed the whole suite.
The sync and async producers are driven through Task._execute_core and
Task._aexecute_core with a distinctive exception class, and each patches a
different agent method (execute_task vs aexecute_task), which is why they can
regress independently. Verified by dropping error_type from both producers: all
three new tests fail, and pass again when restored.

Removes an unused `import os` left behind when the fixture was rewritten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(telemetry): capture producer failures at the emit boundary, not via the bus

The producer tests subscribed a handler to crewai_event_bus and asserted on what
it received. That passed this file in isolation and every randomized local run,
then failed in CI inside a 621-test shard with zero events captured:

  FAILED tests/telemetry/test_task_failure_instrumentation.py::
    test_sync_producer_puts_the_exception_class_on_the_event
  assert 0 == 1  +  where 0 = len([])

task_failed is an "ending" event, and with an empty scope stack - there is no
real kickoff in these tests - dispatch is conditional on event-context state that
other tests in the same worker process can leave behind. Subscribing made the
assertion depend on the bus choosing to dispatch, which is not what these tests
are about: they are about what the producer in task.py constructs.

Patching crewai_event_bus.emit records the event unconditionally at the point the
producer hands it over, with no dispatch involved. Both producers ignore emit's
return value, so returning None is faithful.

Containment re-verified after the change: dropping error_type from both producers
fails exactly these three tests and nothing else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* fix(events): keep TaskFailedEvent JSON-serializable with a class-valued error_type

error_type holds an exception class, which is not a JSON type, so
model_dump(mode="json") raised PydanticSerializationError for the whole event -
not just that field. Two real consumers depend on it: the checkpoint listener
dumps every event through EventRecord, and the tracing listener JSON-POSTs events
to AMP. A single task failure therefore took out checkpointing.

field_serializer with when_used="json" returns the class name. The "json" scope is
load-bearing: event_listener hands the live class to Telemetry.task_failed, which
needs it for _safe_error_type, so python-mode dumps must keep the class.

The annotation is a module-level _ExceptionClass alias rather than an inline
type[BaseException], because TaskFailedEvent declares a field named `type` which
shadows the builtin for the rest of the class body - inline, it raises TypeError
at import ("task_failed"[BaseException]) and mypy rejects it as "Variable ... is
not valid as a type". Quoting satisfies neither tool: ruff flags UP037 and mypy
still resolves it in the class scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* fix(events): let a dumped error_type restore, instead of degrading the event

The serializer added in the previous commit stopped model_dump(mode="json") from
raising, but nothing accepted the class-name string back. _resolve_event
(state/event_record.py:32-35) wraps cls.model_validate in a bare except and falls
back to BaseEvent, so restoring a checkpoint after a task failure silently
dropped the whole event -- including `error`, a plain string that would otherwise
have survived. Traded a loud failure for a quiet one.

Measured before: dumped error_type='ValueError' and error='boom', restored as
BaseEvent with neither attribute. After: restores as TaskFailedEvent with
error='boom' and error_type is ValueError.

A BeforeValidator resolves a name against real exception classes only -- builtins
first, then a walk of BaseException.__subclasses__(). So this does not reopen the
hole the class-typed field closes: "secret_token" resolves to nothing, is returned
unchanged, and is rejected by the field's own type. Asserted for secret_token,
sk_live_1234, dict and os.

A name whose class is not imported in this process still degrades, which is
deliberate: synthesising a class from an arbitrary string is the injection risk
this field exists to avoid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-25 14:30:46 +05:30
Lorenze Jay
a9cb0bdf02 Lorenze/deprecate/answer from history (#7105)
* feat(flows): enhance conversational flow documentation and APIs

- Updated the description to clarify the use of `handle_turn` and structured streaming in multi-turn chat applications.
- Added a warning about the experimental nature of the conversational features.
- Improved the overview section to include structured streaming and refined the explanation of session handling.
- Enhanced the API documentation for `handle_turn`, `stream_turn`, and `chat` methods, emphasizing their roles in conversational flows.
- Clarified the turn lifecycle and the handling of user messages within the flow.
- Updated examples to reflect changes in message handling and session tracing.
- Ensured consistency across language versions in the documentation.

* feat(flow): deprecate answer_from_history route

Guide conversational flows toward the existing converse route while preserving compatibility warnings and schema metadata.

Co-authored-by: Cursor <cursoragent@cursor.com>

* stacklevel=3 raising it higher

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 11:01:22 +05:30
Vidit Ostwal
390ee770cb chore(ci): ignore unpatched chromadb HTTP-server GHSAs (#7108)
No patched PyPI release exists, and CrewAI only uses the embedded
PersistentClient, not the vulnerable HTTP server.
2026-08-25 13:21:10 +08:00
João Moura
9652af6ae0 fix(agents): keep message roles when Agent.kickoff gets a conversation (#7065)
* fix(agents): keep message roles when Agent.kickoff gets a conversation

`Agent.kickoff` accepts `str | list[LLMMessage]`, but `_prepare_kickoff` joined
every message's content into one string. Measured with a recording LLM, a
three-turn conversation reached the provider as two messages:

    system | You are Support...
    user   | Current Task: my order id is 42\nthanks, checking\nwhere is it?

So the agent's own previous reply was presented as something the user said, and
the model could not tell who said what. `LiteAgent.kickoff` already did this
correctly, which is why the same list gave four messages with roles intact
there.

The last message is now this turn's request and the ones before it travel as
`inputs["history"]` -- the way `inputs["files"]` already does -- which both
executors splice in after the system prompt and before the user prompt. Memory
recall still runs over the whole conversation text, not just the last turn.

A plain string and a single-message list are byte-identical to before, which is
what every existing caller passes.

Also widens `LiteAgentExecutionStartedEvent.messages` from `list[dict[str, str]]`
to `list[LLMMessage]`: it raised a ValidationError for a message whose content
was `None` or a content-part list, both of which are valid `LLMMessage` shapes
that `Agent.kickoff` already accepts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(agents): carry history without a system prompt, tool calls, or dup files

Three review findings on the message-role work, all real.

History was spliced only in the branch that builds a system prompt. With
`use_system_prompt=False` or a custom template there is one combined prompt, so
history went nowhere and only the last turn reached the model -- worse than the
flattening this PR replaced, which at least included the text. Both executors
now splice in either branch, via one `_append_history` helper each.

Filtering on truthy content dropped an assistant turn that only requests tool
calls, leaving a `tool` message with no preceding `tool_calls` message -- a
sequence providers reject. A message now counts when it has content, tool
calls, or a tool_call_id.

And `files` were unioned from every message onto the current request while
history messages kept their own, so prior attachments were sent twice. Only the
current request's attachments travel in `inputs["files"]` now.

Found by Cursor and CodeRabbit on #7065.

* fix(agents): treat an attachment as message payload

_carries_payload counted text, tool calls and tool results, but not files.
A final message whose only payload was an attachment was filtered out, so the
previous message became this turn request and the attachment never reached
inputs["files"].

Found independently by Cursor and CodeRabbit on #7065.

* fix(agents): promote the last user message, not the last message

build_agent_context() appends an agent private thread after the current user
turn, so on a later turn the trailing message is an assistant scratch. That
scratch became Current Task while the real question was demoted to history --
reproduced: the request came back as "internal note: checked warehouse".

The request is now the last user message, with everything else kept as history
in order; with no user message the last one stands in, which is what a
single-message caller has always got. Documented on kickoff and kickoff_async.

The old cross-check against LiteAgent only compared roles on a fixture already
ending in a user message, so it could not catch this. It now asserts what holds
for both -- nothing dropped, nothing duplicated -- since Agent has a task slot
in its prompt and LiteAgent does not.

Reported by Vidit-Ostwal on #7065.

* test(agents): cover history placement on the deprecated executor too

`CrewAgentExecutor` carries its own `_setup_messages`, and `Agent.kickoff`
builds an `AgentExecutor` unconditionally, so nothing reached the twin's
two `_append_history` sites. This drives that executor directly with the
real `SystemPromptResult` / `StandardPromptResult` shapes, so both its
branches are pinned.

Verified by mutation: removing either `_append_history` call in either
executor now fails the matching test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

* fix(agents): keep turns that follow the request after it

`_prepare_kickoff` packed every non-request message into one `history`
list, and both executors spliced that list before the current user
prompt. A conversation ending on a tool result therefore reached the
provider as `assistant tool_calls -> tool -> user`, hoisting the tool
pair above the question it answers. `LiteAgent` sends the same list as
`user -> assistant -> tool`.

Splits the carried messages at the request instead: what came before
stays `history`, what came after travels as `trailing` and is appended
after the user prompt. Promoting the last user message to `{input}` is
unchanged.

`test_a_tool_call_sequence_survives` missed this because it ends on a
follow-up user line, so the tool pair was already before the request.

Reported by lorenzejay, who also confirmed gpt-4o-mini and gpt-5.6-sol
accept the reordered payload -- an order bug, not a provider 400.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

* test(agents): assert the ordering through kickoff_async too

`kickoff_async` shares `_prepare_kickoff`, and the async executor entry
points reach the same `_setup_messages`, but sharing a code path is not
the same as covering it. Pins the tool-result ordering through the async
path, and lifts the tool conversation to a module-level fixture so the
sync and async assertions cannot drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

* docs(agents): state where a kickoff conversation's turns land

`concepts/agents.mdx` documents the multi-message form but not which
message becomes the request or what happens to the turns around it --
the contract this branch changed. Corrects the `kickoff` /
`kickoff_async` docstrings to match.

en and ko only: the ar and pt-BR pages do not carry the "Multiple
Messages" section at all, which is a pre-existing translation gap rather
than one this change introduces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VwxiogL9mQg9q8cx4qLfYJ

* feat(flow): let a declarative agent action receive the conversation (#7066)

* feat(flow): let a declarative agent action receive the conversation

A chat handler could only hand its agent a single string, so a declarative
conversational flow's `agent` action never saw prior turns. Both ends blocked
it: `AgentDefinition.input` was `str` with a validator rejecting anything else,
and `AgentAction.run` raised "agent input must render to a string" once a CEL
template rendered to a list.

`input` now takes a string or a list of messages, and the action normalizes
rather than rejecting. A whole-string `${...}` template keeps its evaluated
type, so `state.messages.map(m, {'role': m.role, 'content': m.content})`
renders the exact shape the agent wants -- no new CEL function needed.

Serialized messages carry `name: None` and `metadata` that the agent event
schema rejects, and `message_to_llm_dict` only drops `None` for a model input,
not the plain dicts a CEL render produces. The normalizer drops them, keeping
`content: None` since that is a valid message shape.

Declarative crews are unaffected: they use `CrewAgentDefinition`, which has no
`input` field at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(flows): pin content=None survival through agent input normalization

A filter-all-None change would silently drop the assistant turn that only
requests tool calls, and no test covered that key.

Found by CodeRabbit on #7066.

* test(flows): cover the agent action through kickoff, not the helper

The existing tests called _normalize_agent_input directly, so nothing pinned
that Expression.render_template -> normalization -> Agent.kickoff_async keeps a
message list intact. Removing the normalization call now fails this test.

Found by CodeRabbit on #7066.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-08-24 21:09:36 +00:00
João Moura
500ebc7a68 feat(flow): let a declaration name the router's response format (#7063)
* feat(flow): let a declaration name the router's response format

`conversational.router.response_format` was typed `Any` and dropped with a
warning, because `_router_response_format` hands its value straight to
`llm.call(response_format=...)`, which needs a real class. So the router always
used its synthesized fallback: `intent: str` with the route labels only in a
field description.

The field now takes the same `{"python": "module.path.Class"}` shape a crew
agent's `response_format` uses, resolved through the same
`_resolve_model_class`. That brings the project-root containment with it -- a
declaration cannot reach outside the project to import code -- and gives the
router a `Literal[...]` of the real route labels instead of a bare string.

The DSL projection now emits that shape too, so a live class on a Python flow
round-trips as `{"python": ...}` rather than an opaque `{"ref": ...}` that
nothing could reload.

A bare `module:qualname` ref is now a load-time validation error instead of
being silently discarded; the test that pinned the old drop-with-warning
behavior is updated to assert that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): do not project a response format that cannot be reloaded

`_python_reference` emitted a dotted path for any class, including two that
cannot be imported back: a non-Pydantic class, and one defined inside a
function, whose `__qualname__` carries `<locals>`. The definition then held a
ref that only failed when something tried to resolve it.

Both are dropped at projection time with a warning naming why, so a reload
falls back to the synthesized response format. The live class still drives the
running flow; only the projection omits it.

Found by CodeRabbit on #7063.

* test(flows): assert the response_format omission warnings

The projection tests only checked for None, so removing the warning that tells
an author their response_format was dropped would still pass.

Found by CodeRabbit on #7063.

* fix(flows): only project a response_format ref that imports back

The check rejected <locals> classes but still emitted a path for a nested one.
The loader splits a ref on its last dot, so module.Outer.Route resolves
module.Outer as a module that does not exist - proven: reload raised
JSONProjectError. A create_model() class held only in a local is unreachable
the same way.

Projection now confirms module.qualname resolves back to the class, against the
already-imported module so it never triggers an import.

Found by CodeRabbit on #7063.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 13:53:15 -07:00
Vidit Ostwal
d0e9208627 [OSS-129] Map GPT-5.6 family to the official 1.05M context window (#7012)
* fix(core): map GPT-5.6 to the official 1.05M context window

LiteLLM fallback treated Sol, Terra, Luna, and the gpt-5.6 alias as unknown and used the 8k default.

* fix(openai): give GPT-5.6 its own 1.05M window

Native lookup matched gpt-5 first, so Sol, Terra, and Luna inherited 1,047,576. Longest-prefix matching keeps gpt-5 and gpt-5.4-mini on their own sizes.

* fix(azure): map GPT-5.6 deployments to the official 1.05M window

Azure had no gpt-5 / gpt-5.6 entry, so Sol, Terra, and Luna fell back to 8k.

* feat(cli): list GPT-5.6 Sol, Terra, and Luna in curated catalogs

The family is generally available; keep gpt-5.5 as the offline default.

* refactor: keep context-window tables in longest-prefix order

Drop the runtime sort and document that new keys must be inserted longest-first so startswith matching stays correct.

* fix(core): resolve prefixed LiteLLM models to the GPT-5.6 window

openai/gpt-5.6-luna kept its provider prefix on self.model, so startswith matching missed the 1.05M mapping. Strip recognized prefixes for lookup and leave unknown ones intact.
2026-08-24 19:03:48 +00:00
Vidit Ostwal
2746bb88b7 [OSS-130] Align README setup with current docs (#7036)
Update GitHub and PyPI READMEs to the JSON-first CLI path so setup matches docs.crewai.com.

Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
2026-08-24 11:49:19 -07:00
Lorenze Jay
090633737c feat(flows): enhance conversational flow documentation and APIs (#7104)
- Updated the description to clarify the use of `handle_turn` and structured streaming in multi-turn chat applications.
- Added a warning about the experimental nature of the conversational features.
- Improved the overview section to include structured streaming and refined the explanation of session handling.
- Enhanced the API documentation for `handle_turn`, `stream_turn`, and `chat` methods, emphasizing their roles in conversational flows.
- Clarified the turn lifecycle and the handling of user messages within the flow.
- Updated examples to reflect changes in message handling and session tracing.
- Ensured consistency across language versions in the documentation.
2026-08-24 11:26:31 -07:00
João Moura
f68fd9e850 feat(flow): let a chat flow declare its own state shape (#7061)
* feat(flow): let a chat flow declare its own state shape

A conversational declaration could only use `state: {type: pydantic, ref: ...}`
pointing at a `ConversationState` subclass. Every other shape loaded clean and
then died on the first turn -- inline `json_schema` and a non-subclass ref with
`AttributeError: 'StateWithId' object has no attribute 'messages'`, and
`type: dict` with `AttributeError: 'dict' object has no attribute 'id'`.

`Flow._compose_extension_state_model` is a new runtime extension seam -- the
seventh alongside the existing six -- applied to the model built from `state:`
before the engine wraps it for `id`. The conversational mixin uses it to add
the chat fields to whatever the declaration asked for, so declared fields and
defaults survive; a model that already extends `ConversationState` is returned
untouched, so today's supported shape is a no-op.

`dict` and `unknown` state cannot carry those fields at all, so the default
extension state supplies the real shape (seeded from the declared defaults
where they fit) rather than forbidding it. Raising instead would break
construction, and `Flow[dict]` with `conversational = True` constructs today --
`crewai flow plot` and definition-only consumers would stop working.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): keep declared defaults and cover an unbuildable state model

Two review follow-ups on the declared-state work.

A `dict` state's defaults are arbitrary keys, and the fallback kept only the
ones matching `ConversationState`, so `{"type": "dict", "default": {"topic":
"ai"}}` lost `topic` before the first turn and an action reading `state.topic`
would fail. They are carried as extras now.

And a declared `pydantic`/`json_schema` state whose model cannot be built --
a bad ref, an invalid schema -- fell through to a plain dict with none of the
chat fields, so the turn died on `state.id` instead. The engine now re-asks the
extension in that case, as if nothing had been declared.

Found by Cursor and CodeRabbit on #7061.

* refactor(flows): drop the unreachable extension-state fallback

The _initial_state_t branch sat after an unconditional return. The
state_definition is None case and _conversation_state_with_defaults now cover
every path that used to reach it.

Found by Cursor on #7061.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 22:09:46 +05:30
João Moura
9e9a8577be feat(events): report project creation with the id minted for it (#7074)
* feat(telemetry): report project creation with the id minted for it

Acquisition was only observable from a project's first run. That misses every
project created and never run, and dates the rest to the wrong day.

All three scaffolding paths already mint a project_id into the new
pyproject.toml. None of them reported it, and two of them - create_crew and
create_json_crew, which is the default `crewai create crew` path - emitted no
telemetry at all.

`Project Created` carries the kind (crew, json_crew, flow) and the id that was
just minted, and is emitted after the mint so it can carry it.

The attribute is `created_project_id`, not `project_id`, because those are two
different things. CommonAttributesSpanProcessor stamps `project_id` on every
span from get_project_id(), which reads the current working directory and is
cached for the life of the process - during `crewai create` that describes the
directory the command was run from, not the project being created. Reusing the
name would have given one column two meanings depending on span type.

Nothing is emitted for `create_crew(parent_folder=...)`: that adds a crew to a
project which already exists, mints no id, and is not an acquisition.

The existing `Flow Creation` span is left exactly as it is. Note for whoever
reads it: it is emitted from two places with two different meanings - CLI
scaffolding (create_flow.py) and runtime flow construction (event_listener.py on
FlowCreatedEvent) - so it cannot separate acquisition from usage. Not changed
here because it is a live series and renaming it would break continuity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(telemetry): stop the creation-span tests exporting to the real collector

The recorded_spans fixture has to enable telemetry for its assertions to mean
anything - a disabled Telemetry never builds self.provider, so every assertion
would pass vacuously against a non-recording span. But enabling it is exactly
what makes __init__ wire a BatchSpanProcessor around the real
SafeOTLPSpanExporter, pointed at the production collector.

Measured, with a spy on both SafeOTLPSpanExporter classes reporting at
interpreter exit: before this change the file made 3 real export calls, handed 3
synthetic Project Created spans with invented created_project_id values to the
production exporter, and completed 3 connects to the collector. After: 0, 0, 0.

Sampling at pytest_sessionfinish reports 0 either way and is how this was missed
- BatchSpanProcessor flushes on a background timer, and with no
provider.shutdown() the flush lands in the atexit handler, which runs after
sessionfinish.

--block-network does not prevent it: it is function-scoped and only swaps
socket.connect, which a background batch thread outlives.

Follows telemetry_with_exporter in tests/telemetry/test_tracer_isolation.py:
_NullExporter swapped in before construction, _register_shutdown_handlers
suppressed so no atexit hook is left behind, and provider.shutdown() in finally.
Patch target is crewai_core because that is where this Telemetry comes from.

Also disambiguates the docs rows: the minted ID belongs to the new project, not
to the directory the command ran in, and the two can differ. Reworded in all four
languages rather than renaming the token to created_project_id - this table
documents data, never span-attribute keys (kind and crewai_version on the same
row are unnamed), and `project_id` is already the page's name for the
pyproject.toml key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs(ar): use tanwin fath on the letter, not on the alif

مشروعًا / جديدًا rather than مشروعاً / جديداً, in the row added by this PR.

Both forms appear in docs/edge/ar (3 each), so this is not a house convention
being broken either way; the corrected form is the more standard one and the text
is mine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs: stop the creation row implying the span's project_id holds the minted value

CodeRabbit re-raised this as Major after I rejected the first version, and it was
right to. My rejection argued the page never names wire keys, so introducing
created_project_id would be its only one. That part still holds -- grep finds no
attribute key anywhere in the four files. But it was the wrong conclusion: the row
still used the token `project_id` for a value the span does NOT carry under that
key, while the same span's real project_id holds the cwd-derived value. The row
also already exposes literal wire values (`crew`, `json_crew`, `flow` are the
actual kind values), so "this page has no wire detail" was overstated.

Dropping the token resolves the ambiguity without adding the page's only key
name: the row now says "the project ID minted for that new project", and names
`project_id` only to say the minted value is recorded separately from it.

All four languages. No docs/v*/ touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs: use an em dash in the creation row, matching the rest of the page

The two ASCII `--` occurrences in these files were both mine, introduced by this
PR: the page otherwise uses em dashes throughout (en 4, ar 4, ko 2, pt-BR 4).

CodeRabbit flagged pt-BR; the same slip was in en, so both are fixed. Now zero
ASCII `--` across all four language files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-23 15:59:56 -03:00
João Moura
f4731f5025 feat(events): record whether a run had inputs, without recording the inputs (#7072)
* feat(telemetry): record whether a run had inputs, without recording the inputs

The `crew_inputs` payload is gated behind `share_crew` and stays that way, so the
only way to tell a parameterised run from an unparameterised one was to read a
gated key: it is present on roughly 0.02% of spans, all of them opt-in sharers.
That is a measurement of people who opted into sharing, not of users.

`crew_inputs_present` carries just the answer -- "true"/"false" -- on the
already-ungated `Crew Created` span. The payload stays inside the `share_crew`
branch, so nothing new about the contents of anyone's inputs is collected.

A string, for the reason `crew_memory` is a string, and the encoding matters
more here because the majority case is the empty one. Measured over a single day
(312,424,709 spans): `vInt64='0'` occurs 0 times and `vBool='false'` occurs 0
times, while `vStr='0'` does occur. proto3 omits the zero value for ints as well
as bools, so an integer key count would have silently dropped every
unparameterised run -- and among sharers, 54.46% of runs pass `{}`.

`{}` and `None` are both "false": an empty dict parameterises nothing, so
truthiness is the question being asked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(telemetry): assert input keys are absent too, not only input values

The gating test checked only the input value. A regression that emitted the input
keys - json.dumps(sorted(inputs)) or similar - would have passed it, and key
names are user data as much as values are.

Verified by injecting exactly that regression: the new assertion fails on it and
passes once reverted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 16:10:02 +05:30
João Moura
113e572e05 fix(deps): raise the pip floor to 26.2 for PYSEC-2026-3721 (#7076)
pip-audit started failing on every open PR. The advisory is against pip itself:
PYSEC-2026-3721 / CVE-2026-13346, which OSV records as affecting pip up to but
not including 26.2. The floor was already pinned at >=26.1.2, so the previously
patched version became the vulnerable one.

Not caused by any open PR. Reproduced on tag 1.15.17 itself (`b3ab193c3`), which
resolves pip 26.1.2: `uv run pip-audit` with CI's exact arguments reports
"Found 1 known vulnerability" there with no branch changes at all. That is why
this is its own PR rather than a fix inside whichever PR happened to run first.

Raising the floor rather than adding --ignore-vuln, since a patched release
exists: 26.2 fixes it and 26.2.1 is current. The trailing comment follows the
convention already used for setuptools>=83.0.0.

The uv.lock change is deliberately hand-scoped to pip's four lines. Running
`uv lock` -- with either uv 0.11.12 or 0.11.15 -- also re-expands environment
markers for numpy, humanfriendly, grpcio, mcp and a dozen nvidia-* packages,
because the committed lock was produced by a uv that simplifies markers
differently from any version available here. Those rewrites change CUDA and
platform resolution and have no business riding along in a security fix. The
four lines applied here are exactly the ones uv itself produced for pip.

Verified: `uv lock --check` passes, so the lock is consistent with pyproject and
needs no regeneration; pip resolves to 26.2.1; `uv run pip-audit` with CI's
arguments reports "No known vulnerabilities found, 1 ignored"; crewai and
crewai_core still import.


Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
2026-08-21 16:02:32 +05:30
João Moura
8e6c23430f fix(flow): emit the flow lifecycle on a suppressed resume (#7071)
* fix(flow): a resumed flow must emit flow_started, not only flow_finished

_resume_async_body gated FlowStartedEvent behind suppress_flow_events while the
matching FlowFinishedEvent a few hundred lines below stayed ungated. A suppressed
resume therefore emitted an unpaired finish: a flow that reported finishing
without ever having started. That is worse than a missing row - it breaks every
started/finished pairing and any duration or funnel built on it, and it removed
the resumed leg from telemetry entirely.

suppress_flow_events is also the wrong gate for emission. It asks for console
quiet: _flow_origin in events/event_listener.py says so explicitly and notes it
"can legitimately be set on a caller's own flow", and the listener already
honours it at each point where it prints. So a user who set it on their own flow
for quiet output silently lost their resumed runs from telemetry.

The emit is now unconditional, matching both the kickoff path - which never gated
it - and the FlowFinishedEvent it pairs with. The method-execution gates in this
function are left alone: _execute_method gates the same events on the same flag,
so those are symmetric and intended.

Internal flows that set this flag (agent_executor, the memory recall/encoding
flows) will now emit a started event when resumed. That is the point, and the
is_crewai_internal marker already keeps them out of user-facing flow metrics -
a distinction _flow_origin draws precisely because this flag cannot carry it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* fix(flow): emit the whole lifecycle on a suppressed resume, not just the start

The first commit on this branch ungated FlowStartedEvent on the resume path
while FlowFinishedEvent stayed gated, which left a suppressed resume emitting a
start with no terminal event. CodeRabbit and cursor both caught it.

The premise that commit was written against was wrong: origin/main gated the
started event and the finished event, so it emitted neither and was symmetric.
It was the detection that was broken, not the code -- a fixed-line lookback for
the enclosing condition missed the multi-line `if (not self.suppress_flow_events
and not self._should_defer_trace_finalization()):` guarding the finish.

The defect is therefore not an unpaired event on main but a silent one: a
resumed run with suppress_flow_events set emits no lifecycle events at all, so
it never reaches a listener or the trace exporter and the run is invisible
downstream. kickoff_async emits them either way and lets listeners filter, and
suppress_flow_events asks for console quiet rather than for telemetry to be
dropped, so resume now matches kickoff.

_should_defer_trace_finalization() still withholds the finish, which is a real
reason: finalize_session_traces() emits it later instead.

respect_suppression is deleted rather than left defaulting to False -- the
resume call site was its only caller, so nothing passes True any more.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 15:51:20 +05:30
João Moura
6e714d6cad feat(flow): accept crew-style LLM config in a conversational declaration (#7062)
* feat(flow): accept crew-style LLM config in a conversational declaration

`conversational.llm` / `intent_llm` / `answer_from_history_llm` and
`router.llm` only accepted a model-id string, so a declaration could not set
`max_tokens`, `temperature` or anything else a declarative crew's agent can.

`_coerce_llm` now delegates to `crewai.utilities.llm_utils.create_llm` -- the
same helper the crew/agent declaration layer resolves through -- so the shapes
match: a model-id string, a config mapping, an `LLMDefinition`, or a live
`LLM`/`BaseLLM` passed straight through.

It keeps one thing `create_llm` does not: `create_llm(1234)` takes the int as a
model name and returns an LLM that only fails later with a provider error. A
declaration is hand-written, so a non-string, non-mapping value raises now
instead. A mapping missing `model` keeps `create_llm`'s own message.

The contract fields stay permissively typed and gain descriptions naming the
accepted shapes. Tightening them to `str | LLMDefinition` would break the DSL
projection: a live custom `BaseLLM` whose config dump lacks a `model` key
degrades to a `{"ref": ...}` mapping, which such a type would reject -- turning
`flow_definition()` on an existing Python flow into a validation error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(flow): resolve declared LLM mappings for intent classification

`intent_llm` and `answer_from_history_llm` were documented as taking the same
shapes as `llm`, but both route through `_collapse_to_outcome`, whose own
coercion accepts only `str | BaseLLM`. A declared config mapping reached it
unchanged and raised "Invalid llm type: <class 'dict'>" mid-turn -- so the
documentation promised something that failed.

Also fixes the field descriptions. The previous commit's `llm` description
landed on `FlowConversationalRouterDefinition.llm` rather than
`FlowConversationalDefinition.llm`, because both fields are literally
`llm: Any = None` and the first match won. All four now describe their own
field and name every accepted shape, including `LLMDefinition` and a live
instance.

Test changes: adds an `LLMDefinition` resolution case, and the declared-mapping
turn test now patches `create_llm` to prove the mapping reaches it instead of
swapping the config out beforehand, which proved nothing.

Found by Cursor and CodeRabbit on #7062.

* docs(flows): name the full router LLM precedence; pin the coercion path

The conversational llm description skipped intent_llm in the router fallback
order (router.llm, then intent_llm, then llm), and the intent_llm test replaced
the declared mapping with a scripted LLM before the turn, so it never exercised
the coercion. Patch create_llm instead: removing the coercion now fails the
test with "Invalid llm type: <class 'dict'>".

Found by CodeRabbit on #7062.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 14:47:37 +05:30
João Moura
4718b190d1 fix(cli): open the conversational TUI for a declarative chat flow (#7060)
* fix(cli): open the conversational TUI for a declarative chat flow

`crewai run` refused a declarative conversational flow and told the user to
drive it from Python. That was wrong: the conversational TUI already exists and
already does this job. `CrewRunApp(conversational=True)` renders a chat pane and
drives `handle_turn` per message (crew_run_tui.py:833-935), and
`kickoff_flow._run_conversational_flow_tui` launches it for a Python
conversational Flow.

A declaration-built flow satisfies everything that TUI needs -- `handle_turn`,
a settable `defer_trace_finalization`, and `finalize_session_traces()` -- so it
now routes there instead of exiting.

A chat loop still needs a terminal. A headless run (`is_interactive()` false,
which folds in CREWAI_DMN) says what it would have needed rather than kicking
off a single turn and presenting that as the whole conversation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): do not send a human-feedback chat flow to the Textual TUI

A declaration can carry both a `conversational:` block and a method with
`human_feedback:` -- verified, both predicates return True on the same flow.
Routing it to the chat TUI hangs: the runtime collects feedback with a blocking
`input()` (flow/runtime/__init__.py:3719) that Textual cannot service, so the
prompt is never shown. The STEPS TUI already declines these for exactly this
reason. Such a flow now falls back to the terminal REPL, which can prompt.

Also updates the guide in en/ar/ko/pt-BR: it still said `crewai run` has no
chat loop and exits, which is now the opposite of what the CLI does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): reject --inputs on a conversational flow instead of dropping it

The conversational branch returns before `_resolve_flow_inputs`, and the TUI
calls `handle_turn(message)` -- which owns the kickoff inputs, passing
`{"id": session_id}` itself. So any `--inputs` value was silently discarded and
the conversation ran as if it had been applied. It now errors, and says that
resuming a session by id is not wired up yet rather than implying it worked.

Also corrects the Arabic guide: `مُوجّه محجوز` reads as "reserved router", not
the blocking prompt it describes.

Both found by CodeRabbit on #7060.

* fix(cli): document the conversational routing exceptions

Three review follow-ups:

- The Arabic guide read خدمته (masculine) against the feminine
  مُطالبة introduced by the last fix.
- Both docstrings described a routing path that now has exceptions: a
  conversational declaration rejects --inputs and skips state-schema
  resolution, and a human-feedback one uses the terminal REPL.
- The --inputs rejection test accepted SystemExit(0); it now pins code 1.

Found by CodeRabbit on #7060.

* fix(cli): reject --inputs on a chat flow even when it parses empty

parse_inputs_json returns {} both when the option is absent and when the user
passes --inputs "{}", so the falsy check started the TUI for the second case
while the docs said it was unsupported. The conversational path now takes
whether the option was supplied, not what it parsed to.

Documents the restriction in en, ar, ko and pt-BR.

Found by CodeRabbit on #7060.

* test(cli): pin the headless conversational exit status

pytest.raises(SystemExit) also accepts SystemExit(0), so the error path could
regress to a successful exit unnoticed.

Found by CodeRabbit on #7060.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 14:36:40 +05:30
João Moura
456c67d7c2 fix(telemetry): record crew_memory as a string, not a bool (#7064)
This pipeline cannot carry a false boolean. Measured across 218,400,577 spans,
not one carries vBool=false - proto3 omits the bool zero value, so false is
never serialized and arrives as the key simply being absent. "Memory disabled"
was therefore structurally unrepresentable, and presence had to stand in for the
value, which is why crew_memory read 1 for 99.8% of crews against a field that
defaults to False.

The fix is the convention this file already documents and applies to `resumed`
and `conversational`; crew_memory is the attribute those comments name as the
outstanding case. It was the only remaining CrewAI-emitted boolean attribute -
checked empirically: every other attribute appearing in vBool comes from
third-party instrumentation.

Truthiness rather than `is True`, per the decision that memory counts as enabled
when set by any means: a Memory, MemoryScope or MemorySlice instance is enabled
just as much as `memory=True`. None of those classes defines __bool__ or __len__,
so an instance is always truthy.

Tests cover all four inputs - True, False, None and an instance - and reuse the
existing guard that no attribute is ever passed as a bare boolean. Verified they
fail against the unpatched emitter.


Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 21:59:35 +00:00
Dat Daryl Ngo
89c04a0a08 Clarify Arize Phoenix observability docs (#7069)
Co-authored-by: Dat Ngo <datngo@Mac.digi.box>
2026-08-20 16:08:46 +00:00
João Moura
0c2bcb510c feat(cli): backfill project_id from every user-invoked project command (#7057)
* feat(cli): backfill project_id from every user-invoked project command

`crewai run` has always backfilled: a project declaring [tool.crewai] without a
project_id gets one minted the first time it runs. No other command did, so a
project driven entirely through `crewai test`, `crewai deploy` or
`crewai traces enable` never acquired an id and every one of its runs stayed
unattributable - which is the denominator problem, not a cosmetic gap.

Adds the same call to train, replay, test, login, deploy create, deploy push,
flow add-crew, enterprise configure and traces enable. Every one is an action the
user explicitly invoked, which is the condition run_crew already relies on, so this
is the existing principle applied evenly rather than a new policy. It is still never
called from the SDK during kickoff, and get_or_create_project_id still refuses to
create the [tool.crewai] table, so an unrelated directory is never rewritten.

`crewai flow kickoff` is deliberately untouched: it delegates to run_crew and
already inherits the backfill. A test pins that so the delegation is not
accidentally duplicated. There is no `crewai evaluate` command - `crewai test` is
that path.

The call is the first statement in each command so a command that later fails still
leaves the project with an id. The tests patch the backfill to raise, which proves
the call happened and guarantees nothing after it runs, so no test touches user
settings, spawns a subprocess or reaches the network. Verified they fail against the
unpatched module: 9 command tests fail, the 2 guard tests still pass.

Tests live under lib/crewai/tests/cli/ because that is the path the required CI job
runs; nothing runs lib/cli/tests/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): assert the backfill at runtime instead of reading source text

Addresses CodeRabbit and github-code-quality on #7057. The two guard tests
grepped module source for a call string, which asserts on formatting rather than
behavior: a reformat would break them and a real regression could slip past.

They now invoke the commands in an isolated project and assert on observed calls.
The flow-kickoff test patches the two distinct import sites separately and asserts
run_crew's is called exactly once while cli's is not called at all, which is what
makes 'delegates' and 'duplicates' distinguishable at runtime rather than by
reading the file.

Verified both catch what they claim: injecting a duplicate call into flow_run
fails the delegation test, and removing run_crew's own call fails the run test.

This also drops the module-level 'import crewai_cli.cli as cli_module' that mixed
import styles with the existing 'from crewai_cli.cli import crewai', which is the
code-quality finding - the rewrite removes the need for it entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): make the exact-once assertion observable

Addresses CodeRabbit on #7057, and the finding was correct: with
side_effect=_BackfillReached the mock raised on first use, so call_count == 1 was
guaranteed by the mock rather than by the code. A second backfill call inside the
same run_crew execution could never have been observed.

Both backfill mocks now return normally and execution is stopped at the first call
AFTER the backfill (configured_project_json_crew), so the recorded count is real.

Verified the difference this makes: injecting a duplicate get_or_create_project_id()
INSIDE run_crew now fails both tests, which the previous version could not detect at
all. The flow_run duplicate case is still caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): assert flow kickoff reaches the post-backfill boundary

Addresses CodeRabbit on #7057, and the finding was right: the flow-kickoff test
discarded the runner.invoke() result, so if the path returned or raised after one
backfill call but before configured_project_json_crew, both call-count assertions
would still have passed - for the wrong reason.

test_run_still_backfills already asserted the boundary; this makes the pair
consistent.

Verified it earns its place: injecting an early return after the backfill and
before the boundary now fails both tests, and previously would have failed
neither.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* test(cli): pin that the backfill precedes command-specific work

Addresses CodeRabbit on #7057. The finding is valid: the parametrized test proves
the backfill is reached, not that nothing ran before it, so its assertion message
claimed more than the test established.

Fixed in two parts rather than as proposed. The message now states what the test
actually proves, and a new test pins the ordering on login: , whose first action
goes through a module-level name that can be patched without reaching into the
command.

Deliberately not parameterized across all nine commands, which is what the finding
suggested: that would mean naming each command's current first action, and those
change as commands evolve, so the suite would end up tracking their internals
rather than this ordering property. One representative command establishes it, and
placement is visible in the diff for the rest.

Verified it catches the regression: swapping login's first two statements so its
own work runs before the backfill fails the new test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 23:14:09 -07:00
João Moura
7c72d57b73 fix(events): always emit project_id so absent and empty stay distinct (#7056)
* fix(telemetry): always emit project_id so absent and empty stay distinct

common_span_attributes() stamped project_id onto every span only when the project
declared one, and omitted the key otherwise. That makes two different situations
indistinguishable downstream: a client too old to report a project id at all, and
a current client whose project simply declares none.

The consequence is not cosmetic. The share of clients that COULD have reported an
id is the denominator of every attribution rate, and with both cases collapsed into
"key absent" that denominator cannot be computed at all - it can only be inferred
from a version floor, which is fragile and silently wrong for any client that
backports or pins.

The key is now always present and is the empty string when the project declares
none. It still never invents an identity: get_project_id() remains read-only and
minting stays with the CLI commands a user explicitly invoked.

Two existing tests asserted the old contract and are updated rather than deleted,
one of them renamed because its name described the behaviour that changed. A third
test is added pinning the distinction itself. The test asserting that a foreign
application's spans are never annotated is unaffected and still passes: this
changes what our processor stamps, not where it is attached.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

* docs(telemetry): note that a failed project_id lookup also yields empty

Addresses CodeRabbit on #7056. The docstring and inline comment described the
empty string only as an undeclared project, but the except branch sets
project_id to None and so lands on the same empty value. Both are deliberately
indistinguishable - neither yields an id - and saying so matters to anyone
debugging an empty value, since an unreadable pyproject.toml looks identical to
a project that simply declares nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RfV2uMqWRcdfufMvtdCVoN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 01:10:23 -03:00
João Moura
b3ab193c31 [docs-freeze] docs: snapshot and changelog for v1.15.17 (#7055) 2026-08-20 00:27:10 +00:00
João Moura
5ddf62ae7b feat: bump versions to 1.15.17 (#7054) 2026-08-20 00:16:11 +00:00
1748 changed files with 359826 additions and 4300 deletions

View File

@@ -103,7 +103,8 @@ 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
- 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.
## Testing

23
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,23 @@
## 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". -->

121
.github/workflows/ftc-require-issue.yml vendored Normal file
View File

@@ -0,0 +1,121 @@
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

View File

@@ -86,11 +86,27 @@ jobs:
--skip-editable
--format json
--output pip-audit-report.json
# chromadb <=1.5.9 (CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c): pre-auth RCE in
# the Python HTTP server. Fix merged upstream in chroma-core/chroma#7237
# but no PyPI release beyond 1.5.9 yet. CrewAI only uses PersistentClient
# (embedded), not the HTTP server.
# 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
# nltk <=3.10.3: GHSA-8mgp-746c-j5xp (CVE-2026-81726): model-artifact
# APIs bypass pathsec and read/write outside allowed roots. No patched
# PyPI release yet (fixes are on nltk develop only). Transitive via
# crewai-tools[xml] -> unstructured; CrewAI does not call those APIs.
# TODO: drop this ignore when bumping nltk past 3.10.3 to a patched
# release; keep the ignore list in sync with .pre-commit-config.yaml.
--ignore-vuln GHSA-8mgp-746c-j5xp
)
uv run pip-audit "${pip_audit_args[@]}"
continue-on-error: true

View File

@@ -29,6 +29,7 @@ repos:
- id: pip-audit
name: pip-audit
# Keep this ignore list in sync with .github/workflows/vulnerability-scan.yml.
# TODO: drop --ignore-vuln GHSA-8mgp-746c-j5xp when bumping nltk past 3.10.3.
entry: >-
bash -c 'source .venv/bin/activate && uv run pip-audit --skip-editable
--ignore-vuln PYSEC-2024-277
@@ -56,7 +57,11 @@ 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-f4j7-r4q5-qw2c
--ignore-vuln GHSA-2wm9-hf6c-p5cr
--ignore-vuln GHSA-36p7-vc44-83pf
--ignore-vuln GHSA-xph7-9rjv-w5fr
--ignore-vuln GHSA-8mgp-746c-j5xp' --
language: system
pass_filenames: false
stages: [pre-push, manual]

View File

@@ -14,6 +14,23 @@ 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

323
README.md
View File

@@ -91,7 +91,7 @@ intelligent automations.
- [Learning Resources](#learning-resources)
- [Understanding Flows and Crews](#understanding-flows-and-crews)
- [Installation](#1-installation)
- [Setting Up Your Crew](#2-setting-up-your-crew-with-the-yaml-configuration)
- [Setting Up Your Crew](#2-setting-up-your-crew)
- [Running Your Crew](#3-running-your-crew)
- [Key Features](#key-features)
- [Examples](#examples)
@@ -121,7 +121,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.py` / `main.py` |
| `getting-started` | Scaffolding new projects, choosing between `LLM.call()` / `Agent` / `Crew` / `Flow`, wiring `crew.jsonc` / `main.py` |
| `design-agent` | Configuring agents — role, goal, backstory, tools, LLMs, memory, guardrails |
| `design-task` | Writing task descriptions, dependencies, structured output (`output_pydantic`, `output_json`), human review |
| `ask-docs` | Querying the live [CrewAI docs MCP server](https://docs.crewai.com/mcp) for up-to-date API details |
@@ -189,47 +189,76 @@ 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:
To get started with CrewAI, follow these simple steps. The full walkthrough lives in the [installation guide](https://docs.crewai.com/en/installation).
### 1. Installation
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.
CrewAI requires `Python >=3.10 and <3.14`. Check your version with:
First, install CrewAI:
```shell
uv pip install crewai
```bash
python3 --version
```
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:
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:**
```shell
uv pip install 'crewai[tools]'
curl -LsSf https://astral.sh/uv/install.sh | sh
```
The command above installs the basic package and also adds extra components which require more dependencies to function.
If your system doesn't have `curl`, you can use `wget`:
### Troubleshooting Dependencies
```shell
wget -qO- https://astral.sh/uv/install.sh | sh
```
If you encounter issues during installation or usage, here are some common solutions:
**Windows:**
#### Common Issues
```shell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
1. **ModuleNotFoundError: No module named 'tiktoken'**
If you run into any issues, refer to [UV's installation guide](https://docs.astral.sh/uv/getting-started/installation/).
- Install tiktoken explicitly: `uv pip install 'crewai[embeddings]'`
- If using embedchain or other tools: `uv pip install 'crewai[tools]'`
Then install the CrewAI CLI:
2. **Failed building wheel for tiktoken**
```shell
uv tool install crewai
```
- 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`
If you encounter a `PATH` warning, run:
### 2. Setting Up Your Crew with the YAML Configuration
```shell
uv tool update-shell
```
To create a new CrewAI project, run the following CLI (Command Line Interface) command:
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++*.
Verify the install:
```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.
```shell
crewai create crew <project_name>
@@ -240,200 +269,126 @@ 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
├── .env
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
├── skills/
└── tools/
```
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.
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.
#### To customize your project, you can:
- 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.
- 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/`.
- 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
```
Modify the files as needed to fit your use case:
Then edit the generated files:
**agents.yaml**
**agents/researcher.jsonc**
```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.
```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
}
}
```
**tasks.yaml**
**agents/reporting_analyst.jsonc**
````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 2026.
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 fledged report with the main 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,
)
```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
}
}
```
**main.py**
**crew.jsonc**
```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'
```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
}
LatestAiDevelopmentCrew().crew().kickoff(inputs=inputs)
],
"process": "sequential",
"verbose": true,
"inputs": {
"topic": "AI Agents"
}
}
```
### 3. Running Your Crew
Before running your crew, make sure you have the following keys set as environment variables in your `.env` file:
Before running your crew, set the required keys in your `.env` file:
- 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`
- 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`
Lock the dependencies and install them by using the CLI command but first, navigate to your project directory:
Then install dependencies and run from the project directory:
```shell
cd my_project
crewai install (Optional)
```
To run your crew, execute the following command in the root of your project:
```bash
crewai install
crewai run
```
or
If you need additional packages, use `uv add <package-name>`.
```bash
python src/my_project/main.py
```
If an error happens due to the usage of poetry, please run the following command to update your crewai package:
```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.
You should see the output in the console, and `output/report.md` should be created in the project root.
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).
For a Flow-first walkthrough, see the [Quickstart](https://docs.crewai.com/en/quickstart).
## Key Features
CrewAI gives developers a practical foundation for building agentic systems that move from prototype to production: autonomous collaboration where it helps, explicit workflow control where it matters, and Python-native customization throughout.
@@ -701,17 +656,13 @@ A: CrewAI is a lean, fast Python framework built specifically for orchestrating
### Q: How do I install CrewAI?
A: Install CrewAI with [UV](https://docs.astral.sh/uv/):
A: Install the CrewAI CLI with [UV](https://docs.astral.sh/uv/):
```shell
uv pip install crewai
uv tool install crewai
```
For additional tools, use:
```shell
uv pip install 'crewai[tools]'
```
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.
### Q: Is CrewAI a standalone framework?

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,77 @@ 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

View File

@@ -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-2.0-flash")
memory = Memory(llm="gemini/gemini-3.7-flash")
# Pass a pre-configured LLM instance with custom settings
llm = LLM(model="gpt-4o", temperature=0)

View File

@@ -26,7 +26,7 @@ mode: "wide"
- **معالجة الأخطاء** توجيه كيفية استجابة الـ Agents للإخفاقات والاستثناءات وحالات انتهاء المهلة.
- **مطالبات خاصة بالأدوات** تعريف تعليمات مفصلة لكيفية استدعاء الأدوات أو استخدامها.
اطلع على [قوالب المطالبات الأصلية في مستودع CrewAI](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json) لمعرفة كيفية تنظيم هذه العناصر. من هناك، يمكنك تجاوزها أو تكييفها حسب الحاجة لفتح سلوكيات متقدمة.
اطلع على [قوالب المطالبات الأصلية في مستودع CrewAI](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/translations/en.json) لمعرفة كيفية تنظيم هذه العناصر. من هناك، يمكنك تجاوزها أو تكييفها حسب الحاجة لفتح سلوكيات متقدمة.
## فهم تعليمات النظام الافتراضية

View File

@@ -75,7 +75,7 @@ research_crew/
}
```
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `anthropic/claude-sonnet-4-6` أو `gemini/gemini-2.0-flash-001`.
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `anthropic/claude-sonnet-4-6` أو `gemini/gemini-3.7-flash`.
## الخطوة 3: تعريف المهام وإعدادات الـ Crew

View File

@@ -1,35 +1,37 @@
---
title: تدفقات المحادثة
description: أنشئ تطبيقات دردشة متعددة الجولات مع kickoff لكل جولة وسجل الرسائل وتوجيه النية والتتبع وجسور WebSocket.
description: أنشئ تطبيقات دردشة متعددة الجولات باستخدام handle_turn لكل جولة، وسجل الرسائل، وتوجيه النية، والتتبع، والبث المنظّم.
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` قبل تشغيل الرسم |
| اكتمال الجولة | `FlowFinished` لهذا **التشغيل** فقط؛ تستمر المحادثة في `handle_turn` التالي |
| تتبع الجلسة | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
| اكتمال الجولة | `conversation_turn_completed`؛ ومع تأجيل التتبع الافتراضي ينتظر `FlowFinished` استدعاء `finalize_session_traces()` |
| تتبع الجلسة الكامل | `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` | الموافقة/الرفض على **مخرجات خطوة** — وليس السطر التالي |
| `ChatSession.handle_turn(...)` | طبقة نقل فوق `handle_turn` |
ترفع `handle_turn()` و`stream_turn()` و`chat()` الخطأ `ValueError` ما لم يكن الوضع المحادثاتي مفعّلاً. يؤدي تطبيق `@ConversationConfig(...)` إلى تفعيله تلقائياً؛ وإلا فعيّن `conversational = True`.
## بداية سريعة
@@ -38,7 +40,7 @@ from uuid import uuid4
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
)
@@ -46,31 +48,29 @@ from crewai.experimental.conversational 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 "طلب" in message or "order" in message:
if "order" in message:
return "order"
if "وداع" in message or "goodbye" in message:
if "bye" in message or "goodbye" in message:
return "goodbye"
return "help"
@listen("order")
def handle_order(self):
reply = "طلبك في الطريق."
reply = "Your order is on the way."
self.append_assistant_message(reply)
return reply
@listen("help")
def handle_help(self):
reply = "كيف يمكنني المساعدة؟"
reply = "How can I help?"
self.append_assistant_message(reply)
return reply
@listen("goodbye")
def handle_goodbye(self):
reply = "وداعاً!"
reply = "Goodbye!"
self.append_assistant_message(reply)
return reply
@@ -79,130 +79,141 @@ session_id = str(uuid4())
flow = SupportFlow()
try:
flow.handle_turn("أين طلبي؟", session_id=session_id)
flow.handle_turn("وماذا عن الإرجاع؟", session_id=session_id)
flow.handle_turn("Where is my order?", session_id=session_id)
flow.handle_turn("What about returns?", session_id=session_id)
finally:
flow.finalize_session_traces()
flow.finalize_session_traces() # one trace link for the whole chat
```
## بث جولة
استخدم `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. **`_configure_conversational_kickoff`** — دمج `session_id` / `user_message` في `inputs` وتطبيق `ConversationalConfig`.
2. **استعادة الحالة** — عند وجود `inputs["id"]` و`@persist`.
1. **إعداد الجولة** — يخزن رسالة المستخدم المعلقة، ويحل معرّف الجلسة، ويعيد ضبط تعقّب التنفيذ الخاص بالجولة، ثم يستدعي `kickoff(inputs={"id": session_id})`.
2. **استعادة الحالة** — إذا وُجد `inputs["id"]` وكان `@persist` مهيّأً، تُحمّل أحدث لقطة.
3. **`FlowStarted`** — في أول جولة للجلسة المؤجلة فقط.
4. **`prepare_conversational_turn`** — إضافة رسالة المستخدم و`last_user_message` وتصنيف اختياري.
5. **تنفيذ الرسم** — `@start` → `@router` → معالجات `@listen`.
6. **نهاية التشغيل** — يُتخطى `flow_finished` والتتبع لكل جولة عند التأجيل؛ `Agent.kickoff()` / crews لا تغلق دفعة الأب.
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 دفعة الأب.
استدعِ **`append_assistant_message(reply)`** في المعالجات. سطر المستخدم محفوظ عبر `handle_turn` — لا تُضفه مرة أخرى.
استدعِ **`append_assistant_message(reply)`** عندما لا تطابق الرد الظاهر قيمة الإرجاع، أو عند قصّ التاريخ. تُسجَّل أيضاً سلسلة الإرجاع العامة كمساعد وتُضمَّن في لقطة `@persist`، فتستعيدها نسخة Flow جديدة. سطر المستخدم محفوظ عبر `handle_turn` — لا تُضفه مرة أخرى.
## `ConversationalConfig` (افتراضيات على مستوى الصنف)
## نظرة عامة على الإعداد
عيّن على صنف `Flow` كـ `conversational_config: ClassVar[ConversationalConfig | None]`.
يؤدي تزيين صنف فرعي من `Flow` بـ `ConversationConfig` إلى إرفاق افتراضيات الدردشة وتفعيل الوضع المحادثاتي معاً. راجع [مرجع الحقول الكامل](#conversationconfig) أدناه. ويمكنك تجاوز التصنيف المسبق لكل جولة عبر `handle_turn(..., intents=..., intent_llm=...)`.
| الحقل | الافتراضي | الغرض |
|-------|-----------|--------|
| `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` منخفضة المستوى
يمكن التجاوز لكل kickoff عبر `intents=` و`intent_llm=`.
## `ChatState` (شكل الحالة الموصى به للحفظ)
تظل `ChatState` و`ConversationalConfig` القديمة ومساعدات `crewai.flow.conversation` قابلة للاستيراد للتنسيق المتقدم أو الاختبارات أو الأغلفة المخصصة. وهي منفصلة عن واجهتي `ConversationState` / `ConversationConfig`، ولا تضيف وسيطي `user_message=` أو `session_id=` إلى `Flow.kickoff()`.
```python
from crewai.flow import ChatState
class MyChatState(ChatState):
# موروث: id, messages, last_user_message, last_intent, session_ready
# Inherited: id, messages, last_user_message, last_intent, session_ready
research_turn_count: int = 0
custom_flag: bool = False
```
| الحقل | الدور |
|-------|------|
| `id` | UUID الجلسة (مثل `session_id` / `inputs["id"]`) |
| `messages` | قائمة `{role, content}` لسجل LLM |
| `id` | UUID الجلسة (نفس `inputs["id"]`) |
| `messages` | `list` من `{role, content}` لسجل LLM |
| `last_user_message` | آخر سطر مستخدم في هذه الجولة |
| `last_intent` | تسمية المسار بعد التصنيف (إن وُجد) |
| `session_ready` | علم bootstrap لمرة واحدة |
| `session_ready` | علم bootstrap لمرة واحدة (الصلاحيات، وذاكرات التخزين المؤقت، وغيرها) |
`ConversationalInputs` هو `TypedDict` لـ `kickoff(inputs={...})`: `id`, `user_message`, `last_intent`.
`ConversationalInputs` هو `TypedDict` لمفاتيح `kickoff(inputs={...})` الاصطلاحية: `id` و`user_message` و`last_intent`.
تخزن `ConversationState` رسائل `messages` ككائنات `ConversationMessage`، وتوفر أيضاً `current_user_message` و`ended` و`events` و`agent_threads`. استخدم `conversation_messages` عند تمرير سجلها القانوني إلى LLM.
## API المحادثة على `Flow`
### معاملات `kickoff` / `kickoff_async`
### معاملات `handle_turn`
| المعامل | الغرض |
|---------|--------|
| `user_message` | نص هذه الجولة (أو `{"role": "user", "content": "..."}`) |
| `message` | نص هذه الجولة |
| `session_id` | UUID المحادثة → `inputs["id"]` / `state.id` |
| `intents` | تسميات outcome لـ `classify_intent` قبل kickoff |
| `intents` | تسميات النتائج لـ `classify_intent` قبل kickoff |
| `intent_llm` | LLM للتصنيف (مطلوب مع `intents`) |
| `interactive` | حلقة CLI عبر `ask()` (للعروض المحلية فقط) |
| `interactive_prompt` | مطالبة الوضع التفاعلي |
| `interactive_timeout` | مهلة `ask()` لكل سطر |
| `exit_commands` | كلمات إنهاء الوضع التفاعلي |
| `inputs` | حقول حالة إضافية |
| `restore_from_state_id` | استنساخ من flow محفوظ آخر |
| `**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()` عندما يمثل الاستدعاء رسالة دردشة.
### سمات المثيل
| السمة | الغرض |
|-------|--------|
| `conversational_config` | افتراضيات `ConversationalConfig` على مستوى الصنف |
| `defer_trace_finalization` | علم المثيل؛ يُضبط تلقائياً من config عند kickoff |
| `suppress_flow_events` | يخفي لوحات console؛ **التتبع يُسجّل** |
| `stream` | بث؛ مع `ChatSession.handle_turn(..., stream=True)` |
| `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()`. |
### طرق وخصائص
| الاسم | الوصف |
|------|--------|
| `append_assistant_message(content)` | إضافة رد مساعد مرئي للمستخدم إلى `state.messages` |
| `append_message(role, content, **extra)` | إضافة إلى `state.messages` |
| `conversation_messages` | سجل للقراءة فقط لاستدعاءات LLM |
| `classify_intent(text, outcomes, *, llm, context=None)` | تعيين outcome |
| `receive_user_message(text, *, outcomes=None, llm=None)` | إضافة رسالة مستخدم؛ `last_intent` اختياري |
| `classify_intent(text, outcomes, *, llm, context=None)` | تعيين النص إلى نتيجة واحدة (بنفس منطق الاختزال المستخدم في `@human_feedback`) |
| `receive_user_message(text, *, outcomes=None, llm=None)` | إضافة رسالة مستخدم، وضبط `last_intent` اختيارياً |
| `finalize_session_traces()` | إصدار `flow_finished` المؤجل وإنهاء دفعة trace |
| `_should_defer_trace_finalization()` | هل يُؤجل إنهاء trace لكل جولة |
| `_should_defer_trace_finalization()` | hook متقدم/داخلي يحسم ما إذا كان إنهاء trace لكل جولة مؤجلاً |
| `input_history` | سجل تدقيق مطالبات وردود `ask()` |
### مساعدات الوحدة (`crewai.flow.conversation`)
يمكن استيرادها من `crewai.flow.conversation` للاختبارات أو التنسيق المخصص. تستخدم هذه المساعدات بنية `ConversationalConfig` القديمة؛ كما تمسح `prepare_conversational_turn()` قيمة `last_intent`، بخلاف `handle_turn()` التي تحتفظ بها كسياق للموجّه.
| الدالة | الوصف |
|--------|--------|
| `normalize_kickoff_inputs(...)` | دمج kwargs المحادثة في `inputs` |
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)` | دمج وسائط المحادثة في `inputs` |
| `get_conversation_messages(flow)` | قراءة الرسائل من الحالة أو المخزن |
| `append_message(flow, ...)` | مثل طريقة المثيل |
| `prepare_conversational_turn(flow, ...)` | تهيئة الجولة (عادةً kickoff يستدعيها) |
| `receive_user_message(flow, ...)` | مثل طريقة المثيل |
| `append_message(flow, role, content, **extra)` | مثل طريقة المثيل |
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | ترطيب الجولة منخفض المستوى للأغلفة المخصصة |
| `receive_user_message(flow, text, ...)` | مثل طريقة المثيل |
| `set_state_field(flow, name, value)` | تعيين حقل dict أو Pydantic |
| `get_conversational_config(flow)` | قراءة `conversational_config` |
| `input_history_to_messages(entries)` | تحويل `input_history` لصيغة رسائل LLM |
## أنماط توجيه النية
### أ. تصنيف مسبق عبر `ConversationalConfig` (الأبسط)
### أ. تصنيف مسبق عبر `ConversationConfig` (الأبسط)
عيّن `default_intents` و`intent_llm`. كل kickoff يصنّف قبل `@router`؛ اقرأ `self.state.last_intent` في `route()`.
عيّن `default_intents` و`intent_llm`. يصنّف كل `handle_turn()` الرسالة الحالية مسبقاً. تكون الأولوية لنتيجة غير فارغة يعيدها `route_turn()` مخصص؛ وإلا تستخدم `route_conversation` النية المصنّفة للجولة الحالية.
### ب. تصنيف داخل `@router` (مطالبات أغنى)
### ب. تصنيف داخل `route_turn` (مطالبات أغنى)
عيّن `default_intents=None` ليضيف kickoff الرسالة فقط. في `route()` استدعِ `classify_intent`:
عيّن `default_intents=None` كي يضيف `handle_turn()` رسالة المستخدم فقط. داخل `route_turn()`، استدعِ `classify_intent` بمطالبة أو أوصاف مخصصة:
```python
@router(bootstrap)
def route(self):
def route_turn(self, context):
intent = self.classify_intent(
self._routing_prompt(self.state.last_user_message),
self._routing_prompt(self.state.current_user_message),
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
llm="gpt-4o-mini",
)
self.state.last_intent = intent
return intent
@@ -212,70 +223,59 @@ def route(self):
## عندما ينتهي الـ flow ويستمر المستخدم
`FlowFinished` يعني أن **تنفيذ الرسم هذا** اكتمل. تستمر المحادثة بـ `kickoff` آخر ونفس `session_id`. `@persist` يستعيد `messages` والأعلام والسياق.
يُكمل كل `handle_turn()` تشغيل رسم واحد، وتستمر المحادثة عبر `handle_turn()` آخر يستخدم `session_id` نفسه. مع دورة حياة التتبع المؤجلة افتراضياً، يصدر ذلك التشغيل `conversation_turn_completed`، بينما يصدر `FlowFinished` مرة واحدة عندما تغلق `finalize_session_traces()` الجلسة. ويستعيد `@persist` الرسائل والأعلام والسياق.
**نمط الحفظ:** يُفضّل `@persist` على **خطوة نهائية واحدة** (مثل `finalize`) وليس على صنف `Flow` بالكامل. الحفظ على مستوى الصنف بعد كل method قد يفقد تحديثات المعالجات في نفس الجولة.
**نمط الحفظ:** يُفضّل `@persist` على **خطوة نهائية واحدة** (مثل `finalize`) وليس على صنف `Flow` بالكامل. يحفظ الاستمرار على مستوى الصنف بعد كل طريقة؛ وتستخدم `load_state` أحدث صف، وقد يكون لقطة في منتصف التشغيل (مثلاً بعد `bootstrap` مباشرة) لا تتضمن تحديثات المعالج من الجولة نفسها.
لا تستخدم `@human_feedback` لأسطر المتابعة في الدردشة إلا عند الحاجة لموافقة بشرية على مخرجات خطوة محددة.
## `Flow` المحادثاتي (تجريبي)
## `Flow` المحادثاتي
<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 مفتوحة عبر الجولات. أنت تكتب **المسارات المخصصة** فقط؛ والإطار يتولى الباقي.
اشترك في رسم الدردشة المحادثاتي بتعيين `conversational = True` على صنف فرعي من `Flow` أو بتطبيق `@ConversationConfig(...)`. يوفر `Flow` الأساسي عندئذٍ `route_conversation` كنقطة البدء/الموجّه المدمجة، إضافة إلى مستمعي `converse_turn` و`end_conversation`. يظل المستمع المهمل `answer_from_history_turn` متاحاً للتوافق. يدير الإطار `state.messages`، ويمكنه تشغيل LLM للموجّه، ويبقي دفعة trace مفتوحة عبر الجولات. أنت تكتب **المسارات المخصصة**؛ والإطار يتولى الباقي.
استخدمه عندما تريد دردشة متعددة الجولات مع موجّه قائم على LLM ومعالجات لكل مسار دون توصيل دورة الحياة يدوياً. استخدم `Flow[ChatState]` (النمط الأدنى مستوى في الأعلى) عندما تحتاج تحكماً كاملاً.
### مثال سريع
```python
from crewai import LLM, Flow
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
RouterConfig,
)
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
)
@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:
return "INTERNET_SEARCH"
if "docs" in message or "crewai" in message:
return "CREWAI_DOCS"
return "converse"
@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("ماذا يمكنك أن تفعل؟") # يوجَّه إلى converse (مدمج)
flow.handle_turn("ابحث في الويب عن أخبار الذكاء الاصطناعي.") # يوجَّه إلى INTERNET_SEARCH
flow.handle_turn("لخص النتيجة الأولى.") # يعود إلى converse
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
finally:
flow.finalize_session_traces()
```
@@ -297,27 +297,54 @@ def kickoff() -> None:
|-------|-----------|-------|
| `system_prompt` | `slices.conversational_system_prompt` من i18n | رسالة system يستخدمها `converse_turn` المدمج. مرر `""` للتعطيل التام. |
| `llm` | `None` | LLM المحادثة (يستخدمه `converse_turn` وكاحتياطي للموجّه). |
| `router` | `None` | `RouterConfig` للتوجيه عبر LLM. بدونه، يسقط الـ flow دائماً إلى `converse`. |
| `answer_from_history_prompt` | افتراضي الإطار | رسالة system للمسار الاختياري `answer_from_history`. |
| `answer_from_history_llm` | `None` | يُفعّل الاختصار `answer_from_history` عند تعيينه. |
| `router` | `None` | تجاوزات `RouterConfig` اختيارية. مع وجود مستمعين مخصصين وLLM قابل للحل، يُفعّل التوجيه تلقائياً حتى عند إغفال هذا الحقل. |
| `answer_from_history_prompt` | افتراضي الإطار | **مهمل.** استخدم system prompt الخاص بـ `converse` أو تجاوز `converse_turn()`. |
| `answer_from_history_llm` | `None` | **مهمل.** استخدم `llm`؛ إذ يتلقى `converse` السجل القانوني بالفعل. |
| `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
RouterConfig(
prompt="تأطير اختياري للنطاق (سياسة، صوت، شخصية).",
response_format=MyRoute, # اختياري؛ يُولَّد تلقائياً عند الإغفال
llm=ROUTER_LLM, # يسقط إلى ConversationConfig.llm
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # اختياري؛ يُستنتج من المستمعين
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
route_descriptions={
"INTERNET_SEARCH": "تجاوز الـ docstring لهذا المسار فقط.",
"INTERNET_SEARCH": "Override the docstring for this one route.",
},
default_intent="converse", # يُستخدم عند فشل LLM أو غيابه
fallback_intent="converse", # يُستخدم عندما يعيد LLM مساراً غير صالح
default_intent="converse", # used when LLM call fails or no LLM available
fallback_intent="converse", # used when LLM returns an invalid route
intent_field="intent",
)
```
@@ -325,13 +352,17 @@ RouterConfig(
تُبنى رسالة الموجّه إلى LLM تلقائياً. لكل مسار يختار الإطار وصفاً بهذا الترتيب من الأولوية:
1. `RouterConfig.route_descriptions[label]` — تجاوز صريح.
2. `Flow.builtin_route_descriptions[label]` — نص جاهز من الإطار لـ `converse` و`end` و`answer_from_history` (مصاغ لـ LLM التوجيه).
3. أول سطر غير فارغ من docstring معالج `@listen(label)`.
4. فارغ (المسار يظهر في الفهرس بلا وصف).
2. `Flow.builtin_route_descriptions[label]` — نص جاهز من الإطار لـ `converse` و`end` ولمسار التوافق المهمل `answer_from_history` (مصاغ لـ LLM التوجيه).
3. قيمة `description` المعلنة للطريقة (تستخدمها التدفقات التعريفية وإسقاطات DSL).
4. أول سطر غير فارغ من docstring معالج `@listen(label)`.
5. فارغ (المسار يظهر في الفهرس بلا وصف).
عملياً، **إضافة مسار جديد = `@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."""
@@ -350,13 +381,34 @@ 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` | اختياري. يُوجَّه إليه عندما يكون `ConversationConfig.answer_from_history_llm` مُعيَّناً ويمكن الإجابة على الرسالة من التاريخ فقط. |
| `answer_from_history` | `answer_from_history_turn` | **مسار توافق مهمل.** استخدم `converse`، الذي يتلقى السجل القانوني بالفعل. |
يمكنك تجاوز أي من هذه بتعريف معالج بنفس الاسم في الصنف الفرعي.
@@ -366,9 +418,9 @@ Routes:
1. يعيد ضبط تعقّب التنفيذ لكل جولة (`_completed_methods`, `_method_outputs`) ليُعاد تشغيل الرسم — بدون ذلك، استدعاءات `kickoff` المتكررة على نفس النسخة ستُحدث دائرة قصر من الجولة الثانية لأن `Flow.kickoff_async` يعتبر `inputs={"id": ...}` استعادة من نقطة تفتيش.
2. يُلحق رسالة المستخدم بـ `state.messages` ويضبط `current_user_message` / `last_user_message`. يُحافَظ على `last_intent` **من الجولة السابقة** كي يستخدمها LLM التوجيه كإشارة.
3. يُشغّل `conversation_start` → `route_conversation` → معالج `@listen` المختار.
3. يُشغّل طرق `@start` التي يعرّفها المستخدم (إن وجدت)، ثم `route_conversation` كنقطة البدء/الموجّه المدمجة، ثم معالج `@listen` المختار. وتستدعي `route_conversation` المساعد القابل للتجاوز `conversation_start()`.
4. يخزّن الموجّه قراره في `state.last_intent` (يكون مرئياً لسياق التوجيه في الجولة التالية).
5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ `append_assistant_message`، فإن `handle_turn` يُلحقها نيابةً عنك.
5. إذا أعاد معالجك سلسلة نصية ولم يستدعِ `append_assistant_message`، فإن `handle_turn` يُلحقها نيابةً عنك ويحفظ `state.messages` المحدَّث حتى تشمل استعادة `@persist` جولة المساعد.
استدعِ `handle_turn()` لرسائل الدردشة. استدعاء `kickoff(inputs={"id": ...})` مباشرةً يشغل الرسم بدون غلاف الجولة المحادثية.
@@ -389,6 +441,8 @@ flow.chat()
4. يطبع نتيجة المساعد.
5. ينهي traces الجلسة المؤجلة داخل كتلة `finally`.
يُفعّل `chat(defer_trace_finalization=True)` مؤقتاً علم التأجيل على مستوى المثيل للـ REPL، ثم يعيد قيمته السابقة عند الخروج.
خصص سلوك الطرفية عبر I/O قابل للحقن:
```python
@@ -407,6 +461,12 @@ flow.chat(
لتشغيل آثار جانبية (إعداد ناقل أحداث، قياس عن بُعد) في كل قرار توجيه، تجاوز `route_turn`:
```python
from typing import Any
from crewai import Flow
from crewai.flow import ConversationState
class SupportFlow(Flow[ConversationState]):
conversational = True
@@ -415,7 +475,7 @@ class SupportFlow(Flow[ConversationState]):
return super().route_turn(context)
```
لتجاوز موجّه LLM واختيار مسار برمجياً، أعد سلسلة نصية من `route_turn`؛ إعادة `None` تسقط إلى `_route_with_config(...)`.
لتجاوز موجّه LLM بالكامل واختيار مسار برمجياً، أعد سلسلة نصية غير فارغة من `route_turn`. لا يؤدي إرجاع قيمة falsy من التجاوز إلى استدعاء `_route_with_config()`؛ بل يسقط التوجيه إلى النية المصنّفة مسبقاً لهذه الجولة، ثم إلى مسار التوافق المهمل `answer_from_history` عند إعداده، وأخيراً إلى `converse`. تكون `last_intent` من الجولة السابقة متاحة في سياق الموجّه، لكنها لا تُعاد أبداً كـ fallback.
### `append_assistant_message` و`append_agent_result`
@@ -428,7 +488,7 @@ class SupportFlow(Flow[ConversationState]):
## تعريف تدفق محادثاتي بصيغة JSON/YAML
يمكن لـ [التدفق التعريفي](/edge/en/concepts/cli) أن يكون محادثاتيًا أيضًا. أضف كتلة `conversational` في المستوى الأعلى وعرّف مساراتك الخاصة كطرق تستمع (`listen`) إلى تسمية مسار:
يمكن لـ [التدفق التعريفي](/edge/ar/concepts/cli) أن يكون محادثاتيًا أيضًا. أضف كتلة `conversational` في المستوى الأعلى وعرّف مساراتك الخاصة كطرق تستمع (`listen`) إلى تسمية مسار:
```yaml
schema: crewai.flow/v1
@@ -453,15 +513,17 @@ methods:
input: "${state.current_user_message}"
```
تعريف الكتلة هو الاشتراك نفسه — القيمة الافتراضية لـ `enabled` هي `true`. اضبطها على `enabled: false` للاحتفاظ بالإعدادات مع إيقاف المحادثة.
تعريف الكتلة هو الاشتراك نفسه — القيمة الافتراضية لـ `enabled` هي `true`. اضبطها على `enabled: false` للاحتفاظ بالإعدادات مع إيقاف المحادثة. يؤدي ذلك أيضاً إلى تعطيل إنشاء الطرق المدمجة، ولذلك يجب أن توفر التعريفة رسماً عادياً غير محادثاتي.
تُوفَّر لك ثلاثة أشياء:
| المُوفَّر | التفاصيل |
|----------|--------|
| الرسم البياني المدمج | تُضاف `route_conversation` و`converse_turn` و`end_conversation` و`answer_from_history_turn` تلقائيًا. عرّف طريقة بأحد هذه الأسماء لتجاوزها. |
| حالة المحادثة | تُستخدم `ConversationState` عندما لا تحتوي التعريفة على كتلة `state`. لإضافة حقول، وجّه `state` إلى نموذج Pydantic يرث من `ConversationState`. |
| كتالوج المسارات | يُبنى من الطرق التي تعلن تسمية `listen`. وصف كل طريقة (`description`) هو ما يقرأه نموذج التوجيه عند الاختيار بين المسارات. |
| الرسم البياني المدمج | تُضاف `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 بنفس واجهات الجولة المستخدمة مع تدفق محادثاتي معرّف بصنف:
@@ -484,15 +546,16 @@ finally:
| غير قابل للتعبير | استخدم بدلًا منه |
|-----------------|-------------|
| مثيل `LLM` حي أو `BaseLLM` مخصص | سلسلة معرّف النموذج، مثل `gpt-4o-mini` |
| `router.response_format` كصنف نموذج | احذفه؛ يولّد الإطار واحدًا. يُتجاهل المرجع أو المخطط مع تحذير |
| تجاوزات `route_turn()` / `can_answer_from_history()` | اكتب التدفق بلغة Python، أو وجّه `do` لطريقة إلى مرجع `call: code` |
| مثيل `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` حلقة محادثة بعد: فهو يبلّغ أن التدفق محادثاتي ويخرج بدلًا من تنفيذ جولة واحدة. شغّل التدفق المحادثاتي التعريفي من Python عبر `handle_turn()` أو `stream_turn()` أو `chat()`.
يفتح `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` (افتراضي في `ConversationalConfig`):
مع `defer_trace_finalization=True` (افتراضي في `ConversationConfig`):
- **دفعة trace واحدة** لجلسة الدردشة.
- **`flow_started`** في الجولة الأولى فقط؛ **`flow_finished`** مرة في `finalize_session_traces()`.
@@ -503,17 +566,30 @@ finally:
flow.chat(session_id=session_id)
```
`flow.chat()` يستدعي `finalize_session_traces()` نيابةً عنك. عندما تملك الحلقة عبر `handle_turn()` أو `kickoff(...)`، استدعِ `finalize_session_traces()` عند انتهاء الجلسة.
`flow.chat()` يستدعي `finalize_session_traces()` نيابةً عنك. عندما تملك الحلقة عبر `handle_turn()`، استدعِ `finalize_session_traces()` عند انتهاء الجلسة.
`suppress_flow_events=True` يخفي لوحات Rich فقط؛ أحداث trace والـ methods تُصدر.
يخفي `suppress_flow_events=True` لوحات Rich ويمنع أحداث تنفيذ الطرق. وتظل أحداث بدء/انتهاء Flow تصدر، فيبقى بالإمكان تتبع دورة حياة Flow الخارجية، بينما تُحذف spans الطرق الفردية.
### دورة حياة trace لـ `Flow` المحادثاتي
يستخدم [`Flow` المحادثاتي](#flow-المحادثاتي-تجريبي) التجريبي نفس دورة حياة tracing: `defer_trace_finalization` افتراضياً `True`، فيبقي كل `handle_turn()` أثر الجلسة مفتوحاً. أنهِ دوماً عند نهاية الجلسة — لُف حلقتك بـ `try/finally` واستدعِ `flow.finalize_session_traces()` عند الخروج. بدون ذلك، تبقى الدفعة مفتوحة وقد لا تُصدَّر آخر محادثة أبداً.
يستخدم [`Flow` المحادثاتي](#flow-المحادثاتي) دورة حياة التتبع نفسها: القيمة الافتراضية لـ `defer_trace_finalization` هي `True`، ولذلك يبقي كل `handle_turn()` trace الجلسة مفتوحاً. تمنع الجولات المؤجلة أيضاً إصدار `flow_failed` لكل جولة؛ وعند حدوث خطأ في جولة أو إلغاء الجلسة، أنهِ الجلسة صراحةً. يغلق ذلك الدفعة بحدث `FlowFinished` على مستوى الجلسة بدلاً من حدث `FlowFailed` لكل جولة. لُف REPL/الحلقة دائماً بـ `try/finally` واستدعِ `flow.finalize_session_traces()` عند الخروج. بدون ذلك، تبقى دفعة trace مفتوحة وقد لا تُصدَّر المحادثة النهائية أبداً.
## البث
اضبط `stream = True` على صنف `Flow`. عندئذٍ يُصدر `kickoff(...)` أحداث `assistant_delta` (وما يرتبط بها) عبر ناقل الأحداث القياسي.
استخدم `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()` دورة حياة البث المحادثاتي.
## الاستيراد
@@ -528,10 +604,15 @@ 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

View File

@@ -104,7 +104,7 @@ crewai flow add-crew content-crew
}
```
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `gemini/gemini-2.0-flash-001` أو `anthropic/claude-sonnet-4-6`.
استبدل `provider/model-id` بالنموذج الذي تستخدمه، مثل `openai/gpt-4o` أو `gemini/gemini-3.7-flash` أو `anthropic/claude-sonnet-4-6`.
3. أنشئ `src/guide_creator_flow/crews/content_crew/crew.jsonc`:

View File

@@ -0,0 +1,156 @@
---
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>

View File

@@ -0,0 +1,238 @@
---
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>

View File

@@ -176,7 +176,7 @@ grep -r "llm:" --include="*.yaml" .
# llm = LLM(model="mistral/mistral-large-latest")
# After (Native):
llm = LLM(model="gemini/gemini-2.0-flash")
llm = LLM(model="gemini/gemini-3.7-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-2.0-flash") # Fast & capable
llm = LLM(model="gemini/gemini-3.7-flash") # Fast & capable
# Mistral → Anthropic or OpenAI
# llm = LLM(model="mistral/mistral-large-latest")

View File

@@ -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-2.0-flash" # Add your Gemini model here, under openai/
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-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-2.0-flash",
model="openai/gemini-3.7-flash",
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key="your-gemini-key", # Should start with AIza...
)

View File

@@ -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-2.5-flash-preview-05-20", temperature=0.1)
manager_llm = LLM(model="gemini/gemini-3.7-flash", 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-2.5-flash-preview-05-20"), # Premium for coordination
llm=LLM(model="gemini/gemini-3.7-flash"), # Premium for coordination
# ... rest of config
)

View File

@@ -151,7 +151,7 @@ result = stream.result
```python
from crewai import Flow
from crewai.experimental.conversational import ConversationConfig, ConversationState
from crewai.flow import ConversationConfig, ConversationState
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)

View File

@@ -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://phoenix.arize.com) هو منصة مراقبة LLM توفر التتبع والتقييم لتطبيقات الذكاء الاصطناعي.
> **ما هو 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/) لمنصة سحابية مُدارة أو ذاتية الاستضافة للمؤسسات لأنظمة الذكاء الاصطناعي في الإنتاج.
[![شاهد عرض فيديو لتكاملنا مع Phoenix](https://storage.googleapis.com/arize-assets/fixtures/setup_crewai.png)](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
### الخطوة 2: إعداد متغيرات البيئة
قم بإعداد مفاتيح API لـ Phoenix Cloud وإعداد OpenTelemetry لإرسال التتبعات إلى Phoenix. Phoenix Cloud هو إصدار مستضاف من Arize Phoenix، لكنه ليس مطلوباً لاستخدام هذا التكامل.
قم بإعداد مفتاح API الخاص بـ Phoenix ونقطة نهاية OpenTelemetry لإرسال التتبعات إلى Phoenix. يعمل الإعداد نفسه مع نقطة نهاية 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 Cloud credentials
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
# Get your Phoenix API key
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix 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" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
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["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["SERPER_API_KEY"] = SERPER_API_KEY
```
@@ -131,7 +131,7 @@ print(result)
بعد تشغيل الوكيل، يمكنك عرض التتبعات المولدة من تطبيق CrewAI في Phoenix. سترى خطوات مفصلة لتفاعلات الوكلاء واستدعاءات LLM، مما يساعدك في التصحيح والتحسين.
سجل الدخول إلى حساب Phoenix Cloud الخاص بك وانتقل إلى المشروع الذي حددته في معامل `project_name`. سترى عرض زمني للتتبع مع جميع تفاعلات الوكلاء واستخدامات الأدوات واستدعاءات LLM.
افتح مشروع Phoenix وانتقل إلى المشروع الذي حددته في معامل `project_name`. سترى عرض زمني للتتبع مع جميع تفاعلات الوكلاء واستخدامات الأدوات واستدعاءات LLM.
![مثال تتبع في Phoenix يوضح تفاعلات الوكلاء](https://storage.googleapis.com/arize-assets/fixtures/crewai_traces.png)
@@ -145,6 +145,9 @@ 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.

View File

@@ -23,7 +23,7 @@ mode: "wide"
عند تفعيل ميزة `share_crew`، يتم جمع بيانات تفصيلية تشمل أوصاف المهام وخلفيات وأهداف الوكلاء وسمات محددة أخرى
لتوفير رؤى أعمق. قد يتضمن جمع البيانات الموسع هذا معلومات شخصية إذا دمجها المستخدمون في طواقمهم أو مهامهم.
يجب على المستخدمين النظر بعناية في محتوى طواقمهم ومهامهم قبل تفعيل `share_crew`.
يمكن للمستخدمين تعطيل القياس عن بُعد عبر تعيين متغير البيئة `CREWAI_DISABLE_TELEMETRY` إلى `true` أو تعيين `OTEL_SDK_DISABLED` إلى `true` (لاحظ أن الأخير يعطل جميع أدوات OpenTelemetry عالمياً).
يمكن للمستخدمين تعطيل القياس عن بُعد في CrewAI عبر تعيين `CREWAI_DISABLE_TELEMETRY` إلى `true` أو `1` أو `yes` أو `on` (بغض النظر عن حالة الأحرف). `OTEL_SDK_DISABLED` بنفس القيم يعطّل أيضاً مُصدِّر CrewAI. مجموعة أدوات OpenTelemetry نفسها ما تزال تقبل `true` فقط لتعطيل بقية أدوات القياس في العملية.
### أمثلة:
```python
@@ -34,6 +34,8 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
os.environ['OTEL_SDK_DISABLED'] = 'true'
```
`CREWAI_DISABLE_TELEMETRY=1` (أو `yes` / `on`) يعمل بنفس طريقة `true`. تُتجاهل القيم غير المعروفة ويبقى القياس عن بُعد مفعّلاً.
### العزل عن إعداد OpenTelemetry الخاص بك
يعمل القياس عن بُعد الخاص بـ CrewAI على `TracerProvider` خاص به ولا يسجل نفسه
@@ -52,15 +54,16 @@ os.environ['OTEL_SDK_DISABLED'] = 'true'
| افتراضي | البيانات | السبب والتفاصيل |
|:----------|:------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------|
| نعم | إصدار CrewAI وPython | تتبع إصدارات البرمجيات. مثال: CrewAI v1.2.3، Python 3.8.10. لا بيانات شخصية. |
| نعم | بيانات وصفية للطاقم | تشمل: مفتاح ومعرّف مُولّد عشوائياً، نوع العملية (مثل 'sequential'، 'parallel')، علم منطقي لاستخدام الذاكرة (true/false)، عدد المهام، عدد الوكلاء. كلها غير شخصية. |
| نعم | بيانات وصفية للطاقم | تشمل: مفتاح ومعرّف مُولّد عشوائياً، نوع العملية (مثل 'sequential'، 'parallel')، علم منطقي لاستخدام الذاكرة (true/false)، علم منطقي يوضح ما إذا تم تمرير أي مدخلات للتشغيل (true/false — وليس مفاتيح المدخلات أو قيمها، والتي لا تُجمع إلا عند تمكين `share_crew`)، عدد المهام، عدد الوكلاء. كلها غير شخصية. |
| نعم | بيانات الوكيل | تشمل: مفتاح ومعرّف مُولّد عشوائياً، اسم الدور (يجب ألا يتضمن معلومات شخصية)، إعدادات منطقية (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` عند ضبطه. يتحقق الاكتشاف فقط مما إذا كانت متغيرات البيئة المعروفة مضبوطة، ولا يقرأ قيمها أبدًا. لا بيانات شخصية. |
| نعم | بيئة التنفيذ | تشمل: مساعد البرمجة بالذكاء الاصطناعي الذي يشغّل العملية إن وُجد (واحد من قائمة ثابتة مثل `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. |
| لا | بيانات الوكيل الموسّعة | تشمل: وصف الهدف، نص الخلفية، معرّف ملف موجهات i18n. يجب على المستخدمين التأكد من عدم تضمين معلومات شخصية في حقول النص. |

View File

@@ -50,16 +50,15 @@ mode: "wide"
- **سلامة الذكاء الاصطناعي**: تنفيذ فحوصات الإشراف على المحتوى والسلامة
```python
from crewai_tools import DallETool, VisionTool, CodeInterpreterTool
from crewai_tools import DallETool, VisionTool
# Create AI tools
image_generator = DallETool()
vision_processor = VisionTool()
code_executor = CodeInterpreterTool()
# Add to your agent
agent = Agent(
role="AI Specialist",
tools=[image_generator, vision_processor, code_executor],
tools=[image_generator, vision_processor],
goal="Create and analyze content using AI capabilities"
)

View File

@@ -4,6 +4,77 @@ 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 File

@@ -623,6 +623,12 @@ 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:

View File

@@ -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-2.0-flash")
memory = Memory(llm="gemini/gemini-3.7-flash")
# Pass a pre-configured LLM instance with custom settings
llm = LLM(model="gpt-4o", temperature=0)

View File

@@ -26,7 +26,7 @@ Under the hood, CrewAI employs a modular prompt system that you can customize ex
- **Error handling** Direct how agents respond to failures, exceptions, or timeouts.
- **Tool-specific prompts** Define detailed instructions for how tools are invoked or utilized.
Check out the [original prompt templates in CrewAI's repository](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json) to see how these elements are organized. From there, you can override or adapt them as needed to unlock advanced behaviors.
Check out the [original prompt templates in CrewAI's repository](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/translations/en.json) to see how these elements are organized. From there, you can override or adapt them as needed to unlock advanced behaviors.
## Understanding Default System Instructions

View File

@@ -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-2.0-flash-001`.
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`.
## Step 3: Define Tasks and Crew Settings

View File

@@ -1,19 +1,19 @@
---
title: Conversational Flows
description: Build multi-turn chat apps with handle_turn per turn, message history, intent routing, tracing, and WebSocket bridges.
description: Build multi-turn chat apps with handle_turn per turn, message history, intent routing, tracing, and structured streaming.
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, UI bridges, and a local `flow.chat()` REPL for conversational flows.
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.
| 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 | `FlowFinished` for **this run** only; chat continues on the next `handle_turn` |
| Turn complete | `conversation_turn_completed`; with default trace deferral, `FlowFinished` waits for `finalize_session_traces()` |
| Full-session trace | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
## Turn APIs
@@ -30,7 +30,8 @@ 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 |
| `ChatSession.handle_turn(...)` | Transport layer over `handle_turn` (SSE / WebSocket) |
`handle_turn()`, `stream_turn()`, and `chat()` raise `ValueError` unless conversational mode is enabled. Applying `@ConversationConfig(...)` enables it automatically; otherwise set `conversational = True`.
## Quick start
@@ -39,7 +40,7 @@ from uuid import uuid4
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
)
@@ -47,8 +48,6 @@ from crewai.experimental.conversational 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:
@@ -96,12 +95,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.data.get("chunk", ""), end="", flush=True)
print(frame.content, end="", flush=True)
result = stream.result
```
For the full frame contract, channel list, and async API, see [Streaming Runtime Contract](/edge/en/learn/streaming-runtime-contract).
For the full frame contract and channel list, see [Streaming Runtime Contract](/edge/en/learn/streaming-runtime-contract).
## Turn lifecycle
@@ -111,29 +110,18 @@ 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** — `conversation_start` → `route_conversation` → the selected `@listen` handler.
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.
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)`** so the next turns `conversation_messages` includes assistant text. The user line is already stored by `handle_turn` — do not append it again in handlers.
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.
## `ConversationConfig` (class-level defaults)
## Configuration overview
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=...)`.
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=...)`.
## Lower-level `ChatState` helpers
`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()`.
`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()`.
```python
from crewai.flow import ChatState
@@ -155,6 +143,8 @@ 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
@@ -176,9 +166,9 @@ class MyChatState(ChatState):
| Attribute | Purpose |
|-----------|---------|
| `conversational` | Set to `True` to enable the conversational graph and `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)` |
| `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()`. |
### Methods and properties
@@ -190,12 +180,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()` | Whether this flow defers per-turn trace finalization |
| `_should_defer_trace_finalization()` | Advanced/internal hook that resolves whether per-turn trace finalization is deferred |
| `input_history` | Audit trail of `ask()` prompts and responses |
### Module helpers (`crewai.flow.conversation`)
Importable for tests or custom orchestration:
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.
| Function | Description |
|----------|-------------|
@@ -212,7 +202,7 @@ Importable for tests or custom orchestration:
### A. Pre-classify via `ConversationConfig` (simplest)
Set `default_intents` and `intent_llm`. Each `handle_turn()` runs classification before routing; read `self.state.last_intent` in `route_turn()`.
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.
### B. Classify inside `route_turn` (richer prompts)
@@ -233,24 +223,15 @@ Use **`@listen("RESEARCH")`** (or similar) for steps that run `Agent.kickoff()`
## When the flow finishes but the user keeps chatting
`FlowFinished` means **this graph run** completed. The conversation continues with another `handle_turn()` and the same `session_id`. `@persist` restores `messages`, flags, and context.
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.
**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` (experimental)
## Conversational `Flow`
<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.
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.
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.
@@ -259,7 +240,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.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
)
@@ -267,8 +248,6 @@ from crewai.experimental.conversational 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:
@@ -318,14 +297,26 @@ 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` | `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. |
| `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. |
| `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
@@ -334,7 +325,7 @@ from typing import Literal
from pydantic import BaseModel
from crewai import LLM
from crewai.experimental.conversational import RouterConfig
from crewai.flow import RouterConfig
class MyRoute(BaseModel):
@@ -360,9 +351,10 @@ 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`, `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).
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).
So in practice, **adding a new route is `@listen("X")` + a one-line docstring**:
@@ -415,7 +407,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` | Optional. Routes here when `ConversationConfig.answer_from_history_llm` is set and the message can be answered from existing history. |
| `answer_from_history` | `answer_from_history_turn` | **Deprecated compatibility route.** Use `converse`, which already receives canonical history. |
You can override any of these by defining a same-named handler in your subclass.
@@ -425,9 +417,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 `conversation_start` → `route_conversation` → the chosen `@listen` handler.
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.
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.
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.
Call `handle_turn()` for chat messages. Calling `kickoff(inputs={"id": ...})` directly runs the flow graph without applying the conversational turn wrapper.
@@ -448,6 +440,8 @@ 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
@@ -469,7 +463,7 @@ To run side effects (event bus setup, telemetry) on every routing decision, over
from typing import Any
from crewai import Flow
from crewai.experimental.conversational import ConversationState
from crewai.flow import ConversationState
class SupportFlow(Flow[ConversationState]):
@@ -480,7 +474,7 @@ class SupportFlow(Flow[ConversationState]):
return super().route_turn(context)
```
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(...)`.
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.
### `append_assistant_message` and `append_agent_result`
@@ -518,15 +512,17 @@ methods:
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.
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`, `end_conversation` and `answer_from_history_turn` are added automatically. Declare a method under one of those names to override it. |
| Conversation state | `ConversationState` is used when the declaration has no `state` block. To add fields, point `state` at a Pydantic model that extends `ConversationState`. |
| The route catalog | Built from the methods that declare a `listen` label. Each method's `description` is what the routing model reads when choosing between routes. |
| 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:
@@ -549,11 +545,12 @@ Route labels and method names share one trigger namespace, so a handler must not
| Not expressible | Use instead |
|-----------------|-------------|
| A live `LLM` instance or a custom `BaseLLM` | A model id string, such as `gpt-4o-mini` |
| `router.response_format` as a model class | Omit it; the framework synthesizes one. A ref or schema is ignored with a warning |
| `route_turn()` / `can_answer_from_history()` overrides | Author the Flow in Python, or point a method's `do` at a `call: code` ref |
| 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` has no chat loop yet: it reports that the flow is conversational and exits rather than running a single turn. Drive a declarative conversational flow from Python with `handle_turn()`, `stream_turn()` or `chat()`.
`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
@@ -572,15 +569,28 @@ flow.chat(session_id=session_id)
with `handle_turn()`, call `finalize_session_traces()` when
the session ends.
`suppress_flow_events=True` only hides Rich console panels; trace and method events still emit for observability.
`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.
### Conversational `Flow` trace lifecycle
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.
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.
## Streaming
Set `stream = True` on the `Flow` class. `kickoff(...)` will then emit `assistant_delta` (and related) events through the standard event bus.
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.
## Imports
@@ -595,10 +605,15 @@ 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

View File

@@ -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-2.0-flash-001`, or `anthropic/claude-sonnet-4-6`.
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`.
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-2.0-flash, anthropic/claude...
model="model-id-here", # gpt-4o, gemini/gemini-3.7-flash, anthropic/claude...
response_format=GuideOutline
)
response = llm.call(messages=messages)

View File

@@ -1,117 +1,148 @@
---
title: Channels
description: Run the same CrewAI agent as a chat bot on Slack and Discord with the CopilotKit Channels SDK.
icon: slack
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 bot process drives it.
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/reference/channels) provides that bot process. It ships a platform-agnostic engine plus per-platform adapters.
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 agent server changes. It keeps serving your Crew or Flow over AG-UI exactly as in the Overview. What you add is a separate **bot process**: it connects to a platform adapter, listens for messages, and runs your agent when it is messaged. The reply streams back into the channel.
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 / Discord ──► Channels bot process ──► CrewAI server (AG-UI) ──► Crew / Flow
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
```
Your agent server can keep serving the web frontend from the Overview at the same time. The web app and the bot are just two clients of one AG-UI endpoint.
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.
## Slack
## 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/channels-slack @ag-ui/crewai
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
```
</Step>
<Step title="Create a Slack app and get tokens">
<Step title="Create a Channel in Intelligence">
Create an app in the Slack API dashboard for your workspace, enable Socket Mode, and grant it the message and event scopes it needs to read and post in channels. Then expose its tokens to the bot process:
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 SLACK_BOT_TOKEN=xoxb-... # bot user token
export SLACK_APP_TOKEN=xapp-... # app-level token (Socket Mode)
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="Point the bot at your CrewAI agent">
<Step title="Define the channel">
`createBot` wires a Slack adapter to your agent. The `agent` factory returns a `CrewAIAgent` pointed at the AG-UI path your server exposes (the same URL you registered in the runtime in the Overview).
`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
// bot.ts
import { createBot } from "@copilotkit/channels";
import { slack, defaultSlackTools, defaultSlackContext } from "@copilotkit/channels-slack";
// channel.ts
import { createChannel } from "@copilotkit/channels";
import { CrewAIAgent } from "@ag-ui/crewai";
const bot = createBot({
adapters: [
slack({
botToken: process.env.SLACK_BOT_TOKEN!, // xoxb-…
appToken: process.env.SLACK_APP_TOKEN!, // xapp-… (Socket Mode)
}),
],
agent: (threadId) => new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
tools: [...defaultSlackTools],
context: [...defaultSlackContext],
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;
},
});
bot.start();
// 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="Run the bot">
<Step title="Register the channel on the runtime">
Start the bot process alongside your agent server:
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
node bot.ts # terminal 2 — Slack bot
npx tsx server.ts # terminal 2 — Channels runtime
```
Message the bot in Slack and it runs your Crew or Flow, streaming the reply back into the thread.
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>
<Note>
Slack app scopes, Socket Mode setup, and the full adapter options are maintained by CopilotKit. Follow the [Slack channel reference](https://docs.copilotkit.ai/reference/channels/slack) together with Slack's own app setup guide for the authoritative steps.
</Note>
## The event model
## Discord
A channel reacts to platform events with handlers, and each handler receives a `thread` you drive with a few methods:
Discord uses the same `createBot` engine with the Discord adapter from `@copilotkit/channels-discord`:
- **`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.
```ts
import { createBot } from "@copilotkit/channels";
import { discord } from "@copilotkit/channels-discord";
import { CrewAIAgent } from "@ag-ui/crewai";
const bot = createBot({
adapters: [discord({ token: process.env.DISCORD_BOT_TOKEN! })],
agent: (threadId) => new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
});
bot.start();
```
See the [Discord channel reference](https://docs.copilotkit.ai/reference/channels/discord) for the exact adapter options and bot setup.
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
Slack and Discord have official Channels adapters (`@copilotkit/channels-slack`, `@copilotkit/channels-discord`). Microsoft Teams is available through CopilotKit's managed offering (currently waitlisted). Check the [Channels reference](https://docs.copilotkit.ai/reference/channels) for the current list before promising a platform.
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

View File

@@ -21,7 +21,7 @@ The two connect through the [AG-UI protocol](https://docs.ag-ui.com). The `ag-ui
<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="slack" href="/edge/en/guides/frontend/channels">
<Card title="Channels" icon="messages" href="/edge/en/guides/frontend/channels">
Run the same agent as a Slack, Discord, or Teams bot.
</Card>
</CardGroup>

View File

@@ -176,7 +176,7 @@ grep -r "llm:" --include="*.yaml" .
# llm = LLM(model="mistral/mistral-large-latest")
# After (Native):
llm = LLM(model="gemini/gemini-2.0-flash")
llm = LLM(model="gemini/gemini-3.7-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-2.0-flash") # Fast & capable
llm = LLM(model="gemini/gemini-3.7-flash") # Fast & capable
# Mistral → Anthropic or OpenAI
# llm = LLM(model="mistral/mistral-large-latest")

View File

@@ -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-2.0-flash" # Add your Gemini model here, under openai/
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-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-2.0-flash",
model="openai/gemini-3.7-flash",
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key="your-gemini-key", # Should start with AIza...
)

View File

@@ -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-2.5-flash-preview-05-20", temperature=0.1)
manager_llm = LLM(model="gemini/gemini-3.7-flash", 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-2.5-flash-preview-05-20"), # Premium for coordination
llm=LLM(model="gemini/gemini-3.7-flash"), # Premium for coordination
# ... rest of config
)

View File

@@ -151,7 +151,7 @@ Conversational Flows can stream one user turn with `stream_turn()`:
```python
from crewai import Flow
from crewai.experimental.conversational import ConversationConfig, ConversationState
from crewai.flow import ConversationConfig, ConversationState
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)

View File

@@ -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 easily debug your agents.
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.
> **What is Arize Phoenix?** [Arize Phoenix](https://phoenix.arize.com) is an LLM observability platform that provides tracing and evaluation for AI applications.
> **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.
[![Watch a Video Demo of Our Integration with Phoenix](https://storage.googleapis.com/arize-assets/fixtures/setup_crewai.png)](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
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.
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.
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 Cloud credentials
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
# Get your Phoenix API key
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix 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" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
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["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.
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.
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.
![Example trace in Phoenix showing agent interactions](https://storage.googleapis.com/arize-assets/fixtures/crewai_traces.png)
@@ -147,6 +147,9 @@ Log into your Phoenix Cloud account and navigate to the project you specified in
### 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.

View File

@@ -23,7 +23,7 @@ usage of tools, API calls, responses, any data processed by the agents, or secre
When the `share_crew` feature is enabled, detailed data including task descriptions, agents' backstories or goals, and other specific attributes are collected
to provide deeper insights. This expanded data collection may include personal information if users have incorporated it into their crews or tasks.
Users should carefully consider the content of their crews and tasks before enabling `share_crew`.
Users can disable telemetry by setting the environment variable `CREWAI_DISABLE_TELEMETRY` to `true` or by setting `OTEL_SDK_DISABLED` to `true` (note that the latter disables all OpenTelemetry instrumentation globally).
Users can disable CrewAI telemetry by setting `CREWAI_DISABLE_TELEMETRY` to `true`, `1`, `yes`, or `on` (any case). `OTEL_SDK_DISABLED` with the same values also disables CrewAI's exporter. The OpenTelemetry SDK itself still only honors `true` for disabling other instrumentation in the process.
### Examples:
```python
@@ -34,6 +34,8 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
os.environ['OTEL_SDK_DISABLED'] = 'true'
```
`CREWAI_DISABLE_TELEMETRY=1` (or `yes` / `on`) works the same as `true`. Unrecognized values are ignored and leave telemetry on.
### Isolation from your own OpenTelemetry setup
CrewAI's telemetry runs on its own private `TracerProvider` and never registers
@@ -52,15 +54,16 @@ own tracer provider, which is independent of the one described here.
| 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), 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), 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 | 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. Stored as spans with timestamps. No personal data. |
| 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 | 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`), and the `project_id` from your `pyproject.toml` when one is configured. Detection reads only whether known environment variables are set, never their values. 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. |
| 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. |

View File

@@ -50,16 +50,15 @@ These tools integrate with AI and machine learning services to enhance your agen
- **AI Safety**: Implement content moderation and safety checks
```python
from crewai_tools import DallETool, VisionTool, CodeInterpreterTool
from crewai_tools import DallETool, VisionTool
# Create AI tools
image_generator = DallETool()
vision_processor = VisionTool()
code_executor = CodeInterpreterTool()
# Add to your agent
agent = Agent(
role="AI Specialist",
tools=[image_generator, vision_processor, code_executor],
tools=[image_generator, vision_processor],
goal="Create and analyze content using AI capabilities"
)

View File

@@ -4,6 +4,77 @@ 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

View File

@@ -630,6 +630,11 @@ messages = [
result = researcher.kickoff(messages)
```
마지막 `user` 메시지가 에이전트가 답변할 요청입니다. 나머지 메시지는 각자의 역할과
그 요청을 기준으로 한 위치를 그대로 유지하므로, assistant 또는 tool 메시지로 끝나는
대화도 사용자의 질문을 그대로 전달하며 뒤따르는 턴도 요청 뒤에 그대로 전달됩니다.
`user` 메시지가 전혀 없으면 마지막 메시지를 요청으로 처리합니다.
### 비동기 지원
동일한 매개변수를 사용하는 비동기 버전은 `kickoff_async()`를 통해 사용할 수 있습니다:

View File

@@ -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-2.0-flash")
memory = Memory(llm="gemini/gemini-3.7-flash")
# 사용자 정의 설정이 있는 사전 구성된 LLM 인스턴스 전달
llm = LLM(model="gpt-4o", temperature=0)

View File

@@ -26,7 +26,7 @@ CrewAI의 기본 프롬프트는 많은 시나리오에서 잘 작동하지만,
- **오류 처리** agent가 실패, 예외, 또는 타임아웃에 어떻게 반응할지 지정합니다.
- **도구별 prompt** 도구가 호출되거나 사용되는 방법에 대한 상세 지침을 정의합니다.
이 요소들이 어떻게 구성되어 있는지 보려면 [CrewAI 저장소의 원본 prompt 템플릿](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json)을 확인하세요. 여기서 필요에 따라 오버라이드하거나 수정하여 고급 동작을 구현할 수 있습니다.
이 요소들이 어떻게 구성되어 있는지 보려면 [CrewAI 저장소의 원본 prompt 템플릿](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/translations/en.json)을 확인하세요. 여기서 필요에 따라 오버라이드하거나 수정하여 고급 동작을 구현할 수 있습니다.
## 기본 시스템 지침 이해하기

View File

@@ -75,7 +75,7 @@ research_crew/
}
```
`provider/model-id`를 `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, `gemini/gemini-2.0-flash-001` 같은 모델로 바꾸세요.
`provider/model-id`를 `openai/gpt-4o`, `anthropic/claude-sonnet-4-6`, `gemini/gemini-3.7-flash` 같은 모델로 바꾸세요.
## 3단계: 태스크와 Crew 설정

View File

@@ -1,35 +1,37 @@
---
title: 대화형 Flow
description: 턴마다 kickoff, 메시지 기록, 의도 라우팅, 트레이싱, WebSocket 브리지로 멀티턴 채팅 앱을 만듭니다.
description: 턴별 handle_turn, 메시지 기록, 의도 라우팅, 트레이싱, 구조화된 스트리밍으로 멀티턴 채팅 앱을 만듭니다.
icon: comments
mode: "wide"
---
## 개요
대화형 앱은 각 사용자 입력을 **동일한 세션 id**로 **새 flow 실행**으로 처리합니다. CrewAI는 메시지 기록, 선택적 의도 분류, 지연 트레이싱, UI 브리지, 그리고 대화형 flow용 로컬 `flow.chat()` REPL을 제공합니다.
대화형 앱은 각 사용자 입력을 **동일한 세션 id**로 **새 flow 실행**으로 처리합니다. CrewAI는 메시지 기록, 선택적 의도 라우팅, 지연 트레이싱, 구조화된 턴 스트리밍, 로컬 `flow.chat()` REPL을 위한 헬퍼를 제공합니다.
| 개념 | 구현 |
|------|------|
| 세션 id | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
| 사용자 입력 | `handle_turn(message)`가 그래프 실행 전 `state.messages`에 추가 |
| 턴 완료 | `FlowFinished`는 **이번 실행**만 의미; 다음 `handle_turn`로 대화 계속 |
| 턴 완료 | `conversation_turn_completed`; 기본 trace 지연을 사용하면 `FlowFinished`는 `finalize_session_traces()`까지 대기 |
| 세션 전체 트레이스 | `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` | **스텝 출력** 승인/거부 — 다음 채팅 줄이 아님 |
| `ChatSession.handle_turn(...)` | `handle_turn` 위의 전송 계층 (SSE / WebSocket) |
대화형 모드가 활성화되지 않으면 `handle_turn()`, `stream_turn()`, `chat()`은 `ValueError`를 발생시킵니다. `@ConversationConfig(...)`를 적용하면 자동으로 활성화되며, 그렇지 않으면 `conversational = True`로 설정하세요.
## 빠른 시작
@@ -38,7 +40,7 @@ from uuid import uuid4
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
)
@@ -46,8 +48,6 @@ from crewai.experimental.conversational 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,35 +85,43 @@ 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. **`_configure_conversational_kickoff`** — `session_id` / `user_message`를 `inputs`에 병합, `ConversationalConfig` 적용, 설정 시 지연 트레이싱 활성화.
1. **턴 설정** — 보류 중인 사용자 메시지를 저장하고 세션 id를 결정하며 턴별 실행 추적을 초기화한 뒤 `kickoff(inputs={"id": session_id})`를 호출.
2. **상태 복원** — `inputs["id"]`가 있고 `@persist`가 설정되면 최신 스냅샷 로드.
3. **`FlowStarted`** — 지연 세션의 첫 턴에서만 발생.
4. **`prepare_conversational_turn`** — 사용자 메시지를 `state.messages`에 추가, `last_user_message` 설정, `last_intent` 초기화, `intents` / `default_intents` + `intent_llm` 설정 시 분류.
5. **그래프 실행** — `@start` → `@router` → `@listen` 핸들러.
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()` 헬퍼도 호출합니다.
6. **실행 종료** — 지연 활성화 시 턴별 `flow_finished` 및 trace 종료 **건너뜀**; 중첩 `Agent.kickoff()` / crew도 부모 batch를 닫지 않음.
핸들러는 **`append_assistant_message(reply)`**를 호출해 다음 턴의 `conversation_messages`에 어시스턴트 응답이 포함되게 하세요. 사용자 입력은 `handle_turn`이 이미 저장합니다 — 핸들러에서 다시 추가하지 마세요.
핸들러는 보이는 응답이 반환값과 다를 때, 또는 히스토리를 자를 때 **`append_assistant_message(reply)`**를 호출하세요. public 문자열 반환값도 assistant로 기록되며 `@persist` 스냅샷에 포함되므로, 새 Flow 인스턴스에서도 복원됩니다. 사용자 입력은 `handle_turn`이 이미 저장합니다 — 핸들러에서 다시 추가하지 마세요.
## `ConversationalConfig` (클래스 수준 기본값)
## 설정 개요
`Flow` 서브클래스에 `conversational_config: ClassVar[ConversationalConfig | None]`로 설정합니다.
`Flow` 서브클래스에 `ConversationConfig`를 데코레이터로 적용하면 채팅 기본값이 부착되고 대화형 모드도 활성화됩니다. 아래의 [전체 필드 레퍼런스](#conversationconfig)를 참고하세요. 턴마다 `handle_turn(..., intents=..., intent_llm=...)`로 사전 분류 설정을 재정의할 수 있습니다.
| 필드 | 기본값 | 목적 |
|------|--------|------|
| `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` 헬퍼
`intents=` 및 `intent_llm=` 키워드로 kickoff마다 재정의할 수 있습니다.
## `ChatState` (권장 persist 형태)
`ChatState`, 레거시 `ConversationalConfig`, `crewai.flow.conversation` 헬퍼는 고급 오케스트레이션, 테스트, 커스텀 래퍼에서 계속 import할 수 있습니다. 이들은 `ConversationState` / `ConversationConfig` API와 별개이며 `Flow.kickoff()`에 `user_message=` 또는 `session_id=` 키워드 인자를 추가하지 않습니다.
```python
from crewai.flow import ChatState
@@ -127,7 +135,7 @@ class MyChatState(ChatState):
| 필드 | 역할 |
|------|------|
| `id` | 세션 UUID (`session_id` / `inputs["id"]`와 동일) |
| `id` | 세션 UUID (`inputs["id"]`와 동일) |
| `messages` | LLM 기록용 `{role, content}` 리스트 |
| `last_user_message` | 이번 턴의 최신 사용자 입력 |
| `last_intent` | 분류 후 라우트 라벨 (사용 시) |
@@ -135,76 +143,77 @@ 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
### `kickoff` / `kickoff_async` 파라미터
### `handle_turn` 파라미터
| 파라미터 | 목적 |
|----------|------|
| `user_message` | 이번 턴 텍스트 (또는 `{"role": "user", "content": "..."}`) |
| `message` | 이번 턴 텍스트 |
| `session_id` | 대화 UUID → `inputs["id"]` / `state.id` |
| `intents` | kickoff 전 `classify_intent`용 outcome 라벨 |
| `intents` | kickoff 전 `classify_intent`용 결과 라벨 |
| `intent_llm` | 분류 LLM (`intents`와 함께 필수) |
| `interactive` | `ask()` CLI 루프 (로컬 데모 전용) |
| `interactive_prompt` | 대화형 모드 프롬프트 |
| `interactive_timeout` | 줄 단위 `ask()` 타임아웃 |
| `exit_commands` | 대화형 모드 종료 단어 |
| `inputs` | 추가 상태 필드 |
| `restore_from_state_id` | 다른 persist flow에서 fork 복원 |
| `**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()`을 사용하세요.
### 인스턴스 속성
| 속성 | 목적 |
|------|------|
| `conversational_config` | 클래스 수준 `ConversationalConfig` |
| `defer_trace_finalization` | 인스턴스 플래그; kickoff 시 config에서 자동 설정 |
| `suppress_flow_events` | 콘솔 flow 패널 숨김; **트레이싱은 계속 기록** |
| `stream` | 스트리밍; `ChatSession.handle_turn(..., stream=True)`와 함께 |
| `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()` 사용 |
### 메서드 및 프로퍼티
| 이름 | 설명 |
|------|------|
| `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 종료 지연 여부 |
| `_should_defer_trace_finalization()` | 턴별 trace 종료 지연 여부를 결정하는 고급/내부 hook |
| `input_history` | `ask()` 프롬프트/응답 감사 기록 |
### 모듈 헬퍼 (`crewai.flow.conversation`)
테스트 또는 커스텀 오케스트레이션용:
테스트 또는 커스텀 오케스트레이션을 위해 `crewai.flow.conversation`에서 import할 수 있습니다. 이 헬퍼들은 레거시 `ConversationalConfig` 형태를 사용합니다. 또한 `prepare_conversational_turn()`은 `last_intent`를 지우지만, `handle_turn()`은 router 컨텍스트로 보존합니다.
| 함수 | 설명 |
|------|------|
| `normalize_kickoff_inputs(...)` | 대화 kwargs를 `inputs`에 병합 |
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)` | 대화 kwargs를 `inputs`에 병합 |
| `get_conversation_messages(flow)` | 상태 또는 내부 버퍼에서 메시지 읽기 |
| `append_message(flow, ...)` | 인스턴스 메서드와 동일 |
| `prepare_conversational_turn(flow, ...)` | 턴 수화 (보통 kickoff가 호출) |
| `receive_user_message(flow, ...)` | 인스턴스 메서드와 동일 |
| `append_message(flow, role, content, **extra)` | 인스턴스 메서드와 동일 |
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | 커스텀 래퍼용 하위 수준 턴 수화 |
| `receive_user_message(flow, text, ...)` | 인스턴스 메서드와 동일 |
| `set_state_field(flow, name, value)` | dict 또는 Pydantic 상태 필드 설정 |
| `get_conversational_config(flow)` | 클래스 `conversational_config` 읽기 |
| `input_history_to_messages(entries)` | `input_history`를 LLM 메시지 형식으로 |
## 의도 라우팅 패턴
### A. `ConversationalConfig`로 사전 분류 (가장 단순)
### A. `ConversationConfig`로 사전 분류 (가장 단순)
`default_intents`와 `intent_llm` 설정. 각 kickoff가 `@router` 전에 분류; `route()`에서 `self.state.last_intent` 읽기.
`default_intents`와 `intent_llm` 설정하세요. 각 `handle_turn()`이 현재 메시지를 사전 분류합니다. 커스텀 `route_turn()`이 반환한 비어 있지 않은 결과가 우선하며, 그렇지 않으면 `route_conversation`이 현재 턴의 분류된 intent를 사용합니다.
### B. `@router` 내부에서 분류 (풍부한 프롬프트)
### B. `route_turn` 내부에서 분류 (풍부한 프롬프트)
`default_intents=None`으로 kickoff는 메시지만 추가. `route()`에서 커스텀 프롬프트 `classify_intent` 호출:
`default_intents=None`으로 설정하면 `handle_turn()`은 사용자 메시지만 추가합니다. `route_turn()`에서 커스텀 프롬프트나 설명과 함께 `classify_intent` 호출하세요:
```python
@router(bootstrap)
def route(self):
def route_turn(self, context):
intent = self.classify_intent(
self._routing_prompt(self.state.last_user_message),
self._routing_prompt(self.state.current_user_message),
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
llm="gpt-4o-mini",
)
self.state.last_intent = intent
return intent
@@ -214,69 +223,59 @@ def route(self):
## flow가 끝났지만 사용자는 계속 대화할 때
`FlowFinished`는 **이번 그래프 실행**이 완료됨을 의미합니다. 같은 `session_id`로 또 다른 `kickoff`로 대화가 이어집니다. `@persist` `messages`, 플래그, 컨텍스트를 복원합니다.
각 `handle_turn()`은 하나의 그래프 실행을 완료하며, 같은 `session_id`로 다음 `handle_turn()`을 호출해 대화를 이어갑니다. 기본 지연 trace 수명 주기에서는 해당 실행이 `conversation_turn_completed`를 발생시키고, `finalize_session_traces()`가 세션을 닫을 때 `FlowFinished`가 한 번 발생합니다. `@persist` `messages`, 플래그, 컨텍스트를 복원합니다.
**Persist 패턴:** 전체 `Flow` 클래스보다 **단일 종료 스텝**(예: `finalize`)에 `@persist`를 두는 것이 좋습니다. 클래스 수준 persist는 매 메서드 후 저장하며, `load_state`는 최신 행을 사용해 같은 턴의 핸들러 업데이트를 놓칠 수 있습니다.
후속 채팅 줄에 `@human_feedback`를 쓰지 마세요. 특정 스텝 출력을 사람이 승인해야 할 때만 사용하세요.
## 대화형 `Flow` (실험적)
## 대화형 `Flow`
<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 배치를 열린 상태로 유지합니다. 여러분은 **커스텀 라우트**만 작성하면 되고, 나머지는 프레임워크가 담당합니다.
`Flow` 서브클래스에 `conversational = True`를 지정하거나 `@ConversationConfig(...)`를 적용하면 대화형 채팅 그래프가 활성화됩니다. 베이스 `Flow`는 내장 start/router인 `route_conversation`과 `converse_turn`, `end_conversation` 리스너를 제공합니다. 사용 중단된 `answer_from_history_turn` 리스너는 호환성을 위해 계속 제공됩니다. 또한 `state.messages`를 관리하고 router LLM을 구동할 수 있으며 턴 간 trace batch를 열린 상태로 유지합니다. 여러분은 **커스텀 라우트**를 작성하고 나머지는 프레임워크에 맡기면 됩니다.
LLM 기반 라우터와 라우트별 핸들러로 멀티턴 챗을 만들고 싶지만 라이프사이클을 직접 배선하고 싶지 않을 때 사용하세요. 완전한 제어가 필요하면 위의 `Flow[ChatState]`로 내려가세요.
### 빠른 예제
```python
from crewai import LLM, Flow
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
RouterConfig,
)
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 핸들러에서 자동 발견
)
@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:
return "INTERNET_SEARCH"
if "docs" in message or "crewai" in message:
return "CREWAI_DOCS"
return "converse"
@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("뭘 할 수 있어?") # converse(빌트인)로 라우팅
flow.handle_turn("AI 뉴스를 웹에서 찾아줘.") # INTERNET_SEARCH로 라우팅
flow.handle_turn("첫 번째 결과를 요약해줘.") # 다시 converse로 라우팅
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
finally:
flow.finalize_session_traces()
```
@@ -298,27 +297,53 @@ def kickoff() -> None:
|------|--------|------|
| `system_prompt` | i18n `slices.conversational_system_prompt` | 빌트인 `converse_turn`이 사용하는 system 메시지. 빈 문자열(`""`)을 전달하면 system 메시지를 끕니다. |
| `llm` | `None` | 대화용 LLM (빌트인 `converse_turn`이 사용하고 router 폴백도 됨). |
| `router` | `None` | LLM 기반 라우팅을 위한 `RouterConfig`. 없으면 항상 `converse`로 떨어집니다. |
| `answer_from_history_prompt` | 프레임워크 기본값 | 선택적인 `answer_from_history` 라우트용 system 메시지. |
| `answer_from_history_llm` | `None` | 설정되면 `answer_from_history` 단축 경로가 활성화됩니다. |
| `router` | `None` | 선택적 `RouterConfig` 재정의. 커스텀 listener와 결정 가능한 LLM이 있으면 생략해도 라우팅이 자동 활성화됩니다. |
| `answer_from_history_prompt` | 프레임워크 기본값 | **사용 중단됨.** `converse` system prompt를 사용하거나 `converse_turn()`을 재정의하세요. |
| `answer_from_history_llm` | `None` | **사용 중단됨.** `llm`을 사용하세요. `converse`는 이미 정식 기록을 전달받습니다. |
| `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
RouterConfig(
prompt="선택적인 도메인 프레이밍 (정책, 톤, 페르소나).",
response_format=MyRoute, # 선택; 없으면 자동 생성
llm=ROUTER_LLM, # ConversationConfig.llm으로 폴백
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # 선택; 리스너에서 추론
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
route_descriptions={
"INTERNET_SEARCH": "이 라우트만 docstring 대신 사용할 설명.",
"INTERNET_SEARCH": "Override the docstring for this one route.",
},
default_intent="converse", # LLM 호출 실패 또는 LLM 없음일 때 사용
fallback_intent="converse", # LLM이 잘못된 라우트를 반환할 때 사용
default_intent="converse", # used when LLM call fails or no LLM available
fallback_intent="converse", # used when LLM returns an invalid route
intent_field="intent",
)
```
@@ -326,9 +351,10 @@ RouterConfig(
router에 전달되는 프롬프트는 자동으로 만들어집니다. 각 라우트의 설명은 다음 우선순위로 결정됩니다:
1. `RouterConfig.route_descriptions[label]` — 명시적 오버라이드.
2. `Flow.builtin_route_descriptions[label]` — `converse`, `end`, `answer_from_history`용 프레임워크 캐닝 텍스트 (router LLM용으로 다듬어진 문구).
3. `@listen(label)` 핸들러 docstring의 첫 줄(비어있지 않은 줄).
4. 빈 문자열 (라우트만 카탈로그에 등장하고 설명은 없음).
2. `Flow.builtin_route_descriptions[label]` — `converse`, `end`, 사용 중단된 `answer_from_history` 호환 라우트용 프레임워크 기본 텍스트 (router LLM용으로 다듬어진 문구).
3. 메서드에 선언된 `description` — 선언적 flow와 DSL projection에서 사용.
4. `@listen(label)` 핸들러 docstring의 첫 번째 비어 있지 않은 줄.
5. 빈 문자열 — 설명 없이 라우트만 표시.
실제 사용에서 **새 라우트를 추가하는 방법은 `@listen("X")` + 한 줄짜리 docstring**입니다:
@@ -339,6 +365,27 @@ 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은 다음을 봅니다:
```
@@ -357,7 +404,7 @@ Routes:
|--------|--------|------|
| `converse` | `converse_turn` | 기본 챗 핸들러. system prompt + 정식 메시지 히스토리와 함께 `ConversationConfig.llm`을 호출합니다. |
| `end` | `end_conversation` | `state.ended = True`로 설정하고 종료 응답을 보냅니다. |
| `answer_from_history` | `answer_from_history_turn` | 선택적. `ConversationConfig.answer_from_history_llm`이 설정되어 있고 메시지를 히스토리만으로 답할 수 있을 때 라우팅됩니다. |
| `answer_from_history` | `answer_from_history_turn` | **사용 중단된 호환 라우트.** 이미 정식 기록을 전달받는 `converse`를 사용하세요. |
서브클래스에 같은 이름의 핸들러를 정의하면 어떤 것이든 오버라이드할 수 있습니다.
@@ -367,9 +414,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. `conversation_start` → `route_conversation` 선택된 `@listen` 핸들러 순으로 실행됩니다.
3. 사용자 정의 `@start` 메서드(있는 경우)를 실행한 다음 내장 start/router인 `route_conversation`을 거쳐 선택된 `@listen` 핸들러를 실행합니다. `route_conversation`은 재정의 가능한 `conversation_start()` 헬퍼를 호출합니다.
4. router는 결정을 `state.last_intent`에 저장합니다 (다음 턴의 router 컨텍스트에서 보입니다).
5. 핸들러가 문자열을 반환했지만 `append_assistant_message`를 직접 호출하지 않았다면, `handle_turn`이 대신 추가해 줍니다.
5. 핸들러가 문자열을 반환했지만 `append_assistant_message`를 직접 호출하지 않았다면, `handle_turn`이 대신 추가한 뒤 갱신된 `state.messages`를 persist합니다. `@persist` 복원 시 assistant 턴이 포함됩니다.
채팅 메시지에는 `handle_turn()`을 호출하세요. `kickoff(inputs={"id": ...})`를 직접 호출하면 대화형 턴 래퍼 없이 flow 그래프가 실행됩니다.
@@ -390,6 +437,8 @@ flow.chat()
4. 어시스턴트 결과를 출력합니다.
5. `finally` 블록에서 지연된 세션 trace를 finalize합니다.
`chat(defer_trace_finalization=True)`는 REPL 동안 인스턴스의 지연 플래그를 임시로 활성화하고 종료할 때 이전 값으로 복원합니다.
주입 가능한 I/O로 터미널 동작을 커스터마이즈할 수 있습니다:
```python
@@ -408,6 +457,12 @@ flow.chat(
매 라우팅 결정마다 사이드 이펙트(이벤트 버스 셋업, 텔레메트리)를 실행하려면 `route_turn`을 오버라이드하세요:
```python
from typing import Any
from crewai import Flow
from crewai.flow import ConversationState
class SupportFlow(Flow[ConversationState]):
conversational = True
@@ -416,7 +471,7 @@ class SupportFlow(Flow[ConversationState]):
return super().route_turn(context)
```
LLM router를 우회해 프로그램으로 라우트를 선택하려면 `route_turn`에서 문자열을 반환하세요. `None`을 반환하면 `_route_with_config(...)`로 떨어집니다.
LLM router를 완전히 우회하고 프로그램 방식으로 라우트를 선택하려면 `route_turn`에서 비어 있지 않은 문자열을 반환하세요. falsy 값을 반환해도 오버라이드에서 `_route_with_config()`가 호출되지는 않습니다. 대신 현재 턴의 사전 분류된 intent, 설정된 경우 사용 중단된 `answer_from_history` 호환 경로, 마지막으로 `converse` 순으로 fallback합니다. 이전 턴의 `last_intent`는 router 컨텍스트에서 사용할 수 있지만 fallback으로 다시 실행되지는 않습니다.
### `append_assistant_message`와 `append_agent_result`
@@ -429,7 +484,7 @@ LLM router를 우회해 프로그램적으로 라우트를 선택하려면 `rout
## JSON/YAML로 대화형 플로우 선언하기
[선언적 플로우](/edge/en/concepts/cli)도 대화형이 될 수 있습니다. 최상위 `conversational` 블록을 추가하고, 라우트 레이블을 `listen`하는 메서드로 직접 라우트를 선언하세요:
[선언적 Flow](/edge/ko/concepts/cli)도 대화형으로 만들 수 있습니다. 최상위 `conversational` 블록을 추가하고 라우트 레이블을 `listen`하는 메서드로 자체 라우트를 선언하세요:
```yaml
schema: crewai.flow/v1
@@ -454,15 +509,17 @@ methods:
input: "${state.current_user_message}"
```
블록 선언하는 것 자체가 옵트인입니다 — `enabled`의 기본값은 `true`입니다. 설정은 유지하면서 채팅 끄려면 `enabled: false`로 지정하세요.
블록 선언 자체가 opt-in이며 `enabled`의 기본값은 `true`입니다. 설정은 유지하면서 채팅 끄려면 `enabled: false`로 지정하세요. 이 경우 내장 메서드 합성도 비활성화되므로 선언에 일반 비대화형 그래프를 제공해야 합니다.
세 가지가 자동으로 제공됩니다:
| 제공 항목 | 설명 |
|----------|--------|
| 내장 그래프 | `route_conversation`, `converse_turn`, `end_conversation`, `answer_from_history_turn`이 자동으로 추가됩니다. 같은 이름 메서드를 선언하면 재정의됩니다. |
| 대화 상태 | 선언에 `state` 블록이 없으면 `ConversationState`가 사용됩니다. 필드를 추가하려면 `ConversationState`를 상속한 Pydantic 모델을 `state`에 지정하세요. |
| 라우트 카탈로그 | `listen` 레이블을 선언한 메서드들로부터 구성됩니다. 각 메서드의 `description`이 라우팅 모델이 라우트를 선택할 때 읽는 내용입니다. |
| 내장 그래프 | `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에서 실행합니다:
@@ -485,15 +542,16 @@ finally:
| 표현 불가 | 대신 사용 |
|-----------------|-------------|
| 살아 있는 `LLM` 인스턴스나 커스텀 `BaseLLM` | `gpt-4o-mini` 같은 모델 ID 문자열 |
| 모델 클래스로서의 `router.response_format` | 생략하세요; 프레임워크가 생성합니다. ref나 스키마는 경고와 함께 무시됩니다 |
| `route_turn()` / `can_answer_from_history()` 재정의 | 플로우를 Python으로 작성하거나, 메서드의 `do`를 `call: code` ref로 지정하세요 |
| 살아 있는 `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`에는 아직 채팅 루프가 없습니다: 단일 턴을 실행하는 대신 플로우가 대화형임을 알리고 종료니다. 선언적 대화형 플로우는 Python에서 `handle_turn()`, `stream_turn()`, `chat()`으로 실행하세요.
`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` (`ConversationalConfig` 기본값):
`defer_trace_finalization=True` (`ConversationConfig` 기본값):
- 채팅 세션 전체에 **하나의 trace batch**.
- 첫 턴에만 **`flow_started`**; `finalize_session_traces()`에서 **`flow_finished`** 한 번.
@@ -504,17 +562,30 @@ finally:
flow.chat(session_id=session_id)
```
`flow.chat()`이 `finalize_session_traces()`를 대신 호출합니다. `handle_turn()`이나 `kickoff(...)`로 직접 루프를 소유하는 경우, 세션이 끝날 때 `finalize_session_traces()`를 호출하세요.
`flow.chat()`이 `finalize_session_traces()`를 대신 호출합니다. `handle_turn()`로 직접 루프를 소유하는 경우 세션이 끝날 때 `finalize_session_traces()`를 호출하세요.
`suppress_flow_events=True`는 Rich 콘솔 패널깁니다. trace 및 method 이벤트는 계속 발생합니다.
`suppress_flow_events=True`는 Rich 콘솔 패널기고 메서드 실행 이벤트를 억제합니다. Flow start/finish 이벤트는 계속 발생하므로 바깥쪽 Flow 수명 주기는 추적할 수 있지만 개별 메서드 span은 생략됩니다.
### 대화형 `Flow` trace 수명 주기
실험적 [대화형 `Flow`](#대화형-flow-실험적)는 동일한 tracing 수명 주기를 따릅니다. `defer_trace_finalization` 기본값이 `True`이므로 각 `handle_turn()` 세션 trace를 열어 둡니다. 세션 끝에서 항상 finalize하세요 — REPL/루프 `try/finally`로 감싸고 종료 시 `flow.finalize_session_traces()`를 호출하세요. 호출하지 않으면 batch가 열린 채 남아 마지막 대화가 export되지 않을 수 있습니다.
[대화형 `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` 클래스에 `stream = True`. `kickoff(...)`가 표준 이벤트 버스를 통해 `assistant_delta` 등 이벤트를 발생시킵니다.
대화형 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()`이 관리합니다.
## import
@@ -529,10 +600,15 @@ 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`

View File

@@ -135,7 +135,7 @@ crewai flow add-crew content-crew
}
```
`provider/model-id`를 사용하는 모델로 바꾸세요. 예: `openai/gpt-4o`, `gemini/gemini-2.0-flash-001`, `anthropic/claude-sonnet-4-6`.
`provider/model-id`를 사용하는 모델로 바꾸세요. 예: `openai/gpt-4o`, `gemini/gemini-3.7-flash`, `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-2.0-flash, anthropic/claude...
model="model-id-here", # gpt-4o, gemini/gemini-3.7-flash, anthropic/claude...
response_format=GuideOutline
)
response = llm.call(messages=messages)

View File

@@ -0,0 +1,156 @@
---
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>

View File

@@ -0,0 +1,238 @@
---
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>

View File

@@ -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-2.0-flash" # Gemini 모델을 여기에 추가하세요. openai/ 하위에 위치.
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-flash" # Gemini 모델을 여기에 추가하세요. openai/ 하위에 위치.
```
</CodeGroup>
</Tab>
@@ -159,7 +159,7 @@ OpenAI 호환 LLM에 연결하려면 환경 변수를 사용하거나 LLM 클래
```python Google
# Gemini의 OpenAI 호환 API 예시
llm = LLM(
model="openai/gemini-2.0-flash",
model="openai/gemini-3.7-flash",
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key="your-gemini-key", # AIza...로 시작해야 합니다.
)

View File

@@ -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-2.5-flash-preview-05-20", temperature=0.1)
manager_llm = LLM(model="gemini/gemini-3.7-flash", 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-2.5-flash-preview-05-20"), # 조율을 위한 프리미엄
llm=LLM(model="gemini/gemini-3.7-flash"), # 조율을 위한 프리미엄
# ... 나머지 설정
)

View File

@@ -151,7 +151,7 @@ result = stream.result
```python
from crewai import Flow
from crewai.experimental.conversational import ConversationConfig, ConversationState
from crewai.flow import ConversationConfig, ConversationState
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)

View File

@@ -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://phoenix.arize.com)는 AI 애플리케이션을 위한 추적 및 평가 기능을 제공하는 LLM 가시성(observability) 플랫폼입니다.
> **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/)를 사용하세요.
[![Phoenix와의 통합 영상 데모 보기](https://storage.googleapis.com/arize-assets/fixtures/setup_crewai.png)](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
### 2단계: 환경 변수 설정
Phoenix Cloud API 키를 설정하고 OpenTelemetry를 구성하여 추적 정보를 Phoenix로 전송합니다. Phoenix Cloud는 Arize Phoenix의 호스팅 버전이지만, 이 통합을 사용하는 데 필수는 아닙니다.
Phoenix API 키와 OpenTelemetry endpoint를 구성하여 추적 정보를 Phoenix로 전송합니다. collector URL을 변경하면 동일한 설정을 로컬 또는 self-hosted Phoenix endpoint와 함께 사용할 수 있습니다.
무료 Serper API 키는 [여기](https://serper.dev/)에서 받을 수 있습니다.
@@ -35,8 +35,8 @@ Phoenix Cloud API 키를 설정하고 OpenTelemetry를 구성하여 추적 정
import os
from getpass import getpass
# Get your Phoenix Cloud credentials
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix Cloud API Key: ")
# Get your Phoenix API key
PHOENIX_API_KEY = getpass("🔑 Enter your Phoenix 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" # Phoenix Cloud, change this to your own endpoint if you are using a self-hosted instance
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["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 Cloud 계정에 로그인한 다음 `project_name` 파라미터에서 지정한 프로젝트로 이동하세요. 모든 에이전트 상호작용, 도구 사용 및 LLM 호출이 포함된 트레이스의 타임라인 보기를 확인할 수 있습니다.
Phoenix 프로젝트를 열고 `project_name` 파라미터에서 지정한 프로젝트로 이동하세요. 모든 에이전트 상호작용, 도구 사용 및 LLM 호출이 포함된 트레이스의 타임라인 보기를 확인할 수 있습니다.
![Phoenix에서 에이전트 상호작용을 보여주는 예시 트레이스](https://storage.googleapis.com/arize-assets/fixtures/crewai_traces.png)
@@ -145,6 +145,9 @@ Phoenix Cloud 계정에 로그인한 다음 `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 소스 코드.

View File

@@ -22,7 +22,7 @@ CrewAI는 익명 텔레메트리를 활용하여 사용 통계를 수집하며,
`share_crew` 기능이 활성화되면, 보다 심층적인 통찰을 제공하기 위해 작업 설명, 에이전트의 배경 이야기나 목표, 기타 특정 속성 등 상세한 데이터가 수집됩니다.
이 확대된 데이터 수집에는 사용자가 crew나 작업에 개인정보를 포함한 경우, 개인정보가 포함될 수 있습니다.
사용자는 `share_crew`를 활성화하기 전에 crew와 작업의 내용을 신중하게 검토해야 합니다.
사용자는 환경 변수 `CREWAI_DISABLE_TELEMETRY`를 `true`로 설정하거나, `OTEL_SDK_DISABLED`를 `true`로 설정하여 텔레메트리를 비활성화할 수 있습니다(후자의 경우 전체 OpenTelemetry 계측이 전역에서 비활성화된다는 점에 유의하십시오).
사용자는 `CREWAI_DISABLE_TELEMETRY`를 `true`, `1`, `yes`, `on` 중 하나로 설정하여 CrewAI 텔레메트리를 비활성화할 수 있습니다(대소문자 무관). 같은 값의 `OTEL_SDK_DISABLED`도 CrewAI exporter를 끕니다. 프로세스 내 다른 OpenTelemetry 계측을 끄려면 OpenTelemetry SDK는 여전히 `true`만 인식합니다.
### 예시:
```python
@@ -33,6 +33,8 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
os.environ['OTEL_SDK_DISABLED'] = 'true'
```
`CREWAI_DISABLE_TELEMETRY=1`(`yes` / `on`도 동일)은 `true`와 같습니다. 인식되지 않는 값은 무시되며 텔레메트리는 켜진 채로 남습니다.
### 사용자 OpenTelemetry 설정과의 격리
CrewAI의 telemetry는 자체 전용 `TracerProvider`에서 실행되며 자신을 전역
@@ -50,15 +52,16 @@ provider로 등록하지 않습니다. 이를 통해 양방향이 분리됩니
| 기본값 | 데이터 | 사유 및 세부 사항 |
|:--------|:-------------------------------------------|:----------------------------------------------------------------------------------------------------------------------|
| 예 | CrewAI 및 Python 버전 | 소프트웨어 버전을 추적합니다. 예: CrewAI v1.2.3, Python 3.8.10. 개인 정보 없음. |
| 예 | Crew 메타데이터 | 랜덤으로 생성된 키 및 ID, 프로세스 유형(예: 'sequential', 'parallel'), 메모리 사용 플래그(boolean, true/false), 작업 수, 에이전트 수가 포함됩니다. 모두 비개인 정보입니다. |
| 예 | Crew 메타데이터 | 랜덤으로 생성된 키 및 ID, 프로세스 유형(예: 'sequential', 'parallel'), 메모리 사용 플래그(boolean, true/false), 실행에 입력이 전달되었는지를 나타내는 플래그(boolean, true/false — 입력 키나 값 자체는 포함되지 않으며, 이는 `share_crew`가 활성화된 경우에만 수집됩니다), 작업 수, 에이전트 수가 포함됩니다. 모두 비개인 정보입니다. |
| 예 | 에이전트 데이터 | 랜덤으로 생성된 키 및 ID, 역할 이름(개인 정보 포함 불가), boolean 설정(상세 출력, 위임 가능, 코드 실행 허용), 최대 반복 횟수, 최대 RPM, 최대 재시도 제한, LLM 정보(LLM 속성 참조), 도구 이름 목록(개인 정보 포함 불가) 포함. 개인 정보 없음. |
| 예 | 작업 메타데이터 | 랜덤으로 생성된 키 및 ID, boolean 실행 설정(async_execution, human_input), 관련 에이전트 역할 및 키, 도구 이름 목록이 포함됩니다. 모두 비개인 정보입니다. |
| 예 | 도구 사용 통계 | 도구 이름(개인 정보 포함 불가), 사용 시도 횟수(정수), 사용된 LLM 속성이 포함됩니다. 개인 정보 없음. |
| 예 | 테스트 실행 데이터 | crew의 랜덤 생성 키와 ID, 반복 횟수, 사용된 모델명, 품질 점수(실수), 실행 시간(초 단위)이 포함됩니다. 모두 비개인 정보입니다. |
| 예 | 작업 라이프사이클 데이터 | 생성 및 실행 시작/종료 시각, crew 및 작업 식별자가 포함됩니다. 타임스탬프를 포함한 span으로 저장됩니다. 개인 정보 없음. |
| 예 | 작업 라이프사이클 데이터 | 생성 및 실행 시작/종료 시각, crew 및 작업 식별자, 그리고 작업의 성공 또는 실패 여부가 포함됩니다. 작업이 실패하면 실패를 집계하고 진단할 수 있도록 예외의 **클래스 이름**(예: `TimeoutError`)이 기록되며, 프롬프트·모델 출력·파일 경로·자격 증명이 포함될 수 있는 오류 메시지는 결코 기록되지 않습니다. 타임스탬프를 포함한 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`. 감지는 알려진 환경 변수의 설정 여부만 확인하 값은 읽지 않음. 개인 데이터 없음. |
| 예 | 실행 환경 | 포함: 프로세스를 실행 중인 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 편집 및 보존 설정을 검토하세요. |
| 아니오 | 에이전트 확장 데이터 | 목표 설명, 배경 이야기 텍스트, i18n 프롬프트 파일 식별자가 포함됩니다. 사용자들은 텍스트 필드에 개인 정보가 포함되지 않도록 해야 합니다. |

View File

@@ -48,17 +48,16 @@ mode: "wide"
- **AI 안전성**: 콘텐츠 모더레이션 및 안전성 점검 구현
```python
from crewai_tools import DallETool, VisionTool, CodeInterpreterTool
from crewai_tools import DallETool, VisionTool
# Create AI tools
image_generator = DallETool()
vision_processor = VisionTool()
code_executor = CodeInterpreterTool()
# Add to your agent
agent = Agent(
role="AI Specialist",
tools=[image_generator, vision_processor, code_executor],
tools=[image_generator, vision_processor],
goal="Create and analyze content using AI capabilities"
)
```

View File

@@ -4,6 +4,77 @@ 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

View File

@@ -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-2.0-flash")
memory = Memory(llm="gemini/gemini-3.7-flash")
# Passar uma instância LLM pré-configurada com configurações customizadas
llm = LLM(model="gpt-4o", temperature=0)

View File

@@ -26,7 +26,7 @@ Nos bastidores, o CrewAI adota um sistema de prompt modular que pode ser amplame
- **Tratamento de erros** Definem como os agentes respondem a falhas, exceções ou timeouts.
- **Prompts específicos de ferramentas** Definem instruções detalhadas para como as ferramentas são invocadas ou utilizadas.
Confira os [templates de prompt originais no repositório do CrewAI](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json) para ver como esses elementos são organizados. A partir daí, você pode sobrescrever ou adaptar conforme necessário para desbloquear comportamentos avançados.
Confira os [templates de prompt originais no repositório do CrewAI](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai/src/crewai/translations/en.json) para ver como esses elementos são organizados. A partir daí, você pode sobrescrever ou adaptar conforme necessário para desbloquear comportamentos avançados.
## Entendendo as Instruções de Sistema Padrão

View File

@@ -77,7 +77,7 @@ Substitua o arquivo gerado `agents/researcher.jsonc` e adicione `agents/analyst.
}
```
Substitua `provider/model-id` pelo modelo usado, como `openai/gpt-4o`, `anthropic/claude-sonnet-4-6` ou `gemini/gemini-2.0-flash-001`.
Substitua `provider/model-id` pelo modelo usado, como `openai/gpt-4o`, `anthropic/claude-sonnet-4-6` ou `gemini/gemini-3.7-flash`.
## Etapa 3: Definir tarefas e configurações

View File

@@ -1,35 +1,37 @@
---
title: Flows Conversacionais
description: Crie apps de chat multi-turno com kickoff por turno, histórico de mensagens, roteamento de intenção, tracing e pontes WebSocket.
description: Crie apps de chat multi-turno com handle_turn por turno, histórico de mensagens, roteamento de intenção, tracing e streaming estruturado.
icon: comments
mode: "wide"
---
## Visão geral
Apps conversacionais tratam cada linha do usuário como uma **nova execução do flow** com o **mesmo id de sessão**. A CrewAI oferece helpers para histórico de mensagens, classificação opcional de intenção, tracing adiado, pontes para UI e um REPL local `flow.chat()` para flows conversacionais.
Apps conversacionais tratam cada linha do usuário como uma **nova execução do flow** com o **mesmo id de sessão**. A CrewAI oferece helpers para histórico de mensagens, roteamento opcional de intenção, tracing adiado, streaming estruturado de turnos e um REPL local `flow.chat()`.
| Conceito | Implementação |
|---------|----------------|
| Id de sessão | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id` |
| Linha do usuário | `handle_turn(message)` acrescenta em `state.messages` antes do grafo rodar |
| Fim do turno | `FlowFinished` só para **esta execução**; o chat segue no próximo `handle_turn` |
| Trace da sessão | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
| Turno concluído | `conversation_turn_completed`; com o adiamento padrão de traces, `FlowFinished` aguarda `finalize_session_traces()` |
| Trace da sessão inteira | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()` |
## APIs de turno
Use **`flow.handle_turn(message, session_id=...)`** para cada mensagem de usuário em REST, WebSocket, testes e UIs customizadas. Use **`flow.chat()`** quando quiser um loop de chat local no terminal para um `Flow` conversacional.
`Flow.kickoff()` não aceita os argumentos nomeados `user_message=` ou `session_id=`. Para flows conversacionais, `handle_turn()` guarda a mensagem pendente e chama `kickoff(inputs={"id": session_id})` internamente.
`Flow.kickoff()` não aceita os argumentos nomeados `user_message=` ou `session_id=`. Para flows conversacionais, `handle_turn()` guarda a mensagem pendente e chama `kickoff(inputs={"id": session_id})` internamente depois de redefinir o estado de execução do turno.
| API | Uso |
|-----|-----|
| `handle_turn(message, session_id=...)` | Wrapper ergonômico de um turno para `Flow` conversacional |
| `stream_turn(message, session_id=...)` | Transmite um turno conversacional como frames ordenados do runtime |
| `chat()` | REPL local no terminal para `Flow` conversacional |
| `kickoff(inputs={...})` | Execução avançada do flow sem tratamento de turno conversacional |
| `ask()` | Prompt bloqueante **dentro** de um passo (wizard, esclarecimento) |
| `@human_feedback` | Aprovar/rejeitar **saída de um passo** — não a próxima linha do chat |
| `ChatSession.handle_turn(...)` | Camada de transporte sobre `handle_turn` (SSE / WebSocket) |
`handle_turn()`, `stream_turn()` e `chat()` geram `ValueError` se o modo conversacional não estiver habilitado. Aplicar `@ConversationConfig(...)` o habilita automaticamente; caso contrário, defina `conversational = True`.
## Início rápido
@@ -38,7 +40,7 @@ from uuid import uuid4
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
)
@@ -46,31 +48,29 @@ from crewai.experimental.conversational 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 "pedido" in message or "order" in message:
if "order" in message:
return "order"
if "tchau" in message or "goodbye" in message:
if "bye" in message or "goodbye" in message:
return "goodbye"
return "help"
@listen("order")
def handle_order(self):
reply = "Seu pedido está a caminho."
reply = "Your order is on the way."
self.append_assistant_message(reply)
return reply
@listen("help")
def handle_help(self):
reply = "Como posso ajudar?"
reply = "How can I help?"
self.append_assistant_message(reply)
return reply
@listen("goodbye")
def handle_goodbye(self):
reply = "Até logo!"
reply = "Goodbye!"
self.append_assistant_message(reply)
return reply
@@ -79,41 +79,49 @@ session_id = str(uuid4())
flow = SupportFlow()
try:
flow.handle_turn("Onde está meu pedido?", session_id=session_id)
flow.handle_turn("E as devoluções?", session_id=session_id)
flow.handle_turn("Where is my order?", session_id=session_id)
flow.handle_turn("What about returns?", session_id=session_id)
finally:
flow.finalize_session_traces() # um link de trace para o chat inteiro
flow.finalize_session_traces() # one trace link for the whole chat
```
## Streaming de um turno
Use `stream_turn()` quando uma UI ou um runtime precisar de eventos estruturados para um turno de chat. Ele retorna uma sessão de stream com frames ordenados para roteamento do Flow, chunks do LLM, atividade de tools e mensagens da conversa.
```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
```
Para o contrato completo dos frames e a lista de canais, consulte [Contrato do Runtime de Streaming](/edge/pt-BR/learn/streaming-runtime-contract).
## Ciclo de vida do turno
Cada `handle_turn` executa este pipeline:
1. **`_configure_conversational_kickoff`** — mescla `session_id` / `user_message` em `inputs`, aplica `ConversationalConfig`, habilita tracing adiado quando configurado.
1. **Preparação do turno** — armazena a mensagem pendente do usuário, resolve o id da sessão, redefine o acompanhamento de execução por turno e chama `kickoff(inputs={"id": session_id})`.
2. **Restauração de estado** — se `inputs["id"]` existe e `@persist` está configurado, carrega o snapshot mais recente.
3. **`FlowStarted`** — emitido apenas no primeiro turno da sessão adiada.
4. **`prepare_conversational_turn`** — acrescenta a mensagem do usuário em `state.messages`, define `last_user_message`, limpa `last_intent`, classifica opcionalmente quando `intents` / `default_intents` + `intent_llm` estão definidos.
5. **Execução do grafo** — `@start` → `@router` → handlers `@listen`.
4. **Hidratação do turno pendente** — acrescenta a mensagem do usuário em `state.messages`, define `current_user_message` / `last_user_message` e classifica opcionalmente quando `intents` / `default_intents` + `intent_llm` estão definidos.
5. **Execução do grafo** — métodos `@start` definidos pelo usuário (se houver) → `route_conversation` (o start/router embutido) → o handler `@listen` selecionado. `route_conversation` também chama o helper sobrescrevível `conversation_start()`.
6. **Fim da execução** — `flow_finished` por turno e finalização de trace são **ignorados** com adiamento; `Agent.kickoff()` / crews aninhados também não fecham o batch pai.
Os handlers devem chamar **`append_assistant_message(reply)`** para que o próximo turno inclua a resposta do assistente. A linha do usuário já é salva por `handle_turn` — não acrescente de novo nos handlers.
Os handlers devem chamar **`append_assistant_message(reply)`** quando a resposta visível não for o valor de retorno, ou ao recortar o histórico. Um retorno de string pública também é gravado como assistente e entra no snapshot `@persist`, então uma nova instância de Flow o restaura. A linha do usuário já é salva por `handle_turn` — não acrescente de novo nos handlers.
## `ConversationalConfig` (padrões em nível de classe)
## Visão geral da configuração
Defina na subclasse de `Flow` como `conversational_config: ClassVar[ConversationalConfig | None]`.
Decorar uma subclasse de `Flow` com `ConversationConfig` anexa os padrões de chat e habilita o modo conversacional. Consulte a [referência completa de campos](#conversationconfig) abaixo. Sobrescreva a pré-classificação por turno com `handle_turn(..., intents=..., intent_llm=...)`.
| Campo | Padrão | Propósito |
|-------|---------|-----------|
| `default_intents` | `None` | Rótulos de outcome para classificação automática antes do kickoff |
| `intent_llm` | `None` | Modelo para classificação (obrigatório quando há intents) |
| `interactive_prompt` | `"You: "` | Prompt para `kickoff(interactive=True)` |
| `interactive_timeout` | `None` | Timeout por linha no modo interativo |
| `exit_commands` | `exit`, `quit` | Palavras que encerram o modo interativo |
| `defer_trace_finalization` | `True` | Manter um batch de trace aberto entre turnos |
## Helpers `ChatState` de mais baixo nível
Sobrescreva por kickoff com `intents=` e `intent_llm=`.
## `ChatState` (formato persistido recomendado)
`ChatState`, o `ConversationalConfig` legado e os helpers de `crewai.flow.conversation` continuam disponíveis para importação em orquestração avançada, testes ou wrappers customizados. Eles são separados da API `ConversationState` / `ConversationConfig` e não adicionam os argumentos nomeados `user_message=` ou `session_id=` a `Flow.kickoff()`.
```python
from crewai.flow import ChatState
@@ -127,62 +135,64 @@ class MyChatState(ChatState):
| Campo | Função |
|-------|--------|
| `id` | UUID da sessão (igual a `session_id` / `inputs["id"]`) |
| `id` | UUID da sessão (igual a `inputs["id"]`) |
| `messages` | `list` de `{role, content}` para histórico de LLM |
| `last_user_message` | Última linha do usuário neste turno |
| `last_intent` | Rótulo de rota após classificação (se usado) |
| `session_ready` | Flag de bootstrap único (permissões, caches, etc.) |
`ConversationalInputs` é um `TypedDict` para `kickoff(inputs={...})`: `id`, `user_message`, `last_intent`.
`ConversationalInputs` é um `TypedDict` para as chaves convencionais de `kickoff(inputs={...})`: `id`, `user_message`, `last_intent`.
O `ConversationState` armazena `messages` como objetos `ConversationMessage` e também fornece `current_user_message`, `ended`, `events` e `agent_threads`. Use `conversation_messages` ao passar seu histórico canônico para um LLM.
## API conversacional em `Flow`
### Parâmetros de `kickoff` / `kickoff_async`
### Parâmetros de `handle_turn`
| Parâmetro | Propósito |
|-----------|-----------|
| `user_message` | Texto deste turno (ou `{"role": "user", "content": "..."}`) |
| `message` | Texto deste turno |
| `session_id` | UUID da conversa → `inputs["id"]` / `state.id` |
| `intents` | Rótulos de outcome para `classify_intent` antes do kickoff |
| `intent_llm` | LLM para classificação (obrigatório com `intents`) |
| `interactive` | Loop CLI via `ask()` (só demos locais) |
| `interactive_prompt` | Prompt no modo interativo |
| `interactive_timeout` | Timeout de `ask()` por linha |
| `exit_commands` | Palavras que encerram o modo interativo |
| `inputs` | Campos extras de estado (mesclados com chaves conversacionais) |
| `restore_from_state_id` | Hidratação fork de outro flow persistido |
| `**kickoff_kwargs` | Encaminhados para `kickoff()` para opções como `input_files`, `from_checkpoint` e `restore_from_state_id` |
### Parâmetros de `kickoff`
`Flow.kickoff()` aceita `inputs`, `input_files`, `from_checkpoint` e `restore_from_state_id`. Passe `inputs={"id": session_id}` quando precisar executar o flow diretamente, mas use `handle_turn()` quando a chamada representar uma mensagem de chat.
### Atributos de instância
| Atributo | Propósito |
|-----------|-----------|
| `conversational_config` | Padrões `ConversationalConfig` em nível de classe |
| `defer_trace_finalization` | Flag de instância; definida automaticamente a partir do config no kickoff |
| `suppress_flow_events` | Oculta painéis Rich no console; **tracing ainda registra** eventos |
| `stream` | Habilita streaming; use com `ChatSession.handle_turn(..., stream=True)` |
| `conversational` | Defina como `True` para habilitar o grafo conversacional e `handle_turn()` |
| `defer_trace_finalization` | Sobrescrita opcional na instância. Caso contrário, `_should_defer_trace_finalization()` lê `ConversationConfig.defer_trace_finalization`. |
| `suppress_flow_events` | Oculta painéis do flow no console e suprime eventos de execução de métodos; os eventos de início/fim do flow continuam sendo emitidos |
| `stream` | Flag genérica de streaming do Flow. Para turnos conversacionais, use `stream_turn()` em vez de combinar esta flag com `handle_turn()`. |
### Métodos e propriedades
| Nome | Descrição |
|------|-------------|
| `append_message(role, content, **extra)` | Acrescenta em `state.messages` (roles: `user`, `assistant`, `system`, `tool`) |
| `append_assistant_message(content)` | Acrescenta uma resposta visível ao usuário em `state.messages` |
| `append_message(role, content, **extra)` | Acréscimo de mais baixo nível em `state.messages` |
| `conversation_messages` | Histórico somente leitura para chamadas LLM |
| `classify_intent(text, outcomes, *, llm, context=None)` | Mapeia texto a um outcome (mesma lógica de `@human_feedback`) |
| `receive_user_message(text, *, outcomes=None, llm=None)` | Acrescenta mensagem do usuário; opcionalmente define `last_intent` |
| `finalize_session_traces()` | Emite `flow_finished` adiado e finaliza o batch de trace da sessão |
| `_should_defer_trace_finalization()` | Se este flow adia finalização de trace por turno |
| `_should_defer_trace_finalization()` | Hook avançado/interno que resolve se a finalização de trace por turno é adiada |
| `input_history` | Trilha de auditoria de prompts e respostas de `ask()` |
### Helpers do módulo (`crewai.flow.conversation`)
Importáveis para testes ou orquestração customizada:
Importáveis de `crewai.flow.conversation` para testes ou orquestração customizada. Esses helpers usam o formato legado de `ConversationalConfig`; `prepare_conversational_turn()` também limpa `last_intent`, ao contrário do `handle_turn()`, que o preserva como contexto do router.
| Função | Descrição |
|----------|-------------|
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)` | Mescla kwargs conversacionais em `inputs` |
| `get_conversation_messages(flow)` | Lê mensagens do estado ou buffer interno |
| `append_message(flow, role, content, **extra)` | Igual ao método de instância |
| `prepare_conversational_turn(flow, ...)` | Hidratação do turno (geralmente chamado pelo kickoff) |
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | Hidratação de turno de mais baixo nível para wrappers customizados |
| `receive_user_message(flow, text, ...)` | Igual ao método de instância |
| `set_state_field(flow, name, value)` | Define campo em estado dict ou Pydantic |
| `get_conversational_config(flow)` | Lê `conversational_config` da classe |
@@ -192,19 +202,18 @@ Importáveis para testes ou orquestração customizada:
### A. Pré-classificar via `ConversationalConfig` (mais simples)
Defina `default_intents` e `intent_llm`. Cada kickoff classifica antes do `@router`; leia `self.state.last_intent` em `route()`.
Defina `default_intents` e `intent_llm`. Cada `handle_turn()` pré-classifica a mensagem atual. Um resultado não vazio retornado por um `route_turn()` customizado tem precedência; caso contrário, `route_conversation` usa a intenção classificada do turno atual.
### B. Classificar dentro do `@router` (prompts mais ricos)
### B. Classificar dentro de `route_turn` (prompts mais ricos)
Defina `default_intents=None` para o kickoff só acrescentar a mensagem. Em `route()`, chame `classify_intent` com prompt ou descrições customizadas:
Defina `default_intents=None` para `handle_turn()` apenas acrescentar a mensagem do usuário. Em `route_turn()`, chame `classify_intent` com um prompt ou descrições customizadas:
```python
@router(bootstrap)
def route(self):
def route_turn(self, context):
intent = self.classify_intent(
self._routing_prompt(self.state.last_user_message),
self._routing_prompt(self.state.current_user_message),
("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
llm=self.conversational_config.intent_llm or "gpt-4o-mini",
llm="gpt-4o-mini",
)
self.state.last_intent = intent
return intent
@@ -214,70 +223,59 @@ Use **`@listen("RESEARCH")`** (ou similar) para passos com `Agent.kickoff()` e f
## Quando o flow termina mas o usuário continua conversando
`FlowFinished` significa que **esta execução do grafo** terminou. A conversa segue com outro `kickoff` e o mesmo `session_id`. `@persist` restaura `messages`, flags e contexto.
Cada `handle_turn()` conclui uma execução do grafo, e a conversa continua com outro `handle_turn()` usando o mesmo `session_id`. Com o ciclo de vida de trace adiado padrão, essa execução emite `conversation_turn_completed`, enquanto `FlowFinished` é emitido uma vez quando `finalize_session_traces()` encerra a sessão. `@persist` restaura `messages`, flags e contexto.
**Padrão de persistência:** prefira `@persist` em um **único passo terminal** (por exemplo `finalize`) em vez de na classe `Flow` inteira. Persist em nível de classe salva após cada método; `load_state` usa a linha mais recente, que pode ser snapshot no meio da execução e perder atualizações dos handlers no mesmo turno.
Não use `@human_feedback` para linhas de chat de follow-up, a menos que um humano precise aprovar uma saída específica antes de exibi-la.
## `Flow` conversacional (experimental)
## `Flow` conversacional
<Warning>
**Funcionalidade experimental.** A superfície do `Flow` conversacional
(`conversational = True`, `handle_turn`, `ConversationConfig`,
`RouterConfig`, `ConversationState`, o grafo embutido + helpers) vive em
`crewai.experimental` e pode mudar de formato antes de graduar. Fixe a
versão do CrewAI se depende de comportamento específico e acompanhe o
changelog para mudanças quebradoras. Feedback / issues bem-vindos.
</Warning>
Habilite o grafo de chat conversacional definindo `conversational = True` em uma subclasse de `Flow` ou aplicando `@ConversationConfig(...)`. O `Flow` base passa a fornecer `route_conversation` como start/router embutido, além dos listeners `converse_turn` e `end_conversation`. O listener descontinuado `answer_from_history_turn` permanece disponível para compatibilidade. O framework gerencia `state.messages`, pode acionar um LLM de roteamento e mantém o batch de trace aberto entre turnos. Você escreve as **rotas customizadas**; o framework cuida do resto.
Habilite o grafo conversacional definindo `conversational = True` em uma subclasse de `Flow`. O `Flow` base passa a expor um grafo embutido `@start` / `@router` / `converse_turn` / `end_conversation`, gerencia `state.messages`, dirige o LLM de roteamento e mantém o batch de trace aberto entre os turnos. Você escreve as **rotas customizadas**; o framework cuida do resto.
Use isto quando quiser um chat multi-turno com router LLM e handlers por rota sem cablar o ciclo de vida na mão. Use `Flow[ChatState]` (o padrão de mais baixo nível acima) quando precisar de controle total.
Use isto quando quiser um chat multi-turno com router e handlers por rota sem cablar o ciclo de vida na mão. Use `Flow[ChatState]` (o padrão de mais baixo nível acima) quando precisar de controle total.
### Exemplo rápido
```python
from crewai import LLM, Flow
from crewai import Flow
from crewai.flow import listen
from crewai.experimental.conversational import (
from crewai.flow import (
ConversationConfig,
ConversationState,
RouterConfig,
)
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(), # rotas + descrições auto-descobertas pelos handlers @listen
)
@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:
return "INTERNET_SEARCH"
if "docs" in message or "crewai" in message:
return "CREWAI_DOCS"
return "converse"
@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("O que você pode fazer?") # roteia para converse (built-in)
flow.handle_turn("Pesquise na web por notícias de IA.") # roteia para INTERNET_SEARCH
flow.handle_turn("Resuma o primeiro resultado.") # volta para converse
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
finally:
flow.finalize_session_traces()
```
@@ -299,27 +297,55 @@ Decorador de classe que anexa os defaults de chat por classe.
|-------|--------|-----------|
| `system_prompt` | `slices.conversational_system_prompt` (i18n) | System message usado pelo `converse_turn` embutido. Passe `""` para desativar totalmente. |
| `llm` | `None` | LLM de conversa (usado pelo `converse_turn` e como fallback do router). |
| `router` | `None` | `RouterConfig` para roteamento por LLM. Sem ele, o flow sempre cai em `converse`. |
| `answer_from_history_prompt` | padrão do framework | System message para a rota opcional `answer_from_history`. |
| `answer_from_history_llm` | `None` | Habilita o atalho `answer_from_history` quando definido. |
| `router` | `None` | Sobrescritas opcionais de `RouterConfig`. Com listeners customizados e um LLM que possa ser resolvido, o roteamento é habilitado automaticamente mesmo quando este campo é omitido. |
| `answer_from_history_prompt` | padrão do framework | **Descontinuado.** Use o system prompt de `converse` ou sobrescreva `converse_turn()`. |
| `answer_from_history_llm` | `None` | **Descontinuado.** Use `llm`; `converse` já recebe o histórico canônico. |
| `intent_llm` | `None` | LLM para o caminho legado `intents=`/`default_intents`. |
| `default_intents` | `None` | Labels de outcome para pré-classificação legada. |
| `visible_agent_outputs` | `None` | `"all"` ou lista de nomes de agentes cujos `append_agent_result()` devem virar mensagens públicas. |
| `defer_trace_finalization` | `True` | Mantém um único batch de trace aberto entre chamadas de `handle_turn()`. |
<Warning>
`answer_from_history_prompt`, `answer_from_history_llm` e a rota
`answer_from_history` estão descontinuados e serão removidos em uma versão
futura. Eles duplicam `converse`, que já recebe o histórico canônico,
adicionam uma chamada de LLM para verificar elegibilidade e são ignorados
quando o auto-router normal retorna uma rota. As configurações existentes
continuam funcionando e emitem `DeprecationWarning`.
</Warning>
Sem rotas customizadas, os turnos caem em `converse`. Com rotas customizadas e um LLM de conversa/router, o framework sintetiza um `RouterConfig` padrão; forneça um explicitamente apenas para customizar seu prompt, lista de rotas, descrições ou comportamento de fallback. Definir `default_intents` usa o caminho legado de pré-classificação.
Se nenhum LLM de conversa estiver configurado, o `converse_turn` embutido retorna um placeholder de configuração em vez de gerar uma resposta.
### `RouterConfig` e o catálogo de rotas auto-gerado
```python
RouterConfig(
prompt="Enquadramento de domínio opcional (política, voz, persona).",
response_format=MyRoute, # opcional; auto-gerado caso contrário
llm=ROUTER_LLM, # usa ConversationConfig.llm como fallback
routes=["INTERNET_SEARCH", "CREWAI_DOCS"], # opcional; inferido dos listeners
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
route_descriptions={
"INTERNET_SEARCH": "Sobrescreve a docstring só desta rota.",
"INTERNET_SEARCH": "Override the docstring for this one route.",
},
default_intent="converse", # usado quando a chamada ao LLM falha ou não LLM
fallback_intent="converse", # usado quando o LLM retorna rota inválida
default_intent="converse", # used when LLM call fails or no LLM available
fallback_intent="converse", # used when LLM returns an invalid route
intent_field="intent",
)
```
@@ -327,13 +353,17 @@ RouterConfig(
O prompt do router é montado automaticamente. Para cada rota o framework escolhe a descrição nesta precedência:
1. `RouterConfig.route_descriptions[label]` — override explícito.
2. `Flow.builtin_route_descriptions[label]` — texto canônico do framework para `converse`, `end`, `answer_from_history` (otimizado para o LLM de routing).
3. Primeira linha não vazia da docstring do handler `@listen(label)`.
4. Vazio (a rota aparece no catálogo sem descrição).
2. `Flow.builtin_route_descriptions[label]` — texto canônico do framework para `converse`, `end` e a rota de compatibilidade descontinuada `answer_from_history` (otimizado para o LLM de routing).
3. O `description` declarado do método (usado por flows declarativos e projeções da DSL).
4. Primeira linha não vazia da docstring do handler `@listen(label)`.
5. Vazio (a rota aparece no catálogo sem descrição).
Na prática, **adicionar uma rota é `@listen("X")` + uma docstring de uma linha**:
```python
from crewai.flow import listen
@listen("INTERNET_SEARCH")
def handle_internet_search(self) -> str:
"""Fresh web research, current news, real-time lookups."""
@@ -352,13 +382,34 @@ Routes:
`RouterConfig.prompt` é para **enquadramento de domínio** (persona do assistente, regras de negócio, voz). O catálogo de rotas é auto-gerado — não liste rotas em `prompt`; elas vão sair de sincronia assim que você adicionar um handler.
### Nomeando handlers
A string em `@listen("…")` é um **rótulo de rota do router** (um nome de evento), e não o nome do método Python. Rótulos de rota e eventos de conclusão de métodos compartilham o mesmo namespace de gatilhos; portanto, dar ao handler o mesmo nome de sua rota faria o handler acionar a si próprio em loop.
Use um nome de método diferente — os exemplos da documentação usam o prefixo `handle_*`:
```python
@listen("create_video")
def handle_create_video(self) -> str:
"""User wants a new video."""
...
```
**Não** replique o rótulo da rota no método:
```python
@listen("create_video")
def create_video(self) -> str: # rejected at flow instantiation
...
```
### Rotas embutidas
| Rota | Handler | Propósito |
|------|---------|-----------|
| `converse` | `converse_turn` | Handler de chat padrão. Chama `ConversationConfig.llm` com o system prompt + histórico canônico. |
| `end` | `end_conversation` | Define `state.ended = True` e emite uma resposta de encerramento. |
| `answer_from_history` | `answer_from_history_turn` | Opcional. Cai aqui quando `ConversationConfig.answer_from_history_llm` está definido e a mensagem pode ser respondida só pelo histórico. |
| `answer_from_history` | `answer_from_history_turn` | **Rota de compatibilidade descontinuada.** Use `converse`, que já recebe o histórico canônico. |
Você pode sobrescrever qualquer uma definindo um handler com o mesmo nome na subclasse.
@@ -368,9 +419,9 @@ Você pode sobrescrever qualquer uma definindo um handler com o mesmo nome na su
1. Reseta o tracking por execução (`_completed_methods`, `_method_outputs`) para o grafo re-rodar — sem isso, chamadas repetidas de `kickoff` na mesma instância dariam curto-circuito no turno 2+ porque `Flow.kickoff_async` trata `inputs={"id": ...}` como restauração de checkpoint.
2. Anexa a mensagem do usuário em `state.messages`, define `current_user_message` / `last_user_message`. `last_intent` é **preservado do turno anterior** para que o LLM de routing possa usá-lo como sinal.
3. Roda `conversation_start` → `route_conversation` → o handler `@listen` escolhido.
3. Executa métodos `@start` definidos pelo usuário (se houver), depois `route_conversation` como start/router embutido e, por fim, o handler `@listen` escolhido. `route_conversation` invoca o helper sobrescrevível `conversation_start()`.
4. O router grava sua decisão em `state.last_intent` (visível para o contexto de routing do próximo turno).
5. Se seu handler retornou uma string e ainda não chamou `append_assistant_message`, `handle_turn` anexa para você.
5. Se seu handler retornou uma string e ainda não chamou `append_assistant_message`, `handle_turn` anexa para você e persiste o `state.messages` atualizado para que a restauração `@persist` inclua o turno do assistente.
Chame `handle_turn()` para mensagens de chat. Chamar `kickoff(inputs={"id": ...})` diretamente executa o grafo sem aplicar o wrapper de turno conversacional.
@@ -391,6 +442,8 @@ Ele cobre o loop local comum:
4. Imprime o resultado do assistente.
5. Finaliza traces de sessão adiados em um bloco `finally`.
`chat(defer_trace_finalization=True)` habilita temporariamente a flag de adiamento na instância durante o REPL e restaura o valor anterior ao sair.
Customize o comportamento do terminal com I/O injetável:
```python
@@ -409,6 +462,12 @@ Para apps web, workers em background, testes e transportes customizados, continu
Para rodar efeitos colaterais (setup de event bus, telemetria) em toda decisão de routing, sobrescreva `route_turn`:
```python
from typing import Any
from crewai import Flow
from crewai.flow import ConversationState
class SupportFlow(Flow[ConversationState]):
conversational = True
@@ -417,7 +476,7 @@ class SupportFlow(Flow[ConversationState]):
return super().route_turn(context)
```
Para ignorar o router LLM e escolher uma rota programaticamente, retorne uma string de `route_turn`; retornar `None` cai no `_route_with_config(...)`.
Para ignorar completamente o router LLM e escolher uma rota programaticamente, retorne uma string não vazia de `route_turn`. Um retorno falsy **não** invoca `_route_with_config()` a partir da sua sobrescrita; o roteamento segue para a intenção pré-classificada deste turno, depois para o caminho de compatibilidade descontinuado `answer_from_history` quando configurado e, por fim, para `converse`. O `last_intent` do turno anterior fica disponível no contexto do router, mas nunca é repetido como fallback.
### `append_assistant_message` e `append_agent_result`
@@ -430,7 +489,7 @@ Dentro de um handler `@listen(label)`, escolha:
## Declarando um flow conversacional em JSON/YAML
Um [flow declarativo](/edge/en/concepts/cli) também pode ser conversacional. Adicione um bloco `conversational` no nível raiz e declare suas próprias rotas como métodos que escutam (`listen`) um rótulo de rota:
Um [Flow declarativo](/edge/pt-BR/concepts/cli) também pode ser conversacional. Adicione um bloco `conversational` no nível raiz e declare suas próprias rotas como métodos que fazem `listen` em um rótulo de rota:
```yaml
schema: crewai.flow/v1
@@ -455,15 +514,17 @@ methods:
input: "${state.current_user_message}"
```
Declarar o bloco já é o opt-in — `enabled` tem valor padrão `true`. Use `enabled: false` para manter a configuração e desligar o chat.
Declarar o bloco já é o opt-in — `enabled` tem valor padrão `true`. Use `enabled: false` para manter a configuração e desligar o chat. Isso também desabilita a síntese de métodos embutidos, portanto a declaração deve fornecer um grafo não conversacional normal.
Três coisas são fornecidas para você:
| Fornecido | Detalhe |
|----------|--------|
| O grafo interno | `route_conversation`, `converse_turn`, `end_conversation` e `answer_from_history_turn` são adicionados automaticamente. Declare um método com um desses nomes para sobrescrevê-lo. |
| Estado da conversa | `ConversationState` é usado quando a declaração não tem bloco `state`. Para adicionar campos, aponte `state` para um modelo Pydantic que estenda `ConversationState`. |
| O catálogo de rotas | Construído a partir dos métodos que declaram um rótulo `listen`. O `description` de cada método é o que o modelo de roteamento lê ao escolher entre rotas. |
| O grafo interno | `route_conversation`, `converse_turn` e `end_conversation` são adicionados automaticamente. O `answer_from_history_turn` descontinuado é mantido para compatibilidade. Declare um método com um desses nomes para sobrescrevê-lo. |
| Estado da conversa | `ConversationState` é usado quando não bloco `state`. Um estado Pydantic definido por `ref` ou `json_schema` é composto automaticamente com os campos conversacionais; ele não precisa estender `ConversationState`. |
| O catálogo de rotas | Inferido de métodos que não são routers e têm rótulos `listen`, excluindo rotas internas. As descrições seguem a precedência acima, e `router.routes` explícito pode limitar as opções. |
Os campos declarativos `llm`, `router.llm` e `intent_llm` aceitam um id de modelo ou um mapping de configuração, como `{model: openai/gpt-4o-mini, max_tokens: 512}`. O bloco `conversational` também aceita `default_intents`, `visible_agent_outputs`, `defer_trace_finalization` e os campos de `RouterConfig` mostrados acima. As declarações descontinuadas `answer_from_history_prompt` / `answer_from_history_llm` continuam sendo aceitas para compatibilidade.
Execute a partir do Python com as mesmas APIs de turno de um Flow conversacional baseado em classe:
@@ -486,15 +547,16 @@ Rótulos de rota e nomes de métodos compartilham um único namespace de gatilho
| Não expressável | Use no lugar |
|-----------------|-------------|
| Uma instância `LLM` viva ou um `BaseLLM` customizado | Uma string de id de modelo, como `gpt-4o-mini` |
| `router.response_format` como classe de modelo | Omita; o framework sintetiza uma. Um ref ou schema é ignorado com um aviso |
| Overrides de `route_turn()` / `can_answer_from_history()` | Escreva o Flow em Python, ou aponte o `do` de um método para um ref `call: code` |
| Uma instância `LLM` viva ou um `BaseLLM` customizado | Um id de modelo em string ou mapping estático de configuração |
| `router.response_format` como classe de modelo viva | Nomeie a classe com um ref python: `response_format: {python: my_project.schemas.ConversationRoute}`. Omita e o framework sintetiza uma |
| Uma sobrescrita de `route_turn()` | Escreva o Flow em Python ou substitua o método declarativo `route_conversation` por uma ação `call: code` / expressão |
| Uma sobrescrita de `can_answer_from_history()` | Descontinuado. Use `converse` ou sobrescreva `converse_turn()` no Python. |
O `crewai run` ainda não tem loop de chat: ele informa que o flow é conversacional e sai, em vez de rodar um único turno. Conduza um flow conversacional declarativo pelo Python com `handle_turn()`, `stream_turn()` ou `chat()`.
O `crewai run` abre a TUI de chat para um flow conversacional declarativo — a mesma que um Flow conversacional em Python recebe. Um loop de chat precisa de um terminal, então uma execução headless encerra com código diferente de zero e orientações, em vez de rodar um único turno; ali, conduza pelo Python com `handle_turn()` ou `stream_turn()`. Um método declarativo com um bloco `human_feedback:` (Python: `@human_feedback`) roda em um REPL de terminal, porque o runtime coleta feedback com um prompt bloqueante que a TUI não consegue atender. O `--inputs` não é aceito em um flow conversacional — a entrada de cada turno é a mensagem que você digita — e retomar uma sessão por id ainda não está ligado à CLI; use `flow.handle_turn(message, session_id=...)` no Python para isso.
## Tracing entre turnos
Com `defer_trace_finalization=True` (padrão em `ConversationalConfig`):
Com `defer_trace_finalization=True` (padrão em `ConversationConfig`):
- **Um batch de trace** para toda a sessão de chat.
- **`flow_started`** só no primeiro turno; **`flow_finished`** uma vez em `finalize_session_traces()`.
@@ -505,17 +567,30 @@ Com `defer_trace_finalization=True` (padrão em `ConversationalConfig`):
flow.chat(session_id=session_id)
```
`flow.chat()` chama `finalize_session_traces()` para você. Quando você controla o loop com `handle_turn()` ou `kickoff(...)`, chame `finalize_session_traces()` quando a sessão terminar.
`flow.chat()` chama `finalize_session_traces()` para você. Quando você controla o loop com `handle_turn()`, chame `finalize_session_traces()` quando a sessão terminar.
`suppress_flow_events=True` oculta painéis do console; eventos de trace e método ainda são emitidos.
`suppress_flow_events=True` oculta painéis Rich no console e suprime eventos de execução de métodos. Os eventos de início/fim do Flow continuam sendo emitidos, portanto o ciclo de vida externo do Flow permanece rastreável, mas os spans de métodos individuais são omitidos.
### Ciclo de vida de trace do `Flow` conversacional
O [`Flow` conversacional](#flow-conversacional-experimental) experimental usa o mesmo ciclo de vida de tracing: `defer_trace_finalization` é `True` por padrão, então cada `handle_turn()` mantém o trace da sessão aberto. Sempre finalize ao fim da sessão — envolva seu loop em `try/finally` e chame `flow.finalize_session_traces()` na saída. Sem isso, o batch fica aberto e a última conversa pode nunca ser exportada.
O [`Flow` conversacional](#flow-conversacional) usa o mesmo ciclo de vida de tracing: `defer_trace_finalization` é `True` por padrão, então cada `handle_turn()` mantém o trace da sessão aberto. Turnos adiados também suprimem `flow_failed` por turno; em caso de erro em um turno ou encerramento antecipado da sessão, finalize a sessão explicitamente. Isso fecha o batch com o evento `FlowFinished` no nível da sessão, em vez de um evento `FlowFailed` por turno. Sempre envolva seu REPL/loop em `try/finally` e chame `flow.finalize_session_traces()` na saída. Sem isso, o batch fica aberto e a conversa final pode nunca ser exportada.
## Streaming
Defina `stream = True` na classe `Flow`. `kickoff(...)` então emitirá `assistant_delta` (e eventos relacionados) pelo event bus padrão.
Para UIs conversacionais, use `stream_turn()` e itere sobre seus objetos `StreamFrame` ordenados:
```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
```
Para um Flow não conversacional, definir `stream = True` faz `kickoff()` retornar uma `StreamSession`. Não defina `flow.stream = True` ao usar `handle_turn()`; `stream_turn()` controla o ciclo de vida do streaming conversacional.
## Imports
@@ -530,10 +605,15 @@ from crewai.flow import (
router,
start,
)
from crewai.flow.conversation import prepare_conversational_turn
from crewai.flow import (
ConversationConfig,
ConversationState,
RouterConfig,
)
```
## Veja também
- [Dominando o Gerenciamento de Estado em Flows](/pt-BR/guides/flows/mastering-flow-state) — persistência, estado Pydantic, `@persist`
- [Construa Seu Primeiro Flow](/pt-BR/guides/flows/first-flow) — fundamentos de flow
- Demo: `lib/crewai/runner_conversational_flow_simple.py` — REPL mínimo com `RESEARCH` + agente Exa

View File

@@ -135,7 +135,7 @@ Agora, vamos configurar o crew de redatores com JSONC. Vamos definir dois agente
}
```
Substitua `provider/model-id` pelo modelo que você usa, como `openai/gpt-4o`, `gemini/gemini-2.0-flash-001` ou `anthropic/claude-sonnet-4-6`.
Substitua `provider/model-id` pelo modelo que você usa, como `openai/gpt-4o`, `gemini/gemini-3.7-flash` ou `anthropic/claude-sonnet-4-6`.
3. Crie `src/guide_creator_flow/crews/content_crew/crew.jsonc`:
@@ -481,7 +481,7 @@ Flows permitem que você faça chamadas diretas a modelos de linguagem quando pr
```python
llm = LLM(
model="model-id-here", # gpt-4o, gemini-2.0-flash, anthropic/claude...
model="model-id-here", # gpt-4o, gemini/gemini-3.7-flash, anthropic/claude...
response_format=GuideOutline
)
response = llm.call(messages=messages)

View File

@@ -0,0 +1,156 @@
---
title: Channels
description: Execute o mesmo agente CrewAI como um bot do Slack ou Teams com o Channels SDK do CopilotKit e a plataforma gerenciada Intelligence.
icon: messages
mode: "wide"
---
## Encontre seus usuários onde eles já estão
O agente CrewAI que você construiu na [Visão geral](/edge/pt-BR/guides/frontend/overview) não precisa viver por trás de um web app. O mesmo Crew ou Flow pode rodar como um bot dentro de uma plataforma de mensagens. Sem reconstruir, sem uma segunda cópia da lógica do seu agente: o agente permanece exposto pelo [protocolo AG-UI](https://docs.ag-ui.com), e um **channel** o aciona a partir do Slack ou do Microsoft Teams.
O [Channels SDK](https://docs.copilotkit.ai/slack) do CopilotKit fornece esse channel. Você declara um `createChannel` em um pequeno runtime, aponta-o para o seu agente CrewAI, e a plataforma gerenciada **Intelligence** do CopilotKit intermedia a conexão com o provedor de mensagens.
<Note>
Diferentemente do restante desta seção, Channels **não é self-hosted**. Ele roda através do **CopilotKit Intelligence** — uma superfície obrigatória para Channels, por design (há um plano gratuito disponível). O Intelligence detém a conexão com a plataforma e as credenciais, recebe cada evento da plataforma e entrega o turno ao processo do seu channel; seu processo executa o agente e transmite a resposta de volta. Você configura o Slack uma vez no painel do Intelligence, e as credenciais da plataforma nunca entram no seu processo. Seu agente, suas tools e seu estado continuam sendo seus.
</Note>
## Como tudo se encaixa
Nada muda no servidor do seu agente CrewAI. Ele continua servindo o seu Crew ou Flow por AG-UI exatamente como na Visão geral. O que você adiciona é um processo Node separado, de longa duração, construído com `@copilotkit/channels`: ele registra um channel no `CopilotRuntime`, conecta-se ao Intelligence e executa o seu agente sempre que chega uma mensagem.
```
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
```
O processo do channel mantém uma conexão persistente com o gateway do Intelligence, então ele precisa de um host de longa duração — um handler de requisições serverless não consegue ser dono dessa conexão. Seu servidor CrewAI pode continuar servindo o frontend web da Visão geral ao mesmo tempo: o web app e o channel são apenas dois clientes de um único endpoint AG-UI.
## Guia de integração
<Steps>
<Step title="Instale os pacotes do Channels">
O Channels SDK vem com tudo incluído — cada plataforma é entregue no mesmo pacote, sem nenhum adaptador por plataforma para instalar. Adicione-o junto ao runtime que hospeda o channel e ao cliente AG-UI do CrewAI:
```bash
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
```
</Step>
<Step title="Crie um Channel no Intelligence">
No [painel do CopilotKit](https://docs.copilotkit.ai/slack), crie um Channel e conecte o Slack — o Intelligence guia você na criação do app do Slack e detém suas credenciais. Isso deixa duas variáveis de ambiente para o seu processo, ambas vindas do painel:
```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="Defina o channel">
`createChannel` declara o channel e anexa o seu agente. Construa o agente como uma factory por thread, para que cada conversa ganhe sua própria sessão, usando o mesmo `CrewAIAgent` que a Visão geral usa no runtime web, apontado para o seu endpoint AG-UI. `identifyUser: "platform"` permite que o Intelligence mapeie cada usuário da plataforma para uma identidade estável.
```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="Registre o channel no runtime">
Crie um `CopilotRuntime` com o gateway do Intelligence e o seu channel, e então sirva-o com `createCopilotNodeListener`. O mapa `agents` permanece vazio — o channel fornece seu próprio agente. Aguarde o channel ficar pronto, para que uma configuração quebrada faça a inicialização falhar de forma visível.
```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="Execute o runtime do channel">
Inicie-o junto ao servidor do seu agente CrewAI:
```bash
uvicorn server:app --port 8000 # terminal 1 — CrewAI agent server
npx tsx server.ts # terminal 2 — Channels runtime
```
Mencione o bot no Slack ou no Teams e ele executa o seu Crew ou Flow, transmitindo a resposta de volta para a thread. A thread permanece inscrita, então mensagens de acompanhamento rodam sem outra menção.
</Step>
</Steps>
## O modelo de eventos
Um channel reage a eventos da plataforma com handlers, e cada handler recebe uma `thread` que você aciona com alguns métodos:
- **`channel.onMention`** dispara quando um usuário @-menciona o bot. Chame `thread.subscribe()` para entrar na thread, e então `thread.runAgent()` para executar o seu agente CrewAI na menção.
- **`channel.onMessage`** dispara em cada mensagem de uma thread que o bot consegue ver. Restrinja com `thread.isSubscribed()` para que o agente só responda onde tiver entrado, e então `thread.runAgent()`.
- **`thread.runAgent()`** executa o agente CrewAI anexado para o turno atual e transmite a saída dele de volta para o channel. Passe `{ prompt }` para sobrescrever o texto sobre o qual o agente roda.
Seu agente recebe um `RunAgentInput` comum do AG-UI e emite eventos comuns do AG-UI; as mecânicas da plataforma ficam por trás do channel, então o mesmo Crew ou Flow roda sem alterações em todas as plataformas. O channel também expõe handlers para boas-vindas, interrupções, comandos, reações e modais — consulte a [referência de `Channel`](https://docs.copilotkit.ai/reference/channels/classes/Channel) para conhecer toda a superfície.
## Suporte a plataformas
O caminho gerenciado do Intelligence cobre **Slack** e **Microsoft Teams** hoje — o mesmo código de channel roda em qualquer um dos dois, e `message.platform` / `thread.platform` reportam a origem nativa. Outras plataformas (Discord, Telegram, WhatsApp) são alcançadas através de **adaptadores diretos** operados pelo desenvolvedor, em vez do caminho gerenciado — o seu próprio processo detém as credenciais da plataforma e o transporte. Consulte a [documentação de Channels do CopilotKit](https://docs.copilotkit.ai/slack) para a lista atual de plataformas e a configuração por plataforma.
## Relacionados
<CardGroup cols={2}>
<Card title="Visão geral do Frontend" icon="browser" href="/edge/pt-BR/guides/frontend/overview">
Sirva o seu Crew ou Flow por AG-UI — a base sobre a qual todo channel é construído.
</Card>
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
Pause o agente para coletar aprovação ou input do usuário no meio da execução.
</Card>
</CardGroup>

View File

@@ -0,0 +1,238 @@
---
title: Frontend Overview
description: Construa interfaces de usuário interativas para seus agentes CrewAI com o CopilotKit e o protocolo AG-UI.
icon: browser
mode: "wide"
---
## Dê uma interface de usuário aos seus agentes
O CrewAI executa seus agentes. O [CopilotKit](https://copilotkit.ai) dá a eles um frontend. Juntos, eles permitem que você construa aplicações em que os usuários conversam com um Crew ou Flow, o observam trabalhar em tempo real, aprovam suas decisões e veem sua saída renderizada como UI ao vivo, em vez de paredes de texto.
Os dois se conectam através do [protocolo AG-UI](https://docs.ag-ui.com). O pacote `ag-ui-crewai` expõe qualquer Crew ou Flow como um endpoint AG-UI. Os hooks e componentes React do CopilotKit consomem esse endpoint. Isso desbloqueia experiências que vão muito além de uma caixa de chat:
<CardGroup cols={2}>
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
Renderize as chamadas de tool e o estado do agente como seus próprios componentes React.
</Card>
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
Pause o agente para coletar aprovação ou input do usuário no meio da execução.
</Card>
<Card title="Shared State" icon="arrows-rotate" href="/edge/en/guides/frontend/shared-state">
Mantenha o estado do agente e a UI do seu app em sincronia bidirecional.
</Card>
<Card title="Channels" icon="messages" href="/edge/pt-BR/guides/frontend/channels">
Execute o mesmo agente como um bot do Slack, Discord ou Teams.
</Card>
</CardGroup>
Este guia coloca um Crew ou Flow conversando com um frontend Next.js de ponta a ponta. O restante da seção se apoia no app que você configura aqui.
## Arquitetura
Há três peças:
1. **CrewAI agent server** — um processo Python que serve o seu Crew ou Flow por AG-UI (FastAPI + `ag-ui-crewai`).
2. **CopilotKit runtime** — uma rota Next.js que registra o seu agente e faz o proxy das requisições para ele.
3. **React frontend** — o provider `<CopilotKit>` mais os componentes de chat e de generative UI.
```
React app ──► CopilotKit runtime (/api/copilotkit) ──► CrewAI server (AG-UI) ──► Crew / Flow
```
<Note>
Este guia cobre o caminho **self-hosted**: você mesmo executa o servidor do agente CrewAI com `ag-ui-crewai`, e ele funciona localmente sem nenhum serviço gerenciado. O CopilotKit também oferece um caminho **gerenciado** (CopilotKit Cloud / Enterprise Intelligence) com threads hospedadas e um inspetor — consulte o [quickstart de CrewAI do CopilotKit](https://docs.copilotkit.ai/crewai-crews/quickstart) se preferir isso. O código do frontend nesta seção é o mesmo de qualquer forma; apenas como o agente é hospedado e registrado é que muda.
</Note>
<Note>
O CrewAI roda por trás do AG-UI em três formatos: **Flows** comuns (usados ao longo destes guias), **[Conversational Flows](/edge/en/guides/frontend/conversational-flows)** (nativos, cientes de sessão, baseados em turnos, com paridade total de recursos) e **Crews** (chat básico). O frontend nesta seção é idêntico entre eles — apenas a autoria e o registro no backend é que diferem.
</Note>
## Guia de integração
<Steps>
<Step title="Sirva seu agente por AG-UI">
Instale o pacote de integração no seu projeto CrewAI:
```bash
pip install ag-ui-crewai
```
Exponha o seu agente a partir de um app FastAPI. Flows usam `add_crewai_flow_fastapi_endpoint`; Crews usam `add_crewai_crew_fastapi_endpoint`. Você pode registrar quantos quiser, cada um em seu próprio 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>
Execute:
```bash
uvicorn server:app --port 8000
```
<Note>
Defina as variáveis de ambiente do seu provedor de LLM (por exemplo `OPENAI_API_KEY`) antes de iniciar o servidor.
</Note>
</Step>
<Step title="Crie um app Next.js">
Se você ainda não tem um frontend, gere um:
```bash
npx create-next-app@latest my-app
cd my-app
```
Instale o CopilotKit e o cliente AG-UI do CrewAI:
```bash
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @ag-ui/crewai
```
</Step>
<Step title="Adicione o runtime do CopilotKit">
Crie uma rota que registre o(s) seu(s) agente(s) CrewAI no runtime do CopilotKit. Cada agente aponta para um path no seu servidor Python 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="Envolva seu app com o provider">
Aponte `<CopilotKit>` para a rota do runtime e nomeie o agente que você registrou.
```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="Execute">
Inicie os dois processos e abra o app. Conversar na sidebar agora executa o seu Crew ou Flow.
```bash
uvicorn server:app --port 8000 # terminal 1
npm run dev # terminal 2
```
</Step>
</Steps>
## Opções de UI de chat
O CopilotKit entrega três superfícies de chat intercambiáveis. Troque o componente; a fiação é idêntica.
<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>
## Para onde ir em seguida
<CardGroup cols={2}>
<Card title="Generative UI" icon="wand-magic-sparkles" href="/edge/en/guides/frontend/generative-ui">
Renderize chamadas de tool e o estado do agente como componentes personalizados.
</Card>
<Card title="Frontend Actions" icon="bolt" href="/edge/en/guides/frontend/frontend-actions">
Permita que o agente chame funções que rodam no navegador.
</Card>
<Card title="Human-in-the-Loop" icon="user-check" href="/edge/en/guides/frontend/human-in-the-loop">
Restrinja ações do agente por trás da aprovação do usuário.
</Card>
<Card title="Predictive State" icon="gauge-high" href="/edge/en/guides/frontend/predictive-state-updates">
Transmita o estado em andamento para a UI enquanto o agente trabalha.
</Card>
</CardGroup>

View File

@@ -140,7 +140,7 @@ Você pode se conectar a LLMs compatíveis com a OpenAI usando variáveis de amb
# Exemplo usando a API compatível com OpenAI do Gemini.
os.environ["OPENAI_API_KEY"] = "your-gemini-key" # Deve começar com AIza...
os.environ["OPENAI_API_BASE"] = "https://generativelanguage.googleapis.com/v1beta/openai/"
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-2.0-flash" # Adicione aqui seu modelo do Gemini, sob openai/
os.environ["OPENAI_MODEL_NAME"] = "openai/gemini-3.7-flash" # Adicione aqui seu modelo do Gemini, sob openai/
```
</CodeGroup>
</Tab>
@@ -158,7 +158,7 @@ Você pode se conectar a LLMs compatíveis com a OpenAI usando variáveis de amb
```python Google
# Exemplo usando a API compatível com OpenAI do Gemini
llm = LLM(
model="openai/gemini-2.0-flash",
model="openai/gemini-3.7-flash",
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key="your-gemini-key", # Deve começar com AIza...
)

View File

@@ -148,7 +148,7 @@ Agentes de planejamento se beneficiam de modelos de raciocínio para pensamento
from crewai import Agent, Task, Crew, LLM
# Modelo de raciocínio para planejamento estratégico
manager_llm = LLM(model="gemini-2.5-flash-preview-05-20", temperature=0.1)
manager_llm = LLM(model="gemini/gemini-3.7-flash", temperature=0.1)
# Modelo criativo para gerar conteúdo
content_llm = LLM(model="claude-3-5-sonnet-20241022", temperature=0.7)
@@ -413,7 +413,7 @@ Em vez de repetir o framework estratégico, segue um checklist tático para impl
# Agentes gerenciadores ou de coordenação
manager_agent = Agent(
role="Project Manager",
llm=LLM(model="gemini-2.5-flash-preview-05-20"),
llm=LLM(model="gemini/gemini-3.7-flash"),
# ... demais configs
)

View File

@@ -151,7 +151,7 @@ Flows conversacionais podem transmitir um turno de usuário com `stream_turn()`:
```python
from crewai import Flow
from crewai.experimental.conversational import ConversationConfig, ConversationState
from crewai.flow import ConversationConfig, ConversationState
@ConversationConfig(llm="gpt-4o-mini", defer_trace_finalization=True)

View File

@@ -7,9 +7,9 @@ mode: "wide"
# Integração com Arize Phoenix
Este guia demonstra como integrar o **Arize Phoenix** ao **CrewAI** usando o OpenTelemetry através do [OpenInference](https://github.com/openinference/openinference) SDK. Ao final deste guia, você será capaz de rastrear seus agentes CrewAI e depurá-los com facilidade.
Este guia demonstra como integrar o **Arize Phoenix** ao **CrewAI** usando o OpenTelemetry através do [OpenInference](https://github.com/openinference/openinference) SDK. Ao final deste guia, você será capaz de rastrear seus agentes CrewAI e depurar o comportamento dos agentes.
> **O que é o Arize Phoenix?** O [Arize Phoenix](https://phoenix.arize.com) é uma plataforma de observabilidade de LLM que oferece rastreamento e avaliação para aplicações de IA.
> **O que é o Arize Phoenix?** O [Arize Phoenix](https://arize.com/phoenix/) é a opção open-source de observabilidade e avaliação da [Arize AI](https://arize.com/?utm_source=crewai-docs&utm_medium=partner&utm_campaign=partner-docs&utm_content=observability-arize-phoenix). Use o Phoenix quando quiser executar localmente ou fazer self-host. Use o [Arize AX](https://arize.com/products/ax/) para uma plataforma gerenciada em cloud ou enterprise self-hosted para sistemas de IA em produção.
[![Assista a um vídeo demonstrando a nossa integração com o Phoenix](https://storage.googleapis.com/arize-assets/fixtures/setup_crewai.png)](https://www.youtube.com/watch?v=Yc5q3l6F7Ww)
@@ -27,7 +27,7 @@ pip install openinference-instrumentation-crewai crewai crewai-tools arize-phoen
### Passo 2: Configure as Variáveis de Ambiente
Configure as chaves de API do Phoenix Cloud e ajuste o OpenTelemetry para enviar rastros ao Phoenix. O Phoenix Cloud é uma versão hospedada do Arize Phoenix, mas não é obrigatório para utilizar esta integração.
Configure sua chave de API do Phoenix e o endpoint do OpenTelemetry para enviar rastros ao Phoenix. A mesma configuração funciona com um endpoint local ou self-hosted do Phoenix alterando a URL do coletor.
Você pode obter uma chave de API gratuita do Serper [aqui](https://serper.dev/).
@@ -35,8 +35,8 @@ Você pode obter uma chave de API gratuita do Serper [aqui](https://serper.dev/)
import os
from getpass import getpass
# Obtenha suas credenciais do Phoenix Cloud
PHOENIX_API_KEY = getpass("🔑 Digite sua Phoenix Cloud API Key: ")
# Obtenha sua chave de API do Phoenix
PHOENIX_API_KEY = getpass("🔑 Digite sua Phoenix API key: ")
# Obtenha as chaves de API para os serviços
OPENAI_API_KEY = getpass("🔑 Digite sua OpenAI API key: ")
@@ -44,7 +44,7 @@ SERPER_API_KEY = getpass("🔑 Digite sua Serper API key: ")
# Defina as variáveis de ambiente
os.environ["PHOENIX_CLIENT_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Phoenix Cloud, altere para seu endpoint se estiver utilizando uma instância self-hosted
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com" # Altere para seu próprio endpoint se estiver utilizando uma instância self-hosted
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["SERPER_API_KEY"] = SERPER_API_KEY
```
@@ -126,7 +126,7 @@ print(result)
Após executar o agente, você poderá visualizar os rastros gerados pela sua aplicação CrewAI no Phoenix. Você verá etapas detalhadas das interações dos agentes e chamadas de LLM, o que pode ajudar na depuração e otimização dos seus agentes de IA.
Acesse sua conta Phoenix Cloud e navegue até o projeto que você especificou no parâmetro `project_name`. Você verá uma visualização de linha do tempo do seu rastro, incluindo todas as interações dos agentes, uso de ferramentas e chamadas LLM.
Abra seu projeto no Phoenix e navegue até o projeto que você especificou no parâmetro `project_name`. Você verá uma visualização de linha do tempo do seu rastro, incluindo todas as interações dos agentes, uso de ferramentas e chamadas LLM.
![Exemplo de rastro no Phoenix mostrando interações de agentes](https://storage.googleapis.com/arize-assets/fixtures/crewai_traces.png)
@@ -140,6 +140,9 @@ Acesse sua conta Phoenix Cloud e navegue até o projeto que você especificou no
### Referências
- [Documentação do Phoenix](https://docs.arize.com/phoenix/) - Visão geral da plataforma Phoenix.
- [Arize AX](https://arize.com/products/ax/) - Observabilidade e avaliação gerenciadas em cloud ou enterprise self-hosted.
- [Guia de avaliação de agentes da Arize](https://arize.com/guides/ai-agent-handbook/agent-evaluation/) - Workflow de produção para avaliar o comportamento de agentes a partir de rastros.
- [Guia de avaliação de LLM da Arize](https://arize.com/resources/llm-evaluation/) - Métodos e métricas para avaliar aplicações de LLM.
- [Documentação do CrewAI](https://docs.crewai.com/) - Visão geral do framework CrewAI.
- [Documentação do OpenTelemetry](https://opentelemetry.io/docs/) - Guia do OpenTelemetry
- [OpenInference GitHub](https://github.com/openinference/openinference) - Código-fonte do SDK OpenInference.
- [OpenInference GitHub](https://github.com/openinference/openinference) - Código-fonte do SDK OpenInference.

View File

@@ -23,7 +23,7 @@ uso de ferramentas, chamadas de API, respostas, quaisquer dados processados pelo
Quando o recurso `share_crew` está ativado, dados detalhados, incluindo descrições das tarefas, histórias ou objetivos dos agentes e outros atributos específicos são coletados
para fornecer insights mais detalhados. Essa coleta expandida pode incluir informações pessoais caso o usuário as tenha inserido em seus crews ou tarefas.
Usuários devem considerar cuidadosamente o conteúdo de seus crews e tarefas antes de habilitar o `share_crew`.
A telemetria pode ser desabilitada ao definir a variável de ambiente `CREWAI_DISABLE_TELEMETRY` como `true` ou ao definir `OTEL_SDK_DISABLED` como `true` (observe que esta última desabilita toda instrumentação OpenTelemetry globalmente).
A telemetria do CrewAI pode ser desabilitada ao definir `CREWAI_DISABLE_TELEMETRY` como `true`, `1`, `yes` ou `on` (qualquer capitalização). `OTEL_SDK_DISABLED` com os mesmos valores também desabilita o exportador do CrewAI. O SDK do OpenTelemetry em si ainda só reconhece `true` para desabilitar as demais instrumentações do processo.
### Exemplos:
```python
@@ -34,6 +34,8 @@ os.environ['CREWAI_DISABLE_TELEMETRY'] = 'true'
os.environ['OTEL_SDK_DISABLED'] = 'true'
```
`CREWAI_DISABLE_TELEMETRY=1` (ou `yes` / `on`) funciona como `true`. Valores não reconhecidos são ignorados e a telemetria permanece ligada.
### Isolamento da sua própria configuração do OpenTelemetry
A telemetria do CrewAI roda em seu próprio `TracerProvider` privado e nunca se
@@ -52,15 +54,16 @@ por meio do próprio tracer provider, que é independente do descrito aqui.
| Padrão | Dados | Razão e Especificidades |
|--------|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| Sim | Versão do CrewAI e Python | Rastreia versões dos softwares. Exemplo: CrewAI v1.2.3, Python 3.8.10. Sem dados pessoais. |
| Sim | Metadados do Crew | Inclui: chave e ID gerados aleatoriamente, tipo de processo (ex: 'sequential', 'parallel'), flag booleana para uso de memória (true/false), quantidade de tarefas, quantidade de agentes. Tudo não pessoal. |
| Sim | Metadados do Crew | Inclui: chave e ID gerados aleatoriamente, tipo de processo (ex: 'sequential', 'parallel'), flag booleana para uso de memória (true/false), uma flag booleana indicando se alguma entrada foi passada para a execução (true/false — nunca as chaves ou os valores das entradas, que só são coletados quando `share_crew` está habilitado), quantidade de tarefas, quantidade de agentes. Tudo não pessoal. |
| Sim | Dados do Agente | Inclui: chave e ID gerados aleatoriamente, nome da função (não deve incluir info pessoal), configurações booleanas (verbose, delegação habilitada, execução de código permitida), máximo de iterações, máximo de RPM, limite de tentativas, info do LLM (ver Atributos LLM), lista de nomes de ferramentas (não deve conter info pessoal). Sem dados pessoais. |
| Sim | Metadados da Tarefa | Inclui: chave e ID gerados aleatoriamente, configurações de execução booleanas (async_execution, human_input), função e chave do agente associado, lista de nomes de ferramentas. Tudo não pessoal. |
| Sim | Estatísticas de Uso de Ferramentas | Inclui: nome da ferramenta (não deve incluir info pessoal), número de tentativas de uso (inteiro), atributos LLM utilizados. Sem dados pessoais. |
| Sim | Dados de Execução de Testes | Inclui: chave e ID aleatórias do crew, número de iterações, nome do modelo usado, score de qualidade (float), tempo de execução (em segundos). Tudo não pessoal. |
| Sim | Dados do Ciclo de Vida da Tarefa | Inclui: horários de criação, início/fim de execução, identificadores de crew e tarefa. Armazenado como spans com timestamps. Sem dados pessoais. |
| Sim | Dados do Ciclo de Vida da Tarefa | Inclui: horários de criação, início/fim de execução, identificadores de crew e tarefa, e se a tarefa foi bem-sucedida ou falhou. Quando uma tarefa falha, o **nome da classe** da exceção é registrado (por exemplo `TimeoutError`) para que as falhas possam ser contadas e diagnosticadas — nunca a mensagem de erro, que pode conter prompts, saída do modelo, caminhos de arquivos ou credenciais. Armazenado como spans com timestamps. Sem dados pessoais. |
| Sim | Atributos do LLM | Inclui: nome, model_name, model, top_k, temperatura e nome da classe do LLM. Todos técnicos, sem dados pessoais. |
| Sim | Criação de Projeto pelo CLI do crewAI | Inclui: o fato de um novo projeto ter sido criado por `crewai create`, de qual tipo ele é (`crew`, `json_crew` ou `flow`) e o ID de projeto gerado para esse novo projeto e gravado no `pyproject.toml` dele. É o ID do próprio projeto novo, registrado separadamente do `project_id` do diretório de onde o comando foi executado — os dois podem diferir. Sem nome de projeto, sem conteúdo de arquivos, sem código. Sem dados pessoais. |
| Sim | Tentativa de Deploy do Crew pelo CLI do crewAI | Inclui: O fato de um deploy estar sendo realizado e o crew id, se está tentando buscar logs, e se o deploy foi iniciado por um comando do CLI ou pela TUI de execução. Não inclui conteúdo do projeto ou do crew nem dados pessoais. |
| Sim | Ambiente de Execução | Inclui: qual assistente de código com IA está executando o processo, se houver (um de uma lista fixa como `claude_code`, `codex`, `cursor` ou `unknown`), onde o processo é executado (um de uma lista fixa como `ci`, `container`, `serverless`, `interactive`) e o `project_id` do seu `pyproject.toml` quando houver um configurado. A detecção lê apenas se variáveis de ambiente conhecidas estão definidas, nunca seus valores. Sem dados pessoais. |
| Sim | Ambiente de Execução | Inclui: qual assistente de código com IA está executando o processo, se houver (um valor de uma lista fixa como `claude_code`, `codex`, `cursor` ou `unknown`), onde o processo é executado (um valor de uma lista fixa como `ci`, `container`, `serverless`, `interactive`), o `project_id` do seu `pyproject.toml` quando houver um configurado e uma faixa aproximada de tamanho da máquina (uma de `1-2`, `3-4`, `5-8`, `9-16`, `17-32`, `33+` ou `unknown`). A faixa é um intervalo, nunca a contagem exata de núcleos — a contagem exata é opcional, em Informações de Ambiente abaixo. A faixa de tamanho vem da contagem de núcleos do host; a detecção do assistente e do local de execução lê apenas se variáveis de ambiente conhecidas estão definidas, nunca seus valores. Sem dados pessoais. |
| Sim | Sinais de Ciclo de Vida do Flow | Inclui: que um flow iniciou, se foi concluído ou falhou, se um de seus métodos falhou, se pausou para entrada ou feedback humano, se o início foi uma execução retomada, se um turno de conversa falhou, quanto tempo o flow executou, e se o flow é um que a CrewAI executa internamente ou um que você escreveu. O nome do flow é registrado, como já é para criação e execução de flow. Quando um flow ou um de seus métodos falha, o **nome da classe** da exceção é registrado (por exemplo `TimeoutError`) para permitir o diagnóstico de falhas — nunca a mensagem de erro, que pode conter prompts, saída do modelo, caminhos de arquivo ou credenciais. Nomes de métodos e estado do flow nunca são registrados. Nenhum dado pessoal. |
| Sim | Sinal de Compartilhamento de Trace | Inclui: que um lote de traces foi compartilhado com sucesso com o CrewAI AMP, e se foi compartilhado anonimamente (antes de você ter uma conta) ou vinculado à sua conta. Como todo span, também carrega os atributos de Ambiente de Execução descritos acima (`project_id` quando configurado, o assistente de programação e o runtime). Esta linha descreve apenas a telemetria do compartilhamento — não o conteúdo dos traces nem o acesso concedido por links de traces compartilhados. O conteúdo dos traces, entradas e saídas nunca são registrados neste sinal. Antes de compartilhar traces, revise segredos, dados pessoais e as configurações de redação e retenção do AMP. |
| Não | Dados Expandidos do Agente | Inclui: descrição do objetivo, texto da história, identificador de arquivo i18n prompt. Usuários devem garantir que não haja info pessoal nesses campos de texto. |

View File

@@ -50,17 +50,16 @@ Essas ferramentas se integram com serviços de IA e machine learning para aprimo
- **Segurança em IA**: Implemente moderação de conteúdo e checagens de segurança
```python
from crewai_tools import DallETool, VisionTool, CodeInterpreterTool
from crewai_tools import DallETool, VisionTool
# Create AI tools
image_generator = DallETool()
vision_processor = VisionTool()
code_executor = CodeInterpreterTool()
# Add to your agent
agent = Agent(
role="AI Specialist",
tools=[image_generator, vision_processor, code_executor],
tools=[image_generator, vision_processor],
goal="Create and analyze content using AI capabilities"
)
```

View File

@@ -1,117 +1,148 @@
---
title: Channels
description: Run the same CrewAI agent as a chat bot on Slack and Discord with the CopilotKit Channels SDK.
icon: slack
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 bot process drives it.
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/reference/channels) provides that bot process. It ships a platform-agnostic engine plus per-platform adapters.
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 agent server changes. It keeps serving your Crew or Flow over AG-UI exactly as in the Overview. What you add is a separate **bot process**: it connects to a platform adapter, listens for messages, and runs your agent when it is messaged. The reply streams back into the channel.
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 / Discord ──► Channels bot process ──► CrewAI server (AG-UI) ──► Crew / Flow
Slack / Teams ──► CopilotKit Intelligence ──► channel process (Node) ──► CrewAI server (AG-UI) ──► Crew / Flow
```
Your agent server can keep serving the web frontend from the Overview at the same time. The web app and the bot are just two clients of one AG-UI endpoint.
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.
## Slack
## 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/channels-slack @ag-ui/crewai
npm install @copilotkit/channels @copilotkit/runtime @ag-ui/crewai
```
</Step>
<Step title="Create a Slack app and get tokens">
<Step title="Create a Channel in Intelligence">
Create an app in the Slack API dashboard for your workspace, enable Socket Mode, and grant it the message and event scopes it needs to read and post in channels. Then expose its tokens to the bot process:
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 SLACK_BOT_TOKEN=xoxb-... # bot user token
export SLACK_APP_TOKEN=xapp-... # app-level token (Socket Mode)
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="Point the bot at your CrewAI agent">
<Step title="Define the channel">
`createBot` wires a Slack adapter to your agent. The `agent` factory returns a `CrewAIAgent` pointed at the AG-UI path your server exposes (the same URL you registered in the runtime in the Overview).
`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
// bot.ts
import { createBot } from "@copilotkit/channels";
import { slack, defaultSlackTools, defaultSlackContext } from "@copilotkit/channels-slack";
// channel.ts
import { createChannel } from "@copilotkit/channels";
import { CrewAIAgent } from "@ag-ui/crewai";
const bot = createBot({
adapters: [
slack({
botToken: process.env.SLACK_BOT_TOKEN!, // xoxb-…
appToken: process.env.SLACK_APP_TOKEN!, // xapp-… (Socket Mode)
}),
],
agent: (threadId) => new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
tools: [...defaultSlackTools],
context: [...defaultSlackContext],
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;
},
});
bot.start();
// 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="Run the bot">
<Step title="Register the channel on the runtime">
Start the bot process alongside your agent server:
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
node bot.ts # terminal 2 — Slack bot
npx tsx server.ts # terminal 2 — Channels runtime
```
Message the bot in Slack and it runs your Crew or Flow, streaming the reply back into the thread.
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>
<Note>
Slack app scopes, Socket Mode setup, and the full adapter options are maintained by CopilotKit. Follow the [Slack channel reference](https://docs.copilotkit.ai/reference/channels/slack) together with Slack's own app setup guide for the authoritative steps.
</Note>
## The event model
## Discord
A channel reacts to platform events with handlers, and each handler receives a `thread` you drive with a few methods:
Discord uses the same `createBot` engine with the Discord adapter from `@copilotkit/channels-discord`:
- **`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.
```ts
import { createBot } from "@copilotkit/channels";
import { discord } from "@copilotkit/channels-discord";
import { CrewAIAgent } from "@ag-ui/crewai";
const bot = createBot({
adapters: [discord({ token: process.env.DISCORD_BOT_TOKEN! })],
agent: (threadId) => new CrewAIAgent({ url: "http://localhost:8000/recipe" }),
});
bot.start();
```
See the [Discord channel reference](https://docs.copilotkit.ai/reference/channels/discord) for the exact adapter options and bot setup.
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
Slack and Discord have official Channels adapters (`@copilotkit/channels-slack`, `@copilotkit/channels-discord`). Microsoft Teams is available through CopilotKit's managed offering (currently waitlisted). Check the [Channels reference](https://docs.copilotkit.ai/reference/channels) for the current list before promising a platform.
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 messaging platforms connect the same managed way, with your channel code unchanged. Check the [CopilotKit Channels documentation](https://docs.copilotkit.ai/slack) for the current platform list and per-platform setup.
## Related

View File

@@ -21,7 +21,7 @@ The two connect through the [AG-UI protocol](https://docs.ag-ui.com). The `ag-ui
<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="slack" href="/edge/en/guides/frontend/channels">
<Card title="Channels" icon="messages" href="/edge/en/guides/frontend/channels">
Run the same agent as a Slack, Discord, or Teams bot.
</Card>
</CardGroup>

View File

@@ -0,0 +1,8 @@
---
title: "GET /inputs"
description: "الحصول على المدخلات المطلوبة لطاقمك"
openapi: "/v1.15.17/enterprise-api.en.yaml GET /inputs"
mode: "wide"
---

View File

@@ -0,0 +1,135 @@
---
title: "مقدمة"
description: "المرجع الكامل لواجهة برمجة تطبيقات CrewAI AMP REST"
icon: "code"
mode: "wide"
---
# واجهة برمجة تطبيقات CrewAI AMP
مرحبًا بك في مرجع واجهة برمجة تطبيقات CrewAI AMP. تتيح لك هذه الواجهة التفاعل برمجيًا مع الأطقم المنشورة، مما يمكّنك من دمجها مع تطبيقاتك وسير عملك وخدماتك.
## البدء السريع
<Steps>
<Step title="الحصول على بيانات اعتماد API">
انتقل إلى صفحة تفاصيل طاقمك في لوحة تحكم CrewAI AMP وانسخ رمز Bearer من علامة تبويب الحالة.
</Step>
<Step title="اكتشاف المدخلات المطلوبة">
استخدم نقطة النهاية `GET /inputs` لمعرفة المعاملات التي يتوقعها طاقمك.
</Step>
<Step title="بدء تنفيذ الطاقم">
استدعِ `POST /kickoff` مع مدخلاتك لبدء تنفيذ الطاقم واستلام
`kickoff_id`.
</Step>
<Step title="مراقبة التقدم">
استخدم `GET /status/{kickoff_id}` للتحقق من حالة التنفيذ واسترجاع النتائج.
</Step>
</Steps>
## المصادقة
تتطلب جميع طلبات API المصادقة باستخدام رمز Bearer. أدرج رمزك في ترويسة `Authorization`:
```bash
curl -H "Authorization: Bearer YOUR_CREW_TOKEN" \
https://your-crew-url.crewai.com/inputs
```
### أنواع الرموز
| نوع الرمز | النطاق | حالة الاستخدام |
| :-------------------- | :------------------------ | :----------------------------------------------------------- |
| **Bearer Token** | وصول على مستوى المؤسسة | عمليات الطاقم الكاملة، مثالي للتكامل بين الخوادم |
| **User Bearer Token** | وصول محدد بالمستخدم | صلاحيات محدودة، مناسب للعمليات الخاصة بالمستخدم |
<Tip>
يمكنك العثور على كلا نوعي الرموز في علامة تبويب الحالة من صفحة تفاصيل طاقمك في
لوحة تحكم CrewAI AMP.
</Tip>
## عنوان URL الأساسي
لكل طاقم منشور نقطة نهاية API فريدة خاصة به:
```
https://your-crew-name.crewai.com
```
استبدل `your-crew-name` بعنوان URL الفعلي لطاقمك من لوحة التحكم.
## سير العمل النموذجي
1. **الاكتشاف**: استدعِ `GET /inputs` لفهم ما يحتاجه طاقمك
2. **التنفيذ**: أرسل المدخلات عبر `POST /kickoff` لبدء المعالجة
3. **المراقبة**: استعلم عن `GET /status/{kickoff_id}` حتى الاكتمال
4. **النتائج**: استخرج المخرجات النهائية من الاستجابة المكتملة
## معالجة الأخطاء
تستخدم الواجهة أكواد حالة HTTP القياسية:
| الكود | المعنى |
| ----- | :----------------------------------------- |
| `200` | نجاح |
| `400` | طلب غير صالح - تنسيق مدخلات غير صحيح |
| `401` | غير مصرّح - رمز bearer غير صالح |
| `404` | غير موجود - المورد غير موجود |
| `422` | خطأ في التحقق - مدخلات مطلوبة مفقودة |
| `500` | خطأ في الخادم - تواصل مع الدعم |
## الاختبار التفاعلي
<Info>
**لماذا لا يوجد زر "إرسال"؟** نظرًا لأن كل مستخدم CrewAI AMP لديه عنوان URL
فريد للطاقم، نستخدم **وضع المرجع** بدلاً من بيئة تفاعلية لتجنب
الالتباس. يوضح لك هذا بالضبط كيف يجب أن تبدو الطلبات بدون
أزرار إرسال غير فعالة.
</Info>
تعرض لك كل صفحة نقطة نهاية:
- **تنسيق الطلب الدقيق** مع جميع المعاملات
- **أمثلة الاستجابة** لحالات النجاح والخطأ
- **عينات الكود** بلغات متعددة (cURL، Python، JavaScript، إلخ)
- **أمثلة المصادقة** بتنسيق رمز Bearer الصحيح
### **لاختبار واجهتك الفعلية:**
<CardGroup cols={2}>
<Card title="نسخ أمثلة cURL" icon="terminal">
انسخ أمثلة cURL واستبدل العنوان URL + الرمز بقيمك الحقيقية
</Card>
<Card title="استخدام Postman/Insomnia" icon="play">
استورد الأمثلة في أداة اختبار API المفضلة لديك
</Card>
</CardGroup>
**مثال على سير العمل:**
1. **انسخ مثال cURL هذا** من أي صفحة نقطة نهاية
2. **استبدل `your-actual-crew-name.crewai.com`** بعنوان URL الحقيقي لطاقمك
3. **استبدل رمز Bearer** برمزك الحقيقي من لوحة التحكم
4. **نفّذ الطلب** في طرفيتك أو عميل API
## هل تحتاج مساعدة؟
<CardGroup cols={2}>
<Card
title="دعم المؤسسات"
icon="headset"
href="mailto:support@crewai.com"
>
احصل على مساعدة في تكامل API واستكشاف الأخطاء وإصلاحها
</Card>
<Card
title="لوحة تحكم المؤسسات"
icon="chart-line"
href="https://app.crewai.com"
>
إدارة أطقمك وعرض سجلات التنفيذ
</Card>
</CardGroup>

View File

@@ -0,0 +1,8 @@
---
title: "POST /kickoff"
description: "بدء تنفيذ الطاقم"
openapi: "/v1.15.17/enterprise-api.en.yaml POST /kickoff"
mode: "wide"
---

View File

@@ -0,0 +1,6 @@
---
title: "POST /resume"
description: "استئناف تنفيذ الطاقم مع التغذية الراجعة البشرية"
openapi: "/v1.15.17/enterprise-api.en.yaml POST /resume"
mode: "wide"
---

View File

@@ -0,0 +1,6 @@
---
title: "GET /status/{kickoff_id}"
description: "الحصول على حالة التنفيذ"
openapi: "/v1.15.17/enterprise-api.en.yaml GET /status/{kickoff_id}"
mode: "wide"
---

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
---
title: "قدرات الوكيل"
description: "فهم الطرق الخمس لتوسيع وكلاء CrewAI: الأدوات، MCP، التطبيقات، المهارات، والمعرفة."
icon: puzzle-piece
mode: "wide"
---
## نظرة عامة
يمكن توسيع وكلاء CrewAI بـ **خمسة أنواع مميزة من القدرات**، كل منها يخدم غرضًا مختلفًا. فهم متى تستخدم كل نوع — وكيف يعملون معًا — هو المفتاح لبناء وكلاء فعّالين.
<CardGroup cols={2}>
<Card title="الأدوات" icon="wrench" href="/ar/concepts/tools" color="#3B82F6">
**دوال قابلة للاستدعاء** — تمنح الوكلاء القدرة على اتخاذ إجراءات. البحث على الويب، عمليات الملفات، استدعاءات API، تنفيذ الكود.
</Card>
<Card title="خوادم MCP" icon="plug" href="/ar/mcp/overview" color="#8B5CF6">
**خوادم أدوات عن بُعد** — تربط الوكلاء بخوادم أدوات خارجية عبر Model Context Protocol. نفس تأثير الأدوات، لكن مستضافة خارجيًا.
</Card>
<Card title="التطبيقات" icon="grid-2" color="#EC4899">
**تكاملات المنصة** — تربط الوكلاء بتطبيقات SaaS (Gmail، Slack، Jira، Salesforce) عبر منصة CrewAI. تعمل محليًا مع رمز تكامل المنصة.
</Card>
<Card title="المهارات" icon="bolt" href="/ar/concepts/skills" color="#F59E0B">
**خبرة المجال** — تحقن التعليمات والإرشادات والمواد المرجعية في إرشادات الوكلاء. المهارات تخبر الوكلاء *كيف يفكرون*.
</Card>
<Card title="المعرفة" icon="book" href="/ar/concepts/knowledge" color="#10B981">
**حقائق مُسترجعة** — توفر للوكلاء بيانات من المستندات والملفات وعناوين URL عبر البحث الدلالي (RAG). المعرفة تعطي الوكلاء *ما يحتاجون معرفته*.
</Card>
</CardGroup>
---
## التمييز الأساسي
أهم شيء يجب فهمه: **هذه القدرات تنقسم إلى فئتين**.
### قدرات الإجراء (الأدوات، MCP، التطبيقات)
تمنح الوكلاء القدرة على **فعل أشياء** — استدعاء APIs، قراءة الملفات، البحث على الويب، إرسال رسائل البريد الإلكتروني. عند التنفيذ، تتحول الأنواع الثلاثة إلى نفس التنسيق الداخلي (مثيلات `BaseTool`) وتظهر في قائمة أدوات موحدة يمكن للوكيل استدعاؤها.
```python
from crewai import Agent
from crewai_tools import SerperDevTool, FileReadTool
agent = Agent(
role="Researcher",
goal="Find and compile market data",
backstory="Expert market analyst",
tools=[SerperDevTool(), FileReadTool()], # أدوات محلية
mcps=["https://mcp.example.com/sse"], # أدوات خادم MCP عن بُعد
apps=["gmail", "google_sheets"], # تكاملات المنصة
)
```
### قدرات السياق (المهارات، المعرفة)
تُعدّل **إرشادات** الوكيل — بحقن الخبرة أو التعليمات أو البيانات المُسترجعة قبل أن يبدأ الوكيل في التفكير. لا تمنح الوكلاء إجراءات جديدة؛ بل تُشكّل كيف يفكر الوكلاء وما هي المعلومات التي يمكنهم الوصول إليها.
```python
from crewai import Agent
agent = Agent(
role="Security Auditor",
goal="Audit cloud infrastructure for vulnerabilities",
backstory="Expert in cloud security with 10 years of experience",
skills=["./skills/security-audit"], # تعليمات المجال
knowledge_sources=[pdf_source, url_source], # حقائق مُسترجعة
)
```
---
## متى تستخدم ماذا
| تحتاج إلى... | استخدم | مثال |
| :------------------------------------------------------- | :---------------- | :--------------------------------------- |
| الوكيل يبحث على الويب | **الأدوات** | `tools=[SerperDevTool()]` |
| الوكيل يستدعي API عن بُعد عبر MCP | **MCP** | `mcps=["https://api.example.com/sse"]` |
| الوكيل يرسل بريد إلكتروني عبر Gmail | **التطبيقات** | `apps=["gmail"]` |
| الوكيل يتبع إجراءات محددة | **المهارات** | `skills=["./skills/code-review"]` |
| الوكيل يرجع لمستندات الشركة | **المعرفة** | `knowledge_sources=[pdf_source]` |
| الوكيل يبحث على الويب ويتبع إرشادات المراجعة | **الأدوات + المهارات** | استخدم كليهما معًا |
---
## دمج القدرات
في الممارسة العملية، غالبًا ما يستخدم الوكلاء **أنواعًا متعددة من القدرات معًا**. إليك مثال واقعي:
```python
from crewai import Agent
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
# وكيل بحث مجهز بالكامل
researcher = Agent(
role="Senior Research Analyst",
goal="Produce comprehensive market analysis reports",
backstory="Expert analyst with deep industry knowledge",
# الإجراء: ما يمكن للوكيل فعله
tools=[
SerperDevTool(), # البحث على الويب
FileReadTool(), # قراءة الملفات المحلية
CodeInterpreterTool(), # تشغيل كود Python للتحليل
],
mcps=["https://data-api.example.com/sse"], # الوصول لـ API بيانات عن بُعد
apps=["google_sheets"], # الكتابة في Google Sheets
# السياق: ما يعرفه الوكيل
skills=["./skills/research-methodology"], # كيفية إجراء البحث
knowledge_sources=[company_docs], # بيانات خاصة بالشركة
)
```
---
## جدول المقارنة
| الميزة | الأدوات | MCP | التطبيقات | المهارات | المعرفة |
| :--- | :---: | :---: | :---: | :---: | :---: |
| **يمنح الوكيل إجراءات** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **يُعدّل الإرشادات** | ❌ | ❌ | ❌ | ✅ | ✅ |
| **يتطلب كود** | نعم | إعداد فقط | إعداد فقط | Markdown فقط | إعداد فقط |
| **يعمل محليًا** | نعم | يعتمد | نعم (مع متغير بيئة) | غير متاح | نعم |
| **يحتاج مفاتيح API** | لكل أداة | لكل خادم | رمز التكامل | لا | المُضمّن فقط |
| **يُعيَّن على Agent** | `tools=[]` | `mcps=[]` | `apps=[]` | `skills=[]` | `knowledge_sources=[]` |
| **يُعيَّن على Crew** | ❌ | ❌ | ❌ | `skills=[]` | `knowledge_sources=[]` |
---
## تعمّق أكثر
هل أنت مستعد لمعرفة المزيد عن كل نوع من أنواع القدرات؟
<CardGroup cols={2}>
<Card title="الأدوات" icon="wrench" href="/ar/concepts/tools">
إنشاء أدوات مخصصة، استخدام كتالوج OSS مع أكثر من 75 خيارًا، تكوين التخزين المؤقت والتنفيذ غير المتزامن.
</Card>
<Card title="تكامل MCP" icon="plug" href="/ar/mcp/overview">
الاتصال بخوادم MCP عبر stdio أو SSE أو HTTP. تصفية الأدوات، تكوين المصادقة.
</Card>
<Card title="المهارات" icon="bolt" href="/ar/concepts/skills">
بناء حزم المهارات مع SKILL.md، حقن خبرة المجال، استخدام الكشف التدريجي.
</Card>
<Card title="المعرفة" icon="book" href="/ar/concepts/knowledge">
إضافة المعرفة من ملفات PDF وCSV وعناوين URL والمزيد. تكوين المُضمّنات والاسترجاع.
</Card>
</CardGroup>

View File

@@ -0,0 +1,383 @@
---
title: الوكلاء
description: دليل تفصيلي حول إنشاء وإدارة الوكلاء ضمن إطار عمل CrewAI.
icon: robot
mode: "wide"
---
## نظرة عامة على الوكيل
في إطار عمل CrewAI، الـ `Agent` هو وحدة مستقلة يمكنها:
- أداء مهام محددة
- اتخاذ قرارات بناءً على دوره وهدفه
- استخدام الأدوات لتحقيق الأهداف
- التواصل والتعاون مع وكلاء آخرين
- الاحتفاظ بذاكرة التفاعلات
- تفويض المهام عند السماح بذلك
<Tip>
فكّر في الوكيل كعضو فريق متخصص بمهارات وخبرات ومسؤوليات محددة.
على سبيل المثال، قد يتفوق وكيل `Researcher` في جمع وتحليل المعلومات،
بينما قد يكون وكيل `Writer` أفضل في إنشاء المحتوى.
</Tip>
<Note type="info" title="تحسين المؤسسات: منشئ الوكلاء المرئي">
يتضمن CrewAI AMP منشئ وكلاء مرئي يبسّط إنشاء وتهيئة الوكلاء بدون كتابة كود. صمم وكلاءك بصريًا واختبرهم في الوقت الفعلي.
![Visual Agent Builder Screenshot](/images/enterprise/crew-studio-interface.png)
يُمكّن منشئ الوكلاء المرئي من:
- تهيئة وكلاء بديهية بواجهات نماذج
- اختبار والتحقق في الوقت الفعلي
- مكتبة قوالب مع أنواع وكلاء مهيأة مسبقًا
- تخصيص سهل لخصائص وسلوكيات الوكيل
</Note>
## خصائص الوكيل
| الخاصية | المعامل | النوع | الوصف |
| :-------------------------------------- | :----------------------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------- |
| **الدور** | `role` | `str` | يحدد وظيفة الوكيل وخبرته ضمن الطاقم. |
| **الهدف** | `goal` | `str` | الهدف الفردي الذي يوجه عملية اتخاذ القرار لدى الوكيل. |
| **الخلفية** | `backstory` | `str` | يوفر سياقًا وشخصية للوكيل، مما يثري التفاعلات. |
| **LLM** _(اختياري)_ | `llm` | `Union[str, LLM, Any]` | نموذج اللغة الذي يشغّل الوكيل. افتراضيًا النموذج المحدد في `OPENAI_MODEL_NAME` أو "gpt-4". |
| **الأدوات** _(اختياري)_ | `tools` | `List[BaseTool]` | القدرات أو الوظائف المتاحة للوكيل. افتراضيًا قائمة فارغة. |
| **LLM استدعاء الدوال** _(اختياري)_ | `function_calling_llm` | `Optional[Any]` | نموذج لغة لاستدعاء الأدوات، يتجاوز LLM الطاقم إذا حُدد. |
| **الحد الأقصى للتكرارات** _(اختياري)_ | `max_iter` | `int` | الحد الأقصى للتكرارات قبل أن يقدم الوكيل أفضل إجابته. الافتراضي 20. |
| **الحد الأقصى لـ RPM** _(اختياري)_ | `max_rpm` | `Optional[int]` | الحد الأقصى للطلبات في الدقيقة لتجنب حدود المعدل. |
| **الحد الأقصى لوقت التنفيذ** _(اختياري)_ | `max_execution_time` | `Optional[int]` | الحد الأقصى للوقت (بالثواني) لتنفيذ المهمة. |
| **الوضع المفصل** _(اختياري)_ | `verbose` | `bool` | تفعيل سجلات التنفيذ المفصلة للتصحيح. الافتراضي False. |
| **السماح بالتفويض** _(اختياري)_ | `allow_delegation` | `bool` | السماح للوكيل بتفويض المهام لوكلاء آخرين. الافتراضي False. |
| **دالة الخطوة** _(اختياري)_ | `step_callback` | `Optional[Any]` | دالة تُستدعى بعد كل خطوة للوكيل، تتجاوز دالة الطاقم. |
| **التخزين المؤقت** _(اختياري)_ | `cache` | `bool` | تفعيل التخزين المؤقت لاستخدام الأدوات. الافتراضي True. |
| **قالب النظام** _(اختياري)_ | `system_template` | `Optional[str]` | قالب أمر نظام مخصص للوكيل. |
| **قالب الأمر** _(اختياري)_ | `prompt_template` | `Optional[str]` | قالب أمر مخصص للوكيل. |
| **قالب الاستجابة** _(اختياري)_ | `response_template` | `Optional[str]` | قالب استجابة مخصص للوكيل. |
| **السماح بتنفيذ الكود** _(اختياري)_ | `allow_code_execution` | `Optional[bool]` | تفعيل تنفيذ الكود للوكيل. الافتراضي False. |
| **الحد الأقصى لإعادة المحاولة** _(اختياري)_ | `max_retry_limit` | `int` | الحد الأقصى لإعادات المحاولة عند حدوث خطأ. الافتراضي 2. |
| **احترام نافذة السياق** _(اختياري)_ | `respect_context_window` | `bool` | إبقاء الرسائل تحت حجم نافذة السياق عبر التلخيص. الافتراضي True. |
| **وضع تنفيذ الكود** _(اختياري)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | وضع تنفيذ الكود: 'safe' (باستخدام Docker) أو 'unsafe' (مباشر). الافتراضي 'safe'. |
| **متعدد الوسائط** _(اختياري)_ | `multimodal` | `bool` | ما إذا كان الوكيل يدعم القدرات متعددة الوسائط. الافتراضي False. |
| **حقن التاريخ** _(اختياري)_ | `inject_date` | `bool` | ما إذا كان يتم حقن التاريخ الحالي تلقائيًا في أمر الوكيل. الافتراضي False. |
| **تنسيق التاريخ** _(اختياري)_ | `date_format` | `str` | سلسلة تنسيق التاريخ عند تفعيل inject_date. الافتراضي "%Y-%m-%d" (تنسيق ISO). |
| **الاستدلال** _(اختياري)_ | `reasoning` | `bool` | ما إذا كان يجب على الوكيل التأمل وإنشاء خطة قبل تنفيذ المهمة. الافتراضي False. |
| **الحد الأقصى لمحاولات الاستدلال** _(اختياري)_ | `max_reasoning_attempts` | `Optional[int]` | الحد الأقصى لمحاولات الاستدلال قبل تنفيذ المهمة. إذا None، سيحاول حتى الاستعداد. |
| **المُضمّن** _(اختياري)_ | `embedder` | `Optional[Dict[str, Any]]` | تهيئة المُضمّن المستخدم من قبل الوكيل. |
| **مصادر المعرفة** _(اختياري)_ | `knowledge_sources` | `Optional[List[BaseKnowledgeSource]]` | مصادر المعرفة المتاحة للوكيل. |
| **استخدام أمر النظام** _(اختياري)_ | `use_system_prompt` | `Optional[bool]` | ما إذا كان يُستخدم أمر النظام (لدعم نموذج o1). الافتراضي True. |
## إنشاء الوكلاء
هناك طريقتان شائعتان لإنشاء الوكلاء في CrewAI: باستخدام **تهيئة JSONC (الموصى بها للـ crews الجديدة)** أو تعريفهم **مباشرة في الكود**.
### تهيئة JSONC (موصى بها)
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` تستخدم تهيئة JSON-first. يُعرّف كل Agent في `agents/<agent_name>.jsonc`، ويحدد `crew.jsonc` أي Agents تدخل في الـ crew.
```jsonc agents/researcher.jsonc
{
"role": "{topic} Senior Data Researcher",
"goal": "Uncover cutting-edge developments in {topic}",
"backstory": "You find the most relevant information and present it clearly.",
"llm": "openai/gpt-4o",
"tools": ["SerperDevTool"],
"settings": {
"verbose": true,
"allow_delegation": false
}
}
```
استخدم `{placeholder}` داخل `role` أو `goal` أو `backstory`. ضع القيم الافتراضية في `inputs` داخل `crew.jsonc`؛ وسيطلب `crewai run` أي قيم ناقصة. يمكن وضع حقول السلوك مثل `verbose` و `allow_delegation` و `max_iter` و `memory` و `cache` و `planning_config` في المستوى الأعلى أو داخل `settings`.
<Note>
يدعم JSONC التعليقات والفواصل النهائية. إذا وُجد `agents/<name>.jsonc` و `agents/<name>.json` معًا، يستخدم CrewAI ملف JSONC.
</Note>
### تهيئة YAML الكلاسيكية
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew <name> --classic` تستخدم `config/agents.yaml` وفئة `@CrewBase` في `crew.py`.
تظل تهيئة YAML مدعومة للمشاريع الحالية المبنية بـ Python/YAML وللفِرق التي تفضل تعريف الوكلاء من خلال فئة `@CrewBase`.
بعد إنشاء مشروع كلاسيكي، انتقل إلى ملف `src/<project_name>/config/agents.yaml` وعدّل القالب ليتوافق مع متطلباتك.
<Note>
ستُستبدل المتغيرات في ملفات YAML (مثل `{topic}`) بقيم من مدخلاتك عند تشغيل الطاقم:
```python Code
crew.kickoff(inputs={'topic': 'AI Agents'})
```
</Note>
إليك مثالًا على كيفية تهيئة الوكلاء باستخدام YAML:
```yaml agents.yaml
# src/<project_name>/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.
```
لاستخدام تهيئة YAML في الكود، أنشئ فئة طاقم ترث من `CrewBase`:
```python Code
# src/<project_name>/crew.py
from crewai import Agent, Crew, Process
from crewai.project import CrewBase, agent, crew
from crewai_tools import SerperDevTool
@CrewBase
class LatestAiDevelopmentCrew():
"""LatestAiDevelopment crew"""
agents_config = "config/agents.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config['researcher'], # type: ignore[index]
verbose=True,
tools=[SerperDevTool()]
)
@agent
def reporting_analyst(self) -> Agent:
return Agent(
config=self.agents_config['reporting_analyst'], # type: ignore[index]
verbose=True
)
```
<Note>
يجب أن تتطابق الأسماء المستخدمة في ملفات YAML (`agents.yaml`) مع أسماء
الطرق في كود Python.
</Note>
### تعريف مباشر في الكود
يمكنك إنشاء الوكلاء مباشرة في الكود بإنشاء فئة `Agent`. إليك مثالًا شاملًا يوضح جميع المعاملات المتاحة:
```python Code
from crewai import Agent
from crewai_tools import SerperDevTool
# إنشاء وكيل بجميع المعاملات المتاحة
agent = Agent(
role="Senior Data Scientist",
goal="Analyze and interpret complex datasets to provide actionable insights",
backstory="With over 10 years of experience in data science and machine learning, "
"you excel at finding patterns in complex datasets.",
llm="gpt-4",
function_calling_llm=None,
verbose=False,
allow_delegation=False,
max_iter=20,
max_rpm=None,
max_execution_time=None,
max_retry_limit=2,
allow_code_execution=False,
code_execution_mode="safe",
respect_context_window=True,
use_system_prompt=True,
multimodal=False,
inject_date=False,
date_format="%Y-%m-%d",
reasoning=False,
max_reasoning_attempts=None,
tools=[SerperDevTool()],
knowledge_sources=None,
embedder=None,
system_template=None,
prompt_template=None,
response_template=None,
step_callback=None,
)
```
دعنا نستعرض بعض تركيبات المعاملات الرئيسية لحالات الاستخدام الشائعة:
#### وكيل بحث أساسي
```python Code
research_agent = Agent(
role="Research Analyst",
goal="Find and summarize information about specific topics",
backstory="You are an experienced researcher with attention to detail",
tools=[SerperDevTool()],
verbose=True
)
```
#### وكيل تطوير الكود
```python Code
dev_agent = Agent(
role="Senior Python Developer",
goal="Write and debug Python code",
backstory="Expert Python developer with 10 years of experience",
allow_code_execution=True,
code_execution_mode="safe",
max_execution_time=300,
max_retry_limit=3
)
```
#### وكيل تحليل طويل المدى
```python Code
analysis_agent = Agent(
role="Data Analyst",
goal="Perform deep analysis of large datasets",
backstory="Specialized in big data analysis and pattern recognition",
memory=True,
respect_context_window=True,
max_rpm=10,
function_calling_llm="gpt-4o-mini"
)
```
### تفاصيل المعاملات
#### المعاملات الحرجة
- `role` و `goal` و `backstory` مطلوبة وتشكّل سلوك الوكيل
- `llm` يحدد نموذج اللغة المستخدم (افتراضي: GPT-4 من OpenAI)
#### الذاكرة والسياق
- `memory`: تفعيل للحفاظ على سجل المحادثة
- `respect_context_window`: يمنع مشاكل حد الرموز
- `knowledge_sources`: إضافة قواعد معرفة خاصة بالمجال
#### التحكم في التنفيذ
- `max_iter`: الحد الأقصى للمحاولات قبل تقديم أفضل إجابة
- `max_execution_time`: المهلة بالثواني
- `max_rpm`: تحديد معدل استدعاءات API
- `max_retry_limit`: إعادات المحاولة عند الخطأ
#### تنفيذ الكود
<Warning>
`allow_code_execution` و`code_execution_mode` مهجوران. تمت إزالة `CodeInterpreterTool` من `crewai-tools`. استخدم خدمة بيئة معزولة مخصصة مثل [E2B](https://e2b.dev) أو [Modal](https://modal.com) لتنفيذ الكود بأمان.
</Warning>
- `allow_code_execution` _(مهجور)_: كان يُمكّن تنفيذ الكود المدمج عبر `CodeInterpreterTool`.
- `code_execution_mode` _(مهجور)_: كان يتحكم في وضع التنفيذ (`"safe"` لـ Docker، `"unsafe"` للتنفيذ المباشر).
#### الميزات المتقدمة
- `multimodal`: تفعيل القدرات متعددة الوسائط لمعالجة النص والمحتوى المرئي
- `reasoning`: تمكين الوكيل من التأمل وإنشاء خطط قبل تنفيذ المهام
- `inject_date`: حقن التاريخ الحالي تلقائيًا في أمر الوكيل
#### القوالب
- `system_template`: يحدد السلوك الأساسي للوكيل
- `prompt_template`: ينظم تنسيق الإدخال
- `response_template`: ينسّق استجابات الوكيل
<Note>
عند استخدام القوالب المخصصة، تأكد من تعريف كل من `system_template` و
`prompt_template`. `response_template` اختياري لكن يُوصى به
لتنسيق مخرجات متسق.
</Note>
## أدوات الوكيل
يمكن تجهيز الوكلاء بأدوات متنوعة لتعزيز قدراتهم. يدعم CrewAI أدوات من:
- [مجموعة أدوات CrewAI](https://github.com/joaomdmoura/crewai-tools)
- [أدوات LangChain](https://python.langchain.com/docs/integrations/tools)
إليك كيفية إضافة أدوات لوكيل:
```python Code
from crewai import Agent
from crewai_tools import SerperDevTool, WikipediaTools
# إنشاء الأدوات
search_tool = SerperDevTool()
wiki_tool = WikipediaTools()
# إضافة أدوات للوكيل
researcher = Agent(
role="AI Technology Researcher",
goal="Research the latest AI developments",
tools=[search_tool, wiki_tool],
verbose=True
)
```
## التفاعل المباشر مع الوكيل عبر `kickoff()`
يمكن استخدام الوكلاء مباشرة بدون المرور بمهمة أو سير عمل طاقم باستخدام طريقة `kickoff()`. يوفر هذا طريقة أبسط للتفاعل مع وكيل عندما لا تحتاج إلى إمكانيات تنسيق الطاقم الكاملة.
```python Code
from crewai import Agent
from crewai_tools import SerperDevTool
# إنشاء وكيل
researcher = Agent(
role="AI Technology Researcher",
goal="Research the latest AI developments",
tools=[SerperDevTool()],
verbose=True
)
# استخدام kickoff() للتفاعل مباشرة مع الوكيل
result = researcher.kickoff("What are the latest developments in language models?")
# الوصول إلى الاستجابة الخام
print(result.raw)
```
## اعتبارات مهمة وأفضل الممارسات
### الأمان وتنفيذ الكود
<Warning>
`allow_code_execution` و`code_execution_mode` مهجوران وتمت إزالة `CodeInterpreterTool`. استخدم خدمة بيئة معزولة مخصصة مثل [E2B](https://e2b.dev) أو [Modal](https://modal.com) لتنفيذ الكود بأمان.
</Warning>
### تحسين الأداء
- استخدم `respect_context_window: true` لمنع مشاكل حد الرموز
- عيّن `max_rpm` مناسبًا لتجنب تحديد المعدل
- فعّل `cache: true` لتحسين الأداء للمهام المتكررة
- اضبط `max_iter` و `max_retry_limit` بناءً على تعقيد المهمة
### إدارة الذاكرة والسياق
- استفد من `knowledge_sources` للمعلومات الخاصة بالمجال
- هيّئ `embedder` عند استخدام نماذج تضمين مخصصة
- استخدم القوالب المخصصة للتحكم الدقيق في سلوك الوكيل
### التعاون بين الوكلاء
- فعّل `allow_delegation: true` عندما يحتاج الوكلاء للعمل معًا
- استخدم `step_callback` لمراقبة وتسجيل تفاعلات الوكلاء
- فكّر في استخدام نماذج LLM مختلفة لأغراض مختلفة
### توافق النموذج
- عيّن `use_system_prompt: false` للنماذج القديمة التي لا تدعم رسائل النظام
- تأكد من أن `llm` المختار يدعم الميزات التي تحتاجها

View File

@@ -0,0 +1,423 @@
---
title: Checkpointing
description: حفظ حالة التنفيذ تلقائيا حتى تتمكن الطواقم والتدفقات والوكلاء من الاستئناف بعد الفشل.
icon: floppy-disk
mode: "wide"
---
الـ Checkpointing يحفظ لقطة من حالة التنفيذ أثناء التشغيل بحيث يمكن لطاقم أو تدفق أو وكيل الاستئناف بعد الفشل أو التفرع إلى فرع بديل.
<CardGroup cols={2}>
<Card title="الشرح" icon="lightbulb" href="#الشرح">
كيف يعمل الـ Checkpointing: الأحداث والتخزين والوراثة.
</Card>
<Card title="درس تطبيقي" icon="graduation-cap" href="#درس-تطبيقي-استئناف-طاقم-فاشل">
دليل 5 دقائق: تشغيل، إيقاف، استئناف.
</Card>
<Card title="ادلة عملية" icon="screwdriver-wrench" href="#ادلة-عملية">
وصفات مركزة على المهام لسير العمل الشائع.
</Card>
<Card title="المرجع" icon="book" href="#المرجع">
`CheckpointConfig` والأحداث والمزودات وسطر الأوامر.
</Card>
</CardGroup>
## الشرح
### ما هي نقطة الحفظ
تلتقط نقطة الحفظ كل ما يحتاجه CrewAI لإعادة إنشاء تشغيل أثناء سيره: الحالة الكاملة للطاقم أو التدفق أو الوكيل — التكوين، وذاكرة الوكلاء ومصادر المعرفة، وتقدم المهام، والمخرجات الوسيطة، والحالة الداخلية والسمات — إلى جانب مدخلات الـ kickoff، وسجل الأحداث حتى تلك النقطة، ومعرف نسب يربط نقطة الحفظ بالتشغيل الذي جاءت منه.
الاستعادة تعيد بناء تلك الحالة وتستمر. تتخطى المهام المكتملة، وتعاد ترطيب الذاكرة والمعرفة، ويعمل العمل التابع على نفس المخرجات التي أنتجها التشغيل الأصلي. التفرع يجري نفس الاستعادة تحت نسب جديد، بحيث يكتب الفرع الجديد والتشغيل الأصلي نقاط الحفظ جنبا إلى جنب دون أن يطمس أحدهما الآخر.
### متى تكتب نقاط الحفظ
الـ Checkpointing مدفوع بالأحداث. يشترك وقت التشغيل في الأحداث التي تحددها عبر `on_events` ويكتب نقطة حفظ عند إطلاق أحدها. الافتراضي `task_completed` ينتج نقطة حفظ لكل مهمة منتهية — توازن معقول بين الدقة واستخدام القرص. الأحداث عالية التردد مثل `llm_call_completed` متاحة للاستعادة الدقيقة لكنها تكتب ملفات أكثر بكثير.
### التخزين
يتضمن CrewAI مزودين:
- `JsonProvider` يكتب ملفا لكل نقطة حفظ. قابل للقراءة وسهل التفقد.
- `SqliteProvider` يكتب إلى قاعدة بيانات SQLite واحدة. أفضل لنقاط الحفظ عالية التردد.
كلاهما يحذف أقدم نقاط الحفظ عند تحديد `max_checkpoints`.
<Note>
كتابة نقاط الحفظ بأفضل جهد. فشل نقطة حفظ يسجل لكنه لا يقاطع التشغيل.
</Note>
### نموذج الوراثة
`Crew` و`Flow` و`Agent` كلها تقبل وسيط `checkpoint`. يرث الأبناء من الأب ما لم يحددوا قيمتهم الخاصة أو يمرروا `False` للانسحاب. فعل الـ Checkpointing مرة واحدة على الطاقم وتشارك كل الوكلاء، أو استبعد وكيلا واحدا بشكل انتقائي.
## درس تطبيقي: استئناف طاقم فاشل
هذا الدليل يستغرق حوالي 5 دقائق. ستشغل طاقما بمهمتين، توقفه في المنتصف، ثم تستأنف من نقطة الحفظ المحفوظة.
<Steps>
<Step title="أنشئ الطاقم مع تفعيل الـ Checkpointing">
```python
from crewai import Agent, Crew, Task
researcher = Agent(role="Researcher", goal="Research", backstory="Expert")
writer = Agent(role="Writer", goal="Write", backstory="Expert")
crew = Crew(
agents=[researcher, writer],
tasks=[
Task(description="Research AI trends", agent=researcher, expected_output="bullets"),
Task(description="Write a summary", agent=writer, expected_output="paragraph"),
],
checkpoint=True,
)
```
</Step>
<Step title="شغله وأوقفه بعد المهمة الأولى">
```python
result = crew.kickoff()
```
اضغط `Ctrl+C` بعد انتهاء المهمة الأولى. في `./.checkpoints/`، الملف بصيغة `<timestamp>_<uuid>.json` هو نقطة الحفظ.
</Step>
<Step title="استأنف من نقطة الحفظ">
```python
from crewai import CheckpointConfig
result = crew.kickoff(
from_checkpoint=CheckpointConfig(
restore_from="./.checkpoints/<timestamp>_<uuid>.json",
),
)
```
يتم تخطي مهمة البحث، ويعمل الكاتب على مخرجات البحث المحفوظة، وينتهي الطاقم.
</Step>
</Steps>
## ادلة عملية
<AccordionGroup>
<Accordion title="تفعيل الـ Checkpointing بالإعدادات الافتراضية" icon="play">
```python
crew = Crew(agents=[...], tasks=[...], checkpoint=True)
```
يكتب إلى `./.checkpoints/` عند كل `task_completed`.
</Accordion>
<Accordion title="تخصيص التخزين والتردد" icon="sliders">
```python
from crewai import Crew, CheckpointConfig
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
location="./my_checkpoints",
on_events=["task_completed", "crew_kickoff_completed"],
max_checkpoints=5,
),
)
```
</Accordion>
<Accordion title="اختيار مزود التخزين" icon="database">
<CodeGroup>
```python JsonProvider
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
location="./my_checkpoints",
provider=JsonProvider(),
max_checkpoints=5,
),
)
```
```python SqliteProvider
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider
crew = Crew(
agents=[...],
tasks=[...],
checkpoint=CheckpointConfig(
location="./.checkpoints.db",
provider=SqliteProvider(),
max_checkpoints=50,
),
)
```
</CodeGroup>
<Tip>
SQLite يفعل وضع journal WAL للقراءات المتزامنة. يفضل لنقاط الحفظ عالية التردد.
</Tip>
</Accordion>
<Accordion title="استبعاد وكيل واحد" icon="user-slash">
```python
crew = Crew(
agents=[
Agent(role="Researcher", ...),
Agent(role="Writer", ..., checkpoint=False),
],
tasks=[...],
checkpoint=True,
)
```
</Accordion>
<Accordion title="التفرع إلى فرع جديد" icon="code-branch">
`fork()` يستعيد نقطة حفظ تحت نسب جديد بحيث لا يتصادم التشغيل الجديد مع الأصلي.
```python
config = CheckpointConfig(restore_from="./my_checkpoints/<file>.json")
crew = Crew.fork(config, branch="experiment-a")
result = crew.kickoff(inputs={"strategy": "aggressive"})
```
تسمية `branch` اختيارية؛ يتم إنشاء واحدة إذا أغفلت.
</Accordion>
<Accordion title="Checkpointing لـ Crew أو Flow أو Agent" icon="cubes">
<Tabs>
<Tab title="Crew">
```python
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task, review_task],
checkpoint=CheckpointConfig(location="./crew_cp"),
)
```
المشغل الافتراضي: `task_completed`.
</Tab>
<Tab title="Flow">
```python
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig
class MyFlow(Flow):
@start()
def step_one(self):
return "data"
@listen(step_one)
def step_two(self, data):
return process(data)
flow = MyFlow(
checkpoint=CheckpointConfig(
location="./flow_cp",
on_events=["method_execution_finished"],
),
)
result = flow.kickoff()
```
</Tab>
<Tab title="Agent">
```python
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
checkpoint=CheckpointConfig(
location="./agent_cp",
on_events=["lite_agent_execution_completed"],
),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])
```
</Tab>
</Tabs>
</Accordion>
<Accordion title="كتابة نقطة حفظ يدويا" icon="code">
سجل معالجا على أي حدث واستدع `state.checkpoint()`.
<CodeGroup>
```python Sync
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
if TYPE_CHECKING:
from crewai.state.runtime import RuntimeState
@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source: Any, event: LLMCallCompletedEvent, state: RuntimeState) -> None:
path = state.checkpoint("./my_checkpoints")
print(f"تم حفظ نقطة الحفظ: {path}")
```
```python Async
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent
if TYPE_CHECKING:
from crewai.state.runtime import RuntimeState
@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source: Any, event: LLMCallCompletedEvent, state: RuntimeState) -> None:
path = await state.acheckpoint("./my_checkpoints")
print(f"تم حفظ نقطة الحفظ: {path}")
```
</CodeGroup>
يتم تمرير وسيط `state` تلقائيا عندما يقبل المعالج ثلاثة معاملات. راجع [Event Listeners](/ar/concepts/event-listener) لقائمة الأحداث الكاملة.
</Accordion>
<Accordion title="التصفح والاستئناف والتفرع من سطر الأوامر" icon="terminal">
```bash
crewai checkpoint
crewai checkpoint --location ./my_checkpoints
crewai checkpoint --location ./.checkpoints.db
```
<Frame caption="شجرة نقاط الحفظ — الفروع والتفرعات تتداخل تحت أبيها.">
<img src="/images/checkpoint-tui-tree.png" alt="Checkpoint TUI tree view" />
</Frame>
اللوحة اليسرى تجمع نقاط الحفظ حسب الفرع؛ التفرعات تتداخل تحت أبيها. اختيار نقطة حفظ يفتح لوحة التفاصيل مع بياناتها الوصفية وحالة الكيان وتقدم المهام. **Resume** يكمل التشغيل؛ **Fork** يبدأ فرعا جديدا.
<Frame caption="تبويب النظرة العامة — البيانات الوصفية وحالة الكيان وملخص التشغيل.">
<img src="/images/checkpoint-tui-detail-overview.png" alt="Checkpoint detail overview tab" />
</Frame>
لوحة التفاصيل تعرض منطقتين قابلتين للتحرير:
- **Inputs** — مدخلات الـ kickoff الأصلية، معبأة مسبقا وقابلة للتحرير.
<Frame>
<img src="/images/checkpoint-tui-detail-inputs.png" alt="Editable kickoff inputs" />
</Frame>
- **مخرجات المهام** — مخرجات المهام المكتملة. تحرير مخرج والضغط على **Fork** يبطل المهام التابعة لتعاد بالسياق المعدل.
<Frame>
<img src="/images/checkpoint-tui-detail-tasks.png" alt="Editable task outputs" />
</Frame>
<Frame caption="عرض التفرع — تأكيد فرع جديد من نقطة الحفظ المختارة.">
<img src="/images/checkpoint-tui-details-fork.png" alt="Fork confirmation panel" />
</Frame>
<Tip>
مفيد لاستكشاف "ماذا لو": تفرع، عدل، راقب.
</Tip>
</Accordion>
<Accordion title="تفقد نقاط الحفظ بدون TUI" icon="magnifying-glass">
```bash
crewai checkpoint list ./my_checkpoints
crewai checkpoint info ./my_checkpoints/<file>.json
crewai checkpoint info ./.checkpoints.db
```
</Accordion>
</AccordionGroup>
## المرجع
### `CheckpointConfig`
<ParamField path="location" type="str" default='"./.checkpoints"'>
وجهة التخزين. مجلد لـ `JsonProvider`، مسار ملف قاعدة بيانات لـ `SqliteProvider`.
</ParamField>
<ParamField path="on_events" type='list[CheckpointEventType | Literal["*"]]' default='["task_completed"]'>
أنواع الأحداث التي تطلق نقطة حفظ. `CheckpointEventType` هو `Literal` — مدقق الأنواع يكمل تلقائيا ويرفض القيم غير المدعومة. راجع [أنواع الأحداث](#أنواع-الأحداث) للقائمة الكاملة.
</ParamField>
<ParamField path="provider" type="BaseProvider" default="JsonProvider()">
واجهة التخزين. `JsonProvider` أو `SqliteProvider`.
</ParamField>
<ParamField path="max_checkpoints" type="int | None" default="None">
الحد الاقصى لنقاط الحفظ المحتفظ بها. الأقدم تحذف بعد كل كتابة.
</ParamField>
<ParamField path="restore_from" type="Path | str | None" default="None">
نقطة الحفظ المراد استعادتها عند تمريرها عبر `from_checkpoint`.
</ParamField>
### قيم حقل `checkpoint`
مقبولة في `Crew` و`Flow` و`Agent`.
<ParamField path="None" type="افتراضي">
يرث من الأب.
</ParamField>
<ParamField path="True" type="bool">
تفعيل بالإعدادات الافتراضية.
</ParamField>
<ParamField path="False" type="bool">
انسحاب صريح. يوقف الوراثة.
</ParamField>
<ParamField path="CheckpointConfig(...)" type="CheckpointConfig">
إعدادات مخصصة.
</ParamField>
### أنواع الأحداث
يقبل `on_events` أي مجموعة من قيم `CheckpointEventType`. الافتراضي `["task_completed"]` يكتب نقطة حفظ لكل مهمة منتهية، و`["*"]` يطابق جميع الأحداث.
<Warning>
`["*"]` والأحداث عالية التردد مثل `llm_call_completed` تكتب نقاط حفظ كثيرة وقد تضر بالاداء. استخدمها مع `max_checkpoints`.
</Warning>
<Expandable title="جميع الأحداث المدعومة">
- **Task** — `task_started`, `task_completed`, `task_failed`, `task_evaluation`
- **Crew** — `crew_kickoff_started`, `crew_kickoff_completed`, `crew_kickoff_failed`, `crew_train_started`, `crew_train_completed`, `crew_train_failed`, `crew_test_started`, `crew_test_completed`, `crew_test_failed`, `crew_test_result`
- **Agent** — `agent_execution_started`, `agent_execution_completed`, `agent_execution_error`, `lite_agent_execution_started`, `lite_agent_execution_completed`, `lite_agent_execution_error`, `agent_evaluation_started`, `agent_evaluation_completed`, `agent_evaluation_failed`
- **Flow** — `flow_created`, `flow_started`, `flow_finished`, `flow_paused`, `method_execution_started`, `method_execution_finished`, `method_execution_failed`, `method_execution_paused`, `human_feedback_requested`, `human_feedback_received`, `flow_input_requested`, `flow_input_received`
- **LLM** — `llm_call_started`, `llm_call_completed`, `llm_call_failed`, `llm_stream_chunk`, `llm_thinking_chunk`
- **LLM Guardrail** — `llm_guardrail_started`, `llm_guardrail_completed`, `llm_guardrail_failed`
- **Tool** — `tool_usage_started`, `tool_usage_finished`, `tool_usage_error`, `tool_validate_input_error`, `tool_selection_error`, `tool_execution_error`
- **Memory** — `memory_save_started`, `memory_save_completed`, `memory_save_failed`, `memory_query_started`, `memory_query_completed`, `memory_query_failed`, `memory_retrieval_started`, `memory_retrieval_completed`, `memory_retrieval_failed`
- **Knowledge** — `knowledge_search_query_started`, `knowledge_search_query_completed`, `knowledge_query_started`, `knowledge_query_completed`, `knowledge_query_failed`, `knowledge_search_query_failed`
- **Reasoning** — `agent_reasoning_started`, `agent_reasoning_completed`, `agent_reasoning_failed`
- **MCP** — `mcp_connection_started`, `mcp_connection_completed`, `mcp_connection_failed`, `mcp_tool_execution_started`, `mcp_tool_execution_completed`, `mcp_tool_execution_failed`, `mcp_config_fetch_failed`
- **Observation** — `step_observation_started`, `step_observation_completed`, `step_observation_failed`, `plan_refinement`, `plan_replan_triggered`, `goal_achieved_early`
- **Skill** — `skill_discovery_started`, `skill_discovery_completed`, `skill_loaded`, `skill_activated`, `skill_load_failed`
- **Logging** — `agent_logs_started`, `agent_logs_execution`
- **A2A** — `a2a_delegation_started`, `a2a_delegation_completed`, `a2a_conversation_started`, `a2a_conversation_completed`, `a2a_message_sent`, `a2a_response_received`, `a2a_polling_started`, `a2a_polling_status`, `a2a_push_notification_registered`, `a2a_push_notification_received`, `a2a_push_notification_sent`, `a2a_push_notification_timeout`, `a2a_streaming_started`, `a2a_streaming_chunk`, `a2a_agent_card_fetched`, `a2a_authentication_failed`, `a2a_artifact_received`, `a2a_connection_error`, `a2a_server_task_started`, `a2a_server_task_completed`, `a2a_server_task_canceled`, `a2a_server_task_failed`, `a2a_parallel_delegation_started`, `a2a_parallel_delegation_completed`, `a2a_transport_negotiated`, `a2a_content_type_negotiated`, `a2a_context_created`, `a2a_context_expired`, `a2a_context_idle`, `a2a_context_completed`, `a2a_context_pruned`
- **إشارات النظام** — `SIGTERM`, `SIGINT`, `SIGHUP`, `SIGTSTP`, `SIGCONT`
- **حرف بدل** — `"*"` يطابق جميع الأحداث.
</Expandable>
### مزودات التخزين
<ParamField path="JsonProvider" type="provider">
ملف واحد لكل نقطة حفظ بصيغة `<timestamp>_<uuid>.json` داخل `location`.
</ParamField>
<ParamField path="SqliteProvider" type="provider">
ملف قاعدة بيانات واحد في `location` مع journaling WAL.
</ParamField>
### سطر الأوامر
| الامر | الغرض |
|:------|:------|
| `crewai checkpoint` | تشغيل TUI؛ كشف التخزين تلقائيا. |
| `crewai checkpoint --location <path>` | تشغيل TUI على موقع محدد. |
| `crewai checkpoint list <path>` | سرد نقاط الحفظ. |
| `crewai checkpoint info <path>` | تفقد ملف نقطة حفظ أو آخر مدخل في قاعدة بيانات SQLite. |

View File

@@ -0,0 +1,312 @@
---
title: واجهة سطر الأوامر
description: تعرّف على كيفية استخدام واجهة سطر أوامر CrewAI للتفاعل مع CrewAI.
icon: terminal
mode: "wide"
---
<Warning>
منذ الإصدار 0.140.0، بدأ CrewAI AMP عملية نقل مزود تسجيل الدخول.
لذلك، تم تحديث تدفق المصادقة عبر CLI. المستخدمون الذين يسجلون الدخول
باستخدام Google، أو الذين أنشأوا حساباتهم بعد 3 يوليو 2025 لن يتمكنوا
من تسجيل الدخول مع الإصدارات القديمة من مكتبة `crewai`.
</Warning>
## نظرة عامة
توفر واجهة سطر أوامر CrewAI مجموعة من الأوامر للتفاعل مع CrewAI، مما يتيح لك إنشاء وتدريب وتشغيل وإدارة الأطقم والتدفقات.
## التثبيت
لاستخدام واجهة سطر أوامر CrewAI، تأكد من تثبيت CrewAI:
```shell Terminal
pip install crewai
```
## الاستخدام الأساسي
الهيكل الأساسي لأمر CrewAI CLI هو:
```shell Terminal
crewai [COMMAND] [OPTIONS] [ARGUMENTS]
```
## الأوامر المتاحة
### 1. إنشاء
إنشاء طاقم أو تدفق جديد.
```shell Terminal
crewai create [OPTIONS] TYPE NAME
```
- `TYPE`: اختر بين "crew" أو "flow"
- `NAME`: اسم الطاقم أو التدفق
مثال:
```shell Terminal
crewai create crew my_new_crew
crewai create flow my_new_flow
```
افتراضيًا، ينشئ `crewai create crew` مشروعًا JSON-first يحتوي على `crew.jsonc` و `agents/*.jsonc`. استخدم `crewai create crew my_new_crew --classic` فقط إذا أردت البنية القديمة Python/YAML مع `crew.py` و `config/agents.yaml` و `config/tasks.yaml`.
#### أسماء مستعار قديمة للأعلام (مهملة)
لا تزال أعلام 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.
```shell Terminal
crewai version [OPTIONS]
```
- `--tools`: (اختياري) عرض الإصدار المثبت من أدوات CrewAI
### 3. التدريب
تدريب الطاقم لعدد محدد من التكرارات.
```shell Terminal
crewai train [OPTIONS]
```
- `-n, --n-iterations INTEGER`: عدد تكرارات التدريب (افتراضي: 5)
- `-f, --filename TEXT`: مسار ملف مخصص للتدريب (افتراضي: "trained_agents_data.pkl")
### 4. الإعادة
إعادة تنفيذ الطاقم من مهمة محددة.
```shell Terminal
crewai replay [OPTIONS]
```
- `-t, --task-id TEXT`: إعادة تنفيذ الطاقم من معرّف المهمة هذا، بما في ذلك جميع المهام اللاحقة
### 5. سجل مخرجات المهام
استرجاع أحدث مخرجات مهام crew.kickoff().
```shell Terminal
crewai log-tasks-outputs
```
### 6. إعادة تعيين الذاكرة
إعادة تعيين ذاكرة الطاقم (طويلة، قصيرة، الكيانات، أحدث مخرجات التشغيل).
```shell Terminal
crewai reset-memories [OPTIONS]
```
- `-l, --long`: إعادة تعيين الذاكرة طويلة المدى
- `-s, --short`: إعادة تعيين الذاكرة قصيرة المدى
- `-e, --entities`: إعادة تعيين ذاكرة الكيانات
- `-k, --kickoff-outputs`: إعادة تعيين أحدث مخرجات التشغيل
- `-kn, --knowledge`: إعادة تعيين تخزين المعرفة
- `-akn, --agent-knowledge`: إعادة تعيين تخزين معرفة الوكيل
- `-a, --all`: إعادة تعيين جميع الذاكرات
### 7. الاختبار
اختبار الطاقم وتقييم النتائج.
```shell Terminal
crewai test [OPTIONS]
```
- `-n, --n-iterations INTEGER`: عدد تكرارات الاختبار (افتراضي: 3)
- `-m, --model TEXT`: نموذج LLM لتشغيل الاختبارات (افتراضي: "gpt-4o-mini")
### 8. التشغيل
تشغيل الطاقم أو التدفق.
```shell Terminal
crewai run
```
<Note>
بدءًا من الإصدار 0.103.0، يمكن استخدام أمر `crewai run` لتشغيل
كل من الأطقم القياسية والتدفقات. للتدفقات، يكتشف تلقائيًا النوع
من pyproject.toml ويشغّل الأمر المناسب. هذه هي الطريقة الموصى بها
لتشغيل كل من الأطقم والتدفقات.
</Note>
### 9. الدردشة
بدءًا من الإصدار `0.98.0`، عند تشغيل أمر `crewai chat`، تبدأ جلسة تفاعلية مع طاقمك. سيرشدك المساعد الذكي بطلب المدخلات اللازمة لتنفيذ الطاقم. بمجرد توفير جميع المدخلات، سينفذ الطاقم مهامه.
```shell Terminal
crewai chat
```
<Note>
مهم: عيّن خاصية `chat_llm` في تعريف الـ crew لتفعيل هذا الأمر.
للـ crews بنمط JSON-first، أضفها إلى `crew.jsonc`:
```jsonc
{
"name": "My Crew",
"agents": ["researcher"],
"tasks": [],
"chat_llm": "openai/gpt-4o"
}
```
للـ crews الكلاسيكية Python/YAML، عيّنها في `crew.py`:
```python
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
chat_llm="gpt-4o",
)
```
</Note>
### 10. النشر
نشر الطاقم أو التدفق إلى [CrewAI AMP](https://app.crewai.com).
- **المصادقة**: تحتاج لتكون مصادقًا للنشر إلى CrewAI AMP.
```shell Terminal
crewai login
```
- **إنشاء نشر**:
```shell Terminal
crewai deploy create
```
- **نشر الطاقم**:
```shell Terminal
crewai deploy push
```
- **حالة النشر**:
```shell Terminal
crewai deploy status
```
- **سجلات النشر**:
```shell Terminal
crewai deploy logs
```
- **عرض النشرات**:
```shell Terminal
crewai deploy list
```
- **حذف النشر**:
```shell Terminal
crewai deploy remove
```
### 11. إدارة المؤسسة
إدارة مؤسسات CrewAI AMP.
```shell Terminal
crewai org [COMMAND] [OPTIONS]
```
- `list`: عرض جميع المؤسسات
- `current`: عرض المؤسسة النشطة حاليًا
- `switch`: التبديل إلى مؤسسة محددة
### 12. تسجيل الدخول
المصادقة مع CrewAI AMP باستخدام تدفق رمز الجهاز الآمن.
```shell Terminal
crewai login
```
### 13. إدارة التهيئة
إدارة إعدادات تهيئة CLI لـ CrewAI.
```shell Terminal
crewai config [COMMAND] [OPTIONS]
```
- `list`: عرض جميع معاملات التهيئة
- `set`: تعيين معامل تهيئة
- `reset`: إعادة تعيين جميع المعاملات إلى القيم الافتراضية
### 14. إدارة التتبع
إدارة تفضيلات جمع التتبع لعمليات الطاقم والتدفق.
```shell Terminal
crewai traces [COMMAND]
```
- `enable`: تفعيل جمع التتبع
- `disable`: تعطيل جمع التتبع
- `status`: عرض حالة جمع التتبع الحالية
#### كيف يعمل التتبع
يتم التحكم في جمع التتبع بفحص ثلاثة إعدادات بترتيب الأولوية:
1. **علامة صريحة في الكود** (الأولوية الأعلى):
```python
crew = Crew(agents=[...], tasks=[...], tracing=True) # تفعيل دائمًا
crew = Crew(agents=[...], tasks=[...], tracing=False) # تعطيل دائمًا
crew = Crew(agents=[...], tasks=[...]) # فحص الأولويات الأدنى
```
2. **متغير البيئة** (الأولوية الثانية):
```env
CREWAI_TRACING_ENABLED=true
```
3. **تفضيل المستخدم** (الأولوية الأدنى):
```shell Terminal
crewai traces enable
```
<Note>
**لتفعيل التتبع**، استخدم أيًا من هذه الطرق:
- عيّن `tracing=True` في كود الطاقم/التدفق، أو
- أضف `CREWAI_TRACING_ENABLED=true` إلى ملف `.env`، أو
- شغّل `crewai traces enable`
**لتعطيل التتبع**، استخدم أيًا من هذه الطرق:
- عيّن `tracing=False` في كود الطاقم/التدفق، أو
- أزل أو عيّن `false` لمتغير `CREWAI_TRACING_ENABLED`، أو
- شغّل `crewai traces disable`
</Note>
<Tip>
يتعامل CrewAI CLI مع المصادقة لمستودع الأدوات تلقائيًا عند
إضافة حزم إلى مشروعك. فقط أضف `crewai` قبل أي أمر `uv`
لاستخدامه. مثلًا `crewai uv add requests`.
</Tip>
<Note>
تُخزن إعدادات التهيئة في `~/.config/crewai/settings.json`. بعض
الإعدادات مثل اسم المؤسسة ومعرّفها للقراءة فقط وتُدار من خلال
أوامر المصادقة والمؤسسة.
</Note>

View File

@@ -0,0 +1,363 @@
---
title: التعاون
description: كيفية تمكين الوكلاء من العمل معًا وتفويض المهام والتواصل بفعالية داخل فرق CrewAI.
icon: screen-users
mode: "wide"
---
## نظرة عامة
يُمكّن التعاون في CrewAI الوكلاء من العمل معًا كفريق عن طريق تفويض المهام وطرح الأسئلة للاستفادة من خبرات بعضهم البعض. عندما يكون `allow_delegation=True`، يحصل الوكلاء تلقائيًا على أدوات تعاون قوية.
## البدء السريع: تفعيل التعاون
```python
from crewai import Agent, Crew, Task
# تفعيل التعاون للوكلاء
researcher = Agent(
role="Research Specialist",
goal="Conduct thorough research on any topic",
backstory="Expert researcher with access to various sources",
allow_delegation=True, # الإعداد الرئيسي للتعاون
verbose=True
)
writer = Agent(
role="Content Writer",
goal="Create engaging content based on research",
backstory="Skilled writer who transforms research into compelling content",
allow_delegation=True, # يُمكّن طرح الأسئلة على الوكلاء الآخرين
verbose=True
)
# يمكن للوكلاء الآن التعاون تلقائيًا
crew = Crew(
agents=[researcher, writer],
tasks=[...],
verbose=True
)
```
## كيف يعمل تعاون الوكلاء
عندما يكون `allow_delegation=True`، يوفر CrewAI تلقائيًا للوكلاء أداتين قويتين:
### 1. **أداة تفويض العمل**
تسمح للوكلاء بتعيين مهام لزملاء الفريق ذوي الخبرة المحددة.
```python
# يحصل الوكيل تلقائيًا على هذه الأداة:
# Delegate work to coworker(task: str, context: str, coworker: str)
```
### 2. **أداة طرح الأسئلة**
تُمكّن الوكلاء من طرح أسئلة محددة لجمع المعلومات من الزملاء.
```python
# يحصل الوكيل تلقائيًا على هذه الأداة:
# Ask question to coworker(question: str, context: str, coworker: str)
```
## التعاون في الممارسة
إليك مثالًا كاملًا يوضح تعاون الوكلاء في مهمة إنشاء المحتوى:
```python
from crewai import Agent, Crew, Task, Process
# إنشاء وكلاء تعاونيين
researcher = Agent(
role="Research Specialist",
goal="Find accurate, up-to-date information on any topic",
backstory="""You're a meticulous researcher with expertise in finding
reliable sources and fact-checking information across various domains.""",
allow_delegation=True,
verbose=True
)
writer = Agent(
role="Content Writer",
goal="Create engaging, well-structured content",
backstory="""You're a skilled content writer who excels at transforming
research into compelling, readable content for different audiences.""",
allow_delegation=True,
verbose=True
)
editor = Agent(
role="Content Editor",
goal="Ensure content quality and consistency",
backstory="""You're an experienced editor with an eye for detail,
ensuring content meets high standards for clarity and accuracy.""",
allow_delegation=True,
verbose=True
)
# إنشاء مهمة تشجع التعاون
article_task = Task(
description="""Write a comprehensive 1000-word article about 'The Future of AI in Healthcare'.
The article should include:
- Current AI applications in healthcare
- Emerging trends and technologies
- Potential challenges and ethical considerations
- Expert predictions for the next 5 years
Collaborate with your teammates to ensure accuracy and quality.""",
expected_output="A well-researched, engaging 1000-word article with proper structure and citations",
agent=writer # الكاتب يقود، لكن يمكنه تفويض البحث إلى الباحث
)
# إنشاء طاقم تعاوني
crew = Crew(
agents=[researcher, writer, editor],
tasks=[article_task],
process=Process.sequential,
verbose=True
)
result = crew.kickoff()
```
## أنماط التعاون
### النمط 1: بحث ← كتابة ← تحرير
```python
research_task = Task(
description="Research the latest developments in quantum computing",
expected_output="Comprehensive research summary with key findings and sources",
agent=researcher
)
writing_task = Task(
description="Write an article based on the research findings",
expected_output="Engaging 800-word article about quantum computing",
agent=writer,
context=[research_task] # يحصل على مخرجات البحث كسياق
)
editing_task = Task(
description="Edit and polish the article for publication",
expected_output="Publication-ready article with improved clarity and flow",
agent=editor,
context=[writing_task] # يحصل على مسودة المقال كسياق
)
```
### النمط 2: مهمة واحدة تعاونية
```python
collaborative_task = Task(
description="""Create a marketing strategy for a new AI product.
Writer: Focus on messaging and content strategy
Researcher: Provide market analysis and competitor insights
Work together to create a comprehensive strategy.""",
expected_output="Complete marketing strategy with research backing",
agent=writer # الوكيل القائد، لكن يمكنه التفويض إلى الباحث
)
```
## التعاون الهرمي
للمشاريع المعقدة، استخدم عملية هرمية مع وكيل مدير:
```python
from crewai import Agent, Crew, Task, Process
# وكيل المدير ينسق الفريق
manager = Agent(
role="Project Manager",
goal="Coordinate team efforts and ensure project success",
backstory="Experienced project manager skilled at delegation and quality control",
allow_delegation=True,
verbose=True
)
# وكلاء متخصصون
researcher = Agent(
role="Researcher",
goal="Provide accurate research and analysis",
backstory="Expert researcher with deep analytical skills",
allow_delegation=False, # المتخصصون يركزون على خبرتهم
verbose=True
)
writer = Agent(
role="Writer",
goal="Create compelling content",
backstory="Skilled writer who creates engaging content",
allow_delegation=False,
verbose=True
)
# مهمة يقودها المدير
project_task = Task(
description="Create a comprehensive market analysis report with recommendations",
expected_output="Executive summary, detailed analysis, and strategic recommendations",
agent=manager # المدير سيفوّض إلى المتخصصين
)
# طاقم هرمي
crew = Crew(
agents=[manager, researcher, writer],
tasks=[project_task],
process=Process.hierarchical, # المدير ينسق كل شيء
manager_llm="gpt-4o", # تحديد LLM للمدير
verbose=True
)
```
## أفضل ممارسات التعاون
### 1. **تحديد الأدوار بوضوح**
```python
# جيد: أدوار محددة ومتكاملة
researcher = Agent(role="Market Research Analyst", ...)
writer = Agent(role="Technical Content Writer", ...)
# تجنب: أدوار متداخلة أو غامضة
agent1 = Agent(role="General Assistant", ...)
agent2 = Agent(role="Helper", ...)
```
### 2. **تفعيل التفويض الاستراتيجي**
```python
# فعّل التفويض للمنسقين والعامين
lead_agent = Agent(
role="Content Lead",
allow_delegation=True, # يمكنه التفويض إلى المتخصصين
...
)
# عطّل للمتخصصين المركّزين (اختياري)
specialist_agent = Agent(
role="Data Analyst",
allow_delegation=False, # يركز على الخبرة الأساسية
...
)
```
### 3. **مشاركة السياق**
```python
# استخدم معامل context لاعتماديات المهام
writing_task = Task(
description="Write article based on research",
agent=writer,
context=[research_task], # يشارك نتائج البحث
...
)
```
### 4. **أوصاف المهام الواضحة**
```python
# أوصاف محددة وقابلة للتنفيذ
Task(
description="""Research competitors in the AI chatbot space.
Focus on: pricing models, key features, target markets.
Provide data in a structured format.""",
...
)
# تجنب: أوصاف غامضة لا توجه التعاون
Task(description="Do some research about chatbots", ...)
```
## استكشاف أخطاء التعاون وإصلاحها
### المشكلة: الوكلاء لا يتعاونون
**الأعراض:** يعمل الوكلاء بمعزل، لا يحدث تفويض
```python
# الحل: تأكد من تفعيل التفويض
agent = Agent(
role="...",
allow_delegation=True, # هذا مطلوب!
...
)
```
### المشكلة: كثرة الذهاب والإياب
**الأعراض:** يطرح الوكلاء أسئلة مفرطة، تقدم بطيء
```python
# الحل: وفّر سياقًا أفضل وأدوارًا محددة
Task(
description="""Write a technical blog post about machine learning.
Context: Target audience is software developers with basic ML knowledge.
Length: 1200 words
Include: code examples, practical applications, best practices
If you need specific technical details, delegate research to the researcher.""",
...
)
```
### المشكلة: حلقات التفويض
**الأعراض:** يفوّض الوكلاء ذهابًا وإيابًا بلا نهاية
```python
# الحل: تسلسل هرمي واضح ومسؤوليات
manager = Agent(role="Manager", allow_delegation=True)
specialist1 = Agent(role="Specialist A", allow_delegation=False) # لا إعادة تفويض
specialist2 = Agent(role="Specialist B", allow_delegation=False)
```
## ميزات التعاون المتقدمة
### قواعد التعاون المخصصة
```python
# تعيين إرشادات تعاون محددة في خلفية الوكيل
agent = Agent(
role="Senior Developer",
backstory="""You lead development projects and coordinate with team members.
Collaboration guidelines:
- Delegate research tasks to the Research Analyst
- Ask the Designer for UI/UX guidance
- Consult the QA Engineer for testing strategies
- Only escalate blocking issues to the Project Manager""",
allow_delegation=True
)
```
### مراقبة التعاون
```python
def track_collaboration(output):
"""تتبع أنماط التعاون"""
if "Delegate work to coworker" in output.raw:
print("Delegation occurred")
if "Ask question to coworker" in output.raw:
print("Question asked")
crew = Crew(
agents=[...],
tasks=[...],
step_callback=track_collaboration, # مراقبة التعاون
verbose=True
)
```
## الذاكرة والتعلم
تمكين الوكلاء من تذكر التعاونات السابقة:
```python
agent = Agent(
role="Content Lead",
memory=True, # يتذكر التفاعلات السابقة
allow_delegation=True,
verbose=True
)
```
مع تفعيل الذاكرة، يتعلم الوكلاء من التعاونات السابقة ويحسّنون قرارات التفويض بمرور الوقت.
## الخطوات التالية
- **جرّب الأمثلة**: ابدأ بمثال التعاون الأساسي
- **جرّب أدوارًا مختلفة**: اختبر تركيبات أدوار وكلاء مختلفة
- **راقب التفاعلات**: استخدم `verbose=True` لرؤية التعاون في العمل
- **حسّن أوصاف المهام**: المهام الواضحة تؤدي إلى تعاون أفضل
- **وسّع النطاق**: جرّب العمليات الهرمية للمشاريع المعقدة
يحوّل التعاون وكلاء الذكاء الاصطناعي الفرديين إلى فرق قوية يمكنها معالجة التحديات المعقدة ومتعددة الأوجه معًا.

View File

@@ -0,0 +1,247 @@
---
title: الأطقم
description: فهم واستخدام الأطقم في إطار عمل CrewAI مع خصائص ووظائف شاملة.
icon: people-group
mode: "wide"
---
## نظرة عامة
يمثل الطاقم في CrewAI مجموعة تعاونية من الوكلاء يعملون معًا لتحقيق مجموعة من المهام. يحدد كل طاقم استراتيجية تنفيذ المهام وتعاون الوكلاء وسير العمل العام.
## خصائص الطاقم
| الخاصية | المعامل | الوصف |
| :------------------------------------ | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **المهام** | `tasks` | قائمة المهام المعيّنة للطاقم. |
| **الوكلاء** | `agents` | قائمة الوكلاء الذين يشكلون جزءًا من الطاقم. |
| **العملية** _(اختياري)_ | `process` | تدفق العملية (مثل تسلسلي، هرمي) الذي يتبعه الطاقم. الافتراضي `sequential`. |
| **الوضع المفصل** _(اختياري)_ | `verbose` | مستوى التفصيل في التسجيل أثناء التنفيذ. الافتراضي `False`. |
| **LLM المدير** _(اختياري)_ | `manager_llm` | نموذج اللغة المستخدم بواسطة وكيل المدير في العملية الهرمية. **مطلوب عند استخدام العملية الهرمية.** |
| **LLM استدعاء الدوال** _(اختياري)_ | `function_calling_llm` | إذا مُرر، سيستخدم الطاقم هذا LLM لاستدعاء دوال الأدوات لجميع الوكلاء. يمكن لكل وكيل أن يكون له LLM خاص يتجاوز LLM الطاقم. |
| **التهيئة** _(اختياري)_ | `config` | إعدادات تهيئة اختيارية للطاقم، بتنسيق `Json` أو `Dict[str, Any]`. |
| **الحد الأقصى لـ RPM** _(اختياري)_ | `max_rpm` | الحد الأقصى للطلبات في الدقيقة. الافتراضي `None`. |
| **الذاكرة** _(اختياري)_ | `memory` | تُستخدم لتخزين ذاكرات التنفيذ (قصيرة المدى، طويلة المدى، ذاكرة الكيانات). |
| **التخزين المؤقت** _(اختياري)_ | `cache` | يحدد ما إذا كان يُستخدم تخزين مؤقت لنتائج تنفيذ الأدوات. الافتراضي `True`. |
| **المُضمّن** _(اختياري)_ | `embedder` | تهيئة المُضمّن المستخدم من قبل الطاقم. الافتراضي `{"provider": "openai"}`. |
| **دالة الخطوة** _(اختياري)_ | `step_callback` | دالة تُستدعى بعد كل خطوة لكل وكيل. |
| **دالة المهمة** _(اختياري)_ | `task_callback` | دالة تُستدعى بعد اكتمال كل مهمة. |
| **مشاركة الطاقم** _(اختياري)_ | `share_crew` | ما إذا كنت تريد مشاركة معلومات الطاقم الكاملة وتنفيذه مع فريق CrewAI. |
| **ملف سجل المخرجات** _(اختياري)_ | `output_log_file` | عيّن True لحفظ السجلات كـ logs.txt أو وفّر مسار ملف. الافتراضي `None`. |
| **وكيل المدير** _(اختياري)_ | `manager_agent` | يعيّن وكيلًا مخصصًا سيُستخدم كمدير. |
| **التخطيط** *(اختياري)* | `planning` | يضيف قدرة التخطيط للطاقم. |
| **LLM التخطيط** *(اختياري)* | `planning_llm` | نموذج اللغة المستخدم بواسطة AgentPlanner في عملية التخطيط. |
| **مصادر المعرفة** _(اختياري)_ | `knowledge_sources` | مصادر المعرفة المتاحة على مستوى الطاقم، يمكن لجميع الوكلاء الوصول إليها. |
| **البث** _(اختياري)_ | `stream` | تفعيل مخرجات البث لتلقي تحديثات في الوقت الفعلي. الافتراضي `False`. |
<Tip>
**الحد الأقصى لـ RPM للطاقم**: تعيّن خاصية `max_rpm` الحد الأقصى للطلبات في الدقيقة التي يمكن للطاقم تنفيذها لتجنب حدود المعدل وستتجاوز إعدادات `max_rpm` الفردية للوكلاء إذا عيّنتها.
</Tip>
## إنشاء الأطقم
هناك طريقتان رئيسيتان لإنشاء الأطقم في CrewAI: باستخدام **تهيئة JSONC (الموصى بها للـ crews الجديدة)** أو تعريفها **مباشرة في الكود** للمشاريع الكلاسيكية والحالات المتقدمة.
### تهيئة JSONC (موصى بها)
المشاريع الجديدة التي تُنشأ عبر `crewai create crew <name>` تستخدم `crew.jsonc` لإعدادات الـ crew والمهام، وملفًا منفصلًا لكل Agent داخل `agents/`. يكتشف `crewai run` ملف `crew.jsonc` أو `crew.json`، ويحمّل الـ Agents المشار إليها، ويطلب قيم placeholders الناقصة، ثم يبدأ الـ crew.
```jsonc crew.jsonc
{
"name": "Market Research Crew",
"agents": ["researcher", "analyst"],
"tasks": [
{
"name": "research",
"description": "Research {topic} and collect the most relevant facts.",
"expected_output": "Structured research notes about {topic}.",
"agent": "researcher"
},
{
"name": "analysis",
"description": "Analyze the research and write a concise report.",
"expected_output": "A markdown report with findings and recommendations.",
"agent": "analyst",
"context": ["research"],
"output_file": "output/report.md"
}
],
"process": "sequential",
"verbose": true,
"memory": true,
"inputs": {
"topic": "AI Agents"
}
}
```
كل عنصر في `agents` يُحل أولًا إلى `agents/<name>.jsonc` ثم إلى `agents/<name>.json`. للـ crews الهرمية، استخدم `"process": "hierarchical"` مع `manager_llm` أو `manager_agent`.
<Warning>
شغّل مشاريع JSON crew من مصادر تثق بها فقط. أدوات `custom:<name>` ومراجع `{"python": "module.attribute"}` تنفذ كود Python محليًا عند تحميل الـ crew.
</Warning>
### تهيئة YAML الكلاسيكية
المشاريع الكلاسيكية التي تُنشأ عبر `crewai create crew <name> --classic` تستخدم `crew.py` و `config/agents.yaml` و `config/tasks.yaml` والمزيّنات `@CrewBase` و `@agent` و `@task` و `@crew`.
تظل هذه الطريقة مدعومة للمشاريع الحالية المبنية بـ Python/YAML وللفِرق التي تحتاج تحكمًا صريحًا عبر decorators.
```python code
from crewai import Agent, Crew, Task, Process
from crewai.project import CrewBase, agent, task, crew, before_kickoff, after_kickoff
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List
@CrewBase
class YourCrewName:
"""Description of your crew"""
agents: List[BaseAgent]
tasks: List[Task]
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'
@before_kickoff
def prepare_inputs(self, inputs):
inputs['additional_data'] = "Some extra information"
return inputs
@after_kickoff
def process_output(self, output):
output.raw += "\nProcessed after kickoff."
return output
@agent
def agent_one(self) -> Agent:
return Agent(
config=self.agents_config['agent_one'], # type: ignore[index]
verbose=True
)
@task
def task_one(self) -> Task:
return Task(
config=self.tasks_config['task_one'] # type: ignore[index]
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)
```
<Note>
سيتم تنفيذ المهام بالترتيب الذي عُرّفت به.
</Note>
فئة `CrewBase`، مع هذه المزيّنات، تؤتمت جمع الوكلاء والمهام، مما يقلل الحاجة للإدارة اليدوية.
### تعريف مباشر في الكود (بديل)
بدلاً من ذلك، يمكنك تعريف الطاقم مباشرة في الكود بدون ملفات تهيئة YAML.
## مخرجات الطاقم
تُغلّف مخرجات الطاقم في فئة `CrewOutput`. توفر هذه الفئة طريقة منظمة للوصول إلى نتائج تنفيذ الطاقم، بما في ذلك تنسيقات متنوعة مثل السلاسل النصية الخام وJSON ونماذج Pydantic.
### خصائص مخرجات الطاقم
| الخاصية | المعامل | النوع | الوصف |
| :--------------- | :------------- | :------------------------- | :--------------------------------------------------------------------------------------------------- |
| **Raw** | `raw` | `str` | المخرجات الخام للطاقم. هذا هو التنسيق الافتراضي. |
| **Pydantic** | `pydantic` | `Optional[BaseModel]` | كائن نموذج Pydantic يمثل المخرجات المنظمة. |
| **JSON Dict** | `json_dict` | `Optional[Dict[str, Any]]` | قاموس يمثل مخرجات JSON. |
| **Tasks Output** | `tasks_output` | `List[TaskOutput]` | قائمة كائنات `TaskOutput`، كل منها يمثل مخرجات مهمة. |
| **Token Usage** | `token_usage` | `Dict[str, Any]` | ملخص استخدام الرموز. |
## استخدام الذاكرة
يمكن للأطقم استخدام الذاكرة (قصيرة المدى، طويلة المدى، وذاكرة الكيانات) لتحسين تنفيذها وتعلمها بمرور الوقت.
## استخدام التخزين المؤقت
يمكن استخدام التخزين المؤقت لتخزين نتائج تنفيذ الأدوات، مما يجعل العملية أكثر كفاءة.
## مقاييس استخدام الطاقم
بعد تنفيذ الطاقم، يمكنك الوصول إلى خاصية `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()
print(crew.usage_metrics)
```
## عملية تنفيذ الطاقم
- **العملية التسلسلية**: تُنفذ المهام واحدة تلو الأخرى، مما يسمح بتدفق عمل خطي.
- **العملية الهرمية**: ينسق وكيل مدير الطاقم، ويفوّض المهام ويتحقق من النتائج.
### تشغيل الطاقم
بمجرد تجميع طاقمك، ابدأ سير العمل بطريقة `kickoff()`.
```python Code
result = my_crew.kickoff()
print(result)
```
### طرق مختلفة لتشغيل الطاقم
#### الطرق المتزامنة
- `kickoff()`: يبدأ عملية التنفيذ وفقًا لتدفق العملية المحدد.
- `kickoff_for_each()`: ينفذ المهام بالتتابع لكل مدخل.
#### الطرق غير المتزامنة
| الطريقة | النوع | الوصف |
|--------|------|-------------|
| `akickoff()` | غير متزامن أصلي | async/await أصلي عبر سلسلة التنفيذ بأكملها |
| `akickoff_for_each()` | غير متزامن أصلي | تنفيذ غير متزامن أصلي لكل مدخل في قائمة |
| `kickoff_async()` | مبني على الخيوط | يغلّف التنفيذ المتزامن في `asyncio.to_thread` |
| `kickoff_for_each_async()` | مبني على الخيوط | غير متزامن مبني على الخيوط لكل مدخل في قائمة |
<Note>
لأحمال العمل عالية التزامن، يُوصى بـ `akickoff()` و `akickoff_for_each()` لأنها تستخدم async أصلي.
</Note>
### بث تنفيذ الطاقم
للرؤية في الوقت الفعلي لتنفيذ الطاقم، يمكنك تفعيل البث:
```python Code
crew = Crew(
agents=[researcher],
tasks=[task],
stream=True
)
streaming = crew.kickoff(inputs={"topic": "AI"})
for chunk in streaming:
print(chunk.content, end="", flush=True)
result = streaming.result
```
### الإعادة من مهمة محددة
يمكنك الآن الإعادة من مهمة محددة باستخدام أمر CLI `replay`.
```shell
crewai log-tasks-outputs
```
ثم للإعادة من مهمة محددة:
```shell
crewai replay -t <task_id>
```

View File

@@ -0,0 +1,237 @@
---
title: "مستمعو الأحداث"
description: "الاستفادة من أحداث CrewAI لبناء تكاملات مخصصة ومراقبة"
icon: spinner
mode: "wide"
---
## نظرة عامة
يوفر CrewAI نظام أحداث قوي يتيح لك الاستماع والتفاعل مع الأحداث المختلفة التي تحدث أثناء تنفيذ طاقمك. تُمكّنك هذه الميزة من بناء تكاملات مخصصة وحلول مراقبة وأنظمة تسجيل أو أي وظائف أخرى تحتاج للتشغيل بناءً على أحداث CrewAI الداخلية.
## كيف يعمل
يستخدم CrewAI بنية ناقل أحداث لإرسال الأحداث طوال دورة حياة التنفيذ. يُبنى نظام الأحداث على المكونات التالية:
1. **CrewAIEventsBus**: ناقل أحداث فريد يدير تسجيل الأحداث وإرسالها
2. **BaseEvent**: الفئة الأساسية لجميع الأحداث في النظام
3. **BaseEventListener**: فئة أساسية مجردة لإنشاء مستمعي أحداث مخصصين
عندما تحدث إجراءات محددة في CrewAI (مثل بدء تنفيذ طاقم، أو إكمال وكيل لمهمة، أو استخدام أداة)، يرسل النظام أحداثًا مقابلة. يمكنك تسجيل معالجات لهذه الأحداث لتنفيذ كود مخصص عند حدوثها.
<Note type="info" title="تحسين المؤسسات: تتبع الأوامر">
يوفر CrewAI AMP ميزة تتبع أوامر مدمجة تستفيد من نظام الأحداث لتتبع وتخزين وتصور جميع الأوامر والاستكمالات والبيانات الوصفية المرتبطة.
![Prompt Tracing Dashboard](/images/enterprise/traces-overview.png)
مع تتبع الأوامر يمكنك:
- عرض السجل الكامل لجميع الأوامر المرسلة إلى LLM
- تتبع استخدام الرموز والتكاليف
- تصحيح إخفاقات استدلال الوكيل
- مشاركة تسلسلات الأوامر مع فريقك
- مقارنة استراتيجيات الأوامر المختلفة
- تصدير التتبعات للامتثال والتدقيق
</Note>
## إنشاء مستمع أحداث مخصص
لإنشاء مستمع أحداث مخصص، تحتاج إلى:
1. إنشاء فئة ترث من `BaseEventListener`
2. تنفيذ طريقة `setup_listeners`
3. تسجيل معالجات للأحداث التي تهمك
4. إنشاء مثيل من مستمعك في الملف المناسب
إليك مثالًا بسيطًا:
```python
from crewai.events import (
CrewKickoffStartedEvent,
CrewKickoffCompletedEvent,
AgentExecutionCompletedEvent,
)
from crewai.events import BaseEventListener
class MyCustomListener(BaseEventListener):
def __init__(self):
super().__init__()
def setup_listeners(self, crewai_event_bus):
@crewai_event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source, event):
print(f"Crew '{event.crew_name}' has started execution!")
@crewai_event_bus.on(CrewKickoffCompletedEvent)
def on_crew_completed(source, event):
print(f"Crew '{event.crew_name}' has completed execution!")
print(f"Output: {event.output}")
@crewai_event_bus.on(AgentExecutionCompletedEvent)
def on_agent_execution_completed(source, event):
print(f"Agent '{event.agent.role}' completed task")
print(f"Output: {event.output}")
```
## تسجيل المستمع بشكل صحيح
مجرد تعريف فئة المستمع ليس كافيًا. تحتاج لإنشاء مثيل منه والتأكد من استيراده في تطبيقك.
```python
# في ملف crew.py
from crewai import Agent, Crew, Task
from my_listeners import MyCustomListener
# إنشاء مثيل من المستمع
my_listener = MyCustomListener()
class MyCustomCrew:
def crew(self):
return Crew(
agents=[...],
tasks=[...],
)
```
## أنواع الأحداث المتاحة
يوفر CrewAI مجموعة واسعة من الأحداث يمكنك الاستماع إليها:
### أحداث الطاقم
- **CrewKickoffStartedEvent**: يُرسل عند بدء تنفيذ الطاقم
- **CrewKickoffCompletedEvent**: يُرسل عند اكتمال تنفيذ الطاقم
- **CrewKickoffFailedEvent**: يُرسل عند فشل تنفيذ الطاقم
- **CrewTestStartedEvent**: يُرسل عند بدء اختبار الطاقم
- **CrewTestCompletedEvent**: يُرسل عند اكتمال اختبار الطاقم
- **CrewTestFailedEvent**: يُرسل عند فشل اختبار الطاقم
- **CrewTrainStartedEvent**: يُرسل عند بدء تدريب الطاقم
- **CrewTrainCompletedEvent**: يُرسل عند اكتمال تدريب الطاقم
- **CrewTrainFailedEvent**: يُرسل عند فشل تدريب الطاقم
### أحداث الوكيل
- **AgentExecutionStartedEvent**: يُرسل عند بدء تنفيذ وكيل لمهمة
- **AgentExecutionCompletedEvent**: يُرسل عند اكتمال تنفيذ وكيل لمهمة
- **AgentExecutionErrorEvent**: يُرسل عند مواجهة وكيل لخطأ أثناء التنفيذ
- **LiteAgentExecutionStartedEvent**: يُرسل عند بدء تنفيذ LiteAgent
- **LiteAgentExecutionCompletedEvent**: يُرسل عند اكتمال تنفيذ LiteAgent
### أحداث المهام
- **TaskStartedEvent**: يُرسل عند بدء تنفيذ مهمة
- **TaskCompletedEvent**: يُرسل عند اكتمال تنفيذ مهمة
- **TaskFailedEvent**: يُرسل عند فشل تنفيذ مهمة
### أحداث استخدام الأدوات
- **ToolUsageStartedEvent**: يُرسل عند بدء تنفيذ أداة
- **ToolUsageFinishedEvent**: يُرسل عند اكتمال تنفيذ أداة
- **ToolUsageErrorEvent**: يُرسل عند مواجهة خطأ في تنفيذ أداة
### أحداث MCP
- **MCPConnectionStartedEvent**: يُرسل عند بدء الاتصال بخادم MCP
- **MCPConnectionCompletedEvent**: يُرسل عند اكتمال الاتصال بخادم MCP
- **MCPConnectionFailedEvent**: يُرسل عند فشل الاتصال بخادم MCP
- **MCPToolExecutionStartedEvent**: يُرسل عند بدء تنفيذ أداة MCP
- **MCPToolExecutionCompletedEvent**: يُرسل عند اكتمال تنفيذ أداة MCP
- **MCPToolExecutionFailedEvent**: يُرسل عند فشل تنفيذ أداة MCP
### أحداث المعرفة
- **KnowledgeRetrievalStartedEvent**: يُرسل عند بدء استرجاع المعرفة
- **KnowledgeRetrievalCompletedEvent**: يُرسل عند اكتمال استرجاع المعرفة
- **KnowledgeQueryStartedEvent**: يُرسل عند بدء استعلام المعرفة
- **KnowledgeQueryCompletedEvent**: يُرسل عند اكتمال استعلام المعرفة
- **KnowledgeQueryFailedEvent**: يُرسل عند فشل استعلام المعرفة
### أحداث حواجز LLM
- **LLMGuardrailStartedEvent**: يُرسل عند بدء التحقق من الحاجز
- **LLMGuardrailCompletedEvent**: يُرسل عند اكتمال التحقق من الحاجز
- **LLMGuardrailFailedEvent**: يُرسل عند فشل التحقق من الحاجز
### أحداث التدفق
- **FlowCreatedEvent**: يُرسل عند إنشاء تدفق
- **FlowStartedEvent**: يُرسل عند بدء تنفيذ تدفق
- **FlowFinishedEvent**: يُرسل عند اكتمال تنفيذ تدفق
- **FlowFailedEvent**: يُرسل عند فشل تنفيذ تدفق. يحتوي على اسم التدفق والاستثناء الذي أنهى التنفيذ.
- **FlowPausedEvent**: يُرسل عند إيقاف تدفق مؤقتًا بانتظار ملاحظات بشرية
### أحداث LLM
- **LLMCallStartedEvent**: يُرسل عند بدء استدعاء LLM
- **LLMCallCompletedEvent**: يُرسل عند اكتمال استدعاء LLM
- **LLMCallFailedEvent**: يُرسل عند فشل استدعاء LLM
- **LLMStreamChunkEvent**: يُرسل لكل جزء مستلم أثناء بث استجابات LLM
### أحداث الذاكرة
- **MemoryQueryStartedEvent**: يُرسل عند بدء استعلام الذاكرة
- **MemoryQueryCompletedEvent**: يُرسل عند اكتمال استعلام الذاكرة
- **MemorySaveStartedEvent**: يُرسل عند بدء حفظ الذاكرة
- **MemorySaveCompletedEvent**: يُرسل عند اكتمال حفظ الذاكرة
### أحداث الاستدلال
- **AgentReasoningStartedEvent**: يُرسل عند بدء وكيل الاستدلال حول مهمة
- **AgentReasoningCompletedEvent**: يُرسل عند انتهاء عملية الاستدلال
- **AgentReasoningFailedEvent**: يُرسل عند فشل عملية الاستدلال
### أحداث A2A (وكيل إلى وكيل)
- **A2ADelegationStartedEvent**: يُرسل عند بدء تفويض A2A
- **A2ADelegationCompletedEvent**: يُرسل عند اكتمال تفويض A2A
- **A2AConversationStartedEvent**: يُرسل عند بدء محادثة A2A متعددة الأدوار
- **A2AConversationCompletedEvent**: يُرسل عند انتهاء محادثة A2A
## هيكل معالج الأحداث
يستقبل كل معالج حدث معاملين:
1. **source**: الكائن الذي أرسل الحدث
2. **event**: مثيل الحدث، يحتوي على بيانات خاصة بالحدث
هيكل كائن الحدث يعتمد على نوع الحدث، لكن جميع الأحداث ترث من `BaseEvent` وتتضمن:
- **timestamp**: الوقت الذي أُرسل فيه الحدث
- **type**: معرّف نصي لنوع الحدث
## الاستخدام المتقدم: المعالجات المحددة النطاق
لمعالجة الأحداث المؤقتة، يمكنك استخدام مدير سياق `scoped_handlers`:
```python
from crewai.events import crewai_event_bus, CrewKickoffStartedEvent
with crewai_event_bus.scoped_handlers():
@crewai_event_bus.on(CrewKickoffStartedEvent)
def temp_handler(source, event):
print("This handler only exists within this context")
# قم بشيء يرسل أحداثًا
# خارج السياق، يتم إزالة المعالج المؤقت
```
## حالات الاستخدام
يمكن استخدام مستمعي الأحداث لأغراض متنوعة:
1. **التسجيل والمراقبة**: تتبع تنفيذ طاقمك وتسجيل الأحداث المهمة
2. **التحليلات**: جمع بيانات عن أداء وسلوك طاقمك
3. **التصحيح**: إعداد مستمعين مؤقتين لتصحيح مشاكل محددة
4. **التكامل**: ربط CrewAI بأنظمة خارجية مثل منصات المراقبة وقواعد البيانات أو خدمات الإشعارات
5. **السلوك المخصص**: تشغيل إجراءات مخصصة بناءً على أحداث محددة
## أفضل الممارسات
1. **اجعل المعالجات خفيفة**: يجب أن تكون معالجات الأحداث خفيفة وتتجنب العمليات الحاجبة
2. **معالجة الأخطاء**: أدرج معالجة أخطاء مناسبة في معالجات الأحداث لمنع الاستثناءات من التأثير على التنفيذ الرئيسي
3. **التنظيف**: إذا خصص مستمعك موارد، تأكد من تنظيفها بشكل صحيح
4. **الاستماع الانتقائي**: استمع فقط للأحداث التي تحتاج فعلاً لمعالجتها
5. **الاختبار**: اختبر مستمعي الأحداث بمعزل لضمان سلوكهم كما هو متوقع
بالاستفادة من نظام أحداث CrewAI، يمكنك توسيع وظائفه ودمجه بسلاسة مع بنيتك التحتية الحالية.

View File

@@ -0,0 +1,267 @@
---
title: الملفات
description: تمرير الصور وملفات PDF والصوت والفيديو والنصوص إلى وكلائك للمعالجة متعددة الوسائط.
icon: file-image
---
## نظرة عامة
يدعم CrewAI مدخلات الملفات متعددة الوسائط الأصلية، مما يتيح لك تمرير الصور وملفات PDF والصوت والفيديو والنصوص مباشرة إلى وكلائك. يتم تنسيق الملفات تلقائيًا وفقًا لمتطلبات API لكل مزود LLM.
<Note type="info" title="اعتمادية اختيارية">
يتطلب دعم الملفات حزمة `crewai-files` الاختيارية. ثبّتها بـ:
```bash
uv add 'crewai[file-processing]'
```
</Note>
<Note type="warning" title="وصول مبكر">
واجهة معالجة الملفات حاليًا في مرحلة الوصول المبكر.
</Note>
## أنواع الملفات
يدعم CrewAI خمسة أنواع ملفات محددة بالإضافة إلى فئة `File` العامة التي تكتشف النوع تلقائيًا:
| النوع | الفئة | حالات الاستخدام |
|:-----|:------|:----------|
| **صورة** | `ImageFile` | صور، لقطات شاشة، مخططات، رسوم بيانية |
| **PDF** | `PDFFile` | مستندات، تقارير، أوراق بحثية |
| **صوت** | `AudioFile` | تسجيلات صوتية، بودكاست، اجتماعات |
| **فيديو** | `VideoFile` | تسجيلات شاشة، عروض تقديمية |
| **نص** | `TextFile` | ملفات كود، سجلات، ملفات بيانات |
| **عام** | `File` | اكتشاف تلقائي للنوع من المحتوى |
```python
from crewai_files import File, ImageFile, PDFFile, AudioFile, VideoFile, TextFile
image = ImageFile(source="screenshot.png")
pdf = PDFFile(source="report.pdf")
audio = AudioFile(source="meeting.mp3")
video = VideoFile(source="demo.mp4")
text = TextFile(source="data.csv")
file = File(source="document.pdf")
```
## مصادر الملفات
يقبل معامل `source` أنواع إدخال متعددة ويكتشف تلقائيًا المعالج المناسب:
### من مسار
```python
from crewai_files import ImageFile
image = ImageFile(source="./images/chart.png")
```
### من عنوان URL
```python
from crewai_files import ImageFile
image = ImageFile(source="https://example.com/image.png")
```
### من بايتات
```python
from crewai_files import ImageFile, FileBytes
image_bytes = download_image_from_api()
image = ImageFile(source=FileBytes(data=image_bytes, filename="downloaded.png"))
image = ImageFile(source=image_bytes)
```
## استخدام الملفات
يمكن تمرير الملفات على مستويات متعددة، حيث تأخذ المستويات الأكثر تحديدًا الأولوية.
### مع الأطقم
مرر الملفات عند تشغيل طاقم:
```python
from crewai import Crew
from crewai_files import ImageFile
crew = Crew(agents=[analyst], tasks=[analysis_task])
result = crew.kickoff(
inputs={"topic": "Q4 Sales"},
input_files={
"chart": ImageFile(source="sales_chart.png"),
"report": PDFFile(source="quarterly_report.pdf"),
}
)
```
### مع المهام
أرفق الملفات بمهام محددة:
```python
from crewai import Task
from crewai_files import ImageFile
task = Task(
description="Analyze the sales chart and identify trends in {chart}",
expected_output="A summary of key trends",
input_files={
"chart": ImageFile(source="sales_chart.png"),
}
)
```
### مع التدفقات
مرر الملفات إلى التدفقات، والتي تنتقل تلقائيًا إلى الأطقم:
```python
from crewai.flow.flow import Flow, start
from crewai_files import ImageFile
class AnalysisFlow(Flow):
@start()
def analyze(self):
return self.analysis_crew.kickoff()
flow = AnalysisFlow()
result = flow.kickoff(
input_files={"image": ImageFile(source="data.png")}
)
```
### مع الوكلاء المستقلين
مرر الملفات مباشرة إلى تشغيل الوكيل:
```python
from crewai import Agent
from crewai_files import ImageFile
agent = Agent(
role="Image Analyst",
goal="Analyze images",
backstory="Expert at visual analysis",
llm="gpt-4o",
)
result = agent.kickoff(
messages="What's in this image?",
input_files={"photo": ImageFile(source="photo.jpg")},
)
```
## أولوية الملفات
عند تمرير الملفات على مستويات متعددة، تتجاوز المستويات الأكثر تحديدًا المستويات الأوسع:
```
Flow input_files < Crew input_files < Task input_files
```
على سبيل المثال، إذا عرّف كل من التدفق والمهمة ملفًا باسم `"chart"`، تُستخدم نسخة المهمة.
## دعم المزودين
تدعم المزودات المختلفة أنواع ملفات مختلفة. يقوم CrewAI تلقائيًا بتنسيق الملفات وفقًا لواجهة كل مزود.
| المزود | صورة | PDF | صوت | فيديو | نص |
|:---------|:-----:|:---:|:-----:|:-----:|:----:|
| **OpenAI** (completions API) | ✓ | | | | |
| **OpenAI** (responses API) | ✓ | ✓ | ✓ | | |
| **Anthropic** (claude-3.x) | ✓ | ✓ | | | |
| **Google Gemini** (gemini-1.5, 2.0, 2.5) | ✓ | ✓ | ✓ | ✓ | ✓ |
| **AWS Bedrock** (claude-3) | ✓ | ✓ | | | |
| **Azure OpenAI** (gpt-4o) | ✓ | | ✓ | | |
<Note type="info" title="Gemini لأقصى دعم للملفات">
تدعم نماذج Google Gemini جميع أنواع الملفات بما في ذلك الفيديو (حتى ساعة واحدة، 2 جيجابايت). استخدم Gemini عندما تحتاج لمعالجة محتوى الفيديو.
</Note>
<Note type="warning" title="أنواع الملفات غير المدعومة">
إذا مررت نوع ملف لا يدعمه المزود (مثل الفيديو إلى OpenAI)، ستتلقى خطأ `UnsupportedFileTypeError`. اختر مزودك بناءً على أنواع الملفات التي تحتاج لمعالجتها.
</Note>
## كيف تُرسل الملفات
يختار CrewAI تلقائيًا الطريقة المثلى لإرسال الملفات إلى كل مزود:
| الطريقة | الوصف | متى تُستخدم |
|:-------|:------------|:----------|
| **Inline Base64** | الملف مضمّن مباشرة في الطلب | ملفات صغيرة (< 5 ميجابايت عادة) |
| **File Upload API** | الملف يُرفع بشكل منفصل، يُشار إليه بمعرّف | ملفات كبيرة تتجاوز العتبة |
| **URL Reference** | عنوان URL مباشر يُمرر إلى النموذج | مصدر الملف هو عنوان URL بالفعل |
### طرق الإرسال حسب المزود
| المزود | Inline Base64 | File Upload API | URL References |
|:---------|:-------------:|:---------------:|:--------------:|
| **OpenAI** | ✓ | ✓ (> 5 MB) | ✓ |
| **Anthropic** | ✓ | ✓ (> 5 MB) | ✓ |
| **Google Gemini** | ✓ | ✓ (> 20 MB) | ✓ |
| **AWS Bedrock** | ✓ | | ✓ (S3 URIs) |
| **Azure OpenAI** | ✓ | | ✓ |
<Note type="info" title="تحسين تلقائي">
لا تحتاج لإدارة هذا بنفسك. يستخدم CrewAI تلقائيًا الطريقة الأكثر كفاءة بناءً على حجم الملف وقدرات المزود. المزودات بدون واجهات رفع الملفات تستخدم inline base64 لجميع الملفات.
</Note>
## أوضاع معالجة الملفات
تحكم في كيفية معالجة الملفات عندما تتجاوز حدود المزود:
```python
from crewai_files import ImageFile, PDFFile
image = ImageFile(source="large.png", mode="strict")
image = ImageFile(source="large.png", mode="auto")
image = ImageFile(source="large.png", mode="warn")
pdf = PDFFile(source="large.pdf", mode="chunk")
```
## قيود المزودين
لكل مزود حدود محددة لأحجام الملفات والأبعاد:
### OpenAI
- **الصور**: حد أقصى 20 ميجابايت، حتى 10 صور لكل طلب
- **PDF**: حد أقصى 32 ميجابايت، حتى 100 صفحة
- **الصوت**: حد أقصى 25 ميجابايت، حتى 25 دقيقة
### Anthropic
- **الصور**: حد أقصى 5 ميجابايت، أقصى 8000x8000 بكسل، حتى 100 صورة
- **PDF**: حد أقصى 32 ميجابايت، حتى 100 صفحة
### Google Gemini
- **الصور**: حد أقصى 100 ميجابايت
- **PDF**: حد أقصى 50 ميجابايت
- **الصوت**: حد أقصى 100 ميجابايت، حتى 9.5 ساعة
- **الفيديو**: حد أقصى 2 جيجابايت، حتى ساعة واحدة
### AWS Bedrock
- **الصور**: حد أقصى 4.5 ميجابايت، أقصى 8000x8000 بكسل
- **PDF**: حد أقصى 3.75 ميجابايت، حتى 100 صفحة
## الإشارة إلى الملفات في الأوامر
استخدم اسم مفتاح الملف في أوصاف المهام للإشارة إلى الملفات:
```python
task = Task(
description="""
Analyze the provided materials:
1. Review the chart in {sales_chart}
2. Cross-reference with data in {quarterly_report}
3. Summarize key findings
""",
expected_output="Analysis summary with key insights",
input_files={
"sales_chart": ImageFile(source="chart.png"),
"quarterly_report": PDFFile(source="report.pdf"),
}
)
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,878 @@
---
title: الذاكرة
description: الاستفادة من نظام الذاكرة الموحد في CrewAI لتعزيز قدرات الوكلاء.
icon: database
mode: "wide"
---
## نظرة عامة
يوفر CrewAI **نظام ذاكرة موحد** -- فئة `Memory` واحدة تستبدل أنواع الذاكرة المنفصلة (قصيرة المدى، طويلة المدى، ذاكرة الكيانات، والخارجية) بواجهة برمجة تطبيقات ذكية واحدة. تستخدم الذاكرة LLM لتحليل المحتوى عند الحفظ (استنتاج النطاق والفئات والأهمية) وتدعم الاسترجاع متعدد العمق مع تسجيل مركب يمزج بين التشابه الدلالي والحداثة والأهمية.
يمكنك استخدام الذاكرة بأربع طرق: **مستقلة** (سكربتات، دفاتر ملاحظات)، **مع فرق Crew**، **مع Agents**، أو **داخل التدفقات**.
## البدء السريع
```python
from crewai import Memory
memory = Memory()
# Store -- the LLM infers scope, categories, and importance
memory.remember("We decided to use PostgreSQL for the user database.")
# Retrieve -- results ranked by composite score (semantic + recency + importance)
matches = memory.recall("What database did we choose?")
for m in matches:
print(f"[{m.score:.2f}] {m.record.content}")
# Tune scoring for a fast-moving project
memory = Memory(recency_weight=0.5, recency_half_life_days=7)
# Forget
memory.forget(scope="/project/old")
# Explore the self-organized scope tree
print(memory.tree())
print(memory.info("/"))
```
## أربع طرق لاستخدام الذاكرة
### مستقلة
استخدم الذاكرة في السكربتات ودفاتر الملاحظات وأدوات سطر الأوامر أو كقاعدة معرفة مستقلة -- لا حاجة لوكلاء أو فرق Crew.
```python
from crewai import Memory
memory = Memory()
# Build up knowledge
memory.remember("The API rate limit is 1000 requests per minute.")
memory.remember("Our staging environment uses port 8080.")
memory.remember("The team agreed to use feature flags for all new releases.")
# Later, recall what you need
matches = memory.recall("What are our API limits?", limit=5)
for m in matches:
print(f"[{m.score:.2f}] {m.record.content}")
# Extract atomic facts from a longer text
raw = """Meeting notes: We decided to migrate from MySQL to PostgreSQL
next quarter. The budget is $50k. Sarah will lead the migration."""
facts = memory.extract_memories(raw)
# ["Migration from MySQL to PostgreSQL planned for next quarter",
# "Database migration budget is $50k",
# "Sarah will lead the database migration"]
for fact in facts:
memory.remember(fact)
```
### مع فرق Crew
مرّر `memory=True` للإعدادات الافتراضية، أو مرّر مثيل `Memory` مُعدّ للسلوك المخصص.
```python
from crewai import Crew, Agent, Task, Process, Memory
# Option 1: Default memory
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process=Process.sequential,
memory=True,
verbose=True,
)
# Option 2: Custom memory with tuned scoring
memory = Memory(
recency_weight=0.4,
semantic_weight=0.4,
importance_weight=0.2,
recency_half_life_days=14,
)
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
memory=memory,
)
```
عند استخدام `memory=True`، ينشئ الفريق مثيل `Memory()` افتراضيًا ويمرر إعداد `embedder` الخاص بالفريق تلقائيًا. يشترك جميع الوكلاء في الفريق في ذاكرة الفريق ما لم يكن لدى الوكيل ذاكرته الخاصة.
بعد كل مهمة، يستخرج الفريق تلقائيًا حقائق منفصلة من مخرجات المهمة ويخزّنها. قبل كل مهمة، يسترجع الوكيل السياق ذا الصلة من الذاكرة ويحقنه في موجّه المهمة.
### مع Agents
يمكن للوكلاء استخدام ذاكرة الفريق المشتركة (افتراضيًا) أو تلقي عرض محدد النطاق للسياق الخاص.
```python
from crewai import Agent, Memory
memory = Memory()
# Researcher gets a private scope -- only sees /agent/researcher
researcher = Agent(
role="Researcher",
goal="Find and analyze information",
backstory="Expert researcher with attention to detail",
memory=memory.scope("/agent/researcher"),
)
# Writer uses crew shared memory (no agent-level memory set)
writer = Agent(
role="Writer",
goal="Produce clear, well-structured content",
backstory="Experienced technical writer",
# memory not set -- uses crew._memory when crew has memory enabled
)
```
يمنح هذا النمط الباحث نتائج خاصة بينما يقرأ الكاتب من ذاكرة الفريق المشتركة.
### مع التدفقات
كل تدفق يحتوي على ذاكرة مدمجة. استخدم `self.remember()` و `self.recall()` و `self.extract_memories()` داخل أي دالة تدفق.
```python
from crewai.flow.flow import Flow, listen, start
class ResearchFlow(Flow):
@start()
def gather_data(self):
findings = "PostgreSQL handles 10k concurrent connections. MySQL caps at 5k."
self.remember(findings, scope="/research/databases")
return findings
@listen(gather_data)
def write_report(self, findings):
# Recall past research to provide context
past = self.recall("database performance benchmarks")
context = "\n".join(f"- {m.record.content}" for m in past)
return f"Report:\nNew findings: {findings}\nPrevious context:\n{context}"
```
انظر [وثائق التدفقات](/concepts/flows) لمزيد من المعلومات حول الذاكرة في التدفقات.
## النطاقات الهرمية
### ما هي النطاقات
يتم تنظيم الذكريات في شجرة هرمية من النطاقات، مشابهة لنظام الملفات. كل نطاق هو مسار مثل `/` أو `/project/alpha` أو `/agent/researcher/findings`.
```
/
/company
/company/engineering
/company/product
/project
/project/alpha
/project/beta
/agent
/agent/researcher
/agent/writer
```
توفر النطاقات **ذاكرة تعتمد على السياق** -- عند الاسترجاع ضمن نطاق، تبحث فقط في ذلك الفرع من الشجرة، مما يحسّن كلًا من الدقة والأداء.
### كيف يعمل استنتاج النطاق
عند استدعاء `remember()` دون تحديد نطاق، يحلل LLM المحتوى وشجرة النطاقات الحالية، ثم يقترح أفضل موضع. إذا لم يكن هناك نطاق حالي مناسب، ينشئ واحدًا جديدًا. بمرور الوقت، تنمو شجرة النطاقات عضويًا من المحتوى نفسه -- لا تحتاج إلى تصميم مخطط مسبقًا.
```python
memory = Memory()
# LLM infers scope from content
memory.remember("We chose PostgreSQL for the user database.")
# -> might be placed under /project/decisions or /engineering/database
# You can also specify scope explicitly
memory.remember("Sprint velocity is 42 points", scope="/team/metrics")
```
### تصوير شجرة النطاقات
```python
print(memory.tree())
# / (15 records)
# /project (8 records)
# /project/alpha (5 records)
# /project/beta (3 records)
# /agent (7 records)
# /agent/researcher (4 records)
# /agent/writer (3 records)
print(memory.info("/project/alpha"))
# ScopeInfo(path='/project/alpha', record_count=5,
# categories=['architecture', 'database'],
# oldest_record=datetime(...), newest_record=datetime(...),
# child_scopes=[])
```
### MemoryScope: عروض الأشجار الفرعية
يقيّد `MemoryScope` جميع العمليات على فرع من الشجرة. يمكن للوكيل أو الكود الذي يستخدمه الرؤية والكتابة فقط ضمن تلك الشجرة الفرعية.
```python
memory = Memory()
# Create a scope for a specific agent
agent_memory = memory.scope("/agent/researcher")
# Everything is relative to /agent/researcher
agent_memory.remember("Found three relevant papers on LLM memory.")
# -> stored under /agent/researcher
agent_memory.recall("relevant papers")
# -> searches only under /agent/researcher
# Narrow further with subscope
project_memory = agent_memory.subscope("project-alpha")
# -> /agent/researcher/project-alpha
```
### أفضل الممارسات لتصميم النطاقات
- **ابدأ بشكل مسطح، ودع LLM ينظّم.** لا تبالغ في هندسة تسلسل النطاقات مسبقًا. ابدأ بـ `memory.remember(content)` ودع استنتاج النطاق في LLM ينشئ الهيكل مع تراكم المحتوى.
- **استخدم أنماط `/{entity_type}/{identifier}`.** تنشأ التسلسلات الطبيعية من أنماط مثل `/project/alpha` و `/agent/researcher` و `/company/engineering` و `/customer/acme-corp`.
- **حدد النطاق حسب الاهتمام، وليس حسب نوع البيانات.** استخدم `/project/alpha/decisions` بدلاً من `/decisions/project/alpha`. هذا يبقي المحتوى ذا الصلة معًا.
- **حافظ على العمق ضحلًا (2-3 مستويات).** النطاقات المتداخلة بعمق تصبح متفرقة جدًا. `/project/alpha/architecture` جيد؛ `/project/alpha/architecture/decisions/databases/postgresql` عميق جدًا.
- **استخدم النطاقات الصريحة عندما تعرف، ودع LLM يستنتج عندما لا تعرف.** إذا كنت تخزّن قرار مشروع معروف، مرّر `scope="/project/alpha/decisions"`. إذا كنت تخزّن مخرجات وكيل حرة الشكل، اترك النطاق ودع LLM يحدده.
### أمثلة حالات الاستخدام
**فريق متعدد المشاريع:**
```python
memory = Memory()
# Each project gets its own branch
memory.remember("Using microservices architecture", scope="/project/alpha/architecture")
memory.remember("GraphQL API for client apps", scope="/project/beta/api")
# Recall across all projects
memory.recall("API design decisions")
# Or within a specific project
memory.recall("API design", scope="/project/beta")
```
**سياق خاص لكل وكيل مع معرفة مشتركة:**
```python
memory = Memory()
# Researcher has private findings
researcher_memory = memory.scope("/agent/researcher")
# Writer can read from both its own scope and shared company knowledge
writer_view = memory.slice(
scopes=["/agent/writer", "/company/knowledge"],
read_only=True,
)
```
**دعم العملاء (سياق لكل عميل):**
```python
memory = Memory()
# Each customer gets isolated context
memory.remember("Prefers email communication", scope="/customer/acme-corp")
memory.remember("On enterprise plan, 50 seats", scope="/customer/acme-corp")
# Shared product docs are accessible to all agents
memory.remember("Rate limit is 1000 req/min on enterprise plan", scope="/product/docs")
```
## شرائح الذاكرة
### ما هي الشرائح
`MemorySlice` هو عرض عبر نطاقات متعددة، ربما متباعدة. على عكس النطاق (الذي يقيّد على شجرة فرعية واحدة)، تتيح لك الشريحة الاسترجاع من عدة فروع في وقت واحد.
### متى تستخدم الشرائح مقابل النطاقات
- **النطاق**: استخدمه عندما يجب تقييد وكيل أو كتلة كود على شجرة فرعية واحدة. مثال: وكيل يرى فقط `/agent/researcher`.
- **الشريحة**: استخدمها عندما تحتاج إلى دمج السياق من عدة فروع. مثال: وكيل يقرأ من نطاقه الخاص بالإضافة إلى معرفة الشركة المشتركة.
### شرائح القراءة فقط
النمط الأكثر شيوعًا: منح وكيل إمكانية القراءة من فروع متعددة دون السماح له بالكتابة في المناطق المشتركة.
```python
memory = Memory()
# Agent can recall from its own scope AND company knowledge,
# but cannot write to company knowledge
agent_view = memory.slice(
scopes=["/agent/researcher", "/company/knowledge"],
read_only=True,
)
matches = agent_view.recall("company security policies", limit=5)
# Searches both /agent/researcher and /company/knowledge, merges and ranks results
agent_view.remember("new finding") # Raises PermissionError (read-only)
```
### شرائح القراءة والكتابة
عند تعطيل القراءة فقط، يمكنك الكتابة في أي من النطاقات المضمّنة، لكن يجب تحديد النطاق صراحة.
```python
view = memory.slice(scopes=["/team/alpha", "/team/beta"], read_only=False)
# Must specify scope when writing
view.remember("Cross-team decision", scope="/team/alpha", categories=["decisions"])
```
## التسجيل المركب
يتم ترتيب نتائج الاسترجاع بواسطة مزيج مرجّح من ثلاث إشارات:
```
composite = semantic_weight * similarity + recency_weight * decay + importance_weight * importance
```
حيث:
- **similarity** = `1 / (1 + distance)` من فهرس المتجهات (0 إلى 1)
- **decay** = `0.5^(age_days / half_life_days)` -- اضمحلال أُسي (1.0 لليوم، 0.5 عند نصف العمر)
- **importance** = درجة أهمية السجل (0 إلى 1)، يتم تعيينها وقت الترميز
قم بإعدادها مباشرة على منشئ `Memory`:
```python
# Sprint retrospective: favor recent memories, short half-life
memory = Memory(
recency_weight=0.5,
semantic_weight=0.3,
importance_weight=0.2,
recency_half_life_days=7,
)
# Architecture knowledge base: favor important memories, long half-life
memory = Memory(
recency_weight=0.1,
semantic_weight=0.5,
importance_weight=0.4,
recency_half_life_days=180,
)
```
يتضمن كل `MemoryMatch` قائمة `match_reasons` حتى تتمكن من رؤية سبب ترتيب نتيجة معينة في موضعها (مثل `["semantic", "recency", "importance"]`).
## طبقة تحليل LLM
تستخدم الذاكرة LLM بثلاث طرق:
1. **عند الحفظ** -- عندما تحذف النطاق أو الفئات أو الأهمية، يحلل LLM المحتوى ويقترح النطاق والفئات والأهمية والبيانات الوصفية (الكيانات والتواريخ والموضوعات).
2. **عند الاسترجاع** -- للاسترجاع العميق/التلقائي، يحلل LLM الاستعلام (الكلمات المفتاحية، تلميحات الوقت، النطاقات المقترحة، التعقيد) لتوجيه الاسترجاع.
3. **استخراج الذكريات** -- `extract_memories(content)` يقسم النص الخام (مثل مخرجات المهمة) إلى عبارات ذاكرة منفصلة. يستخدم الوكلاء هذا قبل استدعاء `remember()` على كل عبارة حتى يتم تخزين حقائق ذرية بدلاً من كتلة كبيرة واحدة.
جميع التحليلات تتدهور بسلاسة عند فشل LLM -- انظر [سلوك الفشل](#سلوك-الفشل).
## توحيد الذاكرة
عند حفظ محتوى جديد، يتحقق خط أنابيب الترميز تلقائيًا من وجود سجلات مماثلة في التخزين. إذا كان التشابه أعلى من `consolidation_threshold` (الافتراضي 0.85)، يقرر LLM ما يجب فعله:
- **keep** -- السجل الحالي لا يزال دقيقًا وغير مكرر.
- **update** -- يجب تحديث السجل الحالي بمعلومات جديدة (يوفر LLM المحتوى المدمج).
- **delete** -- السجل الحالي قديم أو تم استبداله أو تناقضه.
- **insert_new** -- ما إذا كان يجب إدراج المحتوى الجديد أيضًا كسجل منفصل.
هذا يمنع تراكم النسخ المكررة. على سبيل المثال، إذا حفظت "CrewAI ensures reliable operation" ثلاث مرات، يتعرف التوحيد على النسخ المكررة ويحتفظ بسجل واحد فقط.
### إزالة التكرار داخل الدفعة
عند استخدام `remember_many()`، تتم مقارنة العناصر داخل نفس الدفعة مع بعضها البعض قبل الوصول إلى التخزين. إذا كان تشابه جيب التمام >= `batch_dedup_threshold` (الافتراضي 0.98)، يتم إسقاط العنصر الأحدث بصمت. هذا يلتقط النسخ المكررة الدقيقة أو شبه الدقيقة داخل دفعة واحدة دون أي استدعاءات LLM (رياضيات متجهات خالصة).
```python
# Only 2 records are stored (the third is a near-duplicate of the first)
memory.remember_many([
"CrewAI supports complex workflows.",
"Python is a great language.",
"CrewAI supports complex workflows.", # dropped by intra-batch dedup
])
```
## الحفظ غير الحاجب
`remember_many()` **غير حاجب** -- يقدم خط أنابيب الترميز إلى خيط خلفي ويعود فورًا. هذا يعني أن الوكيل يمكنه المتابعة إلى المهمة التالية بينما يتم حفظ الذكريات.
```python
# Returns immediately -- save happens in background
memory.remember_many(["Fact A.", "Fact B.", "Fact C."])
# recall() automatically waits for pending saves before searching
matches = memory.recall("facts") # sees all 3 records
```
### حاجز القراءة
كل استدعاء `recall()` يستدعي تلقائيًا `drain_writes()` قبل البحث، مما يضمن أن الاستعلام يرى دائمًا أحدث السجلات المستمرة. هذا شفاف -- لا تحتاج أبدًا إلى التفكير فيه.
### إيقاف الفريق
عند انتهاء الفريق، يستنزف `kickoff()` جميع عمليات حفظ الذاكرة المعلقة في كتلة `finally` الخاصة به، لذا لا تُفقد أي عمليات حفظ حتى لو اكتمل الفريق بينما عمليات الحفظ الخلفية قيد التنفيذ.
### الاستخدام المستقل
للسكربتات أو دفاتر الملاحظات حيث لا توجد دورة حياة فريق، استدعِ `drain_writes()` أو `close()` صراحة:
```python
memory = Memory()
memory.remember_many(["Fact A.", "Fact B."])
# Option 1: Wait for pending saves
memory.drain_writes()
# Option 2: Drain and shut down the background pool
memory.close()
```
## المصدر والخصوصية
يمكن لكل سجل ذاكرة أن يحمل علامة `source` لتتبع المصدر وعلامة `private` للتحكم في الوصول.
### تتبع المصدر
يحدد معامل `source` من أين جاءت الذاكرة:
```python
# Tag memories with their origin
memory.remember("User prefers dark mode", source="user:alice")
memory.remember("System config updated", source="admin")
memory.remember("Agent found a bug", source="agent:debugger")
# Recall only memories from a specific source
matches = memory.recall("user preferences", source="user:alice")
```
### الذكريات الخاصة
الذكريات الخاصة مرئية فقط للاسترجاع عندما يتطابق `source`:
```python
# Store a private memory
memory.remember("Alice's API key is sk-...", source="user:alice", private=True)
# This recall sees the private memory (source matches)
matches = memory.recall("API key", source="user:alice")
# This recall does NOT see it (different source)
matches = memory.recall("API key", source="user:bob")
# Admin access: see all private records regardless of source
matches = memory.recall("API key", include_private=True)
```
هذا مفيد بشكل خاص في النشرات متعددة المستخدمين أو المؤسسية حيث يجب عزل ذكريات المستخدمين المختلفين.
## RecallFlow (الاسترجاع العميق)
يدعم `recall()` عمقين:
- **`depth="shallow"`** -- بحث متجهي مباشر مع تسجيل مركب. سريع (~200 مللي ثانية)، بدون استدعاءات LLM.
- **`depth="deep"` (افتراضي)** -- يشغل RecallFlow متعدد الخطوات: تحليل الاستعلام، اختيار النطاق، بحث متجهي متوازٍ، توجيه قائم على الثقة، واستكشاف متكرر اختياري عندما تكون الثقة منخفضة.
**تخطي LLM الذكي**: الاستعلامات الأقصر من `query_analysis_threshold` (الافتراضي 200 حرف) تتخطى تحليل LLM للاستعلام بالكامل، حتى في الوضع العميق. الاستعلامات القصيرة مثل "ما قاعدة البيانات التي نستخدمها؟" هي بالفعل عبارات بحث جيدة -- تحليل LLM يضيف قيمة قليلة. هذا يوفر ~1-3 ثوانٍ لكل استرجاع للاستعلامات القصيرة النموذجية. فقط الاستعلامات الأطول (مثل أوصاف المهام الكاملة) تمر عبر تقطير LLM إلى استعلامات فرعية مستهدفة.
```python
# Shallow: pure vector search, no LLM
matches = memory.recall("What did we decide?", limit=10, depth="shallow")
# Deep (default): intelligent retrieval with LLM analysis for long queries
matches = memory.recall(
"Summarize all architecture decisions from this quarter",
limit=10,
depth="deep",
)
```
عتبات الثقة التي تتحكم في موجّه RecallFlow قابلة للإعداد:
```python
memory = Memory(
confidence_threshold_high=0.9, # Only synthesize when very confident
confidence_threshold_low=0.4, # Explore deeper more aggressively
exploration_budget=2, # Allow up to 2 exploration rounds
query_analysis_threshold=200, # Skip LLM for queries shorter than this
)
```
## إعداد المُضمِّن
تحتاج الذاكرة إلى نموذج تضمين لتحويل النص إلى متجهات للبحث الدلالي. يمكنك إعداده بثلاث طرق.
### التمرير إلى Memory مباشرة
```python
from crewai import Memory
# As a config dict
memory = Memory(embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}})
# As a pre-built callable
from crewai.rag.embeddings.factory import build_embedder
embedder = build_embedder({"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}})
memory = Memory(embedder=embedder)
```
### عبر إعداد مُضمِّن Crew
عند استخدام `memory=True`، يتم تمرير إعداد `embedder` الخاص بالفريق:
```python
from crewai import Crew
crew = Crew(
agents=[...],
tasks=[...],
memory=True,
embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}},
)
```
### أمثلة المزودين
<AccordionGroup>
<Accordion title="OpenAI (افتراضي)">
```python
memory = Memory(embedder={
"provider": "openai",
"config": {
"model_name": "text-embedding-3-small",
# "api_key": "sk-...", # or set OPENAI_API_KEY env var
},
})
```
</Accordion>
<Accordion title="Ollama (محلي، خاص)">
```python
memory = Memory(embedder={
"provider": "ollama",
"config": {
"model_name": "mxbai-embed-large",
"url": "http://localhost:11434/api/embeddings",
},
})
```
</Accordion>
<Accordion title="Azure OpenAI">
```python
memory = Memory(embedder={
"provider": "azure",
"config": {
"deployment_id": "your-embedding-deployment",
"api_key": "your-azure-api-key",
"api_base": "https://your-resource.openai.azure.com",
"api_version": "2024-02-01",
},
})
```
</Accordion>
<Accordion title="Google AI">
```python
memory = Memory(embedder={
"provider": "google-generativeai",
"config": {
"model_name": "gemini-embedding-001",
# "api_key": "...", # or set GOOGLE_API_KEY env var
},
})
```
</Accordion>
<Accordion title="Google Vertex AI">
```python
memory = Memory(embedder={
"provider": "google-vertex",
"config": {
"model_name": "gemini-embedding-001",
"project_id": "your-gcp-project-id",
"location": "us-central1",
},
})
```
</Accordion>
<Accordion title="Cohere">
```python
memory = Memory(embedder={
"provider": "cohere",
"config": {
"model_name": "embed-english-v3.0",
# "api_key": "...", # or set COHERE_API_KEY env var
},
})
```
</Accordion>
<Accordion title="VoyageAI">
```python
memory = Memory(embedder={
"provider": "voyageai",
"config": {
"model": "voyage-3",
# "api_key": "...", # or set VOYAGE_API_KEY env var
},
})
```
</Accordion>
<Accordion title="AWS Bedrock">
```python
memory = Memory(embedder={
"provider": "amazon-bedrock",
"config": {
"model_name": "amazon.titan-embed-text-v1",
# Uses default AWS credentials (boto3 session)
},
})
```
</Accordion>
<Accordion title="Hugging Face">
```python
memory = Memory(embedder={
"provider": "huggingface",
"config": {
"model_name": "sentence-transformers/all-MiniLM-L6-v2",
},
})
```
</Accordion>
<Accordion title="Jina">
```python
memory = Memory(embedder={
"provider": "jina",
"config": {
"model_name": "jina-embeddings-v2-base-en",
# "api_key": "...", # or set JINA_API_KEY env var
},
})
```
</Accordion>
<Accordion title="IBM WatsonX">
```python
memory = Memory(embedder={
"provider": "watsonx",
"config": {
"model_id": "ibm/slate-30m-english-rtrvr",
"api_key": "your-watsonx-api-key",
"project_id": "your-project-id",
"url": "https://us-south.ml.cloud.ibm.com",
},
})
```
</Accordion>
<Accordion title="مُضمِّن مخصص">
```python
# Pass any callable that takes a list of strings and returns a list of vectors
def my_embedder(texts: list[str]) -> list[list[float]]:
# Your embedding logic here
return [[0.1, 0.2, ...] for _ in texts]
memory = Memory(embedder=my_embedder)
```
</Accordion>
</AccordionGroup>
### مرجع المزودين
| المزود | المفتاح | النموذج النموذجي | ملاحظات |
| :--- | :--- | :--- | :--- |
| OpenAI | `openai` | `text-embedding-3-small` | افتراضي. عيّن `OPENAI_API_KEY`. |
| Ollama | `ollama` | `mxbai-embed-large` | محلي، لا حاجة لمفتاح API. |
| Azure OpenAI | `azure` | `text-embedding-ada-002` | يتطلب `deployment_id`. |
| Google AI | `google-generativeai` | `gemini-embedding-001` | عيّن `GOOGLE_API_KEY`. |
| Google Vertex | `google-vertex` | `gemini-embedding-001` | يتطلب `project_id`. |
| Cohere | `cohere` | `embed-english-v3.0` | دعم قوي متعدد اللغات. |
| VoyageAI | `voyageai` | `voyage-3` | محسّن للاسترجاع. |
| AWS Bedrock | `amazon-bedrock` | `amazon.titan-embed-text-v1` | يستخدم بيانات اعتماد boto3. |
| Hugging Face | `huggingface` | `all-MiniLM-L6-v2` | sentence-transformers محلي. |
| Jina | `jina` | `jina-embeddings-v2-base-en` | عيّن `JINA_API_KEY`. |
| IBM WatsonX | `watsonx` | `ibm/slate-30m-english-rtrvr` | يتطلب `project_id`. |
| Sentence Transformer | `sentence-transformer` | `all-MiniLM-L6-v2` | محلي، لا حاجة لمفتاح API. |
| مخصص | `custom` | -- | يتطلب `embedding_callable`. |
## إعداد LLM
تستخدم الذاكرة LLM لتحليل الحفظ (استنتاج النطاق والفئات والأهمية)، وقرارات التوحيد، وتحليل استعلام الاسترجاع العميق. يمكنك إعداد النموذج المُستخدم.
```python
from crewai import Memory, LLM
# Default: gpt-4o-mini
memory = Memory()
# Use a different OpenAI model
memory = Memory(llm="gpt-4o")
# Use Anthropic
memory = Memory(llm="anthropic/claude-3-haiku-20240307")
# Use Ollama for fully local/private analysis
memory = Memory(llm="ollama/llama3.2")
# Use Google Gemini
memory = Memory(llm="gemini/gemini-2.0-flash")
# Pass a pre-configured LLM instance with custom settings
llm = LLM(model="gpt-4o", temperature=0)
memory = Memory(llm=llm)
```
يتم تهيئة LLM **بشكل كسول** -- يتم إنشاؤه فقط عند الحاجة لأول مرة. هذا يعني أن `Memory()` لا يفشل أبدًا في وقت الإنشاء، حتى لو لم تكن مفاتيح API مُعيّنة. تظهر الأخطاء فقط عند استدعاء LLM فعليًا (مثلاً عند الحفظ بدون نطاق/فئات صريحة، أو أثناء الاسترجاع العميق).
للتشغيل المحلي/الخاص بالكامل، استخدم نموذجًا محليًا لكل من LLM والمُضمِّن:
```python
memory = Memory(
llm="ollama/llama3.2",
embedder={"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}},
)
```
## واجهة التخزين
- **الافتراضي**: LanceDB، مخزّن تحت `./.crewai/memory` (أو `$CREWAI_STORAGE_DIR/memory` إذا تم تعيين متغير البيئة، أو المسار الذي تمرره كـ `storage="path/to/dir"`).
- **واجهة مخصصة**: نفّذ بروتوكول `StorageBackend` (انظر `crewai.memory.storage.backend`) ومرّر مثيلًا إلى `Memory(storage=your_backend)`.
## الاستكشاف
فحص التسلسل الهرمي للنطاقات والفئات والسجلات:
```python
memory.tree() # Formatted tree of scopes and record counts
memory.tree("/project", max_depth=2) # Subtree view
memory.info("/project") # ScopeInfo: record_count, categories, oldest/newest
memory.list_scopes("/") # Immediate child scopes
memory.list_categories() # Category names and counts
memory.list_records(scope="/project/alpha", limit=20) # Records in a scope, newest first
```
## سلوك الفشل
إذا فشل LLM أثناء التحليل (خطأ شبكة، حد معدل، استجابة غير صالحة)، تتدهور الذاكرة بسلاسة:
- **تحليل الحفظ** -- يتم تسجيل تحذير ولا يزال يتم تخزين الذاكرة مع النطاق الافتراضي `/`، فئات فارغة، وأهمية `0.5`.
- **استخراج الذكريات** -- يتم تخزين المحتوى الكامل كذاكرة واحدة حتى لا يُفقد شيء.
- **تحليل الاستعلام** -- يتراجع الاسترجاع إلى اختيار نطاق بسيط وبحث متجهي حتى تستمر في الحصول على نتائج.
لا يتم رفع أي استثناء لفشل التحليل هذه؛ فقط فشل التخزين أو المُضمِّن سيرفع استثناءً.
## ملاحظة حول الخصوصية
يتم إرسال محتوى الذاكرة إلى LLM المُعدّ للتحليل (النطاق/الفئات/الأهمية عند الحفظ، تحليل الاستعلام والاسترجاع العميق الاختياري). للبيانات الحساسة، استخدم LLM محليًا (مثل Ollama) أو تأكد من أن مزودك يلبي متطلبات الامتثال الخاصة بك.
## أحداث الذاكرة
جميع عمليات الذاكرة تُصدر أحداثًا مع `source_type="unified_memory"`. يمكنك الاستماع للتوقيت والأخطاء والمحتوى.
| الحدث | الوصف | الخصائص الرئيسية |
| :---- | :---------- | :------------- |
| **MemoryQueryStartedEvent** | بداية الاستعلام | `query`, `limit` |
| **MemoryQueryCompletedEvent** | نجاح الاستعلام | `query`, `results`, `query_time_ms` |
| **MemoryQueryFailedEvent** | فشل الاستعلام | `query`, `error` |
| **MemorySaveStartedEvent** | بداية الحفظ | `value`, `metadata` |
| **MemorySaveCompletedEvent** | نجاح الحفظ | `value`, `save_time_ms` |
| **MemorySaveFailedEvent** | فشل الحفظ | `value`, `error` |
| **MemoryRetrievalStartedEvent** | بداية استرجاع الوكيل | `task_id` |
| **MemoryRetrievalCompletedEvent** | اكتمال استرجاع الوكيل | `task_id`, `memory_content`, `retrieval_time_ms` |
مثال: مراقبة وقت الاستعلام:
```python
from crewai.events import BaseEventListener, MemoryQueryCompletedEvent
class MemoryMonitor(BaseEventListener):
def setup_listeners(self, crewai_event_bus):
@crewai_event_bus.on(MemoryQueryCompletedEvent)
def on_done(source, event):
if getattr(event, "source_type", None) == "unified_memory":
print(f"Query '{event.query}' completed in {event.query_time_ms:.0f}ms")
```
## استكشاف المشاكل
**الذاكرة لا تستمر؟**
- تأكد من أن مسار التخزين قابل للكتابة (الافتراضي `./.crewai/memory`). مرّر `storage="./your_path"` لاستخدام مجلد مختلف، أو عيّن متغير البيئة `CREWAI_STORAGE_DIR`.
- عند استخدام فريق، تأكد من تعيين `memory=True` أو `memory=Memory(...)`.
**الاسترجاع بطيء؟**
- استخدم `depth="shallow"` لسياق الوكيل الروتيني. احتفظ بـ `depth="deep"` للاستعلامات المعقدة.
- زد `query_analysis_threshold` لتخطي تحليل LLM لمزيد من الاستعلامات.
**أخطاء تحليل LLM في السجلات؟**
- لا تزال الذاكرة تحفظ/تسترجع بإعدادات افتراضية آمنة. تحقق من مفاتيح API وحدود المعدل وتوفر النموذج إذا كنت تريد تحليل LLM كاملاً.
**أخطاء حفظ خلفية في السجلات؟**
- عمليات حفظ الذاكرة تعمل في خيط خلفي. تُصدر الأخطاء كـ `MemorySaveFailedEvent` لكنها لا تعطل الوكيل. تحقق من السجلات للسبب الجذري (عادة مشاكل اتصال LLM أو المُضمِّن).
**تعارضات الكتابة المتزامنة؟**
- عمليات LanceDB مُتسلسلة بقفل مشترك وتُعاد تلقائيًا عند التعارض. هذا يتعامل مع مثيلات `Memory` المتعددة التي تشير إلى نفس قاعدة البيانات (مثل ذاكرة وكيل + ذاكرة فريق). لا حاجة لإجراء.
**تصفح الذاكرة من الطرفية:**
```bash
crewai memory # Opens the TUI browser
crewai memory --storage-path ./my_memory # Point to a specific directory
```
**إعادة تعيين الذاكرة (مثلاً للاختبارات):**
```python
crew.reset_memories(command_type="memory") # Resets unified memory
# Or on a Memory instance:
memory.reset() # All scopes
memory.reset(scope="/project/old") # Only that subtree
```
## مرجع الإعداد
جميع الإعدادات تُمرر كمعاملات كلمة مفتاحية إلى `Memory(...)`. كل معامل له قيمة افتراضية معقولة.
| المعامل | الافتراضي | الوصف |
| :--- | :--- | :--- |
| `llm` | `"gpt-4o-mini"` | LLM للتحليل (اسم نموذج أو مثيل `BaseLLM`). |
| `storage` | `"lancedb"` | واجهة التخزين (`"lancedb"`، سلسلة مسار، أو مثيل `StorageBackend`). |
| `embedder` | `None` (افتراضي OpenAI) | المُضمِّن (قاموس إعداد، دالة قابلة للاستدعاء، أو `None` لافتراضي OpenAI). |
| `recency_weight` | `0.3` | وزن الحداثة في الدرجة المركبة. |
| `semantic_weight` | `0.5` | وزن التشابه الدلالي في الدرجة المركبة. |
| `importance_weight` | `0.2` | وزن الأهمية في الدرجة المركبة. |
| `recency_half_life_days` | `30` | أيام لتنصيف درجة الحداثة (اضمحلال أُسي). |
| `consolidation_threshold` | `0.85` | التشابه الذي يُشغّل فوقه التوحيد عند الحفظ. عيّن إلى `1.0` للتعطيل. |
| `consolidation_limit` | `5` | أقصى عدد سجلات حالية للمقارنة أثناء التوحيد. |
| `default_importance` | `0.5` | الأهمية المُعيّنة عندما لا تُوفَّر ويتم تخطي تحليل LLM. |
| `batch_dedup_threshold` | `0.98` | تشابه جيب التمام لإسقاط النسخ شبه المكررة داخل دفعة `remember_many()`. |
| `confidence_threshold_high` | `0.8` | ثقة الاسترجاع التي تُعاد فوقها النتائج مباشرة. |
| `confidence_threshold_low` | `0.5` | ثقة الاسترجاع التي يُشغّل تحتها استكشاف أعمق. |
| `complex_query_threshold` | `0.7` | للاستعلامات المعقدة، استكشف أعمق تحت هذه الثقة. |
| `exploration_budget` | `1` | عدد جولات الاستكشاف المدفوعة بـ LLM أثناء الاسترجاع العميق. |
| `query_analysis_threshold` | `200` | الاستعلامات الأقصر من هذا (بالأحرف) تتخطى تحليل LLM أثناء الاسترجاع العميق. |

View File

@@ -0,0 +1,155 @@
---
title: التخطيط
description: تعرّف على كيفية إضافة التخطيط إلى طاقم CrewAI وتحسين أدائه.
icon: ruler-combined
mode: "wide"
---
## نظرة عامة
تتيح لك ميزة التخطيط في CrewAI إضافة قدرة التخطيط إلى طاقمك. عند تفعيلها، قبل كل تكرار للطاقم،
يتم إرسال جميع معلومات الطاقم إلى AgentPlanner الذي يخطط للمهام خطوة بخطوة، ويُضاف هذا المخطط إلى وصف كل مهمة.
### استخدام ميزة التخطيط
البدء بميزة التخطيط سهل جدًا، الخطوة الوحيدة المطلوبة هي إضافة `planning=True` إلى طاقمك:
<CodeGroup>
```python Code
from crewai import Crew, Agent, Task, Process
# تجميع طاقمك مع قدرات التخطيط
my_crew = Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
planning=True,
)
```
</CodeGroup>
من هذه النقطة فصاعدًا، سيكون التخطيط مفعّلًا في طاقمك، وسيتم تخطيط المهام قبل كل تكرار.
<Warning>
عند تفعيل التخطيط، سيستخدم CrewAI `gpt-4o-mini` كنموذج LLM افتراضي للتخطيط، مما يتطلب مفتاح API صالحًا من OpenAI. نظرًا لأن وكلاءك قد يستخدمون نماذج LLM مختلفة، فقد يسبب ذلك ارتباكًا إذا لم يكن لديك مفتاح OpenAI API مهيأ أو إذا كنت تواجه سلوكًا غير متوقع متعلقًا باستدعاءات LLM API.
</Warning>
#### LLM التخطيط
يمكنك الآن تحديد نموذج LLM الذي سيُستخدم لتخطيط المهام.
عند تشغيل مثال الحالة الأساسية، سترى شيئًا مشابهًا للمخرجات أدناه، والتي تمثل مخرجات `AgentPlanner`
المسؤول عن إنشاء المنطق التدريجي لإضافته إلى مهام الوكلاء.
<CodeGroup>
```python Code
from crewai import Crew, Agent, Task, Process
# تجميع طاقمك مع قدرات التخطيط ونموذج LLM مخصص
my_crew = Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
planning=True,
planning_llm="gpt-4o"
)
# تشغيل الطاقم
my_crew.kickoff()
```
```markdown Result
[2024-07-15 16:49:11][INFO]: Planning the crew execution
**Step-by-Step Plan for Task Execution**
**Task Number 1: Conduct a thorough research about AI LLMs**
**Agent:** AI LLMs Senior Data Researcher
**Agent Goal:** Uncover cutting-edge developments in AI LLMs
**Task Expected Output:** A list with 10 bullet points of the most relevant information about AI LLMs
**Task Tools:** None specified
**Agent Tools:** None specified
**Step-by-Step Plan:**
1. **Define Research Scope:**
- Determine the specific areas of AI LLMs to focus on, such as advancements in architecture, use cases, ethical considerations, and performance metrics.
2. **Identify Reliable Sources:**
- List reputable sources for AI research, including academic journals, industry reports, conferences (e.g., NeurIPS, ACL), AI research labs (e.g., OpenAI, Google AI), and online databases (e.g., IEEE Xplore, arXiv).
3. **Collect Data:**
- Search for the latest papers, articles, and reports published in 2024 and early 2025.
- Use keywords like "Large Language Models 2025", "AI LLM advancements", "AI ethics 2025", etc.
4. **Analyze Findings:**
- Read and summarize the key points from each source.
- Highlight new techniques, models, and applications introduced in the past year.
5. **Organize Information:**
- Categorize the information into relevant topics (e.g., new architectures, ethical implications, real-world applications).
- Ensure each bullet point is concise but informative.
6. **Create the List:**
- Compile the 10 most relevant pieces of information into a bullet point list.
- Review the list to ensure clarity and relevance.
**Expected Output:**
A list with 10 bullet points of the most relevant information about AI LLMs.
---
**Task Number 2: Review the context you got and expand each topic into a full section for a report**
**Agent:** AI LLMs Reporting Analyst
**Agent Goal:** Create detailed reports based on AI LLMs data analysis and research findings
**Task Expected Output:** A fully fledged report with the main topics, each with a full section of information. Formatted as markdown without '```'
**Task Tools:** None specified
**Agent Tools:** None specified
**Step-by-Step Plan:**
1. **Review the Bullet Points:**
- Carefully read through the list of 10 bullet points provided by the AI LLMs Senior Data Researcher.
2. **Outline the Report:**
- Create an outline with each bullet point as a main section heading.
- Plan sub-sections under each main heading to cover different aspects of the topic.
3. **Research Further Details:**
- For each bullet point, conduct additional research if necessary to gather more detailed information.
- Look for case studies, examples, and statistical data to support each section.
4. **Write Detailed Sections:**
- Expand each bullet point into a comprehensive section.
- Ensure each section includes an introduction, detailed explanation, examples, and a conclusion.
- Use markdown formatting for headings, subheadings, lists, and emphasis.
5. **Review and Edit:**
- Proofread the report for clarity, coherence, and correctness.
- Make sure the report flows logically from one section to the next.
- Format the report according to markdown standards.
6. **Finalize the Report:**
- Ensure the report is complete with all sections expanded and detailed.
- Double-check formatting and make any necessary adjustments.
**Expected Output:**
A fully fledged report with the main topics, each with a full section of information. Formatted as markdown without '```'.
```
</CodeGroup>

View File

@@ -0,0 +1,66 @@
---
title: العمليات
description: دليل تفصيلي حول إدارة سير العمل من خلال العمليات في CrewAI، مع تفاصيل التنفيذ المحدّثة.
icon: bars-staggered
mode: "wide"
---
## نظرة عامة
<Tip>
تنسّق العمليات تنفيذ المهام بواسطة الوكلاء، على غرار إدارة المشاريع في الفرق البشرية.
تضمن هذه العمليات توزيع المهام وتنفيذها بكفاءة، وفقًا لاستراتيجية محددة مسبقًا.
</Tip>
## تنفيذات العمليات
- **تسلسلي**: ينفذ المهام بالتتابع، مما يضمن إكمال المهام بتقدم منظم.
- **هرمي**: ينظم المهام في تسلسل إداري هرمي، حيث يتم تفويض المهام وتنفيذها بناءً على سلسلة أوامر منظمة. يجب تحديد نموذج لغة المدير (`manager_llm`) أو وكيل مدير مخصص (`manager_agent`) في الطاقم لتفعيل العملية الهرمية، مما يسهّل إنشاء وإدارة المهام من قبل المدير.
## دور العمليات في العمل الجماعي
تُمكّن العمليات الوكلاء الأفراد من العمل كوحدة متماسكة، مما يبسّط جهودهم لتحقيق أهداف مشتركة بكفاءة وتناسق.
## تعيين العمليات للطاقم
لتعيين عملية لطاقم، حدد نوع العملية عند إنشاء الطاقم لتعيين استراتيجية التنفيذ. للعملية الهرمية، تأكد من تحديد `manager_llm` أو `manager_agent` لوكيل المدير.
```python
from crewai import Crew, Process
# مثال: إنشاء طاقم بعملية تسلسلية
crew = Crew(
agents=my_agents,
tasks=my_tasks,
process=Process.sequential
)
# مثال: إنشاء طاقم بعملية هرمية
# تأكد من توفير manager_llm أو manager_agent
crew = Crew(
agents=my_agents,
tasks=my_tasks,
process=Process.hierarchical,
manager_llm="gpt-4o"
# أو
# manager_agent=my_manager_agent
)
```
**ملاحظة:** تأكد من تعريف `my_agents` و `my_tasks` قبل إنشاء كائن `Crew`، وللعملية الهرمية، يُعد `manager_llm` أو `manager_agent` مطلوبًا أيضًا.
## العملية التسلسلية
تعكس هذه الطريقة سير عمل الفريق الديناميكي، وتتقدم عبر المهام بطريقة مدروسة ومنهجية. يتبع تنفيذ المهام الترتيب المحدد مسبقًا في قائمة المهام، حيث يعمل ناتج مهمة واحدة كسياق للمهمة التالية.
لتخصيص سياق المهمة، استخدم معامل `context` في فئة `Task` لتحديد المخرجات التي يجب استخدامها كسياق للمهام اللاحقة.
## العملية الهرمية
تحاكي التسلسل الهرمي المؤسسي، حيث يسمح CrewAI بتحديد وكيل مدير مخصص أو إنشاء واحد تلقائيًا، مما يتطلب تحديد نموذج لغة المدير (`manager_llm`). يشرف هذا الوكيل على تنفيذ المهام، بما في ذلك التخطيط والتفويض والتحقق. لا يتم تعيين المهام مسبقًا؛ يخصص المدير المهام للوكلاء بناءً على قدراتهم، ويراجع المخرجات، ويقيّم اكتمال المهام.
## فئة Process: نظرة عامة مفصلة
تم تنفيذ فئة `Process` كتعداد (`Enum`)، مما يضمن أمان الأنواع ويقيّد قيم العملية على الأنواع المحددة (`sequential`، `hierarchical`).
## الخلاصة
التعاون المنظم الذي تسهّله العمليات داخل CrewAI ضروري لتمكين العمل الجماعي المنهجي بين الوكلاء.
تم تحديث هذه الوثائق لتعكس أحدث الميزات والتحسينات، مما يضمن وصول المستخدمين إلى أحدث المعلومات وأكثرها شمولاً.

View File

@@ -0,0 +1,162 @@
---
title: بنية الإنتاج
description: أفضل الممارسات لبناء تطبيقات ذكاء اصطناعي جاهزة للإنتاج مع CrewAI
icon: server
mode: "wide"
---
# عقلية التدفق أولاً
عند بناء تطبيقات ذكاء اصطناعي إنتاجية مع CrewAI، **نوصي بالبدء بتدفق (Flow)**.
بينما يمكن تشغيل أطقم أو وكلاء فرديين، فإن تغليفهم في تدفق يوفر الهيكل اللازم لتطبيق متين وقابل للتوسع.
## لماذا التدفقات؟
1. **إدارة الحالة**: توفر التدفقات طريقة مدمجة لإدارة الحالة عبر مراحل مختلفة من تطبيقك. هذا ضروري لتمرير البيانات بين الأطقم والحفاظ على السياق ومعالجة مدخلات المستخدم.
2. **التحكم**: تتيح لك التدفقات تحديد مسارات تنفيذ دقيقة، بما في ذلك الحلقات والشرطيات ومنطق التفريع. هذا أساسي لمعالجة الحالات الاستثنائية وضمان سلوك تطبيقك بشكل متوقع.
3. **المراقبة**: توفر التدفقات هيكلًا واضحًا يسهّل تتبع التنفيذ وتصحيح الأخطاء ومراقبة الأداء. نوصي باستخدام [تتبع CrewAI](/ar/observability/tracing) للحصول على رؤى تفصيلية. ما عليك سوى تشغيل `crewai login` لتفعيل ميزات المراقبة المجانية.
## البنية
يبدو تطبيق CrewAI الإنتاجي النموذجي هكذا:
```mermaid
graph TD
Start((Start)) --> Flow[Flow Orchestrator]
Flow --> State{State Management}
State --> Step1[Step 1: Data Gathering]
Step1 --> Crew1[Research Crew]
Crew1 --> State
State --> Step2{Condition Check}
Step2 -- "Valid" --> Step3[Step 3: Execution]
Step3 --> Crew2[Action Crew]
Step2 -- "Invalid" --> End((End))
Crew2 --> End
```
### 1. فئة التدفق
فئة `Flow` هي نقطة الدخول. تحدد مخطط الحالة والطرق التي تنفذ منطقك.
```python
from crewai.flow.flow import Flow, listen, start
from pydantic import BaseModel
class AppState(BaseModel):
user_input: str = ""
research_results: str = ""
final_report: str = ""
class ProductionFlow(Flow[AppState]):
@start()
def gather_input(self):
# ... منطق الحصول على المدخلات ...
pass
@listen(gather_input)
def run_research_crew(self):
# ... تشغيل طاقم ...
pass
```
### 2. إدارة الحالة
استخدم نماذج Pydantic لتعريف حالتك. يضمن هذا أمان الأنواع ويوضح البيانات المتاحة في كل مرحلة.
- **اجعلها بسيطة**: خزّن فقط ما تحتاجه للاستمرار بين المراحل.
- **استخدم بيانات منظمة**: تجنب القواميس غير المنظمة قدر الإمكان.
### 3. الأطقم كوحدات عمل
فوّض المهام المعقدة إلى الأطقم. يجب أن يكون الطاقم مركّزًا على هدف محدد (مثل "البحث في موضوع"، "كتابة مقال مدونة").
- **لا تبالغ في هندسة الأطقم**: اجعلها مركّزة.
- **مرر الحالة بشكل صريح**: مرر البيانات الضرورية من حالة التدفق إلى مدخلات الطاقم.
```python
@listen(gather_input)
def run_research_crew(self):
crew = ResearchCrew()
result = crew.kickoff(inputs={"topic": self.state.user_input})
self.state.research_results = result.raw
```
## عناصر التحكم الأولية
استفد من عناصر التحكم الأولية في CrewAI لإضافة المتانة والتحكم إلى أطقمك.
### 1. حواجز المهام
استخدم [حواجز المهام](/ar/concepts/tasks#task-guardrails) للتحقق من مخرجات المهام قبل قبولها. يضمن هذا أن وكلاءك ينتجون نتائج عالية الجودة.
```python
def validate_content(result: TaskOutput) -> Tuple[bool, Any]:
if len(result.raw) < 100:
return (False, "Content is too short. Please expand.")
return (True, result.raw)
task = Task(
...,
guardrail=validate_content
)
```
### 2. المخرجات المنظمة
استخدم دائمًا المخرجات المنظمة (`output_pydantic` أو `output_json`) عند تمرير البيانات بين المهام أو إلى تطبيقك. يمنع هذا أخطاء التحليل ويضمن أمان الأنواع.
```python
class ResearchResult(BaseModel):
summary: str
sources: List[str]
task = Task(
...,
output_pydantic=ResearchResult
)
```
### 3. خطافات LLM
استخدم [خطافات LLM](/ar/learn/llm-hooks) لفحص أو تعديل الرسائل قبل إرسالها إلى LLM، أو لتنقية الاستجابات.
```python
@before_llm_call
def log_request(context):
print(f"Agent {context.agent.role} is calling the LLM...")
```
## أنماط النشر
عند نشر تدفقك، ضع في اعتبارك ما يلي:
### CrewAI Enterprise
أسهل طريقة لنشر تدفقك هي استخدام CrewAI Enterprise. تتعامل مع البنية التحتية والمصادقة والمراقبة نيابة عنك.
راجع [دليل النشر](https://docs-platform.crewai.com/platform/ar/guides/deploy-to-amp) للبدء.
```bash
crewai deploy create
```
### التنفيذ غير المتزامن
للمهام طويلة التشغيل، استخدم `kickoff_async` لتجنب حظر واجهتك البرمجية.
### الاستمرارية
استخدم مزيّن `@persist` لحفظ حالة تدفقك في قاعدة بيانات. يتيح لك هذا استئناف التنفيذ إذا تعطلت العملية أو إذا كنت بحاجة لانتظار مدخلات بشرية.
```python
@persist
class ProductionFlow(Flow[AppState]):
# ...
```
افتراضيًا، يستأنف `@persist` تدفقًا عند توفير `kickoff(inputs={"id": <uuid>})`، مما يمدّ نفس تاريخ `flow_uuid`. لـ **تفرع** تدفق مستمر إلى نسبٍ جديد — ترطيب الحالة من تشغيل سابق ولكن الكتابة تحت `state.id` جديد — مرّر `restore_from_state_id`:
```python
flow.kickoff(restore_from_state_id="<previous-run-state-id>")
```
يحصل التشغيل الجديد على `state.id` جديد (مولّد تلقائيًا، أو `inputs["id"]` إذا تم تثبيته) لذا لا تمتد كتابات `@persist` الخاصة به إلى تاريخ المصدر. الجمع مع `from_checkpoint` يطلق `ValueError`؛ اختر مصدر ترطيب واحدًا.
## الخلاصة
- **ابدأ بتدفق.**
- **حدد حالة واضحة.**
- **استخدم الأطقم للمهام المعقدة.**
- **انشر مع API واستمرارية.**

View File

@@ -0,0 +1,148 @@
---
title: الاستدلال
description: "تعرّف على كيفية تفعيل واستخدام استدلال الوكيل لتحسين تنفيذ المهام."
icon: brain
mode: "wide"
---
## نظرة عامة
استدلال الوكيل هو ميزة تتيح للوكلاء التأمل في المهمة وإنشاء خطة قبل التنفيذ. يساعد هذا الوكلاء على التعامل مع المهام بشكل أكثر منهجية ويضمن استعدادهم لأداء العمل المطلوب.
## الاستخدام
لتفعيل الاستدلال لوكيل، ما عليك سوى تعيين `reasoning=True` عند إنشاء الوكيل:
```python
from crewai import Agent
agent = Agent(
role="Data Analyst",
goal="Analyze complex datasets and provide insights",
backstory="You are an experienced data analyst with expertise in finding patterns in complex data.",
reasoning=True, # تفعيل الاستدلال
max_reasoning_attempts=3 # اختياري: تعيين حد أقصى لمحاولات الاستدلال
)
```
## كيف يعمل
عند تفعيل الاستدلال، قبل تنفيذ المهمة، سيقوم الوكيل بما يلي:
1. التأمل في المهمة وإنشاء خطة مفصلة
2. تقييم ما إذا كان مستعدًا لتنفيذ المهمة
3. تحسين الخطة حسب الحاجة حتى يصبح مستعدًا أو يصل إلى max_reasoning_attempts
4. حقن خطة الاستدلال في وصف المهمة قبل التنفيذ
تساعد هذه العملية الوكيل على تقسيم المهام المعقدة إلى خطوات يمكن إدارتها وتحديد التحديات المحتملة قبل البدء.
## خيارات التهيئة
<ParamField body="reasoning" type="bool" default="False">
تفعيل أو تعطيل الاستدلال
</ParamField>
<ParamField body="max_reasoning_attempts" type="int" default="None">
الحد الأقصى لعدد المحاولات لتحسين الخطة قبل المتابعة بالتنفيذ. إذا كانت القيمة None (الافتراضي)، سيستمر الوكيل في التحسين حتى يصبح مستعدًا.
</ParamField>
## مثال
إليك مثالًا كاملًا:
```python
from crewai import Agent, Task, Crew
# إنشاء وكيل مع تفعيل الاستدلال
analyst = Agent(
role="Data Analyst",
goal="Analyze data and provide insights",
backstory="You are an expert data analyst.",
reasoning=True,
max_reasoning_attempts=3 # اختياري: تعيين حد لمحاولات الاستدلال
)
# إنشاء مهمة
analysis_task = Task(
description="Analyze the provided sales data and identify key trends.",
expected_output="A report highlighting the top 3 sales trends.",
agent=analyst
)
# إنشاء طاقم وتشغيل المهمة
crew = Crew(agents=[analyst], tasks=[analysis_task])
result = crew.kickoff()
print(result)
```
## معالجة الأخطاء
صُممت عملية الاستدلال لتكون متينة، مع معالجة أخطاء مدمجة. إذا حدث خطأ أثناء الاستدلال، سيتابع الوكيل تنفيذ المهمة بدون خطة الاستدلال. يضمن هذا إمكانية تنفيذ المهام حتى في حالة فشل عملية الاستدلال.
إليك كيفية التعامل مع الأخطاء المحتملة في الكود الخاص بك:
```python
from crewai import Agent, Task
import logging
# إعداد التسجيل لالتقاط أي أخطاء في الاستدلال
logging.basicConfig(level=logging.INFO)
# إنشاء وكيل مع تفعيل الاستدلال
agent = Agent(
role="Data Analyst",
goal="Analyze data and provide insights",
reasoning=True,
max_reasoning_attempts=3
)
# إنشاء مهمة
task = Task(
description="Analyze the provided sales data and identify key trends.",
expected_output="A report highlighting the top 3 sales trends.",
agent=agent
)
# تنفيذ المهمة
# إذا حدث خطأ أثناء الاستدلال، سيتم تسجيله وسيستمر التنفيذ
result = agent.execute_task(task)
```
## مثال على مخرجات الاستدلال
إليك مثالًا على شكل خطة الاستدلال لمهمة تحليل البيانات:
```
Task: Analyze the provided sales data and identify key trends.
Reasoning Plan:
I'll analyze the sales data to identify the top 3 trends.
1. Understanding of the task:
I need to analyze sales data to identify key trends that would be valuable for business decision-making.
2. Key steps I'll take:
- First, I'll examine the data structure to understand what fields are available
- Then I'll perform exploratory data analysis to identify patterns
- Next, I'll analyze sales by time periods to identify temporal trends
- I'll also analyze sales by product categories and customer segments
- Finally, I'll identify the top 3 most significant trends
3. Approach to challenges:
- If the data has missing values, I'll decide whether to fill or filter them
- If the data has outliers, I'll investigate whether they're valid data points or errors
- If trends aren't immediately obvious, I'll apply statistical methods to uncover patterns
4. Use of available tools:
- I'll use data analysis tools to explore and visualize the data
- I'll use statistical tools to identify significant patterns
- I'll use knowledge retrieval to access relevant information about sales analysis
5. Expected outcome:
A concise report highlighting the top 3 sales trends with supporting evidence from the data.
READY: I am ready to execute the task.
```
تساعد خطة الاستدلال هذه الوكيل على تنظيم نهجه تجاه المهمة، والنظر في التحديات المحتملة، وضمان تقديم المخرجات المتوقعة.

View File

@@ -0,0 +1,373 @@
---
title: المهارات
description: حزم المهارات المبنية على نظام الملفات التي تحقن خبرة المجال والتعليمات في إرشادات الوكلاء.
icon: bolt
mode: "wide"
---
## نظرة عامة
المهارات هي مجلدات مستقلة توفر للوكلاء **تعليمات وإرشادات ومواد مرجعية خاصة بالمجال**. تُعرّف كل مهارة بملف `SKILL.md` يحتوي على بيانات وصفية YAML ومحتوى Markdown.
عند التفعيل، يتم حقن تعليمات المهارة مباشرة في إرشادات مهمة الوكيل — مما يمنح الوكيل خبرة دون الحاجة لأي تغييرات في الكود.
<Note type="info" title="المهارات مقابل الأدوات — التمييز الأساسي">
**المهارات ليست أدوات.** هذه هي نقطة الارتباك الأكثر شيوعًا.
- **المهارات** تحقن *تعليمات وسياق* في إرشادات الوكيل. تخبر الوكيل *كيف يفكر* في مشكلة ما.
- **الأدوات** تمنح الوكيل *دوال قابلة للاستدعاء* لاتخاذ إجراءات (البحث، قراءة الملفات، استدعاء APIs).
غالبًا ما تحتاج **كليهما**: مهارات للخبرة، وأدوات للإجراء. يتم تكوينهما بشكل مستقل ويُكمّلان بعضهما.
</Note>
---
## البداية السريعة
### 1. إنشاء مهارة باستخدام سطر الأوامر (CLI)
واجهة سطر الأوامر هي الطريقة المدعومة لإنشاء مهارة — فهي تُنشئ لك هيكل المجلد وملف `SKILL.md` صالحًا:
```shell Terminal
crewai skill create code-review
```
داخل مشروع طاقم (حيث يوجد `pyproject.toml`) يُنشئ هذا الأمر `./skills/code-review/`؛ وخارج المشروع يُنشئ `./code-review/` في المجلد الحالي (يمكنك فرض هذا السلوك باستخدام `--no-project`):
```
skills/
└── code-review/
├── SKILL.md # Required — instructions (pre-filled template)
├── references/ # Optional — reference docs
├── scripts/ # Optional — executable scripts
└── assets/ # Optional — static files
```
### 2. كتابة SKILL.md الخاص بك
```markdown
---
name: code-review
description: Guidelines for conducting thorough code reviews with focus on security and performance.
metadata:
author: your-team
version: "1.0"
---
## إرشادات مراجعة الكود
عند مراجعة الكود، اتبع قائمة التحقق هذه:
1. **الأمان**: تحقق من ثغرات الحقن وتجاوز المصادقة وكشف البيانات
2. **الأداء**: ابحث عن استعلامات N+1 والتخصيصات غير الضرورية والاستدعاءات المحظورة
3. **القابلية للقراءة**: تأكد من وضوح التسمية والتعليقات المناسبة والأسلوب المتسق
4. **الاختبارات**: تحقق من تغطية اختبار كافية للوظائف الجديدة
### مستويات الخطورة
- **حرج**: ثغرات أمنية، مخاطر فقدان البيانات → حظر الدمج
- **رئيسي**: مشاكل أداء، أخطاء منطقية → طلب تغييرات
- **ثانوي**: مسائل أسلوبية، اقتراحات تسمية → الموافقة مع تعليقات
```
### 3. ربطها بوكيل
```python
from crewai import Agent
from crewai_tools import GithubSearchTool, FileReadTool
reviewer = Agent(
role="Senior Code Reviewer",
goal="Review pull requests for quality and security issues",
backstory="Staff engineer with expertise in secure coding practices.",
skills=["./skills"], # يحقن إرشادات المراجعة
tools=[GithubSearchTool(), FileReadTool()], # يسمح للوكيل بقراءة الكود
)
```
الوكيل الآن لديه **خبرة** (من المهارة) و**قدرات** (من الأدوات) معًا.
---
## المهارات + الأدوات: العمل معًا
إليك أنماط شائعة توضح كيف تُكمّل المهارات والأدوات بعضهما:
### النمط 1: مهارات فقط (خبرة المجال، بدون إجراءات مطلوبة)
استخدم عندما يحتاج الوكيل لتعليمات محددة لكن لا يحتاج لاستدعاء خدمات خارجية:
```python
agent = Agent(
role="Technical Writer",
goal="Write clear API documentation",
backstory="Expert technical writer",
skills=["./skills/api-docs-style"], # إرشادات وقوالب الكتابة
# لا حاجة لأدوات — الوكيل يكتب بناءً على السياق المقدم
)
```
### النمط 2: أدوات فقط (إجراءات، بدون خبرة خاصة)
استخدم عندما يحتاج الوكيل لاتخاذ إجراءات لكن لا يحتاج لتعليمات مجال محددة:
```python
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
agent = Agent(
role="Web Researcher",
goal="Find information about a topic",
backstory="Skilled at finding information online",
tools=[SerperDevTool(), ScrapeWebsiteTool()], # يمكنه البحث والاستخراج
# لا حاجة لمهارات — البحث العام لا يحتاج إرشادات خاصة
)
```
### النمط 3: مهارات + أدوات (خبرة وإجراءات)
النمط الأكثر شيوعًا في العالم الحقيقي. المهارة توفر *كيف* تقترب من العمل؛ الأدوات توفر *ما* يمكن للوكيل فعله:
```python
from crewai_tools import SerperDevTool, FileReadTool, CodeInterpreterTool
analyst = Agent(
role="Security Analyst",
goal="Audit infrastructure for vulnerabilities",
backstory="Expert in cloud security and compliance",
skills=["./skills/security-audit"], # منهجية وقوائم تحقق التدقيق
tools=[
SerperDevTool(), # البحث عن ثغرات معروفة
FileReadTool(), # قراءة ملفات التكوين
CodeInterpreterTool(), # تشغيل سكربتات التحليل
],
)
```
### النمط 4: مهارات + MCP
المهارات تعمل مع خوادم MCP بنفس الطريقة التي تعمل بها مع الأدوات:
```python
agent = Agent(
role="Data Analyst",
goal="Analyze customer data and generate reports",
backstory="Expert data analyst with strong statistical background",
skills=["./skills/data-analysis"], # منهجية التحليل
mcps=["https://data-warehouse.example.com/sse"], # وصول بيانات عن بُعد
)
```
### النمط 5: مهارات + تطبيقات
المهارات يمكن أن توجّه كيف يستخدم الوكيل تكاملات المنصة:
```python
agent = Agent(
role="Customer Support Agent",
goal="Respond to customer inquiries professionally",
backstory="Experienced support representative",
skills=["./skills/support-playbook"], # قوالب الردود وقواعد التصعيد
apps=["gmail", "zendesk"], # يمكنه إرسال رسائل بريد وتحديث التذاكر
)
```
---
## إنشاء المهارات ونشرها وتثبيتها
للمهارات دورة حياة كاملة تُدار عبر واجهة سطر الأوامر: **أنشئها باستخدام `crewai skill create`، وانشرها باستخدام `crewai skill publish`** — إنشاء المجلدات يدويًا يصلح للتجارب المحلية، لكن واجهة سطر الأوامر هي سير العمل المقصود، وهي تحافظ على صحة هيكل المهارة وبياناتها الوصفية.
### الإنشاء
```shell Terminal
crewai skill create my-skill
```
يُنشئ هذا الأمر المجلد (داخل `./skills/` في مشروع الطاقم) مع قالب `SKILL.md`، بالإضافة إلى مجلدات فارغة `scripts/` و `references/` و `assets/`. عدّل `SKILL.md` لتعريف التعليمات.
### النشر
نفّذ الأمر من داخل مجلد المهارة (حيث يوجد `SKILL.md`):
```shell Terminal
cd skills/my-skill
crewai skill publish
```
يقرأ النشر الحقول `name` و `description` و `metadata.version` من البيانات الوصفية في مقدمة `SKILL.md` ويدفع المهارة إلى سجل CrewAI. **المهارات المنشورة تكون دائمًا مقيّدة بنطاق مؤسستك** — مثل الأدوات، لا يستطيع رؤيتها وتثبيتها إلا أعضاء المؤسسة الناشرة؛ ولا توجد رؤية عامة. أعلام مفيدة:
| العلم | التأثير |
| :--- | :--- |
| `--org <slug>` | النشر تحت مؤسسة محددة (يتجاوز الإعدادات). |
| `--force` | تخطي التحقق من حالة git (تغييرات غير مُثبتة، إلخ). |
### التثبيت
ثبّت مهارة منشورة عبر مرجعها `@org/name`:
```shell Terminal
crewai skill install @acme/code-review
```
داخل مشروع الطاقم تُثبَّت المهارة في `./skills/{name}/`؛ وخارج المشروع تذهب إلى ذاكرة التخزين المؤقتة المشتركة في `~/.crewai/skills/{org}/{name}/`.
يمكن للوكلاء أيضًا الإشارة إلى مهارات السجل مباشرة — يتم حلّها من ذاكرة التخزين المؤقتة المحلية (أو من مجلد `skills/` في المشروع) وقت التشغيل:
```python
agent = Agent(
role="Senior Code Reviewer",
goal="Review pull requests for quality and security issues",
backstory="Staff engineer with expertise in secure coding practices.",
skills=["@acme/code-review"], # registry ref, resolved locally
)
```
### عرض القائمة
```shell Terminal
crewai skill list
```
يعرض المهارات المثبّتة من مجلد المشروع `./skills/` ومن ذاكرة التخزين المؤقتة العامة معًا، مع إصداراتها ومساراتها.
---
## المهارات على مستوى الطاقم
يمكن تعيين المهارات على الطاقم لتُطبّق على **جميع الوكلاء**:
```python
from crewai import Crew
crew = Crew(
agents=[researcher, writer, reviewer],
tasks=[research_task, write_task, review_task],
skills=["./skills"], # جميع الوكلاء يحصلون على هذه المهارات
)
```
المهارات على مستوى الوكيل لها الأولوية — إذا تم اكتشاف نفس المهارة في كلا المستويين، يتم استخدام نسخة الوكيل.
---
## تنسيق SKILL.md
```markdown
---
name: my-skill
description: وصف قصير لما تفعله هذه المهارة ومتى تُستخدم.
license: Apache-2.0 # اختياري
compatibility: crewai>=0.1.0 # اختياري
metadata: # اختياري
author: your-name
version: "1.0"
allowed-tools: web-search file-read # اختياري، تجريبي
---
التعليمات للوكيل تُكتب هنا. يتم حقن محتوى Markdown هذا
في إرشادات الوكيل عند تفعيل المهارة.
```
### حقول البيانات الوصفية
| الحقل | مطلوب | الوصف |
| :-------------- | :------- | :----------------------------------------------------------------------- |
| `name` | نعم | 1-64 حرف. أحرف صغيرة أبجدية رقمية وشرطات. يجب أن يطابق اسم المجلد. |
| `description` | نعم | 1-1024 حرف. يصف ما تفعله المهارة ومتى تُستخدم. |
| `license` | لا | اسم الترخيص أو مرجع لملف ترخيص مضمّن. |
| `compatibility` | لا | حد أقصى 500 حرف. متطلبات البيئة (منتجات، حزم، شبكة). |
| `metadata` | لا | تعيين مفتاح-قيمة نصي عشوائي. |
| `allowed-tools` | لا | قائمة أدوات معتمدة مسبقًا مفصولة بمسافات. تجريبي. |
---
## هيكل المجلد
```
my-skill/
├── SKILL.md # مطلوب — البيانات الوصفية + التعليمات
├── scripts/ # اختياري — سكربتات قابلة للتنفيذ
├── references/ # اختياري — مستندات مرجعية
└── assets/ # اختياري — ملفات ثابتة (إعدادات، بيانات)
```
يجب أن يتطابق اسم المجلد مع حقل `name` في `SKILL.md`. مجلدات `scripts/` و `references/` و `assets/` متاحة في مسار المهارة `path` للوكلاء الذين يحتاجون للإشارة إلى الملفات مباشرة.
---
## المهارات المحمّلة مسبقًا
للمزيد من التحكم، يمكنك اكتشاف المهارات وتفعيلها برمجيًا:
```python
from pathlib import Path
from crewai.skills import discover_skills, activate_skill
# اكتشاف جميع المهارات في مجلد
skills = discover_skills(Path("./skills"))
# تفعيلها (تحميل محتوى SKILL.md الكامل)
activated = [activate_skill(s) for s in skills]
# تمرير إلى وكيل
agent = Agent(
role="Researcher",
goal="Find relevant information",
backstory="An expert researcher.",
skills=activated,
)
```
---
## كيف يتم تحميل المهارات
تستخدم المهارات **الكشف التدريجي** — تحمّل فقط ما هو مطلوب في كل مرحلة:
| المرحلة | ما يتم تحميله | متى |
| :--------- | :------------------------------------ | :------------------ |
| الاكتشاف | الاسم، الوصف، حقول البيانات الوصفية | `discover_skills()` |
| التفعيل | نص محتوى SKILL.md الكامل | `activate_skill()` |
أثناء التنفيذ العادي للوكيل (تمرير مسارات المجلدات عبر `skills=["./skills"]`)، يتم اكتشاف المهارات وتفعيلها تلقائيًا. التحميل التدريجي مهم فقط عند استخدام الواجهة البرمجية.
---
## المهارات مقابل المعرفة
كلا المهارات والمعرفة تُعدّل إرشادات الوكيل، لكنهما يخدمان أغراضًا مختلفة:
| الجانب | المهارات | المعرفة |
| :--- | :--- | :--- |
| **ما توفره** | تعليمات، إجراءات، إرشادات | حقائق، بيانات، معلومات |
| **كيف تُخزّن** | ملفات Markdown (SKILL.md) | مُضمّنة في مخزن متجهي (ChromaDB) |
| **كيف تُسترجع** | يتم حقن المحتوى الكامل في الإرشادات | البحث الدلالي يجد الأجزاء ذات الصلة |
| **الأفضل لـ** | المنهجيات، قوائم التحقق، أدلة الأسلوب | مستندات الشركة، معلومات المنتج، بيانات مرجعية |
| **يُعيّن عبر** | `skills=["./skills"]` | `knowledge_sources=[source]` |
**القاعدة العامة:** إذا كان الوكيل يحتاج لاتباع *عملية*، استخدم مهارة. إذا كان يحتاج للرجوع إلى *بيانات*، استخدم المعرفة.
---
## الأسئلة الشائعة
<AccordionGroup>
<Accordion title="هل أحتاج لتعيين المهارات والأدوات معًا؟">
يعتمد على حالة الاستخدام. المهارات والأدوات **مستقلتان** — يمكنك استخدام أيّ منهما أو كليهما أو لا شيء.
- **مهارات فقط**: عندما يحتاج الوكيل خبرة لكن لا يحتاج إجراءات خارجية (مثال: الكتابة بإرشادات أسلوبية)
- **أدوات فقط**: عندما يحتاج الوكيل إجراءات لكن لا يحتاج منهجية خاصة (مثال: بحث بسيط على الويب)
- **كليهما**: عندما يحتاج الوكيل خبرة وإجراءات (مثال: تدقيق أمني بقوائم تحقق محددة وقدرة على فحص الكود)
</Accordion>
<Accordion title="هل توفر المهارات أدوات تلقائيًا؟">
**لا.** حقل `allowed-tools` في SKILL.md هو بيانات وصفية تجريبية فقط — لا يُنشئ أو يحقن أي أدوات. يجب عليك دائمًا تعيين الأدوات بشكل منفصل عبر `tools=[]` أو `mcps=[]` أو `apps=[]`.
</Accordion>
<Accordion title="ماذا يحدث إذا عيّنت نفس المهارة على كل من الوكيل والطاقم؟">
المهارة على مستوى الوكيل لها الأولوية. يتم إزالة التكرار حسب الاسم — مهارات الوكيل تُعالج أولاً، لذا إذا ظهر نفس اسم المهارة في كلا المستويين، تُستخدم نسخة الوكيل.
</Accordion>
<Accordion title="ما الحجم الأقصى لمحتوى SKILL.md؟">
هناك تحذير ناعم عند 50,000 حرف، لكن بدون حد صارم. حافظ على تركيز المهارات وإيجازها للحصول على أفضل النتائج — الحقن الكبيرة في الإرشادات قد تُشتت انتباه الوكيل.
</Accordion>
</AccordionGroup>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
---
title: الاختبار
description: تعرّف على كيفية اختبار طاقم CrewAI وتقييم أدائه.
icon: vial
mode: "wide"
---
## نظرة عامة
يُعد الاختبار جزءًا حيويًا من عملية التطوير، ومن الضروري التأكد من أن طاقمك يعمل كما هو متوقع. مع CrewAI، يمكنك اختبار طاقمك وتقييم أدائه بسهولة باستخدام إمكانيات الاختبار المدمجة.
### استخدام ميزة الاختبار
أضفنا أمر CLI `crewai test` لتسهيل اختبار طاقمك. سيقوم هذا الأمر بتشغيل طاقمك لعدد محدد من التكرارات وتوفير مقاييس أداء مفصلة. المعاملات هي `n_iterations` و `model`، وهي اختيارية وتكون قيمها الافتراضية 2 و `gpt-4o-mini` على التوالي. حاليًا، المزود الوحيد المتاح هو OpenAI.
```bash
crewai test
```
إذا أردت تشغيل المزيد من التكرارات أو استخدام نموذج مختلف، يمكنك تحديد المعاملات هكذا:
```bash
crewai test --n-iterations 5 --model gpt-4o
```
أو باستخدام الصيغة المختصرة:
```bash
crewai test -n 5 -m gpt-4o
```
<Note>
العلم القديم `--n_iterations` لا يزال يعمل، لكنه مهمل ومخفي من `--help`.
استخدم `--n-iterations` (أو `-n`) بدلاً من ذلك.
</Note>
عند تشغيل أمر `crewai test`، سيتم تنفيذ الطاقم للعدد المحدد من التكرارات، وستُعرض مقاييس الأداء في نهاية التشغيل.
سيظهر جدول الدرجات في النهاية لعرض أداء الطاقم من حيث المقاييس التالية:
<center>**درجات المهام (1-10 الأعلى أفضل)**</center>
| المهام/الطاقم/الوكلاء | التشغيل 1 | التشغيل 2 | المجموع المتوسط | الوكلاء | معلومات إضافية |
|:------------------|:-----:|:-----:|:----------:|:------------------------------:|:---------------------------------|
| المهمة 1 | 9.0 | 9.5 | **9.2** | Professional Insights | |
| | | | | Researcher | |
| المهمة 2 | 9.0 | 10.0 | **9.5** | Company Profile Investigator | |
| المهمة 3 | 9.0 | 9.0 | **9.0** | Automation Insights | |
| | | | | Specialist | |
| المهمة 4 | 9.0 | 9.0 | **9.0** | Final Report Compiler | Automation Insights Specialist |
| الطاقم | 9.00 | 9.38 | **9.2** | | |
| زمن التنفيذ (ثانية) | 126 | 145 | **135** | | |
يوضح المثال أعلاه نتائج الاختبار لتشغيلين للطاقم مع مهمتين، مع الدرجة الإجمالية المتوسطة لكل مهمة والطاقم ككل.

View File

@@ -0,0 +1,290 @@
---
title: الأدوات
description: فهم واستخدام الأدوات ضمن إطار عمل CrewAI لتعاون الوكلاء وتنفيذ المهام.
icon: screwdriver-wrench
mode: "wide"
---
## نظرة عامة
تُمكّن أدوات CrewAI الوكلاء بقدرات تتراوح من البحث على الويب وتحليل البيانات إلى التعاون وتفويض المهام بين الزملاء.
توضح هذه الوثائق كيفية إنشاء هذه الأدوات ودمجها والاستفادة منها ضمن إطار عمل CrewAI، بما في ذلك التركيز على أدوات التعاون.
<Note type="info" title="الأدوات هي أحد أنواع قدرات الوكيل الخمسة">
الأدوات تمنح الوكلاء **دوال قابلة للاستدعاء** لاتخاذ إجراءات. تعمل جنبًا إلى جنب مع [MCP](/ar/mcp/overview) (خوادم أدوات عن بُعد) و[التطبيقات](/ar/concepts/agent-capabilities) (تكاملات المنصة) و[المهارات](/ar/concepts/skills) (خبرة المجال) و[المعرفة](/ar/concepts/knowledge) (حقائق مُسترجعة). راجع نظرة عامة على [قدرات الوكيل](/ar/concepts/agent-capabilities) لفهم متى تستخدم كل نوع.
</Note>
## ما هي الأداة؟
الأداة في CrewAI هي مهارة أو وظيفة يمكن للوكلاء استخدامها لأداء إجراءات مختلفة.
يشمل ذلك أدوات من [مجموعة أدوات CrewAI](https://github.com/joaomdmoura/crewai-tools) و[أدوات LangChain](https://python.langchain.com/docs/integrations/tools)،
مما يُمكّن كل شيء من عمليات البحث البسيطة إلى التفاعلات المعقدة والعمل الجماعي الفعال بين الوكلاء.
<Note type="info" title="تحسين المؤسسات: مستودع الأدوات">
يوفر CrewAI AMP مستودع أدوات شامل مع تكاملات جاهزة لأنظمة الأعمال الشائعة وواجهات API. انشر الوكلاء مع أدوات المؤسسة في دقائق بدلاً من أيام.
يتضمن مستودع أدوات المؤسسة:
- موصلات جاهزة لأنظمة المؤسسة الشائعة
- واجهة إنشاء أدوات مخصصة
- إمكانيات التحكم في الإصدارات والمشاركة
- ميزات الأمان والامتثال
</Note>
## الخصائص الرئيسية للأدوات
- **المنفعة**: مصممة لمهام مثل البحث على الويب وتحليل البيانات وإنشاء المحتوى وتعاون الوكلاء.
- **التكامل**: تعزز قدرات الوكلاء من خلال دمج الأدوات بسلاسة في سير عملهم.
- **القابلية للتخصيص**: توفر المرونة لتطوير أدوات مخصصة أو استخدام الأدوات الموجودة، لتلبية الاحتياجات المحددة للوكلاء.
- **معالجة الأخطاء**: تتضمن آليات معالجة أخطاء قوية لضمان التشغيل السلس.
- **آلية التخزين المؤقت**: تتميز بتخزين مؤقت ذكي لتحسين الأداء وتقليل العمليات المتكررة.
- **الدعم غير المتزامن**: تتعامل مع الأدوات المتزامنة وغير المتزامنة، مما يُمكّن العمليات غير الحاجبة.
## استخدام أدوات CrewAI
لتعزيز قدرات وكلائك بأدوات CrewAI، ابدأ بتثبيت حزمة الأدوات الإضافية:
```bash
pip install 'crewai[tools]'
```
إليك مثالًا يوضح استخدامها:
```python Code
import os
from crewai import Agent, Task, Crew
# استيراد أدوات crewAI
from crewai_tools import (
DirectoryReadTool,
FileReadTool,
SerperDevTool,
WebsiteSearchTool
)
# إعداد مفاتيح API
os.environ["SERPER_API_KEY"] = "Your Key" # serper.dev API key
os.environ["OPENAI_API_KEY"] = "Your Key"
# إنشاء الأدوات
docs_tool = DirectoryReadTool(directory='./blog-posts')
file_tool = FileReadTool()
search_tool = SerperDevTool()
web_rag_tool = WebsiteSearchTool()
# إنشاء الوكلاء
researcher = Agent(
role='Market Research Analyst',
goal='Provide up-to-date market analysis of the AI industry',
backstory='An expert analyst with a keen eye for market trends.',
tools=[search_tool, web_rag_tool],
verbose=True
)
writer = Agent(
role='Content Writer',
goal='Craft engaging blog posts about the AI industry',
backstory='A skilled writer with a passion for technology.',
tools=[docs_tool, file_tool],
verbose=True
)
# تعريف المهام
research = Task(
description='Research the latest trends in the AI industry and provide a summary.',
expected_output='A summary of the top 3 trending developments in the AI industry with a unique perspective on their significance.',
agent=researcher
)
write = Task(
description='Write an engaging blog post about the AI industry, based on the research analyst\'s summary. Draw inspiration from the latest blog posts in the directory.',
expected_output='A 4-paragraph blog post formatted in markdown with engaging, informative, and accessible content, avoiding complex jargon.',
agent=writer,
output_file='blog-posts/new_post.md'
)
# تجميع طاقم مع تفعيل التخطيط
crew = Crew(
agents=[researcher, writer],
tasks=[research, write],
verbose=True,
planning=True,
)
# تنفيذ المهام
crew.kickoff()
```
## أدوات CrewAI المتاحة
- **معالجة الأخطاء**: جميع الأدوات مبنية بقدرات معالجة الأخطاء، مما يسمح للوكلاء بإدارة الاستثناءات بسلاسة ومتابعة مهامهم.
- **آلية التخزين المؤقت**: جميع الأدوات تدعم التخزين المؤقت، مما يُمكّن الوكلاء من إعادة استخدام النتائج المحصلة سابقًا بكفاءة، مما يقلل الحمل على الموارد الخارجية ويسرّع وقت التنفيذ. يمكنك أيضًا تحديد تحكم أدق في آلية التخزين المؤقت باستخدام خاصية `cache_function` على الأداة.
إليك قائمة بالأدوات المتاحة وأوصافها:
| الأداة | الوصف |
| :------------------------------- | :--------------------------------------------------------------------------------------------- |
| **ApifyActorsTool** | أداة تدمج Apify Actors مع سير عملك لمهام استخراج البيانات من الويب والأتمتة. |
| **BrowserbaseLoadTool** | أداة للتفاعل مع المتصفحات واستخراج البيانات منها. |
| **CodeDocsSearchTool** | أداة RAG محسّنة للبحث في وثائق الكود والمستندات التقنية ذات الصلة. |
| **CodeInterpreterTool** | أداة لتفسير كود Python. |
| **ComposioTool** | تُمكّن استخدام أدوات Composio. |
| **CSVSearchTool** | أداة RAG مصممة للبحث في ملفات CSV، مخصصة للتعامل مع البيانات المنظمة. |
| **DALL-E Tool** | أداة لإنشاء الصور باستخدام DALL-E API. |
| **DirectorySearchTool** | أداة RAG للبحث في المجلدات، مفيدة للتنقل في أنظمة الملفات. |
| **DOCXSearchTool** | أداة RAG للبحث في مستندات DOCX، مثالية لمعالجة ملفات Word. |
| **DirectoryReadTool** | تسهّل قراءة ومعالجة هياكل المجلدات ومحتوياتها. |
| **ExaSearchTool** | أداة مصممة لإجراء عمليات بحث شاملة عبر مصادر بيانات متنوعة. |
| **FileReadTool** | تُمكّن قراءة واستخراج البيانات من الملفات، مع دعم تنسيقات ملفات متنوعة. |
| **FirecrawlSearchTool** | أداة للبحث في صفحات الويب باستخدام Firecrawl وإرجاع النتائج. |
| **FirecrawlCrawlWebsiteTool** | أداة لزحف صفحات الويب باستخدام Firecrawl. |
| **FirecrawlScrapeWebsiteTool** | أداة لاستخراج محتوى عناوين URL لصفحات الويب باستخدام Firecrawl. |
| **GithubSearchTool** | أداة RAG للبحث في مستودعات GitHub، مفيدة لبحث الكود والوثائق. |
| **SerperDevTool** | أداة متخصصة لأغراض التطوير، مع وظائف محددة قيد التطوير. |
| **TXTSearchTool** | أداة RAG مركّزة على البحث في ملفات النص (.txt)، مناسبة للبيانات غير المنظمة. |
| **JSONSearchTool** | أداة RAG مصممة للبحث في ملفات JSON، تخدم التعامل مع البيانات المنظمة. |
| **LlamaIndexTool** | تُمكّن استخدام أدوات LlamaIndex. |
| **MDXSearchTool** | أداة RAG مخصصة للبحث في ملفات Markdown (MDX)، مفيدة للوثائق. |
| **PDFSearchTool** | أداة RAG للبحث في مستندات PDF، مثالية لمعالجة المستندات الممسوحة ضوئيًا. |
| **PGSearchTool** | أداة RAG محسّنة للبحث في قواعد بيانات PostgreSQL، مناسبة لاستعلامات قواعد البيانات. |
| **Vision Tool** | أداة لإنشاء الصور باستخدام DALL-E API. |
| **RagTool** | أداة RAG للأغراض العامة قادرة على التعامل مع مصادر وأنواع بيانات متنوعة. |
| **ScrapeElementFromWebsiteTool** | تُمكّن استخراج عناصر محددة من المواقع، مفيدة لاستخراج البيانات المستهدف. |
| **ScrapeWebsiteTool** | تسهّل استخراج المواقع بالكامل، مثالية لجمع البيانات الشامل. |
| **WebsiteSearchTool** | أداة RAG للبحث في محتوى المواقع، محسّنة لاستخراج بيانات الويب. |
| **XMLSearchTool** | أداة RAG مصممة للبحث في ملفات XML، مناسبة لتنسيقات البيانات المنظمة. |
| **YoutubeChannelSearchTool** | أداة RAG للبحث في قنوات YouTube، مفيدة لتحليل محتوى الفيديو. |
| **YoutubeVideoSearchTool** | أداة RAG للبحث في مقاطع فيديو YouTube، مثالية لاستخراج بيانات الفيديو. |
## إنشاء أدواتك الخاصة
<Tip>
يمكن للمطورين إنشاء `أدوات مخصصة` مصممة خصيصًا لاحتياجات وكلائهم أو
استخدام الخيارات الجاهزة.
</Tip>
هناك طريقتان رئيسيتان لإنشاء أداة CrewAI:
### الوراثة من `BaseTool`
```python Code
from crewai.tools import BaseTool
from pydantic import BaseModel, Field
class MyToolInput(BaseModel):
"""Input schema for MyCustomTool."""
argument: str = Field(..., description="Description of the argument.")
class MyCustomTool(BaseTool):
name: str = "Name of my tool"
description: str = "What this tool does. It's vital for effective utilization."
args_schema: Type[BaseModel] = MyToolInput
def _run(self, argument: str) -> str:
# منطق أداتك هنا
return "Tool's result"
```
## دعم الأدوات غير المتزامنة
يدعم CrewAI الأدوات غير المتزامنة، مما يتيح لك تنفيذ أدوات تجري عمليات غير حاجبة مثل طلبات الشبكة وعمليات الإدخال/الإخراج على الملفات أو عمليات async أخرى بدون حجب مسار التنفيذ الرئيسي.
### إنشاء أدوات غير متزامنة
يمكنك إنشاء أدوات غير متزامنة بطريقتين:
#### 1. استخدام مزيّن `tool` مع دوال Async
```python Code
from crewai.tools import tool
@tool("fetch_data_async")
async def fetch_data_async(query: str) -> str:
"""Asynchronously fetch data based on the query."""
# محاكاة عملية غير متزامنة
await asyncio.sleep(1)
return f"Data retrieved for {query}"
```
#### 2. تنفيذ طرق Async في فئات الأدوات المخصصة
```python Code
from crewai.tools import BaseTool
class AsyncCustomTool(BaseTool):
name: str = "async_custom_tool"
description: str = "An asynchronous custom tool"
async def _run(self, query: str = "") -> str:
"""Asynchronously run the tool"""
# تنفيذك غير المتزامن هنا
await asyncio.sleep(1)
return f"Processed {query} asynchronously"
```
### استخدام الأدوات غير المتزامنة
تعمل الأدوات غير المتزامنة بسلاسة في كل من سير عمل الطاقم القياسي وسير عمل التدفق:
```python Code
# في طاقم قياسي
agent = Agent(role="researcher", tools=[async_custom_tool])
# في تدفق
class MyFlow(Flow):
@start()
async def begin(self):
crew = Crew(agents=[agent])
result = await crew.kickoff_async()
return result
```
يتعامل إطار عمل CrewAI تلقائيًا مع تنفيذ الأدوات المتزامنة وغير المتزامنة، لذا لا تحتاج للقلق بشأن كيفية استدعائها بشكل مختلف.
### استخدام مزيّن `tool`
```python Code
from crewai.tools import tool
@tool("Name of my tool")
def my_tool(question: str) -> str:
"""Clear description for what this tool is useful for, your agent will need this information to use it."""
# منطق الدالة هنا
return "Result from your custom tool"
```
### آلية التخزين المؤقت المخصصة
<Tip>
يمكن للأدوات اختياريًا تنفيذ `cache_function` لضبط سلوك
التخزين المؤقت. تحدد هذه الدالة متى يتم تخزين النتائج مؤقتًا بناءً على شروط
محددة، مما يوفر تحكمًا دقيقًا في منطق التخزين المؤقت.
</Tip>
```python Code
from crewai.tools import tool
@tool
def multiplication_tool(first_number: int, second_number: int) -> str:
"""Useful for when you need to multiply two numbers together."""
return first_number * second_number
def cache_func(args, result):
# في هذه الحالة، نخزّن النتيجة مؤقتًا فقط إذا كانت من مضاعفات 2
cache = result % 2 == 0
return cache
multiplication_tool.cache_function = cache_func
writer1 = Agent(
role="Writer",
goal="You write lessons of math for kids.",
backstory="You're an expert in writing and you love to teach kids but you know nothing of math.",
tools=[multiplication_tool],
allow_delegation=False,
)
#...
```
## الخلاصة
الأدوات محورية في توسيع قدرات وكلاء CrewAI، مما يمكّنهم من تنفيذ مجموعة واسعة من المهام والتعاون بفعالية.
عند بناء حلول مع CrewAI، استفد من كل من الأدوات المخصصة والموجودة لتمكين وكلائك وتعزيز نظام الذكاء الاصطناعي البيئي. فكّر في استخدام معالجة الأخطاء وآليات التخزين المؤقت ومرونة معاملات الأدوات لتحسين أداء وقدرات وكلائك.

View File

@@ -0,0 +1,197 @@
---
title: التدريب
description: تعرّف على كيفية تدريب وكلاء CrewAI من خلال تقديم ملاحظات مبكرة والحصول على نتائج متسقة.
icon: dumbbell
mode: "wide"
---
## نظرة عامة
تتيح لك ميزة التدريب في CrewAI تدريب وكلاء الذكاء الاصطناعي باستخدام واجهة سطر الأوامر (CLI).
بتشغيل الأمر `crewai train -n <n_iterations>`، يمكنك تحديد عدد التكرارات لعملية التدريب.
أثناء التدريب، يستخدم CrewAI تقنيات لتحسين أداء وكلائك مع التغذية الراجعة البشرية.
يساعد هذا الوكلاء على تحسين فهمهم واتخاذ القرارات وحل المشكلات.
### تدريب طاقمك باستخدام CLI
لاستخدام ميزة التدريب، اتبع الخطوات التالية:
1. افتح الطرفية أو موجه الأوامر.
2. انتقل إلى المجلد حيث يقع مشروع CrewAI.
3. شغّل الأمر التالي:
```shell
crewai train -n <n_iterations> -f <filename.pkl>
```
<Tip>
استبدل `<n_iterations>` بعدد تكرارات التدريب المرغوب و`<filename>` باسم الملف المناسب المنتهي بـ `.pkl`.
</Tip>
<Note>
إذا حذفت `-f`، فإن المخرجات تُحفظ افتراضيًا في `trained_agents_data.pkl` في مجلد العمل الحالي. يمكنك تمرير مسار مطلق للتحكم في مكان كتابة الملف.
</Note>
### تدريب طاقمك برمجيًا
لتدريب طاقمك برمجيًا، استخدم الخطوات التالية:
1. حدد عدد التكرارات للتدريب.
2. حدد معاملات الإدخال لعملية التدريب.
3. نفّذ أمر التدريب داخل كتلة try-except للتعامل مع الأخطاء المحتملة.
```python Code
n_iterations = 2
inputs = {"topic": "CrewAI Training"}
filename = "your_model.pkl"
try:
YourCrewName_Crew().crew().train(
n_iterations=n_iterations,
inputs=inputs,
filename=filename
)
except Exception as e:
raise Exception(f"An error occurred while training the crew: {e}")
```
## كيف تُستخدم بيانات التدريب من قبل الوكلاء
يستخدم CrewAI مخرجات التدريب بطريقتين: أثناء التدريب لدمج ملاحظاتك البشرية، وبعد التدريب لتوجيه الوكلاء باقتراحات موحدة.
### تدفق بيانات التدريب
```mermaid
flowchart TD
A["Start training<br/>CLI: crewai train -n -f<br/>or Python: crew.train(...)"] --> B["Setup training mode<br/>- task.human_input = true<br/>- disable delegation<br/>- init training_data.pkl + trained file"]
subgraph "Iterations"
direction LR
C["Iteration i<br/>initial_output"] --> D["User human_feedback"]
D --> E["improved_output"]
E --> F["Append to training_data.pkl<br/>by agent_id and iteration"]
end
B --> C
F --> G{"More iterations?"}
G -- "Yes" --> C
G -- "No" --> H["Evaluate per agent<br/>aggregate iterations"]
H --> I["Consolidate<br/>suggestions[] + quality + final_summary"]
I --> J["Save by agent role to trained file<br/>(default: trained_agents_data.pkl)"]
J --> K["Normal (non-training) runs"]
K --> L["Auto-load suggestions<br/>from trained_agents_data.pkl"]
L --> M["Append to prompt<br/>for consistent improvements"]
```
### أثناء تشغيلات التدريب
- في كل تكرار، يسجل النظام لكل وكيل:
- `initial_output`: الإجابة الأولى للوكيل
- `human_feedback`: ملاحظاتك المضمّنة عند الطلب
- `improved_output`: إجابة المتابعة للوكيل بعد الملاحظات
- تُخزن هذه البيانات في ملف عمل باسم `training_data.pkl` مفهرس بمعرّف الوكيل الداخلي والتكرار.
- أثناء نشاط التدريب، يُلحق الوكيل تلقائيًا ملاحظاتك البشرية السابقة بأمره لتطبيق تلك التعليمات في المحاولات اللاحقة ضمن جلسة التدريب.
التدريب تفاعلي: تُعيّن المهام `human_input = true`، لذا سيتوقف التشغيل في بيئة غير تفاعلية بانتظار مدخلات المستخدم.
### بعد اكتمال التدريب
- عند انتهاء `train(...)`، يقيّم CrewAI بيانات التدريب المجمعة لكل وكيل وينتج نتيجة موحدة تحتوي على:
- `suggestions`: تعليمات واضحة وقابلة للتنفيذ مستخلصة من ملاحظاتك والفرق بين المخرجات الأولية/المحسنة
- `quality`: درجة من 0-10 تعكس التحسن
- `final_summary`: مجموعة خطوات عمل تفصيلية للمهام المستقبلية
- تُحفظ هذه النتائج الموحدة في اسم الملف الذي تمرره إلى `train(...)` (الافتراضي عبر CLI هو `trained_agents_data.pkl`). تُفهرس الإدخالات بدور الوكيل `role` لتطبيقها عبر الجلسات.
- أثناء التنفيذ العادي (غير التدريب)، يحمّل كل وكيل تلقائيًا `suggestions` الموحدة ويلحقها بأمر المهمة كتعليمات إلزامية. يمنحك هذا تحسينات متسقة بدون تغيير تعريفات الوكلاء.
### ملخص الملفات
- `training_data.pkl` (مؤقت، لكل جلسة):
- الهيكل: `agent_id -> { iteration_number: { initial_output, human_feedback, improved_output } }`
- الغرض: التقاط البيانات الخام والملاحظات البشرية أثناء التدريب
- الموقع: يُحفظ في مجلد العمل الحالي (CWD)
- `trained_agents_data.pkl` (أو اسم ملفك المخصص):
- الهيكل: `agent_role -> { suggestions: string[], quality: number, final_summary: string }`
- الغرض: استمرار التوجيه الموحد للتشغيلات المستقبلية
- الموقع: يُكتب في CWD افتراضيًا؛ استخدم `-f` لتعيين مسار مخصص (بما في ذلك المطلق)
## اعتبارات نماذج اللغة الصغيرة
<Warning>
عند استخدام نماذج لغة أصغر (≤7 مليار معامل) لتقييم بيانات التدريب، كن على علم أنها قد تواجه تحديات في إنتاج مخرجات منظمة واتباع التعليمات المعقدة.
</Warning>
### قيود النماذج الصغيرة في تقييم التدريب
<CardGroup cols={2}>
<Card title="دقة مخرجات JSON" icon="triangle-exclamation">
غالبًا ما تواجه النماذج الأصغر صعوبة في إنتاج استجابات JSON صالحة مطلوبة لتقييمات التدريب المنظمة، مما يؤدي إلى أخطاء تحليل وبيانات غير مكتملة.
</Card>
<Card title="جودة التقييم" icon="chart-line">
قد توفر النماذج تحت 7 مليار معامل تقييمات أقل دقة مع عمق استدلال محدود مقارنة بالنماذج الأكبر.
</Card>
<Card title="اتباع التعليمات" icon="list-check">
قد لا تُتبع معايير تقييم التدريب المعقدة بالكامل أو تُراعى من قبل النماذج الأصغر.
</Card>
<Card title="الاتساق" icon="rotate">
قد تفتقر التقييمات عبر تكرارات تدريب متعددة إلى الاتساق مع النماذج الأصغر.
</Card>
</CardGroup>
### توصيات للتدريب
<Tabs>
<Tab title="أفضل ممارسة">
لجودة تدريب مثالية وتقييمات موثوقة، نوصي بشدة باستخدام نماذج بحد أدنى 7 مليار معامل أو أكبر:
```python
from crewai import Agent, Crew, Task, LLM
# الحد الأدنى الموصى به لتقييم التدريب
llm = LLM(model="mistral/open-mistral-7b")
# خيارات أفضل لتقييم تدريب موثوق
llm = LLM(model="anthropic/claude-3-sonnet-20240229-v1:0")
llm = LLM(model="gpt-4o")
# استخدم هذا LLM مع وكلائك
agent = Agent(
role="Training Evaluator",
goal="Provide accurate training feedback",
llm=llm
)
```
<Tip>
توفر النماذج الأكثر قوة ملاحظات أعلى جودة مع استدلال أفضل، مما يؤدي إلى تكرارات تدريب أكثر فعالية.
</Tip>
</Tab>
<Tab title="استخدام النماذج الصغيرة">
إذا كان يجب عليك استخدام نماذج أصغر لتقييم التدريب، كن على علم بهذه القيود:
```python
# استخدام نموذج أصغر (توقع بعض القيود)
llm = LLM(model="huggingface/microsoft/Phi-3-mini-4k-instruct")
```
<Warning>
بينما يتضمن CrewAI تحسينات للنماذج الصغيرة، توقع نتائج تقييم أقل موثوقية ودقة قد تتطلب تدخلاً بشريًا أكبر أثناء التدريب.
</Warning>
</Tab>
</Tabs>
### نقاط مهمة يجب ملاحظتها
- **متطلب العدد الصحيح الموجب:** تأكد من أن عدد التكرارات (`n_iterations`) هو عدد صحيح موجب. سيرمي الكود `ValueError` إذا لم يتحقق هذا الشرط.
- **متطلب اسم الملف:** تأكد من أن اسم الملف ينتهي بـ `.pkl`. سيرمي الكود `ValueError` إذا لم يتحقق هذا الشرط.
- **معالجة الأخطاء:** يتعامل الكود مع أخطاء العمليات الفرعية والاستثناءات غير المتوقعة، ويوفر رسائل خطأ للمستخدم.
- يُطبق التوجيه المدرّب في وقت الأمر؛ لا يعدّل تهيئة وكيل Python/YAML.
- يحمّل الوكلاء تلقائيًا الاقتراحات المدربة من ملف باسم `trained_agents_data.pkl` الموجود في مجلد العمل الحالي. إذا درّبت إلى اسم ملف مختلف، أعد تسميته إلى `trained_agents_data.pkl` قبل التشغيل، أو اضبط المحمّل في الكود.
- يمكنك تغيير اسم ملف المخرجات عند استدعاء `crewai train` بـ `-f/--filename`. المسارات المطلقة مدعومة إذا أردت الحفظ خارج CWD.
من المهم ملاحظة أن عملية التدريب قد تستغرق بعض الوقت، اعتمادًا على تعقيد وكلائك وستتطلب أيضًا ملاحظاتك في كل تكرار.
بمجرد اكتمال التدريب، سيكون وكلاؤك مجهزين بقدرات ومعرفة محسّنة، وجاهزين لمعالجة المهام المعقدة وتقديم رؤى أكثر اتساقًا وقيمة.
تذكر تحديث وإعادة تدريب وكلائك بانتظام لضمان بقائهم على اطلاع بأحدث المعلومات والتطورات في المجال.

View File

@@ -0,0 +1,49 @@
---
title: كتب وصفات CrewAI
description: بدايات سريعة ودفاتر ملاحظات مركّزة على الميزات لتعلم الأنماط بسرعة.
icon: book
mode: "wide"
---
## بدايات سريعة وعروض توضيحية
<CardGroup cols={3}>
<Card title="التعاون" icon="people-arrows" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Collaboration/crewai_collaboration.ipynb">
تنسيق عدة Agents على مهام مشتركة. يتضمن دفتر ملاحظات بنمط تعاون شامل.
</Card>
<Card title="التخطيط" icon="timeline" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Planning/crewai_planning.ipynb">
تعليم الـ Agents التفكير في خطط متعددة المراحل قبل التنفيذ باستخدام أدوات التخطيط.
</Card>
<Card title="الاستدلال" icon="lightbulb" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Reasoning/crewai_reasoning.ipynb">
استكشاف حلقات التأمل الذاتي، ومطالبات النقد، وأنماط التفكير المنظم.
</Card>
</CardGroup>
<CardGroup cols={3}>
<Card title="حواجز الحماية المنظمة" icon="shield-check" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Guardrails/task_guardrails.ipynb">
تطبيق حواجز حماية على مستوى المهام مع إعادة المحاولة ودوال التحقق والبدائل الآمنة.
</Card>
<Card title="بحث وتأريض Gemini" icon="magnifying-glass" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Custom%20LLM/gemini_search_grounding_crewai.ipynb">
ربط CrewAI بـ Gemini مع تأريض البحث للحصول على مخرجات واقعية غنية بالاستشهادات.
</Card>
<Card title="ملخصات فيديو Gemini" icon="video" href="https://github.com/crewAIInc/crewAI-quickstarts/blob/main/Custom%20LLM/summarize_video_gemini_crewai.ipynb">
إنشاء ملخصات فيديو باستخدام نموذج Gemini متعدد الوسائط وتنسيق CrewAI.
</Card>
</CardGroup>
<CardGroup cols={2}>
<Card title="تصفح البدايات السريعة" icon="bolt" href="https://github.com/crewAIInc/crewAI-quickstarts">
عرض جميع دفاتر الملاحظات والعروض التوضيحية التي تستعرض إمكانيات CrewAI المحددة.
</Card>
<Card title="اطلب كتاب وصفات" icon="message-plus" href="https://community.crewai.com">
هل يفتقد نمط معين؟ أرسل طلبًا في منتدى المجتمع وسنوسّع المكتبة.
</Card>
</CardGroup>
<Tip>
استخدم كتب الوصفات لتعلم نمط بسرعة، ثم انتقل إلى الأمثلة الكاملة للتطبيقات الجاهزة للإنتاج.
</Tip>

View File

@@ -0,0 +1,86 @@
---
title: أمثلة CrewAI
description: استكشف أمثلة منسّقة مرتبة حسب Crews وFlows والتكاملات ودفاتر الملاحظات.
icon: rocket-launch
mode: "wide"
---
## Crews
<CardGroup cols={3}>
<Card title="استراتيجية التسويق" icon="bullhorn" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/marketing_strategy">
تخطيط حملات تسويقية متعددة الـ Agents.
</Card>
<Card title="رحلة مفاجئة" icon="plane" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/surprise_trip">
تخطيط رحلات مفاجئة مخصصة.
</Card>
<Card title="مطابقة الملف الشخصي بالوظائف" icon="id-card" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/match_profile_to_positions">
مطابقة السيرة الذاتية بالوظائف باستخدام البحث المتجهي.
</Card>
<Card title="نشر وظيفة" icon="newspaper" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/job-posting">
إنشاء أوصاف وظيفية آلية.
</Card>
<Card title="فريق بناء الألعاب" icon="gamepad" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/game-builder-crew">
فريق متعدد الـ Agents يصمم ويبني ألعاب Python.
</Card>
<Card title="التوظيف" icon="user-group" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews/recruitment">
استقطاب المرشحين وتقييمهم.
</Card>
<Card title="تصفح جميع الـ Crews" icon="users" href="https://github.com/crewAIInc/crewAI-examples/tree/main/crews">
عرض القائمة الكاملة لأمثلة الـ Crews.
</Card>
</CardGroup>
## Flows
<CardGroup cols={3}>
<Card title="Flow إنشاء المحتوى" icon="pen" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/content_creator_flow">
إنشاء محتوى متعدد الـ Crews مع التوجيه.
</Card>
<Card title="الرد التلقائي على البريد الإلكتروني" icon="envelope" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/email_auto_responder_flow">
مراقبة البريد الإلكتروني والرد الآلي.
</Card>
<Card title="Flow تقييم العملاء المحتملين" icon="chart-line" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/lead_score_flow">
تأهيل العملاء المحتملين مع تدخل بشري.
</Card>
<Card title="Flow مساعد الاجتماعات" icon="calendar" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/meeting_assistant_flow">
معالجة الملاحظات مع التكاملات.
</Card>
<Card title="حلقة التقييم الذاتي" icon="rotate" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/self_evaluation_loop_flow">
سير عمل التحسين الذاتي التكراري.
</Card>
<Card title="كتابة كتاب (Flows)" icon="book" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows/write_a_book_with_flows">
إنشاء الفصول بالتوازي.
</Card>
<Card title="تصفح جميع الـ Flows" icon="diagram-project" href="https://github.com/crewAIInc/crewAI-examples/tree/main/flows">
عرض القائمة الكاملة لأمثلة الـ Flows.
</Card>
</CardGroup>
## التكاملات
<CardGroup cols={3}>
<Card title="CrewAI ↔ LangGraph" icon="link" href="https://github.com/crewAIInc/crewAI-examples/tree/main/integrations/crewai-langgraph">
التكامل مع إطار عمل LangGraph.
</Card>
<Card title="Azure OpenAI" icon="cloud" href="https://github.com/crewAIInc/crewAI-examples/tree/main/integrations/azure_model">
استخدام CrewAI مع Azure OpenAI.
</Card>
<Card title="نماذج NVIDIA" icon="microchip" href="https://github.com/crewAIInc/crewAI-examples/tree/main/integrations/nvidia_models">
تكاملات منظومة NVIDIA.
</Card>
<Card title="تصفح التكاملات" icon="puzzle-piece" href="https://github.com/crewAIInc/crewAI-examples/tree/main/integrations">
عرض جميع أمثلة التكاملات.
</Card>
</CardGroup>
## دفاتر الملاحظات
<CardGroup cols={2}>
<Card title="Simple QA Crew + Flow" icon="book" href="https://github.com/crewAIInc/crewAI-examples/tree/main/Notebooks/Simple%20QA%20Crew%20%2B%20Flow">
Simple QA Crew + Flow.
</Card>
<Card title="جميع دفاتر الملاحظات" icon="book" href="https://github.com/crewAIInc/crewAI-examples/tree/main/Notebooks">
أمثلة تفاعلية للتعلم والتجريب.
</Card>
</CardGroup>

View File

@@ -0,0 +1,331 @@
---
title: تخصيص المطالبات
description: تعمّق في تخصيص المطالبات على المستوى المنخفض في CrewAI، مما يتيح حالات استخدام مخصصة ومعقدة لنماذج ولغات مختلفة.
icon: message-pen
mode: "wide"
---
## لماذا نخصص المطالبات؟
على الرغم من أن مطالبات CrewAI الافتراضية تعمل بشكل جيد في كثير من السيناريوهات، إلا أن التخصيص على المستوى المنخفض يفتح الباب أمام سلوك أكثر مرونة وقوة للـ Agent. إليك لماذا قد ترغب في الاستفادة من هذا التحكم العميق:
1. **التحسين لنماذج LLM محددة** تزدهر النماذج المختلفة (مثل GPT-4 وClaude وLlama) مع تنسيقات مطالبات مصممة لبنيتها الفريدة.
2. **تغيير اللغة** بناء Agents تعمل حصريًا بلغات غير الإنجليزية مع التعامل مع الفروق الدقيقة بدقة.
3. **التخصص في مجالات معقدة** تكييف المطالبات لصناعات متخصصة للغاية مثل الرعاية الصحية والمالية والقانون.
4. **ضبط النبرة والأسلوب** جعل الـ Agents أكثر رسمية أو عفوية أو إبداعية أو تحليلية.
5. **دعم حالات استخدام مخصصة للغاية** استخدام هياكل وتنسيقات مطالبات متقدمة لتلبية متطلبات معقدة خاصة بالمشروع.
يستكشف هذا الدليل كيفية الوصول إلى مطالبات CrewAI على مستوى أعمق، مما يمنحك تحكمًا دقيقًا في كيفية تفكير الـ Agents وتفاعلها.
## فهم نظام المطالبات في CrewAI
تحت الغطاء، يستخدم CrewAI نظام مطالبات معياري يمكنك تخصيصه على نطاق واسع:
- **قوالب الـ Agent** تحكم في نهج كل Agent تجاه دوره المعيّن.
- **شرائح المطالبات** تتحكم في السلوكيات المتخصصة مثل المهام واستخدام الأدوات وهيكل المخرجات.
- **معالجة الأخطاء** توجيه كيفية استجابة الـ Agents للإخفاقات والاستثناءات وحالات انتهاء المهلة.
- **مطالبات خاصة بالأدوات** تعريف تعليمات مفصلة لكيفية استدعاء الأدوات أو استخدامها.
اطلع على [قوالب المطالبات الأصلية في مستودع CrewAI](https://github.com/crewAIInc/crewAI/blob/main/src/crewai/translations/en.json) لمعرفة كيفية تنظيم هذه العناصر. من هناك، يمكنك تجاوزها أو تكييفها حسب الحاجة لفتح سلوكيات متقدمة.
## فهم تعليمات النظام الافتراضية
<Warning>
**مشكلة شفافية الإنتاج**: يحقن CrewAI تلقائيًا تعليمات افتراضية في مطالباتك قد لا تكون على علم بها. يشرح هذا القسم ما يحدث تحت الغطاء وكيفية الحصول على تحكم كامل.
</Warning>
عندما تعرّف Agent بـ `role` و`goal` و`backstory`، يضيف CrewAI تلقائيًا تعليمات نظام إضافية تتحكم في التنسيق والسلوك. فهم هذه الحقن الافتراضية أمر بالغ الأهمية لأنظمة الإنتاج التي تحتاج شفافية كاملة في المطالبات.
### ما يحقنه CrewAI تلقائيًا
بناءً على تهيئة الـ Agent، يضيف CrewAI تعليمات افتراضية مختلفة:
#### للـ Agents بدون أدوات
```text
"I MUST use these formats, my job depends on it!"
```
#### للـ Agents مع أدوات
```text
"IMPORTANT: Use the following format in your response:
Thought: you should always think about what to do
Action: the action to take, only one name of [tool_names]
Action Input: the input to the action, just a simple JSON object...
```
#### للمخرجات المنظمة (JSON/Pydantic)
```text
"Ensure your final answer contains only the content in the following format: {output_format}
Ensure the final output does not include any code block markers like ```json or ```python."
```
### عرض مطالبة النظام الكاملة
لمعرفة المطالبة المرسلة بالضبط إلى LLM، يمكنك فحص المطالبة المولّدة:
```python
from crewai import Agent, Crew, Task
from crewai.utilities.prompts import Prompts
# Create your agent
agent = Agent(
role="Data Analyst",
goal="Analyze data and provide insights",
backstory="You are an expert data analyst with 10 years of experience.",
verbose=True
)
# Create a sample task
task = Task(
description="Analyze the sales data and identify trends",
expected_output="A detailed analysis with key insights and trends",
agent=agent
)
# Create the prompt generator
prompt_generator = Prompts(
agent=agent,
has_tools=len(agent.tools) > 0,
use_system_prompt=agent.use_system_prompt
)
# Generate and inspect the actual prompt
generated_prompt = prompt_generator.task_execution()
# Print the complete system prompt that will be sent to the LLM
if "system" in generated_prompt:
print("=== SYSTEM PROMPT ===")
print(generated_prompt["system"])
print("\n=== USER PROMPT ===")
print(generated_prompt["user"])
else:
print("=== COMPLETE PROMPT ===")
print(generated_prompt["prompt"])
# You can also see how the task description gets formatted
print("\n=== TASK CONTEXT ===")
print(f"Task Description: {task.description}")
print(f"Expected Output: {task.expected_output}")
```
### تجاوز التعليمات الافتراضية
لديك عدة خيارات للحصول على تحكم كامل في المطالبات:
#### الخيار 1: القوالب المخصصة (مُوصى به)
```python
from crewai import Agent
# Define your own system template without default instructions
custom_system_template = """You are {role}. {backstory}
Your goal is: {goal}
Respond naturally and conversationally. Focus on providing helpful, accurate information."""
custom_prompt_template = """Task: {input}
Please complete this task thoughtfully."""
agent = Agent(
role="Research Assistant",
goal="Help users find accurate information",
backstory="You are a helpful research assistant.",
system_template=custom_system_template,
prompt_template=custom_prompt_template,
use_system_prompt=True # Use separate system/user messages
)
```
#### الخيار 2: ملف مطالبات مخصص
أنشئ ملف `custom_prompts.json` لتجاوز شرائح مطالبات محددة:
```json
{
"slices": {
"no_tools": "\nProvide your best answer in a natural, conversational way.",
"tools": "\nYou have access to these tools: {tools}\n\nUse them when helpful, but respond naturally.",
"formatted_task_instructions": "Format your response as: {output_format}"
}
}
```
ثم استخدمه في Crew:
```python
crew = Crew(
agents=[agent],
tasks=[task],
prompt_file="custom_prompts.json",
verbose=True
)
```
<Note>
يُحتفظ بـ `agent.i18n` للتوافق مع الإصدارات السابقة فقط، وقد تم إهماله. لتخصيص المطالبات أثناء التشغيل، مرّر `prompt_file` إلى `Crew`. وللوصول البرمجي المباشر إلى شرائح المطالبات، استخدم أداة i18n مباشرة:
</Note>
```python
from crewai.utilities.i18n import get_i18n
i18n = get_i18n("custom_prompts.json")
format_slice = i18n.slice("format")
tool_prompt = i18n.tools("ask_question")
```
#### الخيار 3: تعطيل مطالبات النظام لنماذج o1
```python
agent = Agent(
role="Analyst",
goal="Analyze data",
backstory="Expert analyst",
use_system_prompt=False # Disables system prompt separation
)
```
### التصحيح باستخدام أدوات المراقبة
لشفافية الإنتاج، استخدم منصات المراقبة لمتابعة جميع المطالبات وتفاعلات LLM. يتيح لك ذلك رؤية المطالبات المرسلة بالضبط (بما في ذلك التعليمات الافتراضية) إلى نماذج LLM.
راجع [توثيق المراقبة](/ar/observability/overview) للحصول على أدلة تكامل مفصلة مع منصات متعددة بما في ذلك Langfuse وMLflow وWeights & Biases وحلول التسجيل المخصصة.
### أفضل الممارسات للإنتاج
1. **افحص المطالبات المولّدة دائمًا** قبل النشر في الإنتاج
2. **استخدم قوالب مخصصة** عندما تحتاج تحكمًا كاملاً في محتوى المطالبات
3. **دمج أدوات المراقبة** للمتابعة المستمرة للمطالبات (راجع [توثيق المراقبة](/ar/observability/overview))
4. **اختبر مع نماذج LLM مختلفة** حيث قد تعمل التعليمات الافتراضية بشكل مختلف عبر النماذج
5. **وثّق تخصيصات المطالبات** لشفافية الفريق
<Tip>
التعليمات الافتراضية موجودة لضمان سلوك Agent متسق، لكنها قد تتعارض مع المتطلبات الخاصة بالمجال. استخدم خيارات التخصيص أعلاه للحفاظ على تحكم كامل في سلوك Agent في أنظمة الإنتاج.
</Tip>
## أفضل الممارسات لإدارة ملفات المطالبات
عند الانخراط في تخصيص المطالبات على المستوى المنخفض، اتبع هذه الإرشادات للحفاظ على التنظيم وسهولة الصيانة:
1. **احتفظ بالملفات منفصلة** خزّن المطالبات المخصصة في ملفات JSON مخصصة خارج قاعدة الكود الرئيسية.
2. **التحكم في الإصدارات** تتبع التغييرات داخل المستودع مع ضمان توثيق واضح لتعديلات المطالبات بمرور الوقت.
3. **التنظيم حسب النموذج أو اللغة** استخدم تسميات مثل `prompts_llama.json` أو `prompts_es.json` لتحديد التهيئات المتخصصة بسرعة.
4. **توثيق التغييرات** قدم تعليقات أو حافظ على ملف يوضح غرض ونطاق تخصيصاتك.
5. **قلل التعديلات** تجاوز فقط الشرائح المحددة التي تحتاج حقًا لتعديلها مع الحفاظ على الوظائف الافتراضية لكل شيء آخر.
## أبسط طريقة لتخصيص المطالبات
إحدى الطرق المباشرة هي إنشاء ملف JSON للمطالبات التي تريد تجاوزها ثم توجيه Crew إلى ذلك الملف:
1. أنشئ ملف JSON بشرائح المطالبات المحدّثة.
2. أشر إلى ذلك الملف عبر معامل `prompt_file` في Crew.
يدمج CrewAI بعد ذلك تخصيصاتك مع الإعدادات الافتراضية، فلا تحتاج لإعادة تعريف كل مطالبة. إليك الطريقة:
بالنسبة للكود الذي يحتاج إلى قراءة شرائح المطالبات مباشرة، استخدم `crewai.utilities.i18n.get_i18n()` مع ملف المطالبات نفسه بدلًا من قراءة `agent.i18n`.
### مثال: تخصيص أساسي للمطالبات
أنشئ ملف `custom_prompts.json` بالمطالبات التي تريد تعديلها. تأكد من إدراج جميع المطالبات عالية المستوى التي يجب أن يحتويها، وليس فقط تغييراتك:
```json
{
"slices": {
"format": "When responding, follow this structure:\n\nTHOUGHTS: Your step-by-step thinking\nACTION: Any tool you're using\nRESULT: Your final answer or conclusion"
}
}
```
ثم ادمجه هكذا:
```python
from crewai import Agent, Crew, Task, Process
# Create agents and tasks as normal
researcher = Agent(
role="Research Specialist",
goal="Find information on quantum computing",
backstory="You are a quantum physics expert",
verbose=True
)
research_task = Task(
description="Research quantum computing applications",
expected_output="A summary of practical applications",
agent=researcher
)
# Create a crew with your custom prompt file
crew = Crew(
agents=[researcher],
tasks=[research_task],
prompt_file="path/to/custom_prompts.json",
verbose=True
)
# Run the crew
result = crew.kickoff()
```
بهذه التعديلات البسيطة، تحصل على تحكم منخفض المستوى في كيفية تواصل الـ Agents وحل المهام.
## التحسين لنماذج محددة
تزدهر النماذج المختلفة مع مطالبات منظمة بطرق مختلفة. إجراء تعديلات أعمق يمكن أن يعزز الأداء بشكل كبير من خلال مواءمة مطالباتك مع خصائص النموذج.
### مثال: قالب مطالبات Llama 3.3
على سبيل المثال، عند التعامل مع Llama 3.3 من Meta، قد يعكس التخصيص على المستوى الأعمق الهيكل الموصى به الموضح في:
https://www.llama.com/docs/model-cards-and-prompt-formats/llama3_1/#prompt-template
إليك مثالاً يوضح كيف يمكنك ضبط Agent للاستفادة من Llama 3.3 في الكود:
```python
from crewai import Agent, Crew, Task, Process
from crewai_tools import DirectoryReadTool, FileReadTool
# Define templates for system, user (prompt), and assistant (response) messages
system_template = """<|begin_of_text|><|start_header_id|>system<|end_header_id|>{{ .System }}<|eot_id|>"""
prompt_template = """<|start_header_id|>user<|end_header_id|>{{ .Prompt }}<|eot_id|>"""
response_template = """<|start_header_id|>assistant<|end_header_id|>{{ .Response }}<|eot_id|>"""
# Create an Agent using Llama-specific layouts
principal_engineer = Agent(
role="Principal Engineer",
goal="Oversee AI architecture and make high-level decisions",
backstory="You are the lead engineer responsible for critical AI systems",
verbose=True,
llm="groq/llama-3.3-70b-versatile", # Using the Llama 3 model
system_template=system_template,
prompt_template=prompt_template,
response_template=response_template,
tools=[DirectoryReadTool(), FileReadTool()]
)
# Define a sample task
engineering_task = Task(
description="Review AI implementation files for potential improvements",
expected_output="A summary of key findings and recommendations",
agent=principal_engineer
)
# Create a Crew for the task
llama_crew = Crew(
agents=[principal_engineer],
tasks=[engineering_task],
process=Process.sequential,
verbose=True
)
# Execute the crew
result = llama_crew.kickoff()
print(result.raw)
```
من خلال هذه التهيئة العميقة، يمكنك ممارسة تحكم شامل منخفض المستوى في سير العمل القائمة على Llama دون الحاجة إلى ملف JSON منفصل.
## الخلاصة
يفتح تخصيص المطالبات على المستوى المنخفض في CrewAI الباب أمام حالات استخدام مخصصة ومعقدة للغاية. من خلال إنشاء ملفات مطالبات منظمة (أو قوالب مضمّنة مباشرة)، يمكنك استيعاب نماذج ولغات ومجالات متخصصة متنوعة. يضمن هذا المستوى من المرونة أنك تستطيع صياغة سلوك الذكاء الاصطناعي الذي تحتاجه بالضبط، مع العلم أن CrewAI لا يزال يوفر إعدادات افتراضية موثوقة عندما لا تتجاوزها.
<Check>
لديك الآن الأساس لتخصيصات المطالبات المتقدمة في CrewAI. سواء كنت تتكيف مع هياكل خاصة بالنموذج أو قيود خاصة بالمجال، يتيح لك هذا النهج المنخفض المستوى تشكيل تفاعلات الـ Agent بطرق متخصصة للغاية.
</Check>

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