diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index f85128b38..2294fa731 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -220,20 +220,19 @@ However, you MUST respond to the user message by yourself directly, DON'T ask yo """ REPORT_TO_HUMAN_PROMPT = """ -# Current Plan -{plan_status} +# Restrictions +{requirements_constraints} -Your have just finish a task, Use "RoleZero.reply_to_human" to report what you have done. -The output format is : -```json -[ - {{ - "command_name": "RoleZero.reply_to_human", - "args": {{ - "content": "" - }} - }} -] -``` +Your have just finish all tasks, Use "RoleZero.reply_to_human" to answer the user requirements. +Report to human what you have done. Do Not ouput any other format. +Your reply is: """ +SUMMARY_PROMPY = """ +# Restrictions +{requirements_constraints} + +You have just completed some tasks. +Summarize the tasks you have accomplished without including detailed information. +If there are any deliverables, list their descriptions and provide their file paths. +""" diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 727360ae9..48967d7ef 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -28,6 +28,7 @@ from metagpt.prompts.di.role_zero import ( REGENERATE_PROMPT, REPORT_TO_HUMAN_PROMPT, ROLE_INSTRUCTION, + SUMMARY_PROMPY, SYSTEM_PROMPT, THOUGHT_GUIDANCE, ) @@ -68,6 +69,8 @@ class RoleZero(Role): react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode + # Summary Mode + use_summary: bool = True # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None @@ -87,8 +90,6 @@ class RoleZero(Role): use_fixed_sop: bool = False requirements_constraints: str = "" # the constraints in user requirements - command_history: list[str] = [] - @model_validator(mode="after") def set_plan_and_tool(self) -> "RoleZero": # We force using this parameter for DataAnalyst @@ -247,26 +248,24 @@ class RoleZero(Role): logger.info(f"Commands outputs: \n{outputs}") self.rc.memory.add(UserMessage(content=outputs)) - # Report what is done when finishing the task. - current_command_list = [command["command_name"] for command in commands] - self.command_history.extend(current_command_list) - if self.check_whether_report_to_human(): - memory = self.rc.memory.get(self.memory_k) - memory = await self.parse_browser_actions(memory) - memory = self.parse_images(memory) - plan_status, _ = self._get_plan_status() - prompt = REPORT_TO_HUMAN_PROMPT.format(plan_status=plan_status) - req = self.llm.format_msg(memory + [UserMessage(content=prompt)]) - respond_command = await self.llm.aask(msg=req) - commands, ok = await self._parse_commands(respond_command) - if ok and len(commands) == 1 and commands[0]["command_name"] == "RoleZero.reply_to_human": - cmd = commands[0] - report_result = await self.reply_to_human(cmd["args"]) - self.rc.memory.add(AIMessage(content=respond_command)) - self.rc.memory.add(UserMessage(content=report_result)) - self.command_history.append("RoleZero.reply_to_human") - logger.info(f"Commands outputs: \n{report_result}") - outputs += report_result + if any(["end" == command["command_name"] for command in commands]): + # Ensure reply to the human before the "end" command is executed. + if all(["reply_to_human" not in memory.content for memory in self.get_memories(k=5)]): + memory = self.rc.memory.get(self.memory_k) + reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format( + requirements_constraints=self.requirements_constraints, + ) + reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)])) + await self.reply_to_human(content=reply_content) + self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand)) + + # Summary of the Completed Task and Deliverables + if self.use_summary: + memory = self.rc.memory.get(self.memory_k) + summary_prompt = SUMMARY_PROMPY.format( + requirements_constraints=self.requirements_constraints, + ) + outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(summary_prompt)])) return AIMessage( content=f"I have finished the task, please mark my task as finished. Outputs: {outputs}", @@ -274,20 +273,6 @@ class RoleZero(Role): cause_by=RunCommand, ) - def check_whether_report_to_human(self): - """ "Check whether add reply to human command when finish current task""" - interaction_with_human = ["RoleZero.ask_human", "RoleZero.reply_to_human"] - plan_end_action = ["Plan.finish_current_task", "end"] - flag = 0 - for command_name in self.command_history[::-1]: - if command_name in plan_end_action: - flag |= 1 - elif command_name in interaction_with_human: - flag |= 2 - else: - break - return flag == 1 - async def _react(self) -> Message: # NOTE: Diff 1: Each time landing here means news is observed, set todo to allow news processing in _think self._set_state(0) diff --git a/metagpt/roles/di/swe_agent.py b/metagpt/roles/di/swe_agent.py index e90fc3045..d54c9dd44 100644 --- a/metagpt/roles/di/swe_agent.py +++ b/metagpt/roles/di/swe_agent.py @@ -69,11 +69,10 @@ class SWEAgent(RoleZero): This function is specifically added for SWE bench evaluation. """ - # only import when evaluation is needed + # If todo switches to None, it indicates that this is the final round of reactions, and the Swe-Agent will stop. Use git diff to store any changes made. if not self.rc.todo: from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch - # swe agent have been stop. it means 'end' or other command which can stop the swe agent have been executed try: diff_output = await self.terminal.run("git diff --cached") clear_diff = extract_patch(diff_output) diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 97100f295..112ca5a84 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -30,6 +30,8 @@ class TeamLeader(RoleZero): experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever() + use_summary: bool = False + def _update_tool_execution(self): self.tool_execution_map.update( {