Add checks to stop infinite loops (#1293)

* Add checks to stop infinite loops

* Send an AgentErrorObservation for the user to see an oops loop

* (NullAction, Obs) problem should be (NullAction, error Obs)

* Merge the two with AgentErrorObs.

* Update opendevin/controller/agent_controller.py
This commit is contained in:
Engel Nyst 2024-04-23 23:52:32 +02:00 committed by GitHub
parent f6dcbeaa1d
commit d1551c3097
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 4 deletions

View File

@ -81,7 +81,7 @@ class FileWriteAction(ExecutableAction):
def _insert_lines(self, to_insert: list[str], original: list[str]):
"""
Insert the new conent to the original content based on self.start and self.end
Insert the new content to the original content based on self.start and self.end
"""
new_lines = [''] if self.start == 0 else original[:self.start]
new_lines += [i + '\n' for i in to_insert]

View File

@ -18,9 +18,9 @@ from opendevin.observation import AgentErrorObservation, NullObservation, Observ
from opendevin.plan import Plan
from opendevin.state import State
from ..action.tasks import TaskStateChangedAction
from ..schema import TaskState
from .action_manager import ActionManager
from opendevin.action.tasks import TaskStateChangedAction
from opendevin.schema import TaskState
from opendevin.controller.action_manager import ActionManager
MAX_ITERATIONS = config.get('MAX_ITERATIONS')
MAX_CHARS = config.get('MAX_CHARS')
@ -112,6 +112,13 @@ class AgentController:
await self.notify_task_state_changed()
break
if self._is_stuck():
logger.info('Loop detected, stopping task')
observation = AgentErrorObservation('I got stuck into a loop, the task has stopped.')
await self._run_callbacks(observation)
await self.set_task_state_to(TaskState.STOPPED)
break
async def start(self, task: str):
"""Starts the agent controller with a task.
If task already run before, it will continue from the last step.
@ -221,3 +228,27 @@ class AgentController:
def get_state(self):
return self.state
def _is_stuck(self):
if self.state is None or self.state.history is None or len(self.state.history) < 3:
return False
# if the last three (Action, Observation) tuples are too repetitive
# the agent got stuck in a loop
if all(
[self.state.history[-i][0] == self.state.history[-3][0] for i in range(1, 3)]
):
# it repeats same action, give it a chance, but not if:
if (all
(isinstance(self.state.history[-i][1], NullObservation) for i in range(1, 4))):
# same (Action, NullObservation): like 'think' the same thought over and over
logger.debug('Action, NullObservation loop detected')
return True
elif (all
(isinstance(self.state.history[-i][1], AgentErrorObservation) for i in range(1, 4))):
# (NullAction, AgentErrorObservation): errors coming from an exception
# (Action, AgentErrorObservation): the same action getting an error, even if not necessarily the same error
logger.debug('Action, AgentErrorObservation loop detected')
return True
return False