Agent can create todo by op_list
This commit is contained in:
+48
-26
@@ -129,6 +129,7 @@ class AIAgent:
|
||||
self.owner_env : Environment = None
|
||||
self.owenr_bus = None
|
||||
self.enable_function_list = None
|
||||
|
||||
|
||||
@classmethod
|
||||
def create_from_templete(cls,templete:AIAgentTemplete, fullname:str):
|
||||
@@ -148,6 +149,7 @@ class AIAgent:
|
||||
logger.error("agent instance_id is None!")
|
||||
return False
|
||||
self.agent_id = config["instance_id"]
|
||||
self.agent_workspace = WorkspaceEnvironment(self.agent_id)
|
||||
|
||||
if config.get("fullname") is None:
|
||||
logger.error(f"agent {self.agent_id} fullname is None!")
|
||||
@@ -412,7 +414,7 @@ class AIAgent:
|
||||
|
||||
# return None
|
||||
def get_workspace_by_msg(self,msg:AgentMsg) -> WorkspaceEnvironment:
|
||||
return None
|
||||
return self.agent_workspace
|
||||
|
||||
def need_session_summmary(self,msg:AgentMsg,session:AIChatSession) -> bool:
|
||||
return False
|
||||
@@ -455,14 +457,26 @@ class AIAgent:
|
||||
summary = self.llm_select_session_summary(msg,chatsession)
|
||||
prompt.append(AgentPrompt(summary))
|
||||
|
||||
known_info_str = "# 已知信息\n"
|
||||
have_known_info = False
|
||||
todos_str,todo_count = await workspace.get_todo_tree()
|
||||
if todo_count > 0:
|
||||
have_known_info = True
|
||||
known_info_str += f"## 已有todo\n{todos_str}\n"
|
||||
inner_functions,function_token_len = self._get_inner_functions()
|
||||
system_prompt_len = prompt.get_prompt_token_len()
|
||||
input_len = len(msg.body)
|
||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||
history_prmpt,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len)
|
||||
history_str,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len)
|
||||
else:
|
||||
history_prmpt,history_token_len = await self.get_prompt_from_session(chatsession,system_prompt_len + function_token_len,input_len)
|
||||
prompt.append(history_prmpt) # chat context
|
||||
history_str,history_token_len = await self.get_prompt_from_session(chatsession,system_prompt_len + function_token_len,input_len)
|
||||
if history_str:
|
||||
have_known_info = True
|
||||
known_info_str += history_str
|
||||
|
||||
if have_known_info:
|
||||
known_info_prompt = AgentPrompt(known_info_str)
|
||||
prompt.append(known_info_prompt) # chat context
|
||||
|
||||
prompt.append(msg_prompt)
|
||||
|
||||
@@ -475,12 +489,18 @@ class AIAgent:
|
||||
return error_resp
|
||||
|
||||
final_result = task_result.result_str
|
||||
if final_result is not None:
|
||||
if final_result[0] == "{":
|
||||
llm_result = LLMResult.from_json_str(final_result)
|
||||
else:
|
||||
llm_result : LLMResult = LLMResult.from_str(final_result)
|
||||
else:
|
||||
llm_result = LLMResult()
|
||||
llm_result.state = "ignore"
|
||||
|
||||
llm_result : LLMResult = LLMResult.from_str(final_result)
|
||||
|
||||
# extra_info include the operation about workspace
|
||||
if llm_result.extra_info is not None:
|
||||
await workspace.update_state_by_msg(msg,llm_result.extra_info)
|
||||
final_result = llm_result.resp
|
||||
|
||||
await workspace.exec_op_list(llm_result.op_list)
|
||||
|
||||
is_ignore = False
|
||||
result_prompt_str = ""
|
||||
@@ -899,38 +919,37 @@ class AIAgent:
|
||||
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
||||
messages = chatsession.read_history(self.history_len) # read
|
||||
result_token_len = 0
|
||||
result_prompt = AgentPrompt()
|
||||
|
||||
read_history_msg = 0
|
||||
|
||||
have_known_info = False
|
||||
|
||||
known_info = ""
|
||||
if chatsession.summary is not None:
|
||||
if len(chatsession.summary) > 1:
|
||||
result_prompt.messages.append({"role":"user","content":chatsession.summary})
|
||||
known_info += f"## 最近交流的总结 \n {chatsession.summary}\n"
|
||||
result_token_len -= len(chatsession.summary)
|
||||
have_known_info = True
|
||||
|
||||
histroy_str = ""
|
||||
for msg in reversed(messages):
|
||||
read_history_msg += 1
|
||||
dt = datetime.datetime.fromtimestamp(float(msg.create_time))
|
||||
formatted_time = dt.strftime('%y-%m-%d %H:%M:%S')
|
||||
|
||||
if msg.sender == self.agent_id:
|
||||
if self.enable_timestamp:
|
||||
result_prompt.messages.append({"role":"assistant","content":f"(create on {formatted_time}) {msg.body} "})
|
||||
else:
|
||||
result_prompt.messages.append({"role":"assistant","content":msg.body})
|
||||
|
||||
else:
|
||||
if self.enable_timestamp:
|
||||
result_prompt.messages.append({"role":"user","content":f"(create on {formatted_time}) {msg.body} "})
|
||||
else:
|
||||
result_prompt.messages.append({"role":"user","content":msg.body})
|
||||
record_str = f"{msg.sender},[{formatted_time}]\n{msg.body}\n"
|
||||
have_known_info = True
|
||||
histroy_str = histroy_str + record_str
|
||||
|
||||
history_len -= len(msg.body)
|
||||
result_token_len += len(msg.body)
|
||||
if history_len < 0:
|
||||
logger.warning(f"_get_prompt_from_session reach limit of token,just read {read_history_msg} history message.")
|
||||
break
|
||||
|
||||
return result_prompt,result_token_len
|
||||
|
||||
known_info += f"## 最近的沟通记录 \n {histroy_str}\n"
|
||||
|
||||
if have_known_info:
|
||||
return known_info,result_token_len
|
||||
return None,0
|
||||
|
||||
async def _do_llm_complection(self,prompt:AgentPrompt,inner_functions:dict=None,org_msg:AgentMsg=None) -> ComputeTaskResult:
|
||||
from .compute_kernel import ComputeKernel
|
||||
@@ -952,6 +971,9 @@ class AIAgent:
|
||||
|
||||
return task_result
|
||||
|
||||
async def execute_op_list(self,oplist:list,workspace:WorkspaceEnvironment):
|
||||
pass
|
||||
|
||||
def need_work(self) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import copy
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from enum import Enum
|
||||
import uuid
|
||||
import time
|
||||
import re
|
||||
import shlex
|
||||
import json
|
||||
from typing import List
|
||||
from .ai_function import FunctionItem
|
||||
from .compute_task import ComputeTaskResult
|
||||
@@ -78,6 +80,12 @@ class AgentMsg:
|
||||
self.inner_call_chain = []
|
||||
self.resp_msg = None
|
||||
|
||||
@classmethod
|
||||
def from_json(cls,json_obj:dict) -> 'AgentMsg':
|
||||
msg = AgentMsg()
|
||||
|
||||
return msg
|
||||
|
||||
@classmethod
|
||||
def create_internal_call_msg(self,func_name:str,args:dict,prev_msg_id:str,caller:str):
|
||||
msg = AgentMsg(AgentMsgType.TYPE_INTERNAL_CALL)
|
||||
@@ -216,22 +224,49 @@ class AgentPrompt:
|
||||
return False
|
||||
self.messages = []
|
||||
for msg in config:
|
||||
if msg.get("role") == "system":
|
||||
self.system_message = msg
|
||||
if msg.get("content"):
|
||||
if msg.get("role") == "system":
|
||||
self.system_message = msg
|
||||
else:
|
||||
self.messages.append(msg)
|
||||
else:
|
||||
self.messages.append(msg)
|
||||
logger.error("prompt message has no content!")
|
||||
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] = []
|
||||
self.extra_info = None
|
||||
self.op_list : List[FunctionItem] = []
|
||||
|
||||
@classmethod
|
||||
def from_json_str(self,llm_json_str:str) -> 'LLMResult':
|
||||
r = LLMResult()
|
||||
if llm_json_str is None:
|
||||
r.state = "ignore"
|
||||
return r
|
||||
if llm_json_str == "ignore":
|
||||
r.state = "ignore"
|
||||
return r
|
||||
|
||||
llm_json = json.loads(llm_json_str)
|
||||
r.state = llm_json.get("state")
|
||||
r.resp = llm_json.get("resp")
|
||||
|
||||
r.post_msgs = llm_json.get("post_msgs")
|
||||
r.send_msgs = llm_json.get("send_msgs")
|
||||
|
||||
r.calls = llm_json.get("calls")
|
||||
r.post_calls = llm_json.get("post_calls")
|
||||
r.op_list = llm_json.get("op_list")
|
||||
|
||||
return r
|
||||
|
||||
@classmethod
|
||||
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
|
||||
@@ -328,11 +363,52 @@ class AgentTodoResult:
|
||||
self.result_state = "error"
|
||||
|
||||
class AgentTodo:
|
||||
@classmethod
|
||||
def from_dict(cls,json_obj:dict) -> 'AgentTodo':
|
||||
todo = AgentTodo()
|
||||
if json_obj.get("id") is not None:
|
||||
todo.todo_id = json_obj.get("id")
|
||||
todo.parent_id = json_obj.get("parent_id")
|
||||
todo.title = json_obj.get("title")
|
||||
todo.detail = json_obj.get("detail")
|
||||
due_date = json_obj.get("due_date")
|
||||
if due_date:
|
||||
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
||||
#todo.todo_path = json_obj.get("todo_path")
|
||||
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
||||
todo.need_check = json_obj.get("need_check")
|
||||
#todo.result = json_obj.get("result")
|
||||
#todo.last_check_result = json_obj.get("last_check_result")
|
||||
todo.worker = json_obj.get("worker")
|
||||
todo.checker = json_obj.get("checker")
|
||||
todo.createor = json_obj.get("createor")
|
||||
#todo.retry_count = json_obj.get("retry_count")
|
||||
|
||||
return todo
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
result = {}
|
||||
result["id"] = self.todo_id
|
||||
result["parent_id"] = self.parent_id
|
||||
result["title"] = self.title
|
||||
result["detail"] = self.detail
|
||||
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
||||
result["depend_todo_ids"] = self.depend_todo_ids
|
||||
result["need_check"] = self.need_check
|
||||
result["worker"] = self.worker
|
||||
result["checker"] = self.checker
|
||||
result["createor"] = self.createor
|
||||
result["retry_count"] = self.retry_count
|
||||
|
||||
return result
|
||||
|
||||
def __init__(self):
|
||||
self.todo_id = "todo#" + uuid.uuid4().hex
|
||||
self.title = None
|
||||
self.detail = None
|
||||
self.todo_path = None # get parent todo,sub todo by path
|
||||
self.create_time = time.time()
|
||||
self.due_date = time.time() + 3600 * 24 * 2
|
||||
|
||||
self.depend_todo_ids = []
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import List
|
||||
import toml
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
class Contact:
|
||||
def __init__(self, name, phone=None, email=None, telegram=None,added_by=None, tags=[], notes=""):
|
||||
self.name = name
|
||||
@@ -21,7 +22,8 @@ class Contact:
|
||||
|
||||
"added_by": self.added_by,
|
||||
"tags": self.tags,
|
||||
"notes": self.notes
|
||||
"notes": self.notes,
|
||||
"now" : datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -12,37 +12,58 @@ import sys
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
import aiofiles
|
||||
from typing import Any,List
|
||||
import aiofiles.os
|
||||
import os
|
||||
import chardet
|
||||
|
||||
from .agent_base import AgentMsg,AgentTodo
|
||||
from .environment import Environment,EnvironmentEvent
|
||||
from .ai_function import AIFunction,SimpleAIFunction
|
||||
from .storage import AIStorage,ResourceLocation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class WorkspaceEnvironment(Environment):
|
||||
def __init__(self, env_id: str) -> None:
|
||||
super().__init__(env_id)
|
||||
self.root_path = f"./workspace/{env_id}"
|
||||
myai_path = AIStorage.get_instance().get_myai_dir()
|
||||
self.root_path = f"{myai_path}/workspace/{env_id}"
|
||||
if not os.path.exists(self.root_path):
|
||||
os.makedirs(self.root_path+"/todos")
|
||||
|
||||
|
||||
def set_root_path(self,path:str):
|
||||
self.root_path = path
|
||||
|
||||
def get_prompt(self) -> AgentMsg:
|
||||
return None
|
||||
|
||||
def get_role_prompt(self,role_id:str) -> AgentMsg:
|
||||
return None
|
||||
|
||||
def get_knowledge_base(self) -> str:
|
||||
pass
|
||||
|
||||
def exec_op_list(self,oplist:List)->None:
|
||||
async def exec_op_list(self,oplist:List)->None:
|
||||
if oplist is None:
|
||||
return
|
||||
|
||||
for op in oplist:
|
||||
if op["op"] == "create":
|
||||
self.create(op["path"],op["content"])
|
||||
return await self.create(op["path"],op["content"])
|
||||
elif op["op"] == "write":
|
||||
self.write(op["path"],op["content"],op["mode"])
|
||||
return await self.write(op["path"],op["content"],op["mode"])
|
||||
elif op["op"] == "delete":
|
||||
self.delete(op["path"])
|
||||
return await self.delete(op["path"])
|
||||
elif op["op"] == "rename":
|
||||
self.rename(op["path"],op["new_name"])
|
||||
return await self.rename(op["path"],op["new_name"])
|
||||
elif op["op"] == "mkdir":
|
||||
return await self.mkdir(op["path"])
|
||||
elif op["op"] == "create_todo":
|
||||
todoObj = AgentTodo.from_dict(op["todo"])
|
||||
path = op.get("path")
|
||||
return await self.create_todo(path,todoObj)
|
||||
|
||||
else:
|
||||
logger.error(f"execute op list failed: unknown op:{op['op']}")
|
||||
@@ -98,26 +119,44 @@ class WorkspaceEnvironment(Environment):
|
||||
os.remove(file_path)
|
||||
return True
|
||||
|
||||
async def mkdir(self,path:str) -> bool:
|
||||
dir_path = self.root_path + path
|
||||
os.makedirs(dir_path)
|
||||
return True
|
||||
|
||||
async def rename(self,path:str,new_name:str) -> bool:
|
||||
file_path = self.root_path + path
|
||||
new_path = self.root_path + new_name
|
||||
os.rename(file_path,new_path)
|
||||
return True
|
||||
|
||||
async def get_todo_tree(self,path:str = None,deep:int = 4):
|
||||
if path:
|
||||
directory_path = self.root_path + "/todos/" + path
|
||||
else:
|
||||
directory_path = self.root_path + "/todos"
|
||||
|
||||
#easy use functions
|
||||
async def update_state_by_msg(self, msg: AgentMsg,extra_info:dict) -> None:
|
||||
# add todo
|
||||
# update todo status
|
||||
pass
|
||||
str_result:str = "/todos\n"
|
||||
todo_count:int = 0
|
||||
|
||||
async def update_todos(self,oplist:list) -> None:
|
||||
# add todo
|
||||
# update todo status
|
||||
pass
|
||||
async def scan_dir(directory_path:str,deep:int):
|
||||
nonlocal str_result
|
||||
nonlocal todo_count
|
||||
if deep <= 0:
|
||||
return
|
||||
|
||||
for entry in os.scandir(directory_path):
|
||||
is_dir = entry.is_dir()
|
||||
if not is_dir:
|
||||
continue
|
||||
|
||||
todo_count = todo_count + 1
|
||||
str_result = str_result + f"{' '*(4-deep)}└─{entry.name}\n"
|
||||
await scan_dir(entry.path,deep-1)
|
||||
|
||||
await scan_dir(directory_path,deep)
|
||||
return str_result,todo_count
|
||||
|
||||
async def get_todo_tree(self,path:str,deep:int = 4) -> str:
|
||||
pass
|
||||
|
||||
async def get_todo_by_path(self,path:str) -> AgentTodo:
|
||||
pass
|
||||
@@ -125,8 +164,15 @@ class WorkspaceEnvironment(Environment):
|
||||
async def get_todo(self,id:str) -> AgentTodo:
|
||||
pass
|
||||
|
||||
async def save_new_todo(self,path:str,todo:AgentTodo) -> None:
|
||||
pass
|
||||
async def create_todo(self,path:str,todo:AgentTodo) -> None:
|
||||
if path is None:
|
||||
dir_path = f"/todos/{todo.title}"
|
||||
else:
|
||||
dir_path = f"/todos/{path}/{todo.title}"
|
||||
|
||||
os.makedirs(self.root_path + dir_path)
|
||||
detail_path = f"{dir_path}/detail"
|
||||
await self.create(detail_path,json.dumps(todo.to_dict()))
|
||||
|
||||
async def update_todo(self,path:str,todo:AgentTodo)->None:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user