This commit is contained in:
CakeCN
2024-12-03 16:21:19 +08:00
commit 4198ca63b1
975 changed files with 333413 additions and 0 deletions

View File

@@ -0,0 +1,476 @@
# -*- coding: utf-8 -*-
"""An agent class that implements the ReAct algorithm. The agent will reason
and act iteratively to solve problems. More details can be found in the paper
https://arxiv.org/abs/2210.03629.
"""
from typing import Any, Callable, Optional, Union, Sequence
from loguru import logger
from agent.flex_service_toolkit import FlexServiceToolkit
from agentscope.exception import ResponseParsingError, FunctionCallError
from agentscope.agents import AgentBase
from agentscope.memory.temporary_memory import TemporaryMemory
from agentscope.message import Msg
from agentscope.parsers import MarkdownJsonDictParser
from agentscope.service.service_toolkit import ServiceFunction
from agentscope.service import (
ServiceToolkit,
ServiceResponse,
ServiceExecStatus,
)
import json
# 这个INSTRUCTION_PROMPT 交给免费Agent进行使用不包含评测与得分标准但是会在没有自己注释的大段代码或复制粘贴代码是通知学生进入一次理解性测试。
INSTRUCTION_PROMPT = """## Your Role:
You are a powerful AI agent to teach student computer algorithm, you are to company a student to learn algorithm or solve problems.
## What You Should Do:
0. You MUST follow the teaching instructions made by human teacher with several chapters, ONLY focus on one chapter at a time.
1. Student may ask you some questions, DONT answer the question directly or completely, instead, based on context (like student's code) to answer only one step forward.
2. Each chapter in teaching instruction will specifically guide what to teach to student, follow the instruction.
3. System will gather student's infomation sometimes, you neet to analyze student's learning statement from student's history codes, call the necessary function we he is stuck.
## Note:
1. Fully understand the tool functions and their arguments before using them.
2. Make sure the types and values of the arguments you provided to the tool functions are correct.
3. Don't take things for granted. For example, where you are, what's the time now, etc. You can try to use the tool functions to get information.
4. If the function execution fails, you should analyze the error and try to solve it.
5. Remember to decide whether the student's question or idea is regarding about current chapter, if not, you should not awnser and ask the student to focus on the chapter.
## Resources:
1. The tool functions you can use.
2. The context, including the student's code, and teaching instructions.
""" # noqa
CHAPTER_FINISH = 0
CHAPTER_FOCUS = 1
CHAPTER_LATTER = 2
CHAPTER_FAILED = 3
class Chapter():
def __init__(self, No = 1, focus = 2,title="", chapter = "", chapter_prompt = "", score_prompt = "", append_tools : Callable[..., Any] = None) -> None:
self.No = No
self.focus = focus
self.chapter = chapter
self.append_tools = append_tools
self.memory = None
def GetChapter(self):
if self.focus == CHAPTER_FINISH: f = "(Finished Chapter)"
elif self.focus == CHAPTER_FOCUS: f = "(Your Now Chapter)"
elif self.focus == CHAPTER_LATTER: f = "(Ignore for Latter Chapter)"
elif self.focus == CHAPTER_FAILED: f = "(Failed Chapter)"
return f"{f} Chapter {self.No}: {self.chapter}"
def Finished(self, successful: bool = True):
if successful: self.focus = 0
else: self.focus = 3
def Focus(self):
self.focus = 1
def record(content: str, name: str):
"""Call it to save some important info for other chapters. Like file path, function call example.
Args:
content (`str`): The content to be recorded.
name (`str`): The name of the agent to record.
Returns:
None
"""
ChapterChainAgent.Instance[name].memory.add(Msg(
"assistant", content, role="assistant"
))
ChapterChainAgent.Instance[name].chapter_memory.add(Msg(
"assistant", "I recorded globally: "+content, role="assistant"
))
status = ServiceExecStatus.SUCCESS
return ServiceResponse(status, None)
def next_chapter(name: str):
"""Call it to go to next chapter.
Args:
name (`str`): The name of the agent to go to next chapter.
Returns:
None
"""
try:
ChapterChainAgent.Instance[name].next_chapter()
except:
status = ServiceExecStatus.ERROR
return ServiceResponse(status, "Failed to go to next chapter. name '"+name+"' not found in ChapterChainAgent.Instance")
status = ServiceExecStatus.SUCCESS
return ServiceResponse(status, None)
class ChapterChainAgent(AgentBase):
"""An agent class to preform chapter chain algorithm.
Project will be split to some chapters chained.
Each chapter will haave a main objective, having its own tools, and holding its own context.
"""
Instance = {}
def __init__(
self,
name: str,
model_config_name: str,
service_toolkit: FlexServiceToolkit = None,
sys_prompt: str = "You're a helpful assistant.",
max_iters: int = 10,
verbose: bool = True,
chapter_chain: Sequence[Chapter] = None,
stop_when_one_CHAPTER_failed: bool = True,
**kwargs: Any,
) -> None:
"""
Args:
name (`str`):
The name of the agent.
sys_prompt (`str`):
The system prompt of the agent.
model_config_name (`str`):
The name of the model config, which is used to load model from
configuration.
service_toolkit (`ServiceToolkit`):
A `ServiceToolkit` object that contains the tool functions.
max_iters (`int`, defaults to `10`):
The maximum number of iterations of each chapter.
verbose (`bool`, defaults to `True`):
Whether to print the detailed information during reasoning and
acting steps. If `False`, only the content in speak field will
be print out.
chapter_chain (`Sequence[str]`):
The chapter chain to be performed. If None, the agent will add a
init chapter to construct the chapter chain.
"""
super().__init__(
name=name,
sys_prompt=sys_prompt,
model_config_name=model_config_name,
)
# TODO: To compatible with the old version, which will be deprecated
# soon
if "tools" in kwargs:
logger.warning(
"The argument `tools` will be deprecated soon. "
"Please use `service_toolkit` instead. Example refers to "
"https://github.com/modelscope/agentscope/blob/main/"
"examples/conversation_with_react_agent/code/"
"conversation_with_react_agent.py",
)
service_funcs = {}
for func, json_schema in kwargs["tools"]:
name = json_schema["function"]["name"]
service_funcs[name] = ServiceFunction(
name=name,
original_func=func,
processed_func=func,
json_schema=json_schema,
)
if service_toolkit is None:
service_toolkit = FlexServiceToolkit()
service_toolkit.service_funcs = service_funcs
else:
service_toolkit.service_funcs.update(service_funcs)
elif service_toolkit is None:
raise ValueError(
"The argument `service_toolkit` is required to initialize "
"the ReActAgent.",
)
service_toolkit.add(record,name=name)
service_toolkit.add(next_chapter,name=name)
self.service_toolkit = service_toolkit
self.service_toolkit.agent = self
self.verbose = verbose
self.max_iters = max_iters
if not sys_prompt.endswith("\n"):
sys_prompt = sys_prompt + "\n"
self.sys_prompt = "\n".join(
[
# The brief intro of the role and target
sys_prompt.format(name=self.name),
# The detailed instruction prompt for the agent
INSTRUCTION_PROMPT,
],
)
# This memory is used to store all useful memory with the chapter chain switching
#self.memory_on_chain = TemporaryMemory()
self.memory.add(Msg("system", self.sys_prompt, role="system"))
self.chapter_memory = TemporaryMemory()
# Initialize a parser object to formulate the response from the model
self.parser = MarkdownJsonDictParser(
content_hint={
"thought": "what you thought",
"speak": "what you speak",
"function": service_toolkit.tools_calling_format,
},
required_keys=["thought", "speak", "function"],
# Only print the speak field when verbose is False
keys_to_content=True if self.verbose else "speak",
)
self.chapter_chain = chapter_chain
self.chapter_chain_now = 0
self.finish_now_chapter = False
self.stop_when_one_CHAPTER_failed = stop_when_one_CHAPTER_failed
ChapterChainAgent.Instance[name] = self
def chapter_chain_to_memory(self):
mem = "##The Chapter Chain##\n"
for chapter in self.chapter_chain:
mem+=chapter.GetChapter()+"\n"
mem+="\n"
mem+= "Remember your now chapter is "+ self.chapter_chain[self.chapter_chain_now].GetChapter()+"\n"
return Msg("system", mem, role="system")
def _has_unfinished_chapter(self):
for chapter in self.chapter_chain:
if chapter.focus == CHAPTER_LATTER or chapter.focus == CHAPTER_FOCUS:
return True
return False
def next_chapter(self, successful: bool = True):
if self.chapter_chain[self.chapter_chain_now].append_tools is not None:
for callable_tools in self.chapter_chain[self.chapter_chain_now].append_tools:
self.service_toolkit.Remove(callable_tools)
self.chapter_chain[self.chapter_chain_now].Finished(successful)
for i in range(len(self.chapter_chain)):
if self.chapter_chain[i].focus == 2:
self.chapter_chain[i].Focus()
if self.chapter_chain[i].append_tools is not None:
for callable_tools in self.chapter_chain[i].append_tools:
self.service_toolkit.add(callable_tools)
self.chapter_chain_now = i
return
def add_chapters_after(self, CHAPTER_No, chapters: Sequence[Chapter]):
for i in range(len(self.chapter_chain)):
if self.chapter_chain[i].No == CHAPTER_No:
for j in range(len(chapters)):
CHAPTER_to_insert = Chapter(No = CHAPTER_No + j + 1, chapter = chapters[j], focus = 2, append_tools=
self.chapter_chain[self.chapter_chain_now].append_tools)
self.chapter_chain.insert(i+1+j, CHAPTER_to_insert)
for j in range(i+len(chapters), len(self.chapter_chain)):
self.chapter_chain[j].No += len(chapters)
break
def reply(self, x: Optional[Union[Msg, Sequence[Msg]]] = None, user_backboard:str = "") -> Msg:
if self.chapter_memory.size() == 0:
self.chapter_chain[self.chapter_chain_now].memory = self.chapter_memory
for msg in self.memory.get_memory():
self.chapter_memory.add(msg)
self.chapter_memory.add(self.chapter_chain_to_memory())
self.chapter_memory.add(x)
for _ in range(1):
# Step 1: Thought
if self.verbose:
self.speak(f" ITER {_+1}, STEP 1: REASONING ".center(70, "#"))
# Prepare hint to remind model what the response format is
# Won't be recorded in memory to save tokens
backboard_hint_msg = Msg(
"system",
user_backboard,
role="system",
echo=self.verbose,
)
tools_hint_msg = Msg(
"system",
self.service_toolkit.tools_instruction,
role="system",
echo=self.verbose,
)
hint_msg = Msg(
"system",
self.parser.format_instruction+"\n ####",
role="system",
echo=self.verbose,
)
prompt = self.model.format(self.chapter_memory.get_memory(),
backboard_hint_msg,
# The instruction prompt for tools
tools_hint_msg,
hint_msg)
print("*********************************************************************")
print(prompt)
if self.verbose:
self.speak(f"Prompt".center(70, "#"))
self.speak(str(self.chapter_memory.get_memory()[-1].content))
# print(prompt)
try:
res = self.model(
prompt,
parse_func=self.parser.parse,
max_retries=1,
)
if self.verbose:
self.speak(f"Result Parsed".center(70, "#"))
self.speak(str(res.parsed))
print(res.parsed)
self.chapter_memory.add(
Msg(
self.name,
self.parser.to_memory(res.parsed),
"assistant",
),
)
# Print out the response
msg_returned = Msg(
self.name,
self.parser.to_content(res.parsed),
"assistant",
)
if self.verbose:
self.speak(f"Speak".center(70, "#"))
print(msg_returned)
self.speak(msg_returned)
return msg_returned
if self.verbose:
self.speak(f"Function".center(70, "#"))
self.speak(str(res.parsed["function"]))
print(res.parsed["function"])
arg_function = res.parsed["function"]
if (
isinstance(arg_function, str)
and arg_function in ["[]", ""]
or isinstance(arg_function, list)
and len(arg_function) == 0
):
# Only the speak field is exposed to users or other agents
continue
# Only catch the response parsing error and expose runtime
# errors to developers for debugging
except ResponseParsingError as e:
# Print out raw response from models for developers to debug
response_msg = Msg(self.name, e.raw_response, "assistant")
self.speak(response_msg)
# Re-correct by model itself
error_msg = Msg("system", str(e), "system")
self.speak(error_msg)
self.chapter_memory.add([response_msg, error_msg])
# Skip acting step to re-correct the response
continue
# 2. Acting
if self.verbose:
self.speak(f" ITER {_+1}, STEP 2: ACTING ".center(70, "#"))
# Parse, check and execute the tool functions in service toolkit
try:
execute_results = self.service_toolkit.parse_and_call_func(
res.parsed["function"],
)
# 在这里补充任务结束行动
if self.finish_now_chapter:
self.finish_now_chapter = False
hint_msg = Msg(
"system",
"You have finished your own chapter."
"Now generate a reply by summarizing the current "
"situation. Remember this summary will be recorded in global memory. Make sure keep important information.",
role="system",
echo=self.verbose,
)
# Generate a reply by summarizing the current situation
prompt = self.model.format(self.chapter_memory.get_memory(), hint_msg)
res = self.model(prompt)
res_msg = Msg(self.name, res.text, "assistant")
self.memory.add(res_msg)
self.speak(res_msg)
self.next_chapter(successful=True)
self.success_own_chapter = True
break
# Note: Observing the execution results and generate response
# are finished in the next reasoning step. We just put the
# execution results into memory, and wait for the next loop
# to generate response.
# Record execution results into memory as system message
msg_res = Msg("system", execute_results, "system")
self.speak(msg_res)
self.chapter_memory.add(msg_res)
except FunctionCallError as e:
# Catch the function calling error that can be handled by
# the model
error_msg = Msg("system", str(e), "system")
self.speak(error_msg)
self.chapter_memory.add(error_msg)
continue
except Exception as e:
# Catch the function calling error that can not be handled
error_msg = Msg("system", str(e), "system")
self.speak(error_msg)
self.chapter_memory.add(error_msg)
continue
def GetProcess(self):
process = []
t = {}
t['topic'] = "Global Memory"
mems = []
for msg in self.memory.get_memory():
mems.append(msg.serialize())
t['memory'] = "$MEMORYMEMORY$".join(mems)
t['finish'] = "True"
ts = f"{t['topic']}$TOPICTOPIC${t['memory']}$TOPICTOPIC${t['finish']}"
process.append(ts)
for chapter in self.chapter_chain:
t = {}
t['topic'] = f'Chapter {chapter.No}'
mems = []
if chapter.memory is not None:
for msg in self.memory.get_memory():
mems.append(msg.serialize())
t['memory'] = "$MEMORYMEMORY$".join(mems)
t['finish'] = "True" if chapter.focus == CHAPTER_FINISH else "False"
ts = f"{t['topic']}$TOPICTOPIC${t['memory']}$TOPICTOPIC${t['finish']}"
process.append(ts)
returnString = ""
returnString = "$TASKTASK$".join(process)
return returnString

View File

@@ -0,0 +1,10 @@
from agentscope.service.service_toolkit import ServiceToolkit
from loguru import logger
class FlexServiceToolkit(ServiceToolkit):
def Remove(self, service_func_name: str):
if service_func_name in self.service_funcs:
del self.service_funcs[service_func_name]
else:
logger.warning(f"{service_func_name} not found in service_funcs")

View File

@@ -0,0 +1,235 @@
import sys
import os
current_directory = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_directory)
import agentscope
from agentscope.message import Msg
import os
import asyncio
import threading
import json
from agent.flex_service_toolkit import *
from AlgoriAgent.projects.algoriAgent.agent.algori_agent import *
from agentscope.service import (
ServiceToolkit,
ServiceResponse,
ServiceExecStatus,
)
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
openai_api_key = config['Global']['api_key']
OPENAI_CFG_DICT = {
"config_name": "openai_cfg", # 此配置的名称,必须保证唯一
"model_type": "openai_chat", # 模型类型
"model_name": "gpt-4o-mini", # 模型名称
#"model_name": "gpt-4", # 模型名称
#"model_name": "llama3",
"api_key": openai_api_key, # OpenAI API key. 如果没有设置,将使用环境变量中的 OPENAI_API_KEY
"client_args": {
"base_url": config['Global']['base_chat_url']
},
}
import uuid
from AlgoriAgent.projects.algoriAgent.tools.judge_tools import judge
EMPTY_CHAPTER_CHAIN = [ Chapter(1, CHAPTER_FOCUS, "本章是未打开某个具体章节时的默认章节。", "处于本章节时不会有任何章节跳转。请作为一名经验丰富的算法教师,回答用户的问题。") ]
def tool_name_to_tool(tool_name_list):
tools = []
for tool_name in tool_name_list:
if tool_name == "":
continue
if tool_name == "judge":
tools.append(judge)
return tools
class AgentManager:
def __init__(self):
agentscope.init(model_configs=[OPENAI_CFG_DICT])#, studio_url="http://0.0.0.0:5000")
self.agents = {}
def new_agent(self, markdown:str, markdown_prompt:str, score_prompt:str, id = None):
'''
markdown: 教案的markdown文件内容
markdown_prompt: 教案的prompt的markdown文件内容
score_prompt: 评分的prompt的markdown文件内容
根据3个教案的prompt生成一个agent返回agent的id与agent
'''
markdown_list = markdown.split("\n")
markdown_prompt_list = markdown_prompt.split("\n")
score_prompt_list = score_prompt.split("\n")
# 获取 H1 标题
title = ""
for line in markdown_list:
if line.startswith("# "):
title = line[2:]
break
# 对于 H3 标题 构造每一个 Chapter
# 首先找到所有的 H3 标题
chapter_dict = {}
chapter_sequence = []
for line in markdown_list:
if line.startswith("### "):
chapter_name = line[4:]
chapter_dict[chapter_name] = {}
chapter_sequence.append(chapter_name)
# 将 H3 标题 和 其对应的内容 构造成一个 Chapter
h3_name = ""
content = ""
for i in range(len(markdown_list)):
if markdown_list[i].startswith("### "):
if(h3_name != ""):
chapter_dict[h3_name]["markdown"] = content
h3_name = markdown_list[i][4:]
content = ""
continue
if h3_name != "":
content += markdown_list[i]+"\n"
if(h3_name != ""):
chapter_dict[h3_name]["markdown"] = content
h3_name = ""
content = ""
require_tools = []
for i in range(len(markdown_prompt_list)):
if markdown_prompt_list[i].startswith("### "):
if(h3_name != ""):
chapter_dict[h3_name]["markdown_prompt"] = content
chapter_dict[h3_name]["require_tools"] = tool_name_to_tool(require_tools)
h3_name = markdown_prompt_list[i][4:]
content = ""
continue
if h3_name != "":
if markdown_prompt_list[i].startswith("_require_tools"):
require_tools.append(markdown_prompt_list[i].split("=")[1].strip())
continue
content += markdown_prompt_list[i]+"\n"
if(h3_name != ""):
chapter_dict[h3_name]["markdown_prompt"] = content
chapter_dict[h3_name]["require_tools"] = tool_name_to_tool(require_tools)
h3_name = ""
content = ""
for i in range(len(score_prompt_list)):
if score_prompt_list[i].startswith("### "):
if(h3_name != ""):
chapter_dict[h3_name]["score_prompt"] = content
h3_name = score_prompt_list[i][4:]
content = ""
continue
if h3_name != "":
content += score_prompt_list[i]+"\n"
if(h3_name != ""):
chapter_dict[h3_name]["score_prompt"] = content
chapter_chain = []
No = 1
print (chapter_dict)
for chapter_name in chapter_sequence:
chapter_chain.append(Chapter(No, CHAPTER_LATTER, title, chapter_dict[chapter_name]["markdown"], chapter_dict[chapter_name]["markdown_prompt"], chapter_dict[chapter_name]["score_prompt"],chapter_dict[chapter_name]["require_tools"]))
No+=1
chapter_chain[0].Focus()
# Prepare the tools for the agent
service_toolkit = FlexServiceToolkit()
# for tool_function in unity_function_list:
# service_toolkit.add(tool_function)
agent = ChapterChainAgent(
name="assistant",
model_config_name="openai_cfg",
verbose=True,
service_toolkit=service_toolkit,
max_iters=5,
chapter_chain=chapter_chain
)
self.agents[id] = agent
return id, agent
def new_agent_with_chain(self, chapter_chain = None, id = None):
'''
'''
if chapter_chain is None:
chapter_chain = EMPTY_CHAPTER_CHAIN
# Prepare the tools for the agent
service_toolkit = FlexServiceToolkit()
# for tool_function in unity_function_list:
# service_toolkit.add(tool_function)
agent = ChapterChainAgent(
name="assistant",
model_config_name="openai_cfg",
verbose=True,
service_toolkit=service_toolkit,
max_iters=5,
chapter_chain=EMPTY_CHAPTER_CHAIN
)
if id is None: id = uuid.uuid4()
self.agents[id] = agent
return id, agent
def get_agent(self, id=None):
'''
如果在get agent之前没有new agent并用id进行访问就返回一个章节链为空的agent
'''
if id:
if id in self.agents:
return self.agents[id]
self.agents[id] = self.new_agent(id)[1]
else:
id = str(uuid.uuid4())
self.agents[id] = self.new_agent()[1]
return id, self.agents[id]
def invoke(self,id, query, user_backboard):
msg = Msg("user", query, role="user")
return self.agents[id](msg, user_backboard = user_backboard)
import time
if __name__ == '__main__':
# tool_demo = ToolDemo("Lava")
# response = tool_demo.invoke("I want such a Gurouce, when use it, it will be thrown and fly towards the mouse position for a short time, and stop. And will damage Enemy who step on it. I call it Lava Gurouce.")
# print(response)
# Start the WebSocket server in a separate thread
manager = AgentManager()
id, agent = manager.new_agent()
# Main thread logic
try:
# Your code that might be interrupted
while True:
input_data = input("用户:")
print(manager.invoke(id, input_data))
except KeyboardInterrupt:
print("Process interrupted by user.")
# You can add any cleanup code here if needed
finally:
print("Exiting program.")
# Code to run before the program exits

View File

@@ -0,0 +1,16 @@
[Global]
base_chat_url1 = http://58.198.177.26:8000/v1
base_chat_url = https://api.zyai.online/v1
api_key1 = ollama
api_key = sk-B31NVWeWuvbkEnUgA5913e4c63Ac40E7A1B084742299E57f
target_path = C:/CAKE/Game/Unity/Repositorys/auto-ucg-game
project_name = auto-ucg-game
writeable_file_folder = Assets/HotUpdate/Gurouce/
terminal_name = Windows PowerShell
[WebSocket]
listen_port = 3001
server_address = ws://localhost:3000/myService
agent_process_provider_host = localhost
agent_process_provider_port = 3002

View File

@@ -0,0 +1,64 @@
from agentscope.service.service_toolkit import ServiceFunction
from agentscope.service import (
ServiceToolkit,
ServiceResponse,
ServiceExecStatus,
)
import os
def judge(filepath:str, name: str):
"""Call this method to judge the student's solution after they have completed the problem.
Args:
filepath (`str`): The path to the file containing the student's solution.
name (`str`): The name of the problem.
Returns:
Report `tuple(int,str)`: The report of the student's solution.
"""
# 从books/code_tests/name/中获取所有.in文件
directory = 'books/code_tests/'+name
# 如果目录不存在,报错
if not os.path.exists(directory):
status = ServiceExecStatus.FAILED
return ServiceResponse(status, f"The name {name} is not found.")
files = os.listdir(directory)
in_files = [file for file in files if file.endswith('.in')]
save_dir = os.path.dirname(filepath)
Report = ""
passed_num=0
failed_num=0
for file in in_files:
os.remove(os.path.join(save_dir, f"_{name}.out"))
cmd = "cat " + os.path.join(directory, file) + " | " + "python " + filepath +" > " + os.path.join(save_dir, f"_{name}.out")
os.system(cmd)
# 比较结果
re = ""
if compare_file(os.path.join(save_dir, f"_{name}.out"), os.path.join(directory, file.replace(".in", ".out"))):
re = "Passed {file}"
passed_num += 1
else:
re = "Failed {file}"
failed_num += 1
Report = Report + re + "\n"
os.remove(os.path.join(save_dir, f"_{name}.out"))
score = int (100 * passed_num / (passed_num + failed_num))
status = ServiceExecStatus.SUCCESS
return ServiceResponse(status, (Report, score))
def compare_file(path1:str, path2:str):
"""
Compare two files and return true if they are the same.
"""
with open(path1, 'r') as f1, open(path2, 'r') as f2:
# 逐行比较
for line1, line2 in zip(f1, f2):
if line1.strip() != line2.strip():
return False
return True
judge_tools = [judge]