refactor: Refactor Message transmission & filtering

This commit is contained in:
莘权 马 2023-11-01 20:08:58 +08:00
parent 5e8ada5cff
commit 545d77ce0d
30 changed files with 658 additions and 296 deletions

View file

@ -1,22 +1,24 @@
'''
"""
Filename: MetaGPT/examples/agent_creator.py
Created Date: Tuesday, September 12th 2023, 3:28:37 pm
Author: garylin2099
'''
@Modified By: mashenquan, 2023-11-1. Standardize the usage of message filtering-related features.
"""
import re
from metagpt.const import PROJECT_ROOT, WORKSPACE_ROOT
from metagpt.actions import Action
from metagpt.const import PROJECT_ROOT, WORKSPACE_ROOT
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
from metagpt.utils.common import get_object_name
with open(PROJECT_ROOT / "examples/build_customized_agent.py", "r") as f:
# use official example script to guide AgentCreator
MULTI_ACTION_AGENT_CODE_EXAMPLE = f.read()
class CreateAgent(Action):
class CreateAgent(Action):
PROMPT_TEMPLATE = """
### BACKGROUND
You are using an agent framework called metagpt to write agents capable of different actions,
@ -34,7 +36,6 @@ class CreateAgent(Action):
"""
async def run(self, example: str, instruction: str):
prompt = self.PROMPT_TEMPLATE.format(example=example, instruction=instruction)
# logger.info(prompt)
@ -46,13 +47,14 @@ class CreateAgent(Action):
@staticmethod
def parse_code(rsp):
pattern = r'```python(.*)```'
pattern = r"```python(.*)```"
match = re.search(pattern, rsp, re.DOTALL)
code_text = match.group(1) if match else ""
with open(WORKSPACE_ROOT / "agent_created_agent.py", "w") as f:
f.write(code_text)
return code_text
class AgentCreator(Role):
def __init__(
self,
@ -72,15 +74,15 @@ class AgentCreator(Role):
instruction = msg.content
code_text = await CreateAgent().run(example=self.agent_template, instruction=instruction)
msg = Message(content=code_text, role=self.profile, cause_by=todo)
msg = Message(content=code_text, role=self.profile, cause_by=get_object_name(todo))
return msg
if __name__ == "__main__":
import asyncio
async def main():
agent_template = MULTI_ACTION_AGENT_CODE_EXAMPLE
creator = AgentCreator(agent_template=agent_template)

View file

@ -1,21 +1,23 @@
'''
"""
Filename: MetaGPT/examples/build_customized_agent.py
Created Date: Tuesday, September 19th 2023, 6:52:25 pm
Author: garylin2099
'''
@Modified By: mashenquan, 2023-11-1. Standardize the usage of message filtering-related features.
"""
import asyncio
import re
import subprocess
import asyncio
import fire
from metagpt.actions import Action
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
from metagpt.utils.common import get_object_name
class SimpleWriteCode(Action):
PROMPT_TEMPLATE = """
Write a python function that can {instruction} and provide two runnnable test cases.
Return ```python your_code_here ``` with NO other texts,
@ -35,7 +37,6 @@ class SimpleWriteCode(Action):
super().__init__(name, context, llm)
async def run(self, instruction: str):
prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
rsp = await self._aask(prompt)
@ -46,11 +47,12 @@ class SimpleWriteCode(Action):
@staticmethod
def parse_code(rsp):
pattern = r'```python(.*)```'
pattern = r"```python(.*)```"
match = re.search(pattern, rsp, re.DOTALL)
code_text = match.group(1) if match else rsp
return code_text
class SimpleRunCode(Action):
def __init__(self, name="SimpleRunCode", context=None, llm=None):
super().__init__(name, context, llm)
@ -61,6 +63,7 @@ class SimpleRunCode(Action):
logger.info(f"{code_result=}")
return code_result
class SimpleCoder(Role):
def __init__(
self,
@ -75,14 +78,15 @@ class SimpleCoder(Role):
logger.info(f"{self._setting}: ready to {self._rc.todo}")
todo = self._rc.todo
msg = self._rc.memory.get()[-1] # retrieve the latest memory
msg = self._rc.memory.get()[-1] # retrieve the latest memory
instruction = msg.content
code_text = await SimpleWriteCode().run(instruction)
msg = Message(content=code_text, role=self.profile, cause_by=todo)
msg = Message(content=code_text, role=self.profile, cause_by=get_object_name(todo))
return msg
class RunnableCoder(Role):
def __init__(
self,
@ -116,7 +120,7 @@ class RunnableCoder(Role):
code_text = msg.content
result = await SimpleRunCode().run(code_text)
msg = Message(content=result, role=self.profile, cause_by=todo)
msg = Message(content=result, role=self.profile, cause_by=get_object_name(todo))
self._rc.memory.add(msg)
return msg
@ -128,6 +132,7 @@ class RunnableCoder(Role):
await self._act()
return Message(content="All job done", role=self.profile)
def main(msg="write a function that calculates the sum of a list"):
# role = SimpleCoder()
role = RunnableCoder()
@ -135,5 +140,6 @@ def main(msg="write a function that calculates the sum of a list"):
result = asyncio.run(role.run(msg))
logger.info(result)
if __name__ == '__main__':
if __name__ == "__main__":
fire.Fire(main)

View file

@ -1,17 +1,20 @@
'''
"""
Filename: MetaGPT/examples/debate.py
Created Date: Tuesday, September 19th 2023, 6:52:25 pm
Author: garylin2099
'''
@Modified By: mashenquan, 2023-11-1. Standardize the usage of message filtering-related features.
"""
import asyncio
import platform
import fire
from metagpt.software_company import SoftwareCompany
from metagpt.actions import Action, BossRequirement
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
from metagpt.software_company import SoftwareCompany
class ShoutOut(Action):
"""Action: Shout out loudly in a debate (quarrel)"""
@ -31,7 +34,6 @@ class ShoutOut(Action):
super().__init__(name, context, llm)
async def run(self, context: str, name: str, opponent_name: str):
prompt = self.PROMPT_TEMPLATE.format(context=context, name=name, opponent_name=opponent_name)
# logger.info(prompt)
@ -39,6 +41,7 @@ class ShoutOut(Action):
return rsp
class Trump(Role):
def __init__(
self,
@ -55,13 +58,13 @@ class Trump(Role):
async def _observe(self) -> int:
await super()._observe()
# accept messages sent (from opponent) to self, disregard own messages from the last round
self._rc.news = [msg for msg in self._rc.news if msg.send_to == self.name]
self._rc.news = [msg for msg in self._rc.news if msg.is_recipient({self.name})]
return len(self._rc.news)
async def _act(self) -> Message:
logger.info(f"{self._setting}: ready to {self._rc.todo}")
msg_history = self._rc.memory.get_by_actions([ShoutOut])
msg_history = self._rc.memory.get_by_actions([ShoutOut.get_class_name()])
context = []
for m in msg_history:
context.append(str(m))
@ -72,13 +75,14 @@ class Trump(Role):
msg = Message(
content=rsp,
role=self.profile,
cause_by=ShoutOut,
sent_from=self.name,
send_to=self.opponent_name,
cause_by=ShoutOut.get_class_name(),
tx_from=self.name,
tx_to=self.opponent_name,
)
return msg
class Biden(Role):
def __init__(
self,
@ -96,13 +100,14 @@ class Biden(Role):
await super()._observe()
# accept the very first human instruction (the debate topic) or messages sent (from opponent) to self,
# disregard own messages from the last round
self._rc.news = [msg for msg in self._rc.news if msg.cause_by == BossRequirement or msg.send_to == self.name]
message_filter = {BossRequirement.get_class_name(), self.name}
self._rc.news = [msg for msg in self._rc.news if msg.is_recipient(message_filter)]
return len(self._rc.news)
async def _act(self) -> Message:
logger.info(f"{self._setting}: ready to {self._rc.todo}")
msg_history = self._rc.memory.get_by_actions([BossRequirement, ShoutOut])
msg_history = self._rc.memory.get_by_actions([BossRequirement.get_class_name(), ShoutOut.get_class_name()])
context = []
for m in msg_history:
context.append(str(m))
@ -113,17 +118,19 @@ class Biden(Role):
msg = Message(
content=rsp,
role=self.profile,
cause_by=ShoutOut,
sent_from=self.name,
send_to=self.opponent_name,
cause_by=ShoutOut.get_class_name(),
tx_from=self.name,
tx_to=self.opponent_name,
)
return msg
async def startup(idea: str, investment: float = 3.0, n_round: int = 5,
code_review: bool = False, run_tests: bool = False):
async def startup(
idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False, run_tests: bool = False
):
"""We reuse the startup paradigm for roles to interact with each other.
Now we run a startup of presidents and watch they quarrel. :) """
Now we run a startup of presidents and watch they quarrel. :)"""
company = SoftwareCompany()
company.hire([Biden(), Trump()])
company.invest(investment)
@ -133,7 +140,7 @@ async def startup(idea: str, investment: float = 3.0, n_round: int = 5,
def main(idea: str, investment: float = 3.0, n_round: int = 10):
"""
:param idea: Debate topic, such as "Topic: The U.S. should commit more in climate change fighting"
:param idea: Debate topic, such as "Topic: The U.S. should commit more in climate change fighting"
or "Trump: Climate change is a hoax"
:param investment: contribute a certain dollar amount to watch the debate
:param n_round: maximum rounds of the debate
@ -144,5 +151,5 @@ def main(idea: str, investment: float = 3.0, n_round: int = 10):
asyncio.run(startup(idea, investment, n_round))
if __name__ == '__main__':
if __name__ == "__main__":
fire.Fire(main)

View file

@ -4,6 +4,7 @@
@Time : 2023/9/13 12:36
@Author : femto Zheng
@File : sk_agent.py
@Modified By: mashenquan, 2023-11-1. Standardize the usage of message filtering-related features.
"""
import asyncio
@ -39,7 +40,7 @@ async def basic_planner_example():
role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill")
role.import_skill(TextSkill(), "TextSkill")
# using BasicPlanner
await role.run(Message(content=task, cause_by=BossRequirement))
await role.run(Message(content=task, cause_by=BossRequirement.get_class_name()))
async def sequential_planner_example():
@ -53,7 +54,7 @@ async def sequential_planner_example():
role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill")
role.import_skill(TextSkill(), "TextSkill")
# using BasicPlanner
await role.run(Message(content=task, cause_by=BossRequirement))
await role.run(Message(content=task, cause_by=BossRequirement.get_class_name()))
async def basic_planner_web_search_example():
@ -64,7 +65,7 @@ async def basic_planner_web_search_example():
role.import_skill(SkSearchEngine(), "WebSearchSkill")
# role.import_semantic_skill_from_directory(skills_directory, "QASkill")
await role.run(Message(content=task, cause_by=BossRequirement))
await role.run(Message(content=task, cause_by=BossRequirement.get_class_name()))
async def action_planner_example():
@ -75,7 +76,7 @@ async def action_planner_example():
role.import_skill(TimeSkill(), "time")
role.import_skill(TextSkill(), "text")
task = "What is the sum of 110 and 990?"
await role.run(Message(content=task, cause_by=BossRequirement)) # it will choose mathskill.Add
await role.run(Message(content=task, cause_by=BossRequirement.get_class_name())) # it will choose mathskill.Add
if __name__ == "__main__":