diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index 099e46177..0501b4d33 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -1,6 +1,6 @@ from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION -EXTRA_INSTRUCTION = """ +EXTRA_INSTRUCTION_DEPRECATED = """ 4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand. 5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response. 6. 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. @@ -18,5 +18,39 @@ EXTRA_INSTRUCTION = """ 18. If you plan to read a file, do not include other plans in the same response. """ +EXTRA_INSTRUCTION = """ +6. 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. +7. 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. +8. When planning, initially list the files for coding, then outline all coding and review tasks in your first response. +9. If you plan to read a file, do not include other plans in the same response. +10. Use Engineer2.write_new_code to create or modify a file. Write only one code file each time. +11. When the requirement is simple, you don't need to create a plan, just do it right away. +""" ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip() + +WRITE_CODE_SYSTEM_PROMPT = """ +You are a world-class engineer, your goal is to write google-style, elegant, modular, readable, maintainable, fully functional, and ready-for-production code. + +Pay attention to the conversation history and the following constraints: +1. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. +2. When modifying a code, rewrite the full code instead of updating or inserting a snippet. +3. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. +""" + +WRITE_CODE_PROMPT = """ +# User Requirement +{user_requirement} + +# Plan Status +{plan_status} + +# Further Instruction +{instruction} + +# 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: +``` +your code +``` +""" diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 7af5280e1..82f928679 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -2,13 +2,21 @@ from __future__ import annotations from pydantic import Field -from metagpt.actions.write_code_review import ValidateAndRewriteCode -from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION +# from metagpt.actions.write_code_review import ValidateAndRewriteCode +from metagpt.prompts.di.engineer2 import ( + ENGINEER2_INSTRUCTION, + WRITE_CODE_PROMPT, + WRITE_CODE_SYSTEM_PROMPT, +) from metagpt.roles.di.role_zero import RoleZero +from metagpt.schema import UserMessage from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE from metagpt.tools.libs.terminal import Terminal +from metagpt.tools.tool_registry import register_tool +from metagpt.utils.common import CodeParser, awrite +@register_tool(include_functions=["write_new_code"]) class Engineer2(RoleZero): name: str = "Alex" profile: str = "Engineer" @@ -17,16 +25,16 @@ class Engineer2(RoleZero): terminal: Terminal = Field(default_factory=Terminal, exclude=True) - tools: list[str] = ["Plan", "Editor:write,read", "RoleZero", "Terminal:run_command", "ValidateAndRewriteCode"] + tools: list[str] = ["Plan", "Editor:read", "RoleZero", "Terminal:run_command", "Engineer2"] def _update_tool_execution(self): - validate = ValidateAndRewriteCode() - + # validate = ValidateAndRewriteCode() self.tool_execution_map.update( { "Terminal.run_command": self.terminal.run_command, - "ValidateAndRewriteCode.run": validate.run, - "ValidateAndRewriteCode": validate.run, + "Engineer2.write_new_code": self.write_new_code, + # "ValidateAndRewriteCode.run": validate.run, + # "ValidateAndRewriteCode": validate.run, } ) @@ -42,3 +50,25 @@ class Engineer2(RoleZero): command_output += "All tasks are finished.\n" command_output += await super()._run_special_command(cmd) return command_output + + async def write_new_code(self, path: str, instruction: str = "") -> str: + """Write a new code file. + + Args: + path (str): The absolute path of the file to be created. + instruction (optional, str): Further hints or notice other than the current task instruction, must be very concise and can be empty. Defaults to "". + """ + plan_status, _ = self._get_plan_status() + prompt = WRITE_CODE_PROMPT.format( + user_requirement=self.planner.plan.goal, + plan_status=plan_status, + instruction=instruction, + ) + context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)]) + rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT]) + code = CodeParser.parse_code(text=rsp) + + await awrite(path, code) + + # TODO: Consider adding line no to be ready for editing. + return f"The file {path} has been successfully created, with content:\n{code}" diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index 52884296d..4b1c825c9 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -849,7 +849,6 @@ Here's the plan: 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 JavaScript code for the game logic and UI interactions. -4. **Code Review Tasks**: Review each file to ensure they meet the project requirements and adhere to the system design. Let's start by appending the first task to the plan. @@ -881,33 +880,6 @@ Let's start by appending the first task to the plan. "instruction": "Create the script.js file containing the JavaScript logic for the 2048 game.", "assignee": "Alex" } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "4", - "dependent_task_ids": ["1"], - "instruction": "Use ValidateAndRewriteCode to review the code in index.html to ensure it meets the design specifications.", - "assignee": "Alex" - } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "5", - "dependent_task_ids": ["2"], - "instruction": "Use ValidateAndRewriteCode to review the code in style.css to ensure it meets the design specifications.", - "assignee": "Alex" - } - }, - { - "command_name": "Plan.append_task", - "args": { - "task_id": "6", - "dependent_task_ids": ["3"], - "instruction": "Use ValidateAndRewriteCode to review the code in script.js to ensure it meets the design specifications. ", - "assignee": "Alex" - } } ] ``` @@ -918,32 +890,9 @@ Explanation: Take on one task, such as writing a file. Upon completion, finish c ```json [ { - "command_name": "Editor.write", + "command_name": "Engineer2.write_new_code", "args": { - "path": "/Users/gary/Files/temp/workspace/snake_game/src/index.html", - "content": "the code ..." - } - }, - { - "command_name": "Plan.finish_current_task", - "args": {{}} - } -] -``` - -## example 4 -I will now review the code in `script.js`. -Explanation: to review the code, call ValidateAndRewriteCode.run. - -```json -[ - { - "command_name": "ValidateAndRewriteCode.run", - "args": { - "code_path": "/tmp/src/script.js", - "system_design_input": "/tmp/docs/system_design.json", - "project_schedule_input": "/tmp/docs/project_schedule.json", - "code_validate_k_times": 2 + "path": "src/index.html" } }, {