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)