-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagent.py
More file actions
105 lines (80 loc) · 3.5 KB
/
Copy pathagent.py
File metadata and controls
105 lines (80 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import logging
import re
from typing import List, Dict, Tuple, Optional
from prompts import ABSTRACT_REACT_SYSTEM_PROMPT
from llm import LLMClient
from models import UIElement, Window, WorldState
logger = logging.getLogger(__name__)
if not logger.hasHandlers():
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class ReActAgent:
def __init__(
self,
llm_client: LLMClient,
max_trajectory_length: int = 15,
**kwargs
):
self.llm_client = llm_client
self.max_trajectory_length = max_trajectory_length
self.system_message_template = ABSTRACT_REACT_SYSTEM_PROMPT
self.reset()
def reset(self):
self.trajectory: List[Dict] = []
#logger.info("ReActAgent has been reset.")
def get_trajectory(self) -> List[Dict]:
return self.trajectory
def _build_prompt(self, instruction: str, observation: str) -> Tuple[str, str]:
system_prompt = self.system_message_template + f"\n\n### CURRENT TASK ###\n{instruction}"
history = ""
for turn in self.trajectory:
history += f"Previous Thought: {turn['thought']}\n"
history += f"Previous Action: {turn['action']}\n"
user_prompt = f"""
Here is the history of your previous thoughts and actions:
<history>
{history if history else "This is the first step."}
</history>
Here is the current observation of the GUI:
<observation>
{observation}
</observation>
Based on the history and current observation, please provide your next thought and action.
"""
return system_prompt, user_prompt.strip()
def predict(self, instruction: str, observation: str) -> Tuple[str, str]:
if len(self.trajectory) >= self.max_trajectory_length:
logger.warning("Maximum trajectory length reached. Forcing FAIL action.")
return "Max steps reached", "FAIL(reason='Max steps reached')"
system_prompt, user_prompt = self._build_prompt(instruction, observation)
response_text = self.llm_client.call(
prompt=user_prompt,
system_prompt=system_prompt,
temperature=0.0,
max_tokens=1024,
expect_json=False
)
if not response_text:
logger.error("LLM call failed or returned empty response.")
return "LLM call failed.", "FAIL(reason='LLM did not respond')"
#logger.info(f"LLM Raw Response:\n{response_text}")
thought = self._parse_thought(response_text)
action_str = self._parse_action(response_text)
self.trajectory.append({
"observation": observation,
"thought": thought,
"action": action_str,
})
return thought, action_str
def _parse_thought(self, response: str) -> str:
match = re.search(r'<thought>(.*?)</thought>', response, re.DOTALL)
if match:
return match.group(1).strip()
logger.warning("Could not parse <thought> from LLM response.")
return "No thought found in the response."
def _parse_action(self, response: str) -> str:
match = re.search(r'<action>(.*?)</action>', response, re.DOTALL)
if match:
action_content = match.group(1).strip()
return action_content
logger.error("Could not parse <action> tag from LLM response.")
return "FAIL(reason='No action tag found')"