From 349c6d99c6a6f7f127de92895b15af40aba3c79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Sat, 22 Jul 2023 09:47:42 +0800 Subject: [PATCH 1/7] save code --- metagpt/actions/write_code.py | 33 ++++++++------- metagpt/actions/write_code_review.py | 60 ++++++++++++++++---------- metagpt/roles/engineer.py | 63 ++++++++++++++++++++++++++-- startup.py | 13 ++++-- 4 files changed, 124 insertions(+), 45 deletions(-) diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index de06baaf5..60d9b5e71 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -11,6 +11,7 @@ from metagpt.const import WORKSPACE_ROOT from metagpt.logs import logger from metagpt.schema import Message from metagpt.utils.common import CodeParser +from tenacity import retry, stop_after_attempt, wait_fixed PROMPT_TEMPLATE = """ # Context @@ -19,12 +20,11 @@ PROMPT_TEMPLATE = """ NOTICE 1. Role: You are an engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) 2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets -3. Attention1: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code. -4. Attention2: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. -5. Attention3: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN. -6. Think before writing: What should be implemented and provided in this document? -7. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. -Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. +3. Attention1: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. +4. Attention2: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN. +5. Think before writing: What should be implemented and provided in this document? +6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. +7. Do not use public member functions that do not exist in your design. ## {filename}: Write code with triple quoto. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. @@ -43,7 +43,7 @@ class WriteCode(Action): def _is_invalid(self, filename): return any(i in filename for i in ["mp3", "wav"]) - def _save(self, context, filename, code_rsp): + def _save(self, context, filename, code): # logger.info(filename) # logger.info(code_rsp) if self._is_invalid(filename): @@ -57,16 +57,19 @@ class WriteCode(Action): ws_path = ws_path / ws_name code_path = ws_path / filename code_path.parent.mkdir(parents=True, exist_ok=True) - code = CodeParser.parse_code(block="", text=code_rsp) code_path.write_text(code) logger.info(f"Saving Code to {code_path}") - async def run(self, **kwargs): - prompt = PROMPT_TEMPLATE.format(**kwargs) - filename = kwargs['filename'] - context = kwargs['context'] - logger.info(f'Writing {filename}..') + @retry(stop=stop_after_attempt(2), wait=wait_fixed(1)) + async def write_code(self, prompt): code_rsp = await self._aask(prompt) + code = CodeParser.parse_code(block="", text=code_rsp) + return code + + async def run(self, context, filename): + prompt = PROMPT_TEMPLATE.format(context=context, filename=filename) + logger.info(f'Writing {filename}..') + code = await self.write_code(prompt) # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) - self._save(context, filename, code_rsp) - return code_rsp + # self._save(context, filename, code) + return code diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index d7151197a..685f20834 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -7,38 +7,52 @@ """ from metagpt.actions.action import Action +from metagpt.logs import logger +from metagpt.schema import Message +from metagpt.utils.common import CodeParser +from tenacity import retry, stop_after_attempt, wait_fixed PROMPT_TEMPLATE = """ -Please review the following code: +# Context +{context} + +# Code{filename} +``` {code} +``` +----- +NOTICE +1. Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language). +2. Task 1: Based on the following context and code, conduct a code review and provide improvement suggestions. +2. Task 2: Rewrite the code based on the improvement suggestions, ensure the code is complete and do not omit anything. +3. Check 0: Is the code implemented as per the requirements? +4. Check 1: Are there any issues with the code logic? +5. Check 2: Does the existing code follow the "data structure and interface definition"? +6. Check 3: Is the existing code complete and functional? +7. Check 4: Does the code have unnecessary dependencies? -The main aspects you need to focus on include but are not limited to the code structure, coding standards, possible errors, and improvement suggestions. +## Code Review: Provide key, clear, concise, and specific code modification suggestions, up to 5. + +## {filename}: Write code with triple quotes. Do your utmost to optimize THIS SINGLE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. +Ensure that the functionality of the rewritten code is consistent with the source code. -Please write your code review: """ class WriteCodeReview(Action): - def __init__(self, name, context=None, llm=None): + def __init__(self, name="WriteCodeReview", context: list[Message] = None, llm=None): super().__init__(name, context, llm) - async def run(self, code): - """ - Generate a code review for the given code. + @retry(stop=stop_after_attempt(2), wait=wait_fixed(1)) + async def write_code(self, prompt): + code_rsp = await self._aask(prompt) + code = CodeParser.parse_code(block="", text=code_rsp) + return code - :param code: The code to be reviewed. - :type code: str - :return: The code review. - :rtype: str - """ - # Set the context for the llm model - self.context = {"code": code} - - # Generate the prompt - prompt = PROMPT_TEMPLATE.format(**self.context) - - # Generate the code review - self.input_data = prompt - self.output_data = await self._aask(prompt) - - return self.output_data + async def run(self, context, code, filename): + prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=filename) + logger.info(f'Code review {filename}..') + code = await self.write_code(prompt) + # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) + # self._save(context, filename, code) + return code diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index c8cebf680..ac6e9043e 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -14,7 +14,7 @@ from pathlib import Path from metagpt.const import WORKSPACE_ROOT from metagpt.logs import logger from metagpt.roles import Role -from metagpt.actions import WriteCode, RunCode, DebugError, WriteTasks, WriteDesign +from metagpt.actions import WriteCode, WriteCodeReview, WriteTasks, WriteDesign from metagpt.schema import Message from metagpt.utils.common import CodeParser from collections import OrderedDict @@ -50,9 +50,12 @@ 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): + n_borg=1, use_code_review=False): super().__init__(name, profile, goal, constraints) self._init_actions([WriteCode]) + self.use_code_review = use_code_review + if self.use_code_review: + self._init_actions([WriteCode, WriteCodeReview]) self._watch([WriteTasks]) self.todos = [] self.n_borg = n_borg @@ -78,7 +81,8 @@ class Engineer(Role): if not msg: return WORKSPACE_ROOT / 'src' workspace = self.parse_workspace(msg) - return WORKSPACE_ROOT / workspace + # Codes are written in workspace/{package_name}/{package_name} + return WORKSPACE_ROOT / workspace / workspace def recreate_workspace(self): workspace = self.get_workspace() @@ -132,6 +136,57 @@ class Engineer(Role): # logger.info(todo) # logger.info(code_rsp) # code = self.parse_code(code_rsp) + self.write_file(todo, code_rsp) + msg = Message(content=code_rsp, role=self.profile, cause_by=type(self._rc.todo)) + self._rc.memory.add(msg) + + logger.info(f'Done {self.get_workspace()} generating.') + msg = Message(content="all done.", role=self.profile, cause_by=type(self._rc.todo)) + return msg + + async def _act_sp_precision(self) -> Message: + """ + # 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结) + 1. ProductManager 分析和需求 + 2. Architect全部 + 3. ProjectManager全部 + 4. 是否需要其他代码?(目标是不需要。在任务拆分清楚后,根据设计思路, + 不需要其他代码也能够写清楚单个文件, + 如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键) + :return: + """ + context = [] + for msg in self._rc.history: + # todo: 需要再抽象一下 + if msg.role in ["Architect", "Project Manager"]: + context.append(msg.content) + if msg.role in ["Product Manager"]: + tmp_msg = "" + if msg.instruct_content: + temp_str = str(msg.instruct_content.dict().get( + "Requirement Analysis")) + tmp_msg += "## Requirement Analysis\n" + temp_str + "\n" + temp_str = str(msg.instruct_content.dict().get("Requirement Pool")) + tmp_msg += "## Requirement Pool\n" + temp_str + "\n" + context.append(tmp_msg) + context_str = "\n".join(context) + logger.debug(f'context: {context_str}') + for todo in self.todos: + code_rsp = await WriteCode().run( + context=context_str, + filename=todo + ) + try: + code = await WriteCodeReview().run( + context=context_str, + code=code_rsp, + filename=todo + ) + code_rsp = code + except Exception as e: + logger.error("code review failed!", e) + pass + self.write_file(todo, code_rsp) msg = Message(content=code_rsp, role=self.profile, cause_by=type(self._rc.todo)) self._rc.memory.add(msg) @@ -140,4 +195,6 @@ class Engineer(Role): return msg async def _act(self) -> Message: + if self.use_code_review: + return await self._act_sp_precision() return await self._act_sp() diff --git a/startup.py b/startup.py index fca731541..a7e44981a 100644 --- a/startup.py +++ b/startup.py @@ -6,23 +6,28 @@ from metagpt.software_company import SoftwareCompany from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer -async def startup(idea: str, investment: float = 3.0, n_round: int = 5): +async def startup(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False): """Run a startup. Be a boss.""" company = SoftwareCompany() - company.hire([ProductManager(), Architect(), ProjectManager(), Engineer(n_borg=5)]) + company.hire([ProductManager(), + Architect(), + ProjectManager(), + Engineer(n_borg=5, use_code_review=code_review)]) company.invest(investment) company.start_project(idea) await company.run(n_round=n_round) -def main(idea: str, investment: float = 3.0, n_round: int = 5): +def main(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False): """ We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities. :param idea: Your innovative idea, such as "Creating a snake game." :param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company. + :param n_round: + :param code_review: Whether to use code review. :return: """ - asyncio.run(startup(idea, investment, n_round)) + asyncio.run(startup(idea, investment, n_round, code_review)) if __name__ == '__main__': From b4ca2538351e7b03891852636282486ae0cc171b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Sat, 22 Jul 2023 21:32:07 +0800 Subject: [PATCH 2/7] save code --- metagpt/actions/write_code_review.py | 54 ++++++++++++++++++++-------- metagpt/roles/engineer.py | 15 ++------ 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 685f20834..7d21b546a 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -13,31 +13,54 @@ from metagpt.utils.common import CodeParser from tenacity import retry, stop_after_attempt, wait_fixed PROMPT_TEMPLATE = """ +NOTICE +Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language). +ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD WRITE BEFORE the code and triple quote. Output format carefully referenced "Format example". + +## Code Review: Based on the following context and code, and flowing the check list, Provide key, clear, concise, and specific code modification suggestions, up to 5. +``` +1. Check 0: Is the code implemented as per the requirements? +2. Check 1: Are there any issues with the code logic? +3. Check 2: Does the existing code follow the "Data structures and interface definitions"? +4. Check 3: Is there a function in the code that is omitted or not fully implemented that needs to be implemented? +5. Check 4: Does the code have unnecessary or lack dependencies? +6. Check 5: Does the code have ? +``` + +## Rewrite Code: {filename} Base on Code Review and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE.Ensure that the functionality of the rewritten code is consistent with the "Data structures and interface definitions" and ensure the code is complete and do not omit anything. +----- # Context {context} -# Code{filename} +## Code: {filename} ``` {code} ``` ----- -NOTICE -1. Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language). -2. Task 1: Based on the following context and code, conduct a code review and provide improvement suggestions. -2. Task 2: Rewrite the code based on the improvement suggestions, ensure the code is complete and do not omit anything. -3. Check 0: Is the code implemented as per the requirements? -4. Check 1: Are there any issues with the code logic? -5. Check 2: Does the existing code follow the "data structure and interface definition"? -6. Check 3: Is the existing code complete and functional? -7. Check 4: Does the code have unnecessary dependencies? -## Code Review: Provide key, clear, concise, and specific code modification suggestions, up to 5. - -## {filename}: Write code with triple quotes. Do your utmost to optimize THIS SINGLE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. -Ensure that the functionality of the rewritten code is consistent with the source code. +## Format example +----- +{format_example} +----- """ +FORMAT_EXAMPLE = """ + +## Code Review +1. The code ... +2. ... +3. ... +4. ... +5. ... + +## Rewrite Code: {filename} +```python +... +``` +""" + + class WriteCodeReview(Action): def __init__(self, name="WriteCodeReview", context: list[Message] = None, llm=None): @@ -50,7 +73,8 @@ class WriteCodeReview(Action): return code async def run(self, context, code, filename): - prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=filename) + format_example = FORMAT_EXAMPLE.format(filename=filename) + prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=filename, format_example=format_example) logger.info(f'Code review {filename}..') code = await self.write_code(prompt) # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index ac6e9043e..c21df25df 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -156,19 +156,8 @@ class Engineer(Role): :return: """ context = [] - for msg in self._rc.history: - # todo: 需要再抽象一下 - if msg.role in ["Architect", "Project Manager"]: - context.append(msg.content) - if msg.role in ["Product Manager"]: - tmp_msg = "" - if msg.instruct_content: - temp_str = str(msg.instruct_content.dict().get( - "Requirement Analysis")) - tmp_msg += "## Requirement Analysis\n" + temp_str + "\n" - temp_str = str(msg.instruct_content.dict().get("Requirement Pool")) - tmp_msg += "## Requirement Pool\n" + temp_str + "\n" - context.append(tmp_msg) + for msg in self._rc.memory.get_by_actions([WriteTasks, WriteDesign]): + context.append(msg.content) context_str = "\n".join(context) logger.debug(f'context: {context_str}') for todo in self.todos: From 8b876ac6b90af653769e4614e99bfa0d58ea294c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Sun, 23 Jul 2023 16:41:17 +0800 Subject: [PATCH 3/7] save code --- metagpt/roles/engineer.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index c21df25df..7d48e7d01 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -147,20 +147,20 @@ class Engineer(Role): async def _act_sp_precision(self) -> Message: """ # 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结) - 1. ProductManager 分析和需求 - 2. Architect全部 - 3. ProjectManager全部 - 4. 是否需要其他代码?(目标是不需要。在任务拆分清楚后,根据设计思路, - 不需要其他代码也能够写清楚单个文件, - 如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键) + 1. Architect全部 + 2. ProjectManager全部 + 3. 是否需要其他代码(暂时需要)? + (目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件, + 如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键) :return: """ - context = [] - for msg in self._rc.memory.get_by_actions([WriteTasks, WriteDesign]): - context.append(msg.content) - context_str = "\n".join(context) - logger.debug(f'context: {context_str}') for todo in self.todos: + context = [] + msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCodeReview]) + for m in msg: + context.append(m.content) + context_str = "\n".join(context) + logger.debug(f'context: {context_str}') code_rsp = await WriteCode().run( context=context_str, filename=todo @@ -176,11 +176,11 @@ class Engineer(Role): logger.error("code review failed!", e) pass self.write_file(todo, code_rsp) - msg = Message(content=code_rsp, role=self.profile, cause_by=type(self._rc.todo)) + msg = Message(content=code_rsp, role=self.profile, cause_by=WriteCodeReview) self._rc.memory.add(msg) logger.info(f'Done {self.get_workspace()} generating.') - msg = Message(content="all done.", role=self.profile, cause_by=type(self._rc.todo)) + msg = Message(content="all done.", role=self.profile, cause_by=type(WriteCode)) return msg async def _act(self) -> Message: From b187f80f30fa49a3ea10e7526414b2ddcab85c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Mon, 24 Jul 2023 14:21:18 +0800 Subject: [PATCH 4/7] save code --- metagpt/actions/write_code.py | 28 +++++++++------- metagpt/actions/write_code_review.py | 8 ++--- metagpt/roles/engineer.py | 33 ++++++++++--------- .../metagpt/actions/test_write_code_review.py | 30 ++++++++++------- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index 60d9b5e71..cc122ef7a 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -14,11 +14,12 @@ from metagpt.utils.common import CodeParser from tenacity import retry, stop_after_attempt, wait_fixed PROMPT_TEMPLATE = """ -# Context -{context} ------ NOTICE -1. Role: You are an engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) +Role: You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) +ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". + +## Code: {filename} Write code with triple quoto, based on the following list and context. +1. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. 2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets 3. Attention1: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. 4. Attention2: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN. @@ -26,15 +27,20 @@ NOTICE 6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. 7. Do not use public member functions that do not exist in your design. -## {filename}: Write code with triple quoto. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. - +----- +# Context +{context} +----- +## Format example +----- +## Code: {filename} +```python +## {filename} +... +``` +----- """ -## {filename}: Please encapsulate your code within triple quotes. Focus your efforts on implementing ONLY WITHIN THIS FILE. Any class or function labeled as MISSING-DESIGN should be implemented IN THIS FILE ALONE. Do NOT make changes to any other files. -OUTPUT_MAPPING = { - "{filename}": (str, ...), -} - class WriteCode(Action): def __init__(self, name="WriteCode", context: list[Message] = None, llm=None): diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 7d21b546a..1278d6a72 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -15,9 +15,9 @@ from tenacity import retry, stop_after_attempt, wait_fixed PROMPT_TEMPLATE = """ NOTICE Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language). -ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD WRITE BEFORE the code and triple quote. Output format carefully referenced "Format example". +ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". -## Code Review: Based on the following context and code, and flowing the check list, Provide key, clear, concise, and specific code modification suggestions, up to 5. +## Code Review: Based on the following context and code, and following the check list, Provide key, clear, concise, and specific code modification suggestions, up to 5. ``` 1. Check 0: Is the code implemented as per the requirements? 2. Check 1: Are there any issues with the code logic? @@ -27,7 +27,7 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD W 6. Check 5: Does the code have ? ``` -## Rewrite Code: {filename} Base on Code Review and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE.Ensure that the functionality of the rewritten code is consistent with the "Data structures and interface definitions" and ensure the code is complete and do not omit anything. +## Rewrite Code: {filename} Base on "Code Review" and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE. ----- # Context {context} @@ -56,12 +56,12 @@ FORMAT_EXAMPLE = """ ## Rewrite Code: {filename} ```python +## {filename} ... ``` """ - class WriteCodeReview(Action): def __init__(self, name="WriteCodeReview", context: list[Message] = None, llm=None): super().__init__(name, context, llm) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 7d48e7d01..fdbf829fc 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -150,37 +150,38 @@ class Engineer(Role): 1. Architect全部 2. ProjectManager全部 3. 是否需要其他代码(暂时需要)? - (目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件, - 如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键) + TODO:目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件,如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键 :return: """ for todo in self.todos: context = [] - msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCodeReview]) + msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode]) for m in msg: context.append(m.content) context_str = "\n".join(context) - logger.debug(f'context: {context_str}') + # 编写code code_rsp = await WriteCode().run( context=context_str, filename=todo ) - try: - code = await WriteCodeReview().run( - context=context_str, - code=code_rsp, - filename=todo - ) - code_rsp = code - except Exception as e: - logger.error("code review failed!", e) - pass + # code review + if self.use_code_review: + try: + code = await WriteCodeReview().run( + context=context_str, + code=code_rsp, + filename=todo + ) + code_rsp = code + except Exception as e: + logger.error("code review failed!", e) + pass self.write_file(todo, code_rsp) - msg = Message(content=code_rsp, role=self.profile, cause_by=WriteCodeReview) + msg = Message(content=code_rsp, 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=type(WriteCode)) + msg = Message(content="all done.", role=self.profile, cause_by=WriteCode) return msg async def _act(self) -> Message: diff --git a/tests/metagpt/actions/test_write_code_review.py b/tests/metagpt/actions/test_write_code_review.py index dda33f903..10efa1ed2 100644 --- a/tests/metagpt/actions/test_write_code_review.py +++ b/tests/metagpt/actions/test_write_code_review.py @@ -13,23 +13,29 @@ from tests.metagpt.actions.mock import SEARCH_CODE_SAMPLE @pytest.mark.asyncio -async def test_write_code_review(): +async def test_write_code_review(capfd): code = """ def add(a, b): - return a + b + return a + """ - write_code_review = WriteCodeReview("write_code_review") + # write_code_review = WriteCodeReview("write_code_review") - review = await write_code_review.run(code) + code = await WriteCodeReview().run( + context="编写一个从a加b的函数,返回a+b", + code=code, + filename="math.py" + ) # 我们不能精确地预测生成的代码评审,但我们可以检查返回的是否为字符串 - assert isinstance(review, str) - assert len(review) > 0 + assert isinstance(code, str) + assert len(code) > 0 + captured = capfd.readouterr() + print(f"输出内容: {captured.out}") -@pytest.mark.asyncio -async def test_write_code_review_directly(): - code = SEARCH_CODE_SAMPLE - write_code_review = WriteCodeReview("write_code_review") - review = await write_code_review.run(code) - logger.info(review) +# @pytest.mark.asyncio +# async def test_write_code_review_directly(): +# code = SEARCH_CODE_SAMPLE +# write_code_review = WriteCodeReview("write_code_review") +# review = await write_code_review.run(code) +# logger.info(review) From 50574bcbfeb88d4b2ece285c563065e2daddbb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Mon, 24 Jul 2023 14:59:40 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ docs/README_CN.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 398bc0457..f8db80a80 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ ## Tutorial: Initiating a startup ```shell python startup.py "Write a cli snake game" +# Use code review will cost more money, but will opt for better code quality. +python startup.py "Write a cli snake game" --code_review True ``` After running the script, you can find your new project in the `workspace/` directory. diff --git a/docs/README_CN.md b/docs/README_CN.md index 51140475f..72188a415 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -70,6 +70,8 @@ ## 示例:启动一个创业公司 ```shell python startup.py "写一个命令行贪吃蛇" +# 开启code review模式会会花费更多的money, 但是会提升代码质量和成功率 +python startup.py "写一个命令行贪吃蛇" --code_review True ``` 运行脚本后,您可以在 `workspace/` 目录中找到您的新项目。 From 05744bef0ba5003f098f3ee7c8cd75228208e457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Mon, 24 Jul 2023 21:27:47 +0800 Subject: [PATCH 6/7] code review --- metagpt/actions/write_code_review.py | 1 - metagpt/roles/engineer.py | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 1278d6a72..7f6a7a38e 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -24,7 +24,6 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc 3. Check 2: Does the existing code follow the "Data structures and interface definitions"? 4. Check 3: Is there a function in the code that is omitted or not fully implemented that needs to be implemented? 5. Check 4: Does the code have unnecessary or lack dependencies? -6. Check 5: Does the code have ? ``` ## Rewrite Code: {filename} Base on "Code Review" and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE. diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index e38518675..affccc81b 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -143,15 +143,14 @@ class Engineer(Role): return msg async def _act_sp_precision(self) -> Message: - """ - # 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结) - 1. Architect全部 - 2. ProjectManager全部 - 3. 是否需要其他代码(暂时需要)? - TODO:目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件,如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键 - :return: - """ for todo in self.todos: + """ + # 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结) + 1. Architect全部 + 2. ProjectManager全部 + 3. 是否需要其他代码(暂时需要)? + TODO:目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件,如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键 + """ context = [] msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode]) for m in msg: From e30fc92f922c7ebf086bbabed8fa2555dc44b02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E8=8C=82=E5=AE=87?= Date: Mon, 24 Jul 2023 22:36:12 +0800 Subject: [PATCH 7/7] code review2 --- metagpt/roles/engineer.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index affccc81b..514799acc 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -157,24 +157,24 @@ class Engineer(Role): context.append(m.content) context_str = "\n".join(context) # 编写code - code_rsp = await WriteCode().run( + code = await WriteCode().run( context=context_str, filename=todo ) # code review if self.use_code_review: try: - code = await WriteCodeReview().run( + rewrite_code = await WriteCodeReview().run( context=context_str, - code=code_rsp, + code=code, filename=todo ) - code_rsp = code + code = rewrite_code except Exception as e: logger.error("code review failed!", e) pass - self.write_file(todo, code_rsp) - msg = Message(content=code_rsp, role=self.profile, cause_by=WriteCode) + 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.')