diff --git a/metagpt/actions/refine_design_api.py b/metagpt/actions/refine_design_api.py index f1c231525..909fa6db9 100644 --- a/metagpt/actions/refine_design_api.py +++ b/metagpt/actions/refine_design_api.py @@ -3,7 +3,7 @@ import re import shutil from pathlib import Path -from typing import List +from typing import List, Union from metagpt.actions import Action, ActionOutput from metagpt.config import CONFIG @@ -26,34 +26,29 @@ templates = { ## Format example {format_example} ----- -Role: You are an architect; the goal is to perform incremental development and design a state-of-the-art (SOTA) PEP8-compliant Python system based on the context and the provided difference descriptions. Make the best use of good open source tools. -Requirement: Fill in the following missing information based on the context, each section name is a key in json +Role: You are an architect; the goal is to perform incremental development and design a state-of-the-art (SOTA) PEP8-compliant Python system based on the context and legacy design. Make the best use of good open source tools. +Requirement: Fill in the following missing information based on the context, each section name is a key in json. Output exactly as shown in the example, including single and double quotes. Max Output: 8192 chars or 2048 tokens. Try to use them up. -## Difference Description: Provided as Python list[str], the list of differences between the new design and the legacy design +## Difference Description: Provide as list, the foremost differences description for system design here based on the previous. -## Implementation approach: Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. +## Incremental implementation approach: Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. ## Python package name: Provide as Python str with python triple quoto, concise and clear, characters only use a combination of all lowercase and underscores -## File list: Provided as Python list[str], the list of ONLY REQUIRED files needed to write the program(LESS IS MORE!). Only need relative paths, comply with PEP8 standards. ALWAYS write a main.py or app.py here +## Data structures and interface definitions: Use single quotes to wrap content. Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. -## Data structures and interface definitions: Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. +## Program call flow: Use single quotes to wrap content. Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. -## Program call flow: Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. - -output a properly formatted JSON, wrapped inside [CONTENT][/CONTENT] like format example, -and only output the json inside this tag, nothing else +output a properly formatted JSON, wrapped inside [CONTENT][/CONTENT] like format example. +Output exactly as shown in the example, including single and double quotes, and only output the json inside this tag, nothing else """, "FORMAT_EXAMPLE": """ [CONTENT] { "Difference Description": ["The ..."], - "Implementation approach": "We will ...", - "Python package name": "snake_game", - "File list": ["main.py"], + "Incremental implementation approach": "We will ...", + "Python package name": "new_name", "Data structures and interface definitions": ' classDiagram class Game{ @@ -67,8 +62,7 @@ and only output the json inside this tag, nothing else participant M as Main ... G->>M: end game - ', - "Anything UNCLEAR": "The requirement is clear to me." + ' } [/CONTENT] """, @@ -84,24 +78,20 @@ and only output the json inside this tag, nothing else ## Format example {format_example} ----- -Role: You are an architect; the goal is to perform incremental development and design a state-of-the-art (SOTA) PEP8-compliant Python system based on the context and the provided difference descriptions. Make the best use of good open source tools. -Requirement: Fill in the following missing information based on the context, note that all sections are response with code form separately +Role: You are an architect; the goal is to perform incremental development and design a state-of-the-art (SOTA) PEP8-compliant Python system based on the context and legacy design. Make the best use of good open source tools. +Requirement: Fill in the following missing information based on the context, note that all sections are response with code form separately. Output exactly as shown in the example, including single and double quotes. Max Output: 8192 chars or 2048 tokens. Try to use them up. Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. -## Difference Description: Provided as Python list[str], the list of differences between the new design and the legacy design +## Difference Description: Provide as list, the foremost differences description for system design here based on the previous. -## Implementation approach: Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. +## Incremental implementation approach: Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. ## Python package name: Provide as Python str with python triple quoto, concise and clear, characters only use a combination of all lowercase and underscores -## File list: Provided as Python list[str], the list of ONLY REQUIRED files needed to write the program(LESS IS MORE!). Only need relative paths, comply with PEP8 standards. ALWAYS write a main.py or app.py here +## Data structures and interface definitions: Use single quotes to wrap content. Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. -## Data structures and interface definitions: Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. - -## Program call flow: Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. +## Program call flow: Use single quotes to wrap content. Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. """, "FORMAT_EXAMPLE": """ @@ -113,19 +103,12 @@ Attention: Use '##' to split sections, not '#', and '## ' SHOULD W ] ``` -## Implementation approach +## Incremental implementation approach We will ... ## Python package name ```python -"snake_game" -``` - -## File list -```python -[ - "main.py", -] +"new_name" ``` ## Data structures and interface definitions @@ -145,22 +128,19 @@ sequenceDiagram ... G->>M: end game ``` - -## Anything UNCLEAR -The requirement is clear to me. --- """, }, } OUTPUT_MAPPING = { - "Difference Description": (List[str], ...), - "Implementation approach": (str, ...), + # "Incremental Requirements": (str, ...), + "Difference Description": (Union[List[str], str], ...), + "Incremental implementation approach": (str, ...), "Python package name": (str, ...), - "File list": (List[str], ...), + # "File list": (List[str], ...), "Data structures and interface definitions": (str, ...), - "Program call flow": (str, ...), - "Anything UNCLEAR": (str, ...), + "Program call flow": (str, ...) } @@ -201,9 +181,6 @@ class RefineDesign(Action): async def _save_prd(self, docs_path, resources_path, context): prd_file = docs_path / "prd.md" - if context[-1].instruct_content and context[-1].instruct_content.dict()["Competitive Quadrant Chart"]: - quadrant_chart = context[-1].instruct_content.dict()["Competitive Quadrant Chart"] - await mermaid_to_file(quadrant_chart, resources_path / "competitive_analysis") if context[-1].instruct_content: logger.info(f"Saving PRD to {prd_file}") diff --git a/metagpt/actions/refine_prd.py b/metagpt/actions/refine_prd.py index 81cb02af8..0f01b9904 100644 --- a/metagpt/actions/refine_prd.py +++ b/metagpt/actions/refine_prd.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Union from metagpt.actions import Refine, ActionOutput, SearchAndSummarize from metagpt.config import CONFIG @@ -9,96 +9,36 @@ increment_template = { "json": { "PROMPT_TEMPLATE": """ # Context -## User's New Requirements +## User's Incremental Requirements {new_requirements} -## Difference Description -{difference_description} - ## Legacy PRD {legacy} ## Search Information {search_information} -## mermaid quadrantChart code syntax example. DONT USE QUOTO IN CODE DUE TO INVALID SYNTAX. Replace the with REAL COMPETITOR NAME -```mermaid -quadrantChart - title Reach and engagement of campaigns - x-axis Low Reach --> High Reach - y-axis Low Engagement --> High Engagement - quadrant-1 We should expand - quadrant-2 Need to promote - quadrant-3 Re-evaluate - quadrant-4 May be improved - "Campaign: A": [0.3, 0.6] - "Campaign B": [0.45, 0.23] - "Campaign C": [0.57, 0.69] - "Campaign D": [0.78, 0.34] - "Campaign E": [0.40, 0.34] - "Campaign F": [0.35, 0.78] - "Our Target Product": [0.5, 0.6] -``` - ## Format example {format_example} ----- Role: You are a professional Product Manager tasked with overseeing incremental development and crafting Product Requirements Documents (PRDs) for a concise, usable, and efficient product. -Requirements: According to the context, fill in the following missing information, each section name is a key in json ,If the requirements are unclear, ensure minimum viability and avoid excessive design +Requirements: According to the context, fill in the following missing information, each section name is a key in json ,If the requirements are unclear, ensure minimum viability and avoid excessive designOnly output one json, nothing else. -## New Requirements: Provide as Plain text and place the new requirements here +## Incremental Requirements: Provide as str, the foremost incremental requirements for PRD here based on the previous. -## Difference Description: Provide as Python list[str], up to 5 clear, difference descriptions. If the requirement itself is simple, the difference description should also be simple +## Difference Description: Provide as str, the foremost differences description for PRD here based on the previous. ## Incremental Development Plan: Provide as Python list[str], up to 5 clear, incremental development plans. If the requirement itself is simple, the incremental development plan should also be simple -## Product Goals: Provided as Python list[str], up to 3 clear, orthogonal product goals. If the requirement itself is simple, the goal should also be simple - -## User Stories: Provided as Python list[str], up to 5 scenario-based user stories, If the requirement itself is simple, the user stories should also be less - -## Competitive Analysis: Provided as Python list[str], up to 7 competitive product analyses, consider as similar competitors as possible - -## Competitive Quadrant Chart: Use mermaid quadrantChart code syntax. up to 14 competitive products. Translation: Distribute these competitor scores evenly between 0 and 1, trying to conform to a normal distribution centered around 0.5 as much as possible. - -## Requirement Analysis: Provide as Plain text. Be simple. LESS IS MORE. Make your requirements less dumb. Delete the parts unnessasery. - -## Requirement Pool: Provided as Python list[list[str], the parameters are requirement description, priority(P0/P1/P2), respectively, comply with PEP standards; no more than 5 requirements and consider to make its difficulty lower - -## UI Design draft: Provide as Plain text. Be simple. Describe the elements and functions, also provide a simple style description and layout description. -## Anything UNCLEAR: Provide as Plain text. Make clear here. - output a properly formatted JSON, wrapped inside [CONTENT][/CONTENT] like format example, and only output the json inside this tag, nothing else """, "FORMAT_EXAMPLE": """ [CONTENT] { - "New Requirements": "", + "Incremental Requirements": "", "Difference Description": [], - "Search Information": "", - "Requirements": "", "Incremental Development Plan": [], - "Product Goals": [], - "User Stories": [], - "Competitive Analysis": [], - "Competitive Quadrant Chart": "quadrantChart - title Reach and engagement of campaigns - x-axis Low Reach --> High Reach - y-axis Low Engagement --> High Engagement - quadrant-1 We should expand - quadrant-2 Need to promote - quadrant-3 Re-evaluate - quadrant-4 May be improved - Campaign A: [0.3, 0.6] - Campaign B: [0.45, 0.23] - Campaign C: [0.57, 0.69] - Campaign D: [0.78, 0.34] - Campaign E: [0.40, 0.34] - Campaign F: [0.35, 0.78]", - "Requirement Analysis": "", - "Requirement Pool": [["P0","P0 requirement"],["P1","P1 requirement"]], - "UI Design draft": "", - "Anything UNCLEAR": "", } [/CONTENT] """, @@ -106,154 +46,58 @@ and only output the json inside this tag, nothing else "markdown": { "PROMPT_TEMPLATE": """ # Context -You need to refine the requirements based on the new requirements and the existing requirements' output. -## User's New Requirements +You need to refine the requirements based on the Incremental Requirements and the existing requirements' output. +## User's Incremental Requirements {new_requirements} -## Difference Description -{difference_description} - ## Legacy PRD {legacy} ## Search Information {search_information} -## mermaid quadrantChart code syntax example. DONT USE QUOTO IN CODE DUE TO INVALID SYNTAX. Replace the with REAL COMPETITOR NAME -```mermaid -quadrantChart - title Reach and engagement of campaigns - x-axis Low Reach --> High Reach - y-axis Low Engagement --> High Engagement - quadrant-1 We should expand - quadrant-2 Need to promote - quadrant-3 Re-evaluate - quadrant-4 May be improved - "Campaign: A": [0.3, 0.6] - "Campaign B": [0.45, 0.23] - "Campaign C": [0.57, 0.69] - "Campaign D": [0.78, 0.34] - "Campaign E": [0.40, 0.34] - "Campaign F": [0.35, 0.78] - "Our Target Product": [0.5, 0.6] -``` - ## Format example {format_example} ----- Role: You are a professional Product Manager tasked with overseeing incremental development and crafting Product Requirements Documents (PRDs) for a concise, usable, and efficient product. Requirements: According to the context, fill in the following missing information, note that each sections are returned in Python code triple quote form seperatedly. If the requirements are unclear, ensure minimum viability and avoid excessive design -ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD WRITE BEFORE the code and triple quote. Output carefully referenced "Format example" in format. +ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD WRITE BEFORE the code and triple quote. Output carefully referenced "Format example" in format.Only output one json, nothing else. -## New Requirements: Provide as Plain text and place the new requirements here +## Incremental Requirements: Provide as str, the foremost incremental requirements for PRD here based on the previous. -## Difference Description: Provide as Python list[str], up to 5 clear, difference descriptions. If the requirement itself is simple, the difference description should also be simple +## Difference Description: Provide as str, the foremost differences description for PRD here based on the previous. ## Incremental Development Plan: Provide as Python list[str], up to 5 clear, incremental development plans. If the requirement itself is simple, the incremental development plan should also be simple - -## Product Goals: Provided as Python list[str], up to 3 clear, orthogonal product goals. If the requirement itself is simple, the goal should also be simple - -## User Stories: Provided as Python list[str], up to 5 scenario-based user stories, If the requirement itself is simple, the user stories should also be less - -## Competitive Analysis: Provided as Python list[str], up to 7 competitive product analyses, consider as similar competitors as possible - -## Competitive Quadrant Chart: Use mermaid quadrantChart code syntax. up to 14 competitive products. Translation: Distribute these competitor scores evenly between 0 and 1, trying to conform to a normal distribution centered around 0.5 as much as possible. - -## Requirement Analysis: Provide as Plain text. Be simple. LESS IS MORE. Make your requirements less dumb. Delete the parts unnessasery. - -## Requirement Pool: Provided as Python list[list[str], the parameters are requirement description, priority(P0/P1/P2), respectively, comply with PEP standards; no more than 5 requirements and consider to make its difficulty lower - -## UI Design draft: Provide as Plain text. Be simple. Describe the elements and functions, also provide a simple style description and layout description. -## Anything UNCLEAR: Provide as Plain text. Make clear here. """, "FORMAT_EXAMPLE": """ --- -## New Requirements +## Incremental Requirements The boss ... ## Difference Description -```python -[ - "...", -] +... ## Incremental Development Plan [ - "It ...", + "...", ] - -## Product Goals -```python -[ - "Create a ...", -] -``` - -## User Stories -```python -[ - "As a user, ...", -] -``` - -## Competitive Analysis -```python -[ - "Python Snake Game: ...", -] -``` - -## Competitive Quadrant Chart -```mermaid -quadrantChart - title Reach and engagement of campaigns - ... - "Our Target Product": [0.6, 0.7] -``` - -## Requirement Analysis -The product should be a ... - -## Requirement Pool -```python -[ - ["End game ...", "P0"] -] -``` - -## UI Design draft -Give a basic function description, and a draft - -## Anything UNCLEAR -There are no unclear points. ---- """, }, } INCREMENT_OUTPUT_MAPPING = { - "New Requirements": (str, ...), - # "Major Enhancements": (List[str], ...), - "Difference Description": (List[str], ...), + "Incremental Requirements": (str, ...), + "Difference Description": (Union[List[str], str], ...), "Incremental Development Plan": (List[str], ...), - "Product Goals": (List[str], ...), - "User Stories": (List[str], ...), - "Competitive Analysis": (List[str], ...), - "Competitive Quadrant Chart": (str, ...), - "Requirement Analysis": (str, ...), - "Requirement Pool": (List[List[str]], ...), - "UI Design draft": (str, ...), - "Anything UNCLEAR": (str, ...), } -# 对于产品经理,增量开发的动作是:RefinePDR,输出是结合新需求和已有需求输出的新的PDR class RefinePRD(Refine): def __init__(self, name="RefinePRD", context=None, llm=None): super().__init__(name, context, llm) - async def run(self, new_requirements, difference_description, legacy, format=CONFIG.prompt_format, *args, **kwargs): + async def run(self, new_requirements, legacy, format=CONFIG.prompt_format, *args, **kwargs): sas = SearchAndSummarize() rsp = "" info = f"### Search Results\n{sas.result}\n\n### Search Summary\n{rsp}" @@ -263,7 +107,7 @@ class RefinePRD(Refine): prompt_template, format_example = get_template(increment_template, format) prompt = prompt_template.format( - new_requirements=new_requirements, difference_description=difference_description, legacy=legacy, search_information=info, + new_requirements=new_requirements, legacy=legacy, search_information=info, format_example=format_example ) logger.debug(prompt) diff --git a/metagpt/actions/refine_project_management.py b/metagpt/actions/refine_project_management.py index 7d9e118d8..dfeeb2db0 100644 --- a/metagpt/actions/refine_project_management.py +++ b/metagpt/actions/refine_project_management.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from typing import List +from typing import List, Union from metagpt.actions.action import Action from metagpt.config import CONFIG @@ -15,62 +15,50 @@ templates = { # Context {context} -## Legacy Design +## Legacy {legacy} ## Format example {format_example} ----- -Role: You are a project manager; the goal is to perform incremental development based on the context and difference descriptions and the legacy. Break down tasks according to PRD/technical design, provide a task list, and analyze task dependencies to start with the prerequisite modules. -Requirements: Based on the context, fill in the following missing information, each section name is a key in json. Here the granularity of the task is a file, if there are any missing files, you can supplement them +Role: You are a project manager; the goal is to perform incremental development based on the context and difference descriptions and the legacy. Break down tasks according to PRD/technical design, provide a Task list, and analyze task dependencies to start with the prerequisite modules. +Requirements: Based on the context and the Legacy Project Management and Legacy Code, fill in the following missing information. Note that Please try your best to reuse legacy code, and all sections are returned in Python code triple quote form seperatedly. Here the granularity of the task is a file that need to modified. Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. -## Required Python third-party packages: Provided in requirements.txt format +## Difference Description: Provide as a python list, the foremost differences description for project management here based on the previous. -## Required Other language third-party packages: Provided in requirements.txt format +## Incremental Required Python third-party packages: Provided as a python list, the requirements.txt format -## Full API spec: Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend. +## Full API spec: Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend based on the previous. -## Difference Description: Please provide a detailed description of the differences between this project and its predecessors or similar projects that can include changes in technology, architecture. +## Logic Analysis: Only files need to modified, Provided as a Python list[list[str]. If the file has no changes, the file will not be output. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first based on the previous. -## Logic Analysis: Provided as a Python list[list[str]. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first - -## Task list: Provided as Python list[str]. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first - -## Shared Knowledge: Anything that should be public like utils' functions, config's variables details that should make clear first. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. For example, don't forget a main entry. don't forget to init 3rd party libs. +## Task list: Only files need to modified, provided as Python list[str]. If the file has no changes, the file will not be output. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first output a properly formatted JSON, wrapped inside [CONTENT][/CONTENT] like format example, and only output the json inside this tag, nothing else """, "FORMAT_EXAMPLE": ''' { - "Required Python third-party packages": [ + "Incremental Requirements": "...", + "Difference Description": [ + "...", + ] + "Incremental Required Python third-party packages": [ "flask==1.1.2", "bcrypt==3.2.0" ], - "Required Other language third-party packages": [ - "No third-party ..." - ], "Full API spec": """ openapi: 3.0.0 ... description: A JSON object ... """, - "Difference Description": """ - The ... - """, "Logic Analysis": [ ["game.py","Contains..."] ], "Task list": [ "game.py" - ], - "Shared Knowledge": """ - 'game.py' contains ... - """, - "Anything UNCLEAR": "We need ... how to start." + ] } ''', }, @@ -79,48 +67,44 @@ and only output the json inside this tag, nothing else # Context {context} -## Legacy Design +## Legacy {legacy} ## Format example {format_example} ----- -Role: You are a project manager; the goal is to perform incremental development based on the context and difference descriptions and the legacy. Break down tasks according to PRD/technical design, provide a task list, and analyze task dependencies to start with the prerequisite modules. -Requirements: Based on the context, fill in the following missing information, note that all sections are returned in Python code triple quote form seperatedly. Here the granularity of the task is a file, if there are any missing files, you can supplement them +Role: You are a project manager; the goal is to perform incremental development based on the context and the legacy. Break down tasks according to PRD/technical design, provide a Task list need to modified files, and analyze task dependencies to start with the prerequisite modules. +Requirements: Based on the context and the Legacy Project Management and Legacy Code, fill in the following missing information. Note that Please try your best to reuse legacy code, and all sections are returned in Python code triple quote form seperatedly. Here the granularity of the task is a file that need to modified. Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. -## Required Python third-party packages: Provided in requirements.txt format +## Difference Description: Provided as a python list, the foremost differences description for project management here based on the previous. -## Required Other language third-party packages: Provided in requirements.txt format +## Incremental Required Python third-party packages: Provided as a python list, the requirements.txt format -## Full API spec: Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend. +## Full API spec: Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend based on the previous. -## Difference Description: Please provide a detailed description of the differences between this project and its predecessors or similar projects that can include changes in technology, architecture. - -## Logic Analysis: Provided as a Python list[list[str]. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first - -## Task list: Provided as Python list[str]. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first - -## Shared Knowledge: Anything that should be public like utils' functions, config's variables details that should make clear first. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. For example, don't forget a main entry. don't forget to init 3rd party libs. +## Logic Analysis: Only files need to modified, Provided as a Python list[list[str]. If the file has no changes, the file will not be output. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first based on the previous. +## Task list: Only files need to modified, provided as Python list[str]. If the file has no changes, the file will not be output. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first """, "FORMAT_EXAMPLE": ''' --- -## Required Python third-party packages +## Incremental Requirements +... + +## Difference Description ```python -""" -flask==1.1.2 -bcrypt==3.2.0 -""" +[ + "The ...", +] ``` -## Required Other language third-party packages +## Incremental Required Python third-party packages ```python -""" -No third-party ... -""" +[ + "flask==1.1.2", + "bcrypt==3.2.0" +] ``` ## Full API spec @@ -132,13 +116,6 @@ description: A JSON object ... """ ``` -## Difference Description -```python -""" -The ... -""" -``` - ## Logic Analysis ```python [ @@ -152,29 +129,18 @@ The ... "game.py", ] ``` - -## Shared Knowledge -```python -""" -'game.py' contains ... -""" -``` - -## Anything UNCLEAR -We need ... how to start. --- ''', }, } OUTPUT_MAPPING = { - "Required Python third-party packages": (List[str], ...), - "Required Other language third-party packages": (List[str], ...), + # "Incremental Requirements": (str, ...), + # ## Incremental Requirements: Provided as a str, the foremost incremental requirements for project management here based on the previous. + "Difference Description": (Union[List[str], str], ...), + "Incremental Required Python third-party packages": (Union[List[str], str], ...), "Full API spec": (str, ...), - "Difference Description": (str, ...), "Logic Analysis": (List[List[str]], ...), "Task list": (List[str], ...), - "Shared Knowledge": (str, ...), - "Anything UNCLEAR": (str, ...), } @@ -192,11 +158,13 @@ class RefineTasks(Action): # Write requirements.txt requirements_path = WORKSPACE_ROOT / ws_name / "requirements.txt" - requirements_path.write_text("\n".join(rsp.instruct_content.dict().get("Required Python third-party packages"))) + requirements_path.write_text("\n".join(rsp.instruct_content.dict().get("Incremental Required Python third-party packages"))) async def run(self, context, legacy, format=CONFIG.prompt_format): prompt_template, format_example = get_template(templates, format) - prompt = prompt_template.format(context=context, legacy=legacy, format_example=format_example) + prompt = prompt_template.format(context=context, + legacy=legacy, + format_example=format_example) rsp = await self._aask_v1(prompt, "task", OUTPUT_MAPPING, format=format) self._save(context, rsp) return rsp diff --git a/metagpt/actions/write_code_guide.py b/metagpt/actions/write_code_guide.py new file mode 100644 index 000000000..391173bb3 --- /dev/null +++ b/metagpt/actions/write_code_guide.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +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 = """ +NOTICE +Role: You are a professional software engineer, and your main task is to conduct incremental development, proposing incremental development plans and code guideance based on context and legacy code. Existing code and logic that need to be retained must also appear in the code after incremental development, do not omit it. 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 '#'. Output format carefully referenced "Format example". + +## Regulations Review: To make the software directly operable without further coding, follow the regulations below during incremental development: +1) Import all referenced classes. +2) Implement all methods. +3) Add necessary explanation to all methods. +4) Ensure there are no potential bugs. +5) Confirm that the entire project conforms to the tasks proposed by the user. +6) Review the code thoroughly, checking for errors and validating the logic to ensure seamless user interaction without compromising any specified requirements. + +## Incremental Development Plan: Proposed the Minimum essential incremental development plan, based on the following context and legacy code by thinking step by step. +... + +## Code guidelines: Propose the foremost guidelines that how to implement code of modification part for incremental development based on the above context, legacy code and incremental development plan. +```python +... +''' +----- +# Context +{context} + +## Legacy Code +You are tasked with conducting incremental development in the existing code based on the provided legacy code and above information. +``` +{code} +``` +----- + +## Format example +----- +## Incremental Development Guide: +... + +## Code Guidance: +# Implementation the ... +```python +... +''' +----- +""" + + +class WriteCodeGuide(Action): + def __init__(self, name="WriteCodeGuide", context: list[Message] = None, llm=None): + super().__init__(name, context, llm) + + async def run(self, context, code): + prompt = PROMPT_TEMPLATE.format(context=context, code=code) + logger.info(f'Write Code Guide ..') + code_guide = await self._aask(prompt) + # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) + # self._save(context, filename, code) + return code_guide + \ No newline at end of file diff --git a/metagpt/actions/write_code_refine.py b/metagpt/actions/write_code_refine.py index 527952aed..466c30679 100644 --- a/metagpt/actions/write_code_refine.py +++ b/metagpt/actions/write_code_refine.py @@ -7,58 +7,44 @@ from metagpt.schema import Message 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 conduct incremental development, which includes reviewing existing code, providing modification suggestions, rewriting code, and optimizing the codebase. Existing code and logic that need to be retained must also appear in the code after incremental development, do not omit it. 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 '#'. Output format carefully referenced "Format example". +Role: You are a professional engineer; your primary goal is to write PEP8 compliant, elegant, modular, easy-to-read, and maintainable Python 3.9 code (or any other programming language of your choice). +Requirements: You should modify the corresponding code based on the guidance. Then, output the complete code, fixing all errors according to the context. Ensure that you adhere to the specified guidelines for incremental development and modification of legacy code. +ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format should be carefully referenced using the "Format example". Only output the current modified code, nothing else. In the modified code, if unchanged, you should output it, the complete code. -## Code Review: Based on the following context and legacy code, and following the checklist, 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? -``` - -## Incremental Development: {filename} Based on the findings from the "Code Review," context, and the legacy code, conduct incremental development by rewriting, optimizing, and adding new code using triple quotes. +## Code: Only Write {filename}, Write code using triple quotes, 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: Implement one of the following code files based on the provided context. Return the code in the specified format. Your code will be part of the entire project, so ensure it is complete, reliable, and reusable. +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. ----- # Context {context} -## Legacy Code -You are tasked with conducting incremental development in the existing code and creating a new code file, {filename}, based on the provided legacy code and above information. -``` -{code} -``` ----- +## Guidelines: The foremost guidelines of modification for incremental development. +{guide} +----- +## Legacy Code: The Legacy Code that needs to be modified. +{legacy} + +----- ## Format example ----- -{format_example} ------ - -""" - -FORMAT_EXAMPLE = """ - -## Code Review -1. The code ... -2. ... -3. ... -4. ... -5. ... - -## Incremental Development: {filename} +## Modified/Added Code: {filename} ```python -## {filename} - Incremental Development +# {filename} ... ``` +----- """ - class WriteCodeRefine(Action): def __init__(self, name="WriteCodeRefine", context: list[Message] = None, llm=None): super().__init__(name, context, llm) @@ -69,9 +55,8 @@ class WriteCodeRefine(Action): code = CodeParser.parse_code(block="", text=code_rsp) return code - async def run(self, context, code, filename): - format_example = FORMAT_EXAMPLE.format(filename=filename) - prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=filename, format_example=format_example) + async def run(self, context, code, filename, guide): + prompt = PROMPT_TEMPLATE.format(context=context, legacy=code, filename=filename, guide=guide) logger.info(f'Code refine {filename}..') code = await self.write_code(prompt) # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) diff --git a/metagpt/environment.py b/metagpt/environment.py index 24e6ada2f..4e409b921 100644 --- a/metagpt/environment.py +++ b/metagpt/environment.py @@ -24,6 +24,7 @@ class Environment(BaseModel): roles: dict[str, Role] = Field(default_factory=dict) memory: Memory = Field(default_factory=Memory) history: str = Field(default='') + legacy: dict = Field(default_factory=dict) class Config: arbitrary_types_allowed = True @@ -77,3 +78,15 @@ class Environment(BaseModel): get all the environment roles """ return self.roles.get(name, None) + + def set_legacy(self, legacy_dict: dict) -> None: + """设置环境的遗产 + set the environment legacy + """ + self.legacy = legacy_dict + + def get_legacy(self) -> dict: + """获得环境的遗产 + get the environment legacy + """ + return self.legacy diff --git a/metagpt/roles/architect.py b/metagpt/roles/architect.py index dddcd2a8b..f87f8899d 100644 --- a/metagpt/roles/architect.py +++ b/metagpt/roles/architect.py @@ -32,12 +32,10 @@ class Architect(Role): 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", - legacy: str = "", increment: bool = False, ) -> None: """Initializes the Architect with given attributes.""" super().__init__(name, profile, goal, constraints) - self.legacy = legacy self.increment = increment # Initialize actions specific to the Architect role @@ -52,7 +50,8 @@ class Architect(Role): async def _act(self) -> Message: if self.increment: logger.info(f"{self._setting}: ready to RefineDesign") - response = await self._rc.todo.run(self._rc.history, self.legacy) + legacy = self._rc.env.get_legacy()["legacy_design"] + response = await self._rc.todo.run(self._rc.history, legacy) else: logger.info(f"{self._setting}: ready to WriteDesign") diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 13034acaa..6f96aeb78 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -14,8 +14,10 @@ from typing import List from metagpt.actions import WriteCode, WriteCodeReview, WriteDesign, WriteTasks, BossRequirement from metagpt.actions.refine_design_api import RefineDesign +from metagpt.actions.refine_prd import RefinePRD from metagpt.actions.refine_project_management import RefineTasks from metagpt.actions.write_code_refine import WriteCodeRefine +from metagpt.actions.write_code_guide import WriteCodeGuide from metagpt.const import WORKSPACE_ROOT from metagpt.logs import logger from metagpt.roles import Role @@ -73,31 +75,25 @@ class Engineer(Role): constraints: str = "The code should conform to standards like PEP8 and be modular and maintainable", n_borg: int = 1, use_code_review: bool = False, - legacy: str = "", increment: bool = False, - bug_msgs: List = None, bug_fix: 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 - self.legacy = legacy self.increment = increment - self.bug_msgs = bug_msgs self.bug_fix = bug_fix - if self.use_code_review or self.increment: - self._init_actions([WriteCode, WriteCodeReview, WriteCodeRefine]) + if self.use_code_review: + self._init_actions([WriteCode, WriteCodeReview]) if self.increment: + self._init_actions([WriteCodeGuide, WriteCodeRefine, WriteCodeReview]) self._watch([RefineTasks]) - self.todos = [] - elif self.bug_fix: - self._watch([BossRequirement]) - self.todos = [] else: self._watch([WriteTasks]) - self.todos = [] + + self.todos = [] self.n_borg = n_borg @classmethod @@ -210,36 +206,50 @@ class Engineer(Role): ) return msg - async def _act_increment(self, legacy) -> Message: + async def _act_increment(self) -> Message: code_msg_all = [] # gather all code info, will pass to qa_engineer for tests later workspace = self.get_workspace() - flag = True - # legacy_codes = legacy.split('---') + # human_str = "\n".join([msg.content for msg in self._rc.memory.get_by_role("Human")]) + human_str = str(self._rc.memory.get_by_role("Human")[0]) + code = self._rc.env.get_legacy()["legacy_code"] + + # Refine code + context = [] + msg = self._rc.memory.get_by_actions([RefinePRD, RefineDesign, RefineTasks]) + + for m in msg: + context.append(m.content) + context_str = human_str + "\n".join(context) + try: + logger.info("Write Code Guide start!") + guide = await WriteCodeGuide().run(context=context_str, code=code) + msg = Message(content=guide, role=self.profile, cause_by=WriteCodeGuide) + self._rc.memory.add(msg) + except Exception as e: + logger.error("Write Code Guide failed!", e) + pass + + # Write code or Code review for todo in self.todos: - context = [] - - msg = self._rc.memory.get_by_actions([RefineDesign, RefineTasks, WriteCodeRefine]) - for m in msg: - context.append(m.content) - context_str = "\n".join(context) - code = legacy - - # Refine code or Write code - # if self.increment and len(legacy_codes) > 0: - # code = legacy_codes.pop(0) - - # Code review + msg = self._rc.memory.get_by_actions([RefineTasks]) + context_str = human_str + "\n".join([m.content for m in msg]) + # WriteCodeRefine try: - rewrite_code = await WriteCodeRefine().run(context=context_str, code=code, filename=todo) - code = rewrite_code + logger.info("Write Code Refine start!") + code = await WriteCodeRefine().run(context=context_str, code=code, filename=todo, guide=guide) except Exception as e: - logger.error("code review failed!", e) + logger.error("Write Code Refine failed!", e) pass - - # code = await WriteCode().run(context=context_str, filename=todo) - + # FIXME: Code review Action + # 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 file_path = self.write_file(workspace, todo, code) - msg = Message(content=code, role=self.profile, cause_by=WriteCode) + msg = Message(content=code, role=self.profile, cause_by=WriteCodeRefine) self._rc.memory.add(msg) code_msg = todo + FILENAME_CODE_SEP + str(file_path) @@ -273,7 +283,7 @@ class Engineer(Role): # Code review try: - rewrite_code = await WriteCodeRefine().run(context=context_str, code=code, filename=todo) + rewrite_code = await WriteCodeGuide().run(context=context_str, code=code, filename=todo) code = rewrite_code except Exception as e: logger.error("code review failed!", e) @@ -333,35 +343,31 @@ class Engineer(Role): ) return msg - async def _observe(self) -> int: - if self.bug_fix: - msg = Message( - content=self.bug_msgs[0].content + "\n---\n" + self.legacy, - role=self.profile, - cause_by=BossRequirement, - sent_from=self.profile, - send_to=self.profile, - ) - self._publish_message(msg) - await super()._observe() - self._rc.news = [ - msg for msg in self._rc.news if msg.send_to == self.profile - ] # only relevant msgs count as observed news - return len(self._rc.news) + # async def _observe(self) -> int: + # if self.bug_fix: + # msg = Message( + # content=self.bug_msgs[0].content + "\n---\n" + self.legacy, + # role=self.profile, + # cause_by=BossRequirement, + # sent_from=self.profile, + # send_to=self.profile, + # ) + # self._publish_message(msg) + # await super()._observe() + # self._rc.news = [ + # msg for msg in self._rc.news if msg.send_to == self.profile + # ] # only relevant msgs count as observed news + # return len(self._rc.news) async def _act(self) -> Message: """Determines the mode of action based on whether code review is used.""" if self.increment: - logger.info(f"{self._setting}: ready to RefineWriteCode") - elif self.bug_fix: - logger.info(f"{self._setting}: ready to BugFix") + logger.info(f"{self._setting}: ready to WriteExtraCode and WriteCodeRefine") else: logger.info(f"{self._setting}: ready to WriteCode") - if self.use_code_review: + if self.increment: + return await self._act_increment() + elif self.use_code_review: return await self._act_sp_precision() - elif self.increment: - return await self._act_increment(self.legacy) - elif self.bug_fix: - return await self._act_bug_fix(self.bug_msgs) return await self._act_sp() diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index 0b2d83ed0..f48720430 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -29,8 +29,6 @@ class ProductManager(Role): profile: str = "Product Manager", goal: str = "Efficiently create a successful product", constraints: str = "", - difference_description: str = "", - legacy: str = "", increment: bool = False, ) -> None: """ @@ -43,8 +41,6 @@ class ProductManager(Role): constraints (str): Constraints or limitations for the product manager. """ super().__init__(name, profile, goal, constraints) - self.difference_description = difference_description - self.legacy = legacy self.increment = increment if self.increment: @@ -56,7 +52,8 @@ class ProductManager(Role): async def _act(self) -> Message: if self.increment: logger.info(f"{self._setting}: ready to RefinePRD") - response = await self._rc.todo.run(self._rc.history, self.difference_description, self.legacy) + legacy = self._rc.env.get_legacy()["legacy_prd"] + response = await self._rc.todo.run(self._rc.history, legacy) else: logger.info(f"{self._setting}: ready to WritePRD") diff --git a/metagpt/roles/project_manager.py b/metagpt/roles/project_manager.py index 21a196d21..5d4820f4a 100644 --- a/metagpt/roles/project_manager.py +++ b/metagpt/roles/project_manager.py @@ -32,7 +32,6 @@ class ProjectManager(Role): goal: str = "Improve team efficiency and deliver with quality and quantity", constraints: str = "", increment: bool = False, - legacy: str = "", ) -> None: """ Initializes the ProjectManager role with given attributes. @@ -45,8 +44,6 @@ class ProjectManager(Role): """ super().__init__(name, profile, goal, constraints) self.increment = increment - self.legacy = legacy - if self.increment: self._init_actions([RefineTasks]) self._watch([RefineDesign]) @@ -57,7 +54,11 @@ class ProjectManager(Role): async def _act(self) -> Message: if self.increment: logger.info(f"{self._setting}: ready to RefineTasks") - response = await self._rc.todo.run(self._rc.history, self.legacy) + human_str = "\n".join([msg.content for msg in self._rc.memory.get_by_role("Human")]) + # legacy_project_management and legacy_code + legacy_dict = self._rc.env.get_legacy() + legacy_str = "Legacy Project Management:\n" + legacy_dict["legacy_project_management"] + "\nLegacy Code:\n" + legacy_dict["legacy_code"] + response = await self._rc.todo.run(self._rc.history, legacy=legacy_str) else: logger.info(f"{self._setting}: ready to WriteTasks") diff --git a/metagpt/team.py b/metagpt/team.py index 67d3ecec8..67f1973b5 100644 --- a/metagpt/team.py +++ b/metagpt/team.py @@ -59,4 +59,10 @@ class Team(BaseModel): self._check_balance() await self.environment.run() return self.environment.history + + def set_legacy(self, legacy_dict): + self.environment.legacy = legacy_dict + + def get_legacy(self): + return self.environment.legacy \ No newline at end of file diff --git a/startup.py b/startup.py index 8bb8517c9..647fc307e 100644 --- a/startup.py +++ b/startup.py @@ -28,12 +28,11 @@ async def startup( run_tests: bool = False, implement: bool = True, increment: bool = False, - bug_fix: bool = False, ): """Run a startup. Be a boss.""" company = Team() - if increment or bug_fix: + if increment: # 读取文件 prd_path = os.path.join(project_path, 'docs/prd.md') design_path = os.path.join(project_path, 'docs/system_design.md') @@ -47,64 +46,46 @@ async def startup( legacy_design = f.read() with open(api_spec_and_tasks_path, 'r', encoding='utf-8') as f: - legacy_api_spec_and_tasks = f.read() + legacy_project_management = f.read() - # 遍历文件夹,获取所有代码文件 legacy_code = '' for root, dirs, files in os.walk(code_path): - filenames = [filename for filename in files if filename.endswith('.py')] - legacy_code += f'There are {len(files)} scripts in the current folder: {", ".join(filenames)}\n---\n' for file in files: if file.endswith('.py'): with open(os.path.join(root, file), 'r', encoding='utf-8') as f: legacy_code += f.read() + '\n---\n' - if bug_fix: - boss_msg = Message( - content=f"Boss's requirement\n:{idea}\n---\nBoss's difference description:{difference_description}\n---\n", - sent_from="Boss", - ) - product_manager_msg = Message( - content=f"Product Manager's prd legacy:\n{legacy_prd}\n---\n", - sent_from="ProductManager" - ) - architect_msg = Message( - content=f"Architect's design legacy:\n{legacy_design}\n---\n", - sent_from="Architect" - ) - project_manager_msg = Message( - content=f"Project Manager's api spec and tasks legacy:\n{legacy_api_spec_and_tasks}\n---\n", - sent_from="ProjectManager" - ) - engineer_msg = Message( - content=f"Engineer's code legacy:\n{legacy_code}\n---\n", - sent_from="Engineer" - ) - bug_msgs = [boss_msg, product_manager_msg, architect_msg, project_manager_msg, engineer_msg] - else: - company.hire( - [ProductManager(difference_description=difference_description, legacy=legacy_prd, increment=increment), - Architect(legacy=legacy_design, increment=increment), - ProjectManager(legacy=legacy_api_spec_and_tasks, increment=increment)]) + legacy_dict = { + 'legacy_prd': legacy_prd, + 'legacy_design': legacy_design, + 'legacy_project_management': legacy_project_management, + 'legacy_code': legacy_code + } + company.set_legacy(legacy_dict) + company.hire( + [ + ProductManager(increment=increment), + Architect(increment=increment), + ProjectManager(increment=increment) + ] + ) else: company.hire([ProductManager(), Architect(), ProjectManager()]) # if implement or code_review - if bug_fix: - company.hire([Engineer(n_borg=5, bug_msgs=bug_msgs, bug_fix=bug_fix)]) - elif implement or code_review: + if (implement or code_review) and increment: + company.hire([Engineer(n_borg=5, use_code_review=code_review, increment=increment)]) + elif implement: # developing features: implement the idea company.hire([Engineer(n_borg=5, use_code_review=code_review)]) - elif (implement or code_review) and increment: - company.hire([Engineer(n_borg=5, use_code_review=code_review, legacy=legacy_code, increment=increment)]) - if run_tests or bug_fix: + if run_tests: # developing features: run tests on the spot and identify bugs # (bug fixing capability comes soon!) company.hire([QaEngineer()]) company.invest(investment) - company.start_project(idea) + company.start_project(idea+"\n"+difference_description) await company.run(n_round=n_round) @@ -118,7 +99,6 @@ def main( run_tests: bool = False, implement: bool = True, increment: bool = False, - bug_fix: bool = False, ): """ We are a software startup comprised of AI. By investing in us, @@ -130,10 +110,8 @@ def main( :param code_review: Whether to use code review. :return: """ - - asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) asyncio.run( - startup(idea, difference_description, project_path, investment, n_round, code_review, run_tests, implement, increment, bug_fix)) + startup(idea, difference_description, project_path, investment, n_round, code_review, run_tests, implement, increment)) if __name__ == "__main__":