mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
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:
parent
f6dcbeaa1d
commit
d1551c3097
@ -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]
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user