1) Do some rename refactor ,prepare for LLMProcess refactor
2) Fix merge bugs.
This commit is contained in:
+47
-45
@@ -13,10 +13,12 @@ import copy
|
||||
import sys
|
||||
|
||||
from ..proto.agent_msg import AgentMsg
|
||||
from ..proto.ai_function import *
|
||||
from ..proto.agent_task import *
|
||||
from ..proto.compute_task import *
|
||||
|
||||
from .agent_base import *
|
||||
from .chatsession import *
|
||||
from .ai_function import *
|
||||
from ..environment.workspace_env import WorkspaceEnvironment, TodoListType
|
||||
|
||||
from ..frame.contact_manager import ContactManager,Contact,FamilyMember
|
||||
@@ -69,7 +71,7 @@ class AIAgentTemplete:
|
||||
self.template_id:str = None
|
||||
self.introduce:str = None
|
||||
self.author:str = None
|
||||
self.prompt:AgentPrompt = None
|
||||
self.prompt:LLMPrompt = None
|
||||
|
||||
def load_from_config(self,config:dict) -> bool:
|
||||
if config.get("llm_model_name") is not None:
|
||||
@@ -79,7 +81,7 @@ class AIAgentTemplete:
|
||||
if config.get("template_id") is not None:
|
||||
self.template_id = config["template_id"]
|
||||
if config.get("prompt") is not None:
|
||||
self.prompt = AgentPrompt()
|
||||
self.prompt = LLMPrompt()
|
||||
if self.prompt.load_from_config(config["prompt"]) is False:
|
||||
logger.error("load prompt from config failed!")
|
||||
return False
|
||||
@@ -90,9 +92,9 @@ class AIAgentTemplete:
|
||||
|
||||
class AIAgent(BaseAIAgent):
|
||||
def __init__(self) -> None:
|
||||
self.role_prompt:AgentPrompt = None
|
||||
self.agent_prompt:AgentPrompt = None
|
||||
self.agent_think_prompt:AgentPrompt = 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
|
||||
@@ -149,26 +151,26 @@ class AIAgent(BaseAIAgent):
|
||||
self.enable_thread = bool(config["enable_thread"])
|
||||
|
||||
if config.get("prompt") is not None:
|
||||
self.agent_prompt = AgentPrompt()
|
||||
self.agent_prompt = LLMPrompt()
|
||||
self.agent_prompt.load_from_config(config["prompt"])
|
||||
|
||||
if config.get("think_prompt") is not None:
|
||||
self.agent_think_prompt = AgentPrompt()
|
||||
self.agent_think_prompt = LLMPrompt()
|
||||
self.agent_think_prompt.load_from_config(config["think_prompt"])
|
||||
|
||||
def load_todo_config(todo_type:str) -> bool:
|
||||
todo_config = config.get(todo_type)
|
||||
if todo_config is not None:
|
||||
if todo_config.get("do") is not None:
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.load_from_config(todo_config["do"])
|
||||
self.todo_prompts[todo_type]["do"] = prompt
|
||||
if todo_config.get("check") is not None:
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.load_from_config(todo_config["check"])
|
||||
self.todo_prompts[todo_type]["check"] = prompt
|
||||
if todo_config.get("review_prompt") is not None:
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.load_from_config(todo_config["review_prompt"])
|
||||
self.todo_prompts[todo_type]["review"] = prompt
|
||||
|
||||
@@ -224,16 +226,16 @@ class AIAgent(BaseAIAgent):
|
||||
def get_max_token_size(self) -> int:
|
||||
return self.max_token_size
|
||||
|
||||
def get_agent_role_prompt(self) -> AgentPrompt:
|
||||
def get_agent_role_prompt(self) -> LLMPrompt:
|
||||
return self.role_prompt
|
||||
|
||||
def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt:
|
||||
def _get_remote_user_prompt(self,remote_user:str) -> LLMPrompt:
|
||||
cm = ContactManager.get_instance()
|
||||
contact = cm.find_contact_by_name(remote_user)
|
||||
if contact is None:
|
||||
#create guest prompt
|
||||
if self.guest_prompt_str is not None:
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.system_message = {"role":"system","content":self.guest_prompt_str}
|
||||
return prompt
|
||||
return None
|
||||
@@ -241,25 +243,25 @@ class AIAgent(BaseAIAgent):
|
||||
if contact.is_family_member:
|
||||
if self.owner_promp_str is not None:
|
||||
real_str = self.owner_promp_str.format_map(contact.to_dict())
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.system_message = {"role":"system","content":real_str}
|
||||
return prompt
|
||||
else:
|
||||
if self.contact_prompt_str is not None:
|
||||
real_str = self.contact_prompt_str.format_map(contact.to_dict())
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.system_message = {"role":"system","content":real_str}
|
||||
return prompt
|
||||
|
||||
return None
|
||||
|
||||
def get_agent_prompt(self) -> AgentPrompt:
|
||||
def get_agent_prompt(self) -> LLMPrompt:
|
||||
return self.agent_prompt
|
||||
|
||||
async def _get_agent_think_prompt(self) -> AgentPrompt:
|
||||
async def _get_agent_think_prompt(self) -> LLMPrompt:
|
||||
return self.agent_think_prompt
|
||||
|
||||
def _format_msg_by_env_value(self,prompt:AgentPrompt):
|
||||
def _format_msg_by_env_value(self,prompt:LLMPrompt):
|
||||
for msg in prompt.messages:
|
||||
old_content = msg.get("content")
|
||||
msg["content"] = old_content.format_map(self.agent_workspace)
|
||||
@@ -284,7 +286,7 @@ class AIAgent(BaseAIAgent):
|
||||
return image_path
|
||||
|
||||
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
||||
msg_prompt = AgentPrompt()
|
||||
msg_prompt = LLMPrompt()
|
||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||
need_process = False
|
||||
if msg.is_image_msg():
|
||||
@@ -378,7 +380,7 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
workspace = self.get_workspace_by_msg(msg)
|
||||
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
if workspace:
|
||||
prompt.append(workspace.get_prompt())
|
||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||
@@ -390,7 +392,7 @@ class AIAgent(BaseAIAgent):
|
||||
if self.need_session_summmary(msg,chatsession):
|
||||
# get relate session(todos) summary
|
||||
summary = self.llm_select_session_summary(msg,chatsession)
|
||||
prompt.append(AgentPrompt(summary))
|
||||
prompt.append(LLMPrompt(summary))
|
||||
|
||||
known_info_str = "# Known information\n"
|
||||
have_known_info = False
|
||||
@@ -399,7 +401,7 @@ class AIAgent(BaseAIAgent):
|
||||
have_known_info = True
|
||||
known_info_str += f"## todo\n{todos_str}\n"
|
||||
inner_functions,function_token_len = BaseAIAgent.get_inner_functions(self.agent_workspace)
|
||||
system_prompt_len = self.token_len(prompt=prompt)
|
||||
system_prompt_len = ComputeKernel.llm_num_tokens(prompt)
|
||||
input_len = len(msg.body)
|
||||
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)
|
||||
@@ -410,7 +412,7 @@ class AIAgent(BaseAIAgent):
|
||||
known_info_str += history_str
|
||||
|
||||
if have_known_info:
|
||||
known_info_prompt = AgentPrompt(known_info_str)
|
||||
known_info_prompt = LLMPrompt(known_info_str)
|
||||
prompt.append(known_info_prompt) # chat context
|
||||
|
||||
prompt.append(msg_prompt)
|
||||
@@ -436,7 +438,7 @@ class AIAgent(BaseAIAgent):
|
||||
final_result = llm_result.resp
|
||||
|
||||
|
||||
await workspace.exec_op_list(llm_result.op_list,self.agent_id)
|
||||
await workspace.exec_op_list(llm_result.action_list,self.agent_id)
|
||||
|
||||
is_ignore = False
|
||||
result_prompt_str = ""
|
||||
@@ -471,12 +473,12 @@ class AIAgent(BaseAIAgent):
|
||||
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)->(LLMPrompt,int):
|
||||
history_len = (self.max_token_size * 0.7) - system_token_len
|
||||
|
||||
messages = chatsession.read_history(self.history_len,pos,"natural") # read
|
||||
result_token_len = 0
|
||||
result_prompt = AgentPrompt()
|
||||
result_prompt = LLMPrompt()
|
||||
have_summary = False
|
||||
if summary is not None:
|
||||
if len(summary) > 1:
|
||||
@@ -511,7 +513,7 @@ class AIAgent(BaseAIAgent):
|
||||
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
||||
messages = chatsession.read_history(self.history_len) # read
|
||||
result_token_len = 0
|
||||
result_prompt = AgentPrompt()
|
||||
result_prompt = LLMPrompt()
|
||||
read_history_msg = 0
|
||||
for msg in reversed(messages):
|
||||
read_history_msg += 1
|
||||
@@ -569,13 +571,13 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
async def _llm_read_report(self,report:AgentReport,worksapce:WorkspaceEnvironment):
|
||||
work_summary = worksapce.get_work_summary(self.agent_id)
|
||||
prompt : AgentPrompt = AgentPrompt()
|
||||
prompt : LLMPrompt = LLMPrompt()
|
||||
prompt.append(self.agent_prompt)
|
||||
prompt.append(worksapce.get_role_prompt(self.agent_id))
|
||||
prompt.append(self.read_report_prompt)
|
||||
# report is a message from other agent(human) about work
|
||||
prompt.append(AgentPrompt(work_summary))
|
||||
prompt.append(AgentPrompt(report.content))
|
||||
prompt.append(LLMPrompt(work_summary))
|
||||
prompt.append(LLMPrompt(report.content))
|
||||
|
||||
task_result:ComputeTaskResult = await self.do_llm_complection(prompt)
|
||||
|
||||
@@ -606,7 +608,7 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
do_prompts = self._can_do_todo(todo_list_type, todo)
|
||||
if do_prompts:
|
||||
prompt : AgentPrompt = AgentPrompt()
|
||||
prompt : LLMPrompt = LLMPrompt()
|
||||
prompt.append(self.agent_prompt)
|
||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||
prompt.append(do_prompts)
|
||||
@@ -635,13 +637,13 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
check_prompts = self._can_check_todo(todo_list_type, todo)
|
||||
if check_prompts:
|
||||
prompt : AgentPrompt = AgentPrompt()
|
||||
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(AgentPrompt(todo.last_check_result))
|
||||
prompt.append(LLMPrompt(todo.last_check_result))
|
||||
|
||||
prompt.append(todo.detail)
|
||||
prompt.append(todo.result)
|
||||
@@ -669,7 +671,7 @@ class AIAgent(BaseAIAgent):
|
||||
prompt.append(review_prompts)
|
||||
|
||||
todo_tree = todo_list.get_todo_tree("/")
|
||||
prompt.append(AgentPrompt(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()
|
||||
@@ -690,7 +692,7 @@ class AIAgent(BaseAIAgent):
|
||||
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) -> AgentPrompt:
|
||||
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
|
||||
@@ -701,7 +703,7 @@ class AIAgent(BaseAIAgent):
|
||||
return do_prompts
|
||||
|
||||
|
||||
def _can_check_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> AgentPrompt:
|
||||
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
|
||||
@@ -720,7 +722,7 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
return do_prompts
|
||||
|
||||
def _can_do_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> AgentPrompt:
|
||||
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
|
||||
@@ -739,7 +741,7 @@ class AIAgent(BaseAIAgent):
|
||||
|
||||
return do_prompts
|
||||
|
||||
async def _llm_do_todo(self, todo: AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
||||
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)
|
||||
@@ -763,7 +765,7 @@ class AIAgent(BaseAIAgent):
|
||||
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.op_list, self.agent_id)
|
||||
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
|
||||
@@ -771,7 +773,7 @@ class AIAgent(BaseAIAgent):
|
||||
result.result_str = result_str
|
||||
return result
|
||||
|
||||
async def _llm_check_todo(self, todo: AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
||||
async def _llm_check_todo(self, todo: AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
||||
result = AgentTodoResult()
|
||||
|
||||
inner_functions,_ = BaseAIAgent.get_inner_functions(workspace)
|
||||
@@ -786,7 +788,7 @@ class AIAgent(BaseAIAgent):
|
||||
todo.last_check_result = task_result.result_str
|
||||
return result
|
||||
|
||||
async def _llm_review_todo(self, todo:AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment):
|
||||
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)
|
||||
@@ -842,10 +844,10 @@ class AIAgent(BaseAIAgent):
|
||||
while True:
|
||||
cur_pos = chatsession.summarize_pos
|
||||
summary = chatsession.summary
|
||||
prompt:AgentPrompt = AgentPrompt()
|
||||
prompt:LLMPrompt = LLMPrompt()
|
||||
#prompt.append(self._get_agent_prompt())
|
||||
prompt.append(await self._get_agent_think_prompt())
|
||||
system_prompt_len = self.token_len(prompt=prompt)
|
||||
system_prompt_len = ComputeKernel.llm_num_tokens(prompt)
|
||||
#think env?
|
||||
history_prompt,next_pos = await self._get_history_prompt_for_think(chatsession,summary,system_prompt_len,cur_pos)
|
||||
prompt.append(history_prompt)
|
||||
@@ -864,7 +866,7 @@ class AIAgent(BaseAIAgent):
|
||||
chatsession.update_think_progress(next_pos,new_summary)
|
||||
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) -> LLMPrompt:
|
||||
# TODO: get prompt from group chat is different from single chat
|
||||
if self.enable_thread:
|
||||
return None
|
||||
|
||||
+10
-405
@@ -11,400 +11,15 @@ import shlex
|
||||
import json
|
||||
from typing import List, Tuple
|
||||
|
||||
from .ai_function import FunctionItem, AIFunction
|
||||
from ..proto.agent_msg import AgentMsg, AgentMsgType
|
||||
from ..proto.compute_task import ComputeTaskResult,ComputeTaskResultCode
|
||||
from ..environment.environment import BaseEnvironment
|
||||
from ..proto.ai_function import *
|
||||
from ..proto.agent_msg import *
|
||||
from ..proto.compute_task import *
|
||||
from ..environment.environment import *
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AgentPrompt:
|
||||
def __init__(self,prompt_str = None) -> None:
|
||||
self.messages = []
|
||||
if prompt_str:
|
||||
self.messages.append({"role":"user","content":prompt_str})
|
||||
self.system_message = None
|
||||
|
||||
def as_str(self)->str:
|
||||
result_str = ""
|
||||
if self.system_message:
|
||||
result_str += self.system_message.get("role") + ":" + self.system_message.get("content") + "\n"
|
||||
if self.messages:
|
||||
for msg in self.messages:
|
||||
result_str += msg.get("role") + ":" + msg.get("content") + "\n"
|
||||
|
||||
return result_str
|
||||
|
||||
def to_message_list(self):
|
||||
result = []
|
||||
if self.system_message:
|
||||
result.append(self.system_message)
|
||||
result.extend(self.messages)
|
||||
return result
|
||||
|
||||
def append(self,prompt):
|
||||
if prompt is None:
|
||||
return
|
||||
|
||||
if prompt.system_message is not None:
|
||||
if self.system_message is None:
|
||||
self.system_message = copy.deepcopy(prompt.system_message)
|
||||
else:
|
||||
self.system_message["content"] += prompt.system_message.get("content")
|
||||
|
||||
self.messages.extend(prompt.messages)
|
||||
|
||||
def load_from_config(self,config:list) -> bool:
|
||||
if isinstance(config,list) is not True:
|
||||
logger.error("prompt is not list!")
|
||||
return False
|
||||
self.messages = []
|
||||
for msg in config:
|
||||
if msg.get("content"):
|
||||
if msg.get("role") == "system":
|
||||
self.system_message = msg
|
||||
else:
|
||||
self.messages.append(msg)
|
||||
else:
|
||||
logger.error("prompt message has no content!")
|
||||
return True
|
||||
|
||||
class LLMResult:
|
||||
def __init__(self) -> None:
|
||||
self.state : str = "ignore"
|
||||
self.resp : str = ""
|
||||
self.raw_resp = None
|
||||
self.paragraphs : dict[str,FunctionItem] = []
|
||||
|
||||
|
||||
self.post_msgs : List[AgentMsg] = []
|
||||
self.send_msgs : List[AgentMsg] = []
|
||||
self.calls : List[FunctionItem] = []
|
||||
self.post_calls : List[FunctionItem] = []
|
||||
self.op_list : List[FunctionItem] = [] # op_list is a optimize design for saving token
|
||||
@classmethod
|
||||
def from_json_str(self,llm_json_str:str) -> 'LLMResult':
|
||||
r = LLMResult()
|
||||
if llm_json_str is None:
|
||||
r.state = "ignore"
|
||||
return r
|
||||
if llm_json_str == "ignore":
|
||||
r.state = "ignore"
|
||||
return r
|
||||
|
||||
llm_json = json.loads(llm_json_str)
|
||||
r.state = llm_json.get("state")
|
||||
r.resp = llm_json.get("resp")
|
||||
r.raw_resp = llm_json
|
||||
|
||||
post_msgs = llm_json.get("post_msg")
|
||||
r.post_msgs = []
|
||||
if post_msgs:
|
||||
for msg in post_msgs:
|
||||
new_msg = AgentMsg()
|
||||
target_id = msg.get("target")
|
||||
msg_content = msg.get("content")
|
||||
new_msg.set("",target_id,msg_content)
|
||||
r.post_msgs.append(new_msg)
|
||||
#new_msg.msg_type = AgentMsgType.TYPE_MSG
|
||||
|
||||
r.calls = llm_json.get("calls")
|
||||
r.post_calls = llm_json.get("post_calls")
|
||||
r.op_list = llm_json.get("op_list")
|
||||
|
||||
return r
|
||||
|
||||
@classmethod
|
||||
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
|
||||
r = LLMResult()
|
||||
|
||||
if llm_result_str is None:
|
||||
r.state = "ignore"
|
||||
return r
|
||||
if llm_result_str == "ignore":
|
||||
r.state = "ignore"
|
||||
return r
|
||||
|
||||
if llm_result_str[0] == "{":
|
||||
return LLMResult.from_json_str(llm_result_str)
|
||||
# if llm_result_str.startswith("json"):
|
||||
# return LLMResult.from_json_str(llm_result_str[4:])
|
||||
|
||||
lines = llm_result_str.splitlines()
|
||||
is_need_wait = False
|
||||
|
||||
def check_args(func_item:FunctionItem):
|
||||
match func_name:
|
||||
case "send_msg":# /send_msg $target_id
|
||||
if len(func_args) != 1:
|
||||
return False
|
||||
|
||||
new_msg = AgentMsg()
|
||||
target_id = func_item.args[0]
|
||||
msg_content = func_item.body
|
||||
new_msg.set("",target_id,msg_content)
|
||||
|
||||
r.send_msgs.append(new_msg)
|
||||
is_need_wait = True
|
||||
return True
|
||||
|
||||
case "post_msg":# /post_msg $target_id
|
||||
if len(func_args) != 1:
|
||||
return False
|
||||
|
||||
new_msg = AgentMsg()
|
||||
target_id = func_item.args[0]
|
||||
msg_content = func_item.body
|
||||
new_msg.set("",target_id,msg_content)
|
||||
r.post_msgs.append(new_msg)
|
||||
return True
|
||||
|
||||
case "call":# /call $func_name $args_str
|
||||
r.calls.append(func_item)
|
||||
is_need_wait = True
|
||||
return True
|
||||
case "post_call": # /post_call $func_name,$args_str
|
||||
r.post_calls.append(func_item)
|
||||
return True
|
||||
case _:
|
||||
if valid_func is not None:
|
||||
if func_name in valid_func:
|
||||
r.paragraphs[func_name] = func_item
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
current_func : FunctionItem = None
|
||||
for line in lines:
|
||||
if line.startswith("##/"):
|
||||
if current_func:
|
||||
if check_args(current_func) is False:
|
||||
r.resp += current_func.dumps()
|
||||
|
||||
func_name,func_args = AgentMsg.parse_function_call(line[3:])
|
||||
current_func = FunctionItem(func_name,func_args)
|
||||
else:
|
||||
if current_func:
|
||||
current_func.append_body(line + "\n")
|
||||
else:
|
||||
r.resp += line + "\n"
|
||||
|
||||
if current_func:
|
||||
if check_args(current_func) is False:
|
||||
r.resp += current_func.dumps()
|
||||
|
||||
if len(r.send_msgs) > 0 or len(r.calls) > 0:
|
||||
r.state = "waiting"
|
||||
else:
|
||||
r.state = "reponsed"
|
||||
|
||||
return r
|
||||
|
||||
class AgentReport:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class AgentTodoResult:
|
||||
TODO_RESULT_CODE_OK = 0,
|
||||
TODO_RESULT_CODE_LLM_ERROR = 1,
|
||||
TODO_RESULT_CODE_EXEC_OP_ERROR = 2
|
||||
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.result_code = AgentTodoResult.TODO_RESULT_CODE_OK
|
||||
self.result_str = None
|
||||
self.error_str = None
|
||||
self.op_list = None
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
result = {}
|
||||
result["result_code"] = self.result_code
|
||||
result["result_str"] = self.result_str
|
||||
result["error_str"] = self.error_str
|
||||
result["op_list"] = self.op_list
|
||||
return result
|
||||
|
||||
|
||||
class AgentTodo:
|
||||
TODO_STATE_WAIT_ASSIGN = "wait_assign"
|
||||
TODO_STATE_INIT = "init"
|
||||
|
||||
TODO_STATE_PENDING = "pending"
|
||||
TODO_STATE_WAITING_CHECK = "wait_check"
|
||||
TODO_STATE_EXEC_FAILED = "exec_failed"
|
||||
TDDO_STATE_CHECKFAILED = "check_failed"
|
||||
|
||||
TODO_STATE_CANCEL = "cancel"
|
||||
TODO_STATE_DONE = "done"
|
||||
TODO_STATE_REVIEWED = "reviewed"
|
||||
TODO_STATE_EXPIRED = "expired"
|
||||
|
||||
def __init__(self):
|
||||
self.todo_id = "todo#" + uuid.uuid4().hex
|
||||
self.title = None
|
||||
self.detail = None
|
||||
self.todo_path = None # get parent todo,sub todo by path
|
||||
#self.parent = None
|
||||
self.create_time = time.time()
|
||||
|
||||
self.state = "wait_assign"
|
||||
self.worker = None
|
||||
self.checker = None
|
||||
self.createor = None
|
||||
|
||||
self.need_check = True
|
||||
self.due_date = time.time() + 3600 * 24 * 2
|
||||
self.last_do_time = None
|
||||
self.last_check_time = None
|
||||
self.last_review_time = None
|
||||
|
||||
self.depend_todo_ids = []
|
||||
self.sub_todos = {}
|
||||
|
||||
self.result : AgentTodoResult = None
|
||||
self.last_check_result = None
|
||||
self.retry_count = 0
|
||||
self.raw_obj = None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls,json_obj:dict) -> 'AgentTodo':
|
||||
todo = AgentTodo()
|
||||
if json_obj.get("id") is not None:
|
||||
todo.todo_id = json_obj.get("id")
|
||||
|
||||
todo.title = json_obj.get("title")
|
||||
todo.state = json_obj.get("state")
|
||||
create_time = json_obj.get("create_time")
|
||||
if create_time:
|
||||
todo.create_time = datetime.fromisoformat(create_time).timestamp()
|
||||
|
||||
todo.detail = json_obj.get("detail")
|
||||
due_date = json_obj.get("due_date")
|
||||
if due_date:
|
||||
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
||||
|
||||
last_do_time = json_obj.get("last_do_time")
|
||||
if last_do_time:
|
||||
todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp()
|
||||
last_check_time = json_obj.get("last_check_time")
|
||||
if last_check_time:
|
||||
todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp()
|
||||
last_review_time = json_obj.get("last_review_time")
|
||||
if last_review_time:
|
||||
todo.last_review_time = datetime.fromisoformat(last_review_time).timestamp()
|
||||
|
||||
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
||||
todo.need_check = json_obj.get("need_check")
|
||||
#todo.result = json_obj.get("result")
|
||||
#todo.last_check_result = json_obj.get("last_check_result")
|
||||
todo.worker = json_obj.get("worker")
|
||||
todo.checker = json_obj.get("checker")
|
||||
todo.createor = json_obj.get("createor")
|
||||
if json_obj.get("retry_count"):
|
||||
todo.retry_count = json_obj.get("retry_count")
|
||||
|
||||
todo.raw_obj = json_obj
|
||||
|
||||
return todo
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
if self.raw_obj:
|
||||
result = self.raw_obj
|
||||
else:
|
||||
result = {}
|
||||
|
||||
result["id"] = self.todo_id
|
||||
#result["parent_id"] = self.parent_id
|
||||
result["title"] = self.title
|
||||
result["state"] = self.state
|
||||
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
|
||||
result["detail"] = self.detail
|
||||
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
||||
result["last_do_time"] = datetime.fromtimestamp(self.last_do_time).isoformat() if self.last_do_time else None
|
||||
result["last_check_time"] = datetime.fromtimestamp(self.last_check_time).isoformat() if self.last_check_time else None
|
||||
result["last_review_time"] = datetime.fromtimestamp(self.last_review_time).isoformat() if self.last_review_time else None
|
||||
result["depend_todo_ids"] = self.depend_todo_ids
|
||||
result["need_check"] = self.need_check
|
||||
result["worker"] = self.worker
|
||||
result["checker"] = self.checker
|
||||
result["createor"] = self.createor
|
||||
result["retry_count"] = self.retry_count
|
||||
|
||||
return result
|
||||
|
||||
def to_prompt(self) -> AgentPrompt:
|
||||
json_str = json.dumps(self.raw_obj)
|
||||
return AgentPrompt(json_str)
|
||||
|
||||
def can_review(self) -> bool:
|
||||
if self.state != AgentTodo.TODO_STATE_DONE:
|
||||
return False
|
||||
|
||||
now = datetime.now().timestamp()
|
||||
if self.last_review_time:
|
||||
time_diff = now - self.last_review_time
|
||||
if time_diff < 60*15:
|
||||
logger.info(f"todo {self.title} is already reviewed, ignore")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def can_check(self)->bool:
|
||||
if self.state != AgentTodo.TODO_STATE_WAITING_CHECK:
|
||||
return False
|
||||
|
||||
now = datetime.now().timestamp()
|
||||
if self.last_check_time:
|
||||
time_diff = now - self.last_check_time
|
||||
if time_diff < 60*15:
|
||||
logger.info(f"todo {self.title} is already checked, ignore")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def can_do(self) -> bool:
|
||||
match self.state:
|
||||
case AgentTodo.TODO_STATE_DONE:
|
||||
logger.info(f"todo {self.title} is done, ignore")
|
||||
return False
|
||||
case AgentTodo.TODO_STATE_CANCEL:
|
||||
logger.info(f"todo {self.title} is cancel, ignore")
|
||||
return False
|
||||
case AgentTodo.TODO_STATE_EXPIRED:
|
||||
logger.info(f"todo {self.title} is expired, ignore")
|
||||
return False
|
||||
case AgentTodo.TODO_STATE_EXEC_FAILED:
|
||||
if self.retry_count > 3:
|
||||
logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore")
|
||||
return False
|
||||
|
||||
now = datetime.now().timestamp()
|
||||
time_diff = self.due_date - now
|
||||
if time_diff < 0:
|
||||
logger.info(f"todo {self.title} is expired, ignore")
|
||||
self.state = AgentTodo.TODO_STATE_EXPIRED
|
||||
return False
|
||||
|
||||
if time_diff > 7*24*3600:
|
||||
logger.info(f"todo {self.title} is far before due date, ignore")
|
||||
return False
|
||||
|
||||
if self.last_do_time:
|
||||
time_diff = now - self.last_do_time
|
||||
if time_diff < 60*15:
|
||||
logger.info(f"todo {self.title} is already do ignore")
|
||||
return False
|
||||
|
||||
logger.info(f"todo {self.title} can do.")
|
||||
return True
|
||||
|
||||
|
||||
class AgentWorkLog:
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class BaseAIAgent(abc.ABC):
|
||||
@@ -420,19 +35,9 @@ class BaseAIAgent(abc.ABC):
|
||||
def get_max_token_size(self) -> int:
|
||||
pass
|
||||
|
||||
def token_len(self, text:str=None, prompt:AgentPrompt=None) -> int:
|
||||
from ..frame.compute_kernel import ComputeKernel
|
||||
if text:
|
||||
return ComputeKernel.llm_num_tokens_from_text(text,self.get_llm_model_name())
|
||||
elif prompt:
|
||||
result = 0
|
||||
if prompt.system_message:
|
||||
result += ComputeKernel.llm_num_tokens_from_text(prompt.system_message.get("content"),self.get_llm_model_name())
|
||||
for msg in prompt.messages:
|
||||
result += ComputeKernel.llm_num_tokens_from_text(msg.get("content"),self.get_llm_model_name())
|
||||
return result
|
||||
else:
|
||||
return 0
|
||||
@abstractmethod
|
||||
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_inner_functions(cls, env:BaseEnvironment) -> (dict,int):
|
||||
@@ -458,7 +63,7 @@ class BaseAIAgent(abc.ABC):
|
||||
|
||||
async def do_llm_complection(
|
||||
self,
|
||||
prompt:AgentPrompt,
|
||||
prompt:LLMPrompt,
|
||||
org_msg:AgentMsg=None,
|
||||
env:BaseEnvironment=None,
|
||||
inner_functions=None,
|
||||
@@ -503,7 +108,7 @@ class BaseAIAgent(abc.ABC):
|
||||
inner_func_call_node = result_message.get("function_call")
|
||||
|
||||
if inner_func_call_node:
|
||||
call_prompt : AgentPrompt = copy.deepcopy(prompt)
|
||||
call_prompt : LLMPrompt = copy.deepcopy(prompt)
|
||||
func_msg = copy.deepcopy(result_message)
|
||||
del func_msg["tool_calls"]
|
||||
call_prompt.messages.append(func_msg)
|
||||
@@ -515,7 +120,7 @@ class BaseAIAgent(abc.ABC):
|
||||
self,
|
||||
env: BaseEnvironment,
|
||||
inner_func_call_node: dict,
|
||||
prompt: AgentPrompt,
|
||||
prompt: LLMPrompt,
|
||||
inner_functions: dict,
|
||||
org_msg:AgentMsg,
|
||||
stack_limit = 5
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict,Coroutine,Callable
|
||||
|
||||
class ParameterDefine:
|
||||
def __init__(self) -> None:
|
||||
self.name = None
|
||||
self.type = None
|
||||
self.description = None
|
||||
|
||||
|
||||
class AIFunction:
|
||||
@abstractmethod
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
return the name of the function (should be snake case)
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_description(self) -> str:
|
||||
"""
|
||||
return a detailed description of what the function does
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_parameters(self) -> Dict:
|
||||
"""
|
||||
Return the list of parameters to execute this function in the form of
|
||||
JSON schema as specified in the OpenAI documentation:
|
||||
https://platform.openai.com/docs/api-reference/chat/create#chat/create-parameters
|
||||
|
||||
str = run_code(code:str)
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"description": "Python code which needs to be executed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self, **kwargs) -> str:
|
||||
"""
|
||||
Execute the function and return a JSON serializable dict.
|
||||
The parameters are passed in the form of kwargs
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_local(self) -> bool:
|
||||
"""
|
||||
is this function call need network?
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_in_zone(self) -> bool:
|
||||
"""
|
||||
is this function call in Lan?
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_ready_only(self) -> bool:
|
||||
pass
|
||||
|
||||
#def load_from_config(self,config:dict) -> bool:
|
||||
# pass
|
||||
|
||||
class FunctionItem:
|
||||
def __init__(self,name,args) -> None:
|
||||
self.name = name
|
||||
self.args = args
|
||||
self.body = None
|
||||
|
||||
def append_body(self,body:str) -> None:
|
||||
if self.body is None:
|
||||
self.body = body
|
||||
else:
|
||||
self.body += body
|
||||
|
||||
def dumps(self) -> str:
|
||||
pass
|
||||
|
||||
# call chain is a combination of ai_function,group of ai_function.
|
||||
class CallChain:
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def load_from_config(self,config:dict) -> bool:
|
||||
pass
|
||||
|
||||
async def execute(self):
|
||||
pass
|
||||
|
||||
class SimpleAIFunction(AIFunction):
|
||||
def __init__(self,func_id:str,description:str,func_handler:Coroutine,parameters:Dict = None) -> None:
|
||||
self.func_id = func_id
|
||||
self.description = description
|
||||
self.func_handler = func_handler
|
||||
self.parameters = parameters
|
||||
|
||||
def get_name(self) -> str:
|
||||
return self.func_id
|
||||
|
||||
def get_description(self) -> str:
|
||||
return self.description
|
||||
|
||||
def get_parameters(self) -> Dict:
|
||||
if self.parameters is not None:
|
||||
result = {}
|
||||
result["type"] = "object"
|
||||
parm_defines = {}
|
||||
for parm,desc in self.parameters.items():
|
||||
parm_item = {}
|
||||
parm_item["type"] = "string"
|
||||
parm_item["description"] = desc
|
||||
parm_defines[parm] = parm_item
|
||||
result["properties"] = parm_defines
|
||||
return result
|
||||
return {"type": "object", "properties": {}}
|
||||
|
||||
|
||||
async def execute(self,**kwargs) -> str:
|
||||
if self.func_handler is None:
|
||||
return "error: function not implemented"
|
||||
|
||||
return await self.func_handler(**kwargs)
|
||||
|
||||
def is_local(self) -> bool:
|
||||
return True
|
||||
|
||||
def is_in_zone(self) -> bool:
|
||||
return True
|
||||
|
||||
def is_ready_only(self) -> bool:
|
||||
return False
|
||||
|
||||
class AIOperation:
|
||||
@abstractmethod
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
return the name of the operation (should be snake case)
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_description(self) -> str:
|
||||
"""
|
||||
return a detailed description of what the operation does
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self, params: dict) -> str:
|
||||
"""
|
||||
Execute the function and return a JSON serializable dict.
|
||||
The parameters are passed in the form of kwargs
|
||||
"""
|
||||
pass
|
||||
|
||||
class SimpleAIOperation(AIOperation):
|
||||
def __init__(self,op:str,description:str,func_handler:Coroutine) -> None:
|
||||
self.op = op
|
||||
self.description = description
|
||||
self.func_handler = func_handler
|
||||
|
||||
def get_name(self) -> str:
|
||||
return self.op
|
||||
|
||||
def get_description(self) -> str:
|
||||
return self.description
|
||||
|
||||
async def execute(self, params: Dict) -> str:
|
||||
if self.func_handler is None:
|
||||
return "error: function not implemented"
|
||||
|
||||
return await self.func_handler(params)
|
||||
|
||||
|
||||
class AIFunctionOperation(AIOperation):
|
||||
def __init__(self, func: AIFunction) -> None:
|
||||
self.func = func
|
||||
super().__init__()
|
||||
|
||||
@abstractmethod
|
||||
def get_name(self) -> str:
|
||||
return self.func.get_name()
|
||||
|
||||
@abstractmethod
|
||||
def get_description(self) -> str:
|
||||
return self.func.get_description()
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self, params: dict) -> str:
|
||||
self.func.execute(**params)
|
||||
@@ -1,4 +0,0 @@
|
||||
# TODO: let agent develolp custmized behavior easily
|
||||
class AgentBehavior:
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
@@ -0,0 +1,149 @@
|
||||
# Old name is behavior, I belive new name "llm_process" is better
|
||||
from abc import ABC,abstractmethod
|
||||
import copy
|
||||
import json
|
||||
import shlex
|
||||
from typing import Any, Callable, Optional,Dict,Awaitable,List
|
||||
from enum import Enum
|
||||
|
||||
from ..proto.compute_task import *
|
||||
from ..proto.ai_function import *
|
||||
from ..frame.compute_kernel import *
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MIN_PREDICT_TOKEN_LEN = 32
|
||||
|
||||
|
||||
class BaseLLMProcess:
|
||||
def __init__(self) -> None:
|
||||
self.enable_json_resp = False
|
||||
self.model_name = "gpt-4"
|
||||
self.max_token = 2000 # include input prompt
|
||||
self.timeout = 1800 # 30 min
|
||||
|
||||
@abstractmethod
|
||||
async def prepare_prompt(self) -> LLMPrompt:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_inner_function(self,func_name:str) -> AIFunction:
|
||||
pass
|
||||
|
||||
async def _execute_inner_func(self,inner_func_call_node,prompt: LLMPrompt,stack_limit = 5) -> ComputeTaskResult:
|
||||
arguments = None
|
||||
try:
|
||||
func_name = inner_func_call_node.get("name")
|
||||
arguments = json.loads(inner_func_call_node.get("arguments"))
|
||||
logger.info(f"LLMProcess execute inner func:{func_name} :\n\t {json.dumps(arguments)}")
|
||||
|
||||
func_node : AIFunction = await self.get_inner_function(func_name)
|
||||
if func_node is None:
|
||||
result_str:str = f"execute {func_name} error,function not found"
|
||||
else:
|
||||
result_str:str = await func_node.execute(**arguments)
|
||||
except Exception as e:
|
||||
result_str = f"execute {func_name} error:{str(e)}"
|
||||
logger.error(f"LLMProcess execute inner func:{func_name} error:\n\t{e}")
|
||||
|
||||
logger.info("LLMProcess execute inner func result:" + result_str)
|
||||
|
||||
prompt.messages.append({"role":"function","content":result_str,"name":func_name})
|
||||
if self.enable_json_resp:
|
||||
resp_mode = "json"
|
||||
else:
|
||||
resp_mode = "text"
|
||||
|
||||
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt)
|
||||
if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||
task_result = ComputeTaskResult()
|
||||
task_result.result_code = ComputeTaskResultCode.ERROR
|
||||
task_result.error_str = f"prompt too long,can not predict"
|
||||
return task_result
|
||||
|
||||
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
|
||||
prompt,
|
||||
resp_mode=resp_mode,
|
||||
mode_name=self.model_name,
|
||||
max_token=max_result_token,
|
||||
inner_functions=prompt.inner_functions,
|
||||
timeout=self.timeout))
|
||||
|
||||
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||
logger.error(f"llm compute error:{task_result.error_str}")
|
||||
return task_result
|
||||
|
||||
inner_func_call_node = None
|
||||
if stack_limit > 0:
|
||||
result_message : dict = task_result.result.get("message")
|
||||
if result_message:
|
||||
inner_func_call_node = result_message.get("function_call")
|
||||
if inner_func_call_node:
|
||||
func_msg = copy.deepcopy(result_message)
|
||||
del func_msg["tool_calls"]#TODO: support tool_calls?
|
||||
prompt.messages.append(func_msg)
|
||||
else:
|
||||
logger.error(f"inner function call stack limit reached")
|
||||
task_result.result_code = ComputeTaskResultCode.ERROR
|
||||
task_result.error_str = "inner function call stack limit reached"
|
||||
return task_result
|
||||
|
||||
if inner_func_call_node:
|
||||
return await self._execute_inner_func(inner_func_call_node,prompt,stack_limit-1)
|
||||
else:
|
||||
return task_result
|
||||
|
||||
async def process(self) -> LLMResult:
|
||||
if self.enable_json_resp:
|
||||
resp_mode = "json"
|
||||
else:
|
||||
resp_mode = "text"
|
||||
|
||||
prompt = await self.prepare_prompt()
|
||||
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt)
|
||||
if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||
return LLMResult.from_error_str(f"prompt too long,can not predict")
|
||||
|
||||
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
|
||||
prompt,
|
||||
resp_mode=resp_mode,
|
||||
mode_name=self.model_name,
|
||||
max_token=max_result_token,
|
||||
inner_functions=prompt.inner_functions,
|
||||
timeout=self.timeout))
|
||||
|
||||
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||
err_str = f"do_llm_completion error:{task_result.error_str}"
|
||||
logger.error(err_str)
|
||||
return LLMResult.from_error_str(err_str)
|
||||
|
||||
result_message = task_result.result.get("message")
|
||||
inner_func_call_node = None
|
||||
if result_message:
|
||||
inner_func_call_node = result_message.get("function_call")
|
||||
|
||||
if inner_func_call_node:
|
||||
call_prompt : LLMPrompt = copy.deepcopy(prompt)
|
||||
func_msg = copy.deepcopy(result_message)
|
||||
del func_msg["tool_calls"]
|
||||
call_prompt.messages.append(func_msg)
|
||||
task_result = await self._execute_inner_func(inner_func_call_node,call_prompt)
|
||||
|
||||
# parse task_result to LLM Result
|
||||
if self.enable_json_resp:
|
||||
llm_result = LLMResult.from_json_str(task_result.result_str)
|
||||
else:
|
||||
llm_result = LLMResult.from_str(task_result.result_str)
|
||||
|
||||
# execute op_list in LLM Result?
|
||||
|
||||
return llm_result
|
||||
|
||||
#class LLMProcess
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from .agent_base import AgentPrompt
|
||||
from .agent_base import LLMPrompt
|
||||
|
||||
class AIRole:
|
||||
def __init__(self) -> None:
|
||||
@@ -9,7 +9,7 @@ class AIRole:
|
||||
self.role_id :str = None # $workflow_id.$sub_workflow_id.$role_name
|
||||
self.fullname : str = None
|
||||
self.agent_name : str = None
|
||||
self.prompt : AgentPrompt = None
|
||||
self.prompt : LLMPrompt = None
|
||||
self.introduce : str = None
|
||||
self.agent = None
|
||||
self.enable_function_list : list[str] = None
|
||||
@@ -31,7 +31,7 @@ class AIRole:
|
||||
|
||||
prompt_node = config.get("prompt")
|
||||
if prompt_node:
|
||||
self.prompt = AgentPrompt()
|
||||
self.prompt = LLMPrompt()
|
||||
if self.prompt.load_from_config(prompt_node) is False:
|
||||
logging.error("load prompt failed!")
|
||||
return False
|
||||
@@ -56,7 +56,7 @@ class AIRole:
|
||||
def get_name(self) -> str:
|
||||
return self.role_name
|
||||
|
||||
def get_prompt(self) -> AgentPrompt:
|
||||
def get_prompt(self) -> LLMPrompt:
|
||||
return self.prompt
|
||||
|
||||
class AIRoleGroup:
|
||||
|
||||
+15
-15
@@ -9,11 +9,11 @@ from abc import ABC, abstractmethod
|
||||
|
||||
from ..proto.compute_task import *
|
||||
from ..proto.agent_msg import *
|
||||
from ..proto.ai_function import *
|
||||
|
||||
from .agent_base import *
|
||||
from .chatsession import AIChatSession
|
||||
from .role import AIRole,AIRoleGroup
|
||||
from .ai_function import AIFunction,FunctionItem
|
||||
|
||||
from ..frame.compute_kernel import ComputeKernel
|
||||
from ..frame.bus import AIBus
|
||||
@@ -48,7 +48,7 @@ class Workflow:
|
||||
def __init__(self) -> None:
|
||||
self.workflow_name : str = None
|
||||
self.workflow_id : str = None
|
||||
self.rule_prompt : AgentPrompt = None
|
||||
self.rule_prompt : LLMPrompt = None
|
||||
self.workflow_config = None
|
||||
self.role_group : dict = None
|
||||
self.input_filter : MessageFilter= None
|
||||
@@ -83,7 +83,7 @@ class Workflow:
|
||||
self.db_file = self.owner_workflow.db_file
|
||||
|
||||
if config.get("prompt") is not None:
|
||||
self.rule_prompt = AgentPrompt()
|
||||
self.rule_prompt = LLMPrompt()
|
||||
if self.rule_prompt.load_from_config(config.get("prompt")) is False:
|
||||
logger.error("Workflow load prompt failed")
|
||||
return False
|
||||
@@ -279,7 +279,7 @@ class Workflow:
|
||||
logger.info(f"{msg.sender} post message {msg.msg_id} to AIBus: {msg.target}")
|
||||
return await self.get_bus().send_message(msg)
|
||||
|
||||
async def role_call(self,func_item:FunctionItem,the_role:AIRole):
|
||||
async def role_call(self,func_item:ActionItem,the_role:AIRole):
|
||||
logger.info(f"{the_role.role_id} call {func_item.name} ")
|
||||
arguments = func_item.args
|
||||
|
||||
@@ -290,11 +290,11 @@ class Workflow:
|
||||
result_str:str = await func_node.execute(**arguments)
|
||||
return result_str
|
||||
|
||||
async def role_post_call(self,func_item:FunctionItem,the_role:AIRole):
|
||||
async def role_post_call(self,func_item:ActionItem,the_role:AIRole):
|
||||
logger.info(f"{the_role.role_id} post call {func_item.name} ")
|
||||
return await self.role_call(func_item,the_role)
|
||||
|
||||
def _format_msg_by_env_value(self,prompt:AgentPrompt):
|
||||
def _format_msg_by_env_value(self,prompt:LLMPrompt):
|
||||
if self.workflow_env is None:
|
||||
return
|
||||
|
||||
@@ -326,7 +326,7 @@ class Workflow:
|
||||
return result_func
|
||||
return None
|
||||
|
||||
async def _role_execute_func(self,the_role:AIRole,inenr_func_call_node:dict,prompt:AgentPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]:
|
||||
async def _role_execute_func(self,the_role:AIRole,inenr_func_call_node:dict,prompt:LLMPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]:
|
||||
|
||||
func_name = inenr_func_call_node.get("name")
|
||||
arguments = json.loads(inenr_func_call_node.get("arguments"))
|
||||
@@ -372,7 +372,7 @@ class Workflow:
|
||||
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession) -> AgentMsg:
|
||||
msg.target = the_role.get_role_id()
|
||||
|
||||
prompt = AgentPrompt()
|
||||
prompt = LLMPrompt()
|
||||
prompt.append(the_role.agent.agent_prompt)
|
||||
prompt.append(self.get_workflow_rule_prompt())
|
||||
prompt.append(the_role.get_prompt())
|
||||
@@ -382,7 +382,7 @@ class Workflow:
|
||||
#support group chat, user content include sender name!
|
||||
prompt.append(await self._get_prompt_from_session(the_role,workflow_chat_session))
|
||||
|
||||
msg_prompt = AgentPrompt()
|
||||
msg_prompt = LLMPrompt()
|
||||
msg_prompt.messages = [{"role":"user","content":f"user name is {msg.sender}, his question is :{msg.body}"}]
|
||||
prompt.append(msg_prompt)
|
||||
|
||||
@@ -461,20 +461,20 @@ class Workflow:
|
||||
# message will be saved in role.process_message
|
||||
pass
|
||||
|
||||
this_llm_resp_prompt = AgentPrompt()
|
||||
this_llm_resp_prompt = LLMPrompt()
|
||||
this_llm_resp_prompt.messages = [{"role":"assistant","content":result_str}]
|
||||
prompt.append(this_llm_resp_prompt)
|
||||
|
||||
result_prompt = AgentPrompt()
|
||||
result_prompt = LLMPrompt()
|
||||
result_prompt.messages = [{"role":"user","content":result_prompt_str}]
|
||||
prompt.append(result_prompt)
|
||||
return await _do_process_msg()
|
||||
|
||||
return await _do_process_msg()
|
||||
|
||||
async def _get_prompt_from_session(self,the_role:AIRole,chatsession:AIChatSession) -> AgentPrompt:
|
||||
async def _get_prompt_from_session(self,the_role:AIRole,chatsession:AIChatSession) -> LLMPrompt:
|
||||
messages = chatsession.read_history(the_role.history_len) # read last 10 message
|
||||
result_prompt = AgentPrompt()
|
||||
result_prompt = LLMPrompt()
|
||||
|
||||
for msg in reversed(messages):
|
||||
if msg.sender == the_role.role_id:
|
||||
@@ -484,10 +484,10 @@ class Workflow:
|
||||
|
||||
return result_prompt
|
||||
|
||||
def _get_knowlege_prompt(self,role_name:str) -> AgentPrompt:
|
||||
def _get_knowlege_prompt(self,role_name:str) -> LLMPrompt:
|
||||
pass
|
||||
|
||||
def get_workflow_rule_prompt(self) -> AgentPrompt:
|
||||
def get_workflow_rule_prompt(self) -> LLMPrompt:
|
||||
return self.rule_prompt
|
||||
|
||||
# def _env_event_to_msg(self,env_event:EnvironmentEvent) -> AgentMsg:
|
||||
|
||||
Reference in New Issue
Block a user