Spaces:
Runtime error
Runtime error
# coding=utf-8 | |
# Copyright 2023 The AIWaves Inc. team. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
"""standard operation procedure of an LLM Autonomous agent""" | |
import random | |
from LLM.base_LLM import * | |
from State import State | |
from utils import extract, get_relevant_history | |
from Memory import Memory | |
from Prompt import * | |
import json | |
import os | |
class SOP: | |
""" | |
Responsible for managing the operational processes of all agents | |
""" | |
# SOP should have args : "states" "relations" "root" | |
def __init__(self, **kwargs): | |
self.controller_dict = {} | |
self.LLM = init_LLM("logs/god",**kwargs) | |
self.states = {} | |
self.init_states(kwargs["states"]) | |
self.init_relation(kwargs["relations"]) | |
for state_name, states_dict in kwargs["states"].items(): | |
if state_name != "end_state" and "controller" in states_dict: | |
self.controller_dict[state_name] = states_dict["controller"] | |
self.user_names = kwargs["user_names"] if "user_names" in kwargs else [] | |
self.root = self.states[kwargs["root"]] | |
self.current_state = self.root | |
self.finish_state_name = ( | |
kwargs["finish_state_name"] | |
if "finish_state_name" in kwargs | |
else "end_state" | |
) | |
self.roles_to_names = None | |
self.names_to_roles = None | |
self.finished = False | |
def from_config(cls, config_path): | |
with open(config_path) as f: | |
config = json.load(f) | |
os.environ.clear() | |
for key,value in config["config"].items(): | |
if key == "API_BASE": | |
if value == "": | |
pass | |
else: | |
os.environ[key] = value | |
# assert "API_KEY" in os.environ and os.environ["API_KEY"] != "API_KEY","Please go to config.json to set API_KEY" | |
sop = SOP(**config) | |
return sop | |
def init_states(self, states_dict): | |
for state_name, state_dict in states_dict.items(): | |
state_dict["name"] = state_name | |
self.states[state_name] = State(**state_dict) | |
def init_relation(self, relations): | |
for state_name, state_relation in relations.items(): | |
for idx, next_state_name in state_relation.items(): | |
self.states[state_name].next_states[idx] = self.states[next_state_name] | |
def transit(self, chat_history, **kwargs): | |
""" | |
Determine the next state based on the current situation | |
Return : | |
next_state(State) : the next state | |
""" | |
# 如果是单一循环节点,则一直循环即可 | |
# If it is a single loop node, just keep looping | |
if len(self.current_state.next_states) == 1: | |
next_state = "0" | |
# 否则则需要controller去判断进入哪一节点 | |
# Otherwise, the controller needs to determine which node to enter. | |
else: | |
current_state = self.current_state | |
controller_dict = self.controller_dict[current_state.name] | |
relevant_history = kwargs["relevant_history"] | |
max_chat_nums = controller_dict["max_chat_nums"] if "max_chat_nums" in controller_dict else 1000 | |
if current_state.chat_nums>=max_chat_nums: | |
return self.current_state.next_states["1"] | |
# 否则则让controller判断是否结束 | |
# Otherwise, let the controller judge whether to end | |
judge_system_prompt = controller_dict["judge_system_prompt"] | |
environment_prompt = eval(Get_environment_prompt) if current_state.environment_prompt else "" | |
transit_system_prompt = eval(Transit_system_prompt) | |
judge_last_prompt = controller_dict["judge_last_prompt"] | |
transit_last_prompt = eval(Transit_last_prompt) | |
environment = kwargs["environment"] | |
environment_summary = environment.shared_memory["short_term_memory"] | |
chat_history_message = Memory.get_chat_history(chat_history) | |
query = chat_history[-1].get_query() | |
chat_messages = [ | |
{ | |
"role": "user", | |
"content": eval(Transit_message) | |
} | |
] | |
extract_words = controller_dict["judge_extract_words"] if "judge_extract_words" in controller_dict else "end" | |
response = self.LLM.get_response( | |
chat_messages, transit_system_prompt, transit_last_prompt, stream=False, **kwargs | |
) | |
next_state = ( | |
response if response.isdigit() else extract(response, extract_words) | |
) | |
# 如果没有parse出来则继续循环 | |
# If no parse comes out, continue looping | |
if not next_state.isdigit(): | |
next_state = "0" | |
next_state = self.current_state.next_states[next_state] | |
return next_state | |
def route(self, chat_history, **kwargs): | |
""" | |
Determine the role that needs action based on the current situation | |
Return : | |
current_agent(Agent) : the next act agent | |
""" | |
agents = kwargs["agents"] | |
# 知道进入哪一状态后开始分配角色,如果该状态下只有一个角色则直接分配给他 | |
# Start assigning roles after knowing which state you have entered. If there is only one role in that state, assign it directly to him. | |
if len(self.current_state.roles) == 1: | |
next_role = self.current_state.roles[0] | |
# 否则controller进行分配 | |
# Otherwise the controller determines | |
else: | |
relevant_history = kwargs["relevant_history"] | |
controller_type = ( | |
self.controller_dict[self.current_state.name]["controller_type"] | |
if "controller_type" in self.controller_dict[self.current_state.name] | |
else "order" | |
) | |
# 如果是rule 控制器,则交由LLM进行分配角色 | |
# If controller type is rule, it is left to LLM to assign roles. | |
if controller_type == "rule": | |
controller_dict = self.controller_dict[self.current_state.name] | |
call_last_prompt = controller_dict["call_last_prompt"] if "call_last_prompt" in controller_dict else "" | |
allocate_prompt = "" | |
roles = list(set(self.current_state.roles)) | |
for role in roles: | |
allocate_prompt += eval(Allocate_component) | |
call_system_prompt = controller_dict["call_system_prompt"] if "call_system_prompt" in controller_dict else "" | |
environment_prompt = eval(Get_environment_prompt) if self.current_state.environment_prompt else "" | |
# call_system_prompt + environment + allocate_prompt | |
call_system_prompt = eval(Call_system_prompt) | |
query = chat_history[-1].get_query() | |
last_name = chat_history[-1].send_name | |
# last_prompt: note + last_prompt + query | |
call_last_prompt =eval(Call_last_prompt) | |
chat_history_message = Memory.get_chat_history(chat_history) | |
# Intermediate historical conversation records | |
chat_messages = [ | |
{ | |
"role": "user", | |
"content": eval(Call_message), | |
} | |
] | |
extract_words = controller_dict["call_extract_words"] if "call_extract_words" in controller_dict else "end" | |
response = self.LLM.get_response( | |
chat_messages, call_system_prompt, call_last_prompt, stream=False, **kwargs | |
) | |
# get next role | |
next_role = extract(response, extract_words) | |
# Speak in order | |
elif controller_type == "order": | |
# If there is no begin role, it will be given directly to the first person. | |
if not self.current_state.current_role: | |
next_role = self.current_state.roles[0] | |
# otherwise first | |
else: | |
self.current_state.index += 1 | |
self.current_state.index = (self.current_state.index) % len(self.current_state.roles) | |
next_role = self.current_state.roles[self.current_state.index] | |
# random speak | |
elif controller_type == "random": | |
next_role = random.choice(self.current_state.roles) | |
# 如果下一角色不在,则随机挑选一个 | |
# If the next character is not available, pick one at random | |
if next_role not in self.current_state.roles: | |
next_role = random.choice(self.current_state.roles) | |
self.current_state.current_role = next_role | |
next_agent = agents[self.roles_to_names[self.current_state.name][next_role]] | |
return next_agent | |
def next(self, environment, agents): | |
""" | |
Determine the next state and the agent that needs action based on the current situation | |
""" | |
# 如果是第一次进入该状态 | |
# If it is the first time to enter this state | |
if self.current_state.is_begin: | |
agent_name = self.roles_to_names[self.current_state.name][self.current_state.begin_role] | |
agent = agents[agent_name] | |
return self.current_state,agent | |
# get relevant history | |
query = environment.shared_memory["long_term_memory"][-1].content | |
relevant_history = get_relevant_history( | |
query, | |
environment.shared_memory["long_term_memory"][:-1], | |
environment.shared_memory["chat_embeddings"][:-1], | |
) | |
relevant_history = Memory.get_chat_history(relevant_history) | |
next_state = self.transit( | |
chat_history=environment.shared_memory["long_term_memory"][ | |
environment.current_chat_history_idx : | |
], | |
relevant_history=relevant_history, | |
environment=environment, | |
) | |
# 如果进入终止节点,则直接终止 | |
# If you enter the termination node, terminate directly | |
if next_state.name == self.finish_state_name: | |
self.finished = True | |
return None, None | |
self.current_state = next_state | |
# 如果是首次进入该节点且有开场白,则直接分配给开场角色 | |
# If it is the first time to enter the state and there is a begin query, it will be directly assigned to the begin role. | |
if self.current_state.is_begin and self.current_state.begin_role: | |
agent_name = self.roles_to_names[self.current_state.name][self.current_state.begin_role] | |
agent = agents[agent_name] | |
return self.current_state,agent | |
next_agent = self.route( | |
chat_history=environment.shared_memory["long_term_memory"][ | |
environment.current_chat_history_idx : | |
], | |
agents = agents, | |
relevant_history=relevant_history, | |
) | |
return self.current_state, next_agent | |