From 59d8d8b1936050b248b96522ea2605da19186981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BC=9F=E9=9F=AC?= Date: Tue, 23 Jul 2024 17:49:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3TL=20=E7=AD=89=E5=BE=85?= =?UTF-8?q?=E6=88=90=E5=91=98=E5=AE=8C=E6=88=90=E4=BB=BB=E5=8A=A1=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E7=BB=93=E6=9D=9F=E4=BB=BB=E5=8A=A1=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/actions/design_api.py | 2 +- metagpt/actions/design_api_an.py | 4 ++-- metagpt/actions/project_management_an.py | 3 ++- metagpt/actions/write_prd.py | 2 +- metagpt/prompts/di/engineer2.py | 6 +++++- metagpt/prompts/di/role_zero.py | 13 +++++++++++- metagpt/prompts/di/team_leader.py | 6 +++++- metagpt/roles/architect.py | 2 +- metagpt/roles/di/role_zero.py | 8 ++++---- metagpt/strategy/experience_retriever.py | 25 +++++++++++++++++++++++- metagpt/tools/libs/editor.py | 3 ++- 11 files changed, 59 insertions(+), 15 deletions(-) diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index 62b0d435d..9bbb3c0a7 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -279,4 +279,4 @@ class WriteDesign(Action): md_output_filename = output_pathname.with_suffix(".md") await save_json_to_markdown(content=design.content, output_filename=md_output_filename) await reporter.async_report(md_output_filename, "path") - return f'System Design filename: "{str(output_pathname)}"' + return f'System Design filename: "{str(output_pathname)}". \n The "System Design" task has been completed and is now marked as finished.' diff --git a/metagpt/actions/design_api_an.py b/metagpt/actions/design_api_an.py index 5977cbd95..2541d0469 100644 --- a/metagpt/actions/design_api_an.py +++ b/metagpt/actions/design_api_an.py @@ -33,8 +33,8 @@ PROJECT_NAME = ActionNode( FILE_LIST = ActionNode( key="File list", expected_type=List[str], - instruction="Only need relative paths. ALWAYS write a main.py or app.py here", - example=["main.py", "game.py"], + 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/project_management_an.py b/metagpt/actions/project_management_an.py index 0417c0ce4..01b92b7fc 100644 --- a/metagpt/actions/project_management_an.py +++ b/metagpt/actions/project_management_an.py @@ -27,7 +27,8 @@ LOGIC_ANALYSIS = ActionNode( key="Logic Analysis", expected_type=List[List[str]], instruction="Provide a list of files with the classes/methods/functions to be implemented, " - "including dependency analysis and imports.", + "including dependency analysis and imports." + "Ensure consistency between System Design and Logic Analysis; the files must match exactly.", example=[ ["game.py", "Contains Game class and ... functions"], ["main.py", "Contains main function, from game import Game"], diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index 42154837a..caa814a81 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -323,4 +323,4 @@ class WritePRD(Action): md_output_filename = output_pathname.with_suffix(".md") await save_json_to_markdown(content=new_prd.content, output_filename=md_output_filename) await reporter.async_report(md_output_filename, "path") - return f'PRD filename: "{str(output_pathname)}"' + return f'PRD filename: "{str(output_pathname)}". The "Create the PRD" task has been completed and is now marked as finished.' diff --git a/metagpt/prompts/di/engineer2.py b/metagpt/prompts/di/engineer2.py index c4dec8d5e..d81ef5691 100644 --- a/metagpt/prompts/di/engineer2.py +++ b/metagpt/prompts/di/engineer2.py @@ -11,7 +11,11 @@ EXTRA_INSTRUCTION = """ 11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. 12. To modify code in a file, read the entire file, make changes, and update the file with the complete code, ensuring that no line numbers are included in the final write. 13. When a system design or project schedule is provided, at the end of the plan, add a CodeRview Task for each file; for example, if there are three files, add three CodeRview Tasks. For each CodeRview Task, just call ReviewAndRewriteCode.run. -14. When you are making plan.it is hightly recommand to plan all the coding plan and reviews plan in first response. +14. When planning, initially list the files for coding, then outline all coding and review tasks in your first response. +15. Note 'Task for {file_name} completed.' — signifies the {file_name} coding task is done. +16. Avoid re-reviewing or re-coding the same code. When you decide to take a write or review action, include the command 'finish current task' in the same response. +17. When coding JavaScript, avoid using '\'' in strings. +18. If you plan to read a file, do not include other plans in the same response. """ diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 67c1e84fb..26b06534c 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -12,6 +12,9 @@ Note: """ # To ensure compatibility with hard-coded experience, do not add any other content between "# Example" and "# Available Commands". CMD_PROMPT = """ +# observation +{observation} + # Data Structure class Task(BaseModel): task_id: str = "" @@ -42,10 +45,15 @@ Special Command: Use {{"command_name": "end"}} to do nothing or indicate complet Pay close attention to the Example provided, you can reuse the example for your current situation if it fits. You may use any of the available commands to create a plan or update the plan. You may output mutiple commands, they will be executed sequentially. If you finish current task, you will automatically take the next task in the existing plan, use Plan.finish_task, DON'T append a new task. -Pay close attention to what you have done. Be different with your previous action. +Review the latest plan's outcome, focusing on achievements. If your completed task matches the current, consider it finished. +In your response, include at least one command. # Your commands in a json array, in the following output format with correct command_name and args. If there is nothing to do, use the pass or end command: Some text indicating your thoughts before JSON is required, such as what tasks have been completed, what tasks are next, how you should update the plan status, respond to inquiry, or seek for help. Then a json array of commands. You must output ONE and ONLY ONE json array. DON'T output multiple json arrays with thoughts between them. +Firstly, describe in natural language the actions you have taken recently. +Secondly, describe in natural language the messages you have received recently, with a particular emphasis on messages from users. +Thirdly, describe your current task in natural language. Review the histroy, if you find that the current task is identical to a previously completed one, it indicates that the current task has already been accomplished. +Then, articulate your thoughts and list the commands, adhering closely to the instructions provided. ```json [ {{ @@ -67,7 +75,10 @@ JSON_REPAIR_PROMPT = """ ```json ``` +Do not use escape characters in json_data, particularly within file paths. Help check if there are any formatting issues with the JSON data? If so, please help format it. +If no issues are detected, the original json data should be returned unchanged. +Output the JSON data in a format that can be loaded by the json.loads() function. """ QUICK_THINK_PROMPT = """ diff --git a/metagpt/prompts/di/team_leader.py b/metagpt/prompts/di/team_leader.py index f909524d6..b6611f55d 100644 --- a/metagpt/prompts/di/team_leader.py +++ b/metagpt/prompts/di/team_leader.py @@ -13,7 +13,7 @@ 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 bug fixes, issue reporting, environment setup, terminal operations, pip install, 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, usually the sequence of Product Manager -> Architect -> Project Manager -> Engineer -> (optional: QaEngine if present) -> (optional: DataAnalyst if user requests deployment), each assigned ONE task. When publishing message to Product Manager, you should directly copy the full original user requirement. @@ -22,6 +22,10 @@ Note: 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. For exmaple,'from Alice(Product Manager) to {{\'\'}} means PDR is created,'from Bob(Architecture) to {{\'\'}} means software architecture is created. 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. If you have made a plan, simply follow it without creating a new one. +11. Do not use escape characters in json data, particularly within file paths. """ QUICK_THINK_SYSTEM_PROMPT = """ diff --git a/metagpt/roles/architect.py b/metagpt/roles/architect.py index f640d4a87..48b078dc6 100644 --- a/metagpt/roles/architect.py +++ b/metagpt/roles/architect.py @@ -31,7 +31,7 @@ class Architect(RoleZero): "libraries. Use same language as user requirement" ) - instruction: str = """Use WriteDesign tool to write a system design document if a system design is required; Use `write_trd_and_framework` tool to write a software framework if a software framework is required;""" + instruction: str = """Do not output the document directly. Use WriteDesign tool to write a system design document if a system design is required; Use `write_trd_and_framework` tool to write a software framework if a software framework is required;""" max_react_loop: int = 1 # FIXME: Read and edit files requires more steps, consider later tools: list[str] = [ "Editor:write,read,write_content", diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 9355ef8ab..04a66f761 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -146,6 +146,8 @@ class RoleZero(Role): tool_info = json.dumps({tool.name: tool.schemas for tool in tools}) ### Make Decision Dynamically ### + + memory = self.rc.memory.get(self.memory_k) instruction = self.instruction.strip() prompt = self.cmd_prompt.format( example=example, @@ -154,10 +156,9 @@ class RoleZero(Role): plan_status=plan_status, current_task=current_task, instruction=instruction, + observation=memory[-1].content, ) - memory = self.rc.memory.get(self.memory_k) memory = await self.parse_browser_actions(memory) - req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) async with ThoughtReporter(enable_llm_stream=True) as reporter: await reporter.async_report({"type": "react"}) @@ -169,7 +170,6 @@ class RoleZero(Role): self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=self.system_msg, state_data=state_data) self.rc.memory.add(AIMessage(content=self.command_rsp)) - return True @exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer()) @@ -340,7 +340,7 @@ class RoleZero(Role): elif cmd["command_name"] == "end": self._set_state(-1) - command_output = "Everything Done" + command_output = " " return command_output diff --git a/metagpt/strategy/experience_retriever.py b/metagpt/strategy/experience_retriever.py index 0f805d44c..43a9677f8 100644 --- a/metagpt/strategy/experience_retriever.py +++ b/metagpt/strategy/experience_retriever.py @@ -611,7 +611,7 @@ Explanation: The user is asking for a general update on the project status. Give ] ``` -## example 4 +## example 5 OBSERVATION : current task is none and all task is finished. Explanation: Last task is "Plan.finish_current_task" or 'RoleZero.reply_to_human' and now the current task is none, it means everything is done.Just coutput command "end". ```json @@ -620,6 +620,29 @@ Explanation: Last task is "Plan.finish_current_task" or 'RoleZero.reply_to_human "command_name": "end" } ] + +## example 6 +OBSERVATION : The previously completed task is identical to the current task. +Explanation: The current task has been accomplished previously. +```json +[ + { + "command_name": "Plan.finish_current_task", + "args": {} + }, +] +``` + +## example 7 +OBSERVATION : the task assigned to Alice is still ongoing as it has not been marked as finished. The current task in the plan is for Alice to create the PRD. +Explanation: "I attempted to locate historical records containing 'send to []', and discovered an entry stating 'PRD is finished and masked.' This indicates that Alice's task has been completed. +```json +[ + { + "command_name": "Plan.finish_current_task", + "args": {} + }, +] ``` """ diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 24eca65fc..044618d79 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -36,9 +36,10 @@ class Editor: with open(path, "w", encoding="utf-8") as f: f.write(content) # self.resource.report(path, "path") + return f" The task of writing/coding the '{os.path.basename(path)}' file has been completed. The file '{os.path.basename(path)}' has been successfully created." def read(self, path: str) -> FileBlock: - """Read the whole content of a file. It is strongly advised to utilize absolute paths""" + """Read the whole content of a file. Using absolute paths as the argument for specifying the file location.""" with open(path, "r") as f: self.resource.report(path, "path") lines = f.readlines()