init
This commit is contained in:
106
webagents_step/agents/agent.py
Normal file
106
webagents_step/agents/agent.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
class Agent:
|
||||
def __init__(
|
||||
self,
|
||||
max_actions,
|
||||
verbose=0,
|
||||
logging=False,
|
||||
previous_actions: List = None,
|
||||
previous_reasons: List = None,
|
||||
previous_responses: List = None,
|
||||
):
|
||||
self.previous_actions = [] if previous_actions is None else previous_actions
|
||||
self.previous_reasons = [] if previous_reasons is None else previous_reasons
|
||||
self.previous_responses = [] if previous_responses is None else previous_responses
|
||||
self.max_actions = max_actions
|
||||
self.verbose = verbose
|
||||
self.logging = logging
|
||||
self.trajectory = []
|
||||
self.data_to_log = {}
|
||||
|
||||
def reset(self):
|
||||
self.previous_actions = []
|
||||
self.previous_reasons = []
|
||||
self.previous_responses = []
|
||||
self.trajectory = []
|
||||
self.data_to_log = {}
|
||||
|
||||
def get_trajectory(self):
|
||||
return self.trajectory
|
||||
|
||||
def update_history(self, action, reason):
|
||||
if action:
|
||||
self.previous_actions += [action]
|
||||
if reason:
|
||||
self.previous_reasons += [reason]
|
||||
|
||||
def predict_action(self, objective, observation, url=None):
|
||||
pass
|
||||
|
||||
def receive_response(self, response):
|
||||
self.previous_responses += [response]
|
||||
|
||||
def act(self, objective, env):
|
||||
while not env.done():
|
||||
observation = env.observation()
|
||||
action, reason = self.predict_action(
|
||||
objective=objective, observation=observation, url=env.get_url()
|
||||
)
|
||||
status = env.step(action)
|
||||
|
||||
if self.logging:
|
||||
self.log_step(
|
||||
objective=objective,
|
||||
url=env.get_url(),
|
||||
observation=observation,
|
||||
action=action,
|
||||
reason=reason,
|
||||
status=status,
|
||||
)
|
||||
|
||||
if len(self.previous_actions) >= self.max_actions:
|
||||
print(f"Agent exceeded max actions: {self.max_actions}")
|
||||
break
|
||||
|
||||
return status
|
||||
|
||||
async def async_act(self, objective, env):
|
||||
while not env.done():
|
||||
observation = await env.observation()
|
||||
action, reason = self.predict_action(
|
||||
objective=objective, observation=observation, url=env.get_url()
|
||||
)
|
||||
status = await env.step(action)
|
||||
|
||||
if self.logging:
|
||||
self.log_step(
|
||||
objective=objective,
|
||||
url=env.get_url(),
|
||||
observation=observation,
|
||||
action=action,
|
||||
reason=reason,
|
||||
status=status,
|
||||
)
|
||||
|
||||
if len(self.previous_actions) >= self.max_actions:
|
||||
print(f"Agent exceeded max actions: {self.max_actions}")
|
||||
break
|
||||
|
||||
return status
|
||||
|
||||
def log_step(self, objective, url, observation, action, reason, status):
|
||||
self.data_to_log['objective'] = objective
|
||||
self.data_to_log['url'] = url
|
||||
self.data_to_log['observation'] = observation if isinstance(observation, str) else observation["text"]
|
||||
self.data_to_log['previous_actions'] = self.previous_actions[:-1]
|
||||
self.data_to_log['previous_responses'] = self.previous_responses[:-1]
|
||||
self.data_to_log['previous_reasons'] = self.previous_reasons[:-1]
|
||||
self.data_to_log['action'] = action
|
||||
self.data_to_log['reason'] = reason
|
||||
if status:
|
||||
for (k, v) in status.items():
|
||||
self.data_to_log[k] = v
|
||||
self.trajectory.append(self.data_to_log)
|
||||
self.data_to_log = {}
|
||||
59
webagents_step/agents/prompt_agent.py
Normal file
59
webagents_step/agents/prompt_agent.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from webagents_step.agents.agent import Agent
|
||||
from typing import List
|
||||
from webagents_step.utils.llm import fill_prompt_template, construct_llm_message_openai, call_openai_llm, parse_action_reason, calculate_cost_openai, call_anthropic_llm
|
||||
|
||||
class PromptAgent(Agent):
|
||||
def __init__(self, max_actions: int = 10, verbose: bool = False, logging: bool = False,
|
||||
debug: bool = False, prompt_template: str = None, model: str = "gpt-3.5-turbo",
|
||||
prompt_mode: str = "chat", previous_actions: List = None, previous_reasons: List = None, previous_responses: List = None):
|
||||
super().__init__(max_actions=max_actions, verbose=verbose, logging=logging, previous_actions=previous_actions, previous_reasons=previous_reasons, previous_responses=previous_responses)
|
||||
self.debug = debug
|
||||
self.prompt_template = prompt_template
|
||||
self.model = model
|
||||
self.prompt_mode = prompt_mode
|
||||
|
||||
def previous_history(self):
|
||||
previous_history = []
|
||||
|
||||
if len(self.previous_actions) == len(self.previous_responses):
|
||||
for action, response in zip(self.previous_actions, self.previous_responses):
|
||||
if response:
|
||||
previous_history.append(f"{response} = {action}")
|
||||
else:
|
||||
previous_history.append(action)
|
||||
previous_history="\n".join(previous_history)
|
||||
else:
|
||||
previous_history = "\n".join(action for action in self.previous_actions if action is not None) if self.previous_actions is not None else ""
|
||||
|
||||
|
||||
return previous_history
|
||||
|
||||
def predict_action(self, objective, observation, url=None):
|
||||
prompt = fill_prompt_template(prompt_template=self.prompt_template, objective=objective,
|
||||
observation=observation, url=url,
|
||||
previous_history=self.previous_history())
|
||||
messages = construct_llm_message_openai(prompt=prompt, prompt_mode=self.prompt_mode)
|
||||
model_response = call_openai_llm(messages=messages, model=self.model)
|
||||
action, reason = parse_action_reason(model_response)
|
||||
|
||||
if self.logging:
|
||||
self.data_to_log['prompt'] = messages
|
||||
|
||||
if self.verbose > 0:
|
||||
print(f"\n OBJECTIVE: {objective}")
|
||||
print(f"\n URL: {url}")
|
||||
print(f"\n PREVIOUS HISTORY: {self.previous_history()}")
|
||||
print(f"\n REASON: {reason}")
|
||||
print(f"\n ACTION: {action}")
|
||||
if self.verbose > 1:
|
||||
print(f"\n OBSERVATION: {observation}")
|
||||
print(f"\n RESPONSE: {model_response}")
|
||||
|
||||
if self.debug:
|
||||
human_input = input()
|
||||
if human_input != "c":
|
||||
action = human_input
|
||||
reason = "None"
|
||||
|
||||
self.update_history(action=action, reason=reason)
|
||||
return action, reason
|
||||
105
webagents_step/agents/step_agent.py
Normal file
105
webagents_step/agents/step_agent.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from webagents_step.agents.agent import Agent
|
||||
from webagents_step.utils.stack import Stack
|
||||
from webagents_step.agents.prompt_agent import PromptAgent
|
||||
|
||||
from typing import List, Dict
|
||||
import re
|
||||
import time
|
||||
|
||||
class StepAgent(Agent):
|
||||
def __init__(self, max_actions: int = 10, verbose: bool = False, logging: bool = False,
|
||||
debug: bool = False,
|
||||
root_action: str = None,
|
||||
action_to_prompt_dict: Dict = None,
|
||||
low_level_action_list: List = None,
|
||||
model: str = "gpt-3.5-turbo",
|
||||
prompt_mode: str = "chat", previous_actions: List = None):
|
||||
super().__init__(max_actions=max_actions, verbose=verbose, logging=logging, previous_actions=previous_actions)
|
||||
self.debug = debug
|
||||
self.root_action = root_action
|
||||
self.action_to_prompt_dict = {} if action_to_prompt_dict is None else action_to_prompt_dict
|
||||
self.low_level_action_list = [] if low_level_action_list is None else low_level_action_list
|
||||
self.model = model
|
||||
self.prompt_mode = prompt_mode
|
||||
self.stack = Stack()
|
||||
|
||||
def is_done(self, action):
|
||||
if action and "stop" in action:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_low_level_action(self, action):
|
||||
if not action:
|
||||
return False
|
||||
action_type = action.split()[0]
|
||||
return (action_type in self.low_level_action_list)
|
||||
|
||||
def is_high_level_action(self, action):
|
||||
if not action:
|
||||
return False
|
||||
action_type = action.split()[0]
|
||||
return (action_type in self.action_to_prompt_dict)
|
||||
|
||||
def init_root_agent(self, objective):
|
||||
root_prompt_template = self.action_to_prompt_dict[self.root_action]
|
||||
agent = PromptAgent(
|
||||
prompt_template=root_prompt_template,
|
||||
model=self.model,
|
||||
prompt_mode=self.prompt_mode,
|
||||
max_actions=self.max_actions,
|
||||
verbose=self.verbose,
|
||||
logging=self.logging,
|
||||
debug=self.debug,
|
||||
previous_actions=[],
|
||||
previous_reasons=[],
|
||||
previous_responses=[]
|
||||
)
|
||||
return {'agent': agent, 'objective': objective}
|
||||
|
||||
def init_agent(self, action):
|
||||
pattern = r'(\w+)\s+\[(.*?)\]'
|
||||
matches = re.findall(pattern, action)
|
||||
action_type, _ = matches[0]
|
||||
objective = action
|
||||
prompt_template = self.action_to_prompt_dict[action_type]
|
||||
agent = PromptAgent(
|
||||
prompt_template=prompt_template,
|
||||
model=self.model,
|
||||
prompt_mode=self.prompt_mode,
|
||||
max_actions=self.max_actions,
|
||||
verbose=self.verbose,
|
||||
logging=self.logging,
|
||||
debug=self.debug,
|
||||
previous_actions=[],
|
||||
previous_reasons=[],
|
||||
previous_responses=[]
|
||||
)
|
||||
return {'agent': agent, 'objective': objective}
|
||||
|
||||
def predict_action(self, objective, observation, url=None):
|
||||
observation = observation["text"] if isinstance(observation, dict) else observation
|
||||
if self.stack.is_empty():
|
||||
new_element = self.init_root_agent(objective=objective)
|
||||
self.stack.push(new_element)
|
||||
|
||||
action, reason = None, None
|
||||
while not self.stack.is_empty():
|
||||
element = self.stack.peek()
|
||||
action, reason = element['agent'].predict_action(objective=element['objective'], observation=observation, url=url)
|
||||
if (not self.is_done(action)) and self.is_low_level_action(action):
|
||||
element['agent'].receive_response("")
|
||||
return action, reason
|
||||
if (not self.is_done(action)) and self.is_high_level_action(action):
|
||||
new_element = self.init_agent(action)
|
||||
self.stack.push(new_element)
|
||||
if self.logging:
|
||||
self.log_step(objective=element['objective'], url=url, observation=observation, action=action, reason=reason, status={})
|
||||
continue
|
||||
if self.is_done(action):
|
||||
self.stack.pop()
|
||||
if not self.stack.is_empty():
|
||||
self.stack.peek()['agent'].receive_response(re.search(r"\[(.*?)\]", action).group(1))
|
||||
if self.logging:
|
||||
self.log_step(objective=element['objective'], url=url, observation=observation, action=action, reason=reason, status={})
|
||||
continue
|
||||
return action, reason
|
||||
Reference in New Issue
Block a user