init
This commit is contained in:
0
AlgoriAgent/projects/algoriAgent/agent/__init__.py
Normal file
0
AlgoriAgent/projects/algoriAgent/agent/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
476
AlgoriAgent/projects/algoriAgent/agent/algori_agent.py
Normal file
476
AlgoriAgent/projects/algoriAgent/agent/algori_agent.py
Normal 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
|
||||
@@ -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")
|
||||
Reference in New Issue
Block a user