feat: deprecate CrewAgentExecutor, default Crew agents to AgentExecutor#5745
Conversation
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
2 similar comments
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR deprecates CrewAgentExecutor and makes AgentExecutor the default. It removes executor-gated reasoning, adds a sync-path guard for awaitable invoke results, lazy-exports CrewAgentExecutor, updates tests to target the new executor, and re-records VCR cassettes to reflect changed LLM responses. ChangesExecutor Deprecation & Migration
Test Updates
VCR Cassette Updates
🎯 3 (Moderate) | ⏱️ ~20 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/crewai/src/crewai/agent/core.py (2)
1041-1068:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHonor
executor_classwhen creating the executor.Line 1045 hard-codes
AgentExecutor, soexecutor_class=CrewAgentExecutorno longer has any effect even though the validator, alias map, and deprecation contract say that path still works. That silently changes behavior for existing callers and breaks the intended compatibility window.Possible fix
- self.agent_executor = AgentExecutor( - llm=self.llm, - task=task, - agent=self, - crew=self.crew, - tools=parsed_tools, - prompt=prompt, - original_tools=raw_tools, - stop_words=stop_words, - max_iter=self.max_iter, - tools_handler=self.tools_handler, - tools_names=get_tool_names(parsed_tools), - tools_description=render_text_description_and_args(parsed_tools), - step_callback=self.step_callback, - function_calling_llm=self.function_calling_llm, - respect_context_window=self.respect_context_window, - request_within_rpm_limit=rpm_limit_fn, - callbacks=[TokenCalcHandler(self._token_process)], - response_model=( - task.response_model or task.output_pydantic or task.output_json - ) - if task - else None, - ) + executor_cls = self.executor_class + executor_kwargs = { + "llm": self.llm, + "task": task, + "agent": self, + "crew": self.crew, + "tools": parsed_tools, + "prompt": prompt, + "original_tools": raw_tools, + "max_iter": self.max_iter, + "tools_handler": self.tools_handler, + "tools_names": get_tool_names(parsed_tools), + "tools_description": render_text_description_and_args(parsed_tools), + "step_callback": self.step_callback, + "function_calling_llm": self.function_calling_llm, + "respect_context_window": self.respect_context_window, + "request_within_rpm_limit": rpm_limit_fn, + "callbacks": [TokenCalcHandler(self._token_process)], + "response_model": ( + task.response_model or task.output_pydantic or task.output_json + ) + if task + else None, + } + if executor_cls is AgentExecutor: + executor_kwargs["stop_words"] = stop_words + else: + executor_kwargs["stop"] = stop_words + + self.agent_executor = executor_cls(**executor_kwargs)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/src/crewai/agent/core.py` around lines 1041 - 1068, The code currently hard-codes AgentExecutor when setting self.agent_executor; change it to use the configured executor_class (e.g., self.executor_class or executor_class) so executor_class=CrewAgentExecutor is honored: locate the assignment to self.agent_executor in create agent executor code (the block that builds AgentExecutor with llm=self.llm, task=task, agent=self, crew=self.crew, tools=parsed_tools, prompt=prompt, ...), instantiate executor_class(...) with the exact same keyword args (and the fallback to AgentExecutor if executor_class is None) so all existing parameters (tools_handler, step_callback, function_calling_llm, request_within_rpm_limit, callbacks, response_model, etc.) are forwarded unchanged.
850-864:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftFix the sync task path for
AgentExecutorunder an active event loop.With
AgentExecutornow the default, this branch can receive a coroutine frominvoke()inside async flows, but Line 853 still treats the return value as a dict. That makescrew.kickoff()from async flow methods regress at runtime; the skipped telemetry test is just hiding it.Either keep this path fully async end-to-end (
aexecute_task/kickoff_async) or explicitly handle/reject awaitables here instead of indexing into them asresult["output"].🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/src/crewai/agent/core.py` around lines 850 - 864, The sync path treats self.agent_executor.invoke(...) as a dict but invoke can return an awaitable when running under an active event loop (AgentExecutor default); update the sync branch to detect awaitables (use inspect.isawaitable on the value returned by agent_executor.invoke) and if it is awaitable, raise a clear RuntimeError instructing callers to use the async variants (e.g. AgentExecutor.aexecute_task or kickoff_async) instead of indexing into the coroutine; otherwise, cast to dict and return result["output"] as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/crewai/tests/agents/test_agent.py`:
- Around line 704-708: Tests were skipped because human-input support wasn't
migrated to the new default AgentExecutor, causing Task(human_input=True) to
break; either preserve routing of human-input tasks to CrewAgentExecutor or
finish wiring the human_input provider to the new state proxy. To fix: update
the human_input provider and any callsites (search for ask_for_human_input,
_invoke_loop, and Task(human_input=True)) to read/write the flag from the new
state proxy used by AgentExecutor (e.g., state.ask_for_human_input) and ensure
AgentExecutor._invoke_loop honours that state, or revert human-input task
handling so CrewAgentExecutor remains the codepath for human_input tasks until
the provider migration is complete. Ensure tests reference the same executor
that supports human input.
- Around line 393-396: The test reveals AgentExecutor (flow-based) is making an
extra LLM provider call on the forced-final-answer path; update the executor so
that when max_iter triggers a forced final answer it emits exactly one provider
request. In practice modify the AgentExecutor loop/dispatch logic (the code that
handles the loop termination / force-final-answer routing) to either (a)
short-circuit the routing step and directly call the final-answer generator
once, or (b) detect that the routing call would send the same
forced-final-answer prompt as the final-answer call and skip/merge the duplicate
provider invocation; locate the flow-based path inside AgentExecutor and its
loop/route-to-final-answer handling and ensure only one provider call is made
when force-final-answer is set.
In
`@lib/crewai/tests/cassettes/TestCrewMultimodalAnthropic.test_pdf_file`[anthropic-claude-sonnet-4-20250514].yaml:
- Around line 4-8: The test cassette
TestCrewMultimodalAnthropic.test_pdf_file[anthropic-claude-sonnet-4-20250514].yaml
is recording a full-PDF dump instead of the required one-sentence description;
update the prompt template used by the flow that produces "Describe the file(s)
you see..." to explicitly constrain responses to a single sentence and to
include an instruction like "return only the complete content: one sentence" and
a hard stop token, and then strengthen the test assertions (or add a validation
step in the test harness) to fail if response contains multiple paragraphs,
exceeds a short token/character limit, or records stop_reason: "max_tokens" so
the cassette cannot accept full-document outputs; apply the same prompt
fix/validation to the related cases (e.g., entries referenced by lines 52-326)
to prevent regressions.
In `@lib/crewai/tests/test_crew.py`:
- Around line 2993-2996: The test currently uses pytest.mark.skip which hides
regressions in Crew.train(); change the decorator so CI fails when behavior
regresses by replacing the skip with pytest.mark.xfail(strict=True) or modify
the test setup to force the compatibility executor path (instantiate or inject
CrewAgentExecutor or set the executor creation to return CrewAgentExecutor) so
Crew.train() still exercises CrewAgentExecutor._format_feedback_message rather
than the new AgentExecutor; update the test decorator or setup accordingly to
ensure the test runs as an expected-failure (strict) or uses CrewAgentExecutor
explicitly.
---
Outside diff comments:
In `@lib/crewai/src/crewai/agent/core.py`:
- Around line 1041-1068: The code currently hard-codes AgentExecutor when
setting self.agent_executor; change it to use the configured executor_class
(e.g., self.executor_class or executor_class) so
executor_class=CrewAgentExecutor is honored: locate the assignment to
self.agent_executor in create agent executor code (the block that builds
AgentExecutor with llm=self.llm, task=task, agent=self, crew=self.crew,
tools=parsed_tools, prompt=prompt, ...), instantiate executor_class(...) with
the exact same keyword args (and the fallback to AgentExecutor if executor_class
is None) so all existing parameters (tools_handler, step_callback,
function_calling_llm, request_within_rpm_limit, callbacks, response_model, etc.)
are forwarded unchanged.
- Around line 850-864: The sync path treats self.agent_executor.invoke(...) as a
dict but invoke can return an awaitable when running under an active event loop
(AgentExecutor default); update the sync branch to detect awaitables (use
inspect.isawaitable on the value returned by agent_executor.invoke) and if it is
awaitable, raise a clear RuntimeError instructing callers to use the async
variants (e.g. AgentExecutor.aexecute_task or kickoff_async) instead of indexing
into the coroutine; otherwise, cast to dict and return result["output"] as
before.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 77ba7f73-9bb2-4c60-8281-2fce6496a7be
📒 Files selected for processing (13)
lib/crewai/src/crewai/agent/core.pylib/crewai/src/crewai/agents/__init__.pylib/crewai/src/crewai/agents/crew_agent_executor.pylib/crewai/tests/agents/test_agent.pylib/crewai/tests/agents/test_agent_reasoning.pylib/crewai/tests/cassettes/TestCrewMultimodalAnthropic.test_image_file[anthropic-claude-sonnet-4-20250514].yamllib/crewai/tests/cassettes/TestCrewMultimodalAnthropic.test_mixed_files[anthropic-claude-sonnet-4-20250514].yamllib/crewai/tests/cassettes/TestCrewMultimodalAnthropic.test_pdf_file[anthropic-claude-sonnet-4-20250514].yamllib/crewai/tests/cassettes/agents/test_agent_custom_max_iterations.yamllib/crewai/tests/cassettes/utilities/test_tools_emits_error_events.yamllib/crewai/tests/telemetry/test_flow_crew_span_integration.pylib/crewai/tests/test_crew.pylib/crewai/tests/utilities/test_events.py
💤 Files with no reviewable changes (1)
- lib/crewai/tests/agents/test_agent_reasoning.py
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
4 similar comments
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Slack user <@U0773HGAUSJ> — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
5 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
2 similar comments
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
|
requested by Lorenze Jay — friendly nudge from Iris: this PR is still open. Want to take a look or close it out? |
Summary
Agents used inside
Crew()now default to the experimentalAgentExecutor(Flow-based) instead ofCrewAgentExecutor. This is the first step toward fully removingCrewAgentExecutorin a future release.Changes
agent/core.pyexecutor_classfield default changed fromCrewAgentExecutor→AgentExecutor_validate_executor_classvalidator now emits aDeprecationWarningwhenCrewAgentExecutoris passed explicitlyagents/crew_agent_executor.pymodel_post_init(or__init__) emits aDeprecationWarningon instantiation, directing users tocrewai.experimental.AgentExecutoragents/__init__.py__getattr__export forCrewAgentExecutorso existingfrom crewai.agents import CrewAgentExecutorimports continue to work without breaking circular-import chainsBackward compatibility
executor_class=CrewAgentExecutorexplicitly still works — it just emits aDeprecationWarningfrom crewai.agents import CrewAgentExecutorstill resolves correctlySmoke test
Next steps (follow-on PRs)
CrewAgentExecutorfrom_EXECUTOR_CLASS_MAPand the string-alias mapCrewAgentExecutorafter one minor version deprecation windowSummary by CodeRabbit
Refactor
Deprecation
Bug Fixes
Tests