From 5170ee720a666d2f6cab4d98a45754e69ce65ded Mon Sep 17 00:00:00 2001 From: brucemeek <113046530+brucemeek@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:09:05 -0500 Subject: [PATCH] roles update. More comments and docstrings --- metagpt/roles/architect.py | 24 ++++++- metagpt/roles/engineer.py | 110 ++++++++++++++++++------------- metagpt/roles/product_manager.py | 29 ++++++-- metagpt/roles/project_manager.py | 29 ++++++-- metagpt/roles/qa_engineer.py | 25 ++++++- metagpt/roles/seacher.py | 39 +++++++++-- 6 files changed, 193 insertions(+), 63 deletions(-) diff --git a/metagpt/roles/architect.py b/metagpt/roles/architect.py index 5fc7bdef7..d0756672e 100644 --- a/metagpt/roles/architect.py +++ b/metagpt/roles/architect.py @@ -11,10 +11,28 @@ from metagpt.roles import Role class Architect(Role): - """Architect: Listen to PRD, responsible for designing API, designing code files""" - def __init__(self, name="Bob", profile="Architect", goal="Design a concise, usable, complete python system", - constraints="Try to specify good open source tools as much as possible"): + """ + Represents an Architect role in a software development process. + + Attributes: + name (str): Name of the architect. + profile (str): Role profile, default is 'Architect'. + goal (str): Primary goal or responsibility of the architect. + constraints (str): Constraints or guidelines for the architect. + """ + + def __init__(self, + name: str = "Bob", + profile: str = "Architect", + goal: str = "Design a concise, usable, complete python system", + constraints: str = "Try to specify good open source tools as much as possible") -> None: + """Initializes the Architect with given attributes.""" super().__init__(name, profile, goal, constraints) + + # Initialize actions specific to the Architect role self._init_actions([WriteDesign]) + + # Set events or actions the Architect should watch or be aware of self._watch({WritePRD}) + \ No newline at end of file diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 7567b2ed9..a9963c25b 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -46,9 +46,27 @@ async def gather_ordered_k(coros, k) -> list: class Engineer(Role): - def __init__(self, name="Alex", profile="Engineer", goal="Write elegant, readable, extensible, efficient code", - constraints="The code you write should conform to code standard like PEP8, be modular, easy to read and maintain", - n_borg=1, use_code_review=False): + """ + Represents an Engineer role responsible for writing and possibly reviewing code. + + Attributes: + name (str): Name of the engineer. + profile (str): Role profile, default is 'Engineer'. + goal (str): Goal of the engineer. + constraints (str): Constraints for the engineer. + n_borg (int): Number of borgs. + use_code_review (bool): Whether to use code review. + todos (list): List of tasks. + """ + + def __init__(self, + name: str = "Alex", + profile: str = "Engineer", + goal: str = "Write elegant, readable, extensible, efficient code", + constraints: str = "The code should conform to standards like PEP8 and be modular and maintainable", + n_borg: int = 1, + use_code_review: bool = False) -> None: + """Initializes the Engineer role with given attributes.""" super().__init__(name, profile, goal, constraints) self._init_actions([WriteCode]) self.use_code_review = use_code_review @@ -142,47 +160,47 @@ class Engineer(Role): msg = Message(content="all done.", role=self.profile, cause_by=type(self._rc.todo)) return msg -async def _act_sp_precision(self) -> Message: - for todo in self.todos: - """ - # Select essential information from the historical data to reduce the length of the prompt (summarized from human experience): - 1. All from Architect - 2. All from ProjectManager - 3. Do we need other codes (currently needed)? - TODO: The goal is not to need it. After clear task decomposition, based on the design idea, you should be able to write a single file without needing other codes. If you can't, it means you need a clearer definition. This is the key to writing longer code. - """ - context = [] - msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode]) - for m in msg: - context.append(m.content) - context_str = "\n".join(context) - # Write code - code = await WriteCode().run( - context=context_str, - filename=todo - ) - # Code review + async def _act_sp_precision(self) -> Message: + for todo in self.todos: + """ + # Select essential information from the historical data to reduce the length of the prompt (summarized from human experience): + 1. All from Architect + 2. All from ProjectManager + 3. Do we need other codes (currently needed)? + TODO: The goal is not to need it. After clear task decomposition, based on the design idea, you should be able to write a single file without needing other codes. If you can't, it means you need a clearer definition. This is the key to writing longer code. + """ + context = [] + msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode]) + for m in msg: + context.append(m.content) + context_str = "\n".join(context) + # Write code + code = await WriteCode().run( + context=context_str, + filename=todo + ) + # Code review + if self.use_code_review: + try: + rewrite_code = await WriteCodeReview().run( + context=context_str, + code=code, + filename=todo + ) + code = rewrite_code + except Exception as e: + logger.error("code review failed!", e) + pass + self.write_file(todo, code) + msg = Message(content=code, role=self.profile, cause_by=WriteCode) + self._rc.memory.add(msg) + + logger.info(f'Done {self.get_workspace()} generating.') + msg = Message(content="all done.", role=self.profile, cause_by=WriteCode) + return msg + + async def _act(self) -> Message: + """Determines the mode of action based on whether code review is used.""" if self.use_code_review: - try: - rewrite_code = await WriteCodeReview().run( - context=context_str, - code=code, - filename=todo - ) - code = rewrite_code - except Exception as e: - logger.error("code review failed!", e) - pass - self.write_file(todo, code) - msg = Message(content=code, role=self.profile, cause_by=WriteCode) - self._rc.memory.add(msg) - - logger.info(f'Done {self.get_workspace()} generating.') - msg = Message(content="all done.", role=self.profile, cause_by=WriteCode) - return msg - -async def _act(self) -> Message: - if self.use_code_review: - return await self._act_sp_precision() - return await self._act_sp() - \ No newline at end of file + return await self._act_sp_precision() + return await self._act_sp() \ No newline at end of file diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index b89aac28c..9996e907a 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -10,9 +10,30 @@ from metagpt.roles import Role class ProductManager(Role): - def __init__(self, name="Alice", profile="Product Manager", goal="Efficiently create a successful product", - constraints=""): + """ + Represents a Product Manager role responsible for product development and management. + + Attributes: + name (str): Name of the product manager. + profile (str): Role profile, default is 'Product Manager'. + goal (str): Goal of the product manager. + constraints (str): Constraints or limitations for the product manager. + """ + + def __init__(self, + name: str = "Alice", + profile: str = "Product Manager", + goal: str = "Efficiently create a successful product", + constraints: str = "") -> None: + """ + Initializes the ProductManager role with given attributes. + + Args: + name (str): Name of the product manager. + profile (str): Role profile. + goal (str): Goal of the product manager. + constraints (str): Constraints or limitations for the product manager. + """ super().__init__(name, profile, goal, constraints) self._init_actions([WritePRD]) - self._watch([BossRequirement]) - \ No newline at end of file + self._watch([BossRequirement]) \ No newline at end of file diff --git a/metagpt/roles/project_manager.py b/metagpt/roles/project_manager.py index 0ad871b4c..dd4ba42ae 100644 --- a/metagpt/roles/project_manager.py +++ b/metagpt/roles/project_manager.py @@ -10,9 +10,30 @@ from metagpt.roles import Role class ProjectManager(Role): - def __init__(self, name="Eve", profile="Project Manager", - goal="Improve team efficiency and deliver with quality and quantity", constraints=""): + """ + Represents a Project Manager role responsible for overseeing project execution and team efficiency. + + Attributes: + name (str): Name of the project manager. + profile (str): Role profile, default is 'Project Manager'. + goal (str): Goal of the project manager. + constraints (str): Constraints or limitations for the project manager. + """ + + def __init__(self, + name: str = "Eve", + profile: str = "Project Manager", + goal: str = "Improve team efficiency and deliver with quality and quantity", + constraints: str = "") -> None: + """ + Initializes the ProjectManager role with given attributes. + + Args: + name (str): Name of the project manager. + profile (str): Role profile. + goal (str): Goal of the project manager. + constraints (str): Constraints or limitations for the project manager. + """ super().__init__(name, profile, goal, constraints) self._init_actions([WriteTasks]) - self._watch([WriteDesign]) - \ No newline at end of file + self._watch([WriteDesign]) \ No newline at end of file diff --git a/metagpt/roles/qa_engineer.py b/metagpt/roles/qa_engineer.py index 70968761f..1dfb0dc6e 100644 --- a/metagpt/roles/qa_engineer.py +++ b/metagpt/roles/qa_engineer.py @@ -10,6 +10,29 @@ from metagpt.roles import Role class QaEngineer(Role): - def __init__(self, name, profile, goal, constraints): + """ + Represents a Quality Assurance (QA) Engineer role responsible for writing tests to ensure software quality. + + Attributes: + name (str): Name of the QA engineer. + profile (str): Role profile. + goal (str): Goal of the QA engineer. + constraints (str): Constraints or limitations for the QA engineer. + """ + + def __init__(self, + name: str, + profile: str, + goal: str, + constraints: str) -> None: + """ + Initializes the QaEngineer role with given attributes. + + Args: + name (str): Name of the QA engineer. + profile (str): Role profile. + goal (str): Goal of the QA engineer. + constraints (str): Constraints or limitations for the QA engineer. + """ super().__init__(name, profile, goal, constraints) self._init_actions([WriteTest]) \ No newline at end of file diff --git a/metagpt/roles/seacher.py b/metagpt/roles/seacher.py index da617974e..0b6e089da 100644 --- a/metagpt/roles/seacher.py +++ b/metagpt/roles/seacher.py @@ -13,26 +13,55 @@ from metagpt.tools import SearchEngineType class Searcher(Role): - def __init__(self, name='Alice', profile='Smart Assistant', goal='Provide search services for users', - constraints='Answer is rich and complete', engine=SearchEngineType.SERPAPI_GOOGLE, **kwargs): + """ + Represents a Searcher role responsible for providing search services to users. + + Attributes: + name (str): Name of the searcher. + profile (str): Role profile. + goal (str): Goal of the searcher. + constraints (str): Constraints or limitations for the searcher. + engine (SearchEngineType): The type of search engine to use. + """ + + def __init__(self, + name: str = 'Alice', + profile: str = 'Smart Assistant', + goal: str = 'Provide search services for users', + constraints: str = 'Answer is rich and complete', + engine=SearchEngineType.SERPAPI_GOOGLE, + **kwargs) -> None: + """ + Initializes the Searcher role with given attributes. + + Args: + name (str): Name of the searcher. + profile (str): Role profile. + goal (str): Goal of the searcher. + constraints (str): Constraints or limitations for the searcher. + engine (SearchEngineType): The type of search engine to use. + """ super().__init__(name, profile, goal, constraints, **kwargs) self._init_actions([SearchAndSummarize(engine=engine)]) def set_search_func(self, search_func): + """Sets a custom search function for the searcher.""" action = SearchAndSummarize("", engine=SearchEngineType.CUSTOM_ENGINE, search_func=search_func) self._init_actions([action]) async def _act_sp(self) -> Message: + """Performs the search action in a single process.""" logger.info(f"{self._setting}: ready to {self._rc.todo}") response = await self._rc.todo.run(self._rc.memory.get(k=0)) - # logger.info(response) + if isinstance(response, ActionOutput): msg = Message(content=response.content, instruct_content=response.instruct_content, - role=self.profile, cause_by=type(self._rc.todo)) + role=self.profile, cause_by=type(self._rc.todo)) else: msg = Message(content=response, role=self.profile, cause_by=type(self._rc.todo)) self._rc.memory.add(msg) + return msg async def _act(self) -> Message: + """Determines the mode of action for the searcher.""" return await self._act_sp() - \ No newline at end of file