Refactor before imporve knowledge base.

This commit is contained in:
Liu Zhicong
2023-10-18 11:19:11 -07:00
parent 760087d945
commit b74b86b4d4
19 changed files with 683 additions and 445 deletions
+150
View File
@@ -0,0 +1,150 @@
<mxfile host="Electron" modified="2023-10-10T07:47:12.198Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="ooen5M4lIGDak3P-0D5_" version="22.0.2" type="device" pages="5">
<diagram name="Init" id="eZ0Fsh2vPRb0vo3J8o-a">
<mxGraphModel dx="2286" dy="1168" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="IzO59fDZ9m1edTcSyALi-11" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="90" width="155" height="60" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-9" value="" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.containers.userFemale;strokeColor=#666666;strokeColor2=#008cff;" parent="1" vertex="1">
<mxGeometry x="45" y="95" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-12" value="Main Page" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="150" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-13" value="AIGC" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="210" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-20" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.rect;fillColor2=none;strokeWidth=1;size=20;indent=5;" parent="1" vertex="1">
<mxGeometry x="195" y="90" width="565" height="420" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-21" value="My Devices" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="180" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-22" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
<mxGeometry x="312.5" y="130" width="340" height="340" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-23" value="Config LLM Kernel:" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="320" y="145" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-24" value="Config Owner Info:" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="320" y="220" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-25" value="Done" style="strokeWidth=1;shadow=0;dashed=0;align=center;html=1;shape=mxgraph.mockup.buttons.button;strokeColor=#666666;fontColor=#ffffff;mainText=;buttonStyle=round;fontSize=17;fontStyle=1;fillColor=#008cff;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="420" y="410" width="150" height="50" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-26" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="240" width="155" height="270" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-27" value="&lt;font style=&quot;font-size: 10px;&quot;&gt;Version 0.5.2&lt;br&gt;http://www.opendan.ai&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="480" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-28" value="" style="shape=mxgraph.signs.science.nuclear_2;html=1;pointerEvents=1;fillColor=#000000;strokeColor=none;verticalLabelPosition=bottom;verticalAlign=top;align=center;sketch=0;" parent="1" vertex="1">
<mxGeometry x="45" y="155.5" width="20" height="19" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-31" value="" style="shape=mxgraph.signs.tech.ethernet;html=1;pointerEvents=1;fillColor=#000000;strokeColor=none;verticalLabelPosition=bottom;verticalAlign=top;align=center;sketch=0;" parent="1" vertex="1">
<mxGeometry x="45" y="190" width="20.63" height="10" as="geometry" />
</mxCell>
<mxCell id="IzO59fDZ9m1edTcSyALi-32" value="" style="shape=mxgraph.signs.travel.information_2;html=1;pointerEvents=1;fillColor=#000000;strokeColor=none;verticalLabelPosition=bottom;verticalAlign=top;align=center;sketch=0;" parent="1" vertex="1">
<mxGeometry x="49.900000000000006" y="216" width="10.84" height="18" as="geometry" />
</mxCell>
<mxCell id="l8k4_rfZMrNWla8Lv80Y-1" value="AI Store" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="40" y="240" width="155" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="EX4l-TP_pYMqB1o-9Pjg" name="MainPage">
<mxGraphModel dx="1577" dy="806" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="A7pxehO7BnMPWpYIlLSW-1" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.rect;fillColor2=none;strokeWidth=1;size=20;indent=5;" vertex="1" parent="1">
<mxGeometry x="97.5" y="66" width="565" height="580" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-3" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="127.5" y="231" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-2" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="142.5" y="201" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-4" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="227.5" y="231" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-5" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="242.5" y="201" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-6" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="327.5" y="231" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="342.5" y="201" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-8" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="422.5" y="231" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-9" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="437.5" y="201" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-10" value="" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="517.5" y="231" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-11" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
<mxGeometry x="532.5" y="201" width="50" height="50" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-12" value="My Agents:" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="117.5" y="171" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-17" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shadow=0;dashed=0;strokeWidth=1;shape=mxgraph.android.quickscroll3;dy=0.5;fillColor=#33b5e5;strokeColor=#66D5F5;rotation=90;" vertex="1" parent="1">
<mxGeometry x="380.5" y="66" width="6" height="520" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-19" value="" style="shape=cross;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="610.5" y="175" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="A7pxehO7BnMPWpYIlLSW-20" value="System Stauts" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="120" y="80" width="520" height="80" as="geometry" />
</mxCell>
<mxCell id="38aqMLvnZxr78S6UhFXi-2" value="" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="127.5" y="740" width="497" height="240" as="geometry" />
</mxCell>
<mxCell id="38aqMLvnZxr78S6UhFXi-3" value="Agent Detials:" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="298.5" y="750" width="139" height="30" as="geometry" />
</mxCell>
<mxCell id="38aqMLvnZxr78S6UhFXi-4" value="My Knowledge Base:" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="117.5" y="330" width="132.5" height="30" as="geometry" />
</mxCell>
<mxCell id="38aqMLvnZxr78S6UhFXi-6" value="Logs/Command Line" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="117.5" y="570" width="122.5" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="x4dUm2vkSZsrNgmtw8U3" name="Devices">
<mxGraphModel dx="2286" dy="1168" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="nG_j26PwBAeOLUIgazIM-1" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.rect;fillColor2=none;strokeWidth=1;size=20;indent=5;" vertex="1" parent="1">
<mxGeometry x="130" y="80" width="565" height="580" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="5KzlG5ec7zi2r4DX32yK" name="AIGC">
<mxGraphModel dx="2286" dy="1168" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
</root>
</mxGraphModel>
</diagram>
<diagram id="qqyI6l9TxLyHAeHWymlS" name="AI Store">
<mxGraphModel dx="2286" dy="1168" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
</root>
</mxGraphModel>
</diagram>
</mxfile>
-1
View File
@@ -8,5 +8,4 @@ max_token_size=4000
role = "system" role = "system"
content = """ content = """
Your name is Lachlan, and you are my advanced private Spanish tutor. Your name is Lachlan, and you are my advanced private Spanish tutor.
You are also a local guide familiar with the history of the Inca Empire. While teaching me Spanish, you will introduce some related historical and cultural origins.
""" """
+2 -2
View File
@@ -1,7 +1,7 @@
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .agent_message import AgentMsg,AgentMsgStatus,AgentMsgType from .agent_base import AgentMsg,AgentMsgStatus,AgentMsgType,AgentPrompt
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .agent import AIAgent,AIAgentTemplete,AgentPrompt from .agent import AIAgent,AIAgentTemplete
from .compute_kernel import ComputeKernel,ComputeTask,ComputeTaskResult,ComputeTaskState,ComputeTaskType from .compute_kernel import ComputeKernel,ComputeTask,ComputeTaskResult,ComputeTaskState,ComputeTaskType
from .compute_node import ComputeNode,LocalComputeNode from .compute_node import ComputeNode,LocalComputeNode
from .open_ai_node import OpenAI_ComputeNode from .open_ai_node import OpenAI_ComputeNode
+123 -188
View File
@@ -10,73 +10,20 @@ import shlex
import datetime import datetime
import copy import copy
from .agent_message import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt
from .chatsession import AIChatSession from .chatsession import AIChatSession
from .compute_task import ComputeTaskResult,ComputeTaskResultCode from .compute_task import ComputeTaskResult,ComputeTaskResultCode
from .ai_function import AIFunction from .ai_function import AIFunction
from .environment import Environment from .environment import Environment
from .contact_manager import ContactManager,Contact,FamilyMember from .contact_manager import ContactManager,Contact,FamilyMember
from .knowledge_base import KnowledgeBase
from .compute_kernel import ComputeKernel
from .bus import AIBus
from knowledge import *
logger = logging.getLogger(__name__) 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 get_prompt_token_len(self):
result = 0
if self.system_message:
result += len(self.system_message.get("content"))
for msg in self.messages:
result += len(msg.get("content"))
return result
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("role") == "system":
self.system_message = msg
else:
self.messages.append(msg)
return True
class AIAgentTemplete: class AIAgentTemplete:
def __init__(self) -> None: def __init__(self) -> None:
@@ -106,10 +53,13 @@ class AIAgentTemplete:
class AIAgent: class AIAgent:
def __init__(self) -> None: def __init__(self) -> None:
self.role_prompt:AgentPrompt = None
self.agent_prompt:AgentPrompt = None self.agent_prompt:AgentPrompt = None
self.agent_think_prompt:AgentPrompt = None self.agent_think_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.agent_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
@@ -122,6 +72,9 @@ class AIAgent:
self.contact_prompt_str = None self.contact_prompt_str = None
self.history_len = 10 self.history_len = 10
self.learn_token_limit = 500
self.learn_prompt = None
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
@@ -189,77 +142,31 @@ class AIAgent:
if config.get("history_len"): if config.get("history_len"):
self.history_len = int(config.get("history_len")) self.history_len = int(config.get("history_len"))
return True return True
def get_id(self) -> str:
return self.agent_id
def get_fullname(self) -> str:
return self.fullname
def _get_llm_result_type(self,llm_result_str:str) -> LLMResult: def get_template_id(self) -> str:
r = LLMResult() return self.template_id
if llm_result_str is None:
r.state = "ignore"
return r
if llm_result_str == "ignore":
r.state = "ignore"
return r
lines = llm_result_str.splitlines() def get_llm_model_name(self) -> str:
is_need_wait = False return self.llm_model_name
def check_args(func_item:FunctionItem): def get_max_token_size(self) -> int:
match func_name: return self.max_token_size
case "send_msg":# sendmsg($target_id,$msg_content)
if len(func_args) != 1: def get_llm_learn_token_limit(self) -> int:
logger.error(f"parse sendmsg failed! {func_name}") return self.learn_token_limit
return False
new_msg = AgentMsg() def get_learn_prompt(self) -> AgentPrompt:
target_id = func_item.args[0] return self.learn_prompt
msg_content = func_item.body
new_msg.set(self.agent_id,target_id,msg_content) def get_agent_role_prompt(self) -> AgentPrompt:
return self.role_prompt
r.send_msgs.append(new_msg)
is_need_wait = True
case "post_msg":# postmsg($target_id,$msg_content)
if len(func_args) != 1:
logger.error(f"parse postmsg failed! {func_name}")
return False
new_msg = AgentMsg()
target_id = func_item.args[0]
msg_content = func_item.body
new_msg.set(self.agent_id,target_id,msg_content)
r.post_msgs.append(new_msg)
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
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
def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt: def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt:
cm = ContactManager.get_instance() cm = ContactManager.get_instance()
@@ -314,18 +221,18 @@ class AIAgent:
return result_func,result_len return result_func,result_len
async def _execute_func(self,inenr_func_call_node:dict,prompt:AgentPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]: async def _execute_func(self,inner_func_call_node:dict,prompt:AgentPrompt,inner_functions,org_msg:AgentMsg=None,stack_limit = 5) -> ComputeTaskResult:
from .compute_kernel import ComputeKernel func_name = inner_func_call_node.get("name")
arguments = json.loads(inner_func_call_node.get("arguments"))
func_name = inenr_func_call_node.get("name")
arguments = json.loads(inenr_func_call_node.get("arguments"))
logger.info(f"llm execute inner func:{func_name} ({json.dumps(arguments)})") logger.info(f"llm execute inner func:{func_name} ({json.dumps(arguments)})")
func_node : AIFunction = self.owner_env.get_ai_function(func_name) func_node : AIFunction = self.owner_env.get_ai_function(func_name)
if func_node is None: if func_node is None:
result_str = f"execute {func_name} error,function not found" result_str = f"execute {func_name} error,function not found"
else: else:
ineternal_call_record = AgentMsg.create_internal_call_msg(func_name,arguments,org_msg.get_msg_id(),org_msg.target) if org_msg:
ineternal_call_record = AgentMsg.create_internal_call_msg(func_name,arguments,org_msg.get_msg_id(),org_msg.target)
try: try:
result_str:str = await func_node.execute(**arguments) result_str:str = await func_node.execute(**arguments)
except Exception as e: except Exception as e:
@@ -334,27 +241,29 @@ class AIAgent:
logger.info("llm execute inner func result:" + result_str) logger.info("llm execute inner func result:" + result_str)
inner_functions,inner_function_len = self._get_inner_functions()
prompt.messages.append({"role":"function","content":result_str,"name":func_name}) prompt.messages.append({"role":"function","content":result_str,"name":func_name})
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"llm compute error:{task_result.error_str}") logger.error(f"llm compute error:{task_result.error_str}")
return task_result.error_str,1 return task_result
ineternal_call_record.result_str = task_result.result_str ineternal_call_record.result_str = task_result.result_str
ineternal_call_record.done_time = time.time() ineternal_call_record.done_time = time.time()
org_msg.inner_call_chain.append(ineternal_call_record) if org_msg:
org_msg.inner_call_chain.append(ineternal_call_record)
inner_func_call_node = None
if stack_limit > 0: if stack_limit > 0:
result_message = task_result.result.get("message") result_message : dict = task_result.result.get("message")
if result_message: if result_message:
inner_func_call_node = result_message.get("function_call") inner_func_call_node = result_message.get("function_call")
if inner_func_call_node: if inner_func_call_node:
return await self._execute_func(inner_func_call_node,prompt,org_msg,stack_limit-1) return await self._execute_func(inner_func_call_node,prompt,org_msg,stack_limit-1)
else: else:
return task_result.result_str,0 return task_result
async def _get_agent_prompt(self) -> AgentPrompt: async def _get_agent_prompt(self) -> AgentPrompt:
return self.agent_prompt return self.agent_prompt
@@ -384,12 +293,12 @@ class AIAgent:
#4) advanced: reload all chatrecord,and think the topic of message. #4) advanced: reload all chatrecord,and think the topic of message.
#5) some topic could be end(not be thinked in futured ) #5) some topic could be end(not be thinked in futured )
return return
async def think_chatsession(self,session_id): async def think_chatsession(self,session_id):
if self.agent_think_prompt is None: if self.agent_think_prompt is None:
return return
logger.info(f"agent {self.agent_id} think session {session_id}") logger.info(f"agent {self.agent_id} think session {session_id}")
from .compute_kernel import ComputeKernel
chatsession = AIChatSession.get_session_by_id(session_id,self.chat_db) chatsession = AIChatSession.get_session_by_id(session_id,self.chat_db)
while True: while True:
@@ -420,10 +329,7 @@ class AIAgent:
return return
async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg: async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg:
from .compute_kernel import ComputeKernel
from .bus import AIBus
session_topic = msg.target + "#" + msg.topic session_topic = msg.target + "#" + msg.topic
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db) chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
need_process = False need_process = False
@@ -453,26 +359,13 @@ class AIAgent:
prompt.append(msg_prompt) prompt.append(msg_prompt)
logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ") logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) task_result = await self._do_llm_complection(prompt,inner_functions,msg)
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"llm compute error:{task_result.error_str}")
error_resp = msg.create_error_resp(task_result.error_str) error_resp = msg.create_error_resp(task_result.error_str)
return error_resp return error_resp
final_result = task_result.result_str final_result = task_result.result_str
llm_result : LLMResult = LLMResult.from_str(final_result)
result_message = task_result.result.get("message")
if result_message:
inner_func_call_node = result_message.get("function_call")
if inner_func_call_node:
#TODO to save more token ,can i use msg_prompt?
call_prompt : AgentPrompt = copy.deepcopy(prompt)
final_result,error_code = await self._execute_func(inner_func_call_node,call_prompt,msg)
if error_code != 0:
error_resp = msg.create_error_resp(final_result)
return error_resp
llm_result : LLMResult = self._get_llm_result_type(final_result)
is_ignore = False is_ignore = False
result_prompt_str = "" result_prompt_str = ""
match llm_result.state: match llm_result.state:
@@ -481,6 +374,7 @@ class AIAgent:
case "waiting": case "waiting":
for sendmsg in llm_result.send_msgs: for sendmsg in llm_result.send_msgs:
target = sendmsg.target target = sendmsg.target
sendmsg.sender = self.agent_id
sendmsg.topic = msg.topic sendmsg.topic = msg.topic
sendmsg.prev_msg_id = msg.get_msg_id() sendmsg.prev_msg_id = msg.get_msg_id()
send_resp = await AIBus.get_default_bus().send_message(sendmsg) send_resp = await AIBus.get_default_bus().send_message(sendmsg)
@@ -502,16 +396,12 @@ class AIAgent:
return None return None
async def _process_msg(self,msg:AgentMsg) -> AgentMsg: async def _process_msg(self,msg:AgentMsg) -> AgentMsg:
from .compute_kernel import ComputeKernel
from .bus import AIBus
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG: if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
return await self._process_group_chat_msg(msg) return await self._process_group_chat_msg(msg)
session_topic = msg.get_sender() + "#" + msg.topic session_topic = msg.get_sender() + "#" + msg.topic
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db) chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
msg_prompt = AgentPrompt() msg_prompt = AgentPrompt()
msg_prompt.messages = [{"role":"user","content":msg.body}] msg_prompt.messages = [{"role":"user","content":msg.body}]
@@ -530,26 +420,15 @@ class AIAgent:
prompt.append(msg_prompt) prompt.append(msg_prompt)
logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ") logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions) #task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
task_result = await self._do_llm_complection(prompt,inner_functions,msg)
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"llm compute error:{task_result.error_str}")
error_resp = msg.create_error_resp(task_result.error_str) error_resp = msg.create_error_resp(task_result.error_str)
return error_resp return error_resp
final_result = task_result.result_str final_result = task_result.result_str
result_message = task_result.result.get("message") llm_result : LLMResult = LLMResult.from_str(final_result)
if result_message:
inner_func_call_node = result_message.get("function_call")
if inner_func_call_node:
#TODO to save more token ,can i use msg_prompt?
call_prompt : AgentPrompt = copy.deepcopy(prompt)
final_result,error_code = await self._execute_func(inner_func_call_node,call_prompt,msg)
if error_code != 0:
error_resp = msg.create_error_resp(final_result)
return error_resp
llm_result : LLMResult = self._get_llm_result_type(final_result)
is_ignore = False is_ignore = False
result_prompt_str = "" result_prompt_str = ""
match llm_result.state: match llm_result.state:
@@ -557,6 +436,7 @@ class AIAgent:
is_ignore = True is_ignore = True
case "waiting": case "waiting":
for sendmsg in llm_result.send_msgs: for sendmsg in llm_result.send_msgs:
sendmsg.sender = self.agent_id
target = sendmsg.target target = sendmsg.target
sendmsg.topic = msg.topic sendmsg.topic = msg.topic
sendmsg.prev_msg_id = msg.get_msg_id() sendmsg.prev_msg_id = msg.get_msg_id()
@@ -578,20 +458,7 @@ class AIAgent:
return None return None
def get_id(self) -> str:
return self.agent_id
def get_fullname(self) -> str:
return self.fullname
def get_template_id(self) -> str:
return self.template_id
def get_llm_model_name(self) -> str:
return self.llm_model_name
def get_max_token_size(self) -> int:
return self.max_token_size
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int): async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int):
history_len = (self.max_token_size * 0.7) - system_token_len history_len = (self.max_token_size * 0.7) - system_token_len
@@ -660,6 +527,74 @@ class AIAgent:
return result_prompt,result_token_len return result_prompt,result_token_len
async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict,org_msg:AgentMsg=None) -> ComputeTaskResult:
from .compute_kernel import ComputeKernel
#logger.debug(f"Agent {self.agent_id} do llm token static system:{system_prompt_len},function:{function_token_len},history:{history_token_len},input:{input_len}, totoal prompt:{system_prompt_len + function_token_len + history_token_len} ")
task_result:ComputeTaskResult = await ComputeKernel.get_instance().do_llm_completion(prompt,self.llm_model_name,self.max_token_size,inner_functions)
if task_result.result_code != ComputeTaskResultCode.OK:
logger.error(f"llm compute error:{task_result.error_str}")
#error_resp = msg.create_error_resp(task_result.error_str)
return task_result
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 : AgentPrompt = copy.deepcopy(prompt)
task_result = await self._execute_func(inner_func_call_node,call_prompt,inner_functions,org_msg)
return task_result
def parser_learn_llm_result(self,llm_result:str):
pass
async def _llm_read_article(self,kb:KnowledgeBase,item:KnowledgeObject) -> ComputeTaskResult:
#kb_env = KnowledgeBaseFileSystemEnvironment()
full_content = item.get_article_full_content()
full_content_len = ComputeKernel.llm_num_tokens_from_text(full_content,self.get_llm_model_name())
if full_content_len < self.get_llm_learn_token_limit():
# 短文章不用总结catelog
#path_list,summary = llm_get_summary(summary,full_content)
prompt = self.get_agent_role_prompt()
learn_prompt = self.get_learn_prompt()
cotent_prompt = AgentPrompt(full_content)
prompt.append(learn_prompt)
prompt.append(cotent_prompt)
env_functions = self._get_inner_functions()
task_result:ComputeTaskResult = await self._do_llm_complection(prompt,env_functions)
if task_result.result_code != ComputeTaskResultCode.OK:
return task_result
path_list,summary = self.parser_learn_llm_result(task_result.result_str)
else:
# 用传统方法对文章进行一些处理,目的是尽可能减少LLM调用的次数
catelog = item.get_articl_catelog()
chunk_content = full_content.read(self.get_llm_learn_token_limit())
summary = kb.try_get_summary(catelog,full_content)
while chunk_content is not None:
#path_list,summarycatelog = llm_get_summary(summary,chunk_content)
#learn_prompt = self.get_learn_prompt_with_summary()
prompt = AgentPrompt("summary")
learn_prompt.append(prompt)
prompt = AgentPrompt(chunk_content)
learn_prompt.append(prompt)
#llm_result = self.do_llm_competion(learn_prompt)
#path_list,summary,catelog = parser_learn_llm_result(llm_result)
#chunk_content = full_content.read(self.get_llm_learn_token_limit())
kb.insert_item(path_list,item,catelog,summary)
async def _get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt: async def _get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt:
# TODO: get prompt from group chat is different from single chat # TODO: get prompt from group chat is different from single chat
+317
View File
@@ -0,0 +1,317 @@
import copy
import logging
from enum import Enum
import uuid
import time
import re
import shlex
from typing import List
from .ai_function import FunctionItem
logger = logging.getLogger(__name__)
class AgentMsgType(Enum):
TYPE_MSG = 0
TYPE_GROUPMSG = 1
TYPE_INTERNAL_CALL = 10
TYPE_ACTION = 20
TYPE_EVENT = 30
TYPE_SYSTEM = 40
class AgentMsgStatus(Enum):
RESPONSED = 0
INIT = 1
SENDING = 2
PROCESSING = 3
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:
def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None:
self.msg_id = "msg#" + uuid.uuid4().hex
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
#forword info
self.create_time = 0
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.mentions:[] = None #use in group chat only
#self.title:str = None
self.body:str = None
self.body_mime:str = None #//default is "text/plain",encode is utf8
#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
@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.create_time = time.time()
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.create_time = time.time()
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_error_resp(self,error_msg:str):
resp_msg = AgentMsg(AgentMsgType.TYPE_SYSTEM)
resp_msg.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id
resp_msg.body = error_msg
resp_msg.topic = self.topic
resp_msg.sender = self.target
resp_msg.target = self.sender
return resp_msg
def create_resp_msg(self,resp_body):
resp_msg = AgentMsg()
resp_msg.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 create_group_resp_msg(self,sender_id,resp_body):
resp_msg = AgentMsg(AgentMsgType.TYPE_GROUPMSG)
resp_msg.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id
resp_msg.target = self.target
resp_msg.sender = sender_id
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:
self.sender = sender
self.target = target
self.body = body
self.create_time = time.time()
if topic:
self.topic = topic
def get_msg_id(self) -> str:
return self.msg_id
def get_sender(self) -> str:
return self.sender
def get_target(self) -> str:
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
def parse_function_call(cls,func_string:str):
str_list = shlex.split(func_string)
func_name = str_list[0]
params = str_list[1:]
return func_name, params
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 get_prompt_token_len(self):
result = 0
if self.system_message:
result += len(self.system_message.get("content"))
for msg in self.messages:
result += len(msg.get("content"))
return result
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("role") == "system":
self.system_message = msg
else:
self.messages.append(msg)
return True
class LLMResult:
def __init__(self) -> None:
self.state : str = "ignore"
self.resp : str = ""
self.paragraphs : dict[str,FunctionItem] = []
self.post_msgs : List[AgentMsg] = []
self.send_msgs : List[AgentMsg] = []
self.calls : List[FunctionItem] = []
self.post_calls : List[FunctionItem] = []
@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
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 BaseAIAgent:
def __init__(self) -> None:
pass
-169
View File
@@ -1,169 +0,0 @@
from enum import Enum
import uuid
import time
import re
import shlex
from typing import List
from .ai_function import FunctionItem
class AgentMsgType(Enum):
TYPE_MSG = 0
TYPE_GROUPMSG = 1
TYPE_INTERNAL_CALL = 10
TYPE_ACTION = 20
TYPE_EVENT = 30
TYPE_SYSTEM = 40
class AgentMsgStatus(Enum):
RESPONSED = 0
INIT = 1
SENDING = 2
PROCESSING = 3
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:
def __init__(self,msg_type=AgentMsgType.TYPE_MSG) -> None:
self.msg_id = "msg#" + uuid.uuid4().hex
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
#forword info
self.create_time = 0
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.mentions:[] = None #use in group chat only
#self.title:str = None
self.body:str = None
self.body_mime:str = None #//default is "text/plain",encode is utf8
#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
@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.create_time = time.time()
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.create_time = time.time()
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_error_resp(self,error_msg:str):
resp_msg = AgentMsg(AgentMsgType.TYPE_SYSTEM)
resp_msg.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id
resp_msg.body = error_msg
resp_msg.topic = self.topic
resp_msg.sender = self.target
resp_msg.target = self.sender
return resp_msg
def create_resp_msg(self,resp_body):
resp_msg = AgentMsg()
resp_msg.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 create_group_resp_msg(self,sender_id,resp_body):
resp_msg = AgentMsg(AgentMsgType.TYPE_GROUPMSG)
resp_msg.create_time = time.time()
resp_msg.rely_msg_id = self.msg_id
resp_msg.target = self.target
resp_msg.sender = sender_id
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:
self.sender = sender
self.target = target
self.body = body
self.create_time = time.time()
if topic:
self.topic = topic
def get_msg_id(self) -> str:
return self.msg_id
def get_sender(self) -> str:
return self.sender
def get_target(self) -> str:
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
def parse_function_call(cls,func_string:str):
str_list = shlex.split(func_string)
func_name = str_list[0]
params = str_list[1:]
return func_name, params
class LLMResult:
def __init__(self) -> None:
self.state : str = "ignore"
self.resp : str = ""
self.post_msgs : List[AgentMsg] = []
self.send_msgs : List[AgentMsg] = []
self.calls : List[FunctionItem] = []
self.post_calls : List[FunctionItem] = []
+1 -1
View File
@@ -1,5 +1,5 @@
from typing import Coroutine,Dict,Any from typing import Coroutine,Dict,Any
from .agent_message import AgentMsg,AgentMsgStatus,AgentMsgType from .agent_base import AgentMsg,AgentMsgStatus,AgentMsgType
import asyncio import asyncio
from asyncio import Queue from asyncio import Queue
+1 -1
View File
@@ -7,7 +7,7 @@ import datetime
import uuid import uuid
import json import json
from .agent_message import AgentMsgType, AgentMsg, AgentMsgStatus from .agent_base import AgentMsgType, AgentMsg, AgentMsgStatus
class ChatSessionDB: class ChatSessionDB:
def __init__(self, db_file): def __init__(self, db_file):
+15 -1
View File
@@ -3,10 +3,12 @@ import random
from typing import Optional from typing import Optional
import logging import logging
import asyncio import asyncio
import tiktoken
from asyncio import Queue from asyncio import Queue
from knowledge import ObjectID from knowledge import ObjectID
from .agent import AgentPrompt from .agent_base import AgentPrompt
from .compute_node import ComputeNode from .compute_node import ComputeNode
from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult, ComputeTaskType,ComputeTaskResultCode from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult, ComputeTaskType,ComputeTaskResultCode
@@ -104,6 +106,18 @@ class ComputeKernel:
def is_task_support(self, task: ComputeTask) -> bool: def is_task_support(self, task: ComputeTask) -> bool:
return True return True
@staticmethod
def llm_num_tokens_from_text(text:str,model:str) -> int:
try:
encoding = tiktoken.encoding_for_model(model)
except KeyError:
logger.debug("Warning: model not found. Using cl100k_base encoding.")
encoding = tiktoken.get_encoding("cl100k_base")
token_count = len(encoding.encode(text))
return token_count
# friendly interface for use: # friendly interface for use:
def llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None): def llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None):
# craete a llm_work_task ,push on queue's end # craete a llm_work_task ,push on queue's end
+1 -1
View File
@@ -8,7 +8,7 @@ import logging
import time import time
import datetime import datetime
from .tunnel import AgentTunnel from .tunnel import AgentTunnel
from .agent_message import AgentMsg from .agent_base import AgentMsg
from email.message import EmailMessage from email.message import EmailMessage
+2 -1
View File
@@ -1,7 +1,8 @@
# define a knowledge base class # define a knowledge base class
import json import json
import logging import logging
from .agent import AgentPrompt
from .agent_base import AgentPrompt
from .compute_kernel import ComputeKernel from .compute_kernel import ComputeKernel
from .storage import AIStorage from .storage import AIStorage
from .environment import Environment from .environment import Environment
+1 -1
View File
@@ -118,7 +118,7 @@ class OpenAI_ComputeNode(ComputeNode):
#max_tokens=result_token, #max_tokens=result_token,
temperature=0.7) temperature=0.7)
else: else:
logger.info(f"call openai {mode_name} prompts: {prompts} functions: {json.dumps(llm_inner_functions)}") logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions)}")
resp = openai.ChatCompletion.create(model=mode_name, resp = openai.ChatCompletion.create(model=mode_name,
messages=prompts, messages=prompts,
functions=llm_inner_functions, functions=llm_inner_functions,
+1 -1
View File
@@ -1,6 +1,6 @@
import logging import logging
from .agent import AIAgent,AgentPrompt from .agent_base import AgentPrompt
class AIRole: class AIRole:
def __init__(self) -> None: def __init__(self) -> None:
+1 -1
View File
@@ -17,7 +17,7 @@ from .knowledge_base import KnowledgeBase
from .tunnel import AgentTunnel from .tunnel import AgentTunnel
from .storage import AIStorage from .storage import AIStorage
from .contact_manager import ContactManager,Contact,FamilyMember from .contact_manager import ContactManager,Contact,FamilyMember
from .agent_message import AgentMsg,AgentMsgType from .agent_base import AgentMsg,AgentMsgType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+1 -1
View File
@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import logging import logging
from typing import Coroutine from typing import Coroutine
from .agent_message import AgentMsg from .agent_base import AgentMsg
from .bus import AIBus from .bus import AIBus
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+2 -76
View File
@@ -8,8 +8,7 @@ from typing import Optional,Tuple,List
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,AgentMsgStatus,FunctionItem,LLMResult from .agent_base import AgentMsg,AgentMsgStatus,FunctionItem,LLMResult,AgentPrompt
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 AIFunction,FunctionItem from .ai_function import AIFunction,FunctionItem
@@ -238,77 +237,6 @@ class Workflow:
error_resp = msg.create_error_resp(err_str) error_resp = msg.create_error_resp(err_str)
return error_resp return error_resp
@classmethod
def prase_llm_result(cls,llm_result_str:str)->LLMResult:
r = LLMResult()
if llm_result_str is None:
r.state = "ignore"
return r
if llm_result_str == "ignore":
r.state = "ignore"
return r
lines = llm_result_str.splitlines()
is_need_wait = False
def check_args(func_item:FunctionItem):
match func_name:
case "send_msg":# sendmsg($target_id,$msg_content)
if len(func_item.args) != 1:
logger.error(f"parse sendmsg failed! {func_item}")
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
case "post_msg":# postmsg($target_id,$msg_content)
if len(func_item.args) != 1:
logger.error(f"parse postmsg failed! {func_item}")
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)
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
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
async def role_post_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession): 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()
@@ -395,7 +323,6 @@ class Workflow:
return None 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:AgentPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]:
from .compute_kernel import ComputeKernel
func_name = inenr_func_call_node.get("name") func_name = inenr_func_call_node.get("name")
arguments = json.loads(inenr_func_call_node.get("arguments")) arguments = json.loads(inenr_func_call_node.get("arguments"))
@@ -441,7 +368,6 @@ class Workflow:
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession) -> AgentMsg: async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession) -> AgentMsg:
msg.target = the_role.get_role_id() msg.target = the_role.get_role_id()
prompt = AgentPrompt() prompt = AgentPrompt()
prompt.append(the_role.agent.agent_prompt) prompt.append(the_role.agent.agent_prompt)
prompt.append(self.get_workflow_rule_prompt()) prompt.append(self.get_workflow_rule_prompt())
@@ -481,7 +407,7 @@ class Workflow:
error_resp = msg.create_error_resp(result_str) error_resp = msg.create_error_resp(result_str)
return error_resp return error_resp
result : LLMResult = Workflow.prase_llm_result(result_str) result : LLMResult = LLMResult.from_str(result_str)
for postmsg in result.post_msgs: for postmsg in result.post_msgs:
postmsg.prev_msg_id = msg.get_msg_id() postmsg.prev_msg_id = msg.get_msg_id()
# might be craete a new msg.topic for this postmsg # might be craete a new msg.topic for this postmsg
+50
View File
@@ -1,5 +1,6 @@
# this env is designed for workflow owner filesystem, support file/directory operations # this env is designed for workflow owner filesystem, support file/directory operations
import json
import subprocess import subprocess
import tempfile import tempfile
import threading import threading
@@ -9,6 +10,9 @@ import ast
import sys import sys
import os import os
import re import re
import asyncio
import aiofiles.os
import chardet
from .environment import Environment,EnvironmentEvent from .environment import Environment,EnvironmentEvent
from .ai_function import AIFunction,SimpleAIFunction from .ai_function import AIFunction,SimpleAIFunction
@@ -170,4 +174,50 @@ class WorkspaceEnvironment(Environment):
async def run_code(self,pycode:str) -> str: async def run_code(self,pycode:str) -> str:
interpreter = CodeInterpreter("python",True) interpreter = CodeInterpreter("python",True)
return interpreter.run(pycode) return interpreter.run(pycode)
class KnowledgeBaseFileSystemEnvironment(Environment):
def __init__(self, env_id: str) -> None:
super().__init__(env_id)
self.root_path = "."
operator_param = {
"path": "full path of target directory",
}
self.add_ai_function(SimpleAIFunction("list",
"list the files and sub directory in target directory,result is a json array",
self.list,operator_param))
operator_param = {
"path": "full path of target file",
}
self.add_ai_function(SimpleAIFunction("cat",
"cat the file content in target path,result is a string",
self.cat,operator_param))
def set_root_path(self,path:str):
self.root_path = path
async def list(self,path:str) -> str:
directory_path = self.root_path + path
items = []
with await aiofiles.os.scandir(directory_path) as entries:
async for entry in entries:
item_type = "directory" if entry.is_dir() else "file"
items.append({"name": entry.name, "type": item_type})
return json.dumps(items)
async def cat(self,path:str) -> str:
file_path = self.root_path + path
cur_encode = "utf-8"
async with aiofiles.open(file_path,'rb') as f:
cur_encode = chardet.detect(await f.read())['encoding']
async with aiofiles.open(file_path, mode='r', encoding=cur_encode) as f:
content = await f.read(2048)
return content
+11
View File
@@ -47,6 +47,17 @@ class KnowledgeObject(ABC):
def get_body(self) -> dict: def get_body(self) -> dict:
return self.body return self.body
def get_summary(self) -> str:
return self.desc.get("summary")
def get_articl_catelog(self) -> str:
assert self.object_type == ObjectType.Document
return self.desc.get("catelog")
def get_article_full_content(self) -> str:
assert self.object_type == ObjectType.Document
return self.body
def calculate_id(self): def calculate_id(self):
# Convert the object_type and desc to string and compute the SHA256 hash # Convert the object_type and desc to string and compute the SHA256 hash
+4
View File
@@ -6,6 +6,8 @@ from .vector import ChromaVectorStore, VectorBase
import logging import logging
# KnowledgeStore class, which aggregates ChunkStore, ChunkTracker, and ObjectStore, and is a global singleton that makes it easy to use these three built-in store examples # KnowledgeStore class, which aggregates ChunkStore, ChunkTracker, and ObjectStore, and is a global singleton that makes it easy to use these three built-in store examples
class KnowledgeStore: class KnowledgeStore:
_instance = None _instance = None
@@ -41,6 +43,8 @@ class KnowledgeStore:
self.chunk_list_writer = ChunkListWriter(self.chunk_store, self.chunk_tracker) self.chunk_list_writer = ChunkListWriter(self.chunk_store, self.chunk_tracker)
self.chunk_reader = ChunkReader(self.chunk_store, self.chunk_tracker) self.chunk_reader = ChunkReader(self.chunk_store, self.chunk_tracker)
self.vector_store = {} self.vector_store = {}
def get_relation_store(self) -> ObjectRelationStore: def get_relation_store(self) -> ObjectRelationStore:
return self.relation_store return self.relation_store