Fix shared LLM stop words mutation causing cross-agent state pollution#5289
Fix shared LLM stop words mutation causing cross-agent state pollution#5289Christian-Sidak wants to merge 1 commit intocrewAIInc:mainfrom
Conversation
When multiple agents share the same LLM instance, each CrewAgentExecutor.__init__ was mutating the shared LLM stop attribute, causing stop words to accumulate across agents. This led to premature generation termination and race conditions in async execution. Instead of permanently mutating the shared LLM object, we now: 1. Compute effective stop words (original + executor-specific) locally 2. Apply them to the LLM only during invoke/ainvoke execution 3. Restore the original stop words in a finally block Fixes crewAIInc#5141
769b2c3 to
c886cad
Compare
|
The finally-block restore pattern is the correct fix — set before execution, restore unconditionally on exit, zero shared state mutation. The test plan covers the right cases. Worth noting the trust dimension beyond the bug itself: shared LLM state mutation is a trust boundary violation between agents. Agent A modifying An agent that has been verified to behave within certain parameters (stop words, response patterns) is implicitly recertified by that verification. If the actual runtime configuration diverges from the verified configuration due to shared state mutation, the behavioral attestation is no longer valid. Two additions worth considering alongside the fix: 1. Configuration integrity check at task handoff. Before an agent begins a task that was handed off from another agent, assert that 2. Behavioral drift alert on stop word change. A sudden change in stop words mid-session (even legitimately via the fixed code) represents a configuration state change worth flagging in observability tools — not as an error, but as an event that can explain behavioral differences across task boundaries. |
Summary
CrewAgentExecutor.__init__permanently mutatesself.llm.stop, causing stop words to accumulate across agents sharing the same LLM instanceinvoke/ainvoke, restoring the original value in afinallyblockChanges
Single file:
lib/crewai/src/crewai/agents/crew_agent_executor.py__init__: Instead of mutatingself.llm.stopdirectly, we now store the original stop words (self._original_stop) and the merged per-executor stop words (self._effective_stop) without touching the shared LLM.invoke/ainvoke: Setself.llm.stop = self._effective_stopbefore execution, then restoreself.llm.stop = self._original_stopin afinallyblock.Test plan
stopattribute is not permanently modified after an executor runsllm.stopis correctly restored even when execution raises an exceptionFixes #5141