diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index e04155fe8..aea00934c 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -119,7 +119,12 @@ END_COMMAND = """ ``` """ -SUMMARY_PROBLEM_WHEN_DUPLICATE = """Please directly tell me what is confusing or troubling you. Your response should be in {language} and within 30 words.""" +SUMMARY_PROBLEM_WHEN_DUPLICATE = """ +You has meet a problem and cause duplicate command. +Please directly tell me what is confusing or troubling you. +Do Not output any command. +Ouput you problem in {language} and within 30 words. +""" ASK_HUMAN_GUIDANCE_FORMAT = """ I am facing the following problem: {problem} diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 044ff0afd..6352a217f 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -107,11 +107,7 @@ class Engineer2(RoleZero): async def _run_special_command(self, cmd) -> str: """command requiring special check or parsing.""" # finish current task before end. - command_output = "" - if cmd["command_name"] == "end" and not self.planner.plan.is_plan_finished(): - self.planner.plan.finish_all_tasks() - command_output += "All tasks are finished.\n" - command_output += await super()._run_special_command(cmd) + command_output = await super()._run_special_command(cmd) return command_output async def write_new_code(self, path: str, file_description: str = "") -> str: @@ -154,3 +150,8 @@ class Engineer2(RoleZero): else: command_output = await self.terminal.run_command(cmd) return command_output + + async def _end(self): + if not self.planner.plan.is_plan_finished(): + self.planner.plan.finish_all_tasks() + return await super()._end() diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 8d6fbf7b2..0aadaac81 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -75,7 +75,7 @@ class RoleZero(Role): tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None tool_execution_map: Annotated[dict[str, Callable], Field(exclude=True)] = {} - special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run"] + special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run", "RoleZero.ask_human"] # List of exclusive tool commands. # If multiple instances of these commands appear, only the first occurrence will be retained. exclusive_tool_commands: list[str] = [ @@ -122,7 +122,7 @@ class RoleZero(Role): "Plan.append_task": self.planner.plan.append_task, "Plan.reset_task": self.planner.plan.reset_task, "Plan.replace_task": self.planner.plan.replace_task, - "RoleZero.ask_human": self.request_and_analyze_human_response, + "RoleZero.ask_human": self.ask_human, "RoleZero.reply_to_human": self.reply_to_human, "SearchEnhancedQA.run": SearchEnhancedQA().run, } @@ -229,7 +229,6 @@ class RoleZero(Role): await reporter.async_report({"type": "react"}) self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=[system_prompt], state_data=state_data) self.command_rsp = await self._check_duplicates(req, self.command_rsp) - return True @exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer()) @@ -403,7 +402,7 @@ class RoleZero(Role): problem = await self.llm.aask( req + [UserMessage(content=SUMMARY_PROBLEM_WHEN_DUPLICATE.format(language=self.respond_language))] ) - ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem) + ASK_HUMAN_COMMAND[0]["args"]["question"] = ASK_HUMAN_GUIDANCE_FORMAT.format(problem=problem).strip() ask_human_command = "```json\n" + json.dumps(ASK_HUMAN_COMMAND, indent=4, ensure_ascii=False) + "\n```" return ask_human_command # Try correction by self @@ -513,7 +512,15 @@ class RoleZero(Role): elif cmd["command_name"] == "end": command_output = await self._end() - + elif cmd["command_name"] == "RoleZero.ask_human": + human_response = await self.ask_human(**cmd["args"]) + if "" in human_response: + human_response += "The user has asked me to stop because I have encountered a problem." + self.rc.memory.add(UserMessage(content=human_response, cause_by=RunCommand)) + end_output = "\nCommand end executed:" + end_output += await self._end() + return end_output + return human_response # output from bash.run may be empty, add decorations to the output to ensure visibility. elif cmd["command_name"] == "Bash.run": tool_obj = self.tool_execution_map[cmd["command_name"]] @@ -564,15 +571,6 @@ class RoleZero(Role): return "Not in MGXEnv, command will not be executed." return await self.rc.env.ask_human(question, sent_from=self) - async def request_and_analyze_human_response(self, question: str) -> str: - """This fucntion will call ask_human and then analyze and decorate the human response.""" - human_response = await self.ask_human(question) - if "" in human_response: - human_response += "\nCommand end executed:" - human_response += await self._end() - return human_response - return human_response - async def reply_to_human(self, content: str) -> str: """Reply to human user with the content provided. Use this when you have a clear answer or solution to the user's question.""" # NOTE: Can be overwritten in remote setting diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index b56f7bf0e..4181383c8 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -550,9 +550,9 @@ def test_edit_file_by_replace(temp_py_file): MISMATCH_ERROR = """ -Error: The `first_replaced_replaced_line_number` does not match the `first_replaced_replaced_line_content`. Please correct the parameters. -The `first_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2". -But the `first_replaced_replaced_line_content ` is "". +Error: The `first_replaced_line_number` does not match the `first_replaced_line_content`. Please correct the parameters. +The `first_replaced_line_number` is 5 and the corresponding content is " b = 2". +But the `first_replaced_line_content ` is "". The content around the specified line is: The 002 line is "def test_function_for_fm():" The 003 line is " "some docstring"" @@ -561,9 +561,9 @@ The 005 line is " b = 2" The 006 line is " c = 3" The 007 line is " # this is the 7th line" Pay attention to the new content. Ensure that it aligns with the new parameters. -Error: The `last_replaced_replaced_line_number` does not match the `last_replaced_replaced_line_content`. Please correct the parameters. -The `last_replaced_replaced_line_number` is 5 and the corresponding content is " b = 2". -But the `last_replaced_replaced_line_content ` is "". +Error: The `last_replaced_line_number` does not match the `last_replaced_line_content`. Please correct the parameters. +The `last_replaced_line_number` is 5 and the corresponding content is " b = 2". +But the `last_replaced_line_content ` is "". The content around the specified line is: The 002 line is "def test_function_for_fm():" The 003 line is " "some docstring""