From 24c5ebd5d6541686e045c3040b46946591a2ded3 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 10 Jul 2024 22:13:16 +0800 Subject: [PATCH] add quick think --- metagpt/prompts/di/role_zero.py | 8 ++++ metagpt/roles/di/role_zero.py | 37 +++++++++++++++++-- .../environment/mgx_env/run_mgx_env.py | 6 ++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/metagpt/prompts/di/role_zero.py b/metagpt/prompts/di/role_zero.py index 8f4a8804e..fd89f9d44 100644 --- a/metagpt/prompts/di/role_zero.py +++ b/metagpt/prompts/di/role_zero.py @@ -51,6 +51,7 @@ Some text indicating your thoughts, such as how you should update the plan statu ``` Notice: your output JSON data section must start with **```json [** """ + JSON_REPAIR_PROMPT = """ ## json data {json_data} @@ -61,3 +62,10 @@ Formatted JSON data ``` Help check if there are any formatting issues with the JSON data? If so, please help format it """ + +QUICK_THINK_PROMPT = """ +Decide if the latest user message is a quick question. +Quick questions include common-sense, logical, math questions, greetings, or casual chat that you can answer directly, excluding software development tasks. +Respond with "#YES#, (then start your actual response to the question...)" if so, otherwise, simply respond with "#NO#". +Your response: +""" diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index c6ed6f6c6..1c292fdde 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -8,12 +8,13 @@ from typing import Callable, Dict, List, Literal, Tuple from pydantic import model_validator -from metagpt.actions import Action +from metagpt.actions import Action, UserRequirement from metagpt.actions.di.run_command import RunCommand from metagpt.logs import logger from metagpt.prompts.di.role_zero import ( CMD_PROMPT, JSON_REPAIR_PROMPT, + QUICK_THINK_PROMPT, ROLE_INSTRUCTION, ) from metagpt.roles import Role @@ -24,7 +25,7 @@ from metagpt.tools.libs.browser import Browser from metagpt.tools.libs.editor import Editor from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender from metagpt.tools.tool_registry import register_tool -from metagpt.utils.common import CodeParser +from metagpt.utils.common import CodeParser, any_to_str from metagpt.utils.repair_llm_raw_output import RepairType, repair_llm_raw_output from metagpt.utils.report import ThoughtReporter @@ -47,7 +48,7 @@ class RoleZero(Role): # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools - tool_recommender: ToolRecommender = None + tool_recommender: ToolRecommender = ToolRecommender() tool_execution_map: dict[str, Callable] = {} special_tool_commands: list[str] = ["Plan.finish_current_task", "end"] # Equipped with three basic tools by default for optional use @@ -183,9 +184,14 @@ class RoleZero(Role): ) async def _react(self) -> Message: - # NOTE: Diff 1: Each time landing here means observing news, set todo to allow news processing in _think + # NOTE: Diff 1: Each time landing here means news is observed, set todo to allow news processing in _think self._set_state(0) + # problems solvable by quick thinking doesn't need to a formal think-act cycle + quick_rsp = await self._quick_think() + if quick_rsp: + return quick_rsp + actions_taken = 0 rsp = AIMessage(content="No actions taken yet", cause_by=Action) # will be overwritten after Role _act while actions_taken < self.rc.max_react_loop: @@ -202,6 +208,29 @@ class RoleZero(Role): actions_taken += 1 return rsp # return output from the last action + async def _quick_think(self) -> Message: + msg = self.rc.news[-1] + rsp_msg = None + if msg.cause_by != any_to_str(UserRequirement): + # Agents themselves won't generate quick questions, use this rule to reduce extra llm calls + return rsp_msg + + context = self.llm.format_msg(self.get_memories(k=4) + [UserMessage(content=QUICK_THINK_PROMPT)]) + rsp = await self.llm.aask(context) + + pattern = r"#YES#,? ?" + if re.search(pattern, rsp): + answer = re.sub(pattern, "", rsp).strip() + self.rc.memory.add(AIMessage(content=answer, cause_by=RunCommand)) + await self.reply_to_human(content=answer) + rsp_msg = AIMessage( + content="Complete run", + sent_from=self.name, + cause_by=RunCommand, + ) + + return rsp_msg + async def _parse_commands(self) -> Tuple[List[Dict], bool]: """Retrieves commands from the Large Language Model (LLM). diff --git a/tests/metagpt/environment/mgx_env/run_mgx_env.py b/tests/metagpt/environment/mgx_env/run_mgx_env.py index 93c134bb4..ecc2facca 100644 --- a/tests/metagpt/environment/mgx_env/run_mgx_env.py +++ b/tests/metagpt/environment/mgx_env/run_mgx_env.py @@ -129,9 +129,11 @@ TL_CHAT2 = """Solve the issue at this link""" # expecting clarification TL_CHAT3 = """Who is the first man landing on Moon""" # expecting answering directly TL_CHAT4 = """Find all zeros in the indicated finite field of the given polynomial with coefficients in that field. x^5 + 3x^3 + x^2 + 2x in Z_5""" # expecting answering directly TL_CHAT5 = """Find the degree for the given field extension Q(sqrt(2), sqrt(3), sqrt(18)) over Q.""" # expecting answering directly -TL_CHAT6 = """Statement 1 | A ring homomorphism is one to one if and only if the kernel is {{0}},. Statement 2 | Q is an ideal in R""" # expecting answering directly +TL_CHAT6 = """True or False? Statement 1 | A ring homomorphism is one to one if and only if the kernel is {{0}},. Statement 2 | Q is an ideal in R""" # expecting answering directly TL_CHAT7 = """Jean has 30 lollipops. Jean eats 2 of the lollipops. With the remaining lollipops, Jean wants to package 2 lollipops in one bag. How many bags can Jean fill?""" # expecting answering directly - +TL_CHAT9 = """What's your name?""" +TL_CHAT10 = "Hi" +TL_CHAT11 = "Tell me about your team" if __name__ == "__main__": # NOTE: Add access_token to test github issue fixing