From c84f021467ccaf2385a4b1a95a837b9f63624ee8 Mon Sep 17 00:00:00 2001 From: Sirui Hong <34952977+stellaHSR@users.noreply.github.com> Date: Tue, 7 Nov 2023 20:45:14 +0800 Subject: [PATCH 1/5] Update README.md update paper citation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70460ceb4..34fc970df 100644 --- a/README.md +++ b/README.md @@ -299,8 +299,8 @@ ## Citation ```bibtex @misc{hong2023metagpt, - title={MetaGPT: Meta Programming for Multi-Agent Collaborative Framework}, - author={Sirui Hong and Xiawu Zheng and Jonathan Chen and Yuheng Cheng and Jinlin Wang and Ceyao Zhang and Zili Wang and Steven Ka Shing Yau and Zijuan Lin and Liyang Zhou and Chenyu Ran and Lingfeng Xiao and Chenglin Wu}, + title={MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework}, + author={Sirui Hong and Mingchen Zhuge and Jonathan Chen and Xiawu Zheng and Yuheng Cheng and Ceyao Zhang and Jinlin Wang and Zili Wang and Steven Ka Shing Yau and Zijuan Lin and Liyang Zhou and Chenyu Ran and Lingfeng Xiao and Chenglin Wu and Jürgen Schmidhuber}, year={2023}, eprint={2308.00352}, archivePrefix={arXiv}, From 0a57bad7425cfeb5ce22159eba2f2da9d7c95e9b Mon Sep 17 00:00:00 2001 From: Sirui Hong <34952977+stellaHSR@users.noreply.github.com> Date: Tue, 7 Nov 2023 20:46:59 +0800 Subject: [PATCH 2/5] Update README_JA.md update paper citation --- docs/README_JA.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README_JA.md b/docs/README_JA.md index 2b2c35a62..cfacff0b9 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -299,8 +299,8 @@ ## 引用 ```bibtex @misc{hong2023metagpt, - title={MetaGPT: Meta Programming for Multi-Agent Collaborative Framework}, - author={Sirui Hong and Xiawu Zheng and Jonathan Chen and Yuheng Cheng and Jinlin Wang and Ceyao Zhang and Zili Wang and Steven Ka Shing Yau and Zijuan Lin and Liyang Zhou and Chenyu Ran and Lingfeng Xiao and Chenglin Wu}, + title={MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework}, + author={Sirui Hong and Mingchen Zhuge and Jonathan Chen and Xiawu Zheng and Yuheng Cheng and Ceyao Zhang and Jinlin Wang and Zili Wang and Steven Ka Shing Yau and Zijuan Lin and Liyang Zhou and Chenyu Ran and Lingfeng Xiao and Chenglin Wu and Jürgen Schmidhuber}, year={2023}, eprint={2308.00352}, archivePrefix={arXiv}, From 56c2a162acc4221c1966f41dcc4f2d54549b26a2 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 8 Nov 2023 19:42:30 +0800 Subject: [PATCH 3/5] change PROJECT_ROOT setting & SoftwareCompany -> Team --- README.md | 4 +- docs/README_CN.md | 4 +- docs/README_JA.md | 4 +- examples/debate.py | 99 +++++++----------------- metagpt/const.py | 8 +- metagpt/roles/engineer.py | 1 + metagpt/{software_company.py => team.py} | 10 +-- startup.py | 4 +- tests/metagpt/roles/test_ui.py | 4 +- tests/metagpt/test_software_company.py | 6 +- 10 files changed, 54 insertions(+), 90 deletions(-) rename metagpt/{software_company.py => team.py} (79%) diff --git a/README.md b/README.md index 70460ceb4..1171ab83c 100644 --- a/README.md +++ b/README.md @@ -270,12 +270,12 @@ ### Usage ### Code walkthrough ```python -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer async def startup(idea: str, investment: float = 3.0, n_round: int = 5): """Run a startup. Be a boss.""" - company = SoftwareCompany() + company = Team() company.hire([ProductManager(), Architect(), ProjectManager(), Engineer()]) company.invest(investment) company.start_project(idea) diff --git a/docs/README_CN.md b/docs/README_CN.md index 9d6f34c11..3aa0cb05d 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -190,12 +190,12 @@ ### 使用 ### 代码实现 ```python -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer async def startup(idea: str, investment: float = 3.0, n_round: int = 5): """运行一个创业公司。做一个老板""" - company = SoftwareCompany() + company = Team() company.hire([ProductManager(), Architect(), ProjectManager(), Engineer()]) company.invest(investment) company.start_project(idea) diff --git a/docs/README_JA.md b/docs/README_JA.md index 2b2c35a62..538208631 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -270,12 +270,12 @@ ### 使用方法 ### コードウォークスルー ```python -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer async def startup(idea: str, investment: float = 3.0, n_round: int = 5): """スタートアップを実行する。ボスになる。""" - company = SoftwareCompany() + company = Team() company.hire([ProductManager(), Architect(), ProjectManager(), Engineer()]) company.invest(investment) company.start_project(idea) diff --git a/examples/debate.py b/examples/debate.py index 05db28070..4db7567f0 100644 --- a/examples/debate.py +++ b/examples/debate.py @@ -7,14 +7,14 @@ import asyncio import platform import fire -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team from metagpt.actions import Action, BossRequirement from metagpt.roles import Role from metagpt.schema import Message from metagpt.logs import logger -class ShoutOut(Action): - """Action: Shout out loudly in a debate (quarrel)""" +class SpeakAloud(Action): + """Action: Speak out aloud in a debate (quarrel)""" PROMPT_TEMPLATE = """ ## BACKGROUND @@ -27,7 +27,7 @@ class ShoutOut(Action): craft a strong and emotional response in 80 words, in {name}'s rhetoric and viewpoints, your will argue: """ - def __init__(self, name="ShoutOut", context=None, llm=None): + def __init__(self, name="SpeakAloud", context=None, llm=None): super().__init__(name, context, llm) async def run(self, context: str, name: str, opponent_name: str): @@ -39,96 +39,55 @@ class ShoutOut(Action): return rsp -class Trump(Role): +class Debator(Role): def __init__( self, - name: str = "Trump", - profile: str = "Republican", + name: str, + profile: str, + opponent_name: str, **kwargs, ): super().__init__(name, profile, **kwargs) - self._init_actions([ShoutOut]) - self._watch([ShoutOut]) - self.name = "Trump" - self.opponent_name = "Biden" + self._init_actions([SpeakAloud]) + self._watch([BossRequirement, SpeakAloud]) + self.name = name + self.opponent_name = opponent_name 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.send_to == self.name] return len(self._rc.news) async def _act(self) -> Message: logger.info(f"{self._setting}: ready to {self._rc.todo}") + todo = self._rc.todo # An instance of SpeakAloud - msg_history = self._rc.memory.get_by_actions([ShoutOut]) - context = [] - for m in msg_history: - context.append(str(m)) - context = "\n".join(context) + memories = self.get_memories() + context = "\n".join(f"{msg.sent_from}: {msg.content}" for msg in memories) + # print(context) - rsp = await ShoutOut().run(context=context, name=self.name, opponent_name=self.opponent_name) + rsp = await todo.run(context=context, name=self.name, opponent_name=self.opponent_name) msg = Message( content=rsp, role=self.profile, - cause_by=ShoutOut, + cause_by=type(todo), sent_from=self.name, send_to=self.opponent_name, ) return msg -class Biden(Role): - def __init__( - self, - name: str = "Biden", - profile: str = "Democrat", - **kwargs, - ): - super().__init__(name, profile, **kwargs) - self._init_actions([ShoutOut]) - self._watch([BossRequirement, ShoutOut]) - self.name = "Biden" - self.opponent_name = "Trump" - - async def _observe(self) -> int: - 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] - 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]) - context = [] - for m in msg_history: - context.append(str(m)) - context = "\n".join(context) - - rsp = await ShoutOut().run(context=context, name=self.name, opponent_name=self.opponent_name) - - msg = Message( - content=rsp, - role=self.profile, - cause_by=ShoutOut, - sent_from=self.name, - send_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): - """We reuse the startup paradigm for roles to interact with each other. - Now we run a startup of presidents and watch they quarrel. :) """ - company = SoftwareCompany() - company.hire([Biden(), Trump()]) - company.invest(investment) - company.start_project(idea) - await company.run(n_round=n_round) +async def debate(idea: str, investment: float = 3.0, n_round: int = 5): + """Run a team of presidents and watch they quarrel. :) """ + Biden = Debator(name="Biden", profile="Democrat", opponent_name="Trump") + Trump = Debator(name="Trump", profile="Republican", opponent_name="Biden") + team = Team() + team.hire([Biden, Trump]) + team.invest(investment) + team.start_project(idea, send_to="Biden") # send debate topic to Biden and let him speak first + await team.run(n_round=n_round) def main(idea: str, investment: float = 3.0, n_round: int = 10): @@ -141,7 +100,7 @@ def main(idea: str, investment: float = 3.0, n_round: int = 10): """ if platform.system() == "Windows": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - asyncio.run(startup(idea, investment, n_round)) + asyncio.run(debate(idea, investment, n_round)) if __name__ == '__main__': diff --git a/metagpt/const.py b/metagpt/const.py index 7f3f87dfa..86b2b6c87 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -6,7 +6,7 @@ @File : const.py """ from pathlib import Path - +from loguru import logger def get_project_root(): """Search upwards to find the project root directory.""" @@ -17,10 +17,14 @@ def get_project_root(): or (current_path / ".project_root").exists() or (current_path / ".gitignore").exists() ): + # use metagpt with git clone will land here return current_path parent_path = current_path.parent if parent_path == current_path: - raise Exception("Project root not found.") + # use metagpt with pip install will land here + cwd = Path.cwd() + logger.info(f"RPOJECT_ROOT set to current working directory: {str(cwd)}") + return cwd current_path = parent_path diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 6d65575a8..1f6685b38 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -207,6 +207,7 @@ class Engineer(Role): async def _act(self) -> Message: """Determines the mode of action based on whether code review is used.""" + logger.info(f"{self._setting}: ready to WriteCode") if self.use_code_review: return await self._act_sp_precision() return await self._act_sp() diff --git a/metagpt/software_company.py b/metagpt/team.py similarity index 79% rename from metagpt/software_company.py rename to metagpt/team.py index b2bd18c58..67d3ecec8 100644 --- a/metagpt/software_company.py +++ b/metagpt/team.py @@ -16,10 +16,10 @@ from metagpt.schema import Message from metagpt.utils.common import NoMoneyException -class SoftwareCompany(BaseModel): +class Team(BaseModel): """ - Software Company: Possesses a team, SOP (Standard Operating Procedures), and a platform for instant messaging, - dedicated to writing executable code. + Team: Possesses one or more roles (agents), SOP (Standard Operating Procedures), and a platform for instant messaging, + dedicated to perform any multi-agent activity, such as collaboratively writing executable code. """ environment: Environment = Field(default_factory=Environment) investment: float = Field(default=10.0) @@ -42,10 +42,10 @@ class SoftwareCompany(BaseModel): if CONFIG.total_cost > CONFIG.max_budget: raise NoMoneyException(CONFIG.total_cost, f'Insufficient funds: {CONFIG.max_budget}') - def start_project(self, idea): + def start_project(self, idea, send_to: str = ""): """Start a project from publishing boss requirement.""" self.idea = idea - self.environment.publish_message(Message(role="BOSS", content=idea, cause_by=BossRequirement)) + self.environment.publish_message(Message(role="Human", content=idea, cause_by=BossRequirement, send_to=send_to)) def _save(self): logger.info(self.json()) diff --git a/startup.py b/startup.py index e2a903c9b..e9fbf94d3 100644 --- a/startup.py +++ b/startup.py @@ -11,7 +11,7 @@ from metagpt.roles import ( ProjectManager, QaEngineer, ) -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team async def startup( @@ -23,7 +23,7 @@ async def startup( implement: bool = True, ): """Run a startup. Be a boss.""" - company = SoftwareCompany() + company = Team() company.hire( [ ProductManager(), diff --git a/tests/metagpt/roles/test_ui.py b/tests/metagpt/roles/test_ui.py index 285bff323..d58d31bd9 100644 --- a/tests/metagpt/roles/test_ui.py +++ b/tests/metagpt/roles/test_ui.py @@ -2,7 +2,7 @@ # @Date : 2023/7/22 02:40 # @Author : stellahong (stellahong@fuzhi.ai) # -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team from metagpt.roles import ProductManager from tests.metagpt.roles.ui_role import UI @@ -15,7 +15,7 @@ def test_add_ui(): async def test_ui_role(idea: str, investment: float = 3.0, n_round: int = 5): """Run a startup. Be a boss.""" - company = SoftwareCompany() + company = Team() company.hire([ProductManager(), UI()]) company.invest(investment) company.start_project(idea) diff --git a/tests/metagpt/test_software_company.py b/tests/metagpt/test_software_company.py index 00538442c..4fc651f52 100644 --- a/tests/metagpt/test_software_company.py +++ b/tests/metagpt/test_software_company.py @@ -8,12 +8,12 @@ import pytest from metagpt.logs import logger -from metagpt.software_company import SoftwareCompany +from metagpt.team import Team @pytest.mark.asyncio -async def test_software_company(): - company = SoftwareCompany() +async def test_team(): + company = Team() company.start_project("做一个基础搜索引擎,可以支持知识库") history = await company.run(n_round=5) logger.info(history) From a5bc55f18a6cab172e10ad77270ae9ed1d1f7e5a Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Thu, 9 Nov 2023 16:41:24 +0800 Subject: [PATCH 4/5] add deprecated warnings --- metagpt/software_company.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 metagpt/software_company.py diff --git a/metagpt/software_company.py b/metagpt/software_company.py new file mode 100644 index 000000000..d44a0068a --- /dev/null +++ b/metagpt/software_company.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/5/12 00:30 +@Author : alexanderwu +@File : software_company.py +""" +from metagpt.team import Team as SoftwareCompany + +import warnings +warnings.warn("metagpt.software_company is deprecated and will be removed in the future" + "Please use metagpt.team instead. SoftwareCompany class is now named as Team.", + DeprecationWarning, 2) From f0098f0ae9fef395b3842f15c8410eb29e6a916b Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Thu, 9 Nov 2023 16:51:20 +0800 Subject: [PATCH 5/5] add PROJECT_ROOT logging info --- metagpt/const.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metagpt/const.py b/metagpt/const.py index 86b2b6c87..407ce803a 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -18,12 +18,13 @@ def get_project_root(): or (current_path / ".gitignore").exists() ): # use metagpt with git clone will land here + logger.info(f"PROJECT_ROOT set to {str(current_path)}") return current_path parent_path = current_path.parent if parent_path == current_path: # use metagpt with pip install will land here cwd = Path.cwd() - logger.info(f"RPOJECT_ROOT set to current working directory: {str(cwd)}") + logger.info(f"PROJECT_ROOT set to current working directory: {str(cwd)}") return cwd current_path = parent_path