Merge pull request #95 from wugren/MVP

Support custom agent
This commit is contained in:
Liu Zhicong
2023-11-15 20:35:23 -08:00
committed by GitHub
3 changed files with 229 additions and 187 deletions
+109 -121
View File
@@ -12,7 +12,8 @@ import datetime
import copy import copy
import sys import sys
from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt,AgentReport,AgentTodo,AgentTodoResult,AgentWorkLog from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType, FunctionItem, LLMResult, AgentPrompt, AgentReport, \
AgentTodo, AgentTodoResult, AgentWorkLog, BaseAIAgent
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .compute_task import ComputeTaskResult,ComputeTaskResultCode from .compute_task import ComputeTaskResult,ComputeTaskResultCode
from .ai_function import AIFunction from .ai_function import AIFunction
@@ -115,7 +116,7 @@ class AIAgentTemplete:
return True return True
class AIAgent: class AIAgent(BaseAIAgent):
def __init__(self) -> None: def __init__(self) -> None:
self.role_prompt:AgentPrompt = None self.role_prompt:AgentPrompt = None
self.agent_prompt:AgentPrompt = None self.agent_prompt:AgentPrompt = None
@@ -127,7 +128,7 @@ class AIAgent:
self.last_recover_time = time.time() self.last_recover_time = time.time()
self.enable_thread = False self.enable_thread = False
self.can_do_unassigned_task = True self.can_do_unassigned_task = True
self.agent_id:str = None self.agent_id:str = None
self.template_id:str = None self.template_id:str = None
@@ -136,7 +137,7 @@ class AIAgent:
self.enable = True self.enable = True
self.enable_kb = False self.enable_kb = False
self.enable_timestamp = False self.enable_timestamp = False
self.guest_prompt_str = None self.guest_prompt_str = None
self.owner_promp_str = None self.owner_promp_str = None
self.contact_prompt_str = None self.contact_prompt_str = None
self.history_len = 10 self.history_len = 10
@@ -158,7 +159,7 @@ class AIAgent:
self.owner_env : Environment = None self.owner_env : Environment = None
self.owenr_bus = None self.owenr_bus = None
self.enable_function_list = None self.enable_function_list = None
@classmethod @classmethod
def create_from_templete(cls,templete:AIAgentTemplete, fullname:str): def create_from_templete(cls,templete:AIAgentTemplete, fullname:str):
@@ -191,7 +192,7 @@ class AIAgent:
if config.get("prompt") is not None: if config.get("prompt") is not None:
self.agent_prompt = AgentPrompt() self.agent_prompt = AgentPrompt()
self.agent_prompt.load_from_config(config["prompt"]) self.agent_prompt.load_from_config(config["prompt"])
if config.get("think_prompt") is not None: if config.get("think_prompt") is not None:
self.agent_think_prompt = AgentPrompt() self.agent_think_prompt = AgentPrompt()
self.agent_think_prompt.load_from_config(config["think_prompt"]) self.agent_think_prompt.load_from_config(config["think_prompt"])
@@ -206,13 +207,13 @@ class AIAgent:
if config.get("owner_prompt") is not None: if config.get("owner_prompt") is not None:
self.owner_promp_str = config["owner_prompt"] self.owner_promp_str = config["owner_prompt"]
if config.get("contact_prompt") is not None: if config.get("contact_prompt") is not None:
self.contact_prompt_str = config["contact_prompt"] self.contact_prompt_str = config["contact_prompt"]
if config.get("owner_env") is not None: if config.get("owner_env") is not None:
self.owner_env = config.get("owner_env") self.owner_env = config.get("owner_env")
if config.get("powerby") is not None: if config.get("powerby") is not None:
self.powerby = config["powerby"] self.powerby = config["powerby"]
@@ -231,7 +232,7 @@ class AIAgent:
if config.get("history_len"): if config.get("history_len"):
self.history_len = int(config.get("history_len")) self.history_len = int(config.get("history_len"))
return True return True
def get_id(self) -> str: def get_id(self) -> str:
return self.agent_id return self.agent_id
@@ -244,22 +245,22 @@ class AIAgent:
def get_llm_model_name(self) -> str: def get_llm_model_name(self) -> str:
if self.llm_model_name is None: if self.llm_model_name is None:
return AIStorage.get_instance().get_user_config().get_value("llm_model_name") return AIStorage.get_instance().get_user_config().get_value("llm_model_name")
return self.llm_model_name return self.llm_model_name
def get_max_token_size(self) -> int: def get_max_token_size(self) -> int:
return self.max_token_size return self.max_token_size
def get_llm_learn_token_limit(self) -> int: def get_llm_learn_token_limit(self) -> int:
return self.learn_token_limit return self.learn_token_limit
def get_learn_prompt(self) -> AgentPrompt: def get_learn_prompt(self) -> AgentPrompt:
return self.learn_prompt return self.learn_prompt
def get_agent_role_prompt(self) -> AgentPrompt: def get_agent_role_prompt(self) -> AgentPrompt:
return self.role_prompt return self.role_prompt
def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt: def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt:
cm = ContactManager.get_instance() cm = ContactManager.get_instance()
contact = cm.find_contact_by_name(remote_user) contact = cm.find_contact_by_name(remote_user)
@@ -283,7 +284,7 @@ class AIAgent:
prompt = AgentPrompt() prompt = AgentPrompt()
prompt.system_message = {"role":"system","content":real_str} prompt.system_message = {"role":"system","content":real_str}
return prompt return prompt
return None return None
def _get_inner_functions(self) -> dict: def _get_inner_functions(self) -> dict:
@@ -333,13 +334,13 @@ class AIAgent:
logger.info("llm execute inner func result:" + result_str) logger.info("llm execute inner func result:" + result_str)
prompt.messages.append({"role":"function","content":result_str,"name":func_name}) prompt.messages.append({"role":"function","content":result_str,"name":func_name})
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"_execute_func llm compute error:{task_result.error_str}") logger.error(f"_execute_func llm compute error:{task_result.error_str}")
return task_result return task_result
ineternal_call_record.result_str = task_result.result_str ineternal_call_record.result_str = task_result.result_str
ineternal_call_record.done_time = time.time() ineternal_call_record.done_time = time.time()
if org_msg: if org_msg:
@@ -355,10 +356,10 @@ class AIAgent:
return await self._execute_func(inner_func_call_node,prompt,org_msg,stack_limit-1) return await self._execute_func(inner_func_call_node,prompt,org_msg,stack_limit-1)
else: else:
return task_result return task_result
def get_agent_prompt(self) -> AgentPrompt: def get_agent_prompt(self) -> AgentPrompt:
return self.agent_prompt return self.agent_prompt
async def _get_agent_think_prompt(self) -> AgentPrompt: async def _get_agent_think_prompt(self) -> AgentPrompt:
return self.agent_think_prompt return self.agent_think_prompt
@@ -373,11 +374,11 @@ class AIAgent:
async def _handle_event(self,event): async def _handle_event(self,event):
if event.type == "AgentThink": if event.type == "AgentThink":
return await self.do_self_think() return await self.do_self_think()
# async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg:
# async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg:
# session_topic = msg.target + "#" + msg.topic # session_topic = msg.target + "#" + msg.topic
# chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db) # chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
# workspace = self.get_current_workspace() # workspace = self.get_current_workspace()
@@ -409,7 +410,7 @@ class AIAgent:
# self._format_msg_by_env_value(prompt) # self._format_msg_by_env_value(prompt)
# inner_functions,function_token_len = self._get_inner_functions() # inner_functions,function_token_len = self._get_inner_functions()
# system_prompt_len = prompt.get_prompt_token_len() # system_prompt_len = prompt.get_prompt_token_len()
# input_len = len(msg.body) # input_len = len(msg.body)
@@ -422,7 +423,7 @@ class AIAgent:
# if task_result.result_code != ComputeTaskResultCode.OK: # if task_result.result_code != ComputeTaskResultCode.OK:
# error_resp = msg.create_error_resp(task_result.error_str) # error_resp = msg.create_error_resp(task_result.error_str)
# return error_resp # return error_resp
# final_result = task_result.result_str # final_result = task_result.result_str
# llm_result : LLMResult = LLMResult.from_str(final_result) # llm_result : LLMResult = LLMResult.from_str(final_result)
# is_ignore = False # is_ignore = False
@@ -455,12 +456,12 @@ class AIAgent:
# return None # return None
def get_workspace_by_msg(self,msg:AgentMsg) -> WorkspaceEnvironment: def get_workspace_by_msg(self,msg:AgentMsg) -> WorkspaceEnvironment:
return self.agent_workspace return self.agent_workspace
def need_session_summmary(self,msg:AgentMsg,session:AIChatSession) -> bool: def need_session_summmary(self,msg:AgentMsg,session:AIChatSession) -> bool:
return False return False
async def _create_openai_thread(self) -> str: async def _create_openai_thread(self) -> str:
return None return None
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg: async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
msg_prompt = AgentPrompt() msg_prompt = AgentPrompt()
@@ -474,7 +475,7 @@ class AIAgent:
if self.agent_id in msg.mentions: if self.agent_id in msg.mentions:
need_process = True need_process = True
logger.info(f"agent {self.agent_id} recv a group chat message from {msg.sender},but is not mentioned,ignore!") 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: if need_process is not True:
chatsession.append(msg) chatsession.append(msg)
resp_msg = msg.create_group_resp_msg(self.agent_id,"") resp_msg = msg.create_group_resp_msg(self.agent_id,"")
@@ -490,7 +491,7 @@ class AIAgent:
need_create_thread = True need_create_thread = True
else: else:
need_create_thread = True need_create_thread = True
if need_create_thread: if need_create_thread:
openai_thread_id = await self._create_openai_thread() openai_thread_id = await self._create_openai_thread()
if openai_thread_id is not None: if openai_thread_id is not None:
@@ -507,12 +508,12 @@ class AIAgent:
prompt.append(self.get_agent_prompt()) prompt.append(self.get_agent_prompt())
prompt.append(self._get_remote_user_prompt(msg.sender)) prompt.append(self._get_remote_user_prompt(msg.sender))
self._format_msg_by_env_value(prompt) self._format_msg_by_env_value(prompt)
if self.need_session_summmary(msg,chatsession): if self.need_session_summmary(msg,chatsession):
# get relate session(todos) summary # get relate session(todos) summary
summary = self.llm_select_session_summary(msg,chatsession) summary = self.llm_select_session_summary(msg,chatsession)
prompt.append(AgentPrompt(summary)) prompt.append(AgentPrompt(summary))
known_info_str = "# Known information\n" known_info_str = "# Known information\n"
have_known_info = False have_known_info = False
todos_str,todo_count = await workspace.get_todo_tree() todos_str,todo_count = await workspace.get_todo_tree()
@@ -525,25 +526,25 @@ class AIAgent:
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG: if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
history_str,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len) history_str,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len)
else: else:
history_str,history_token_len = await self.get_prompt_from_session(chatsession,system_prompt_len + function_token_len,input_len) history_str,history_token_len = await self.get_prompt_from_session(chatsession,system_prompt_len + function_token_len,input_len)
if history_str: if history_str:
have_known_info = True have_known_info = True
known_info_str += history_str known_info_str += history_str
if have_known_info: if have_known_info:
known_info_prompt = AgentPrompt(known_info_str) known_info_prompt = AgentPrompt(known_info_str)
prompt.append(known_info_prompt) # chat context prompt.append(known_info_prompt) # chat context
prompt.append(msg_prompt) prompt.append(msg_prompt)
logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ") logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
#task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) #task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
task_result = await self._do_llm_complection(prompt,inner_functions,msg) task_result = await self._do_llm_complection(prompt,inner_functions,msg)
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
error_resp = msg.create_error_resp(task_result.error_str) error_resp = msg.create_error_resp(task_result.error_str)
return error_resp return error_resp
final_result = task_result.result_str final_result = task_result.result_str
if final_result is not None: if final_result is not None:
llm_result : LLMResult = LLMResult.from_str(final_result) llm_result : LLMResult = LLMResult.from_str(final_result)
@@ -593,11 +594,11 @@ class AIAgent:
return None return None
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int): async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int):
history_len = (self.max_token_size * 0.7) - system_token_len history_len = (self.max_token_size * 0.7) - system_token_len
messages = chatsession.read_history(self.history_len,pos,"natural") # read messages = chatsession.read_history(self.history_len,pos,"natural") # read
result_token_len = 0 result_token_len = 0
result_prompt = AgentPrompt() result_prompt = AgentPrompt()
@@ -605,7 +606,7 @@ class AIAgent:
if summary is not None: if summary is not None:
if len(summary) > 1: if len(summary) > 1:
have_summary = True have_summary = True
if have_summary: if have_summary:
result_prompt.messages.append({"role":"user","content":summary}) result_prompt.messages.append({"role":"user","content":summary})
result_token_len -= len(summary) result_token_len -= len(summary)
@@ -627,10 +628,10 @@ class AIAgent:
if history_len < 0: if history_len < 0:
logger.warning(f"_get_prompt_from_session reach limit of token,just read {read_history_msg} history message.") logger.warning(f"_get_prompt_from_session reach limit of token,just read {read_history_msg} history message.")
break break
result_prompt.messages.append({"role":"user","content":history_str}) result_prompt.messages.append({"role":"user","content":history_str})
return result_prompt,pos+read_history_msg return result_prompt,pos+read_history_msg
async def _get_prompt_from_session_for_groupchat(self,chatsession:AIChatSession,system_token_len,input_token_len,is_groupchat=False): async def _get_prompt_from_session_for_groupchat(self,chatsession:AIChatSession,system_token_len,input_token_len,is_groupchat=False):
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
messages = chatsession.read_history(self.history_len) # read messages = chatsession.read_history(self.history_len) # read
@@ -647,7 +648,7 @@ class AIAgent:
result_prompt.messages.append({"role":"assistant","content":f"(create on {formatted_time}) {msg.body} "}) result_prompt.messages.append({"role":"assistant","content":f"(create on {formatted_time}) {msg.body} "})
else: else:
result_prompt.messages.append({"role":"assistant","content":msg.body}) result_prompt.messages.append({"role":"assistant","content":msg.body})
else: else:
if self.enable_timestamp: if self.enable_timestamp:
result_prompt.messages.append({"role":"user","content":f"(create on {formatted_time}) {msg.body} "}) result_prompt.messages.append({"role":"user","content":f"(create on {formatted_time}) {msg.body} "})
@@ -665,11 +666,11 @@ class AIAgent:
async def _llm_summary_work(self,workspace:WorkspaceEnvironment): async def _llm_summary_work(self,workspace:WorkspaceEnvironment):
# read report ,and update work summary of # read report ,and update work summary of
# build todo list from work summary and goals # build todo list from work summary and goals
# #
report_list = self.get_unread_reports() report_list = self.get_unread_reports()
for report in report_list: for report in report_list:
if self.agent_energy <= 0: if self.agent_energy <= 0:
break break
@@ -690,7 +691,7 @@ class AIAgent:
async def _llm_review_unassigned_todos(self,workspace:WorkspaceEnvironment): async def _llm_review_unassigned_todos(self,workspace:WorkspaceEnvironment):
pass pass
async def _llm_read_report(self,report:AgentReport,worksapce:WorkspaceEnvironment): async def _llm_read_report(self,report:AgentReport,worksapce:WorkspaceEnvironment):
work_summary = worksapce.get_work_summary(self.agent_id) work_summary = worksapce.get_work_summary(self.agent_id)
prompt : AgentPrompt = AgentPrompt() prompt : AgentPrompt = AgentPrompt()
@@ -709,7 +710,7 @@ class AIAgent:
worksapce.set_work_summary(self.agent_id,task_result.result_str) worksapce.set_work_summary(self.agent_id,task_result.result_str)
# 尝试完成自己的TOOD (不依赖任何其他Agnet) # 尝试完成自己的TOOD (不依赖任何其他Agnet)
async def do_my_work(self) -> None: async def do_my_work(self) -> None:
workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None) workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None)
@@ -722,7 +723,7 @@ class AIAgent:
todo_list = await workspace.get_todo_list(self.agent_id) todo_list = await workspace.get_todo_list(self.agent_id)
check_count = 0 check_count = 0
do_count = 0 do_count = 0
for todo in todo_list: for todo in todo_list:
if self.agent_energy <= 0: if self.agent_energy <= 0:
break break
@@ -739,7 +740,7 @@ class AIAgent:
case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR: case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR:
continue continue
case AgentTodoResult.TODO_RESULT_CODE_OK: case AgentTodoResult.TODO_RESULT_CODE_OK:
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_DONE) await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_DONE)
case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR: case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR:
await workspace.update_todo(todo.todo_id,AgentTodo.TDDO_STATE_CHECKFAILED) await workspace.update_todo(todo.todo_id,AgentTodo.TDDO_STATE_CHECKFAILED)
@@ -747,7 +748,7 @@ class AIAgent:
self.agent_energy -= 1 self.agent_energy -= 1
check_count += 1 check_count += 1
elif await self.can_do(todo,workspace): elif await self.can_do(todo,workspace):
do_result : AgentTodoResult = await self._llm_do(todo,workspace) do_result : AgentTodoResult = await self._llm_do(todo,workspace)
todo.last_do_time = datetime.datetime.now().timestamp() todo.last_do_time = datetime.datetime.now().timestamp()
todo.retry_count += 1 todo.retry_count += 1
@@ -755,14 +756,14 @@ class AIAgent:
case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR: case AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR:
continue continue
case AgentTodoResult.TODO_RESULT_CODE_OK: case AgentTodoResult.TODO_RESULT_CODE_OK:
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_WAITING_CHECK) await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_WAITING_CHECK)
case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR: case AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR:
await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_EXEC_FAILED) await workspace.update_todo(todo.todo_id,AgentTodo.TODO_STATE_EXEC_FAILED)
await workspace.append_worklog(todo,do_result) await workspace.append_worklog(todo,do_result)
self.agent_energy -= 2 self.agent_energy -= 2
do_count += 1 do_count += 1
logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.") logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.")
def get_review_todo_prompt(self,todo:AgentTodo) -> AgentPrompt: def get_review_todo_prompt(self,todo:AgentTodo) -> AgentPrompt:
@@ -783,50 +784,50 @@ class AIAgent:
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"_llm_review_todos compute error:{task_result.error_str}") logger.error(f"_llm_review_todos compute error:{task_result.error_str}")
return return
return return
def get_do_prompt(self,todo:AgentTodo) -> AgentPrompt: def get_do_prompt(self,todo:AgentTodo) -> AgentPrompt:
return self.do_prompt return self.do_prompt
def get_prompt_from_todo(self,todo:AgentTodo) -> AgentPrompt: def get_prompt_from_todo(self,todo:AgentTodo) -> AgentPrompt:
json_str = json.dumps(todo.raw_obj) json_str = json.dumps(todo.raw_obj)
return AgentPrompt(json_str) return AgentPrompt(json_str)
async def need_review_todo(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool: async def need_review_todo(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
return False return False
async def can_check(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool: async def can_check(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
if self.get_check_prompt(todo) is None: if self.get_check_prompt(todo) is None:
return False return False
if todo.can_check() is False: if todo.can_check() is False:
return False return False
if todo.checker is not None: if todo.checker is not None:
if todo.checker != self.agent_id: if todo.checker != self.agent_id:
return False return False
else: else:
if self.can_do_unassigned_task is False: if self.can_do_unassigned_task is False:
return False return False
else: else:
todo.checker = self.agent_id todo.checker = self.agent_id
return True return True
async def can_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool: async def can_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> bool:
if todo.can_do() is False: if todo.can_do() is False:
return False return False
if todo.worker is not None: if todo.worker is not None:
if todo.worker != self.agent_id: if todo.worker != self.agent_id:
return False return False
else: else:
if self.can_do_unassigned_task is False: if self.can_do_unassigned_task is False:
return False return False
else: else:
todo.worker = self.agent_id todo.worker = self.agent_id
return True return True
async def _llm_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> AgentTodoResult: async def _llm_do(self,todo:AgentTodo,workspace:WorkspaceEnvironment) -> AgentTodoResult:
@@ -834,7 +835,7 @@ class AIAgent:
prompt : AgentPrompt = AgentPrompt() prompt : AgentPrompt = AgentPrompt()
#prompt.append(self.agent_prompt) #prompt.append(self.agent_prompt)
prompt.append(workspace.get_role_prompt(self.agent_id)) prompt.append(workspace.get_role_prompt(self.agent_id))
do_prompt = workspace.get_do_prompt(todo) do_prompt = workspace.get_do_prompt(todo)
if do_prompt is None: if do_prompt is None:
do_prompt = self.get_do_prompt(todo) do_prompt = self.get_do_prompt(todo)
@@ -853,7 +854,7 @@ class AIAgent:
result.result_code = AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR result.result_code = AgentTodoResult.TODO_RESULT_CODE_LLM_ERROR
result.error_str = task_result.error_str result.error_str = task_result.error_str
return result return result
llm_result = LLMResult.from_str(task_result.result_str) llm_result = LLMResult.from_str(task_result.result_str)
# result_str is the explain of how to do this todo # result_str is the explain of how to do this todo
result.result_str = llm_result.resp result.result_str = llm_result.resp
@@ -873,19 +874,19 @@ class AIAgent:
result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR
#result.error_str = error_str #result.error_str = error_str
return result return result
return result return result
async def append_toddo_result(self,todo,worksapce,llm_result,result_str): async def append_toddo_result(self,todo,worksapce,llm_result,result_str):
pass pass
def get_check_prompt(self,todo:AgentTodo) -> AgentPrompt: def get_check_prompt(self,todo:AgentTodo) -> AgentPrompt:
return self.check_prompt return self.check_prompt
async def _llm_check_todo(self, todo:AgentTodo,workspace:WorkspaceEnvironment) : async def _llm_check_todo(self, todo:AgentTodo,workspace:WorkspaceEnvironment) :
if self.get_check_prompt(todo) is None: if self.get_check_prompt(todo) is None:
return None return None
prompt : AgentPrompt = AgentPrompt() prompt : AgentPrompt = AgentPrompt()
prompt.append(self.agent_prompt) prompt.append(self.agent_prompt)
prompt.append(workspace.get_role_prompt(self.agent_id)) prompt.append(workspace.get_role_prompt(self.agent_id))
@@ -906,11 +907,11 @@ class AIAgent:
return True return True
todo.last_check_result = task_result.result_str todo.last_check_result = task_result.result_str
return False return False
# 尝试自我学习,会主动获取、读取资料并进行整理 # 尝试自我学习,会主动获取、读取资料并进行整理
# LLM的本质能力是处理海量知识,应该让LLM能基于知识把自己的工作处理的更好 # LLM的本质能力是处理海量知识,应该让LLM能基于知识把自己的工作处理的更好
async def do_self_learn(self) -> None: async def do_self_learn(self) -> None:
# 不同的workspace是否应该有不同的学习方法? # 不同的workspace是否应该有不同的学习方法?
workspace = self.get_workspace_by_msg(None) workspace = self.get_workspace_by_msg(None)
hash_list = workspace.kb_db.get_knowledge_without_llm_title() hash_list = workspace.kb_db.get_knowledge_without_llm_title()
for hash in hash_list: for hash in hash_list:
@@ -952,7 +953,7 @@ class AIAgent:
# self.llm_read_book(kb,item) # self.llm_read_book(kb,item)
# learn_power -= 1 # learn_power -= 1
# case "article": # case "article":
# #
# self.llm_read_article(kb,item) # self.llm_read_article(kb,item)
# learn_power -= 1 # learn_power -= 1
# case "video": # case "video":
@@ -981,8 +982,8 @@ class AIAgent:
current_list = kb.get_list(current_path) current_list = kb.get_list(current_path)
self_assessment_with_goal = self.get_self_assessment_with_goal() self_assessment_with_goal = self.get_self_assessment_with_goal()
learn_goal = {} learn_goal = {}
llm_blance_knowledge_base(current_path,current_list,self_assessment_with_goal,learn_goal,learn_power) llm_blance_knowledge_base(current_path,current_list,self_assessment_with_goal,learn_goal,learn_power)
# 主动学习 # 主动学习
@@ -991,7 +992,7 @@ class AIAgent:
self.llm_learn_with_search_engine(kb,goal,learn_power) self.llm_learn_with_search_engine(kb,goal,learn_power)
if learn_power <= 0: if learn_power <= 0:
break break
def parser_learn_llm_result(self,llm_result:LLMResult): def parser_learn_llm_result(self,llm_result:LLMResult):
pass pass
@@ -1010,13 +1011,13 @@ class AIAgent:
known_obj["summary"] = summary known_obj["summary"] = summary
tags = knowledge_item.get("tags") tags = knowledge_item.get("tags")
if tags: if tags:
known_obj["tags"] = tags known_obj["tags"] = tags
if need_catalogs: if need_catalogs:
catalogs = knowledge_item.get("catalogs") catalogs = knowledge_item.get("catalogs")
if catalogs: if catalogs:
known_obj["catalogs"] = catalogs known_obj["catalogs"] = catalogs
if temp_meta: if temp_meta:
for key in temp_meta.keys(): for key in temp_meta.keys():
known_obj[key] = temp_meta[key] known_obj[key] = temp_meta[key]
@@ -1029,7 +1030,7 @@ class AIAgent:
# Objectives: # Objectives:
# Obtain better titles, abstracts, table of contents (if necessary), tags # Obtain better titles, abstracts, table of contents (if necessary), tags
# Determine the appropriate place to put it (in line with the organization's goals) # Determine the appropriate place to put it (in line with the organization's goals)
# Known information: # Known information:
# The reason why the target service's learn_prompt is being sorted # The reason why the target service's learn_prompt is being sorted
# Summary of the organization's work (if any) # Summary of the organization's work (if any)
# The current structure of the knowledge base (note the size control) gen_kb_tree_prompt (when empty, LLM should generate an appropriate initial directory structure) # The current structure of the knowledge base (note the size control) gen_kb_tree_prompt (when empty, LLM should generate an appropriate initial directory structure)
@@ -1039,26 +1040,13 @@ class AIAgent:
# Indicate that the input is part of the content, let LLM generate intermediate results for the task # Indicate that the input is part of the content, let LLM generate intermediate results for the task
# Enter the content in sequence, when the last content block is input, LLM gets the result # Enter the content in sequence, when the last content block is input, LLM gets the result
#full_content = item.get_article_full_content() #full_content = item.get_article_full_content()
workspace = self.get_workspace_by_msg(None) workspace = self.get_workspace_by_msg(None)
try:
full_content = await workspace.load_knowledge_content(full_path)
if full_content is None:
return None
except Exception as e:
logger.error(f"llm_read_article: load knowledge {full_path} error:{e}")
return None
if len(full_content) < 16:
logger.warning(f"llm_read_article: article {knowledge_item['path']} is too short,just read summary!")
return None
str_len = len(full_content)
full_content_len = self.token_len(full_content) full_content_len = self.token_len(full_content)
if full_content_len < self.get_llm_learn_token_limit(): if full_content_len < self.get_llm_learn_token_limit():
# 短文章不用总结catelog # 短文章不用总结catelog
#path_list,summary = llm_get_summary(summary,full_content) #path_list,summary = llm_get_summary(summary,full_content)
#prompt = self.get_agent_role_prompt() #prompt = self.get_agent_role_prompt()
@@ -1075,7 +1063,7 @@ class AIAgent:
result_obj = {} result_obj = {}
result_obj["error_str"] = task_result.error_str result_obj["error_str"] = task_result.error_str
return result_obj return result_obj
result_obj = json.loads(task_result.result_str) result_obj = json.loads(task_result.result_str)
return result_obj return result_obj
@@ -1110,14 +1098,14 @@ class AIAgent:
result_obj = {} result_obj = {}
result_obj["error_str"] = task_result.error_str result_obj["error_str"] = task_result.error_str
return result_obj return result_obj
result_obj = json.loads(task_result.result_str) result_obj = json.loads(task_result.result_str)
temp_meta_data = result_obj temp_meta_data = result_obj
if is_final: if is_final:
return result_obj return result_obj
return None return None
async def do_self_think(self): async def do_self_think(self):
session_id_list = AIChatSession.list_session(self.agent_id,self.chat_db) session_id_list = AIChatSession.list_session(self.agent_id,self.chat_db)
@@ -1134,8 +1122,8 @@ class AIAgent:
used_energy = await self.think_todo_log(todo_log) used_energy = await self.think_todo_log(todo_log)
self.agent_energy -= used_energy self.agent_energy -= used_energy
return return
async def think_todo_log(self,todo_log:AgentWorkLog): async def think_todo_log(self,todo_log:AgentWorkLog):
pass pass
@@ -1168,24 +1156,24 @@ class AIAgent:
else: else:
new_summary= task_result.result_str new_summary= task_result.result_str
logger.info(f"agent {self.agent_id} think session {session_id} from {cur_pos} to {next_pos} summary:{new_summary}") logger.info(f"agent {self.agent_id} think session {session_id} from {cur_pos} to {next_pos} summary:{new_summary}")
chatsession.update_think_progress(next_pos,new_summary) chatsession.update_think_progress(next_pos,new_summary)
return return
async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt: async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt:
# TODO: get prompt from group chat is different from single chat # TODO: get prompt from group chat is different from single chat
if self.enable_thread: if self.enable_thread:
return None return None
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
messages = chatsession.read_history(self.history_len) # read messages = chatsession.read_history(self.history_len) # read
result_token_len = 0 result_token_len = 0
read_history_msg = 0 read_history_msg = 0
have_known_info = False have_known_info = False
known_info = "" known_info = ""
if chatsession.summary is not None: if chatsession.summary is not None:
if len(chatsession.summary) > 1: if len(chatsession.summary) > 1:
known_info += f"## Recent conversation summary \n {chatsession.summary}\n" known_info += f"## Recent conversation summary \n {chatsession.summary}\n"
result_token_len -= len(chatsession.summary) result_token_len -= len(chatsession.summary)
have_known_info = True have_known_info = True
@@ -1204,13 +1192,13 @@ class AIAgent:
if history_len < 0: if history_len < 0:
logger.warning(f"_get_prompt_from_session reach limit of token,just read {read_history_msg} history message.") logger.warning(f"_get_prompt_from_session reach limit of token,just read {read_history_msg} history message.")
break break
known_info += f"## Recent conversation history \n {histroy_str}\n" known_info += f"## Recent conversation history \n {histroy_str}\n"
if have_known_info: if have_known_info:
return known_info,result_token_len return known_info,result_token_len
return None,0 return None,0
async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict=None,org_msg:AgentMsg=None,is_json_resp = False) -> ComputeTaskResult: async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict=None,org_msg:AgentMsg=None,is_json_resp = False) -> ComputeTaskResult:
from .compute_kernel import ComputeKernel from .compute_kernel import ComputeKernel
#logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ") #logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
@@ -1231,28 +1219,28 @@ class AIAgent:
if inner_func_call_node: if inner_func_call_node:
call_prompt : AgentPrompt = copy.deepcopy(prompt) call_prompt : AgentPrompt = copy.deepcopy(prompt)
task_result = await self._execute_func(inner_func_call_node,call_prompt,inner_functions,org_msg) task_result = await self._execute_func(inner_func_call_node,call_prompt,inner_functions,org_msg)
return task_result return task_result
def need_work(self) -> bool: def need_work(self) -> bool:
if self.do_prompt is not None: if self.do_prompt is not None:
return True return True
if self.check_prompt is not None: if self.check_prompt is not None:
return True return True
if self.agent_energy > 2: if self.agent_energy > 2:
return True return True
return False return False
def need_self_think(self) -> bool: def need_self_think(self) -> bool:
return False return False
def need_self_learn(self) -> bool: def need_self_learn(self) -> bool:
if self.learn_prompt is not None: if self.learn_prompt is not None:
return True return True
return False return False
def wake_up(self) -> None: def wake_up(self) -> None:
if self.agent_task is None: if self.agent_task is None:
self.agent_task = asyncio.create_task(self._on_timer()) self.agent_task = asyncio.create_task(self._on_timer())
@@ -1296,7 +1284,7 @@ class AIAgent:
def token_len(self,text:str) -> int: def token_len(self,text:str) -> int:
return ComputeKernel.llm_num_tokens_from_text(text,self.get_llm_model_name()) return ComputeKernel.llm_num_tokens_from_text(text,self.get_llm_model_name())
+85 -44
View File
@@ -1,13 +1,16 @@
import abc
import copy import copy
from abc import abstractmethod
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
from enum import Enum from enum import Enum
import uuid import uuid
import time import time
import re import re
import shlex import shlex
import json import json
from typing import List from typing import List
from .ai_function import FunctionItem from .ai_function import FunctionItem
from .compute_task import ComputeTaskResult from .compute_task import ComputeTaskResult
@@ -47,9 +50,9 @@ class AgentMsg:
def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None: def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None:
self.msg_id = "msg#" + uuid.uuid4().hex self.msg_id = "msg#" + uuid.uuid4().hex
self.msg_type:AgentMsgType = msg_type self.msg_type:AgentMsgType = msg_type
self.prev_msg_id:str = None self.prev_msg_id:str = None
self.quote_msg_id:str = None self.quote_msg_id:str = None
self.rely_msg_id:str = None # if not none means this is a respone msg self.rely_msg_id:str = None # if not none means this is a respone msg
self.session_id:str = None self.session_id:str = None
@@ -68,7 +71,7 @@ class AgentMsg:
self.body_mime:str = None #//default is "text/plain",encode is utf8 self.body_mime:str = None #//default is "text/plain",encode is utf8
#type is call / action #type is call / action
self.func_name = None self.func_name = None
self.args = None self.args = None
self.result_str = None self.result_str = None
@@ -95,7 +98,7 @@ class AgentMsg:
msg.prev_msg_id = prev_msg_id msg.prev_msg_id = prev_msg_id
msg.sender = caller msg.sender = caller
return msg return msg
def create_action_msg(self,action_name:str,args:dict,caller:str): def create_action_msg(self,action_name:str,args:dict,caller:str):
msg = AgentMsg(AgentMsgType.TYPE_ACTION) msg = AgentMsg(AgentMsgType.TYPE_ACTION)
msg.create_time = time.time() msg.create_time = time.time()
@@ -105,11 +108,11 @@ class AgentMsg:
msg.topic = self.topic msg.topic = self.topic
msg.sender = caller msg.sender = caller
return msg return msg
def create_error_resp(self,error_msg:str): def create_error_resp(self,error_msg:str):
resp_msg = AgentMsg(AgentMsgType.TYPE_SYSTEM) resp_msg = AgentMsg(AgentMsgType.TYPE_SYSTEM)
resp_msg.create_time = time.time() resp_msg.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id resp_msg.rely_msg_id = self.msg_id
resp_msg.body = error_msg resp_msg.body = error_msg
resp_msg.topic = self.topic resp_msg.topic = self.topic
@@ -129,7 +132,7 @@ class AgentMsg:
resp_msg.topic = self.topic resp_msg.topic = self.topic
return resp_msg return resp_msg
def create_group_resp_msg(self,sender_id,resp_body): def create_group_resp_msg(self,sender_id,resp_body):
resp_msg = AgentMsg(AgentMsgType.TYPE_GROUPMSG) resp_msg = AgentMsg(AgentMsgType.TYPE_GROUPMSG)
resp_msg.create_time = time.time() resp_msg.create_time = time.time()
@@ -158,13 +161,13 @@ class AgentMsg:
def get_target(self) -> str: def get_target(self) -> str:
return self.target return self.target
def get_prev_msg_id(self) -> str: def get_prev_msg_id(self) -> str:
return self.prev_msg_id return self.prev_msg_id
def get_quote_msg_id(self) -> str: def get_quote_msg_id(self) -> str:
return self.quote_msg_id return self.quote_msg_id
@classmethod @classmethod
def parse_function_call(cls,func_string:str): def parse_function_call(cls,func_string:str):
str_list = shlex.split(func_string) str_list = shlex.split(func_string)
@@ -237,9 +240,9 @@ class LLMResult:
def __init__(self) -> None: def __init__(self) -> None:
self.state : str = "ignore" self.state : str = "ignore"
self.resp : str = "" self.resp : str = ""
self.raw_resp = None self.raw_resp = None
self.paragraphs : dict[str,FunctionItem] = [] self.paragraphs : dict[str,FunctionItem] = []
self.post_msgs : List[AgentMsg] = [] self.post_msgs : List[AgentMsg] = []
self.send_msgs : List[AgentMsg] = [] self.send_msgs : List[AgentMsg] = []
@@ -281,14 +284,14 @@ class LLMResult:
@classmethod @classmethod
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult': def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
r = LLMResult() r = LLMResult()
if llm_result_str is None: if llm_result_str is None:
r.state = "ignore" r.state = "ignore"
return r return r
if llm_result_str == "ignore": if llm_result_str == "ignore":
r.state = "ignore" r.state = "ignore"
return r return r
if llm_result_str[0] == "{": if llm_result_str[0] == "{":
return LLMResult.from_json_str(llm_result_str) return LLMResult.from_json_str(llm_result_str)
@@ -300,7 +303,7 @@ class LLMResult:
case "send_msg":# /send_msg $target_id case "send_msg":# /send_msg $target_id
if len(func_args) != 1: if len(func_args) != 1:
return False return False
new_msg = AgentMsg() new_msg = AgentMsg()
target_id = func_item.args[0] target_id = func_item.args[0]
msg_content = func_item.body msg_content = func_item.body
@@ -313,7 +316,7 @@ class LLMResult:
case "post_msg":# /post_msg $target_id case "post_msg":# /post_msg $target_id
if len(func_args) != 1: if len(func_args) != 1:
return False return False
new_msg = AgentMsg() new_msg = AgentMsg()
target_id = func_item.args[0] target_id = func_item.args[0]
msg_content = func_item.body msg_content = func_item.body
@@ -333,9 +336,9 @@ class LLMResult:
if func_name in valid_func: if func_name in valid_func:
r.paragraphs[func_name] = func_item r.paragraphs[func_name] = func_item
return True return True
return False return False
current_func : FunctionItem = None current_func : FunctionItem = None
for line in lines: for line in lines:
@@ -361,11 +364,11 @@ class LLMResult:
else: else:
r.state = "reponsed" r.state = "reponsed"
return r return r
class AgentReport: class AgentReport:
def __init__(self): def __init__(self):
pass pass
class AgentTodoResult: class AgentTodoResult:
TODO_RESULT_CODE_OK = 0, TODO_RESULT_CODE_OK = 0,
@@ -386,7 +389,7 @@ class AgentTodoResult:
result["error_str"] = self.error_str result["error_str"] = self.error_str
result["op_list"] = self.op_list result["op_list"] = self.op_list
return result return result
class AgentTodo: class AgentTodo:
TODO_STATE_WAIT_ASSIGN = "wait_assign" TODO_STATE_WAIT_ASSIGN = "wait_assign"
@@ -400,7 +403,7 @@ class AgentTodo:
TODO_STATE_CASNCEL = "cancel" TODO_STATE_CASNCEL = "cancel"
TODO_STATE_DONE = "done" TODO_STATE_DONE = "done"
TODO_STATE_EXPIRED = "expired" TODO_STATE_EXPIRED = "expired"
def __init__(self): def __init__(self):
self.todo_id = "todo#" + uuid.uuid4().hex self.todo_id = "todo#" + uuid.uuid4().hex
self.title = None self.title = None
@@ -409,9 +412,9 @@ class AgentTodo:
#self.parent = None #self.parent = None
self.create_time = time.time() self.create_time = time.time()
self.state = "wait_assign" self.state = "wait_assign"
self.worker = None self.worker = None
self.checker = None self.checker = None
self.createor = None self.createor = None
self.need_check = True self.need_check = True
@@ -433,7 +436,7 @@ class AgentTodo:
todo = AgentTodo() todo = AgentTodo()
if json_obj.get("id") is not None: if json_obj.get("id") is not None:
todo.todo_id = json_obj.get("id") todo.todo_id = json_obj.get("id")
todo.title = json_obj.get("title") todo.title = json_obj.get("title")
todo.state = json_obj.get("state") todo.state = json_obj.get("state")
create_time = json_obj.get("create_time") create_time = json_obj.get("create_time")
@@ -448,7 +451,7 @@ class AgentTodo:
last_do_time = json_obj.get("last_do_time") last_do_time = json_obj.get("last_do_time")
if last_do_time: if last_do_time:
todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp() todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp()
last_check_time = json_obj.get("last_check_time") last_check_time = json_obj.get("last_check_time")
if last_check_time: if last_check_time:
todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp() todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp()
last_review_time = json_obj.get("last_review_time") last_review_time = json_obj.get("last_review_time")
@@ -492,21 +495,21 @@ class AgentTodo:
result["createor"] = self.createor result["createor"] = self.createor
result["retry_count"] = self.retry_count result["retry_count"] = self.retry_count
return result return result
def can_check(self)->bool: def can_check(self)->bool:
if self.state != AgentTodo.TODO_STATE_WAITING_CHECK: if self.state != AgentTodo.TODO_STATE_WAITING_CHECK:
return False return False
now = datetime.now().timestamp() now = datetime.now().timestamp()
if self.last_check_time: if self.last_check_time:
time_diff = now - self.last_check_time time_diff = now - self.last_check_time
if time_diff < 60*15: if time_diff < 60*15:
logger.info(f"todo {self.title} is already checked, ignore") logger.info(f"todo {self.title} is already checked, ignore")
return False return False
return True return True
def can_do(self) -> bool: def can_do(self) -> bool:
match self.state: match self.state:
case AgentTodo.TODO_STATE_DONE: case AgentTodo.TODO_STATE_DONE:
@@ -522,32 +525,70 @@ class AgentTodo:
if self.retry_count > 3: if self.retry_count > 3:
logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore") logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore")
return False return False
now = datetime.now().timestamp() now = datetime.now().timestamp()
time_diff = self.due_date - now time_diff = self.due_date - now
if time_diff < 0: if time_diff < 0:
logger.info(f"todo {self.title} is expired, ignore") logger.info(f"todo {self.title} is expired, ignore")
self.state = AgentTodo.TODO_STATE_EXPIRED self.state = AgentTodo.TODO_STATE_EXPIRED
return False return False
if time_diff > 7*24*3600: if time_diff > 7*24*3600:
logger.info(f"todo {self.title} is far before due date, ignore") logger.info(f"todo {self.title} is far before due date, ignore")
return False return False
if self.last_do_time: if self.last_do_time:
time_diff = now - self.last_do_time time_diff = now - self.last_do_time
if time_diff < 60*15: if time_diff < 60*15:
logger.info(f"todo {self.title} is already do ignore") logger.info(f"todo {self.title} is already do ignore")
return False return False
logger.info(f"todo {self.title} can do.") logger.info(f"todo {self.title} can do.")
return True return True
class AgentWorkLog: class AgentWorkLog:
def __init__(self) -> None: def __init__(self) -> None:
pass pass
class BaseAIAgent:
def __init__(self) -> None: class BaseAIAgent(abc.ABC):
pass @abstractmethod
def get_id(self) -> str:
pass
@abstractmethod
def get_llm_model_name(self) -> str:
pass
@abstractmethod
def get_max_token_size(self) -> int:
pass
@abstractmethod
def get_llm_learn_token_limit(self) -> int:
pass
@abstractmethod
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
pass
class CustomAIAgent(BaseAIAgent):
def __init__(self, agent_id: str, llm_model_name: str, max_token_size: int, llm_learn_token_limit: int) -> None:
self.agent_id = agent_id
self.llm_model_name = llm_model_name
self.max_token_size = max_token_size
self.llm_learn_token_limit = llm_learn_token_limit
def get_id(self) -> str:
return self.agent_id
def get_llm_model_name(self) -> str:
return self.llm_model_name
def get_max_token_size(self) -> int:
return self.max_token_size
def get_llm_learn_token_limit(self) -> int:
return self.llm_learn_token_limit
+35 -22
View File
@@ -1,11 +1,13 @@
import importlib
import logging import logging
import toml import toml
import os import os
import sys
import runpy import runpy
from typing import Any, Callable, Dict, List, Optional, Union from typing import Any, Callable, Dict, List, Optional, Union
from aios_kernel import AIAgent,AIAgentTemplete,AIStorage,Environment from aios_kernel import AIAgent,AIAgentTemplete,AIStorage,Environment
from aios_kernel.agent_base import BaseAIAgent
from package_manager import PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask from package_manager import PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -17,19 +19,19 @@ cache = "./.agents"
class AgentManager: class AgentManager:
_instance = None _instance = None
@classmethod @classmethod
def get_instance(cls)->'AgentManager': def get_instance(cls)->'AgentManager':
if cls._instance is None: if cls._instance is None:
cls._instance = AgentManager() cls._instance = AgentManager()
return cls._instance return cls._instance
def __init__(self) -> None: def __init__(self) -> None:
self.agent_templete_env : PackageEnv = None self.agent_templete_env : PackageEnv = None
self.agent_env : PackageEnv = None self.agent_env : PackageEnv = None
self.db_path : str = None self.db_path : str = None
self.loaded_agent_instance : Dict[str,AIAgent] = None self.loaded_agent_instance : Dict[str,BaseAIAgent] = None
async def initial(self) -> None: async def initial(self) -> None:
system_app_dir = AIStorage.get_instance().get_system_app_dir() system_app_dir = AIStorage.get_instance().get_system_app_dir()
user_data_dir = AIStorage.get_instance().get_myai_dir() user_data_dir = AIStorage.get_instance().get_myai_dir()
@@ -43,13 +45,13 @@ class AgentManager:
self.db_path = f"{user_data_dir}/messages.db" self.db_path = f"{user_data_dir}/messages.db"
self.loaded_agent_instance = {} self.loaded_agent_instance = {}
return True return True
async def scan_all_agent(self)->None: async def scan_all_agent(self)->None:
pass pass
async def is_exist(self,agent_id:str) -> bool: async def is_exist(self,agent_id:str) -> bool:
the_aget = await self.get(agent_id) the_aget = await self.get(agent_id)
if the_aget: if the_aget:
@@ -60,17 +62,17 @@ class AgentManager:
the_agent = self.loaded_agent_instance.get(agent_id) the_agent = self.loaded_agent_instance.get(agent_id)
if the_agent: if the_agent:
return the_agent return the_agent
# try load from disk # try load from disk
agent_media_info = self.agent_env.load(agent_id) agent_media_info = self.agent_env.load(agent_id)
if agent_media_info is None: if agent_media_info is None:
return None return None
the_agent : AIAgent = await self._load_agent_from_media(agent_media_info) the_agent : AIAgent = await self._load_agent_from_media(agent_media_info)
if the_agent is None: if the_agent is None:
logger.warn(f"load agent {agent_id} from media failed!") logger.warn(f"load agent {agent_id} from media failed!")
return None return None
the_agent.chat_db = self.db_path the_agent.chat_db = self.db_path
return the_agent return the_agent
@@ -86,19 +88,19 @@ class AgentManager:
def install(self,templete_id) -> PackageInstallTask: def install(self,templete_id) -> PackageInstallTask:
installer = self.agent_templete_env.get_installer() installer = self.agent_templete_env.get_installer()
return installer.install(templete_id) return installer.install(templete_id)
def uninstall(self,templete_id) -> int: def uninstall(self,templete_id) -> int:
pass pass
async def _load_templete_from_media(self,templete_media:PackageMediaInfo) -> AIAgentTemplete: async def _load_templete_from_media(self,templete_media:PackageMediaInfo) -> AIAgentTemplete:
pass pass
async def _load_agent_from_media(self,agent_media:PackageMediaInfo) -> AIAgent: async def _load_agent_from_media(self,agent_media:PackageMediaInfo) -> BaseAIAgent:
reader = self.agent_env._create_media_loader(agent_media) reader = self.agent_env._create_media_loader(agent_media)
if reader is None: if reader is None:
logger.error(f"create media loader for {agent_media} failed!") logger.error(f"create media loader for {agent_media} failed!")
return None return None
try: try:
config_file = await reader.read("agent.toml","r") config_file = await reader.read("agent.toml","r")
if config_file is None: if config_file is None:
@@ -125,11 +127,22 @@ class AgentManager:
return None return None
return result_agent return result_agent
except Exception as e: except Exception as e:
logger.error(f"read agent.toml cfg from {agent_media} failed! unexpected error occurred: {str(e)}") custom_agent = os.path.join(agent_media.full_path,"agent.py")
return None if not os.path.exists(custom_agent):
logger.error(f"read agent.toml cfg from {agent_media} failed! unexpected error occurred: {str(e)}")
return None
agent_name = os.path.split(agent_media.full_path)[1]
spec = importlib.util.spec_from_file_location(agent_name, custom_agent)
the_api = importlib.util.module_from_spec(spec)
spec.loader.exec_module(the_api)
if not hasattr(the_api,"Agent"):
logger.error(f"read agent.toml cfg from {agent_media} failed! unexpected error occurred: {str(e)}")
return None
return the_api.Agent()
def create(self,template,agent_name,agent_last_name,agent_introduce) -> AIAgent: def create(self,template,agent_name,agent_last_name,agent_introduce) -> AIAgent:
pass pass