import traceback from typing import Optional from asyncio import Queue import asyncio import logging import uuid import time import json import shlex import datetime import copy import sys from ..proto.agent_msg import AgentMsg from ..proto.ai_function import * from ..proto.agent_task import AgentTaskState,AgentTask,AgentTodo,AgentTodoResult from ..proto.compute_task import * from .agent_base import * from .llm_process import * from .chatsession import * from ..environment.workspace_env import WorkspaceEnvironment, TodoListType from ..frame.contact_manager import ContactManager,Contact,FamilyMember from ..frame.compute_kernel import ComputeKernel from ..frame.bus import AIBus from ..environment.environment import * from ..environment.workspace_env import WorkspaceEnvironment from ..storage.storage import AIStorage from ..knowledge import * from ..utils import video_utils, image_utils from ..proto.compute_task import ComputeTaskResult,ComputeTaskResultCode,LLMPrompt,LLMResult logger = logging.getLogger(__name__) class AIAgentTemplete: def __init__(self) -> None: self.llm_model_name:str = "gpt-4-0613" self.max_token_size:int = 0 self.template_id:str = None self.introduce:str = None self.author:str = None self.prompt:LLMPrompt = None def load_from_config(self,config:dict) -> bool: if config.get("llm_model_name") is not None: self.llm_model_name = config["llm_model_name"] if config.get("max_token_size") is not None: self.max_token_size = config["max_token_size"] if config.get("template_id") is not None: self.template_id = config["template_id"] if config.get("prompt") is not None: self.prompt = LLMPrompt() if self.prompt.load_from_config(config["prompt"]) is False: logger.error("load prompt from config failed!") return False class AIAgent(BaseAIAgent): def __init__(self) -> None: self.role_prompt:LLMPrompt = None self.agent_prompt:LLMPrompt = None self.agent_think_prompt:LLMPrompt = None self.llm_model_name:str = None self.max_token_size:int = 128000 self.agent_energy = 15 self.agent_task = None self.last_recover_time = time.time() self.enable_thread = False self.can_do_unassigned_task = True self.agent_id:str = None self.template_id:str = None self.fullname:str = None self.powerby = None self.enable = True self.enable_kb = False self.enable_timestamp = False self.guest_prompt_str = None self.owner_promp_str = None self.contact_prompt_str = None self.history_len = 10 self.read_report_prompt = None todo_prompts = {} todo_prompts[TodoListType.TO_WORK] = { "do": None, "check": None, "review": None, } todo_prompts[TodoListType.TO_LEARN] = { "do": None, "check": None, "review": None, } self.todo_prompts = todo_prompts self.chat_db = None self.unread_msg = Queue() # msg from other agent self.owenr_bus = None self.memory : AgentMemory = None self.prviate_workspace : AgentWorkspace = None self.behaviors:Dict[str,BaseLLMProcess] = {} async def initial(self,params:Dict = None): self.memory = AgentMemory(self.agent_id,self.chat_db) self.prviate_workspace = AgentWorkspace(self.agent_id) init_params = {} init_params["memory"] = self.memory init_params["workspace"] = self.prviate_workspace for process_name in self.behaviors.keys(): init_result = await self.behaviors[process_name].initial(init_params) if init_result is False: logger.error(f"llm process {process_name} initial failed! initial return False") return False self.wake_up() return True async def load_from_config(self,config:dict) -> bool: if config.get("instance_id") is None: logger.error("agent instance_id is None!") return False self.agent_id = config["instance_id"] if config.get("fullname") is None: logger.error(f"agent {self.agent_id} fullname is None!") return False self.fullname = config["fullname"] if config.get("enable_thread") is not None: self.enable_thread = bool(config["enable_thread"]) if config.get("powerby") is not None: self.powerby = config["powerby"] if config.get("template_id") is not None: self.template_id = config["template_id"] if config.get("llm_model_name") is not None: self.llm_model_name = config["llm_model_name"] if config.get("max_token_size") is not None: self.max_token_size = config["max_token_size"] if config.get("enable_function") is not None: self.enable_function_list = config["enable_function"] if config.get("enable_kb") is not None: self.enable_kb = bool(config["enable_kb"]) if config.get("enable_timestamp") is not None: self.enable_timestamp = bool(config["enable_timestamp"]) if config.get("history_len"): self.history_len = int(config.get("history_len")) #load all LLMProcess self.behaviors = {} behaviors = config.get("behavior") for process_config_name in behaviors.keys(): process_config = behaviors[process_config_name] real_config = {} real_config.update(config) real_config.update(process_config) load_result = await LLMProcessLoader.get_instance().load_from_config(real_config) if load_result: self.behaviors[process_config_name] = load_result else: logger.error(f"load LLMProcess {process_config_name} failed!") return False return True def get_id(self) -> str: return self.agent_id def get_fullname(self) -> str: return self.fullname def get_template_id(self) -> str: return self.template_id def get_llm_model_name(self) -> str: if self.llm_model_name is None: return AIStorage.get_instance().get_user_config().get_value("llm_model_name") return self.llm_model_name def get_max_token_size(self) -> int: return self.max_token_size def get_agent_role_prompt(self) -> LLMPrompt: return self.role_prompt def get_agent_prompt(self) -> LLMPrompt: return self.agent_prompt async def llm_process_msg(self,msg:AgentMsg) -> AgentMsg: need_process:bool = True if msg.msg_type == AgentMsgType.TYPE_GROUPMSG: need_process = False session_topic = msg.target + "#" + msg.topic chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db) if msg.mentions is not None: if self.agent_id in msg.mentions: need_process = True logger.info(f"agent {self.agent_id} recv a group chat message from {msg.sender},but is not mentioned,ignore!") if need_process is not True: chatsession.append(msg) resp_msg = msg.create_group_resp_msg(self.agent_id,"") return resp_msg input_parms = { "msg":msg } msg_process = self.behaviors.get("on_message") llm_result : LLMResult = await msg_process.process(input_parms) if llm_result.state == LLMResultStates.ERROR: error_resp = msg.create_error_resp(llm_result.error_str) return error_resp elif llm_result.state == LLMResultStates.IGNORE: return None else: # OK resp_msg = llm_result.raw_result.get("_resp_msg") return resp_msg async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg: msg.context_info = {} msg.context_info["location"] = "SanJose" msg.context_info["now"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") msg.context_info["weather"] = "Partly Cloudy, 60°F" return await self.llm_process_msg(msg) async def llm_review_tasklist(self): llm_process : BaseLLMProcess = self.behaviors.get("review_task") if llm_process: if self.prviate_workspace: tasklist = await self.prviate_workspace.task_mgr.list_task() if tasklist: for agent_task in tasklist: if self.agent_energy <= 0: break if agent_task.state == AgentTaskState.TASK_STATE_WAIT: input_parms = { "task":agent_task } llm_result : LLMResult = await llm_process.process(input_parms) if llm_result.state == LLMResultStates.ERROR: logger.error(f"llm process review_task error:{llm_result.error_str}") continue elif llm_result.state == LLMResultStates.IGNORE: logger.info(f"llm process review_task ignore!") continue else: determine = llm_result.raw_result.get("determine") logger.info(f"llm process review_task ok!,think is:{determine}") self.agent_energy -= 1 async def _llm_run_todo_list(self, todo_list_type: TodoListType): workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None) logger.info(f"agent {self.agent_id} do my work start!") # review todolist #if await self.need_review_todolist(): # await self._llm_review_todolist(workspace) todo_list = workspace.todo_list[todo_list_type] need_todo = await todo_list.get_todo_list(self.agent_id) check_count = 0 do_count = 0 review_count = 0 for todo in need_todo: if self.agent_energy <= 0: break do_prompts = self._can_do_todo(todo_list_type, todo) if do_prompts: prompt : LLMPrompt = LLMPrompt() prompt.append(self.agent_prompt) prompt.append(workspace.get_role_prompt(self.agent_id)) prompt.append(do_prompts) prompt.append(todo.to_prompt()) do_result : AgentTodoResult = await self._llm_do_todo(todo, prompt, workspace) todo.last_do_time = datetime.datetime.now().timestamp() todo.retry_count += 1 match do_result.result_code: case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR: continue case AgentTodoResult.TODO_RESULT_CODE_OK: todo.result = do_result await todo_list.update_todo(todo.todo_id,AgentTodo.TODO_STATE_WAITING_CHECK) case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR: await todo_list.update_todo(todo.todo_id,AgentTodo.TODO_STATE_EXEC_FAILED) await todo_list.append_worklog(todo,do_result) self.agent_energy -= 2 do_count += 1 # review_result = await self._llm_review_todo(todo,workspace) # todo.last_review_time = datetime.datetime.now().timestamp() continue check_prompts = self._can_check_todo(todo_list_type, todo) if check_prompts: prompt : LLMPrompt = LLMPrompt() prompt.append(self.agent_prompt) prompt.append(workspace.get_role_prompt(self.agent_id)) prompt.append(check_prompts) if todo.last_check_result: prompt.append(LLMPrompt(todo.last_check_result)) prompt.append(todo.detail) prompt.append(todo.result) check_result: AgentTodoResult = await self._llm_check_todo(todo, prompt, workspace) todo.last_check_time = datetime.datetime.now().timestamp() match check_result.result_code: case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR: continue case AgentTodoResult.TODO_RESULT_CODE_OK: await todo_list.update_todo(todo.todo_id,AgentTodo.TODO_STATE_DONE) case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR: await todo_list.update_todo(todo.todo_id,AgentTodo.TDDO_STATE_CHECKFAILED) await todo_list.append_worklog(todo, check_result) self.agent_energy -= 1 check_count += 1 continue review_prompts = self._can_review_todo(todo_list_type, todo) if review_prompts: prompt.append(workspace.get_prompt()) prompt.append(workspace.get_role_prompt(self.agent_id)) prompt.append(review_prompts) todo_tree = todo_list.get_todo_tree("/") prompt.append(LLMPrompt(todo_tree)) do_result : AgentTodoResult = await self._llm_review_todo(todo, prompt, workspace) todo.last_review_time = datetime.datetime.now().timestamp() match do_result.result_code: case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR: continue case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR: continue case AgentTodoResult.TODO_RESULT_CODE_OK: await todo_list.update_todo(todo.todo_id,AgentTodo.TODO_STATE_REVIEWED) await todo_list.append_worklog(todo,do_result) self.agent_energy -= 1 review_count += 1 continue logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.") def _can_review_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt: do_prompts = self.todo_prompts[todo_list_type].get("review") if not do_prompts: return None if todo.can_review() is False: return None return do_prompts def _can_check_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt: do_prompts = self.todo_prompts[todo_list_type].get("check") if not do_prompts: return None if todo.can_check() is False: return None if todo.checker is not None: if todo.checker != self.agent_id: return None else: if self.can_do_unassigned_task is False: return None else: todo.checker = self.agent_id return do_prompts def _can_do_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt: do_prompts = self.todo_prompts[todo_list_type].get("do") if not do_prompts: return None if todo.can_do() is False: return None if todo.worker is not None: if todo.worker != self.agent_id: return None else: if self.can_do_unassigned_task is False: return None else: todo.worker = self.agent_id return do_prompts async def _llm_do_todo(self, todo: AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult: result = AgentTodoResult() task_result:ComputeTaskResult = await self.do_llm_complection(prompt, is_json_resp=True) if task_result.error_str is not None: logger.error(f"_llm_do compute error:{task_result.error_str}") result.result_code = AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR result.error_str = task_result.error_str return result llm_result = LLMResult.from_str(task_result.result_str) # result_str is the explain of how to do this todo result.result_str = llm_result.resp result.op_list = llm_result.op_list if llm_result.post_msgs is not None: for msg in llm_result.post_msgs: msg.sender = self.agent_id msg.topic = f"{todo.title}##{todo.todo_id}" #msg.prev_msg_id = todo.todo_id chatsession = AIChatSession.get_session(self.agent_id,f"{msg.target}#{msg.topic}",self.chat_db) chatsession.append(msg) resp = await AIBus.get_default_bus().post_message(msg) logging.info(f"agent {self.agent_id} send msg to {msg.target} result:{resp}") result_str, have_error = await workspace.exec_op_list(llm_result.action_list, self.agent_id) if have_error: result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR #result.error_str = error_str return result result.result_str = result_str return result async def _llm_check_todo(self, todo: AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult: result = AgentTodoResult() inner_functions,_ = BaseAIAgent.get_inner_functions(workspace) task_result:ComputeTaskResult = await self.do_llm_complection(prompt,inner_functions=inner_functions,is_json_resp=True) if task_result.error_str is not None: logger.error(f"_llm_do compute error:{task_result.error_str}") result.result_code = AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR result.error_str = task_result.error_str return result result.result_str = task_result.result_str todo.last_check_result = task_result.result_str return result async def _llm_review_todo(self, todo:AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment): inner_functions,_ = BaseAIAgent.get_inner_functions(workspace) task_result:ComputeTaskResult = await self.do_llm_complection(prompt,inner_functions=inner_functions) if task_result.result_code != ComputeTaskResultCode.OK: logger.error(f"_llm_review_todos compute error:{task_result.error_str}") return return # async def do_blance_knowledge_base(selft): # # 整理自己的知识库(让分类更平衡,更由于自己以后的工作),并尝试更新学习目标 # current_path = "/" # current_list = kb.get_list(current_path) # self_assessment_with_goal = self.get_self_assessment_with_goal() # learn_goal = {} # llm_blance_knowledge_base(current_path,current_list,self_assessment_with_goal,learn_goal,learn_power) # # 主动学习 # # 方法目前只有使用搜索引擎一种? # for goal in learn_goal.items(): # self.llm_learn_with_search_engine(kb,goal,learn_power) # if learn_power <= 0: # break async def do_self_think(self): session_id_list = AIChatSession.list_session(self.agent_id,self.chat_db) for session_id in session_id_list: if self.agent_energy <= 0: break used_energy = await self.think_chatsession(session_id) self.agent_energy -= used_energy # todo_logs = await self.get_todo_logs() # for todo_log in todo_logs: # if self.agent_energy <= 0: # break # used_energy = await self.think_todo_log(todo_log) # self.agent_energy -= used_energy return #async def think_todo_log(self,todo_log:AgentWorkLog): # pass def need_self_think(self) -> bool: return False def wake_up(self) -> None: if self.agent_task is None: self.agent_task = asyncio.create_task(self._on_timer()) else: logger.warning(f"agent {self.agent_id} is already wake up!") # agent loop async def _on_timer(self): while True: await asyncio.sleep(15) try: now = time.time() if self.last_recover_time is None: self.last_recover_time = now else: if now - self.last_recover_time > 60: self.agent_energy += (now - self.last_recover_time) / 60 self.last_recover_time = now if self.agent_energy <= 1: continue await self.llm_review_tasklist() # complete & check todo #await self._llm_run_todo_list(TodoListType.TO_WORK) ##await self._llm_run_todo_list(TodoListType.TO_LEARN) if self.need_self_think(): await self.do_self_think() # review other's todo # self.review_other_works() except Exception as e: tb_str = traceback.format_exc() logger.error(f"agent {self.agent_id} on timer error:{e},{tb_str}") continue