Quellcode durchsuchen

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
Engel Nyst vor 1 Jahr
Ursprung
Commit
d1551c3097
2 geänderte Dateien mit 35 neuen und 4 gelöschten Zeilen
  1. 1 1
      opendevin/action/fileop.py
  2. 34 3
      opendevin/controller/agent_controller.py

+ 1 - 1
opendevin/action/fileop.py

@@ -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]

+ 34 - 3
opendevin/controller/agent_controller.py

@@ -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