Refactor before imporve knowledge base.
This commit is contained in:
@@ -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="<font style="font-size: 10px;">Version 0.5.2<br>http://www.opendan.ai</font>" 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>
|
||||
@@ -8,5 +8,4 @@ max_token_size=4000
|
||||
role = "system"
|
||||
content = """
|
||||
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.
|
||||
"""
|
||||
@@ -1,7 +1,7 @@
|
||||
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 .agent import AIAgent,AIAgentTemplete,AgentPrompt
|
||||
from .agent import AIAgent,AIAgentTemplete
|
||||
from .compute_kernel import ComputeKernel,ComputeTask,ComputeTaskResult,ComputeTaskState,ComputeTaskType
|
||||
from .compute_node import ComputeNode,LocalComputeNode
|
||||
from .open_ai_node import OpenAI_ComputeNode
|
||||
|
||||
+123
-188
@@ -10,73 +10,20 @@ import shlex
|
||||
import datetime
|
||||
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 .compute_task import ComputeTaskResult,ComputeTaskResultCode
|
||||
from .ai_function import AIFunction
|
||||
from .environment import Environment
|
||||
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__)
|
||||
|
||||
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:
|
||||
def __init__(self) -> None:
|
||||
@@ -106,10 +53,13 @@ class AIAgentTemplete:
|
||||
|
||||
class AIAgent:
|
||||
def __init__(self) -> None:
|
||||
self.role_prompt:AgentPrompt = None
|
||||
self.agent_prompt:AgentPrompt = None
|
||||
self.agent_think_prompt:AgentPrompt = None
|
||||
self.llm_model_name:str = None
|
||||
self.max_token_size:int = 3600
|
||||
|
||||
|
||||
self.agent_id:str = None
|
||||
self.template_id:str = None
|
||||
self.fullname:str = None
|
||||
@@ -122,6 +72,9 @@ class AIAgent:
|
||||
self.contact_prompt_str = None
|
||||
self.history_len = 10
|
||||
|
||||
self.learn_token_limit = 500
|
||||
self.learn_prompt = None
|
||||
|
||||
self.chat_db = None
|
||||
self.unread_msg = Queue() # msg from other agent
|
||||
self.owner_env : Environment = None
|
||||
@@ -189,77 +142,31 @@ class AIAgent:
|
||||
if config.get("history_len"):
|
||||
self.history_len = int(config.get("history_len"))
|
||||
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:
|
||||
r = LLMResult()
|
||||
if llm_result_str is None:
|
||||
r.state = "ignore"
|
||||
return r
|
||||
if llm_result_str == "ignore":
|
||||
r.state = "ignore"
|
||||
return r
|
||||
def get_template_id(self) -> str:
|
||||
return self.template_id
|
||||
|
||||
lines = llm_result_str.splitlines()
|
||||
is_need_wait = False
|
||||
def get_llm_model_name(self) -> str:
|
||||
return self.llm_model_name
|
||||
|
||||
def check_args(func_item:FunctionItem):
|
||||
match func_name:
|
||||
case "send_msg":# sendmsg($target_id,$msg_content)
|
||||
if len(func_args) != 1:
|
||||
logger.error(f"parse sendmsg 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)
|
||||
def get_max_token_size(self) -> int:
|
||||
return self.max_token_size
|
||||
|
||||
def get_llm_learn_token_limit(self) -> int:
|
||||
return self.learn_token_limit
|
||||
|
||||
def get_learn_prompt(self) -> AgentPrompt:
|
||||
return self.learn_prompt
|
||||
|
||||
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:
|
||||
cm = ContactManager.get_instance()
|
||||
@@ -314,18 +221,18 @@ class AIAgent:
|
||||
|
||||
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]:
|
||||
from .compute_kernel import ComputeKernel
|
||||
|
||||
func_name = inenr_func_call_node.get("name")
|
||||
arguments = json.loads(inenr_func_call_node.get("arguments"))
|
||||
async def _execute_func(self,inner_func_call_node:dict,prompt:AgentPrompt,inner_functions,org_msg:AgentMsg=None,stack_limit = 5) -> ComputeTaskResult:
|
||||
func_name = inner_func_call_node.get("name")
|
||||
arguments = json.loads(inner_func_call_node.get("arguments"))
|
||||
logger.info(f"llm execute inner func:{func_name} ({json.dumps(arguments)})")
|
||||
|
||||
func_node : AIFunction = self.owner_env.get_ai_function(func_name)
|
||||
if func_node is None:
|
||||
result_str = f"execute {func_name} error,function not found"
|
||||
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:
|
||||
result_str:str = await func_node.execute(**arguments)
|
||||
except Exception as e:
|
||||
@@ -334,27 +241,29 @@ class AIAgent:
|
||||
|
||||
|
||||
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})
|
||||
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}")
|
||||
return task_result.error_str,1
|
||||
return task_result
|
||||
|
||||
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)
|
||||
if org_msg:
|
||||
org_msg.inner_call_chain.append(ineternal_call_record)
|
||||
|
||||
inner_func_call_node = None
|
||||
if stack_limit > 0:
|
||||
result_message = task_result.result.get("message")
|
||||
result_message : dict = task_result.result.get("message")
|
||||
if result_message:
|
||||
inner_func_call_node = result_message.get("function_call")
|
||||
|
||||
if inner_func_call_node:
|
||||
return await self._execute_func(inner_func_call_node,prompt,org_msg,stack_limit-1)
|
||||
else:
|
||||
return task_result.result_str,0
|
||||
|
||||
return task_result
|
||||
|
||||
async def _get_agent_prompt(self) -> AgentPrompt:
|
||||
return self.agent_prompt
|
||||
|
||||
@@ -384,12 +293,12 @@ class AIAgent:
|
||||
#4) advanced: reload all chatrecord,and think the topic of message.
|
||||
#5) some topic could be end(not be thinked in futured )
|
||||
return
|
||||
|
||||
|
||||
async def think_chatsession(self,session_id):
|
||||
if self.agent_think_prompt is None:
|
||||
return
|
||||
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)
|
||||
|
||||
while True:
|
||||
@@ -420,10 +329,7 @@ class AIAgent:
|
||||
|
||||
return
|
||||
|
||||
async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg:
|
||||
from .compute_kernel import ComputeKernel
|
||||
from .bus import AIBus
|
||||
|
||||
async def _process_group_chat_msg(self,msg:AgentMsg) -> AgentMsg:
|
||||
session_topic = msg.target + "#" + msg.topic
|
||||
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
|
||||
need_process = False
|
||||
@@ -453,26 +359,13 @@ class AIAgent:
|
||||
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} ")
|
||||
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:
|
||||
logger.error(f"llm compute error:{task_result.error_str}")
|
||||
error_resp = msg.create_error_resp(task_result.error_str)
|
||||
return error_resp
|
||||
|
||||
final_result = task_result.result_str
|
||||
|
||||
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)
|
||||
llm_result : LLMResult = LLMResult.from_str(final_result)
|
||||
is_ignore = False
|
||||
result_prompt_str = ""
|
||||
match llm_result.state:
|
||||
@@ -481,6 +374,7 @@ class AIAgent:
|
||||
case "waiting":
|
||||
for sendmsg in llm_result.send_msgs:
|
||||
target = sendmsg.target
|
||||
sendmsg.sender = self.agent_id
|
||||
sendmsg.topic = msg.topic
|
||||
sendmsg.prev_msg_id = msg.get_msg_id()
|
||||
send_resp = await AIBus.get_default_bus().send_message(sendmsg)
|
||||
@@ -502,16 +396,12 @@ class AIAgent:
|
||||
return None
|
||||
|
||||
async def _process_msg(self,msg:AgentMsg) -> AgentMsg:
|
||||
from .compute_kernel import ComputeKernel
|
||||
from .bus import AIBus
|
||||
|
||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||
return await self._process_group_chat_msg(msg)
|
||||
|
||||
session_topic = msg.get_sender() + "#" + msg.topic
|
||||
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.chat_db)
|
||||
|
||||
|
||||
msg_prompt = AgentPrompt()
|
||||
msg_prompt.messages = [{"role":"user","content":msg.body}]
|
||||
|
||||
@@ -530,26 +420,15 @@ class AIAgent:
|
||||
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} ")
|
||||
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:
|
||||
logger.error(f"llm compute error:{task_result.error_str}")
|
||||
error_resp = msg.create_error_resp(task_result.error_str)
|
||||
return error_resp
|
||||
|
||||
final_result = task_result.result_str
|
||||
|
||||
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)
|
||||
llm_result : LLMResult = LLMResult.from_str(final_result)
|
||||
is_ignore = False
|
||||
result_prompt_str = ""
|
||||
match llm_result.state:
|
||||
@@ -557,6 +436,7 @@ class AIAgent:
|
||||
is_ignore = True
|
||||
case "waiting":
|
||||
for sendmsg in llm_result.send_msgs:
|
||||
sendmsg.sender = self.agent_id
|
||||
target = sendmsg.target
|
||||
sendmsg.topic = msg.topic
|
||||
sendmsg.prev_msg_id = msg.get_msg_id()
|
||||
@@ -578,20 +458,7 @@ class AIAgent:
|
||||
|
||||
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):
|
||||
history_len = (self.max_token_size * 0.7) - system_token_len
|
||||
@@ -660,6 +527,74 @@ class AIAgent:
|
||||
|
||||
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:
|
||||
# TODO: get prompt from group chat is different from single chat
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,5 +1,5 @@
|
||||
from typing import Coroutine,Dict,Any
|
||||
from .agent_message import AgentMsg,AgentMsgStatus,AgentMsgType
|
||||
from .agent_base import AgentMsg,AgentMsgStatus,AgentMsgType
|
||||
import asyncio
|
||||
from asyncio import Queue
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import datetime
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from .agent_message import AgentMsgType, AgentMsg, AgentMsgStatus
|
||||
from .agent_base import AgentMsgType, AgentMsg, AgentMsgStatus
|
||||
|
||||
class ChatSessionDB:
|
||||
def __init__(self, db_file):
|
||||
|
||||
@@ -3,10 +3,12 @@ import random
|
||||
from typing import Optional
|
||||
import logging
|
||||
import asyncio
|
||||
import tiktoken
|
||||
|
||||
from asyncio import Queue
|
||||
|
||||
from knowledge import ObjectID
|
||||
from .agent import AgentPrompt
|
||||
from .agent_base import AgentPrompt
|
||||
from .compute_node import ComputeNode
|
||||
from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskResult, ComputeTaskType,ComputeTaskResultCode
|
||||
|
||||
@@ -104,6 +106,18 @@ class ComputeKernel:
|
||||
def is_task_support(self, task: ComputeTask) -> bool:
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ import logging
|
||||
import time
|
||||
import datetime
|
||||
from .tunnel import AgentTunnel
|
||||
from .agent_message import AgentMsg
|
||||
from .agent_base import AgentMsg
|
||||
|
||||
from email.message import EmailMessage
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# define a knowledge base class
|
||||
import json
|
||||
import logging
|
||||
from .agent import AgentPrompt
|
||||
|
||||
from .agent_base import AgentPrompt
|
||||
from .compute_kernel import ComputeKernel
|
||||
from .storage import AIStorage
|
||||
from .environment import Environment
|
||||
|
||||
@@ -118,7 +118,7 @@ class OpenAI_ComputeNode(ComputeNode):
|
||||
#max_tokens=result_token,
|
||||
temperature=0.7)
|
||||
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,
|
||||
messages=prompts,
|
||||
functions=llm_inner_functions,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from .agent import AIAgent,AgentPrompt
|
||||
from .agent_base import AgentPrompt
|
||||
|
||||
class AIRole:
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -17,7 +17,7 @@ from .knowledge_base import KnowledgeBase
|
||||
from .tunnel import AgentTunnel
|
||||
from .storage import AIStorage
|
||||
from .contact_manager import ContactManager,Contact,FamilyMember
|
||||
from .agent_message import AgentMsg,AgentMsgType
|
||||
from .agent_base import AgentMsg,AgentMsgType
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import logging
|
||||
from typing import Coroutine
|
||||
from .agent_message import AgentMsg
|
||||
from .agent_base import AgentMsg
|
||||
from .bus import AIBus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -8,8 +8,7 @@ from typing import Optional,Tuple,List
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from .environment import Environment,EnvironmentEvent
|
||||
from .agent_message import AgentMsg,AgentMsgStatus,FunctionItem,LLMResult
|
||||
from .agent import AgentPrompt,AgentMsg
|
||||
from .agent_base import AgentMsg,AgentMsgStatus,FunctionItem,LLMResult,AgentPrompt
|
||||
from .chatsession import AIChatSession
|
||||
from .role import AIRole,AIRoleGroup
|
||||
from .ai_function import AIFunction,FunctionItem
|
||||
@@ -238,77 +237,6 @@ class Workflow:
|
||||
error_resp = msg.create_error_resp(err_str)
|
||||
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):
|
||||
msg.sender = the_role.get_role_id()
|
||||
|
||||
@@ -395,7 +323,6 @@ class Workflow:
|
||||
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]:
|
||||
from .compute_kernel import ComputeKernel
|
||||
|
||||
func_name = inenr_func_call_node.get("name")
|
||||
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:
|
||||
msg.target = the_role.get_role_id()
|
||||
|
||||
|
||||
prompt = AgentPrompt()
|
||||
prompt.append(the_role.agent.agent_prompt)
|
||||
prompt.append(self.get_workflow_rule_prompt())
|
||||
@@ -481,7 +407,7 @@ class Workflow:
|
||||
error_resp = msg.create_error_resp(result_str)
|
||||
return error_resp
|
||||
|
||||
result : LLMResult = Workflow.prase_llm_result(result_str)
|
||||
result : LLMResult = LLMResult.from_str(result_str)
|
||||
for postmsg in result.post_msgs:
|
||||
postmsg.prev_msg_id = msg.get_msg_id()
|
||||
# might be craete a new msg.topic for this postmsg
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# this env is designed for workflow owner filesystem, support file/directory operations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
import threading
|
||||
@@ -9,6 +10,9 @@ import ast
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
import aiofiles.os
|
||||
import chardet
|
||||
|
||||
from .environment import Environment,EnvironmentEvent
|
||||
from .ai_function import AIFunction,SimpleAIFunction
|
||||
@@ -170,4 +174,50 @@ class WorkspaceEnvironment(Environment):
|
||||
async def run_code(self,pycode:str) -> str:
|
||||
interpreter = CodeInterpreter("python",True)
|
||||
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
|
||||
|
||||
|
||||
@@ -47,6 +47,17 @@ class KnowledgeObject(ABC):
|
||||
|
||||
def get_body(self) -> dict:
|
||||
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):
|
||||
# Convert the object_type and desc to string and compute the SHA256 hash
|
||||
|
||||
@@ -6,6 +6,8 @@ from .vector import ChromaVectorStore, VectorBase
|
||||
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
|
||||
class KnowledgeStore:
|
||||
_instance = None
|
||||
@@ -41,6 +43,8 @@ class KnowledgeStore:
|
||||
self.chunk_list_writer = ChunkListWriter(self.chunk_store, self.chunk_tracker)
|
||||
self.chunk_reader = ChunkReader(self.chunk_store, self.chunk_tracker)
|
||||
self.vector_store = {}
|
||||
|
||||
|
||||
|
||||
def get_relation_store(self) -> ObjectRelationStore:
|
||||
return self.relation_store
|
||||
|
||||
Reference in New Issue
Block a user