From ccecb45b13f5786c5ff842ee27516f67ec97b7f4 Mon Sep 17 00:00:00 2001 From: geekan Date: Thu, 14 Dec 2023 23:54:38 +0800 Subject: [PATCH 1/4] resolve conflicts --- metagpt/actions/action.py | 1 + metagpt/actions/action_node.py | 2 +- metagpt/actions/write_code.py | 61 ++++++++---------- metagpt/actions/write_code_review.py | 95 ++++++++++++++++------------ metagpt/actions/write_prd_an.py | 2 +- metagpt/provider/base_gpt_api.py | 2 +- metagpt/roles/engineer.py | 7 +- metagpt/roles/role.py | 6 +- metagpt/schema.py | 2 +- 9 files changed, 95 insertions(+), 83 deletions(-) diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index 1d9be60e0..6c1f63f45 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -44,6 +44,7 @@ class Action(ABC): self.prefix = prefix self.profile = profile self.llm.system_prompt = prefix + return self def __str__(self): return self.__class__.__name__ diff --git a/metagpt/actions/action_node.py b/metagpt/actions/action_node.py index 9fb10f35c..1d808ec70 100644 --- a/metagpt/actions/action_node.py +++ b/metagpt/actions/action_node.py @@ -243,7 +243,7 @@ class ActionNode: ) return prompt - @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) + @retry(wait=wait_random_exponential(min=1, max=10), stop=stop_after_attempt(6)) async def _aask_v1( self, prompt: str, diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index b759f4e2a..a91e4ee1e 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -34,59 +34,52 @@ from metagpt.utils.file_repository import FileRepository PROMPT_TEMPLATE = """ NOTICE -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) +Role: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code Language: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". ------ -# Design -```json +# Context +## Design {design} -``` ------ -# Tasks -```json + +## Tasks {tasks} -``` ------ -# Legacy Code -```python + +## Legacy Code +```Code {code} ``` ------ -# Debug logs + +## Debug logs ```text {logs} {summary_log} ``` ------ -# Bug Feedback logs + +## Bug Feedback logs ```text {feedback} ``` ------ - -## 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. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. -4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". 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. -8. Before using a variable, make sure you reference it first -9. Write out EVERY DETAIL, DON'T LEAVE TODO. - -## Format example ------ +# Format example ## Code: {filename} ```python ## {filename} ... ``` ------ + +# Instruction: Based on the context, follow "Format example", write code. + +## Code: {filename} Write code with triple quoto, based on the following attentions and context. +1. Only One file: do your best to implement THIS ONLY ONE FILE. +2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. +3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import. +4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. +5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. +6. Before using a external variable/module, make sure you import it first. +7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. + """ @@ -148,5 +141,5 @@ class WriteCode(Action): doc = await src_file_repo.get(filename=filename) if not doc: continue - codes.append(doc.content) - return "\n----------\n".join(codes) + codes.append(f"----- {filename}\n" + doc.content) + return "\n".join(codes) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 75313fea5..f63a399a9 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -18,8 +18,8 @@ from metagpt.schema import CodingContext from metagpt.utils.common import CodeParser 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). +# System +Role: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain. Language: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". @@ -27,53 +27,52 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc {context} ## Code to be Reviewed: {filename} -``` +```Code {code} ``` +""" ------ -## Code Review: Based on the "Code to be Reviewed", provide key, clear, concise, and specific code modification suggestions, up to 5. +EXAMPLE_AND_INSTRUCTION = """ + +{format_example} + + +# Instruction: Based on the actual code situation, follow one of the "Format example". + +## Code Review: Ordered List. Based on the "Code to be Reviewed", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step. 1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step. 2. Is the code logic completely correct? If there are errors, please indicate how to correct them. 3. Does the existing code follow the "Data structures and interfaces"? 4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step. 5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported -6. Is the code implemented concisely enough? Are methods from other files being reused correctly? +6. Are methods from other files being reused correctly? -## Code Review Result: If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM. +## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B + +## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM. LGTM/LBTM -## Rewrite Code: if it still has some bugs, rewrite {filename} based on "Code Review" with triple quotes, try to get LGTM. Do your utmost to optimize THIS SINGLE FILE. Implement ALL TODO. RETURN ALL CODE, NEVER OMIT ANYTHING. 以任何方式省略代码都是不允许的。 -``` -``` - -## Format example -{format_example} - """ FORMAT_EXAMPLE = """ ------ -# EXAMPLE 1 +# Format example 1 ## Code Review: {filename} -1. No, we should add the logic of ... +1. No, we should fix the logic of class A due to ... 2. ... 3. ... -4. ... +4. No, function B is not implemented, ... 5. ... 6. ... -## Code Review Result: {filename} +## Actions +1. fix class A +2. implement function B + +## Code Review Result LBTM -## Rewrite Code: {filename} -```python -## {filename} -... -``` ------ -# EXAMPLE 2 +# Format example 2 ## Code Review: {filename} 1. Yes. 2. Yes. @@ -82,12 +81,20 @@ LBTM 5. Yes. 6. Yes. -## Code Review Result: {filename} -LGTM - -## Rewrite Code: {filename} +## Actions pass ------ + +## Code Review Result +LGTM +""" + +REWRITE_CODE_TEMPLATE = """ +# Instruction: rewrite code based on the Code Review and Actions +## Rewrite Code: CodeBlock. If it still has some bugs, rewrite {filename} with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes. +```Code +## {filename} +... +``` """ @@ -96,11 +103,15 @@ class WriteCodeReview(Action): super().__init__(name, context, llm) @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) - async def write_code_review_and_rewrite(self, prompt): - code_rsp = await self._aask(prompt) - result = CodeParser.parse_block("Code Review Result", code_rsp) + async def write_code_review_and_rewrite(self, context_prompt, cr_prompt, filename): + cr_rsp = await self._aask(context_prompt + cr_prompt) + result = CodeParser.parse_block("Code Review Result", cr_rsp) if "LGTM" in result: return result, None + + # if LBTM, rewrite code + rewrite_prompt = f"{context_prompt}\n{cr_rsp}\n{REWRITE_CODE_TEMPLATE.format(filename=filename)}" + code_rsp = await self._aask(rewrite_prompt) code = CodeParser.parse_code(block="", text=code_rsp) return result, code @@ -111,23 +122,23 @@ class WriteCodeReview(Action): format_example = FORMAT_EXAMPLE.format(filename=self.context.code_doc.filename) task_content = self.context.task_doc.content if self.context.task_doc else "" code_context = await WriteCode.get_codes(self.context.task_doc, exclude=self.context.filename) - context = "\n----------\n".join( + context = "\n".join( [ - "```text\n" + self.context.design_doc.content + "```\n", - "```text\n" + task_content + "```\n", - "```python\n" + code_context + "```\n", + "## System Design\n" + self.context.design_doc.content + "\n", + "## Tasks\n" + task_content + "\n", + "## Code Files\n" + code_context + "\n", ] ) - prompt = PROMPT_TEMPLATE.format( + context_prompt = PROMPT_TEMPLATE.format( context=context, code=iterative_code, filename=self.context.code_doc.filename, - format_example=format_example, ) + cr_prompt = EXAMPLE_AND_INSTRUCTION.format(format_example=format_example, ) logger.info( - f"Code review and rewrite {self.context.code_doc.filename,}: {i+1}/{k} | {len(iterative_code)=}, {len(self.context.code_doc.content)=}" + f"Code review and rewrite {self.context.code_doc.filename}: {i+1}/{k} | {len(iterative_code)=}, {len(self.context.code_doc.content)=}" ) - result, rewrited_code = await self.write_code_review_and_rewrite(prompt) + result, rewrited_code = await self.write_code_review_and_rewrite(context_prompt, cr_prompt, self.context.code_doc.filename) if "LBTM" in result: iterative_code = rewrited_code elif "LGTM" in result: diff --git a/metagpt/actions/write_prd_an.py b/metagpt/actions/write_prd_an.py index 68402e504..d96c0aeac 100644 --- a/metagpt/actions/write_prd_an.py +++ b/metagpt/actions/write_prd_an.py @@ -93,7 +93,7 @@ REQUIREMENT_POOL = ActionNode( key="Requirement Pool", expected_type=list[list[str]], instruction="List down the requirements with their priority (P0, P1, P2).", - example=[["P0", "High priority requirement"], ["P1", "Medium priority requirement"]], + example=[["P0", "..."], ["P1", "..."]], ) UI_DESIGN_DRAFT = ActionNode( diff --git a/metagpt/provider/base_gpt_api.py b/metagpt/provider/base_gpt_api.py index 6c1dc8338..c38576806 100644 --- a/metagpt/provider/base_gpt_api.py +++ b/metagpt/provider/base_gpt_api.py @@ -49,8 +49,8 @@ class BaseGPTAPI(BaseChatbot): message = ( [self._default_system_msg(), self._user_msg(msg)] if self.use_system_prompt else [self._user_msg(msg)] ) - rsp = await self.acompletion_text(message, stream=stream) logger.debug(message) + rsp = await self.acompletion_text(message, stream=stream) # logger.debug(rsp) return rsp diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 2f99d132e..f1e65b177 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -72,7 +72,7 @@ class Engineer(Role): 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. " + constraints: str = "the code should conform to standards like google-style and be modular and maintainable. " "Use same language as user requirement", n_borg: int = 1, use_code_review: bool = False, @@ -105,7 +105,9 @@ class Engineer(Role): coding_context = await todo.run() # Code review if review: - coding_context = await WriteCodeReview(context=coding_context, llm=self._llm).run() + action = WriteCodeReview(context=coding_context, llm=self._llm) + self._init_action_system_message(action) + coding_context = await action.run() await src_file_repo.save( coding_context.filename, dependencies={coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path}, @@ -224,6 +226,7 @@ class Engineer(Role): task_doc = await task_file_repo.get(i.name) elif str(i.parent) == SYSTEM_DESIGN_FILE_REPO: design_doc = await design_file_repo.get(i.name) + # FIXME: design doc没有加载进来,是None context = CodingContext(filename=filename, design_doc=design_doc, task_doc=task_doc, code_doc=old_code_doc) return context diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 633ad6051..66475da72 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -134,6 +134,7 @@ class Role: self._setting = RoleSetting( name=name, profile=profile, goal=goal, constraints=constraints, desc=desc, is_human=is_human ) + self._llm.system_prompt = self._get_prefix() self._states = [] self._actions = [] self._role_id = str(self._setting) @@ -144,6 +145,9 @@ class Role: self._states = [] self._actions = [] + def _init_action_system_message(self, action: Action): + action.set_prefix(self._get_prefix(), self.profile) + def _init_actions(self, actions): self._reset() for idx, action in enumerate(actions): @@ -158,7 +162,7 @@ class Role: ) i = action # i.set_env(self._rc.env) - i.set_prefix(self._get_prefix(), self.profile) + self._init_action_system_message(i) self._actions.append(i) self._states.append(f"{idx}. {action}") diff --git a/metagpt/schema.py b/metagpt/schema.py index baed5582b..799bb9253 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -259,7 +259,7 @@ class MessageQueue: class CodingContext(BaseModel): filename: str - design_doc: Document + design_doc: Optional[Document] task_doc: Optional[Document] code_doc: Optional[Document] From 222694c329d5bddc412317d4e20c774d391776b3 Mon Sep 17 00:00:00 2001 From: geekan Date: Fri, 15 Dec 2023 00:37:10 +0800 Subject: [PATCH 2/4] fix bugs --- metagpt/actions/write_code.py | 13 +++++++++---- metagpt/actions/write_code_review.py | 2 +- metagpt/schema.py | 6 ++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index a91e4ee1e..5960e2621 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -71,7 +71,7 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenc # Instruction: Based on the context, follow "Format example", write code. -## Code: {filename} Write code with triple quoto, based on the following attentions and context. +## Code: {filename}. Write code with triple quoto, based on the following attentions and context. 1. Only One file: do your best to implement THIS ONLY ONE FILE. 2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import. @@ -100,7 +100,7 @@ class WriteCode(Action): filename="test_" + coding_context.filename + ".json", relative_path=TEST_OUTPUTS_FILE_REPO ) summary_doc = None - if coding_context.design_doc.filename: + if coding_context.design_doc and coding_context.design_doc.filename: summary_doc = await FileRepository.get_file( filename=coding_context.design_doc.filename, relative_path=CODE_SUMMARIES_FILE_REPO ) @@ -108,9 +108,14 @@ class WriteCode(Action): if test_doc: test_detail = RunCodeResult.loads(test_doc.content) logs = test_detail.stderr - code_context = await self.get_codes(coding_context.task_doc, exclude=self.context.filename) + + if bug_feedback: + code_context = coding_context.code_doc.content + else: + code_context = await self.get_codes(coding_context.task_doc, exclude=self.context.filename) + prompt = PROMPT_TEMPLATE.format( - design=coding_context.design_doc.content, + design=coding_context.design_doc.content if coding_context.design_doc else "", tasks=coding_context.task_doc.content if coding_context.task_doc else "", code=code_context, logs=logs, diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index f63a399a9..62e96acd8 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -124,7 +124,7 @@ class WriteCodeReview(Action): code_context = await WriteCode.get_codes(self.context.task_doc, exclude=self.context.filename) context = "\n".join( [ - "## System Design\n" + self.context.design_doc.content + "\n", + "## System Design\n" + str(self.context.design_doc) + "\n", "## Tasks\n" + task_content + "\n", "## Code Files\n" + code_context + "\n", ] diff --git a/metagpt/schema.py b/metagpt/schema.py index 799bb9253..758149efa 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -74,6 +74,12 @@ class Document(BaseModel): return None return str(CONFIG.git_repo.workdir / self.root_path / self.filename) + def __str__(self): + return self.content + + def __repr__(self): + return self.content + class Documents(BaseModel): """A class representing a collection of documents. From 126bcdafb966ef694dcf764dc98302bc57497f27 Mon Sep 17 00:00:00 2001 From: geekan Date: Fri, 15 Dec 2023 10:44:18 +0800 Subject: [PATCH 3/4] fix error msg --- metagpt/utils/git_repository.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metagpt/utils/git_repository.py b/metagpt/utils/git_repository.py index 1340b1768..d2bdf5d85 100644 --- a/metagpt/utils/git_repository.py +++ b/metagpt/utils/git_repository.py @@ -233,6 +233,8 @@ class GitRepository: files = [] try: directory_path = Path(self.workdir) / relative_path + if not directory_path.exists(): + return [] for file_path in directory_path.iterdir(): if file_path.is_file(): rpath = file_path.relative_to(root_relative_path) From 862707d4b7bd319873e550010253a8df0844f6b8 Mon Sep 17 00:00:00 2001 From: geekan Date: Fri, 15 Dec 2023 10:56:08 +0800 Subject: [PATCH 4/4] use react instead of _react --- metagpt/roles/role.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 66475da72..b673c330d 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -412,7 +412,7 @@ class Role: logger.debug(f"{self._setting}: no news. waiting.") return - rsp = await self._react() + rsp = await self.react() # Reset the next action to be taken. self._rc.todo = None