From 6abebf05d52742f39d7dfff7593cad1db3584b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Thu, 10 Oct 2024 14:31:27 +0800 Subject: [PATCH 1/4] create React/Vue project --- metagpt/actions/design_api_an.py | 39 +++++++++++++++-- metagpt/actions/write_prd_an.py | 4 +- metagpt/const.py | 5 +++ metagpt/prompts/di/engineer2.py | 32 +++++++++----- metagpt/prompts/di/team_leader.py | 2 +- metagpt/roles/di/engineer2.py | 8 +--- metagpt/strategy/experience_retriever.py | 56 ++++++++++++++++-------- metagpt/tools/libs/editor.py | 6 +++ metagpt/tools/libs/terminal.py | 4 +- 9 files changed, 113 insertions(+), 43 deletions(-) diff --git a/metagpt/actions/design_api_an.py b/metagpt/actions/design_api_an.py index 2541d0469..b9ab3a146 100644 --- a/metagpt/actions/design_api_an.py +++ b/metagpt/actions/design_api_an.py @@ -13,7 +13,7 @@ from metagpt.utils.mermaid import MMC1, MMC2 IMPLEMENTATION_APPROACH = ActionNode( key="Implementation approach", expected_type=str, - instruction="Analyze the difficult points of the requirements, select the appropriate open-source framework", + instruction="Analyze the difficult points of the requirements, select the appropriate open-source framework. The default technical stack is Vite, React and Tailwind CSS.", example="We will ...", ) @@ -29,12 +29,45 @@ REFINED_IMPLEMENTATION_APPROACH = ActionNode( PROJECT_NAME = ActionNode( key="Project name", expected_type=str, instruction="The project name with underline", example="game_2048" ) +FILE_LIST_INSTRUCTION = """ +The default React project file list is: +├── ./eslint.config.js +├── ./index.html +├── ./package.json +├── ./pnpm-lock.yaml +├── ./postcss.config.js +├── ./public +├── ./src +│ ├── ./src/App.jsx +│ ├── ./src/index.css +│ └── ./src/main.jsx +├── ./tailwind.config.js +└── ./vite.config.js +The default Vue project file list is: +. +├── ./index.html +├── ./package.json +├── ./pnpm-lock.yaml +├── ./postcss.config.js +├── ./public +│ └── ./public/vite.svg +├── ./README.md +├── ./src +│ ├── ./src/App.vue +│ ├── ./src/main.js +│ └── ./src/style.css +├── ./tailwind.config.js +└── ./vite.config.js + +If no technical stack is specified, use the Vite, React, MUI and Tailwind CSS as the default stack. +Succinctly designate the correct entry file for your project based on the programming language. If the project is React or Vue, refer to the file structure provided in the template. +""" FILE_LIST = ActionNode( key="File list", expected_type=List[str], - instruction="Only need relative paths. Succinctly designate the correct entry file for your project based on the programming language: use main.js for JavaScript, main.py for Python, and so on for other languages.", - example=["a.js", "b.py", "c.css", "d.html"], + instruction=FILE_LIST_INSTRUCTION, + example=["a.js", "b.py", "c.css", "d.html", "e.vue", "f.jsx"], ) REFINED_FILE_LIST = ActionNode( diff --git a/metagpt/actions/write_prd_an.py b/metagpt/actions/write_prd_an.py index 574dcef89..d923e8cd0 100644 --- a/metagpt/actions/write_prd_an.py +++ b/metagpt/actions/write_prd_an.py @@ -19,8 +19,8 @@ LANGUAGE = ActionNode( PROGRAMMING_LANGUAGE = ActionNode( key="Programming Language", expected_type=str, - instruction="Mainstream programming language. If not specified in the requirements, use native HTML", - example="native HTML", + instruction="Mainstream programming language. If not specified in the requirements, technical stack Vite, React and Tailwind CSS", + example="Vite, React and Tailwind CSS", ) ORIGINAL_REQUIREMENTS = ActionNode( diff --git a/metagpt/const.py b/metagpt/const.py index 4fe8dca3d..9dc08b8d4 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -71,6 +71,11 @@ SKILL_DIRECTORY = SOURCE_ROOT / "skills" TOOL_SCHEMA_PATH = METAGPT_ROOT / "metagpt/tools/schemas" TOOL_LIBS_PATH = METAGPT_ROOT / "metagpt/tools/libs" +# TEMPLATE PATH +TEMPLATE_FOLDER_PATH = METAGPT_ROOT / "template" +VUE_TEMPLATE_PATH = TEMPLATE_FOLDER_PATH / "vue_template" +REACT_TEMPLATE_PATH = TEMPLATE_FOLDER_PATH / "react_template" + # REAL CONSTS MEM_TTL = 24 * 30 * 3600 diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 8a3860742..2a1725da9 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -1,3 +1,4 @@ +from metagpt.const import REACT_TEMPLATE_PATH, VUE_TEMPLATE_PATH from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION EXTRA_INSTRUCTION = """ @@ -7,7 +8,6 @@ The special interface consists of a file editor that shows you 100 lines of a fi You can use terminal commands (e.g., cat, ls, cd) by calling Terminal.run_command. - You should carefully observe the behavior and results of the previous action, and avoid triggering repeated errors. In addition to the terminal, I also provide additional tools. @@ -70,19 +70,28 @@ Note: 12. If you choose Editor.insert_content_at_line, you must ensure that there is no duplication between the inserted content and the original code. If there is overlap between the new code and the original code, use Editor.edit_file_by_replace instead. 13. If you choose Editor.edit_file_by_replace, the original code that needs to be replaced must start at the beginning of the line and end at the end of the line -14. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path. +14. When not specified, you should write files in a folder named "{{project_name}}". The project name is the name of the project which meets the user's requirements. 15. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. 16. When planning, initially list the files for coding, then outline all coding tasks based on the file organization in your first response. 17. If you plan to read a file, do not include other plans in the same response. 18. Write only one code file each time and provide its full implementation. 19. When the requirement is simple, you don't need to create a plan, just do it right away. -20. If the code exists, use the Editor tool's open and edit commands to modify it. Since it is not a new code, do not use write_new_code. -21. When using the editor, pay attention to the editor's current directory. When you use editor tools, the paths must be either absolute or relative to the editor's current directory. -22. The default programming languages are Native HTML. -23. When planning, consider whether images are needed. If you are developing a showcase website, start by using ImageGetter.get_image to obtain the necessary images. -24. When planning, merge multiple tasks that operate on the same file into a single task. For example, create one task for writing unit tests for all functions in a class. Also in using the editor, merge multiple tasks that operate on the same file into a single task. -25. When create unit tests for a code file, use Editor.read() to read the code file before planing. And create one plan to writing the unit test for the whole file. -""" +20. When using the editor, pay attention to current directory. When you use editor tools, the paths must be either absolute or relative to the editor's current directory. +21. When planning, consider whether images are needed. If you are developing a showcase website, start by using ImageGetter.get_image to obtain the necessary images. +22. When planning, merge multiple tasks that operate on the same file into a single task. For example, create one task for writing unit tests for all functions in a class. Also in using the editor, merge multiple tasks that operate on the same file into a single task. +23. When create unit tests for a code file, use Editor.read() to read the code file before planing. And create one plan to writing the unit test for the whole file. + +24. Follow the Sytem Design and Project Schedule if exists. Otherwise, use default template folder of Vite, React, MUI and Tailwind CSS. If the template does not exist, use native HTML. +25. When writing Vue/React project: The Vue template is in the {vue_template_path}, the React template is in the {react_template_path}. +25.1. Create the project folder first. Use cmd " mkdir -p {{project_name}} " +25.2. Copy a Vue/React template to your project and view all files. This must be a single respond. Use cmd "cp -r {{template_folder}}/* {{workspace}}/{{project_name}}/ && cd {{workspace}}/{{project_name}} && pwd && tree -f". +25.3. Read the content of each file and use the write_new_code command to rewrite the code. Be sure you are in the {{project_name}}. Reorganize the file structure to match the project template if it differs from the system design. +25.4. After finish the project. use "npm install" and "npm run build" to build the project. +26. Engineer2.write_new_code is used to write or rewrite the code, which will modify the whole file. Editor.edit_file_by_replace is used to edit a small part of the file. +""".format( + vue_template_path=VUE_TEMPLATE_PATH.absolute(), + react_template_path=REACT_TEMPLATE_PATH.absolute(), +) CURRENT_STATE = """ The current editor state is: (Current directory: {current_directory}) @@ -113,10 +122,11 @@ WRITE_CODE_PROMPT = """ {file_description} # Instruction -Your task is to write the {file_name} according to the User Requirement. +Your task is to write the {file_name} according to the User Requirement. You must ensure the code is complete, correct, and bug-free. # Output -While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. Output code in the following format: +While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. DO NOT leave any TODO or placeholder. +Output code in the following format: ``` your code ``` diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 9b0163710..578b3432d 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -34,7 +34,7 @@ Note: 11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements. 12. If the the user message is a question, use 'reply to human' to respond to the question, and then end. 13. Instructions and reply must be in the same language. -14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. +14. The default type of software development is web, and the default programming tech stack is Vite, React, MUI and Tailwind CSS. 15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. 16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. """ diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 6352a217f..799361df6 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -104,12 +104,6 @@ class Engineer2(RoleZero): def _retrieve_experience(self) -> str: return ENGINEER_EXAMPLE - async def _run_special_command(self, cmd) -> str: - """command requiring special check or parsing.""" - # finish current task before end. - command_output = await super()._run_special_command(cmd) - return command_output - async def write_new_code(self, path: str, file_description: str = "") -> str: """Write a new code file. @@ -117,6 +111,8 @@ class Engineer2(RoleZero): path (str): The absolute path of the file to be created. file_description (optional, str): "Brief description and important notes of the file content, must be very concise and can be empty. Defaults to "". """ + # If the path is not absolute, try to fix it with the editor's working directory. + path = self.editor._try_fix_path(path) plan_status, _ = self._get_plan_status() prompt = WRITE_CODE_PROMPT.format( user_requirement=self.planner.plan.goal, diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index aec8a3337..b35fc73af 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -479,7 +479,7 @@ Explanation: The requirement is about software development. Assign each tasks to "args": { "task_id": "1", "dependent_task_ids": [], - "instruction": "Use native HTML for the program. And create a product requirement document (PRD) outlining the features, user interface. ", + "instruction": "Use Vite, React and Tailwind CSS for the program. And create a product requirement document (PRD) outlining the features, user interface. ", "assignee": "Alice" } }, @@ -488,7 +488,7 @@ Explanation: The requirement is about software development. Assign each tasks to "args": { "task_id": "2", "dependent_task_ids": ["1"], - "instruction": "Use native HTML for the program. Design the software architecture for the CLI snake game, including the data flow.", + "instruction": "Use Vite, React and Tailwind CSS for the program. Design the software architecture for the CLI snake game, including the data flow.", "assignee": "Bob" } }, @@ -839,19 +839,19 @@ Explanation: I will first need to read the system design document and the projec } ] ``` +## example 2.1 +User Requirement: Implement the core game project in Vue/React framework. +Explanation: This is a project that needs to be implemented using Vue.js. Therefore, I need to copy the Vue/React template to the project folder first. -## example 2 -Consider this example only after you have obtained the content of system design and project schedule documents. -Suppose the system design and project schedule prescribes three files index.html, style.css, script.js, to follow the design and schedule, I will create a plan consisting of three tasks, each corresponding to the creation of one of the required files: `index.html`, `style.css`, and `script.js`. - +## example 2.2 +User Requirement: Writing code. Here's the plan: -[Optional] 0. **Task 0**: Obtain images before coding. -1. **Task 1**: Create `index.html` - This file will contain the HTML structure necessary for the game's UI. -2. **Task 2**: Create `style.css` - This file will define the CSS styles to make the game visually appealing and responsive. -3. **Task 3**: Create `script.js` - This file will contain the Pure JavaScript code for the game logic and UI interactions. - -Let's start by appending the first task to the plan. - +[Optional] Read the original file in the template if they exist. +[Optional] Obtain images before coding. +1. **Task 1**: Rewrite `App.vue` - This file will contain the Vue structure necessary for the game's UI, the game logic and UI interactions. +2. **Task 2**: Rewrite `style.css` - This file will define the CSS styles to make the game visually appealing and responsive. Default use tailwindcss. +3. **Task 3**: Rewrite `main.js` - This file is the main entry of Vue project, including the main Vue instance, global styles, and the router. +[Optional] Install the dependencies after finishing project. ```json [ { @@ -859,7 +859,7 @@ Let's start by appending the first task to the plan. "args": { "task_id": "1", "dependent_task_ids": [], - "instruction": "Create the index.html file with the basic HTML structure for the 2048 game.", + "instruction": "Read the original file in the template if they exist.", "assignee": "Alex" } }, @@ -867,8 +867,8 @@ Let's start by appending the first task to the plan. "command_name": "Plan.append_task", "args": { "task_id": "2", - "dependent_task_ids": ["1"], - "instruction": "Create the style.css file with the necessary CSS to style the 2048 game.", + "dependent_task_ids": [], + "instruction": "Create the App.vue file with the game's UI, the game logic and UI interactions, the style will implemented in the global style file using Tailwind CSS.", "assignee": "Alex" } }, @@ -876,8 +876,26 @@ Let's start by appending the first task to the plan. "command_name": "Plan.append_task", "args": { "task_id": "3", - "dependent_task_ids": ["1", "2"], - "instruction": "Create the script.js file containing the Pure JavaScript logic for the 2048 game.", + "dependent_task_ids": ["2"], + "instruction": "Create the style.css file with Tailwind CSS for the 2048 Game.", + "assignee": "Alex" + } + }, + { + "command_name": "Plan.append_task", + "args": { + "task_id": "4", + "dependent_task_ids": ["2","3"], + "instruction": "Create the main.js, which will include the main Vue instance, global styles, and the router.", + "assignee": "Alex" + } + }, + { + "command_name": "Plan.append_task", + "args": { + "task_id": "5", + "dependent_task_ids": ["2","3","4"], + "instruction": "Install the necessary dependencies and configure the project structure.", "assignee": "Alex" } } @@ -885,7 +903,7 @@ Let's start by appending the first task to the plan. ``` ## example 3 -Explanation: Take on one task, such as writing a file. Upon completion, finish current task +Explanation: Take on one task, such as writing or rewriting a file. Upon completion, finish current task. ```json [ diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index b297c9989..c0f8b7de4 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -116,6 +116,9 @@ class Editor(BaseModel): def write(self, path: str, content: str): """Write the whole content to a file. When used, make sure content arg contains the full content of the file.""" + + path = self._try_fix_path(path) + if "\n" not in content and "\\n" in content: # A very raw rule to correct the content: If 'content' lacks actual newlines ('\n') but includes '\\n', consider # replacing them with '\n' to potentially correct mistaken representations of newline characters. @@ -130,6 +133,9 @@ class Editor(BaseModel): async def read(self, path: str) -> FileBlock: """Read the whole content of a file. Using absolute paths as the argument for specifying the file location.""" + + path = self._try_fix_path(path) + error = FileBlock( file_path=str(path), block_content="The file is too large to read. Use `Editor.similarity_search` to read the file instead.", diff --git a/metagpt/tools/libs/terminal.py b/metagpt/tools/libs/terminal.py index 24daa184d..a621e4b0e 100644 --- a/metagpt/tools/libs/terminal.py +++ b/metagpt/tools/libs/terminal.py @@ -5,7 +5,7 @@ from asyncio.subprocess import PIPE, STDOUT from typing import Optional from metagpt.config2 import Config -from metagpt.const import SWE_SETUP_PATH +from metagpt.const import DEFAULT_WORKSPACE_ROOT, SWE_SETUP_PATH from metagpt.logs import logger from metagpt.tools.tool_registry import register_tool from metagpt.utils.report import END_MARKER_VALUE, TerminalReporter @@ -33,6 +33,8 @@ class Terminal: *self.shell_command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, executable="bash", env=os.environ.copy() ) await self._check_state() + # Goto the default directory + await self.run_command(f"cd {DEFAULT_WORKSPACE_ROOT.absolute()}") async def _check_state(self): """ From 823280c5b8440888dda1a65645ce8d990c282560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 11 Oct 2024 10:25:40 +0800 Subject: [PATCH 2/4] keep original Architecture and Product Manager --- metagpt/actions/design_api_an.py | 37 +----- metagpt/actions/write_prd_an.py | 4 +- metagpt/roles/di/team_leader.py | 145 ++++++++++------------- metagpt/strategy/experience_retriever.py | 4 +- 4 files changed, 66 insertions(+), 124 deletions(-) diff --git a/metagpt/actions/design_api_an.py b/metagpt/actions/design_api_an.py index b9ab3a146..165f2b57b 100644 --- a/metagpt/actions/design_api_an.py +++ b/metagpt/actions/design_api_an.py @@ -29,45 +29,12 @@ REFINED_IMPLEMENTATION_APPROACH = ActionNode( PROJECT_NAME = ActionNode( key="Project name", expected_type=str, instruction="The project name with underline", example="game_2048" ) -FILE_LIST_INSTRUCTION = """ -The default React project file list is: -├── ./eslint.config.js -├── ./index.html -├── ./package.json -├── ./pnpm-lock.yaml -├── ./postcss.config.js -├── ./public -├── ./src -│ ├── ./src/App.jsx -│ ├── ./src/index.css -│ └── ./src/main.jsx -├── ./tailwind.config.js -└── ./vite.config.js -The default Vue project file list is: -. -├── ./index.html -├── ./package.json -├── ./pnpm-lock.yaml -├── ./postcss.config.js -├── ./public -│ └── ./public/vite.svg -├── ./README.md -├── ./src -│ ├── ./src/App.vue -│ ├── ./src/main.js -│ └── ./src/style.css -├── ./tailwind.config.js -└── ./vite.config.js - -If no technical stack is specified, use the Vite, React, MUI and Tailwind CSS as the default stack. -Succinctly designate the correct entry file for your project based on the programming language. If the project is React or Vue, refer to the file structure provided in the template. -""" FILE_LIST = ActionNode( key="File list", expected_type=List[str], - instruction=FILE_LIST_INSTRUCTION, - example=["a.js", "b.py", "c.css", "d.html", "e.vue", "f.jsx"], + instruction="Only need relative paths. Succinctly designate the correct entry file for your project based on the programming language: use main.js for JavaScript, main.py for Python, and so on for other languages.", + example=["a.js", "b.py", "c.css", "d.html"], ) REFINED_FILE_LIST = ActionNode( diff --git a/metagpt/actions/write_prd_an.py b/metagpt/actions/write_prd_an.py index d923e8cd0..574dcef89 100644 --- a/metagpt/actions/write_prd_an.py +++ b/metagpt/actions/write_prd_an.py @@ -19,8 +19,8 @@ LANGUAGE = ActionNode( PROGRAMMING_LANGUAGE = ActionNode( key="Programming Language", expected_type=str, - instruction="Mainstream programming language. If not specified in the requirements, technical stack Vite, React and Tailwind CSS", - example="Vite, React and Tailwind CSS", + instruction="Mainstream programming language. If not specified in the requirements, use native HTML", + example="native HTML", ) ORIGINAL_REQUIREMENTS = ActionNode( diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 0724ffdea..9b0163710 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -1,88 +1,63 @@ -from __future__ import annotations +from metagpt.prompts.di.role_zero import THOUGHT_GUIDANCE -from typing import Annotated - -from pydantic import Field - -from metagpt.actions.di.run_command import RunCommand -from metagpt.const import TEAMLEADER_NAME -from metagpt.prompts.di.team_leader import ( - FINISH_CURRENT_TASK_CMD, - TL_INFO, - TL_INSTRUCTION, - TL_THOUGHT_GUIDANCE, +TL_INSTRUCTION = """ +You are a team leader, and you are responsible for drafting tasks and routing tasks to your team members. +Your team member: +{team_info} +You should NOT assign consecutive tasks to the same team member, instead, assign an aggregated task (or the complete requirement) and let the team member to decompose it. +When drafting and routing tasks, ALWAYS include necessary or important info inside the instruction, such as path, link, environment to team members, because you are their sole info source. +Each time you do something, reply to human letting them know what you did. +When creating a new plan involving multiple members, create all tasks at once. +If plan is created, you should track the progress based on team member feedback message, and update plan accordingly, such as Plan.finish_current_task, Plan.reset_task, Plan.replace_task, etc. +You should use TeamLeader.publish_team_message to team members, asking them to start their task. DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. +Pay close attention to new user message, review the conversation history, use RoleZero.reply_to_human to respond to the user directly, DON'T ask your team members. +Pay close attention to messages from team members. If a team member has finished a task, do not ask them to repeat it; instead, mark the current task as completed. +Note: +1. If the requirement is a pure DATA-RELATED requirement, such as web browsing, web scraping, web searching, web imitation, data science, data analysis, machine learning, deep learning, text-to-image etc. DON'T decompose it, assign a single task with the original user requirement as instruction directly to Data Analyst. +2. If the requirement is developing a software, game, app, or website, excluding the above data-related tasks, you should decompose the requirement into multiple tasks and assign them to different team members based on their expertise. The standard software development process has four steps: creating a Product Requirement Document (PRD) by the Product Manager -> writing a System Design by the Architect -> creating tasks by the Project Manager -> and coding by the Engineer. You may choose to execute any of these steps. When publishing message to Product Manager, you should directly copy the full original user requirement. +2.1. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly. +2.2. For software development requirement, estimate the complexity of the requirement before assignment, following the common industry practice of t-shirt sizing: + - XS: snake game, static personal homepage, basic calculator app + - S: Basic photo gallery, basic file upload system, basic feedback form + - M: Offline menu ordering system, news aggregator app + - L: Online booking system, inventory management system + - XL: Social media platform, e-commerce app, real-time multiplayer game + - For XS and S requirements, you don't need the standard software development process, you may directly ask Engineer to write the code. Otherwise, estimate if any part of the standard software development process may contribute to a better final code. If so, assign team members accordingly. +3.1 If the task involves code review (CR) or code checking, you should assign it to Engineer. +4. If the requirement is a common-sense, logical, or math problem, you should respond directly without assigning any task to team members. +5. If you think the requirement is not clear or ambiguous, you should ask the user for clarification immediately. Assign tasks only after all info is clear. +6. It is helpful for Engineer to have both the system design and the project schedule for writing the code, so include paths of both files (if available) and remind Engineer to definitely read them when publishing message to Engineer. +7. If the requirement is writing a TRD and software framework, you should assign it to Architect. When publishing message to Architect, you should directly copy the full original user requirement. +8. If the receiver message reads 'from {{team member}} to {{\'\'}}, it indicates that someone has completed the current task. Note this in your thoughts. +9. Do not use the 'end' command when the current task remains unfinished; instead, use the 'finish_current_task' command to indicate completion before switching to the next task. +10. Do not use escape characters in json data, particularly within file paths. +11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements. +12. If the the user message is a question, use 'reply to human' to respond to the question, and then end. +13. Instructions and reply must be in the same language. +14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. +15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. +16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. +""" +TL_THOUGHT_GUIDANCE = ( + THOUGHT_GUIDANCE + + """ +Sixth, describe the requirements as they pertain to software development, data analysis, or other areas. If the requirements is a software development and no specific restrictions are mentioned, you must create a Product Requirements Document (PRD), write a System Design document, develop a project schedule, and then begin coding. List the steps you will undertake. Plan these steps in a single response. +Seventh, describe the technologies you must use. +""" ) -from metagpt.roles.di.role_zero import RoleZero -from metagpt.schema import AIMessage, Message, UserMessage -from metagpt.strategy.experience_retriever import ExpRetriever, SimpleExpRetriever -from metagpt.tools.tool_registry import register_tool +TL_INFO = """ +{role_info} +Your team member: +{team_info} +""" - -@register_tool(include_functions=["publish_team_message"]) -class TeamLeader(RoleZero): - name: str = TEAMLEADER_NAME - profile: str = "Team Leader" - goal: str = "Manage a team to assist users" - thought_guidance: str = TL_THOUGHT_GUIDANCE - # TeamLeader only reacts once each time, but may encounter errors or need to ask human, thus allowing 2 more turns - max_react_loop: int = 3 - - tools: list[str] = ["Plan", "RoleZero", "TeamLeader"] - - experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() - - use_summary: bool = False - - def _update_tool_execution(self): - self.tool_execution_map.update( - { - "TeamLeader.publish_team_message": self.publish_team_message, - "TeamLeader.publish_message": self.publish_team_message, # alias - } - ) - - def _get_team_info(self) -> str: - if not self.rc.env: - return "" - team_info = "" - for role in self.rc.env.roles.values(): - # if role.profile == "Team Leader": - # continue - team_info += f"{role.name}: {role.profile}, {role.goal}\n" - return team_info - - def _get_prefix(self) -> str: - role_info = super()._get_prefix() - team_info = self._get_team_info() - return TL_INFO.format(role_info=role_info, team_info=team_info) - - async def _think(self) -> bool: - self.instruction = TL_INSTRUCTION.format(team_info=self._get_team_info()) - return await super()._think() - - def publish_message(self, msg: Message, send_to="no one"): - """Overwrite Role.publish_message, send to no one if called within Role.run, send to the specified role if called dynamically.""" - if not msg: - return - if not self.rc.env: - # If env does not exist, do not publish the message - return - msg.send_to = send_to - self.rc.env.publish_message(msg, publicer=self.profile) - - def publish_team_message(self, content: str, send_to: str): - """ - Publish a message to a team member, use member name to fill send_to args. You may copy the full original content or add additional information from upstream. This will make team members start their work. - DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. - """ - self._set_state(-1) # each time publishing a message, pause to wait for the response - if send_to == self.name: - return # Avoid sending message to self - # Specify the outer send_to to overwrite the default "no one" value. Use UserMessage because message from self is like a user request for others. - self.publish_message( - UserMessage(content=content, sent_from=self.name, send_to=send_to, cause_by=RunCommand), send_to=send_to - ) - - def finish_current_task(self): - self.planner.plan.finish_current_task() - self.rc.memory.add(AIMessage(content=FINISH_CURRENT_TASK_CMD)) +FINISH_CURRENT_TASK_CMD = """ +```json +[ + { + "command_name": "Plan.finish_current_task", + "args": {{}} + } +] +``` +""" diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index b35fc73af..da66f4796 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -479,7 +479,7 @@ Explanation: The requirement is about software development. Assign each tasks to "args": { "task_id": "1", "dependent_task_ids": [], - "instruction": "Use Vite, React and Tailwind CSS for the program. And create a product requirement document (PRD) outlining the features, user interface. ", + "instruction": "Use native HTML for the program. And create a product requirement document (PRD) outlining the features, user interface. ", "assignee": "Alice" } }, @@ -488,7 +488,7 @@ Explanation: The requirement is about software development. Assign each tasks to "args": { "task_id": "2", "dependent_task_ids": ["1"], - "instruction": "Use Vite, React and Tailwind CSS for the program. Design the software architecture for the CLI snake game, including the data flow.", + "instruction": "Use native HTML for the program. Design the software architecture for the CLI snake game, including the data flow.", "assignee": "Bob" } }, From 44bfe1a9b81adcbc50349d75657b47a67dc026e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 11 Oct 2024 11:10:34 +0800 Subject: [PATCH 3/4] fix typo --- metagpt/actions/design_api_an.py | 2 +- metagpt/prompts/di/engineer2.py | 2 - metagpt/prompts/di/team_leader.py | 2 +- metagpt/roles/di/team_leader.py | 145 +++++++++++++++++------------- 4 files changed, 87 insertions(+), 64 deletions(-) diff --git a/metagpt/actions/design_api_an.py b/metagpt/actions/design_api_an.py index 165f2b57b..0de17f32c 100644 --- a/metagpt/actions/design_api_an.py +++ b/metagpt/actions/design_api_an.py @@ -13,7 +13,7 @@ from metagpt.utils.mermaid import MMC1, MMC2 IMPLEMENTATION_APPROACH = ActionNode( key="Implementation approach", expected_type=str, - instruction="Analyze the difficult points of the requirements, select the appropriate open-source framework. The default technical stack is Vite, React and Tailwind CSS.", + instruction="Analyze the difficult points of the requirements, select the appropriate open-source framework.", example="We will ...", ) diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 2a1725da9..96392e334 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -69,7 +69,6 @@ Note: 11.1 Do not use Editor.insert_content_at_line or Editor.edit_file_by_replace more than once per command list. 12. If you choose Editor.insert_content_at_line, you must ensure that there is no duplication between the inserted content and the original code. If there is overlap between the new code and the original code, use Editor.edit_file_by_replace instead. 13. If you choose Editor.edit_file_by_replace, the original code that needs to be replaced must start at the beginning of the line and end at the end of the line - 14. When not specified, you should write files in a folder named "{{project_name}}". The project name is the name of the project which meets the user's requirements. 15. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file. 16. When planning, initially list the files for coding, then outline all coding tasks based on the file organization in your first response. @@ -80,7 +79,6 @@ Note: 21. When planning, consider whether images are needed. If you are developing a showcase website, start by using ImageGetter.get_image to obtain the necessary images. 22. When planning, merge multiple tasks that operate on the same file into a single task. For example, create one task for writing unit tests for all functions in a class. Also in using the editor, merge multiple tasks that operate on the same file into a single task. 23. When create unit tests for a code file, use Editor.read() to read the code file before planing. And create one plan to writing the unit test for the whole file. - 24. Follow the Sytem Design and Project Schedule if exists. Otherwise, use default template folder of Vite, React, MUI and Tailwind CSS. If the template does not exist, use native HTML. 25. When writing Vue/React project: The Vue template is in the {vue_template_path}, the React template is in the {react_template_path}. 25.1. Create the project folder first. Use cmd " mkdir -p {{project_name}} " diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 578b3432d..86e91b8a8 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -34,7 +34,7 @@ Note: 11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements. 12. If the the user message is a question, use 'reply to human' to respond to the question, and then end. 13. Instructions and reply must be in the same language. -14. The default type of software development is web, and the default programming tech stack is Vite, React, MUI and Tailwind CSS. +14. 14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. 15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. 16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. """ diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 9b0163710..0724ffdea 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -1,63 +1,88 @@ -from metagpt.prompts.di.role_zero import THOUGHT_GUIDANCE +from __future__ import annotations -TL_INSTRUCTION = """ -You are a team leader, and you are responsible for drafting tasks and routing tasks to your team members. -Your team member: -{team_info} -You should NOT assign consecutive tasks to the same team member, instead, assign an aggregated task (or the complete requirement) and let the team member to decompose it. -When drafting and routing tasks, ALWAYS include necessary or important info inside the instruction, such as path, link, environment to team members, because you are their sole info source. -Each time you do something, reply to human letting them know what you did. -When creating a new plan involving multiple members, create all tasks at once. -If plan is created, you should track the progress based on team member feedback message, and update plan accordingly, such as Plan.finish_current_task, Plan.reset_task, Plan.replace_task, etc. -You should use TeamLeader.publish_team_message to team members, asking them to start their task. DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. -Pay close attention to new user message, review the conversation history, use RoleZero.reply_to_human to respond to the user directly, DON'T ask your team members. -Pay close attention to messages from team members. If a team member has finished a task, do not ask them to repeat it; instead, mark the current task as completed. -Note: -1. If the requirement is a pure DATA-RELATED requirement, such as web browsing, web scraping, web searching, web imitation, data science, data analysis, machine learning, deep learning, text-to-image etc. DON'T decompose it, assign a single task with the original user requirement as instruction directly to Data Analyst. -2. If the requirement is developing a software, game, app, or website, excluding the above data-related tasks, you should decompose the requirement into multiple tasks and assign them to different team members based on their expertise. The standard software development process has four steps: creating a Product Requirement Document (PRD) by the Product Manager -> writing a System Design by the Architect -> creating tasks by the Project Manager -> and coding by the Engineer. You may choose to execute any of these steps. When publishing message to Product Manager, you should directly copy the full original user requirement. -2.1. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly. -2.2. For software development requirement, estimate the complexity of the requirement before assignment, following the common industry practice of t-shirt sizing: - - XS: snake game, static personal homepage, basic calculator app - - S: Basic photo gallery, basic file upload system, basic feedback form - - M: Offline menu ordering system, news aggregator app - - L: Online booking system, inventory management system - - XL: Social media platform, e-commerce app, real-time multiplayer game - - For XS and S requirements, you don't need the standard software development process, you may directly ask Engineer to write the code. Otherwise, estimate if any part of the standard software development process may contribute to a better final code. If so, assign team members accordingly. -3.1 If the task involves code review (CR) or code checking, you should assign it to Engineer. -4. If the requirement is a common-sense, logical, or math problem, you should respond directly without assigning any task to team members. -5. If you think the requirement is not clear or ambiguous, you should ask the user for clarification immediately. Assign tasks only after all info is clear. -6. It is helpful for Engineer to have both the system design and the project schedule for writing the code, so include paths of both files (if available) and remind Engineer to definitely read them when publishing message to Engineer. -7. If the requirement is writing a TRD and software framework, you should assign it to Architect. When publishing message to Architect, you should directly copy the full original user requirement. -8. If the receiver message reads 'from {{team member}} to {{\'\'}}, it indicates that someone has completed the current task. Note this in your thoughts. -9. Do not use the 'end' command when the current task remains unfinished; instead, use the 'finish_current_task' command to indicate completion before switching to the next task. -10. Do not use escape characters in json data, particularly within file paths. -11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements. -12. If the the user message is a question, use 'reply to human' to respond to the question, and then end. -13. Instructions and reply must be in the same language. -14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. -15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. -16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. -""" -TL_THOUGHT_GUIDANCE = ( - THOUGHT_GUIDANCE - + """ -Sixth, describe the requirements as they pertain to software development, data analysis, or other areas. If the requirements is a software development and no specific restrictions are mentioned, you must create a Product Requirements Document (PRD), write a System Design document, develop a project schedule, and then begin coding. List the steps you will undertake. Plan these steps in a single response. -Seventh, describe the technologies you must use. -""" +from typing import Annotated + +from pydantic import Field + +from metagpt.actions.di.run_command import RunCommand +from metagpt.const import TEAMLEADER_NAME +from metagpt.prompts.di.team_leader import ( + FINISH_CURRENT_TASK_CMD, + TL_INFO, + TL_INSTRUCTION, + TL_THOUGHT_GUIDANCE, ) -TL_INFO = """ -{role_info} -Your team member: -{team_info} -""" +from metagpt.roles.di.role_zero import RoleZero +from metagpt.schema import AIMessage, Message, UserMessage +from metagpt.strategy.experience_retriever import ExpRetriever, SimpleExpRetriever +from metagpt.tools.tool_registry import register_tool -FINISH_CURRENT_TASK_CMD = """ -```json -[ - { - "command_name": "Plan.finish_current_task", - "args": {{}} - } -] -``` -""" + +@register_tool(include_functions=["publish_team_message"]) +class TeamLeader(RoleZero): + name: str = TEAMLEADER_NAME + profile: str = "Team Leader" + goal: str = "Manage a team to assist users" + thought_guidance: str = TL_THOUGHT_GUIDANCE + # TeamLeader only reacts once each time, but may encounter errors or need to ask human, thus allowing 2 more turns + max_react_loop: int = 3 + + tools: list[str] = ["Plan", "RoleZero", "TeamLeader"] + + experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() + + use_summary: bool = False + + def _update_tool_execution(self): + self.tool_execution_map.update( + { + "TeamLeader.publish_team_message": self.publish_team_message, + "TeamLeader.publish_message": self.publish_team_message, # alias + } + ) + + def _get_team_info(self) -> str: + if not self.rc.env: + return "" + team_info = "" + for role in self.rc.env.roles.values(): + # if role.profile == "Team Leader": + # continue + team_info += f"{role.name}: {role.profile}, {role.goal}\n" + return team_info + + def _get_prefix(self) -> str: + role_info = super()._get_prefix() + team_info = self._get_team_info() + return TL_INFO.format(role_info=role_info, team_info=team_info) + + async def _think(self) -> bool: + self.instruction = TL_INSTRUCTION.format(team_info=self._get_team_info()) + return await super()._think() + + def publish_message(self, msg: Message, send_to="no one"): + """Overwrite Role.publish_message, send to no one if called within Role.run, send to the specified role if called dynamically.""" + if not msg: + return + if not self.rc.env: + # If env does not exist, do not publish the message + return + msg.send_to = send_to + self.rc.env.publish_message(msg, publicer=self.profile) + + def publish_team_message(self, content: str, send_to: str): + """ + Publish a message to a team member, use member name to fill send_to args. You may copy the full original content or add additional information from upstream. This will make team members start their work. + DONT omit any necessary info such as path, link, environment, programming language, framework, requirement, constraint from original content to team members because you are their sole info source. + """ + self._set_state(-1) # each time publishing a message, pause to wait for the response + if send_to == self.name: + return # Avoid sending message to self + # Specify the outer send_to to overwrite the default "no one" value. Use UserMessage because message from self is like a user request for others. + self.publish_message( + UserMessage(content=content, sent_from=self.name, send_to=send_to, cause_by=RunCommand), send_to=send_to + ) + + def finish_current_task(self): + self.planner.plan.finish_current_task() + self.rc.memory.add(AIMessage(content=FINISH_CURRENT_TASK_CMD)) From 67e491d97abfaa127f1484f810e11022fdc92eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Fri, 11 Oct 2024 11:15:36 +0800 Subject: [PATCH 4/4] fix typo --- metagpt/prompts/di/team_leader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index 86e91b8a8..9b0163710 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -34,7 +34,7 @@ Note: 11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements. 12. If the the user message is a question, use 'reply to human' to respond to the question, and then end. 13. Instructions and reply must be in the same language. -14. 14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. +14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software. 15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language. 16. Data collection and web/software development are two separate tasks. You must assign these tasks to data analysts and engineers, respectively. Wait for the data collection to be completed before starting the coding. """