diff --git a/dbgpt/agent/resource/app.py b/dbgpt/agent/resource/app.py index 912f88cfe..595c5fdb9 100644 --- a/dbgpt/agent/resource/app.py +++ b/dbgpt/agent/resource/app.py @@ -1,20 +1,23 @@ +"""Application Resources for the agent.""" + import dataclasses import uuid -from typing import Optional, Tuple, Dict, Type, Any, List, cast +from typing import Any, Dict, List, Optional, Tuple, Type, cast -from dbgpt.agent import ConversableAgent, AgentMessage, AgentContext +from dbgpt.agent import AgentMessage, ConversableAgent from dbgpt.serve.agent.agents.app_agent_manage import get_app_manager from dbgpt.util import ParameterDescription + from .base import Resource, ResourceParameters, ResourceType -def get_app_list(): +def _get_app_list(): apps = get_app_manager().get_dbgpts() results = [ { "label": f"{app.app_name}({app.app_code})", "key": app.app_code, - "description": app.app_describe + "description": app.app_describe, } for app in apps ] @@ -23,20 +26,21 @@ def get_app_list(): @dataclasses.dataclass class AppResourceParameters(ResourceParameters): + """Application resource class.""" + app_code: str = dataclasses.field( - default=None, metadata={ "help": "app code", - "valid_values": get_app_list(), + "valid_values": _get_app_list(), }, ) @classmethod def to_configurations( - cls, - parameters: Type["AppResourceParameters"], - version: Optional[str] = None, - **kwargs, + cls, + parameters: Type["AppResourceParameters"], + version: Optional[str] = None, + **kwargs, ) -> Any: """Convert the parameters to configurations.""" conf: List[ParameterDescription] = cast( @@ -53,7 +57,7 @@ class AppResourceParameters(ResourceParameters): @classmethod def from_dict( - cls, data: dict, ignore_extra_fields: bool = True + cls, data: dict, ignore_extra_fields: bool = True ) -> ResourceParameters: """Create a new instance from a dictionary.""" copied_data = data.copy() @@ -66,6 +70,7 @@ class AppResource(Resource[AppResourceParameters]): """AppResource resource class.""" def __init__(self, name: str, app_code: str, **kwargs): + """Initialize AppResource resource.""" self._resource_name = name self._app_code = app_code @@ -73,12 +78,24 @@ class AppResource(Resource[AppResourceParameters]): self._app_name = app.app_name self._app_desc = app.app_describe + @property + def app_desc(self): + """Return the app description.""" + return self._app_desc + + @property + def app_name(self): + """Return the app name.""" + return self._app_name + @classmethod def type(cls) -> ResourceType: + """Return the resource type.""" return ResourceType.App @property def name(self) -> str: + """Return the resource name.""" return self._resource_name @classmethod @@ -86,13 +103,18 @@ class AppResource(Resource[AppResourceParameters]): """Return the resource parameters class.""" return AppResourceParameters - async def get_prompt(self, *, lang: str = "en", prompt_type: str = "default", question: Optional[str] = None, - resource_name: Optional[str] = None, **kwargs) -> Tuple[str, Optional[Dict]]: + async def get_prompt( + self, + *, + lang: str = "en", + prompt_type: str = "default", + question: Optional[str] = None, + resource_name: Optional[str] = None, + **kwargs, + ) -> Tuple[str, Optional[Dict]]: """Get the prompt.""" - prompt_template_zh = ( - "{name}:调用此资源与应用 {app_name} 进行交互。" - "应用 {app_name} 有什么用?{description}" + "{name}:调用此资源与应用 {app_name} 进行交互。" "应用 {app_name} 有什么用?{description}" ) prompt_template_en = ( "{name}:Call this resource to interact with the application {app_name} ." @@ -102,9 +124,7 @@ class AppResource(Resource[AppResourceParameters]): return ( template.format( - name=self.name, - app_name=self._app_name, - description=self._app_desc + name=self.name, app_name=self._app_name, description=self._app_desc ), None, ) @@ -114,15 +134,16 @@ class AppResource(Resource[AppResourceParameters]): """Return whether the tool is asynchronous.""" return True - async def execute(self, *args, resource_name: Optional[str] = None, **kwargs) -> Any: + def execute(self, *args, resource_name: Optional[str] = None, **kwargs) -> Any: + """Execute the resource.""" if self.is_async: - raise RuntimeError("Async execution is not supported") + raise RuntimeError("Sync execution is not supported") async def async_execute( - self, - *args, - resource_name: Optional[str] = None, - **kwargs, + self, + *args, + resource_name: Optional[str] = None, + **kwargs, ) -> Any: """Execute the tool asynchronously. @@ -132,35 +153,41 @@ class AppResource(Resource[AppResourceParameters]): specific tool). **kwargs: The keyword arguments. """ + user_input: Optional[str] = kwargs.get("user_input") + parent_agent: Optional[ConversableAgent] = kwargs.get("parent_agent") - user_input = kwargs.get("user_input") - parent_agent = kwargs.get("parent_agent") + if user_input is None: + raise RuntimeError("AppResource async execution user_input is None") + if parent_agent is None: + raise RuntimeError("AppResource async execution parent_agent is None") - reply_message = await self.chat_2_app_once(self._app_code, user_input=user_input, sender=parent_agent) + reply_message = await _start_app(self._app_code, user_input, parent_agent) return reply_message.content - async def chat_2_app_once(self, - app_code: str, - user_input: str, - conv_uid: str = None, - sender: ConversableAgent = None) -> AgentMessage: - # create a new conv_uid - conv_uid = str(uuid.uuid4()) if conv_uid is None else conv_uid - gpts_app = get_app_manager().get_app(app_code) +async def _start_app( + app_code: str, + user_input: str, + sender: ConversableAgent, + conv_uid: Optional[str] = None, +) -> AgentMessage: + """Start App By AppResource.""" + conv_uid = str(uuid.uuid4()) if conv_uid is None else conv_uid + gpts_app = get_app_manager().get_app(app_code) + app_agent = await get_app_manager().create_agent_by_app_code( + gpts_app, conv_uid=conv_uid + ) - app_agent = await get_app_manager().create_agent_by_app_code(gpts_app, conv_uid=conv_uid) + agent_message = AgentMessage( + content=user_input, + current_goal=user_input, + context={ + "conv_uid": conv_uid, + }, + rounds=0, + ) + reply_message: AgentMessage = await app_agent.generate_reply( + received_message=agent_message, sender=sender + ) - agent_message = AgentMessage( - content=user_input, - current_goal=user_input, - context={ - "conv_uid": conv_uid, - }, - rounds=0, - ) - - reply_message: AgentMessage = await app_agent.generate_reply(received_message=agent_message, - sender=sender) - - return reply_message + return reply_message diff --git a/dbgpt/app/component_configs.py b/dbgpt/app/component_configs.py index 51ed43c2b..e2fa16b17 100644 --- a/dbgpt/app/component_configs.py +++ b/dbgpt/app/component_configs.py @@ -106,12 +106,12 @@ def _initialize_resource_manager(system_app: SystemApp): get_current_host_system_load, ) from dbgpt.agent.expand.resources.search_tool import baidu_search + from dbgpt.agent.resource.app import AppResource from dbgpt.agent.resource.base import ResourceType from dbgpt.agent.resource.manage import get_resource_manager, initialize_resource from dbgpt.serve.agent.resource.datasource import DatasourceResource from dbgpt.serve.agent.resource.knowledge import KnowledgeSpaceRetrieverResource from dbgpt.serve.agent.resource.plugin import PluginToolPack - from dbgpt.agent.resource.app import AppResource initialize_resource(system_app) rm = get_resource_manager(system_app) diff --git a/dbgpt/serve/agent/agents/app_agent_manage.py b/dbgpt/serve/agent/agents/app_agent_manage.py index c7fb83871..3ca071dbf 100644 --- a/dbgpt/serve/agent/agents/app_agent_manage.py +++ b/dbgpt/serve/agent/agents/app_agent_manage.py @@ -6,28 +6,29 @@ from typing import List, Type from dbgpt.agent import ( AgentContext, AgentMemory, + AutoPlanChatManager, ConversableAgent, DefaultAWELLayoutManager, GptsMemory, LLMConfig, - UserProxyAgent, get_agent_manager, + UserProxyAgent, + get_agent_manager, ) from dbgpt.agent.core.schema import Status from dbgpt.agent.resource import get_resource_manager from dbgpt.agent.util.llm.llm import LLMStrategyType from dbgpt.app.component_configs import CFG from dbgpt.component import BaseComponent, ComponentType, SystemApp -from dbgpt.core import LLMClient -from dbgpt.core import PromptTemplate +from dbgpt.core import LLMClient, PromptTemplate from dbgpt.model.cluster import WorkerManagerFactory from dbgpt.model.cluster.client import DefaultLLMClient from dbgpt.serve.prompt.api.endpoints import get_service -from .db_gpts_memory import MetaDbGptsMessageMemory, MetaDbGptsPlansMemory + from ..db import GptsMessagesDao -from ..db.gpts_app import GptsApp, GptsAppDao, GptsAppQuery -from ..db.gpts_app import GptsAppDetail +from ..db.gpts_app import GptsApp, GptsAppDao, GptsAppDetail, GptsAppQuery from ..db.gpts_conversations_db import GptsConversationsDao from ..team.base import TeamMode +from .db_gpts_memory import MetaDbGptsMessageMemory, MetaDbGptsPlansMemory logger = logging.getLogger(__name__) @@ -63,16 +64,16 @@ class AppManager(BaseComponent, ABC): return self.gpts_app.app_detail(app_code) async def user_chat_2_app( - self, - user_query: str, - conv_uid: str, - gpts_app: GptsApp, - agent_memory: AgentMemory, - is_retry_chat: bool = False, - last_speaker_name: str = None, - init_message_rounds: int = 0, - enable_verbose: bool = True, - **ext_info, + self, + user_query: str, + conv_uid: str, + gpts_app: GptsApp, + agent_memory: AgentMemory, + is_retry_chat: bool = False, + last_speaker_name: str = None, + init_message_rounds: int = 0, + enable_verbose: bool = True, + **ext_info, ) -> Status: context: AgentContext = AgentContext( conv_id=conv_uid, @@ -107,41 +108,41 @@ class AppManager(BaseComponent, ABC): return Status.COMPLETE async def create_app_agent( - self, - gpts_app: GptsApp, - agent_memory: AgentMemory, - context: AgentContext, + self, + gpts_app: GptsApp, + agent_memory: AgentMemory, + context: AgentContext, ) -> ConversableAgent: # init default llm provider llm_provider = DefaultLLMClient( - self.system_app.get_component(ComponentType.WORKER_MANAGER_FACTORY, WorkerManagerFactory).create(), - auto_convert_message=True + self.system_app.get_component( + ComponentType.WORKER_MANAGER_FACTORY, WorkerManagerFactory + ).create(), + auto_convert_message=True, ) # init team employees # TODO employee has it own llm provider employees: List[ConversableAgent] = [] for record in gpts_app.details: - agent = (await create_agent_from_gpt_detail(record, llm_provider, context, agent_memory)) + agent = await create_agent_from_gpt_detail( + record, llm_provider, context, agent_memory + ) agent.name_prefix = gpts_app.app_name employees.append(agent) - app_agent: ConversableAgent = ( - await create_agent_of_gpts_app(gpts_app, - llm_provider, - context, - agent_memory, - employees) + app_agent: ConversableAgent = await create_agent_of_gpts_app( + gpts_app, llm_provider, context, agent_memory, employees ) app_agent.name_prefix = gpts_app.app_name return app_agent async def create_agent_by_app_code( - self, - gpts_app: GptsApp, - conv_uid: str = None, - agent_memory: AgentMemory = None, - context: AgentContext = None, + self, + gpts_app: GptsApp, + conv_uid: str = None, + agent_memory: AgentMemory = None, + context: AgentContext = None, ) -> ConversableAgent: """ Create a conversable agent by application code. @@ -157,7 +158,10 @@ class AppManager(BaseComponent, ABC): """ conv_uid = str(uuid.uuid4()) if conv_uid is None else conv_uid - from dbgpt.agent.core.memory.gpts import DefaultGptsPlansMemory, DefaultGptsMessageMemory + from dbgpt.agent.core.memory.gpts import ( + DefaultGptsMessageMemory, + DefaultGptsPlansMemory, + ) if agent_memory is None: gpt_memory = GptsMemory( @@ -179,24 +183,23 @@ class AppManager(BaseComponent, ABC): context.gpts_app_name = gpts_app.app_name context.language = gpts_app.language - agent: ConversableAgent = ( - await self.create_app_agent(gpts_app, agent_memory, context) + agent: ConversableAgent = await self.create_app_agent( + gpts_app, agent_memory, context ) return agent async def create_agent_from_gpt_detail( - record: GptsAppDetail, - llm_client: LLMClient, - agent_context: AgentContext, - agent_memory: AgentMemory) -> ConversableAgent: + record: GptsAppDetail, + llm_client: LLMClient, + agent_context: AgentContext, + agent_memory: AgentMemory, +) -> ConversableAgent: """ Get the agent object from the GPTsAppDetail object. """ agent_manager = get_agent_manager() - agent_cls: Type[ConversableAgent] = agent_manager.get_by_name( - record.agent_name - ) + agent_cls: Type[ConversableAgent] = agent_manager.get_by_name(record.agent_name) llm_config = LLMConfig( llm_client=llm_client, llm_strategy=LLMStrategyType(record.llm_strategy), @@ -208,25 +211,29 @@ async def create_agent_from_gpt_detail( prompt_code=record.prompt_template ) - depend_resource = get_resource_manager().build_resource(record.resources, version="v1") + depend_resource = get_resource_manager().build_resource( + record.resources, version="v1" + ) - agent = (await agent_cls() - .bind(agent_context) - .bind(agent_memory) - .bind(llm_config) - .bind(depend_resource) - .bind(prompt_template) - .build()) + agent = ( + await agent_cls() + .bind(agent_context) + .bind(agent_memory) + .bind(llm_config) + .bind(depend_resource) + .bind(prompt_template) + .build() + ) return agent async def create_agent_of_gpts_app( - gpts_app: GptsApp, - llm_client: LLMClient, - context: AgentContext, - memory: AgentMemory, - employees: List[ConversableAgent], + gpts_app: GptsApp, + llm_client: LLMClient, + context: AgentContext, + memory: AgentMemory, + employees: List[ConversableAgent], ) -> ConversableAgent: llm_config = LLMConfig( llm_client=llm_client, @@ -240,13 +247,10 @@ async def create_agent_of_gpts_app( agent_of_app: ConversableAgent = employees[0] else: if TeamMode.AUTO_PLAN == team_mode: - if not employees or len(employees) < 0: + if not gpts_app.details or len(gpts_app.details) < 0: raise ValueError("APP exception no available agent!") - from dbgpt.agent.v2 import AutoPlanChatManagerV2, MultiAgentTeamPlanner - planner = MultiAgentTeamPlanner() - planner.name_prefix = gpts_app.app_name - manager = AutoPlanChatManagerV2(planner) - manager.name_prefix = gpts_app.app_name + llm_config = employees[0].llm_config + manager = AutoPlanChatManager() elif TeamMode.AWEL_LAYOUT == team_mode: if not awel_team_context: raise ValueError( @@ -257,12 +261,7 @@ async def create_agent_of_gpts_app( raise ValueError(f"Native APP chat not supported!") else: raise ValueError(f"Unknown Agent Team Mode!{team_mode}") - manager = ( - await manager.bind(context) - .bind(memory) - .bind(llm_config) - .build() - ) + manager = await manager.bind(context).bind(memory).bind(llm_config).build() manager.hire(employees) agent_of_app: ConversableAgent = manager diff --git a/dbgpt/serve/agent/agents/expand/__init__.py b/dbgpt/serve/agent/agents/expand/__init__.py index cc9b68513..65ea3d1c4 100644 --- a/dbgpt/serve/agent/agents/expand/__init__.py +++ b/dbgpt/serve/agent/agents/expand/__init__.py @@ -1,6 +1,7 @@ -from dbgpt.serve.agent.agents.expand.app_resource_start_assisant_agent import AppStarterAgent +from dbgpt.serve.agent.agents.expand.app_resource_start_assisant_agent import ( + AppStarterAgent, +) __all__ = [ "AppStarterAgent", ] - diff --git a/dbgpt/serve/agent/agents/expand/app_resource_start_assisant_agent.py b/dbgpt/serve/agent/agents/expand/app_resource_start_assisant_agent.py index 61cf640d7..fa3417d09 100644 --- a/dbgpt/serve/agent/agents/expand/app_resource_start_assisant_agent.py +++ b/dbgpt/serve/agent/agents/expand/app_resource_start_assisant_agent.py @@ -1,13 +1,17 @@ import json import logging -from typing import Any, Dict, List -from typing import Optional +from typing import Any, Dict, List, Optional from dbgpt._private.pydantic import BaseModel, Field -from dbgpt.agent import Action, ActionOutput, AgentResource, AgentMessage, ResourceType from dbgpt.agent import ( + Action, + ActionOutput, Agent, + AgentMessage, + AgentResource, ConversableAgent, + Resource, + ResourceType, get_agent_manager, ) from dbgpt.agent.core.profile import DynConfig, ProfileConfig @@ -23,7 +27,7 @@ class AppResourceInput(BaseModel): app_name: str = Field( ..., description="The name of a application that can be used to answer the current question" - " or solve the current task.", + " or solve the current task.", ) app_query: str = Field( @@ -60,7 +64,7 @@ class AppResourceAction(Action[AppResourceInput]): """Return the AI output schema.""" out_put_schema = { "app_name": "the agent name you selected", - "app_query": "the query to the selected agent, must input a str, base on the natural language " + "app_query": "the query to the selected agent, must input a str, base on the natural language ", } return f"""Please response in the following json format: @@ -69,12 +73,12 @@ class AppResourceAction(Action[AppResourceInput]): """ async def run( - self, - ai_message: str, - resource: Optional[AgentResource] = None, - rely_action_out: Optional[ActionOutput] = None, - need_vis_render: bool = True, - **kwargs, + self, + ai_message: str, + resource: Optional[AgentResource] = None, + rely_action_out: Optional[ActionOutput] = None, + need_vis_render: bool = True, + **kwargs, ) -> ActionOutput: """Perform the plugin action. @@ -92,7 +96,9 @@ class AppResourceAction(Action[AppResourceInput]): err_msg = None app_result = None try: - param: AppResourceInput = self._input_convert(ai_message, AppResourceInput) + param: AppResourceInput = self._input_convert( + ai_message, AppResourceInput + ) except Exception as e: logger.exception((str(e))) return ActionOutput( @@ -126,7 +132,9 @@ class AppResourceAction(Action[AppResourceInput]): is_exe_success=False, content=f"App action run failed!{str(e)}" ) - async def __get_plugin_view(self, param: AppResourceInput, app_result: Any, err_msg: str): + async def __get_plugin_view( + self, param: AppResourceInput, app_result: Any, err_msg: str + ): if not self.render_protocol: return None # raise NotImplementedError("The render_protocol should be implemented.") @@ -181,11 +189,8 @@ class AppStarterAgent(ConversableAgent): constraints=DynConfig( [ "请一步一步思考参为用户问题选择一个最匹配的应用来进行用户问题回答,可参考给出示例的应用选择逻辑.", - "请阅读用户问题,确定问题所属领域和问题意图,按领域和意图匹配应用,如果用户问题意图缺少操作类应用需要的参数,优先使用咨询类型应用,有明确操作目标才使用操作类应用.", "必须从已知的应用中选出一个可用的应用来进行回答,不要瞎编应用的名称", - "仅选择可回答问题的应用即可,不要直接回答用户问题.", - "如果用户的问题和提供的所有应用全都不相关,则应用code和name都输出为空", - "注意应用意图定义中如果有槽位信息,再次阅读理解用户输入信息,将对应的内容填入对应槽位参数定义中.", + "如果选择出了合适的应用,传递给应用的请求和用户输入的请求中的信息必须完全对等,不能丢失或增加任何信息", ], category="agent", key="dbgpt_ant_agent_agents_app_resource_starter_assistant_agent_profile_constraints", @@ -203,11 +208,11 @@ class AppStarterAgent(ConversableAgent): self._init_actions([AppResourceAction]) def prepare_act_param( - self, - received_message: Optional[AgentMessage], - sender: Agent, - rely_messages: Optional[List[AgentMessage]] = None, - **kwargs, + self, + received_message: Optional[AgentMessage], + sender: Agent, + rely_messages: Optional[List[AgentMessage]] = None, + **kwargs, ) -> Dict[str, Any]: return { "user_input": received_message.content, @@ -215,6 +220,23 @@ class AppStarterAgent(ConversableAgent): "parent_agent": self, } + @property + def desc(self) -> Optional[str]: + resources: List[AppResource] = get_app_resource_list(self.resource) + + return f"根据用户问题匹配合适的应用来进行回答. AppStart下的应用中可以解决下列问题:{[f' {res.app_desc}' for res in resources]}" + + +def get_app_resource_list(resource: Resource) -> List[AppResource]: + app_resource_list: List[AppResource] = [] + if resource.type() == ResourceType.Pack: + for sub_resource in resource.sub_resources: + if sub_resource.type() == ResourceType.App: + app_resource_list.extend(AppResource.from_resource(sub_resource)) + if resource.type() == ResourceType.App: + app_resource_list.extend(AppResource.from_resource(resource)) + return app_resource_list + agent_manage = get_agent_manager() agent_manage.register_agent(AppStarterAgent)