Skip to content

Commit fef3cd1

Browse files
fix: set hard stop to avoid infinite loop in parsing errors
1 parent b45cb7a commit fef3cd1

File tree

4 files changed

+377
-227
lines changed

4 files changed

+377
-227
lines changed

lib/crewai/src/crewai/agents/crew_agent_executor.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def __init__(
130130
self.messages: list[LLMMessage] = []
131131
self.iterations = 0
132132
self.log_error_after = 3
133+
self.max_iterations_exceeded_count = 0
133134
if self.llm:
134135
# This may be mutating the shared llm object and needs further evaluation
135136
existing_stop = getattr(self.llm, "stop", [])
@@ -202,31 +203,33 @@ def _invoke_loop(self) -> AgentFinish:
202203
Returns:
203204
Final answer from the agent.
204205
"""
205-
formatted_answer = None
206+
formatted_answer: AgentAction | AgentFinish | None = None
206207
while not isinstance(formatted_answer, AgentFinish):
207208
try:
208209
if has_reached_max_iterations(self.iterations, self.max_iter):
210+
self.max_iterations_exceeded_count += 1
209211
formatted_answer = handle_max_iterations_exceeded(
210212
formatted_answer,
211213
printer=self._printer,
212214
i18n=self._i18n,
213215
messages=self.messages,
214216
llm=self.llm,
215217
callbacks=self.callbacks,
218+
max_iterations_exceeded_count=self.max_iterations_exceeded_count,
216219
)
220+
else:
221+
enforce_rpm_limit(self.request_within_rpm_limit)
217222

218-
enforce_rpm_limit(self.request_within_rpm_limit)
219-
220-
answer = get_llm_response(
221-
llm=self.llm,
222-
messages=self.messages,
223-
callbacks=self.callbacks,
224-
printer=self._printer,
225-
from_task=self.task,
226-
from_agent=self.agent,
227-
response_model=self.response_model,
228-
)
229-
formatted_answer = process_llm_response(answer, self.use_stop_words)
223+
answer = get_llm_response(
224+
llm=self.llm,
225+
messages=self.messages,
226+
callbacks=self.callbacks,
227+
printer=self._printer,
228+
from_task=self.task,
229+
from_agent=self.agent,
230+
response_model=self.response_model,
231+
)
232+
formatted_answer = process_llm_response(answer, self.use_stop_words)
230233

231234
if isinstance(formatted_answer, AgentAction):
232235
# Extract agent fingerprint if available

lib/crewai/src/crewai/utilities/agent_utils.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def handle_max_iterations_exceeded(
127127
messages: list[LLMMessage],
128128
llm: LLM | BaseLLM,
129129
callbacks: list[TokenCalcHandler],
130+
max_iterations_exceeded_count: int = 1,
130131
) -> AgentAction | AgentFinish:
131132
"""Handles the case when the maximum number of iterations is exceeded. Performs one more LLM call to get the final answer.
132133
@@ -137,9 +138,11 @@ def handle_max_iterations_exceeded(
137138
messages: List of messages to send to the LLM.
138139
llm: The LLM instance to call.
139140
callbacks: List of callbacks for the LLM call.
141+
max_iterations_exceeded_count: Number of times max iterations has been exceeded.
140142
141143
Returns:
142-
The final formatted answer after exceeding max iterations.
144+
The final formatted answer after exceeding max iterations. Returns AgentAction on first
145+
call to allow one more tool execution, then forces AgentFinish on subsequent calls.
143146
"""
144147
printer.print(
145148
content="Maximum iterations reached. Requesting final answer.",
@@ -168,8 +171,23 @@ def handle_max_iterations_exceeded(
168171
)
169172
raise ValueError("Invalid response from LLM call - None or empty.")
170173

171-
# Return the formatted answer, regardless of its type
172-
return format_answer(answer=answer)
174+
try:
175+
result = format_answer(answer=answer)
176+
# Allow returning AgentAction on first two calls to execute tools
177+
# On third call (count > 2), force AgentFinish to prevent infinite loop
178+
if isinstance(result, AgentAction) and max_iterations_exceeded_count > 2:
179+
return AgentFinish(
180+
thought="Maximum iterations reached - forcing final answer",
181+
output=answer,
182+
text=answer,
183+
)
184+
return result
185+
except OutputParserError:
186+
return AgentFinish(
187+
thought="Maximum iterations reached with parse error",
188+
output=answer,
189+
text=answer,
190+
)
173191

174192

175193
def format_message_for_llm(

lib/crewai/tests/agents/test_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ def counting_call(*args, **kwargs):
508508
assert isinstance(result, str)
509509
assert len(result) > 0
510510
assert call_count > 0
511-
assert call_count == 3
511+
assert call_count == 2
512512

513513

514514
@pytest.mark.vcr(filter_headers=["authorization"])

0 commit comments

Comments
 (0)