Refactor chatsession impl, fix bug.

This commit is contained in:
Liu Zhicong
2023-09-14 01:50:18 -07:00
parent 25fec9a683
commit c4d97942cb
15 changed files with 477 additions and 236 deletions
+27 -1
View File
@@ -1,6 +1,6 @@
<mxfile host="65bd71144e" pages="3"> <mxfile host="65bd71144e" pages="3">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1"> <diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1881" dy="676" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="2069" dy="1139" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/> <mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/> <mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/>
@@ -365,4 +365,30 @@
</root> </root>
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>
<diagram id="zNgk-d6xdACtSja1wnUq" name="Page-4">
<mxGraphModel dx="2069" dy="1139" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="PKOyaMforMDOFoUY4PA1-1" value="ChatSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="140" y="150" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="PKOyaMforMDOFoUY4PA1-2" value="SubSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="230" y="240" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="PKOyaMforMDOFoUY4PA1-3" value="SubSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="230" y="330" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="PKOyaMforMDOFoUY4PA1-4" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
<mxGeometry x="600" y="150" width="130" height="60" as="geometry"/>
</mxCell>
<mxCell id="PKOyaMforMDOFoUY4PA1-5" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
<mxGeometry x="600" y="230" width="130" height="60" as="geometry"/>
</mxCell>
<mxCell id="PKOyaMforMDOFoUY4PA1-6" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
<mxGeometry x="600" y="320" width="130" height="60" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile> </mxfile>
+1
View File
@@ -1,5 +1,6 @@
instance_id = "agent_1" instance_id = "agent_1"
fullname = "tracy wang" fullname = "tracy wang"
[[prompt]] [[prompt]]
role = "system" role = "system"
content = "你是我的私人英文老师,和我用地道的美式英语进行交流。你会在和我交流的同时,调整我的输入成为更地道的美式句子,并根据你对我英文水平的预测,对可能发错英的单词标上音标。如果我给你发中文,说明我不知道这句话用美式英语怎么说,你依旧按上述规则回应我。" content = "你是我的私人英文老师,和我用地道的美式英语进行交流。你会在和我交流的同时,调整我的输入成为更地道的美式句子,并根据你对我英文水平的预测,对可能发错英的单词标上音标。如果我给你发中文,说明我不知道这句话用美式英语怎么说,你依旧按上述规则回应我。"
+1 -1
View File
@@ -7,7 +7,7 @@ GOAL="成为最好的学校"
[[connected_env]] [[connected_env]]
env_id = "calender" env_id = "calender"
[[connected_env.event2msg]] [[connected_env.event2msg]]
timer = "现在是{data}" timer = "现在是{now}"
role = "教导处主任" role = "教导处主任"
[filter] [filter]
+4 -2
View File
@@ -42,11 +42,13 @@ fullname = "经理"
agent="manager" agent="manager"
[[roles.manager.prompt]] [[roles.manager.prompt]]
role="system" role="system"
content="""你是一个活动策划公司的经理,与客户对接并向团队下达指令。你的团队分为下面几个小组:嘉宾对接组,酒店预定组,行程预订组,财务组,活动摄像组。活动策划分为四个阶段:方案讨论,活动前,活动中,活动后。你会根据客户的需求,对团队进行分工,分别完成各个阶段的工作。你的基本工作模式是: content="""
你是一个活动策划公司的经理,与客户对接并向团队下达指令。你的团队分为下面几个小组:嘉宾对接组,酒店预定组,行程预订组,财务组,活动摄像组。活动策划分为四个阶段:方案讨论,活动前,活动中,活动后。你会根据客户的需求,对团队进行分工,分别完成各个阶段的工作。你的基本工作模式是:
1. 收到客户的明确的指令后,基于客户的已有信息和客户商量活动方案,和活动策划公司无关的业务你会回答‘与我无关’。当和客户完成活动方案的确认后,你会将拆解后的任务分配给各个小组 1. 收到客户的明确的指令后,基于客户的已有信息和客户商量活动方案,和活动策划公司无关的业务你会回答‘与我无关’。当和客户完成活动方案的确认后,你会将拆解后的任务分配给各个小组
2. 根据目前已经确认的活动方案,你要根据时间适时的检查不同小组的工作情况。当收到小组的工作情况反馈后,你会站在全局的角度判断是否需要调整活动方案,如果需要调整,你会和客户商量重新确定方案,然后再将调整后的方案分配给各个小组。 2. 根据目前已经确认的活动方案,你要根据时间适时的检查不同小组的工作情况。当收到小组的工作情况反馈后,你会站在全局的角度判断是否需要调整活动方案,如果需要调整,你会和客户商量重新确定方案,然后再将调整后的方案分配给各个小组。
3. 有时工作小组会主动与你沟通,反馈一些问题。你会站在全局的角度给与指导,适当的调整工作小组的工作目标。如果反馈的问题需要你和客户沟通,你会和客户沟通后重新确定方案。再将调整后的方案分配给受到影响各个小组。 3. 有时工作小组会主动与你沟通,反馈一些问题。你会站在全局的角度给与指导,适当的调整工作小组的工作目标。如果反馈的问题需要你和客户沟通,你会和客户沟通后重新确定方案。再将调整后的方案分配给受到影响各个小组。
4. 当你决定要和工作小组通信时,请使用 sendmsg(小组名称,内容) 的形式。""" 4. 当你决定要和工作小组通信时,请使用 sendmsg(小组名称,内容) 的形式。
"""
+1 -1
View File
@@ -1,5 +1,5 @@
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .agent_message import AgentMsg,AgentMsgState from .agent_message import AgentMsg,AgentMsgStatus
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .agent import AIAgent,AIAgentTemplete,AgentPrompt from .agent import AIAgent,AIAgentTemplete,AgentPrompt
from .compute_kernel import ComputeKernel,ComputeTask from .compute_kernel import ComputeKernel,ComputeTask
+32 -57
View File
@@ -7,7 +7,7 @@ import uuid
import time import time
import json import json
from .agent_message import AgentMsg from .agent_message import AgentMsg, AgentMsgStatus, AgentMsgType
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .compute_task import ComputeTaskResult from .compute_task import ComputeTaskResult
from .ai_function import AIFunction from .ai_function import AIFunction
@@ -73,7 +73,7 @@ class AIAgent:
self.prompt:AgentPrompt = None self.prompt:AgentPrompt = None
self.llm_model_name:str = None self.llm_model_name:str = None
self.max_token_size:int = 3600 self.max_token_size:int = 3600
self.instance_id:str = None self.agent_id:str = None
self.template_id:str = None self.template_id:str = None
self.fullname:str = None self.fullname:str = None
self.powerby = None self.powerby = None
@@ -82,6 +82,7 @@ class AIAgent:
self.chat_db = None self.chat_db = None
self.unread_msg = Queue() # msg from other agent self.unread_msg = Queue() # msg from other agent
self.owner_env : Environment = None self.owner_env : Environment = None
self.owenr_bus : AIBus = None
@classmethod @classmethod
def create_from_templete(cls,templete:AIAgentTemplete, fullname:str): def create_from_templete(cls,templete:AIAgentTemplete, fullname:str):
@@ -90,7 +91,7 @@ class AIAgent:
result_agent.llm_model_name = templete.llm_model_name result_agent.llm_model_name = templete.llm_model_name
result_agent.max_token_size = templete.max_token_size result_agent.max_token_size = templete.max_token_size
result_agent.template_id = templete.template_id result_agent.template_id = templete.template_id
result_agent.instance_id = "agent#" + uuid.uuid4().hex result_agent.agent_id = "agent#" + uuid.uuid4().hex
result_agent.fullname = fullname result_agent.fullname = fullname
result_agent.powerby = templete.author result_agent.powerby = templete.author
result_agent.prompt = templete.prompt result_agent.prompt = templete.prompt
@@ -100,10 +101,10 @@ class AIAgent:
if config.get("instance_id") is None: if config.get("instance_id") is None:
logger.error("agent instance_id is None!") logger.error("agent instance_id is None!")
return False return False
self.instance_id = config["instance_id"] self.agent_id = config["instance_id"]
if config.get("fullname") is None: if config.get("fullname") is None:
logger.error(f"agent {self.instance_id} fullname is None!") logger.error(f"agent {self.agent_id} fullname is None!")
return False return False
self.fullname = config["fullname"] self.fullname = config["fullname"]
@@ -147,7 +148,7 @@ class AIAgent:
return result_func return result_func
async def _execute_func(self,inenr_func_call_node:dict,msg_prompt:AgentPrompt) -> str: async def _execute_func(self,inenr_func_call_node:dict,prompt:AgentPrompt,org_msg:AgentMsg) -> str:
from .compute_kernel import ComputeKernel from .compute_kernel import ComputeKernel
func_name = inenr_func_call_node.get("name") func_name = inenr_func_call_node.get("name")
@@ -157,14 +158,21 @@ class AIAgent:
if func_node is None: if func_node is None:
return "execute failed,function not found" return "execute failed,function not found"
ineternal_call_record = AgentMsg.create_internal_call_msg(func_name,arguments,org_msg.get_msg_id(),org_msg.target)
result_str:str = await func_node.execute(**arguments) result_str:str = await func_node.execute(**arguments)
inner_functions = self._get_inner_functions() inner_functions = self._get_inner_functions()
msg_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().do_llm_completion(msg_prompt,self.llm_model_name,self.max_token_size,inner_functions) task_result:ComputeTaskResult = await ComputeKernel().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
ineternal_call_record.result_str = task_result.result_str
ineternal_call_record.done_time = time.time()
org_msg.inner_call_chain.append(ineternal_call_record)
inner_func_call_node = task_result.result_message.get("function_call") inner_func_call_node = task_result.result_message.get("function_call")
if inner_func_call_node: if inner_func_call_node:
return await self._execute_func(inner_func_call_node,msg_prompt) return await self._execute_func(inner_func_call_node,prompt,org_msg)
else: else:
return task_result.result_str return task_result.result_str
@@ -172,7 +180,13 @@ class AIAgent:
from .compute_kernel import ComputeKernel from .compute_kernel import ComputeKernel
session_topic = msg.get_sender() + "#" + msg.topic session_topic = msg.get_sender() + "#" + msg.topic
chatsession = AIChatSession.get_session(self.instance_id,session_topic,self.chat_db) chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
if msg.mentions is not None:
if not self.agent_id in msg.mentions:
chatsession.append(msg)
logger.info(f"agent {self.agent_id} recv a group chat message from {msg.sender},but is not mentioned,ignore!")
return None
prompt = AgentPrompt() prompt = AgentPrompt()
prompt.append(self.prompt) prompt.append(self.prompt)
# prompt.append(self._get_knowlege_prompt(the_role.get_name())) # prompt.append(self._get_knowlege_prompt(the_role.get_name()))
@@ -189,67 +203,27 @@ class AIAgent:
inner_func_call_node = task_result.result_message.get("function_call") inner_func_call_node = task_result.result_message.get("function_call")
if inner_func_call_node: if inner_func_call_node:
final_result = await self._execute_func(inner_func_call_node,msg_prompt) #TODO to save more token ,can i use msg_prompt?
final_result = await self._execute_func(inner_func_call_node,prompt,msg)
result_type : str = self._get_llm_result_type(final_result) result_type : str = self._get_llm_result_type(final_result)
is_ignore = False is_ignore = False
match result_type: match result_type:
# case "function":
# callchain:CallChain = self._parse_function_call_chain(result)
# resp = await callchain.exec()
# if callchain.have_result():
# # generator proc resp prompt with WAITING state
# proc_resp_prompt:AgentPrompt = self._get_resp_prompt(resp,msg,the_role,prompt,chatsession)
# final_result = await ComputeKernel().do_llm_completion(proc_resp_prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size())
# return final_result
# case "send_message":
# # send message to other / sub workflow
# next_msg:AgentMsg = self._parse_to_msg(result)
# if next_msg is not None:
# # TODO: Next Target can be another role in workflow
# next_workflow:Workflow = self.get_workflow(next_msg.get_target())
# inner_chat_session = the_role.agent.get_chat_session(next_msg.get_target(),next_msg.get_session_id())
# inner_chat_session.append_post(next_msg)
# resp = await next_workflow.send_msg(next_msg)
# inner_chat_session.append_recv(resp)
# # generator proc resp prompt with WAITING state
# proc_resp_prompt:AgentPrompt = self._get_resp_prompt(resp,msg,the_role,prompt,chatsession)
# final_result = await ComputeKernel().do_llm_completion(proc_resp_prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size())
# return final_result
#case "post_message":
# # post message to other / sub workflow
# next_msg:AgentMsg = self._parse_to_msg(result)
# if next_msg is not None:
# next_workflow:Workflow = self.get_workflow(next_msg.get_target())
# inner_chat_session = the_role.agent.get_chat_session(next_msg.get_target(),next_msg.get_session_id())
# inner_chat_session.append_post(next_msg)
# next_workflow.post_msg(next_msg)
case "ignore": case "ignore":
is_ignore = True is_ignore = True
if is_ignore is not True: if is_ignore is not True:
# TODO : how to get inner chat session? resp_msg = msg.create_resp_msg(final_result)
resp_msg = AgentMsg() chatsession.append(msg)
resp_msg.set(self.instance_id,msg.sender,final_result) chatsession.append(resp_msg)
resp_msg.topic = msg.topic
if chatsession is not None:
chatsession.append_recv(msg)
chatsession.append_post(resp_msg)
return resp_msg return resp_msg
return None return None
def get_id(self) -> str: def get_id(self) -> str:
return self.instance_id return self.agent_id
def get_fullname(self) -> str: def get_fullname(self) -> str:
return self.fullname return self.fullname
@@ -263,7 +237,8 @@ class AIAgent:
def get_max_token_size(self) -> int: def get_max_token_size(self) -> int:
return self.max_token_size return self.max_token_size
async def _get_prompt_from_session(self,chatsession:AIChatSession) -> AgentPrompt: async def _get_prompt_from_session(self,chatsession:AIChatSession,is_groupchat=False) -> AgentPrompt:
# TODO: get prompt from group chat is different from single chat
messages = chatsession.read_history() # read last 10 message messages = chatsession.read_history() # read last 10 message
result_prompt = AgentPrompt() result_prompt = AgentPrompt()
for msg in reversed(messages): for msg in reversed(messages):
+88 -8
View File
@@ -3,26 +3,100 @@ import uuid
import time import time
import re import re
class AgentMsgState(Enum): class AgentMsgType(Enum):
TYPE_MSG = 0
TYPE_INTERNAL_CALL = 1
TYPE_ACTION = 2
TYPE_EVENT = 3
class AgentMsgStatus(Enum):
RESPONSED = 0 RESPONSED = 0
INIT = 1 INIT = 1
SENDING = 2 SENDING = 2
PROCESSING = 3 PROCESSING = 3
ERROR = 4 ERROR = 4
RECVED = 5
EXECUTED = 6
# msg is a msg / msg resp
# msg body可以有内容类型(MIME标签),text, image, voice, video, file,以及富文本(html)
# msg is a inner function call with result
# msg is a Action with result
# qutoe Msg
# forword msg
# reply msg
# 逻辑上的同一个Message在同一个session中看到的msgid相同
# 在不同的session中看到的msgid不同
class AgentMsg: class AgentMsg:
def __init__(self) -> None: def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None:
self.msg_id = ""
self.msg_type:AgentMsgType = msg_type
self.prev_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.session_id:str = None
self.create_time = 0 self.create_time = 0
self.sender:str = None self.done_time = 0
self.topic:str = None # topic is use to find session, not store in db
self.sender:str = None # obj_id.sub_objid@tunnel_id
self.target:str = None self.target:str = None
self.mentions:[] = None #use in group chat only
#self.title:str = None
self.body:str = None self.body:str = None
self.topic:str = "T#" + uuid.uuid4().hex self.body_mime:str = None #//default is "text/plain",encode is utf8
#self.msg_type = 0
self.state = AgentMsgState.INIT #type is call / action
self.func_name = None
self.args = None
self.result_str = None
#type is event
self.event_name = None
self.event_args = None
self.status = AgentMsgStatus.INIT
self.inner_call_chain = []
self.resp_msg = None self.resp_msg = None
@classmethod
def create_internal_call_msg(self,func_name:str,args:dict,prev_msg_id:str,caller:str):
msg = AgentMsg(AgentMsgType.TYPE_INTERNAL_CALL)
msg.func_name = func_name
msg.args = args
msg.prev_msg_id = prev_msg_id
msg.sender = caller
return msg
def create_action_msg(self,action_name:str,args:dict,caller:str):
msg = AgentMsg(AgentMsgType.TYPE_ACTION)
msg.func_name = action_name
msg.args = args
msg.prev_msg_id = self.msg_id
msg.topic = self.topic
msg.sender = caller
return msg
def create_resp_msg(self,resp_body):
resp_msg = AgentMsg()
resp_msg.msg_id = "msg#" + uuid.uuid4().hex
self.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id
resp_msg.sender = self.target
resp_msg.target = self.sender
resp_msg.body = resp_body
resp_msg.topic = self.topic
return resp_msg
def set(self,sender:str,target:str,body:str,topic:str=None) -> None: def set(self,sender:str,target:str,body:str,topic:str=None) -> None:
self.id = "msg#" + uuid.uuid4().hex self.msg_id = "msg#" + uuid.uuid4().hex
self.sender = sender self.sender = sender
self.target = target self.target = target
self.body = body self.body = body
@@ -31,7 +105,7 @@ class AgentMsg:
self.topic = topic self.topic = topic
def get_msg_id(self) -> str: def get_msg_id(self) -> str:
return self.id return self.msg_id
def get_sender(self) -> str: def get_sender(self) -> str:
return self.sender return self.sender
@@ -39,6 +113,12 @@ 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:
return self.prev_msg_id
def get_quote_msg_id(self) -> str:
return self.quote_msg_id
@classmethod @classmethod
def parse_function_call(cls,func_string:str): def parse_function_call(cls,func_string:str):
match = re.search(r'\s*(\w+)\s*\(\s*(.*)\s*\)\s*', func_string) match = re.search(r'\s*(\w+)\s*\(\s*(.*)\s*\)\s*', func_string)
+43 -46
View File
@@ -1,5 +1,5 @@
from typing import Any from typing import Coroutine,Dict,Any
from .agent_message import AgentMsg,AgentMsgState from .agent_message import AgentMsg,AgentMsgStatus
import asyncio import asyncio
from asyncio import Queue from asyncio import Queue
@@ -8,19 +8,32 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AIBusHandler: class AIBusHandler:
def __init__(self,handler:Any) -> None: def __init__(self,handler:Coroutine,owner_bus,enable_defualt_proc=True) -> None:
self.handler = handler self.handler = handler
self.working_task = None self.working_task = None
self.results = {} self.results = {} # recv resps
self.queue:Queue = Queue() self.queue:Queue = Queue()
self.enable_defualt_proc = enable_defualt_proc
self.owner_bus = owner_bus
async def handle_message(self,msg:AgentMsg) -> Any: async def handle_message(self,msg:AgentMsg) -> Any:
if self.handler is None: if self.handler is None:
return None return None
return await self.handler(msg) if self.enable_defualt_proc:
# do default process
if msg.rely_msg_id is not None:
self.results[msg.rely_msg_id] = msg
return None
resp_msg = await self.handler(msg)
if self.enable_defualt_proc:
if resp_msg is not None:
await self.owner_bus.post_message(resp_msg,False)
return resp_msg
class AIBus: class AIBus:
_instance = None _instance = None
@classmethod @classmethod
@@ -30,11 +43,13 @@ class AIBus:
return cls._instance return cls._instance
def __init__(self) -> None: def __init__(self) -> None:
self.handlers = {} self.handlers:Dict[AIBusHandler] = {}
self.unhandle_handler = None self.unhandle_handler:Coroutine = None
async def post_message(self,msg:AgentMsg,use_unhandle=True) -> bool:
target_id = msg.target.split(".")[0]
async def post_message(self,target_id,msg:AgentMsg,use_unhandle=True) -> bool:
target_id = target_id.split(".")[0]
handler = self.handlers.get(target_id) handler = self.handlers.get(target_id)
if handler: if handler:
handler.queue.put_nowait(msg) handler.queue.put_nowait(msg)
@@ -44,46 +59,39 @@ class AIBus:
if use_unhandle: if use_unhandle:
if self.unhandle_handler is not None: if self.unhandle_handler is not None:
if await self.unhandle_handler(self,msg): if await self.unhandle_handler(self,msg):
return await self.post_message(target_id,msg,False) return await self.post_message(msg,False)
logger.warn(f"post message to {msg.target} failed!,target not found") logger.warn(f"post message to {msg.target} failed!,target not found")
return False return False
def resp_message(self,my_id:str,org_msg_id:str,resp:AgentMsg) -> None: async def resp_message(self,org_msg_id:str,resp:AgentMsg) -> None:
handler = self.handlers.get(my_id) assert resp.rely_msg_id == org_msg_id
if handler is None: return await self.post_message(resp)
return None
handler.results[org_msg_id] = resp
async def get_message_resp(self,name:str,msg_id:str) -> AgentMsg: async def send_message(self,msg:AgentMsg) -> AgentMsg:
handler = self.handlers.get(name) sender_id = msg.sender.split(".")[0]
if handler is None: sender_handler = self.handlers.get(sender_id) # sender already register on bus
if sender_handler is None:
logger.warn(f"sender {sender_id} not register on AI_BUS!")
return None return None
return handler.results.get(msg_id) post_result = await self.post_message(msg)
async def send_message(self,target_id:str,msg:AgentMsg) -> AgentMsg:
target_id = target_id.split(".")[0]
post_result = await self.post_message(target_id,msg)
if post_result is False: if post_result is False:
return None return None
handler = self.handlers.get(target_id)
if handler is None:
return None
retry_times = 0 retry_times = 0
while True: while True:
resp = handler.results.get(msg.id) resp = sender_handler.results.get(msg.msg_id)
if resp is not None: if resp is not None:
msg.resp_msg = resp msg.resp_msg = resp
msg.state = AgentMsgState.RESPONSED msg.status = AgentMsgStatus.RESPONSED
del sender_handler.results[msg.msg_id]
return resp return resp
await asyncio.sleep(0.2) await asyncio.sleep(0.2)
retry_times += 1 retry_times += 1
if retry_times > 100: if retry_times > 100:
msg.state = AgentMsgState.ERROR msg.status = AgentMsgStatus.ERROR
return None return None
return None return None
@@ -93,7 +101,7 @@ class AIBus:
# means sub # means sub
def register_message_handler(self,handler_name:str,handler:Any) -> Queue: def register_message_handler(self,handler_name:str,handler:Any) -> Queue:
handler_node = AIBusHandler(handler) handler_node = AIBusHandler(handler,self)
self.handlers[handler_name] = handler_node self.handlers[handler_name] = handler_node
return handler_node.queue return handler_node.queue
@@ -102,13 +110,12 @@ class AIBus:
# Wait for a message # Wait for a message
message = await handler.queue.get() message = await handler.queue.get()
try: #try:
# Try to handle the message # Try to handle the message
result = await handler.handle_message(message) await handler.handle_message(message)
handler.results[message.id] = result #except Exception as e:
except Exception as e:
# If an error occurs, put the message back into the queue # If an error occurs, put the message back into the queue
logger.error(f"handle message {message.id} failed! {e}") # logger.error(f"handle message {message.msg_id} failed! {e}")
#self.queues[name].put_nowait(message) #self.queues[name].put_nowait(message)
return return
@@ -127,13 +134,3 @@ class AIBus:
return return
handler.working_task = asyncio.create_task(self.process_queue(handler)) handler.working_task = asyncio.create_task(self.process_queue(handler))
#send message to target logic:
# find target handler:
# process_message(msg):
# session = get_session(msg.sender,msg.target)
# history: open(sender,target,topic)
+73 -25
View File
@@ -5,8 +5,9 @@ import logging
import threading import threading
import datetime import datetime
import uuid import uuid
import json
from .agent_message import AgentMsg from .agent_message import AgentMsgType, AgentMsg, AgentMsgStatus
class ChatSessionDB: class ChatSessionDB:
def __init__(self, db_file): def __init__(self, db_file):
@@ -54,14 +55,31 @@ class ChatSessionDB:
""") """)
# create messages table # create messages table
# reciver_id could be None
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS Messages ( CREATE TABLE IF NOT EXISTS Messages (
MessageID TEXT PRIMARY KEY, MessageID TEXT PRIMARY KEY,
SessionID TEXT, SessionID TEXT,
MsgType INTEGER,
PrevMsgID TEXT,
QuoteMsgID TEXT,
RelyMsgID TEXT,
SenderID TEXT, SenderID TEXT,
ReceiverID TEXT, ReceiverID TEXT,
Timestamp TEXT, Timestamp TEXT,
Topic TEXT,
Mentions TEXT,
ContentMIME TEXT,
Content TEXT, Content TEXT,
ActionName TEXT,
ActionParams TEXT,
ActionResult TEXT,
DoneTime TEXT,
Status INTEGER Status INTEGER
); );
""") """)
@@ -83,15 +101,43 @@ class ChatSessionDB:
logging.error("Error occurred while inserting session: %s", e) logging.error("Error occurred while inserting session: %s", e)
return -1 # return -1 if an error occurs return -1 # return -1 if an error occurs
def insert_message(self, message_id, session_id, sender_id, receiver_id, timestamp, content, status): def insert_message(self, msg:AgentMsg):
""" insert a new message into the Messages table """ """ insert a new message into the Messages table """
try: try:
action_name = None
action_params = None
action_result = None
mentions = None
if msg.mentions:
mentions = json.dumps(msg.mentions)
match msg.msg_type:
case AgentMsgType.TYPE_MSG:
pass
case AgentMsgType.TYPE_ACTION:
action_name = msg.func_name
action_params = json.dumps(msg.args)
action_result = msg.result_str
case AgentMsgType.TYPE_INTERNAL_CALL:
action_name = msg.func_name
action_params = json.dumps(msg.args)
action_result = msg.result_str
case AgentMsgType.TYPE_EVENT:
action_name = msg.event_name
action_params = json.dumps(msg.event_args)
conn = self._get_conn() conn = self._get_conn()
conn.execute(""" conn.execute("""
INSERT INTO Messages (MessageID, SessionID, SenderID, ReceiverID, Timestamp, Content, Status) INSERT INTO Messages (MessageID, SessionID, MsgType, PrevMsgID, SenderID, ReceiverID, Timestamp, Topic,Mentions,ContentMIME,Content,ActionName,ActionParams,ActionResult,DoneTime,Status)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (message_id, session_id, sender_id, receiver_id, timestamp, content, status)) """, (msg.msg_id, msg.session_id, msg.msg_type.value, msg.prev_msg_id, msg.sender, msg.target, msg.create_time, msg.topic,mentions,msg.body_mime,msg.body,action_name,action_params,action_result,msg.done_time,msg.status.value))
conn.commit() conn.commit()
if msg.inner_call_chain:
for inner_call in msg.inner_call_chain:
self.insert_message(inner_call)
return 0 # return 0 if successful return 0 # return 0 if successful
except Error as e: except Error as e:
logging.error("Error occurred while inserting message: %s", e) logging.error("Error occurred while inserting message: %s", e)
@@ -134,7 +180,7 @@ class ChatSessionDB:
"""Get a message by its ID""" """Get a message by its ID"""
conn =self._get_conn() conn =self._get_conn()
c = conn.cursor() c = conn.cursor()
c.execute("SELECT MessageID,SessionID,SenderID,ReceiverID,Timestamp,Content,Status FROM Messages WHERE MessageID = ?", (message_id,)) c.execute("SELECT MessageID, SessionID, MsgType, PrevMsgID, SenderID, ReceiverID, Timestamp, Topic,Mentions,ContentMIME,Content,ActionName,ActionParams,ActionResult,DoneTime,Status FROM Messages WHERE MessageID = ?", (message_id,))
message = c.fetchone() message = c.fetchone()
return message return message
@@ -144,7 +190,7 @@ class ChatSessionDB:
conn = self._get_conn() conn = self._get_conn()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(""" cursor.execute("""
SELECT MessageID,SessionID,SenderID,ReceiverID,Timestamp,Content,Status FROM Messages SELECT MessageID, SessionID, MsgType, PrevMsgID, SenderID, ReceiverID, Timestamp, Topic,Mentions,ContentMIME,Content,ActionName,ActionParams,ActionResult,DoneTime,Status FROM Messages
WHERE SessionID = ? WHERE SessionID = ?
ORDER BY Timestamp DESC ORDER BY Timestamp DESC
LIMIT ? OFFSET ? LIMIT ? OFFSET ?
@@ -222,29 +268,31 @@ class AIChatSession:
result = [] result = []
for msg in msgs: for msg in msgs:
agent_msg = AgentMsg() agent_msg = AgentMsg()
agent_msg.id = msg[0] agent_msg.msg_id = msg[0]
agent_msg.sender = msg[2] agent_msg.session_id = msg[1]
agent_msg.target = msg[3] agent_msg.msg_type = AgentMsgType(msg[2])
agent_msg.create_time = msg[4] agent_msg.prev_msg_id = msg[3]
agent_msg.body = msg[5] agent_msg.sender = msg[4]
# agent_msg.state = msg[6] agent_msg.target = msg[5]
agent_msg.create_time = msg[6]
agent_msg.topic = msg[7]
if msg[8] is not None:
agent_msg.mentions = json.loads(msg[8])
agent_msg.body_mime = msg[9]
agent_msg.body = msg[10]
agent_msg.func_name = msg[11]
if msg[12] is not None:
agent_msg.args = json.loads(msg[12])
agent_msg.result_str = msg[13]
agent_msg.done_time = msg[14]
agent_msg.status = AgentMsgStatus(msg[15])
result.append(agent_msg) result.append(agent_msg)
return result return result
def append(self,msg:AgentMsg) -> None: def append(self,msg:AgentMsg) -> None:
self.db.insert_message(msg.id,self.session_id,msg.sender,msg.target,msg.create_time,msg.body,0) msg.session_id = self.session_id
self.db.insert_message(msg)
def append_post(self,msg:AgentMsg) -> None:
"""append msg to session, msg is post from session (owner => msg.target)"""
#assert msg.sender == self.owner_id,"post message means msg.sender == self.owner_id"
self.append(msg)
def append_recv(self,msg:AgentMsg) -> None:
"""append msg to session, msg is recv from msg'sender (msg.sender => owner)"""
#assert msg.target == self.owner_id,"recv message means msg.target == self.owner_id"
self.append(msg)
#def attach_event_handler(self,handler) -> None: #def attach_event_handler(self,handler) -> None:
# """chat session changed event handler""" # """chat session changed event handler"""
-6
View File
@@ -70,12 +70,6 @@ class Environment:
def _do_get_value(self,key:str) -> Optional[str]: def _do_get_value(self,key:str) -> Optional[str]:
pass pass
def get_functions(self):
# system functions
# env functions
# user install functions
pass
def register_get_handler(self,key:str,handler:Callable) -> None: def register_get_handler(self,key:str,handler:Callable) -> None:
h = self.get_handlers.get(key) h = self.get_handlers.get(key)
if h is not None: if h is not None:
+9 -3
View File
@@ -54,11 +54,17 @@ class OpenAI_ComputeNode(ComputeNode):
prompts = task.params["prompts"] prompts = task.params["prompts"]
logger.info(f"call openai {mode_name} prompts: {prompts}") logger.info(f"call openai {mode_name} prompts: {prompts}")
resp = openai.ChatCompletion.create(model=mode_name, if task.params.get("inner_functions") is None:
resp = openai.ChatCompletion.create(model=mode_name,
messages=prompts, messages=prompts,
functions=task.params["inner_functions"],
max_tokens=task.params["max_token_size"], max_tokens=task.params["max_token_size"],
temperature=0.7) # TODO: add temperature to task params? temperature=0.7)
else:
resp = openai.ChatCompletion.create(model=mode_name,
messages=prompts,
functions=task.params["inner_functions"],
max_tokens=task.params["max_token_size"],
temperature=0.7) # TODO: add temperature to task params?
logger.info(f"openai response: {resp}") logger.info(f"openai response: {resp}")
+170 -70
View File
@@ -2,21 +2,23 @@ import logging
import asyncio import asyncio
import json import json
import os import os
import time
from asyncio import Queue from asyncio import Queue
from typing import Optional,Tuple from typing import Optional,Tuple
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .agent_message import AgentMsg,AgentMsgState from .agent_message import AgentMsg,AgentMsgStatus
from .agent import AgentPrompt,AgentMsg from .agent import AgentPrompt,AgentMsg
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .role import AIRole,AIRoleGroup from .role import AIRole,AIRoleGroup
from .ai_function import CallChain from .ai_function import AIFunction
from .compute_kernel import ComputeKernel from .compute_kernel import ComputeKernel
from .compute_task import ComputeTask,ComputeTaskResult,ComputeTaskState from .compute_task import ComputeTask,ComputeTaskResult,ComputeTaskState
from .bus import AIBus from .bus import AIBus
from .workflow_env import WorkflowEnvironment from .workflow_env import WorkflowEnvironment
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MessageFilter: class MessageFilter:
@@ -158,11 +160,12 @@ class Workflow:
while i < len(inner_obj_id): while i < len(inner_obj_id):
if i == len(inner_obj_id) - 1: if i == len(inner_obj_id) - 1:
the_role : AIRole = current_workflow.role_group.get(inner_obj_id[i]) the_role : AIRole = current_workflow.role_group.get(inner_obj_id[i])
current_workflow_chatsession = AIChatSession.get_session(current_workflow.workflow_id,msg.sender + "#" + msg.topic,current_workflow.db_file)
if the_role is not None: if the_role is not None:
return await current_workflow.role_process_msg(msg,the_role) return await current_workflow.role_process_msg(msg,the_role,current_workflow_chatsession)
sub_workflow = current_workflow.sub_workflows.get(inner_obj_id[i]) sub_workflow = current_workflow.sub_workflows.get(inner_obj_id[i])
if sub_workflow is not None: if sub_workflow is not None:
return await sub_workflow.process_msg(msg) return await sub_workflow._process_msg(msg)
logger.error(f"{msg.target} not found! forword message failed!") logger.error(f"{msg.target} not found! forword message failed!")
return None return None
else: else:
@@ -176,17 +179,49 @@ class Workflow:
logger.error(f"{msg.target} not found! forword message failed!") logger.error(f"{msg.target} not found! forword message failed!")
return None return None
def get_workflow_id_from_target(self,target:str) -> str:
target_list = target.split(".")
if len(target_list) == 0:
return target
else:
result_str = ""
p = 0
for s in target_list:
p = p + 1
result_str += s
if p < len(target_list)-1:
result_str += "."
else:
return result_str
async def _process_msg(self,msg:AgentMsg): async def _process_msg(self,msg:AgentMsg):
# workflow can be a message handler, but never be a message sender real_target = msg.target.split(".")[0]
# all message forword to roles or sub workflow
# workflow no chatsession record, but role have
final_result = None
targets = self._parse_msg_target(msg.target) targets = self._parse_msg_target(msg.target)
if len(targets) > 1: if len(targets) > 1:
return await self._forword_msg(targets,msg) return await self._forword_msg(targets,msg)
#0 we don't support workflow join a group right now, this cloud be a feture in future
if msg.mentions is not None:
logger.warn(f"workflow {self.workflow_id} recv a group chat message,not support ignore!")
return None
#1. workflow start process message
final_result = None
# this is workflow's group_chat session
session_topic = msg.sender + "#" + msg.topic
chatsesssion = AIChatSession.get_session(self.workflow_id,session_topic,self.db_file)
#2. find role by msg.mentions or workflow's selector logic
if msg.mentions is not None:
if not self.workflow_id in msg.mentions:
chatsesssion.append(msg)
logger.info(f"workflow {self.workflow_id} recv a group chat message from {msg.sender},but is not mentioned,ignore!")
return None
for mention in msg.mentions:
this_role = self.role_group.get(mention)
if this_role is not None:
return await self.role_process_msg(msg,this_role,chatsesssion)
if self.input_filter is not None: if self.input_filter is not None:
select_role_id = self.input_filter.select(msg) select_role_id = self.input_filter.select(msg)
@@ -196,26 +231,12 @@ class Workflow:
logger.error(f"input_filter return invalid role id:{select_role_id}, role not found in role_group") logger.error(f"input_filter return invalid role id:{select_role_id}, role not found in role_group")
return None return None
result = await self.role_process_msg(msg,select_role) return await self.role_process_msg(msg,select_role,chatsesssion)
if result is None:
logger.error(f"_process_msg return None for :{msg}")
return
final_result = result
else: else:
logger.error(f"input_filter return None for :{msg}") logger.error(f"input_filter return None for :{msg.body}")
return return None
else:
# no input filter, we would process all message, slowly,not suggest to use
results = {}
final_result:AgentMsg = None
for this_role in self.role_group.roles.values():
# TODO : we would do this in parallel
a_result = await self.role_process_msg(msg,this_role)
results[this_role.get_name()] = a_result
final_result = a_result
logger.error(f"{self.workflow_id}:no role can process this msg:{msg.body}")
return final_result return final_result
@classmethod @classmethod
@@ -276,52 +297,59 @@ class Workflow:
return r return r
async def role_post_msg(self,msg:AgentMsg,the_role:AIRole): async def role_post_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession):
msg.sender = the_role.get_role_id() msg.sender = the_role.get_role_id()
target_role = self.role_group.get(msg.target) target_role = self.role_group.get(msg.target)
if target_role: if target_role:
msg.target = target_role.get_role_id() msg.target = target_role.get_role_id()
logger.info(f"{msg.sender} post message {msg.id} to inner role: {msg.target}") logger.info(f"{msg.sender} post message {msg.msg_id} to inner role: {msg.target}")
asyncio.create_task(self.role_process_msg(msg,target_role)) asyncio.create_task(self.role_process_msg(msg,target_role,workflow_chat_session))
return return
target_workflow = self.sub_workflows.get(msg.target) target_workflow = self.sub_workflows.get(msg.target)
if target_workflow: if target_workflow:
msg.target = target_workflow.workflow_id msg.target = target_workflow.workflow_id
logger.info(f"{msg.sender} post message {msg.id} to sub workflow: {msg.target}") logger.info(f"{msg.sender} post message {msg.msg_id} to sub workflow: {msg.target}")
asyncio.create_task(target_workflow._process_msg(msg)) asyncio.create_task(target_workflow._process_msg(msg))
logger.info(f"{msg.sender} post message {msg.id} to AIBus: {msg.target}") logger.info(f"{msg.sender} post message {msg.msg_id} to AIBus: {msg.target}")
await self.get_bus().post_message(msg.target,msg) await self.get_bus().post_message(msg.target,msg)
return return
async def role_send_msg(self,msg:AgentMsg,the_role:AIRole): async def role_send_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession):
msg.sender = the_role.get_role_id() msg.sender = the_role.get_role_id()
target_role = self.role_group.get(msg.target) target_role = self.role_group.get(msg.target)
if target_role: if target_role:
# msg.target = target_role.get_role_id() # msg.target = target_role.get_role_id()
logger.info(f"{msg.sender} send message {msg.id} to inner role: {msg.target}") logger.info(f"{msg.sender} send message {msg.msg_id} to inner role: {msg.target}")
return await self.role_process_msg(msg,target_role) return await self.role_process_msg(msg,target_role,workflow_chat_session)
target_workflow = self.sub_workflows.get(msg.target) target_workflow = self.sub_workflows.get(msg.target)
if target_workflow: if target_workflow:
# msg.target = target_workflow.workflow_id # msg.target = target_workflow.workflow_id
logger.info(f"{msg.sender} send message {msg.id} to sub workflow: {msg.target}") logger.info(f"{msg.sender} send message {msg.msg_id} to sub workflow: {msg.target}")
return await target_workflow._process_msg(msg) return await target_workflow._process_msg(msg)
logger.info(f"{msg.sender} post message {msg.id} to AIBus: {msg.target}") logger.info(f"{msg.sender} post message {msg.msg_id} to AIBus: {msg.target}")
return await self.get_bus().send_message(msg.target,msg) return await self.get_bus().send_message(msg)
async def role_call(self,call:tuple,the_role:AIRole): async def role_call(self,call:tuple,the_role:AIRole):
logger.info(f"{the_role.role_id} call {call[0]} with args {call[1]}") logger.info(f"{the_role.role_id} call {call[0]} with args {call[1]}")
return """{result:"timeout"}""" func_name = call[0]
arguments = call[1]
func_node : AIFunction = self.workflow_env.get_ai_function(func_name)
if func_node is None:
return "execute failed,function not found"
result_str:str = await func_node.execute(**arguments)
return result_str
async def role_post_call(self,call:tuple,the_role:AIRole): async def role_post_call(self,call:tuple,the_role:AIRole):
logger.info(f"{the_role.role_id} post call {call[0]} with args {call[1]}") logger.info(f"{the_role.role_id} post call {call[0]} with args {call[1]}")
return return await self.role_call(call,the_role)
def _format_msg_by_env_value(self,prompt:AgentPrompt): def _format_msg_by_env_value(self,prompt:AgentPrompt):
if self.workflow_env is None: if self.workflow_env is None:
@@ -331,66 +359,148 @@ class Workflow:
old_content = msg.get("content") old_content = msg.get("content")
msg["content"] = old_content.format_map(self.workflow_env) msg["content"] = old_content.format_map(self.workflow_env)
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole): def _get_inner_functions(self) -> dict:
session_topic = f"{msg.sender}#{msg.topic}" all_inner_function = self.workflow_env.get_all_ai_functions()
session_owner = the_role.get_role_id() if all_inner_function is None:
chatsession : AIChatSession = AIChatSession.get_session(session_owner,session_topic,self.db_file)
if chatsession is None:
logger.error(f"get session {session_topic}@{session_owner} failed!")
return None return None
result_func = []
for inner_func in all_inner_function:
this_func = {}
this_func["name"] = inner_func.get_name()
this_func["description"] = inner_func.get_description()
this_func["parameters"] = inner_func.get_parameters()
result_func.append(this_func)
if len(result_func) > 0:
return result_func
return None
async def _role_execute_func(self,the_role:AIRole,inenr_func_call_node:dict,prompt:AgentPrompt,org_msg:AgentMsg) -> str:
from .compute_kernel import ComputeKernel
func_name = inenr_func_call_node.get("name")
arguments = json.loads(inenr_func_call_node.get("arguments"))
func_node : AIFunction = self.workflow_env.get_ai_function(func_name)
if func_node is None:
return "execute failed,function not found"
ineternal_call_record = AgentMsg.create_internal_call_msg(func_name,arguments,org_msg.get_msg_id(),org_msg.target)
result_str:str = await func_node.execute(**arguments)
inner_functions = self._get_inner_functions()
prompt.messages.append({"role":"function","content":result_str,"name":func_name})
task_result:ComputeTaskResult = await ComputeKernel().do_llm_completion(prompt,
the_role.agent.llm_model_name,the_role.agent.max_token_size,
inner_functions)
ineternal_call_record.result_str = task_result.result_str
ineternal_call_record.done_time = time.time()
org_msg.inner_call_chain.append(ineternal_call_record)
inner_func_call_node = task_result.result_message.get("function_call")
if inner_func_call_node:
return await self._role_execute_func(the_role,inner_func_call_node,prompt,org_msg)
else:
return task_result.result_str
def _is_in_same_workflow(self,msg) -> bool:
pass
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession):
msg.target = the_role.get_role_id()
prompt = AgentPrompt() prompt = AgentPrompt()
prompt.append(the_role.agent.prompt) prompt.append(the_role.agent.prompt)
prompt.append(self.get_workflow_rule_prompt()) prompt.append(self.get_workflow_rule_prompt())
prompt.append(the_role.get_prompt()) prompt.append(the_role.get_prompt())
# prompt.append(self._get_function_prompt(the_role.get_name())) # prompt.append(self._get_function_prompt(the_role.get_name()))
# prompt.append(self._get_knowlege_prompt(the_role.get_name())) # prompt.append(self._get_knowlege_prompt(the_role.get_name()))
prompt.append(await self._get_prompt_from_session(chatsession))
#support group chat, user content include sender name!
prompt.append(await self._get_prompt_from_session(workflow_chat_session))
msg_prompt = AgentPrompt() msg_prompt = AgentPrompt()
msg_prompt.messages = [{"role":"user","content":msg.body}] msg_prompt.messages = [{"role":"user","content":f"{msg.sender}:{msg.body}"}]
prompt.append(msg_prompt) prompt.append(msg_prompt)
self._format_msg_by_env_value(prompt) self._format_msg_by_env_value(prompt)
inner_functions = self._get_inner_functions()
async def _do_process_msg(): async def _do_process_msg():
#TODO: send msg to agent might be better? #TODO: send msg to agent might be better?
task_result:ComputeTaskResult = await ComputeKernel().do_llm_completion(prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size()) task_result:ComputeTaskResult = await ComputeKernel().do_llm_completion(prompt,the_role.agent.get_llm_model_name(),the_role.agent.get_max_token_size(),inner_functions)
result_str = task_result.result_str result_str = task_result.result_str
result = Workflow.prase_llm_result(result_str)
logger.info(f"{the_role.role_id} process {msg.sender}:{msg.body},llm str is :{result_str}") logger.info(f"{the_role.role_id} process {msg.sender}:{msg.body},llm str is :{result_str}")
inner_func_call_node = task_result.result_message.get("function_call")
if inner_func_call_node:
#TODO to save more token ,can i use msg_prompt?
result_str = await self._role_execute_func(the_role,inner_func_call_node,prompt,msg)
result = Workflow.prase_llm_result(result_str)
for postmsg in result.post_msgs: for postmsg in result.post_msgs:
postmsg.prev_msg_id = msg.get_msg_id()
# might be craete a new msg.topic for this postmsg
postmsg.topic = msg.topic postmsg.topic = msg.topic
await self.role_post_msg(postmsg,the_role)
await self.role_post_msg(postmsg,the_role,workflow_chat_session)
if not self._is_in_same_workflow(postmsg):
role_sesion = AIChatSession.get_session(the_role.get_role_id(),f"{postmsg.target}#{msg.topic}",self.db_file)
role_sesion.append(postmsg)
else:
# message will be saved in role.process_message
pass
for post_call in result.post_calls: for post_call in result.post_calls:
action_msg = msg.create_action_msg(post_call[0],post_call[1],the_role.get_role_id())
workflow_chat_session.append(action_msg)
await self.role_post_call(post_call,the_role) await self.role_post_call(post_call,the_role)
#save post_call
result_prompt_str = "" result_prompt_str = ""
match result.state: match result.state:
case "ignore": case "ignore":
return None return None
case "reponsed": case "reponsed":
resp_msg = AgentMsg() resp_msg = msg.create_resp_msg(result.resp)
resp_msg.topic = msg.topic resp_msg.sender = the_role.get_role_id()
resp_msg.set(session_owner,msg.sender,result.resp) # It is always the person handling the messages who puts them into the session.
workflow_chat_session.append(msg)
chatsession.append_recv(msg) workflow_chat_session.append(resp_msg)
chatsession.append_post(resp_msg) #await self.get_bus().resp_message(resp_msg)
return resp_msg return resp_msg
case "waiting": case "waiting":
# TODO: Use role:"function" would be better # TODO: Use role:"function" would be better
for sendmsg in result.send_msgs: for sendmsg in result.send_msgs:
target = sendmsg.target target = sendmsg.target
sendmsg.topic = msg.topic sendmsg.topic = msg.topic
send_resp = await self.role_send_msg(sendmsg,the_role) sendmsg.prev_msg_id = msg.get_msg_id()
send_resp = await self.role_send_msg(sendmsg,the_role,workflow_chat_session)
if send_resp is not None: if send_resp is not None:
result_prompt_str += f"\n{target} response is :{send_resp.body}" result_prompt_str += f"\n{target} response is :{send_resp.body}"
if not self._is_in_same_workflow(sendmsg):
role_sesion = AIChatSession.get_session(the_role.get_role_id(),f"{sendmsg.target}#{sendmsg.topic}",self.db_file)
role_sesion.append(sendmsg)
role_sesion.append(send_resp)
else:
# message will be saved in role.process_message
pass
for call in result.calls: for call in result.calls:
action_msg = msg.create_action_msg(call[0],call[1],call_result,the_role.get_role_id)
call_result = await self.role_call(call,the_role) call_result = await self.role_call(call,the_role)
if call_result is not None: if call_result is not None:
result_prompt_str += f"\ncall {call[0]} result is :{call_result}" result_prompt_str += f"\ncall {call[0]} result is :{call_result}"
#save action
action_msg.result_str = call_result
workflow_chat_session.append(action_msg)
result_prompt = AgentPrompt() result_prompt = AgentPrompt()
result_prompt.messages = [{"role":"user","content":result_prompt_str}] result_prompt.messages = [{"role":"user","content":result_prompt_str}]
@@ -411,17 +521,9 @@ class Workflow:
return result_prompt return result_prompt
def _get_function_prompt(self,role_name:str) -> AgentPrompt:
# system functions
# env functions
# user install functions
pass
def _get_knowlege_prompt(self,role_name:str) -> AgentPrompt: def _get_knowlege_prompt(self,role_name:str) -> AgentPrompt:
pass pass
def get_workflow_rule_prompt(self) -> AgentPrompt: def get_workflow_rule_prompt(self) -> AgentPrompt:
return self.rule_prompt return self.rule_prompt
@@ -447,6 +549,4 @@ class Workflow:
# the_env.attach_event_handler(k,_env_msg_handler) # the_env.attach_event_handler(k,_env_msg_handler)
# break # break
else:
logger.warn(f"environment {env.get_id()} already connected!")
+8 -2
View File
@@ -29,7 +29,7 @@ class CalenderEnvironment(Environment):
self.add_ai_function(SimpleAIFunction("get_time", self.add_ai_function(SimpleAIFunction("get_time",
"get current time", "get current time",
self.get_now)) self._get_now))
def _do_get_value(self,key:str) -> Optional[str]: def _do_get_value(self,key:str) -> Optional[str]:
return None return None
@@ -58,7 +58,12 @@ class CalenderEnvironment(Environment):
def stop(self): def stop(self):
self.is_run = False self.is_run = False
async def get_now(self) -> str: def get_now(self,key)->str:
now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
return formatted_time
async def _get_now(self) -> str:
now = datetime.now() now = datetime.now()
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S') formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
return formatted_time return formatted_time
@@ -71,6 +76,7 @@ class WorkflowEnvironment(Environment):
self.local = threading.local() self.local = threading.local()
self.table_name = "WorkflowEnv_" + env_id self.table_name = "WorkflowEnv_" + env_id
def _get_conn(self): def _get_conn(self):
""" get db connection """ """ get db connection """
if not hasattr(self.local, 'conn'): if not hasattr(self.local, 'conn'):
+9 -3
View File
@@ -26,7 +26,7 @@ shell_style = Style.from_dict({
directory = os.path.dirname(__file__) directory = os.path.dirname(__file__)
sys.path.append(directory + '/../../') sys.path.append(directory + '/../../')
from aios_kernel import Workflow,AIAgent,AgentMsg,AgentMsgState,ComputeKernel,OpenAI_ComputeNode,AIBus,AIChatSession from aios_kernel import Workflow,AIAgent,AgentMsg,AgentMsgStatus,ComputeKernel,OpenAI_ComputeNode,AIBus,AIChatSession
sys.path.append(directory + '/../../component/') sys.path.append(directory + '/../../component/')
from agent_manager import AgentManager from agent_manager import AgentManager
@@ -73,6 +73,7 @@ class AIOS_Shell:
open_ai_node.start() open_ai_node.start()
ComputeKernel().add_compute_node(open_ai_node) ComputeKernel().add_compute_node(open_ai_node)
AIBus().get_default_bus().register_unhandle_message_handler(self._handle_no_target_msg) AIBus().get_default_bus().register_unhandle_message_handler(self._handle_no_target_msg)
AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
return True return True
@@ -83,7 +84,7 @@ class AIOS_Shell:
agent_msg = AgentMsg() agent_msg = AgentMsg()
agent_msg.set(sender,target_id,msg) agent_msg.set(sender,target_id,msg)
agent_msg.topic = topic agent_msg.topic = topic
resp = await AIBus.get_default_bus().send_message(target_id,agent_msg) resp = await AIBus.get_default_bus().send_message(agent_msg)
if resp is not None: if resp is not None:
return resp.body return resp.body
else: else:
@@ -92,6 +93,9 @@ class AIOS_Shell:
async def install_workflow(self,workflow_id:Workflow) -> None: async def install_workflow(self,workflow_id:Workflow) -> None:
pass pass
async def _user_process_msg(self,msg:AgentMsg) -> AgentMsg:
pass
async def call_func(self,func_name, args): async def call_func(self,func_name, args):
match func_name: match func_name:
case 'send': case 'send':
@@ -115,6 +119,7 @@ class AIOS_Shell:
case 'login': case 'login':
if len(args) >= 1: if len(args) >= 1:
self.username = args[0] self.username = args[0]
AIBus().get_default_bus().register_message_handler(self.username,self._user_process_msg)
return self.username + " login success!" return self.username + " login success!"
case 'history': case 'history':
num = 10 num = 10
@@ -134,7 +139,7 @@ class AIOS_Shell:
if chatsession is not None: if chatsession is not None:
msgs = chatsession.read_history(num,offset) msgs = chatsession.read_history(num,offset)
format_texts = [] format_texts = []
for msg in reversed(msgs): for msg in msgs:
format_texts.append(("class:content",f"{msg.sender} >>> {msg.body}")) format_texts.append(("class:content",f"{msg.sender} >>> {msg.body}"))
format_texts.append(("",f"\n-------------------\n")) format_texts.append(("",f"\n-------------------\n"))
return FormattedText(format_texts) return FormattedText(format_texts)
@@ -171,6 +176,7 @@ async def main():
format='[%(asctime)s]%(name)s[%(levelname)s]: %(message)s') format='[%(asctime)s]%(name)s[%(levelname)s]: %(message)s')
shell = AIOS_Shell("user") shell = AIOS_Shell("user")
await shell.initial() await shell.initial()
print(f"aios shell {shell.get_version()} ready.") print(f"aios shell {shell.get_version()} ready.")
completer = WordCompleter(['send($target,$msg,$topic)', completer = WordCompleter(['send($target,$msg,$topic)',
+1 -1
View File
@@ -14,7 +14,7 @@ from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandl
directory = os.path.dirname(__file__) directory = os.path.dirname(__file__)
sys.path.append(directory + '/../../') sys.path.append(directory + '/../../')
from aios_kernel import Workflow,AIAgent,AgentMsg,AgentMsgState,ComputeKernel,OpenAI_ComputeNode,AIBus,AIChatSession from aios_kernel import Workflow,AIAgent,AgentMsg,AgentMsgStatus,ComputeKernel,OpenAI_ComputeNode,AIBus,AIChatSession
sys.path.append(directory + '/../../component/') sys.path.append(directory + '/../../component/')
from agent_manager import AgentManager from agent_manager import AgentManager