agent.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. from opendevin.controller.agent import Agent
  2. from opendevin.controller.state.state import State
  3. from opendevin.events.action import (
  4. Action,
  5. FileReadAction,
  6. FileWriteAction,
  7. MessageAction,
  8. )
  9. from opendevin.events.observation import Observation
  10. from opendevin.events.serialization.event import event_to_memory
  11. from opendevin.llm.llm import LLM
  12. from .parser import parse_command
  13. from .prompts import (
  14. CONTEXT_PROMPT,
  15. MEMORY_FORMAT,
  16. NO_ACTION,
  17. STEP_PROMPT,
  18. SYSTEM_MESSAGE,
  19. )
  20. class SWEAgent(Agent):
  21. """
  22. An attempt to recreate swe_agent with output parsing, prompting style, and Application Computer Interface (ACI).
  23. SWE-agent includes ACI functions like 'goto', 'search_for', 'edit', 'scroll', 'run'
  24. """
  25. def __init__(self, llm: LLM):
  26. super().__init__(llm)
  27. self.memory_window = 4
  28. self.max_retries = 2
  29. self.running_memory: list[str] = []
  30. self.cur_file: str = ''
  31. self.cur_line: int = 0
  32. def _remember(self, action: Action, observation: Observation) -> None:
  33. """Agent has a limited memory of the few steps implemented as a queue"""
  34. memory = MEMORY_FORMAT(event_to_memory(action), event_to_memory(observation))
  35. self.running_memory.append(memory)
  36. def _think_act(self, messages: list[dict]) -> tuple[Action, str]:
  37. resp = self.llm.completion(
  38. messages=messages,
  39. temperature=0.05,
  40. )
  41. action_resp = resp['choices'][0]['message']['content']
  42. print(f"\033[1m\033[91m{resp['usage']}\033[0m")
  43. print(
  44. '\n==== RAW OUTPUT ====',
  45. f'\033[96m{action_resp}\033[0m',
  46. '==== END RAW ====\n',
  47. sep='\n',
  48. )
  49. return parse_command(action_resp, self.cur_file, self.cur_line)
  50. def _update(self, action: Action) -> None:
  51. if isinstance(action, (FileReadAction, FileWriteAction)):
  52. self.cur_file = action.path
  53. self.cur_line = action.start
  54. def step(self, state: State) -> Action:
  55. """
  56. SWE-Agent step:
  57. 1. Get context - past actions, custom commands, current step
  58. 2. Perform think-act - prompt model for action and reasoning
  59. 3. Catch errors - ensure model takes action (5 attempts max)
  60. """
  61. for prev_action, obs in state.updated_info:
  62. self._remember(prev_action, obs)
  63. goal = state.get_current_user_intent()
  64. prompt = STEP_PROMPT(goal, self.cur_file, self.cur_line)
  65. msgs = [
  66. {'content': SYSTEM_MESSAGE, 'role': 'system'},
  67. {'content': prompt, 'role': 'user'},
  68. ]
  69. if len(self.running_memory) > 0:
  70. context = CONTEXT_PROMPT(self.running_memory, self.memory_window)
  71. msgs.insert(1, {'content': context, 'role': 'user'})
  72. # clrs = [''] * (len(msgs)-2) + ['\033[0;36m', '\033[0;35m']
  73. # print('\n\n'.join([c+m['content']+'\033[0m' for c, m in zip(clrs, msgs)]))
  74. action, thought = self._think_act(messages=msgs)
  75. start_msg_len = len(msgs)
  76. while not action and len(msgs) < self.max_retries + start_msg_len:
  77. error = NO_ACTION(thought)
  78. error_msg = {'content': error, 'role': 'user'}
  79. msgs.append(error_msg)
  80. action, thought = self._think_act(messages=msgs)
  81. if not action:
  82. action = MessageAction(thought)
  83. self._update(action)
  84. self.latest_action = action
  85. return action
  86. def search_memory(self, query: str) -> list[str]:
  87. return [item for item in self.running_memory if query in item]
  88. def reset(self) -> None:
  89. """Used to reset the agent"""
  90. self.running_memory = []
  91. super().reset()