diff --git a/metagpt/actions/design_api_an.py b/metagpt/actions/design_api_an.py index 3e8265e95..02f20a133 100644 --- a/metagpt/actions/design_api_an.py +++ b/metagpt/actions/design_api_an.py @@ -49,9 +49,8 @@ FILE_LIST = ActionNode( REFINED_FILE_LIST = ActionNode( key="Refined File List", expected_type=List[str], - instruction="Update and expand the original file list, including only relative paths. " - "Ensure that the refined file list reflects the evolving structure of the project due to incremental development." - "Only output filename! Do not include comments in the list.", + instruction="Update and expand the original file list including only relative paths, up to 2 files can be added." + "Ensure that the refined file list reflects the evolving structure of the project due to incremental development.", example=["main.py", "game.py", "new_feature.py"], ) @@ -148,7 +147,7 @@ INC_NODES = [INCREMENTAL_IMPLEMENTATION_APPROACH, INCREMENTAL_DATA_STRUCTURES_AN REFINE_NODES = [ REFINED_IMPLEMENTATION_APPROACH, - FILE_LIST, + REFINED_FILE_LIST, REFINED_DATA_STRUCTURES_AND_INTERFACES, REFINED_PROGRAM_CALL_FLOW, ANYTHING_UNCLEAR, diff --git a/metagpt/actions/project_management_an.py b/metagpt/actions/project_management_an.py index a970c05a3..9b54698a1 100644 --- a/metagpt/actions/project_management_an.py +++ b/metagpt/actions/project_management_an.py @@ -75,16 +75,16 @@ INCREMENTAL_TASK_LIST = ActionNode( instruction="Break down the incremental development tasks into a prioritized list of filenames." "Organize the tasks based on dependency order, ensuring a systematic and efficient implementation." "Only output filename! Do not include comments in the list ", - example=["new_feature.py", "utils.py", "main.py"], + example=["new_feature.py", "main.py"], ) REFINED_TASK_LIST = ActionNode( key="Refined Task list", expected_type=List[str], - instruction="Review and refine the combined task list after the merger of Legacy Content and Incremental Content. " + instruction="Review and refine the combined task list after the merger of Legacy Content and Incremental Content, and consistent with Refined File List." "Ensure that tasks are organized in a logical and prioritized order, considering dependencies for a streamlined and" - " efficient development process. Only output filename! Do not include comments in the list", - example=["game.py", "utils.py", "new_feature.py", "main.py"], + " efficient development process. ", + example=["new_feature.py", "utils", "game.py", "main.py"], ) FULL_API_SPEC = ActionNode( diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index 7555ce101..acc25627e 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -27,6 +27,7 @@ from metagpt.const import ( BUGFIX_FILENAME, CODE_SUMMARIES_FILE_REPO, DOCS_FILE_REPO, + PRDS_FILE_REPO, REQUIREMENT_FILENAME, TASK_FILE_REPO, TEST_OUTPUTS_FILE_REPO, @@ -115,6 +116,11 @@ class WriteCode(Action): docs_file_repo = CONFIG.git_repo.new_file_repository(relative_path=DOCS_FILE_REPO) requirement_doc = await docs_file_repo.get(filename=REQUIREMENT_FILENAME) + + prd_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) + prd = await prd_file_repo.get_all() + prd_json = json.loads("\n".join([doc.content for doc in prd])) + product_requirement_pool = prd_json.get("Requirement Pool", prd_json.get("Refined Requirement Pool")) guideline = kwargs.get("guideline", "") if bug_feedback: code_context = coding_context.code_doc.content @@ -125,7 +131,8 @@ class WriteCode(Action): if guideline: prompt = REFINED_CODE_TEMPLATE.format( - requirement=requirement_doc.content if requirement_doc else "", + user_requirement=requirement_doc.content if requirement_doc else "", + product_requirement_pool=str(product_requirement_pool), guideline=guideline, design=coding_context.design_doc.content if coding_context.design_doc else "", tasks=coding_context.task_doc.content if coding_context.task_doc else "", diff --git a/metagpt/actions/write_code_guideline_an.py b/metagpt/actions/write_code_guideline_an.py index c08340cb7..9521e51e6 100644 --- a/metagpt/actions/write_code_guideline_an.py +++ b/metagpt/actions/write_code_guideline_an.py @@ -15,7 +15,8 @@ GUIDELINES_AND_INCREMENTAL_CHANGE = ActionNode( expected_type=str, instruction="Developing comprehensive and step-by-step incremental development guideline, and Write Incremental " "Change by making a code draft that how to implement incremental development including detailed steps based on the " - "context. Note: Track incremental changes using mark of '+' or '-' for add/modify/delete code", + "context. Note: Track incremental changes using mark of '+' or '-' for add/modify/delete code, and conforms to the " + "output format of git diff", example=""" 1. Guideline for calculator.py: Enhance the functionality of `calculator.py` by extending it to incorporate methods for subtraction, multiplication, and division. Additionally, implement robust error handling for the division operation to mitigate potential issues related to division by zero. ```python @@ -105,11 +106,11 @@ def add_numbers(): ) CODE_GUIDELINE_CONTEXT = """ -## New Requirements -{requirement} +## User New Requirements +{user_requirement} -## PRD -{prd} +## Product Requirement Pool +{product_requirement_pool} ## Design {design} @@ -388,13 +389,16 @@ class Interface: REFINED_CODE_TEMPLATE = """ NOTICE -Role: You are a professional engineer; The main goal is to complete incremental development by combining legacy code and Incremental Change, ensuring the integration of new features. +Role: You are a professional engineer; The main goal is to complete incremental development by combining legacy code and Guidelines and Incremental Change, ensuring the integration of new features. # Context -## New Requirement -{requirement} +## User New Requirements +{user_requirement} -## Incremental Change +## Product Requirement Pool +{product_requirement_pool} + +## Guidelines and Incremental Change {guideline} ## Design diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index a8c913573..e3c5fd2ac 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -7,6 +7,7 @@ @Modified By: mashenquan, 2023/11/27. Following the think-act principle, solidify the task parameters when creating the WriteCode object, rather than passing them in when calling the run function. """ +import json from pydantic import Field from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -14,6 +15,7 @@ from tenacity import retry, stop_after_attempt, wait_random_exponential from metagpt.actions import WriteCode from metagpt.actions.action import Action from metagpt.config import CONFIG +from metagpt.const import DOCS_FILE_REPO, PRDS_FILE_REPO, REQUIREMENT_FILENAME from metagpt.logs import logger from metagpt.schema import CodingContext from metagpt.utils.common import CodeParser @@ -138,17 +140,41 @@ class WriteCodeReview(Action): async def run(self, *args, **kwargs) -> CodingContext: iterative_code = self.context.code_doc.content k = CONFIG.code_review_k_times or 1 + guideline = kwargs.get("guideline") + mode = "guide" if guideline else "normal" for i in range(k): 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".join( - [ - "## System Design\n" + str(self.context.design_doc) + "\n", - "## Tasks\n" + task_content + "\n", - "## Code Files\n" + code_context + "\n", - ] - ) + code_context = await WriteCode.get_codes(self.context.task_doc, exclude=self.context.filename, mode=mode) + + if not guideline: + context = "\n".join( + [ + "## System Design\n" + str(self.context.design_doc) + "\n", + "## Tasks\n" + task_content + "\n", + "## Code Files\n" + code_context + "\n", + ] + ) + else: + docs_file_repo = CONFIG.git_repo.new_file_repository(relative_path=DOCS_FILE_REPO) + requirement_doc = await docs_file_repo.get(filename=REQUIREMENT_FILENAME) + user_requirement = requirement_doc.content if requirement_doc else "" + prd_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) + prd = await prd_file_repo.get_all() + prd_json = json.loads("\n".join([doc.content for doc in prd])) + product_requirement_pool = prd_json.get("Requirement Pool", prd_json.get("Refined Requirement Pool")) + + context = "\n".join( + [ + "## User New Requirements\n" + str(user_requirement) + "\n", + "## Product Requirement Pool\n" + str(product_requirement_pool) + "\n", + "## Guidelines and Incremental Change\n" + guideline + "\n", + "## System Design\n" + str(self.context.design_doc) + "\n", + "## Tasks\n" + task_content + "\n", + "## Code Files\n" + code_context + "\n", + ] + ) + context_prompt = PROMPT_TEMPLATE.format( context=context, code=iterative_code, diff --git a/metagpt/actions/write_prd_an.py b/metagpt/actions/write_prd_an.py index 91c1b2837..2eaed7114 100644 --- a/metagpt/actions/write_prd_an.py +++ b/metagpt/actions/write_prd_an.py @@ -160,7 +160,7 @@ REQUIREMENT_POOL = ActionNode( REFINED_REQUIREMENT_POOL = ActionNode( key="Refined Requirement Pool", expected_type=List[List[str]], - instruction="List no less than 5 requirements with their priority (P0, P1, P2). " + instruction="List no less than 5 requirements with their priority (P0, P1, P2) from high to low. " "Cover both legacy content and incremental content. Retain any content unrelated to incremental development", example=[["P0", "The main code ..."], ["P0", "The game algorithm ..."]], ) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index e0b22ea5b..e67963de5 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -117,7 +117,7 @@ class Engineer(Role): if review: action = WriteCodeReview(context=coding_context, llm=self.llm) self._init_action_system_message(action) - coding_context = await action.run() + coding_context = await action.run(guideline=guideline) # Get dependencies if guideline: @@ -346,12 +346,14 @@ class Engineer(Role): async def _write_code_guideline(self): logger.info("Writing code guideline..") - requirement = str(self.rc.memory.get_by_role("Human")[0]) + user_requirement = str(self.rc.memory.get_by_role("Human")[0]) prd_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) design_file_repo = CONFIG.git_repo.new_file_repository(SYSTEM_DESIGN_FILE_REPO) task_file_repo = CONFIG.git_repo.new_file_repository(TASK_FILE_REPO) prd = await prd_file_repo.get_all() - prd = "\n".join([doc.content for doc in prd]) + prd_json = json.loads("\n".join([doc.content for doc in prd])) + product_requirement_pool = prd_json.get("Requirement Pool", prd_json.get("Refined Requirement Pool")) + design = await design_file_repo.get_all() design = "\n".join([doc.content for doc in design]) tasks = await task_file_repo.get_all() @@ -359,7 +361,11 @@ class Engineer(Role): old_codes = await self.get_old_codes() context = CODE_GUIDELINE_CONTEXT.format( - requirement=requirement, prd=prd, tasks=tasks, design=design, code=old_codes + user_requirement=user_requirement, + product_requirement_pool=str(product_requirement_pool), + tasks=tasks, + design=design, + code=old_codes, ) node = await WriteCodeGuideline().run(context=context) guideline = node.instruct_content.model_dump_json()