From ba448dbdadc339b27287b9db6d478d5f475ad0fc Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 3 Jan 2025 16:41:36 +0800 Subject: [PATCH 01/14] optimize the content format for the agent's memory access --- dbgpt/agent/core/role.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dbgpt/agent/core/role.py b/dbgpt/agent/core/role.py index 5f28538dc..ff5e812f2 100644 --- a/dbgpt/agent/core/role.py +++ b/dbgpt/agent/core/role.py @@ -233,12 +233,13 @@ async def write_memories( raise ValueError("Action output is required to save to memory.") mem_thoughts = action_output.thoughts or ai_message - observation = action_output.observations + observation = check_fail_reason or action_output.observations + action = action_output.action memory_map = { "question": question, "thought": mem_thoughts, - "action": check_fail_reason, + "action": action, "observation": observation, } write_memory_template = self.write_memory_template From a736e8ebee041e531a8c4d911221e96d9afd47c2 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Mon, 9 Dec 2024 23:00:36 +0800 Subject: [PATCH 02/14] implement function get_last_message in DefaultGptsMessageMemeory --- dbgpt/agent/core/memory/gpts/default_gpts_memory.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dbgpt/agent/core/memory/gpts/default_gpts_memory.py b/dbgpt/agent/core/memory/gpts/default_gpts_memory.py index b95dbc11b..e9dc6e2e7 100644 --- a/dbgpt/agent/core/memory/gpts/default_gpts_memory.py +++ b/dbgpt/agent/core/memory/gpts/default_gpts_memory.py @@ -146,4 +146,7 @@ def get_by_conv_id(self, conv_id: str) -> List[GptsMessage]: def get_last_message(self, conv_id: str) -> Optional[GptsMessage]: """Get the last message in the conversation.""" - return None + messages: List[GptsMessage] = self.get_by_conv_id(conv_id) + if messages is None or len(messages) == 0: + return None + return messages[-1] From 23272bc27fbc46d0ac8e8e3d6581059906637f3e Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Sat, 4 Jan 2025 17:44:18 +0800 Subject: [PATCH 03/14] ConversableAgent - ConversableAgent add name prefix, To confirm which APP an Agent originates from in a cascading app scenario --- dbgpt/agent/core/base_agent.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dbgpt/agent/core/base_agent.py b/dbgpt/agent/core/base_agent.py index a5fef6361..8d50cb925 100644 --- a/dbgpt/agent/core/base_agent.py +++ b/dbgpt/agent/core/base_agent.py @@ -47,6 +47,7 @@ class ConversableAgent(Role, Agent): stream_out: bool = True # 确认当前Agent是否需要进行参考资源展示 show_reference: bool = False + name_prefix: str = None executor: Executor = Field( default_factory=lambda: ThreadPoolExecutor(max_workers=1), @@ -58,6 +59,13 @@ def __init__(self, **kwargs): Role.__init__(self, **kwargs) Agent.__init__(self) + @property + def name(self) -> str: + """Return the name of the agent.""" + if self.name_prefix is not None: + return f"{self.current_profile.get_name()}[{self.name_prefix}]" + return self.current_profile.get_name() + def check_available(self) -> None: """Check if the agent is available. From 156e9b6f8ea17eea1b6ee2cec65e01b1c9692629 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Sat, 4 Jan 2025 17:57:25 +0800 Subject: [PATCH 04/14] Conversable support force_retry --- dbgpt/agent/core/action/base.py | 2 ++ dbgpt/agent/core/base_agent.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dbgpt/agent/core/action/base.py b/dbgpt/agent/core/action/base.py index 79929999e..d3f367209 100644 --- a/dbgpt/agent/core/action/base.py +++ b/dbgpt/agent/core/action/base.py @@ -53,6 +53,8 @@ class ActionOutput(BaseModel): ask_user: Optional[bool] = False # 如果当前agent能确定下个发言者,需要在这里指定 next_speakers: Optional[List[str]] = None + # 强制重试,不受重试次数影响限制 + force_retry: Optional[bool] = False @model_validator(mode="before") @classmethod diff --git a/dbgpt/agent/core/base_agent.py b/dbgpt/agent/core/base_agent.py index 8d50cb925..40a34a9e8 100644 --- a/dbgpt/agent/core/base_agent.py +++ b/dbgpt/agent/core/base_agent.py @@ -355,7 +355,20 @@ async def generate_reply( fail_reason = None current_retry_counter = 0 is_success = True - while current_retry_counter < self.max_retry_count: + force_retry = False + while force_retry or current_retry_counter < self.max_retry_count: + # Action force_retry 强制重试的处理: Action中明确指定需要进行重试,重试的消息按以下规则重新生成 + # - 重新生成消息,保留上一轮的Action,并增加Rounds + # - 将上一轮的Action的Content作为当前输入消息 + if force_retry: + received_message.content = reply_message.action_report.content + received_message.rounds = reply_message.rounds + 1 + reply_message = self._init_reply_message( + received_message=received_message, + rely_messages=rely_messages, + ) + + # 普通重试的处理 if current_retry_counter > 0: retry_message = self._init_reply_message( received_message=received_message, @@ -468,6 +481,20 @@ async def generate_reply( question: str = received_message.content or "" ai_message: str = llm_reply or "" + + # force_retry means this reply do not complete, should reentry and do more things + force_retry = False + if act_out is not None and act_out.force_retry: + await self.write_memories( + question=question, + ai_message=ai_message, + action_output=act_out, + check_pass=check_pass, + check_fail_reason=fail_reason, + ) + force_retry = True + continue + # 5.Optimize wrong answers myself if not check_pass: if not act_out.have_retry: From a5aec91ca0218e59dc9e3a45c30db57019b81fc4 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 3 Jan 2025 16:37:21 +0800 Subject: [PATCH 05/14] =?UTF-8?q?ToolAssistant-=E4=BC=98=E5=8C=96ToolAssis?= =?UTF-8?q?tant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbgpt/agent/expand/actions/tool_action.py | 48 +++++++++++++++++++--- dbgpt/agent/expand/tool_assistant_agent.py | 35 +++++++++++++++- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/dbgpt/agent/expand/actions/tool_action.py b/dbgpt/agent/expand/actions/tool_action.py index 1f8dd63d4..78e1313f8 100644 --- a/dbgpt/agent/expand/actions/tool_action.py +++ b/dbgpt/agent/expand/actions/tool_action.py @@ -21,12 +21,14 @@ class ToolInput(BaseModel): tool_name: str = Field( ..., description="The name of a tool that can be used to answer the current question" - " or solve the current task.", + " or solve the current task. " + "If no suitable tool is selected, leave this blank.", ) args: dict = Field( default={"arg name1": "", "arg name2": ""}, description="The tool selected for the current target, the parameter " - "information required for execution", + "information required for execution, " + "If no suitable tool is selected, leave this blank.", ) thought: str = Field(..., description="Summary of thoughts to the user") @@ -68,9 +70,9 @@ def ai_out_schema(self) -> Optional[str]: } return f"""Please response in the following json format: - {json.dumps(out_put_schema, indent=2, ensure_ascii=False)} - Make sure the response is correct json and can be parsed by Python json.loads. - """ +{json.dumps(out_put_schema, indent=2, ensure_ascii=False)} +Make sure the response is correct json and can be parsed by Python json.loads. +and do not write the comment in json,only write the json content.""" async def run( self, @@ -91,6 +93,15 @@ async def run( need_vis_render (bool, optional): Whether need visualization rendering. Defaults to True. """ + success, error = parse_json_safe(ai_message) + if not success: + return ActionOutput( + is_exe_success=False, + content=f"Tool Action execute failed! llm reply {ai_message} " + f"is not a valid json format, json error: {error}. " + f"You need to strictly return the raw JSON format. ", + ) + try: param: ToolInput = self._input_convert(ai_message, ToolInput) except Exception as e: @@ -100,6 +111,16 @@ async def run( content="The requested correctly structured answer could not be found.", ) + if param.tool_name is None or param.tool_name == "": + # can not choice tools, it must be some reason + return ActionOutput( + is_exe_success=False, + # content= param.thought, + content=f"There are no suitable tools available " + f"to achieve the user's goal: '{param.thought}'", + have_retry=False, + ) + try: tool_packs = ToolPack.from_resource(self.resource) if not tool_packs: @@ -137,10 +158,25 @@ async def run( is_exe_success=response_success, content=str(tool_result), view=view, + thoughts=param.thought, + action=str({"tool_name": param.tool_name, "args": param.args}), observations=str(tool_result), ) except Exception as e: logger.exception("Tool Action Run Failed!") return ActionOutput( - is_exe_success=False, content=f"Tool action run failed!{str(e)}" + is_exe_success=False, + content=f"Tool action run failed!{str(e)}", + action=str({"tool_name": param.tool_name, "args": param.args}), ) + + +def parse_json_safe(json_str): + """Try to parse json.""" + try: + # try to parse json + data = json.loads(json_str) + return True, data + except json.JSONDecodeError as e: + # 捕捉JSON解析错误并返回详细信息 + return False, e.msg diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index ddf6f8cc8..7eb84f1e0 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -1,9 +1,12 @@ """Plugin Assistant Agent.""" import logging +from typing import List, Optional +from .. import Resource, ResourceType from ..core.base_agent import ConversableAgent from ..core.profile import DynConfig, ProfileConfig +from ..resource import BaseTool from .actions.tool_action import ToolAction logger = logging.getLogger(__name__) @@ -37,7 +40,10 @@ class ToolAssistantAgent(ConversableAgent): "goal.", "Please output the selected tool name and specific parameter " "information in json format according to the following required format." - " If there is an example, please refer to the sample format output.", + "If there is an example, please refer to the sample format output.", + "It is not necessarily required to select a tool for execution. " + "If the tool to be used or its parameters cannot be clearly " + "determined based on the user's input, you can choose not to execute.", ], category="agent", key="dbgpt_agent_expand_plugin_assistant_agent_constraints", @@ -54,3 +60,30 @@ def __init__(self, **kwargs): """Create a new instance of ToolAssistantAgent.""" super().__init__(**kwargs) self._init_actions([ToolAction]) + + @property + def desc(self) -> Optional[str]: + tools = _get_tools_by_resource(self.resource) + + if tools is None or len(tools) == 0: + return "Has no tools to use" + + return ( + "Can use the following tools to complete the task objectives, tool information: " + f"{' '.join([f'{i+1}. tool {tools[i].name}, can {tools[i].description}.' for i in range(len(tools))])}" + ) + + +def _get_tools_by_resource(resource: Resource) -> List[BaseTool]: + tools: List[BaseTool] = [] + + if resource is None: + return tools + + if resource.type() == ResourceType.Tool and isinstance(resource, BaseTool): + tools.append(resource) + elif resource.type() == ResourceType.Pack: + for sub_res in resource.sub_resources: + tools.extend(_get_tools_by_resource(sub_res)) + + return tools From e9204217fc4dd6f04eefcd18ad36549bc1c4599b Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Mon, 6 Jan 2025 14:36:45 +0800 Subject: [PATCH 06/14] fix format problem --- dbgpt/agent/core/base_agent.py | 3 ++- dbgpt/agent/expand/tool_assistant_agent.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dbgpt/agent/core/base_agent.py b/dbgpt/agent/core/base_agent.py index 40a34a9e8..de73265e8 100644 --- a/dbgpt/agent/core/base_agent.py +++ b/dbgpt/agent/core/base_agent.py @@ -482,7 +482,8 @@ async def generate_reply( question: str = received_message.content or "" ai_message: str = llm_reply or "" - # force_retry means this reply do not complete, should reentry and do more things + # force_retry means this reply do not complete + # should reentry and do more things force_retry = False if act_out is not None and act_out.force_retry: await self.write_memories( diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 7eb84f1e0..7c7379915 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -63,14 +63,21 @@ def __init__(self, **kwargs): @property def desc(self) -> Optional[str]: + """Return desc of this agent.""" tools = _get_tools_by_resource(self.resource) - if tools is None or len(tools) == 0: return "Has no tools to use" + tools_desc_list = [] + for i in range(len(tools)): + tool = tools[i] + s = f"{i + 1}. tool {tool.name}, can {tool.description}." + tools_desc_list.append(s) + return ( - "Can use the following tools to complete the task objectives, tool information: " - f"{' '.join([f'{i+1}. tool {tools[i].name}, can {tools[i].description}.' for i in range(len(tools))])}" + "Can use the following tools to complete the task objectives, " + "tool information: " + f"{' '.join(tools_desc_list)}" ) From f9821459ecdb356b4e672afbe4e29df1436f64a9 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Mon, 6 Jan 2025 15:29:18 +0800 Subject: [PATCH 07/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 7c7379915..890ec1a52 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -81,7 +81,7 @@ def desc(self) -> Optional[str]: ) -def _get_tools_by_resource(resource: Resource) -> List[BaseTool]: +def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTool]]: tools: List[BaseTool] = [] if resource is None: From 25b8bd193f9a56f719def2f09b7769cf1c781eea Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 09:47:49 +0800 Subject: [PATCH 08/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 890ec1a52..c4e4acec3 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -91,6 +91,8 @@ def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTo tools.append(resource) elif resource.type() == ResourceType.Pack: for sub_res in resource.sub_resources: - tools.extend(_get_tools_by_resource(sub_res)) + res_list = _get_tools_by_resource(sub_res) + if res_list is not None and len(res_list) > 0: + tools.extend(_get_tools_by_resource(sub_res)) return tools From 5bb9a7abe48677a0e7bf376be71b7bdf0c1e35e4 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 10:39:30 +0800 Subject: [PATCH 09/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index c4e4acec3..948824f0e 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -91,7 +91,7 @@ def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTo tools.append(resource) elif resource.type() == ResourceType.Pack: for sub_res in resource.sub_resources: - res_list = _get_tools_by_resource(sub_res) + res_list: List[BaseTool] = _get_tools_by_resource(sub_res) if res_list is not None and len(res_list) > 0: tools.extend(_get_tools_by_resource(sub_res)) From bd64014942c05bed544f41115ad08c2354ba950a Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 10:47:20 +0800 Subject: [PATCH 10/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 948824f0e..3cc2727ba 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -93,6 +93,6 @@ def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTo for sub_res in resource.sub_resources: res_list: List[BaseTool] = _get_tools_by_resource(sub_res) if res_list is not None and len(res_list) > 0: - tools.extend(_get_tools_by_resource(sub_res)) + tools.extend(res_list) return tools From ee84fcb94adb88f184a824499b8e0b0730a65bfd Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 11:31:19 +0800 Subject: [PATCH 11/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 3cc2727ba..4ef7fb70e 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -93,6 +93,7 @@ def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTo for sub_res in resource.sub_resources: res_list: List[BaseTool] = _get_tools_by_resource(sub_res) if res_list is not None and len(res_list) > 0: - tools.extend(res_list) + for res in res_list: + tools.append(res) return tools From f3fef1a4059fbeca10975363f3826a4d0c8de4d1 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 17:34:02 +0800 Subject: [PATCH 12/14] fix format problem --- dbgpt/agent/expand/tool_assistant_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbgpt/agent/expand/tool_assistant_agent.py b/dbgpt/agent/expand/tool_assistant_agent.py index 4ef7fb70e..0cd7d13e8 100644 --- a/dbgpt/agent/expand/tool_assistant_agent.py +++ b/dbgpt/agent/expand/tool_assistant_agent.py @@ -91,7 +91,7 @@ def _get_tools_by_resource(resource: Optional[Resource]) -> Optional[List[BaseTo tools.append(resource) elif resource.type() == ResourceType.Pack: for sub_res in resource.sub_resources: - res_list: List[BaseTool] = _get_tools_by_resource(sub_res) + res_list = _get_tools_by_resource(sub_res) if res_list is not None and len(res_list) > 0: for res in res_list: tools.append(res) From 6a1ba7f202b97bd5128fb2ae0f9a9cd2c374457d Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 17:52:56 +0800 Subject: [PATCH 13/14] fix format problem --- dbgpt/agent/core/base_agent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbgpt/agent/core/base_agent.py b/dbgpt/agent/core/base_agent.py index de73265e8..4c7b04b12 100644 --- a/dbgpt/agent/core/base_agent.py +++ b/dbgpt/agent/core/base_agent.py @@ -361,6 +361,8 @@ async def generate_reply( # - 重新生成消息,保留上一轮的Action,并增加Rounds # - 将上一轮的Action的Content作为当前输入消息 if force_retry: + if reply_message.action_report is None: + raise ValueError("action output is None when force_retry") received_message.content = reply_message.action_report.content received_message.rounds = reply_message.rounds + 1 reply_message = self._init_reply_message( From f22b9327f08d3149c56201c94a6b093b483e4da2 Mon Sep 17 00:00:00 2001 From: cinjospeh Date: Fri, 10 Jan 2025 18:03:18 +0800 Subject: [PATCH 14/14] fix format problem --- dbgpt/agent/core/base_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbgpt/agent/core/base_agent.py b/dbgpt/agent/core/base_agent.py index 4c7b04b12..1ade69b43 100644 --- a/dbgpt/agent/core/base_agent.py +++ b/dbgpt/agent/core/base_agent.py @@ -47,7 +47,7 @@ class ConversableAgent(Role, Agent): stream_out: bool = True # 确认当前Agent是否需要进行参考资源展示 show_reference: bool = False - name_prefix: str = None + name_prefix: Optional[str] = None executor: Executor = Field( default_factory=lambda: ThreadPoolExecutor(max_workers=1),