agent.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. from typing import List
  2. from opendevin.agent import Agent
  3. from opendevin.llm.llm import LLM
  4. from opendevin.state import State
  5. from opendevin.action import (
  6. Action,
  7. AgentThinkAction,
  8. FileReadAction,
  9. FileWriteAction,
  10. )
  11. from opendevin.observation import Observation
  12. from .parser import parse_command
  13. from .prompts import (
  14. SYSTEM_MESSAGE,
  15. STEP_PROMPT,
  16. MEMORY_FORMAT,
  17. NO_ACTION,
  18. CONTEXT_PROMPT
  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(action.to_memory(), observation.to_memory())
  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('\n==== RAW OUTPUT ====',
  44. f'\033[96m{action_resp}\033[0m',
  45. '==== END RAW ====\n', sep='\n')
  46. return parse_command(action_resp, self.cur_file, self.cur_line)
  47. def _update(self, action: Action) -> None:
  48. if isinstance(action, (FileReadAction, FileWriteAction)):
  49. self.cur_file = action.path
  50. self.cur_line = action.start
  51. def step(self, state: State) -> Action:
  52. """
  53. SWE-Agent step:
  54. 1. Get context - past actions, custom commands, current step
  55. 2. Perform think-act - prompt model for action and reasoning
  56. 3. Catch errors - ensure model takes action (5 attempts max)
  57. """
  58. for prev_action, obs in state.updated_info:
  59. self._remember(prev_action, obs)
  60. prompt = STEP_PROMPT(
  61. state.plan.main_goal,
  62. self.cur_file,
  63. self.cur_line
  64. )
  65. msgs = [
  66. {'content': SYSTEM_MESSAGE, 'role': 'user'},
  67. {'content': prompt, 'role': 'user'}
  68. ]
  69. if len(self.running_memory) > 0:
  70. context = CONTEXT_PROMPT(
  71. self.running_memory,
  72. self.memory_window
  73. )
  74. msgs.insert(1, {'content': context, 'role': 'user'})
  75. # clrs = [''] * (len(msgs)-2) + ['\033[0;36m', '\033[0;35m']
  76. # print('\n\n'.join([c+m['content']+'\033[0m' for c, m in zip(clrs, msgs)]))
  77. action, thought = self._think_act(messages=msgs)
  78. start_msg_len = len(msgs)
  79. while not action and len(msgs) < self.max_retries + start_msg_len:
  80. error = NO_ACTION(thought)
  81. error_msg = {'content': error, 'role': 'user'}
  82. msgs.append(error_msg)
  83. action, thought = self._think_act(messages=msgs)
  84. if not action:
  85. action = AgentThinkAction(thought)
  86. self._update(action)
  87. self.latest_action = action
  88. return action
  89. def search_memory(self, query: str) -> List[str]:
  90. return [item for item in self.running_memory if query in item]
  91. def reset(self) -> None:
  92. """Used to reset the agent"""
  93. self.running_memory = []
  94. super().reset()