Update Jarvis implement
This commit is contained in:
+104
-27
@@ -1,10 +1,14 @@
|
||||
# pylint:disable=E0402
|
||||
from datetime import datetime,timedelta
|
||||
from typing import Dict
|
||||
import json
|
||||
import threading
|
||||
from typing import Dict, List
|
||||
import sqlite3
|
||||
|
||||
from ..frame.compute_kernel import ComputeKernel
|
||||
from ..proto.ai_function import SimpleAIAction
|
||||
from ..proto.agent_msg import AgentMsg, AgentMsgType
|
||||
from ..proto.agent_task import AgentWorkLog
|
||||
|
||||
from .llm_context import GlobaToolsLibrary
|
||||
from .chatsession import AIChatSession
|
||||
@@ -16,28 +20,39 @@ logger = logging.getLogger(__name__)
|
||||
class AgentMemory:
|
||||
def __init__(self,agent_id:str,db_path:str) -> None:
|
||||
self.agent_id:str= agent_id
|
||||
self.chat_db:str = db_path
|
||||
self.memory_db:str = db_path
|
||||
self.model_name:str = "gp4-1106-preview"
|
||||
self.threshold_hours = 72
|
||||
|
||||
@classmethod
|
||||
def register_actions(cls):
|
||||
async def action_chatlog_append(parms:Dict):
|
||||
memory = parms.get("_memory")
|
||||
if memory:
|
||||
return await memory.action_chatlog_append(parms)
|
||||
|
||||
chatlog_append_action = SimpleAIAction("chatlog_append","Append request & reply message to chatlog. No params",action_chatlog_append)
|
||||
GlobaToolsLibrary.get_instance().register_tool_function(chatlog_append_action,"agent.memory.chatlog.append")
|
||||
|
||||
|
||||
def _get_conn(self):
|
||||
""" get db connection """
|
||||
local = threading.local()
|
||||
if not hasattr(local, 'conn'):
|
||||
local.conn = self._create_connection(self.memory_db)
|
||||
return local.conn
|
||||
|
||||
def _create_connection(self, db_file):
|
||||
""" create a database connection to a SQLite database """
|
||||
conn = None
|
||||
try:
|
||||
conn = sqlite3.connect(db_file)
|
||||
except Error as e:
|
||||
logging.error("Error occurred while connecting to database: %s", e)
|
||||
return None
|
||||
|
||||
if conn:
|
||||
self._create_table(conn)
|
||||
|
||||
return conn
|
||||
|
||||
def get_session_from_msg(self,msg:AgentMsg) -> AIChatSession:
|
||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||
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.memory_db)
|
||||
else:
|
||||
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.memory_db)
|
||||
return chatsession
|
||||
|
||||
async def load_chatlogs(self,msg:AgentMsg,n:int=6,m:int=64,token_limit=800)->str:
|
||||
@@ -84,19 +99,83 @@ class AgentMemory:
|
||||
|
||||
return histroy_str
|
||||
|
||||
async def action_chatlog_append(self,params:Dict) -> str:
|
||||
# 使用params可以得到: LLM Process的输入,LLM Result,基于LLM Result构造的参数,当前actionItem
|
||||
input_msg:AgentMsg = params.get("input").get("msg")
|
||||
llm_result = params.get("llm_result")
|
||||
chatsession = self.get_session_from_msg(input_msg)
|
||||
resp_msg = params.get("resp_msg")
|
||||
if resp_msg:
|
||||
tags = llm_result.raw_result.get("tags")
|
||||
chatsession.append(input_msg,tags)
|
||||
chatsession.append(resp_msg,tags)
|
||||
# async def action_chatlog_append(self,params:Dict) -> str:
|
||||
#
|
||||
# input_msg:AgentMsg = params.get("input").get("msg")
|
||||
# llm_result = params.get("llm_result")
|
||||
# chatsession = self.get_session_from_msg(input_msg)
|
||||
# resp_msg = params.get("resp_msg")
|
||||
# if resp_msg:
|
||||
# tags = llm_result.raw_result.get("tags")
|
||||
# chatsession.append(input_msg,tags)
|
||||
# chatsession.append(resp_msg,tags)
|
||||
|
||||
return "OK"
|
||||
# return "OK"
|
||||
|
||||
async def load_worklogs(self,operator_id:str,owner_id:str=None, work_types:List[str]=None,token_limit=800):
|
||||
conn = self._get_conn()
|
||||
c = conn.cursor()
|
||||
|
||||
query = 'SELECT * FROM worklog WHERE 1=1'
|
||||
params = []
|
||||
|
||||
if operator_id is not None:
|
||||
query += ' AND operator=?'
|
||||
params.append(operator_id)
|
||||
|
||||
if owner_id is not None:
|
||||
query += ' AND owner_id=?'
|
||||
params.append(owner_id)
|
||||
|
||||
if work_types:
|
||||
query += ' AND work_type IN ({})'.format(', '.join('?'*len(work_types)))
|
||||
params.extend(work_types)
|
||||
|
||||
query += ' ORDER BY timestamp DESC LIMIT 8'
|
||||
|
||||
c.execute(query, tuple(params))
|
||||
rows = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
return [self.from_db_row(row) for row in rows]
|
||||
|
||||
def _create_table(self,conn):
|
||||
c = conn.cursor()
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS worklog (
|
||||
logid TEXT PRIMARY KEY,
|
||||
owner_id TEXT,
|
||||
work_type TEXT,
|
||||
timestamp REAL,
|
||||
content TEXT,
|
||||
result TEXT,
|
||||
meta TEXT,
|
||||
operator TEXT
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@classmethod
|
||||
def from_db_row(self,row):
|
||||
log = AgentWorkLog()
|
||||
# 这里高度依赖表结构的顺序
|
||||
log.logid, log.owner_id, log.work_type, log.timestamp, log.content, log.result, meta_str, log.operator = row
|
||||
log.meta = json.loads(meta_str) if meta_str else None
|
||||
return log
|
||||
|
||||
async def append_worklog(self,log:AgentWorkLog)->str:
|
||||
conn = self._get_conn()
|
||||
c = conn.cursor()
|
||||
# 将meta字典转换为JSON字符串
|
||||
meta_str = json.dumps(log.meta,ensure_ascii=False) if log.meta else None
|
||||
c.execute('''
|
||||
INSERT INTO worklog (logid, owner_id, work_type, timestamp, content, result, meta, operator)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (log.logid, log.owner_id, log.work_type, log.timestamp, log.content, log.result, meta_str, log.operator))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
async def get_contact_summary(self,contact_id:str) -> str:
|
||||
if contact_id is None:
|
||||
return None
|
||||
@@ -114,8 +193,6 @@ class AgentMemory:
|
||||
async def update_sth_summary(self,sth_id:str,summary:str) -> str:
|
||||
return None
|
||||
|
||||
async def get_log_summary(self,msg:AgentMsg) -> str:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
+270
-77
@@ -1,7 +1,7 @@
|
||||
from ..proto.compute_task import LLMPrompt,LLMResult,ComputeTaskResult,ComputeTaskResultCode
|
||||
from ..proto.ai_function import AIFunction,AIAction,ActionNode
|
||||
from ..proto.agent_msg import AgentMsg,AgentMsgType
|
||||
from ..proto.agent_task import AgentTask
|
||||
from ..proto.agent_task import AgentTask, AgentTodo, AgentWorkLog
|
||||
from ..frame.compute_kernel import ComputeKernel
|
||||
|
||||
from .agent_memory import AgentMemory
|
||||
@@ -20,6 +20,7 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#LLM Process All the unfinished tasks,will sort the priority of the task after LLM, determine the next execution time, and complete the simple task
|
||||
class AgentTriageTaskList(LLMAgentBaseProcess):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
@@ -45,6 +46,27 @@ class AgentTriageTaskList(LLMAgentBaseProcess):
|
||||
prompt.append_user_message(json.dumps(task_dict_list,ensure_ascii=False))
|
||||
|
||||
system_prompt_dict = self.prepare_role_system_prompt(context_info)
|
||||
# May all logs is good for Agent Triage Task List?
|
||||
have_known_info = False
|
||||
known_info = {}
|
||||
working_logs = await self.memory.load_worklogs(self.memory.agent_id)
|
||||
if len(working_logs) > 0:
|
||||
have_known_info = True
|
||||
all_worklog_node = []
|
||||
for worklog in working_logs:
|
||||
workNode = {}
|
||||
dt = datetime.fromtimestamp(float(worklog.timestamp))
|
||||
workNode["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
workNode["type"] = worklog.work_type
|
||||
workNode["content"] = worklog.content
|
||||
workNode["result"] = worklog.result
|
||||
all_worklog_node.append(workNode)
|
||||
|
||||
known_info["worklogs"] = all_worklog_node
|
||||
|
||||
if have_known_info:
|
||||
system_prompt_dict["known_info"] = known_info
|
||||
|
||||
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
return prompt
|
||||
@@ -58,127 +80,298 @@ class AgentTriageTaskList(LLMAgentBaseProcess):
|
||||
action_params["_llm_result"] = llm_result
|
||||
action_params["_agentid"] = self.memory.agent_id
|
||||
action_params["_start_at"] = datetime.now()
|
||||
await self._execute_actions(actions,action_params)
|
||||
|
||||
|
||||
result_str = "OK"
|
||||
try:
|
||||
if await self._execute_actions(actions,action_params) is False:
|
||||
result_str = "execute action failed!"
|
||||
except Exception as e:
|
||||
logger.error(f"execute action failed! {e}")
|
||||
result_str = "execute action failed!,error:" + str(e)
|
||||
|
||||
worklog = AgentWorkLog.create_by_content(self.memory.agent_id,"triage",llm_result.resp,self.memory.agent_id)
|
||||
worklog.result = result_str
|
||||
await self.memory.append_worklog(worklog)
|
||||
|
||||
# LLM a Task that never been LLMed, the result of LLM Process may be adjusted, splitting subtask or do simple task as a todo directly.
|
||||
class AgentPlanTask(LLMAgentBaseProcess):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.role_description:str = None
|
||||
self.process_description:str = None
|
||||
self.reply_format = None
|
||||
|
||||
# 虽然在架构上LLM Process可以很容易的去Call另一个Process,但实际应用中还是应该慎重的保持LLM Process的简单性
|
||||
#self.do_task_llm_process : BaseLLMProcess = None
|
||||
|
||||
async def initial(self,params:Dict = None) -> bool:
|
||||
self.memory = params.get("memory")
|
||||
if self.memory is None:
|
||||
logger.error(f"LLMAgeMessageProcess initial failed! memory not found")
|
||||
return False
|
||||
self.workspace = params.get("workspace")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
|
||||
|
||||
|
||||
async def load_from_config(self, config: dict,is_load_default=True) -> bool:
|
||||
if await super().load_from_config(config) is False:
|
||||
return False
|
||||
|
||||
self.role_description = config.get("role_desc")
|
||||
if self.role_description is None:
|
||||
logger.error(f"role_description not found in config")
|
||||
return False
|
||||
|
||||
if config.get("process_description"):
|
||||
self.process_description = config.get("process_description")
|
||||
|
||||
if config.get("reply_format"):
|
||||
self.reply_format = config.get("reply_format")
|
||||
|
||||
if config.get("context"):
|
||||
self.context = config.get("context")
|
||||
|
||||
self.llm_context = SimpleLLMContext()
|
||||
if config.get("llm_context"):
|
||||
self.llm_context.load_from_config(config.get("llm_context"))
|
||||
|
||||
async def prepare_prompt(self,input:Dict) -> LLMPrompt:
|
||||
agent_task = input.get("task")
|
||||
prompt = LLMPrompt()
|
||||
system_prompt_dict = {}
|
||||
system_prompt_dict["role_description"] = self.role_description
|
||||
system_prompt_dict["process_rule"] = self.process_description
|
||||
system_prompt_dict["reply_format"] = self.reply_format
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
|
||||
agent_task : AgentTask= input.get("task")
|
||||
context_info = input.get("context_info")
|
||||
if agent_task is None:
|
||||
logger.error(f"task not found in input")
|
||||
return None
|
||||
|
||||
prompt.append_user_message(json.dumps(agent_task.to_dict(),ensure_ascii=False))
|
||||
|
||||
system_prompt_dict = self.prepare_role_system_prompt(context_info)
|
||||
|
||||
have_known_info = False
|
||||
known_info = {}
|
||||
working_logs = await self.memory.load_worklogs(None,agent_task.task_id)
|
||||
if len(working_logs) > 0:
|
||||
have_known_info = True
|
||||
all_worklog_node = []
|
||||
for worklog in working_logs:
|
||||
workNode = {}
|
||||
dt = datetime.fromtimestamp(float(worklog.timestamp))
|
||||
workNode["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
workNode["type"] = worklog.work_type
|
||||
workNode["operator"] = worklog.operator
|
||||
workNode["content"] = worklog.content
|
||||
workNode["result"] = worklog.result
|
||||
all_worklog_node.append(workNode)
|
||||
|
||||
known_info["worklogs"] = all_worklog_node
|
||||
|
||||
if have_known_info:
|
||||
system_prompt_dict["known_info"] = known_info
|
||||
|
||||
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
return prompt
|
||||
|
||||
|
||||
async def get_review_task_actions(self) -> Dict[str,Dict]:
|
||||
pass
|
||||
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
|
||||
action_params = {}
|
||||
action_params["_input"] = input
|
||||
agent_task : AgentTask= input.get("task")
|
||||
action_params["_memory"] = self.memory
|
||||
action_params["_workspace"] = self.workspace
|
||||
action_params["_llm_result"] = llm_result
|
||||
action_params["_agentid"] = self.memory.agent_id
|
||||
action_params["_start_at"] = datetime.now()
|
||||
|
||||
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
|
||||
pass
|
||||
result_str = "OK"
|
||||
try:
|
||||
if await self._execute_actions(actions,action_params) is False:
|
||||
result_str = "execute action failed!"
|
||||
except Exception as e:
|
||||
logger.error(f"execute action failed! {e}")
|
||||
result_str = "execute action failed!,error:" + str(e)
|
||||
|
||||
worklog = AgentWorkLog.create_by_content(agent_task.task_id,"plan",llm_result.resp,self.memory.agent_id)
|
||||
worklog.result = result_str
|
||||
await self.memory.append_worklog(worklog)
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode]) -> bool:
|
||||
pass
|
||||
|
||||
class AgentReviewTask(BaseLLMProcess):
|
||||
# Agent DO Todo
|
||||
# The purpose is to complete Todo.It is the core LLM process. Can use sufficient external tools to do your best according to the identity and ability of AGENT.It is also the LLM Process of the main extension of Agent extension
|
||||
class AgentDo(LLMAgentBaseProcess):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
async def load_from_config(self, config: dict):
|
||||
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
|
||||
if await super().load_from_config(config) is False:
|
||||
return False
|
||||
|
||||
async def prepare_prompt(self) -> LLMPrompt:
|
||||
async def prepare_prompt(self,input:Dict) -> LLMPrompt:
|
||||
prompt = LLMPrompt()
|
||||
pass
|
||||
|
||||
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
|
||||
pass
|
||||
agent_todo : AgentTodo= input.get("todo")
|
||||
context_info = input.get("context_info")
|
||||
if agent_todo is None:
|
||||
logger.error(f"task not found in input")
|
||||
return None
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode]) -> bool:
|
||||
pass
|
||||
prompt.append_user_message(json.dumps(agent_todo.to_dict(),ensure_ascii=False))
|
||||
|
||||
system_prompt_dict = self.prepare_role_system_prompt(context_info)
|
||||
# May all logs is good for Agent Triage Task List?
|
||||
have_known_info = False
|
||||
known_info = {}
|
||||
working_logs = await self.memory.load_worklogs(None,agent_todo.todo_id)
|
||||
if len(working_logs) > 0:
|
||||
have_known_info = True
|
||||
all_worklog_node = []
|
||||
for worklog in working_logs:
|
||||
workNode = {}
|
||||
dt = datetime.fromtimestamp(float(worklog.timestamp))
|
||||
workNode["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
workNode["type"] = worklog.work_type
|
||||
workNode["content"] = worklog.content
|
||||
workNode["result"] = worklog.result
|
||||
all_worklog_node.append(workNode)
|
||||
|
||||
known_info["worklogs"] = all_worklog_node
|
||||
|
||||
class AgentCheck(BaseLLMProcess):
|
||||
if have_known_info:
|
||||
system_prompt_dict["known_info"] = known_info
|
||||
|
||||
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
return prompt
|
||||
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
|
||||
action_params = {}
|
||||
action_params["_input"] = input
|
||||
agent_todo : AgentTodo= input.get("todo")
|
||||
action_params["_memory"] = self.memory
|
||||
action_params["_workspace"] = self.workspace
|
||||
action_params["_llm_result"] = llm_result
|
||||
action_params["_agentid"] = self.memory.agent_id
|
||||
action_params["_start_at"] = datetime.now()
|
||||
|
||||
result_str = "OK"
|
||||
try:
|
||||
if await self._execute_actions(actions,action_params) is False:
|
||||
result_str = "execute action failed!"
|
||||
except Exception as e:
|
||||
logger.error(f"execute action failed! {e}")
|
||||
result_str = "execute action failed!,error:" + str(e)
|
||||
|
||||
worklog = AgentWorkLog.create_by_content(agent_todo.todo_id,"do",llm_result.resp,self.memory.agent_id)
|
||||
worklog.result = result_str
|
||||
await self.memory.append_worklog(worklog)
|
||||
|
||||
#Agent check todo
|
||||
# LLM a already-DO TODO, the purpose is to check whether it is completed to face the illusion of LLM.Check can use some tools, which is also the core of the agent extension。
|
||||
class AgentCheck(LLMAgentBaseProcess):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
async def load_from_config(self, config: dict) -> Coroutine[Any, Any, bool]:
|
||||
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
|
||||
if await super().load_from_config(config) is False:
|
||||
return False
|
||||
|
||||
async def prepare_prompt(self) -> LLMPrompt:
|
||||
async def prepare_prompt(self,input:Dict) -> LLMPrompt:
|
||||
prompt = LLMPrompt()
|
||||
pass
|
||||
|
||||
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
|
||||
pass
|
||||
agent_todo : AgentTodo= input.get("todo")
|
||||
context_info = input.get("context_info")
|
||||
if agent_todo is None:
|
||||
logger.error(f"task not found in input")
|
||||
return None
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode]) -> bool:
|
||||
pass
|
||||
prompt.append_user_message(json.dumps(agent_todo.to_dict(),ensure_ascii=False))
|
||||
|
||||
class AgentDo(BaseLLMProcess):
|
||||
system_prompt_dict = self.prepare_role_system_prompt(context_info)
|
||||
# May all logs is good for Agent Triage Task List?
|
||||
have_known_info = False
|
||||
known_info = {}
|
||||
working_logs = await self.memory.load_worklogs(None,agent_todo.todo_id)
|
||||
if len(working_logs) > 0:
|
||||
have_known_info = True
|
||||
all_worklog_node = []
|
||||
for worklog in working_logs:
|
||||
workNode = {}
|
||||
dt = datetime.fromtimestamp(float(worklog.timestamp))
|
||||
workNode["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
workNode["type"] = worklog.work_type
|
||||
workNode["content"] = worklog.content
|
||||
workNode["result"] = worklog.result
|
||||
all_worklog_node.append(workNode)
|
||||
|
||||
known_info["worklogs"] = all_worklog_node
|
||||
|
||||
if have_known_info:
|
||||
system_prompt_dict["known_info"] = known_info
|
||||
|
||||
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
return prompt
|
||||
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
|
||||
action_params = {}
|
||||
action_params["_input"] = input
|
||||
agent_todo : AgentTodo= input.get("todo")
|
||||
action_params["_memory"] = self.memory
|
||||
action_params["_workspace"] = self.workspace
|
||||
action_params["_llm_result"] = llm_result
|
||||
action_params["_agentid"] = self.memory.agent_id
|
||||
action_params["_start_at"] = datetime.now()
|
||||
|
||||
result_str = "OK"
|
||||
try:
|
||||
if await self._execute_actions(actions,action_params) is False:
|
||||
result_str = "execute action failed!"
|
||||
except Exception as e:
|
||||
logger.error(f"execute action failed! {e}")
|
||||
result_str = "execute action failed!,error:" + str(e)
|
||||
|
||||
worklog = AgentWorkLog.create_by_content(agent_todo.todo_id,"check",llm_result.resp,self.memory.agent_id)
|
||||
worklog.result = result_str
|
||||
await self.memory.append_worklog(worklog)
|
||||
|
||||
#Agent review task
|
||||
#When Task's Todolist is completed, or Task's subtask is completed, LLM review a TASK to determine that the Task has been completed.This Review also failed to execute.
|
||||
class AgentReviewTask(LLMAgentBaseProcess):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
async def load_from_config(self, config: dict):
|
||||
|
||||
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
|
||||
if await super().load_from_config(config) is False:
|
||||
return False
|
||||
|
||||
async def prepare_prompt(self) -> LLMPrompt:
|
||||
async def prepare_prompt(self,input:Dict) -> LLMPrompt:
|
||||
prompt = LLMPrompt()
|
||||
pass
|
||||
|
||||
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
|
||||
pass
|
||||
agent_task : AgentTask= input.get("task")
|
||||
context_info = input.get("context_info")
|
||||
if agent_task is None:
|
||||
logger.error(f"task not found in input")
|
||||
return None
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode]) -> bool:
|
||||
pass
|
||||
prompt.append_user_message(json.dumps(agent_task.to_dict(),ensure_ascii=False))
|
||||
|
||||
system_prompt_dict = self.prepare_role_system_prompt(context_info)
|
||||
# May all logs is good for Agent Triage Task List?
|
||||
have_known_info = False
|
||||
known_info = {}
|
||||
working_logs = await self.memory.load_worklogs(None,agent_task.task_id)
|
||||
if len(working_logs) > 0:
|
||||
have_known_info = True
|
||||
all_worklog_node = []
|
||||
for worklog in working_logs:
|
||||
workNode = {}
|
||||
dt = datetime.fromtimestamp(float(worklog.timestamp))
|
||||
workNode["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
workNode["type"] = worklog.work_type
|
||||
workNode["operator"] = worklog.operator
|
||||
workNode["content"] = worklog.content
|
||||
workNode["result"] = worklog.result
|
||||
all_worklog_node.append(workNode)
|
||||
|
||||
known_info["worklogs"] = all_worklog_node
|
||||
|
||||
if have_known_info:
|
||||
system_prompt_dict["known_info"] = known_info
|
||||
|
||||
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
|
||||
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
|
||||
return prompt
|
||||
|
||||
|
||||
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
|
||||
action_params = {}
|
||||
action_params["_input"] = input
|
||||
agent_task : AgentTask= input.get("task")
|
||||
action_params["_memory"] = self.memory
|
||||
action_params["_workspace"] = self.workspace
|
||||
action_params["_llm_result"] = llm_result
|
||||
action_params["_agentid"] = self.memory.agent_id
|
||||
action_params["_start_at"] = datetime.now()
|
||||
|
||||
result_str = "OK"
|
||||
try:
|
||||
if await self._execute_actions(actions,action_params) is False:
|
||||
result_str = "execute action failed!"
|
||||
except Exception as e:
|
||||
logger.error(f"execute action failed! {e}")
|
||||
result_str = "execute action failed!,error:" + str(e)
|
||||
|
||||
worklog = AgentWorkLog.create_by_content(agent_task.task_id,"review",llm_result.resp,self.memory.agent_id)
|
||||
worklog.result = result_str
|
||||
await self.memory.append_worklog(worklog)
|
||||
@@ -160,8 +160,8 @@ class BaseLLMProcess(ABC):
|
||||
# Action define in prompt, will be execute after llm compute
|
||||
prompt = await self.prepare_prompt(input)
|
||||
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt,self.model_name)
|
||||
if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||
return LLMResult.from_error_str(f"prompt too long,can not predict")
|
||||
#if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||
# return LLMResult.from_error_str(f"prompt too long,can not predict")
|
||||
|
||||
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
|
||||
prompt,
|
||||
|
||||
@@ -3,13 +3,14 @@ from ast import Dict
|
||||
import json
|
||||
import sqlite3
|
||||
import os
|
||||
import glob
|
||||
import time
|
||||
from typing import List, Optional
|
||||
import aiofiles
|
||||
|
||||
from ..proto.agent_msg import AgentMsg
|
||||
from ..proto.ai_function import AIFunction, ParameterDefine,SimpleAIFunction,ActionNode,SimpleAIAction
|
||||
from ..proto.agent_task import AgentTask, AgentTaskState,AgentTodoTask,AgentWorkLog,AgentTaskManager
|
||||
from ..proto.agent_task import AgentTask, AgentTaskState,AgentTodo,AgentWorkLog,AgentTaskManager
|
||||
from ..storage.storage import AIStorage
|
||||
from ..frame.bus import AIBus
|
||||
from .llm_context import GlobaToolsLibrary
|
||||
@@ -86,11 +87,23 @@ class LocalAgentTaskManger(AgentTaskManager):
|
||||
|
||||
|
||||
|
||||
async def create_todos(self,owner_task_id:str,todos:List[AgentTodoTask]):
|
||||
async def set_todos(self,owner_task_id:str,todos:List[AgentTodo]):
|
||||
owner_task_path = self._get_obj_path(owner_task_id)
|
||||
if owner_task_path is None:
|
||||
return f"owner task {owner_task_id} not found"
|
||||
|
||||
try:
|
||||
directory = f"{self.root_path}/{owner_task_path}"
|
||||
file_extension = "*.todo"
|
||||
pattern = os.path.join(directory, file_extension)
|
||||
files = glob.glob(pattern)
|
||||
|
||||
for file in files:
|
||||
os.remove(file)
|
||||
logger.info(f"Deleted {file}")
|
||||
except Exception as e:
|
||||
logger.error("set_todos deleted todos failed:%s",e)
|
||||
|
||||
try:
|
||||
step_order = 0
|
||||
for todo in todos:
|
||||
@@ -176,7 +189,7 @@ class LocalAgentTaskManger(AgentTaskManager):
|
||||
full_path = f"{self.root_path}/{task_path}"
|
||||
return await self._get_task_by_fullpath(full_path)
|
||||
|
||||
async def get_todo(self,todo_id:str) -> AgentTodoTask:
|
||||
async def get_todo(self,todo_id:str) -> AgentTodo:
|
||||
todo_path = self._get_obj_path(todo_id)
|
||||
if todo_path is None:
|
||||
logger.error("get_todo:%s,not found!",todo_id)
|
||||
@@ -185,7 +198,7 @@ class LocalAgentTaskManger(AgentTaskManager):
|
||||
try:
|
||||
with open(todo_path, mode='r', encoding="utf-8") as f:
|
||||
todo_dict = json.load(f)
|
||||
result_todo:AgentTodoTask = AgentTodoTask.from_dict(todo_dict)
|
||||
result_todo:AgentTodo = AgentTodo.from_dict(todo_dict)
|
||||
if result_todo:
|
||||
result_todo.todo_path = todo_path
|
||||
else:
|
||||
@@ -214,10 +227,11 @@ class LocalAgentTaskManger(AgentTaskManager):
|
||||
sub_task = await self.get_task_by_path(f"{task_path}/{sub_item}")
|
||||
if sub_task:
|
||||
sub_tasks.append(sub_task)
|
||||
pass
|
||||
|
||||
return sub_tasks
|
||||
|
||||
|
||||
async def get_sub_todos(self,task_id:str) -> List[AgentTodoTask]:
|
||||
async def get_sub_todos(self,task_id:str) -> List[AgentTodo]:
|
||||
task_path = self._get_obj_path(task_id)
|
||||
if task_path is None:
|
||||
return []
|
||||
@@ -266,14 +280,14 @@ class LocalAgentTaskManger(AgentTaskManager):
|
||||
try:
|
||||
new_task_content = json.dumps(task.to_dict(),ensure_ascii=False)
|
||||
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
|
||||
await f.write(new_task_content))
|
||||
await f.write(new_task_content)
|
||||
except Exception as e:
|
||||
logger.error("update_task failed:%s",e)
|
||||
return str(e)
|
||||
|
||||
return None
|
||||
|
||||
async def update_todo(self,todo:AgentTodoTask):
|
||||
async def update_todo(self,todo:AgentTodo):
|
||||
todo_path = self._get_obj_path(todo.todo_id)
|
||||
if todo_path is None:
|
||||
return f"todo {todo.todo_id} not found"
|
||||
@@ -424,6 +438,8 @@ class AgentWorkspace:
|
||||
)
|
||||
GlobaToolsLibrary.get_instance().register_tool_function(create_task_action)
|
||||
|
||||
|
||||
|
||||
async def cancel_task(parameters):
|
||||
_workspace = parameters.get("_workspace")
|
||||
if _workspace is None:
|
||||
@@ -498,3 +514,54 @@ class AgentWorkspace:
|
||||
update_task,parameters)
|
||||
GlobaToolsLibrary.get_instance().register_tool_function(update_task_ai_function)
|
||||
|
||||
async def set_todos(parameters):
|
||||
_workspace : AgentWorkspace= parameters.get("_workspace")
|
||||
if _workspace is None:
|
||||
return "_workspace not found"
|
||||
task_id = parameters.get("task_id")
|
||||
task:AgentTask = await _workspace.task_mgr.get_task(task_id)
|
||||
if task is None:
|
||||
return f"task {task_id} not found"
|
||||
todos = parameters.get("todos")
|
||||
if todos is None:
|
||||
return "todos not found"
|
||||
await _workspace.task_mgr.set_todos(task_id,todos)
|
||||
return "set todos ok"
|
||||
|
||||
todo_demo = """
|
||||
[
|
||||
{
|
||||
"title": "todo1",
|
||||
"detail": "todo1 detail",
|
||||
"tags": "tag1,tag2",
|
||||
"due_date": "2021-01-01",
|
||||
"priority": 1
|
||||
},
|
||||
]
|
||||
"""
|
||||
parameters = ParameterDefine.create_parameters({
|
||||
"task_id": {"type": "string", "description": "task id which want to set todos"},
|
||||
"todos": {"type": "list", "description": f"List of todo, todo is a dict like {todo_demo}"},
|
||||
})
|
||||
set_todos_ai_function = SimpleAIFunction("agent.workspace.set_todos",
|
||||
"set todos for task",
|
||||
set_todos,parameters)
|
||||
GlobaToolsLibrary.get_instance().register_tool_function(set_todos_ai_function)
|
||||
|
||||
async def update_todo(parameters):
|
||||
_workspace : AgentWorkspace= parameters.get("_workspace")
|
||||
if _workspace is None:
|
||||
return "_workspace not found"
|
||||
todo_id = parameters.get("todo_id")
|
||||
todo : AgentTodo = await _workspace.task_mgr.get_todo(todo_id)
|
||||
if todo is None:
|
||||
return f"todo {todo_id} not found"
|
||||
|
||||
parameters = ParameterDefine.create_parameters({
|
||||
"todo_id": {"type": "string", "description": "todo id which want to update"},
|
||||
"new_state": {"type": "string", "description": "optional,new todo state: execute_ok , execute_failed, done or check_failed"},
|
||||
})
|
||||
update_todo_ai_function = SimpleAIFunction("agent.workspace.update_todo",
|
||||
"update todo to new state",
|
||||
update_todo,parameters)
|
||||
GlobaToolsLibrary.get_instance().register_tool_function(update_todo_ai_function)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# pylint:disable=E0402
|
||||
from abc import ABC, abstractmethod
|
||||
import json
|
||||
from typing import List, Optional
|
||||
import datetime
|
||||
import time
|
||||
@@ -31,9 +32,6 @@ class AgentTodoResult:
|
||||
result["op_list"] = self.op_list
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
class AgentTodo:
|
||||
TODO_STATE_WAIT_ASSIGN = "wait_assign"
|
||||
TODO_STATE_INIT = "init"
|
||||
@@ -220,7 +218,7 @@ class AgentTodoState(Enum):
|
||||
def from_str(value):
|
||||
return next((s for s in AgentTodoState.__members__.values() if s.value == value), None)
|
||||
|
||||
class AgentTodoTask:
|
||||
class AgentTodo:
|
||||
def __init__(self) -> None:
|
||||
self.todo_id = "todo#" + uuid.uuid4().hex
|
||||
self.todo_path : str = None
|
||||
@@ -385,20 +383,30 @@ class AgentTask:
|
||||
|
||||
return result
|
||||
|
||||
# 谁在什么时间做了什么
|
||||
class AgentWorkLog:
|
||||
# work type : [triage,plan,do,check]
|
||||
def __init__(self) -> None:
|
||||
self.logid = "worklog#" + uuid.uuid4().hex
|
||||
self.owner_taskid:str = None
|
||||
self.owner_todoid:str = None
|
||||
self.type:str = "" # 默认为普通类型的log,特殊类型的Log一般伴随着重要的状态改变
|
||||
self.owner_id:str = None # taskid or todoid
|
||||
self.work_type:str = "" # 默认为普通类型的log,特殊类型的Log一般伴随着重要的状态改变
|
||||
self.timestamp = time.time()
|
||||
self.content:str = None
|
||||
self.result:str = None
|
||||
self.meta : dict = None
|
||||
self.operator = None
|
||||
|
||||
@classmethod
|
||||
def create_by_content(cls,owner_id:str,work_type:str,content:str,operator:str) -> 'AgentWorkLog':
|
||||
log = AgentWorkLog()
|
||||
log.owner_id = owner_id
|
||||
log.work_type = work_type
|
||||
log.content = content
|
||||
log.operator = operator
|
||||
log.result = "OK"
|
||||
return log
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
pass
|
||||
|
||||
|
||||
class AgentTaskManager(ABC):
|
||||
def __init__(self) -> None:
|
||||
@@ -409,7 +417,7 @@ class AgentTaskManager(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def create_todos(self,owner_task_id:str,todos:List[AgentTodoTask]):
|
||||
async def set_todos(self,owner_task_id:str,todos:List[AgentTodo]):
|
||||
# return todo_id
|
||||
pass
|
||||
|
||||
@@ -430,7 +438,7 @@ class AgentTaskManager(ABC):
|
||||
# pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_todo(self,todo_id:str) -> AgentTodoTask:
|
||||
async def get_todo(self,todo_id:str) -> AgentTodo:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@@ -438,7 +446,7 @@ class AgentTaskManager(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_sub_todos(self,task_id:str) -> List[AgentTodoTask]:
|
||||
async def get_sub_todos(self,task_id:str) -> List[AgentTodo]:
|
||||
pass
|
||||
|
||||
#@abstractmethod
|
||||
@@ -454,7 +462,7 @@ class AgentTaskManager(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update_todo(self,todo:AgentTodoTask):
|
||||
async def update_todo(self,todo:AgentTodo):
|
||||
pass
|
||||
|
||||
#@abstractmethod
|
||||
|
||||
Reference in New Issue
Block a user