mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 22:09:44 +00:00
feat(agent): Release agent SDK (#1396)
This commit is contained in:
@@ -3,20 +3,21 @@ import json
|
||||
import logging
|
||||
import uuid
|
||||
from abc import ABC
|
||||
from collections import defaultdict
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
|
||||
from fastapi import APIRouter, Body
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.agents.agent_new import Agent, AgentContext
|
||||
from dbgpt.agent.agents.agents_manage import agent_manage
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.agents.llm.llm import LLMConfig, LLMStrategyType
|
||||
from dbgpt.agent.agents.user_proxy_agent import UserProxyAgent
|
||||
from dbgpt.agent.common.schema import Status
|
||||
from dbgpt.agent.core.agent import Agent, AgentContext
|
||||
from dbgpt.agent.core.agent_manage import agent_manager
|
||||
from dbgpt.agent.core.base_agent import ConversableAgent
|
||||
from dbgpt.agent.core.llm.llm import LLMConfig, LLMStrategyType
|
||||
from dbgpt.agent.core.schema import Status
|
||||
from dbgpt.agent.core.user_proxy_agent import UserProxyAgent
|
||||
from dbgpt.agent.memory.gpts_memory import GptsMemory
|
||||
from dbgpt.agent.plan.awel.team_awel_layout import DefaultAWELLayoutManager
|
||||
from dbgpt.agent.plan.team_auto_plan import AutoPlanChatManager
|
||||
from dbgpt.agent.resource.resource_loader import ResourceLoader
|
||||
from dbgpt.app.openapi.api_view_model import Result
|
||||
from dbgpt.app.scene.base import ChatScene
|
||||
@@ -25,18 +26,16 @@ from dbgpt.core.interface.message import StorageConversation
|
||||
from dbgpt.model.cluster import WorkerManagerFactory
|
||||
from dbgpt.model.cluster.client import DefaultLLMClient
|
||||
from dbgpt.serve.agent.model import PagenationFilter, PluginHubFilter
|
||||
from dbgpt.serve.agent.team.plan.team_auto_plan import AutoPlanChatManager
|
||||
from dbgpt.serve.conversation.serve import Serve as ConversationServe
|
||||
from dbgpt.util.json_utils import serialize
|
||||
|
||||
from ..db.gpts_app import GptsApp, GptsAppDao, GptsAppQuery
|
||||
from ..db.gpts_conversations_db import GptsConversationsDao, GptsConversationsEntity
|
||||
from ..db.gpts_manage_db import GptsInstanceDao, GptsInstanceEntity
|
||||
from ..db.gpts_manage_db import GptsInstanceEntity
|
||||
from ..resource_loader.datasource_load_client import DatasourceLoadClient
|
||||
from ..resource_loader.knowledge_space_load_client import KnowledgeSpaceLoadClient
|
||||
from ..resource_loader.plugin_hub_load_client import PluginHubLoadClient
|
||||
from ..team.base import TeamMode
|
||||
from ..team.layout.team_awel_layout_new import AwelLayoutChatNewManager
|
||||
from .db_gpts_memory import MetaDbGptsMessageMemory, MetaDbGptsPlansMemory
|
||||
|
||||
CFG = Config()
|
||||
@@ -204,11 +203,11 @@ class MultiAgents(BaseComponent, ABC):
|
||||
# Prepare resource loader
|
||||
resource_loader = ResourceLoader()
|
||||
plugin_hub_loader = PluginHubLoadClient()
|
||||
resource_loader.register_resesource_api(plugin_hub_loader)
|
||||
resource_loader.register_resource_api(plugin_hub_loader)
|
||||
datasource_loader = DatasourceLoadClient()
|
||||
resource_loader.register_resesource_api(datasource_loader)
|
||||
resource_loader.register_resource_api(datasource_loader)
|
||||
knowledge_space_loader = KnowledgeSpaceLoadClient()
|
||||
resource_loader.register_resesource_api(knowledge_space_loader)
|
||||
resource_loader.register_resource_api(knowledge_space_loader)
|
||||
context: AgentContext = AgentContext(
|
||||
conv_id=conv_uid,
|
||||
gpts_app_name=gpts_app.app_name,
|
||||
@@ -223,7 +222,7 @@ class MultiAgents(BaseComponent, ABC):
|
||||
self.llm_provider = DefaultLLMClient(worker_manager, auto_convert_message=True)
|
||||
|
||||
for record in gpts_app.details:
|
||||
cls: Type[ConversableAgent] = agent_manage.get_by_name(record.agent_name)
|
||||
cls: Type[ConversableAgent] = agent_manager.get_by_name(record.agent_name)
|
||||
llm_config = LLMConfig(
|
||||
llm_client=self.llm_provider,
|
||||
llm_strategy=LLMStrategyType(record.llm_strategy),
|
||||
@@ -248,7 +247,7 @@ class MultiAgents(BaseComponent, ABC):
|
||||
if TeamMode.AUTO_PLAN == team_mode:
|
||||
manager = AutoPlanChatManager()
|
||||
elif TeamMode.AWEL_LAYOUT == team_mode:
|
||||
manager = AwelLayoutChatNewManager(dag=gpts_app.team_context)
|
||||
manager = DefaultAWELLayoutManager(dag=gpts_app.team_context)
|
||||
else:
|
||||
raise ValueError(f"Unknown Agent Team Mode!{team_mode}")
|
||||
manager = (
|
||||
@@ -273,7 +272,7 @@ class MultiAgents(BaseComponent, ABC):
|
||||
self.gpts_conversations.update(conv_uid, Status.RUNNING.value)
|
||||
|
||||
try:
|
||||
await user_proxy.a_initiate_chat(
|
||||
await user_proxy.initiate_chat(
|
||||
recipient=recipient,
|
||||
message=user_query,
|
||||
)
|
||||
@@ -301,7 +300,7 @@ class MultiAgents(BaseComponent, ABC):
|
||||
]
|
||||
else False
|
||||
)
|
||||
message = await self.memory.one_chat_competions_v2(conv_id)
|
||||
message = await self.memory.one_chat_completions_v2(conv_id)
|
||||
yield message
|
||||
|
||||
if is_complete:
|
||||
@@ -321,7 +320,7 @@ class MultiAgents(BaseComponent, ABC):
|
||||
else False
|
||||
)
|
||||
if is_complete:
|
||||
return await self.memory.one_chat_competions_v2(conv_id)
|
||||
return await self.memory.one_chat_completions_v2(conv_id)
|
||||
else:
|
||||
pass
|
||||
# raise ValueError(
|
||||
@@ -341,7 +340,7 @@ multi_agents = MultiAgents()
|
||||
async def agents_list():
|
||||
logger.info("agents_list!")
|
||||
try:
|
||||
agents = agent_manage.all_agents()
|
||||
agents = agent_manager.all_agents()
|
||||
return Result.succ(agents)
|
||||
except Exception as e:
|
||||
return Result.failed(code="E30001", msg=str(e))
|
||||
|
@@ -56,9 +56,9 @@ class MetaDbGptsPlansMemory(GptsPlansMemory):
|
||||
task_num: int,
|
||||
state: str,
|
||||
retry_times: int,
|
||||
agent: str = None,
|
||||
model: str = None,
|
||||
result: str = None,
|
||||
agent: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
result: Optional[str] = None,
|
||||
):
|
||||
self.gpts_plan.update_task(
|
||||
conv_id=conv_id,
|
||||
@@ -95,7 +95,7 @@ class MetaDbGptsMessageMemory(GptsMessageMemory):
|
||||
agent1: str,
|
||||
agent2: str,
|
||||
current_goal: Optional[str] = None,
|
||||
) -> Optional[List[GptsMessage]]:
|
||||
) -> List[GptsMessage]:
|
||||
db_results = self.gpts_message.get_between_agents(
|
||||
conv_id, agent1, agent2, current_goal
|
||||
)
|
||||
@@ -105,7 +105,7 @@ class MetaDbGptsMessageMemory(GptsMessageMemory):
|
||||
results.append(GptsMessage.from_dict(item.__dict__))
|
||||
return results
|
||||
|
||||
def get_by_conv_id(self, conv_id: str) -> Optional[List[GptsMessage]]:
|
||||
def get_by_conv_id(self, conv_id: str) -> List[GptsMessage]:
|
||||
db_results = self.gpts_message.get_by_conv_id(conv_id)
|
||||
|
||||
results = []
|
||||
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
|
@@ -3,8 +3,8 @@ import logging
|
||||
from fastapi import APIRouter
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.agents.agents_manage import agent_manage
|
||||
from dbgpt.agent.agents.llm.llm import LLMStrategyType
|
||||
from dbgpt.agent.core.agent_manage import agent_manager
|
||||
from dbgpt.agent.core.llm.llm import LLMStrategyType
|
||||
from dbgpt.agent.resource.resource_api import ResourceType
|
||||
from dbgpt.app.knowledge.api import knowledge_space_service
|
||||
from dbgpt.app.knowledge.request.request import KnowledgeSpaceRequest
|
||||
@@ -63,7 +63,7 @@ async def edit(gpts_app: GptsApp):
|
||||
@router.get("/v1/agents/list")
|
||||
async def all_agents():
|
||||
try:
|
||||
return Result.succ(agent_manage.list_agents())
|
||||
return Result.succ(agent_manager.list_agents())
|
||||
except Exception as ex:
|
||||
return Result.failed(code="E000X", msg=f"query agents error: {ex}")
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.component import ComponentType
|
||||
from dbgpt.model.cluster import BaseModelController
|
||||
from dbgpt.serve.agent.db.gpts_app import GptsApp, GptsAppCollectionDao, GptsAppDao
|
||||
from dbgpt.serve.agent.db.gpts_app import GptsAppCollectionDao, GptsAppDao
|
||||
|
||||
CFG = Config()
|
||||
|
||||
|
@@ -5,11 +5,11 @@ from datetime import datetime
|
||||
from itertools import groupby
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import Column, DateTime, Integer, String, Text, UniqueConstraint
|
||||
|
||||
from dbgpt._private.pydantic import BaseModel
|
||||
from dbgpt.agent.plan.awel.team_awel_layout import AWELTeamContext
|
||||
from dbgpt.agent.resource.resource_api import AgentResource
|
||||
from dbgpt.serve.agent.model import AwelTeamContext
|
||||
from dbgpt.serve.agent.team.base import TeamMode
|
||||
from dbgpt.storage.metadata import BaseDao, Model
|
||||
|
||||
@@ -60,7 +60,7 @@ class GptsAppDetail(BaseModel):
|
||||
app_name=d["app_name"],
|
||||
agent_name=d["agent_name"],
|
||||
node_id=d["node_id"],
|
||||
resources=AgentResource.from_josn_list_str(d.get("resources", None)),
|
||||
resources=AgentResource.from_json_list_str(d.get("resources", None)),
|
||||
prompt_template=d.get("prompt_template", None),
|
||||
llm_strategy=d.get("llm_strategy", None),
|
||||
llm_strategy_value=llm_strategy_value,
|
||||
@@ -70,7 +70,7 @@ class GptsAppDetail(BaseModel):
|
||||
|
||||
@classmethod
|
||||
def from_entity(cls, entity):
|
||||
resources = AgentResource.from_josn_list_str(entity.resources)
|
||||
resources = AgentResource.from_json_list_str(entity.resources)
|
||||
return cls(
|
||||
app_code=entity.app_code,
|
||||
app_name=entity.app_name,
|
||||
@@ -91,7 +91,7 @@ class GptsApp(BaseModel):
|
||||
app_describe: Optional[str] = None
|
||||
team_mode: Optional[str] = None
|
||||
language: Optional[str] = None
|
||||
team_context: Optional[Union[str, AwelTeamContext]] = None
|
||||
team_context: Optional[Union[str, AWELTeamContext]] = None
|
||||
user_code: Optional[str] = None
|
||||
sys_code: Optional[str] = None
|
||||
is_collected: Optional[str] = None
|
||||
@@ -560,26 +560,26 @@ class GptsAppDao(BaseDao):
|
||||
return True
|
||||
|
||||
|
||||
def _parse_team_context(team_context: Optional[Union[str, AwelTeamContext]] = None):
|
||||
def _parse_team_context(team_context: Optional[Union[str, AWELTeamContext]] = None):
|
||||
"""
|
||||
parse team_context to str
|
||||
"""
|
||||
if isinstance(team_context, AwelTeamContext):
|
||||
if isinstance(team_context, AWELTeamContext):
|
||||
return team_context.json()
|
||||
return team_context
|
||||
|
||||
|
||||
def _load_team_context(
|
||||
team_mode: str = None, team_context: str = None
|
||||
) -> Union[str, AwelTeamContext]:
|
||||
) -> Union[str, AWELTeamContext]:
|
||||
"""
|
||||
load team_context to str or AwelTeamContext
|
||||
load team_context to str or AWELTeamContext
|
||||
"""
|
||||
if team_mode is not None:
|
||||
match team_mode:
|
||||
case TeamMode.AWEL_LAYOUT.value:
|
||||
try:
|
||||
awel_team_ctx = AwelTeamContext(**json.loads(team_context))
|
||||
awel_team_ctx = AWELTeamContext(**json.loads(team_context))
|
||||
return awel_team_ctx
|
||||
except Exception as ex:
|
||||
logger.info(
|
||||
|
@@ -1,18 +1,7 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
DateTime,
|
||||
Index,
|
||||
Integer,
|
||||
String,
|
||||
Text,
|
||||
and_,
|
||||
desc,
|
||||
func,
|
||||
or_,
|
||||
)
|
||||
from sqlalchemy import Column, DateTime, Index, Integer, String, Text, and_, desc, or_
|
||||
|
||||
from dbgpt.storage.metadata import BaseDao, Model
|
||||
|
||||
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, Integer, String, Text, UniqueConstraint
|
||||
|
||||
from dbgpt.agent.common.schema import Status
|
||||
from dbgpt.agent.core.schema import Status
|
||||
from dbgpt.storage.metadata import BaseDao, Model
|
||||
|
||||
|
||||
|
@@ -8,7 +8,7 @@ from typing import Any
|
||||
|
||||
from fastapi import UploadFile
|
||||
|
||||
from dbgpt.agent.common.schema import PluginStorageType
|
||||
from dbgpt.agent.core.schema import PluginStorageType
|
||||
from dbgpt.agent.plugin.plugins_util import scan_plugins, update_from_git
|
||||
from dbgpt.configs.model_config import PLUGINS_DIR
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, Generic, List, Optional, TypedDict, TypeVar
|
||||
from typing import Generic, List, Optional, TypeVar
|
||||
|
||||
from dbgpt._private.pydantic import BaseModel, Field
|
||||
|
||||
@@ -32,59 +32,6 @@ class PagenationResult(BaseModel, Generic[T]):
|
||||
}
|
||||
|
||||
|
||||
class AwelTeamContext(BaseModel):
|
||||
dag_id: str = Field(
|
||||
...,
|
||||
description="The unique id of dag",
|
||||
examples=["flow_dag_testflow_66d8e9d6-f32e-4540-a5bd-ea0648145d0e"],
|
||||
)
|
||||
uid: str = Field(
|
||||
default=None,
|
||||
description="The unique id of flow",
|
||||
examples=["66d8e9d6-f32e-4540-a5bd-ea0648145d0e"],
|
||||
)
|
||||
name: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The name of dag",
|
||||
)
|
||||
label: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The label of dag",
|
||||
)
|
||||
version: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The version of dag",
|
||||
)
|
||||
description: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The description of dag",
|
||||
)
|
||||
editable: bool = Field(
|
||||
default=False,
|
||||
description="is the dag is editable",
|
||||
examples=[True, False],
|
||||
)
|
||||
state: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The state of dag",
|
||||
)
|
||||
user_name: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The owner of current dag",
|
||||
)
|
||||
sys_code: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The system code of current dag",
|
||||
)
|
||||
flow_category: Optional[str] = Field(
|
||||
default="common",
|
||||
description="The flow category of current dag",
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
return self.dict()
|
||||
|
||||
|
||||
@dataclass
|
||||
class PluginHubFilter(BaseModel):
|
||||
name: str
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
|
||||
from dbgpt.agent.resource.resource_api import AgentResource
|
||||
from dbgpt.agent.resource.resource_db_api import ResourceDbClient
|
||||
from dbgpt.component import ComponentType
|
||||
from dbgpt.util.executor_utils import ExecutorFactory, blocking_func_to_async
|
||||
from dbgpt.util.tracer import root_tracer, trace
|
||||
from dbgpt.util.tracer import root_tracer
|
||||
|
||||
CFG = Config()
|
||||
|
||||
@@ -25,7 +25,9 @@ class DatasourceLoadClient(ResourceDbClient):
|
||||
conn = CFG.local_db_manager.get_connector(resource.value)
|
||||
return conn.db_type
|
||||
|
||||
async def a_get_schema_link(self, db: str, question: Optional[str] = None) -> str:
|
||||
async def get_schema_link(
|
||||
self, db: str, question: Optional[str] = None
|
||||
) -> Union[str, List[str]]:
|
||||
try:
|
||||
from dbgpt.rag.summary.db_summary_client import DBSummaryClient
|
||||
except ImportError:
|
||||
@@ -51,14 +53,14 @@ class DatasourceLoadClient(ResourceDbClient):
|
||||
|
||||
return table_infos
|
||||
|
||||
async def a_query_to_df(self, db: str, sql: str):
|
||||
async def query_to_df(self, db: str, sql: str):
|
||||
conn = CFG.local_db_manager.get_connector(db)
|
||||
return conn.run_to_df(sql)
|
||||
|
||||
async def a_query(self, db: str, sql: str):
|
||||
async def query(self, db: str, sql: str):
|
||||
conn = CFG.local_db_manager.get_connector(db)
|
||||
return conn.query_ex(sql)
|
||||
|
||||
async def a_run_sql(self, db: str, sql: str):
|
||||
async def run_sql(self, db: str, sql: str):
|
||||
conn = CFG.local_db_manager.get_connector(db)
|
||||
return conn.run(sql)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import logging
|
||||
from typing import Any, List, Optional
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.resource.resource_api import AgentResource
|
||||
@@ -13,10 +13,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KnowledgeSpaceLoadClient(ResourceKnowledgeClient):
|
||||
async def a_get_space_desc(self, space_name) -> str:
|
||||
async def get_space_desc(self, space_name) -> str:
|
||||
pass
|
||||
|
||||
async def a_get_kn(
|
||||
async def get_kn(
|
||||
self, space_name: str, question: Optional[str] = None
|
||||
) -> List[Chunk]:
|
||||
kn_retriver = KnowledgeSpaceRetriever(space_name=space_name)
|
||||
@@ -28,10 +28,8 @@ class KnowledgeSpaceLoadClient(ResourceKnowledgeClient):
|
||||
):
|
||||
kn_retriver = KnowledgeSpaceRetriever(space_name=space_name)
|
||||
|
||||
pass
|
||||
|
||||
async def get_data_introduce(
|
||||
self, resource: AgentResource, question: Optional[str] = None
|
||||
) -> str:
|
||||
docs = await self.a_get_kn(resource.value, question)
|
||||
) -> Union[str, List[str]]:
|
||||
docs = await self.get_kn(resource.value, question)
|
||||
return "\n".join([doc.content for doc in docs])
|
||||
|
@@ -1,14 +1,13 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import Optional
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.plugin.generator import PluginPromptGenerator
|
||||
from dbgpt.agent.resource.resource_plugin_api import ResourcePluginClient
|
||||
from dbgpt.component import ComponentType
|
||||
from dbgpt.serve.agent.hub.controller import ModulePlugin
|
||||
from dbgpt.util.executor_utils import ExecutorFactory, blocking_func_to_async
|
||||
from dbgpt.util.tracer import root_tracer, trace
|
||||
from dbgpt.util.executor_utils import ExecutorFactory
|
||||
|
||||
CFG = Config()
|
||||
|
||||
@@ -23,7 +22,7 @@ class PluginHubLoadClient(ResourcePluginClient):
|
||||
ComponentType.EXECUTOR_DEFAULT, ExecutorFactory
|
||||
).create()
|
||||
|
||||
async def a_load_plugin(
|
||||
async def load_plugin(
|
||||
self, value: str, plugin_generator: Optional[PluginPromptGenerator] = None
|
||||
) -> PluginPromptGenerator:
|
||||
logger.info(f"PluginHubLoadClient load plugin:{value}")
|
||||
|
@@ -1,58 +1,7 @@
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import List, Union
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TeamMode(Enum):
|
||||
AUTO_PLAN = "auto_plan"
|
||||
AWEL_LAYOUT = "awel_layout"
|
||||
SINGLE_AGENT = "singe_agent"
|
||||
|
||||
|
||||
def content_str(content: Union[str, List, None]) -> str:
|
||||
"""Converts `content` into a string format.
|
||||
|
||||
This function processes content that may be a string, a list of mixed text and image URLs, or None,
|
||||
and converts it into a string. Text is directly appended to the result string, while image URLs are
|
||||
represented by a placeholder image token. If the content is None, an empty string is returned.
|
||||
|
||||
Args:
|
||||
- content (Union[str, List, None]): The content to be processed. Can be a string, a list of dictionaries
|
||||
representing text and image URLs, or None.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the input content. Image URLs are replaced with an image token.
|
||||
|
||||
Note:
|
||||
- The function expects each dictionary in the list to have a "type" key that is either "text" or "image_url".
|
||||
For "text" type, the "text" key's value is appended to the result. For "image_url", an image token is appended.
|
||||
- This function is useful for handling content that may include both text and image references, especially
|
||||
in contexts where images need to be represented as placeholders.
|
||||
"""
|
||||
if content is None:
|
||||
return ""
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
if not isinstance(content, list):
|
||||
raise TypeError(f"content must be None, str, or list, but got {type(content)}")
|
||||
|
||||
rst = ""
|
||||
for item in content:
|
||||
if not isinstance(item, dict):
|
||||
raise TypeError(
|
||||
"Wrong content format: every element should be dict if the content is a list."
|
||||
)
|
||||
assert (
|
||||
"type" in item
|
||||
), "Wrong content format. Missing 'type' key in content's dict."
|
||||
if item["type"] == "text":
|
||||
rst += item["text"]
|
||||
elif item["type"] == "image_url":
|
||||
rst += "<image>"
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Wrong content format: unknown type {item['type']} within the content"
|
||||
)
|
||||
return rst
|
||||
|
@@ -1,271 +0,0 @@
|
||||
from abc import ABC
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from dbgpt.agent.agents.agent_new import Agent, AgentGenerateContext
|
||||
from dbgpt.agent.agents.agents_manage import agent_manage
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.agents.llm.llm import LLMConfig
|
||||
from dbgpt.core.awel import BranchFunc, BranchOperator, MapOperator
|
||||
from dbgpt.core.awel.flow import (
|
||||
IOField,
|
||||
OperatorCategory,
|
||||
OperatorType,
|
||||
Parameter,
|
||||
ResourceCategory,
|
||||
ViewMetadata,
|
||||
)
|
||||
from dbgpt.core.awel.trigger.base import Trigger
|
||||
from dbgpt.core.interface.message import ModelMessageRoleType
|
||||
from dbgpt.model.operators.llm_operator import MixinLLMOperator
|
||||
|
||||
from .agent_operator_resource import AwelAgent
|
||||
|
||||
|
||||
class BaseAgentOperator:
|
||||
"""The abstract operator for a Agent."""
|
||||
|
||||
SHARE_DATA_KEY_MODEL_NAME = "share_data_key_agent_name"
|
||||
|
||||
def __init__(self, agent: Optional[Agent] = None):
|
||||
self._agent: ConversableAgent = agent
|
||||
|
||||
@property
|
||||
def agent(self) -> Agent:
|
||||
"""Return the Agent."""
|
||||
if not self._agent:
|
||||
raise ValueError("agent is not set")
|
||||
return self._agent
|
||||
|
||||
|
||||
class AgentOperator(
|
||||
BaseAgentOperator, MapOperator[AgentGenerateContext, AgentGenerateContext], ABC
|
||||
):
|
||||
def __init__(self, agent: Agent, **kwargs):
|
||||
super().__init__(agent=agent)
|
||||
MapOperator.__init__(self, **kwargs)
|
||||
|
||||
async def map(self, input_value: AgentGenerateContext) -> AgentGenerateContext:
|
||||
now_rely_messages: List[Dict] = []
|
||||
|
||||
# Isolate the message delivery mechanism and pass it to the operator
|
||||
input_value.message["current_goal"] = (
|
||||
f"[{self._agent.name if self._agent.name else self._agent.profile}]:"
|
||||
+ input_value.message["content"]
|
||||
)
|
||||
###What was received was the User message
|
||||
human_message = input_value.message.copy()
|
||||
human_message["role"] = ModelMessageRoleType.HUMAN
|
||||
now_rely_messages.append(human_message)
|
||||
|
||||
###Send a message (no reply required) and pass the message content
|
||||
now_message = input_value.message
|
||||
if input_value.rely_messages and len(input_value.rely_messages) > 0:
|
||||
now_message = input_value.rely_messages[-1]
|
||||
await input_value.sender.a_send(
|
||||
now_message, self._agent, input_value.reviewer, False
|
||||
)
|
||||
|
||||
is_success, reply_message = await self._agent.a_generate_reply(
|
||||
recive_message=input_value.message,
|
||||
sender=input_value.sender,
|
||||
reviewer=input_value.reviewer,
|
||||
rely_messages=input_value.rely_messages,
|
||||
)
|
||||
|
||||
if not is_success:
|
||||
raise ValueError(
|
||||
f"The task failed at step {self._agent.profile} and the attempt to repair it failed. The final reason for failure:{reply_message['content']}!"
|
||||
)
|
||||
|
||||
###What is sent is an AI message
|
||||
ai_message = reply_message
|
||||
ai_message["role"] = ModelMessageRoleType.AI
|
||||
now_rely_messages.append(ai_message)
|
||||
|
||||
### Handle user goals and outcome dependencies
|
||||
return AgentGenerateContext(
|
||||
message=input_value.message,
|
||||
sender=self._agent,
|
||||
reviewer=input_value.reviewer,
|
||||
rely_messages=now_rely_messages, ## Default single step transfer of information
|
||||
silent=input_value.silent,
|
||||
)
|
||||
|
||||
|
||||
class AwelAgentOperator(
|
||||
MixinLLMOperator, MapOperator[AgentGenerateContext, AgentGenerateContext]
|
||||
):
|
||||
metadata = ViewMetadata(
|
||||
label="Agent Operator",
|
||||
name="agent_operator",
|
||||
category=OperatorCategory.AGENT,
|
||||
description="The Agent operator.",
|
||||
parameters=[
|
||||
Parameter.build_from(
|
||||
"Agent",
|
||||
"awel_agent",
|
||||
AwelAgent,
|
||||
description="The dbgpt agent.",
|
||||
),
|
||||
],
|
||||
inputs=[
|
||||
IOField.build_from(
|
||||
"Agent Operator Request",
|
||||
"agent_operator_request",
|
||||
AgentGenerateContext,
|
||||
"The Agent Operator request.",
|
||||
)
|
||||
],
|
||||
outputs=[
|
||||
IOField.build_from(
|
||||
"Agent Operator Output",
|
||||
"agent_operator_output",
|
||||
AgentGenerateContext,
|
||||
description="The Agent Operator output.",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def __init__(self, awel_agent: AwelAgent, **kwargs):
|
||||
MixinLLMOperator.__init__(self)
|
||||
MapOperator.__init__(self, **kwargs)
|
||||
self.awel_agent = awel_agent
|
||||
|
||||
async def map(
|
||||
self,
|
||||
input_value: AgentGenerateContext,
|
||||
) -> AgentGenerateContext:
|
||||
now_message = input_value.message
|
||||
agent = await self.get_agent(input_value)
|
||||
if agent.fixed_subgoal and len(agent.fixed_subgoal) > 0:
|
||||
# Isolate the message delivery mechanism and pass it to the operator
|
||||
input_value.message["current_goal"] = (
|
||||
f"[{agent.name if agent.name else agent.profile}]:"
|
||||
+ agent.fixed_subgoal
|
||||
)
|
||||
now_message["content"] = agent.fixed_subgoal
|
||||
else:
|
||||
# Isolate the message delivery mechanism and pass it to the operator
|
||||
input_value.message["current_goal"] = (
|
||||
f"[{agent.name if agent.name else agent.profile}]:"
|
||||
+ input_value.message["content"]
|
||||
)
|
||||
|
||||
now_rely_messages: List[Dict] = []
|
||||
###What was received was the User message
|
||||
human_message = input_value.message.copy()
|
||||
human_message["role"] = ModelMessageRoleType.HUMAN
|
||||
now_rely_messages.append(human_message)
|
||||
|
||||
###Send a message (no reply required) and pass the message content
|
||||
|
||||
if input_value.rely_messages and len(input_value.rely_messages) > 0:
|
||||
now_message = input_value.rely_messages[-1]
|
||||
await input_value.sender.a_send(now_message, agent, input_value.reviewer, False)
|
||||
|
||||
is_success, reply_message = await agent.a_generate_reply(
|
||||
recive_message=input_value.message,
|
||||
sender=input_value.sender,
|
||||
reviewer=input_value.reviewer,
|
||||
rely_messages=input_value.rely_messages,
|
||||
)
|
||||
|
||||
if not is_success:
|
||||
raise ValueError(
|
||||
f"The task failed at step {agent.profile} and the attempt to repair it failed. The final reason for failure:{reply_message['content']}!"
|
||||
)
|
||||
|
||||
###What is sent is an AI message
|
||||
ai_message = reply_message
|
||||
ai_message["role"] = ModelMessageRoleType.AI
|
||||
now_rely_messages.append(ai_message)
|
||||
|
||||
### Handle user goals and outcome dependencies
|
||||
return AgentGenerateContext(
|
||||
message=input_value.message,
|
||||
sender=agent,
|
||||
reviewer=input_value.reviewer,
|
||||
rely_messages=now_rely_messages, ## Default single step transfer of information
|
||||
silent=input_value.silent,
|
||||
memory=input_value.memory,
|
||||
agent_context=input_value.agent_context,
|
||||
resource_loader=input_value.resource_loader,
|
||||
llm_client=input_value.llm_client,
|
||||
round_index=agent.consecutive_auto_reply_counter,
|
||||
)
|
||||
|
||||
async def get_agent(
|
||||
self,
|
||||
input_value: AgentGenerateContext,
|
||||
):
|
||||
### agent build
|
||||
agent_cls: ConversableAgent = agent_manage.get_by_name(
|
||||
self.awel_agent.agent_profile
|
||||
)
|
||||
llm_config = self.awel_agent.llm_config
|
||||
|
||||
if not llm_config:
|
||||
if input_value.llm_client:
|
||||
llm_config = LLMConfig(llm_client=input_value.llm_client)
|
||||
else:
|
||||
llm_config = LLMConfig(llm_client=self.llm_client)
|
||||
else:
|
||||
if not llm_config.llm_client:
|
||||
if input_value.llm_client:
|
||||
llm_config.llm_client = input_value.llm_client
|
||||
else:
|
||||
llm_config.llm_client = self.llm_client
|
||||
|
||||
kwargs = {}
|
||||
if self.awel_agent.role_name:
|
||||
kwargs["name"] = self.awel_agent.role_name
|
||||
if self.awel_agent.fixed_subgoal:
|
||||
kwargs["fixed_subgoal"] = self.awel_agent.fixed_subgoal
|
||||
|
||||
agent = (
|
||||
await agent_cls(**kwargs)
|
||||
.bind(input_value.memory)
|
||||
.bind(llm_config)
|
||||
.bind(input_value.agent_context)
|
||||
.bind(self.awel_agent.resources)
|
||||
.bind(input_value.resource_loader)
|
||||
.build()
|
||||
)
|
||||
|
||||
return agent
|
||||
|
||||
|
||||
class AgentDummyTrigger(Trigger):
|
||||
"""Http trigger for AWEL.
|
||||
|
||||
Http trigger is used to trigger a DAG by http request.
|
||||
"""
|
||||
|
||||
metadata = ViewMetadata(
|
||||
label="Agent Trigger",
|
||||
name="agent_trigger",
|
||||
category=OperatorCategory.AGENT,
|
||||
operator_type=OperatorType.INPUT,
|
||||
description="Trigger your workflow by agent",
|
||||
inputs=[],
|
||||
parameters=[],
|
||||
outputs=[
|
||||
IOField.build_from(
|
||||
"Agent Operator Context",
|
||||
"agent_operator_context",
|
||||
AgentGenerateContext,
|
||||
description="The Agent Operator output.",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""Initialize a HttpTrigger."""
|
||||
super().__init__(**kwargs)
|
||||
|
||||
async def trigger(self, **kwargs) -> None:
|
||||
"""Trigger the DAG. Not used in HttpTrigger."""
|
||||
raise NotImplementedError("Dummy trigger does not support trigger.")
|
@@ -1,190 +0,0 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from dbgpt._private.pydantic import root_validator
|
||||
from dbgpt.agent.agents.agents_manage import agent_manage
|
||||
from dbgpt.agent.agents.llm.llm import LLMConfig, LLMStrategyType
|
||||
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
|
||||
from dbgpt.core import LLMClient
|
||||
from dbgpt.core.awel.flow import (
|
||||
FunctionDynamicOptions,
|
||||
OptionValue,
|
||||
Parameter,
|
||||
ResourceCategory,
|
||||
register_resource,
|
||||
)
|
||||
|
||||
|
||||
@register_resource(
|
||||
label="Awel Agent Resource",
|
||||
name="agent_operator_resource",
|
||||
description="The Agent Resource.",
|
||||
category=ResourceCategory.AGENT,
|
||||
parameters=[
|
||||
Parameter.build_from(
|
||||
label="Agent Resource Type",
|
||||
name="agent_resource_type",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
options=[
|
||||
OptionValue(label=item.name, name=item.value, value=item.value)
|
||||
for item in ResourceType
|
||||
],
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent Resource Name",
|
||||
name="agent_resource_name",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent resource name.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent Resource Value",
|
||||
name="agent_resource_value",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent resource value.",
|
||||
),
|
||||
],
|
||||
)
|
||||
class AwelAgentResource(AgentResource):
|
||||
@root_validator(pre=True)
|
||||
def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Pre fill the agent ResourceType"""
|
||||
|
||||
name = values.pop("agent_resource_name")
|
||||
type = values.pop("agent_resource_type")
|
||||
value = values.pop("agent_resource_value")
|
||||
|
||||
values["name"] = name
|
||||
values["type"] = ResourceType(type)
|
||||
values["value"] = value
|
||||
|
||||
return values
|
||||
|
||||
|
||||
@register_resource(
|
||||
label="Awel Agent LLM Config",
|
||||
name="agent_operator_llm_config",
|
||||
description="The Agent LLM Config.",
|
||||
category=ResourceCategory.AGENT,
|
||||
parameters=[
|
||||
Parameter.build_from(
|
||||
"LLM Client",
|
||||
"llm_client",
|
||||
LLMClient,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The LLM Client.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent LLM Strategy",
|
||||
name="llm_strategy",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
options=[
|
||||
OptionValue(label=item.name, name=item.value, value=item.value)
|
||||
for item in LLMStrategyType
|
||||
],
|
||||
description="The Agent LLM Strategy.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent LLM Strategy Value",
|
||||
name="strategy_context",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent LLM Strategy Value.",
|
||||
),
|
||||
],
|
||||
)
|
||||
class AwelAgentConfig(LLMConfig):
|
||||
@root_validator(pre=True)
|
||||
def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Pre fill the agent ResourceType"""
|
||||
return values
|
||||
|
||||
|
||||
def _agent_resource_option_values() -> List[OptionValue]:
|
||||
return [
|
||||
OptionValue(label=item["name"], name=item["name"], value=item["name"])
|
||||
for item in agent_manage.list_agents()
|
||||
]
|
||||
|
||||
|
||||
@register_resource(
|
||||
label="Awel Layout Agent",
|
||||
name="agent_operator_agent",
|
||||
description="The Agent to build the Agent Operator.",
|
||||
category=ResourceCategory.AGENT,
|
||||
parameters=[
|
||||
Parameter.build_from(
|
||||
label="Agent Profile",
|
||||
name="agent_profile",
|
||||
type=str,
|
||||
description="Which agent want use.",
|
||||
options=FunctionDynamicOptions(func=_agent_resource_option_values),
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Role Name",
|
||||
name="role_name",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent role name.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Fixed Gogal",
|
||||
name="fixed_subgoal",
|
||||
type=str,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent fixed gogal.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent Resource",
|
||||
name="agent_resource",
|
||||
type=AwelAgentResource,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent resource.",
|
||||
),
|
||||
Parameter.build_from(
|
||||
label="Agent LLM Config",
|
||||
name="agent_llm_Config",
|
||||
type=AwelAgentConfig,
|
||||
optional=True,
|
||||
default=None,
|
||||
description="The agent llm config.",
|
||||
),
|
||||
],
|
||||
)
|
||||
class AwelAgent(BaseModel):
|
||||
agent_profile: str
|
||||
role_name: Optional[str] = None
|
||||
llm_config: Optional[LLMConfig] = None
|
||||
resources: List[AgentResource] = Field(default_factory=list)
|
||||
fixed_subgoal: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
@root_validator(pre=True)
|
||||
def pre_fill(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Pre fill the agent ResourceType"""
|
||||
|
||||
resource = values.pop("agent_resource")
|
||||
llm_config = values.pop("agent_llm_Config")
|
||||
|
||||
if resource is not None:
|
||||
values["resources"] = [resource]
|
||||
|
||||
if llm_config is not None:
|
||||
values["llm_config"] = llm_config
|
||||
|
||||
return values
|
@@ -1,73 +0,0 @@
|
||||
import logging
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from dbgpt.agent.actions.action import ActionOutput, T
|
||||
from dbgpt.agent.agents.agent import Agent, AgentContext, AgentGenerateContext
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.agents.base_team import ManagerAgent
|
||||
from dbgpt.core.awel import DAG
|
||||
from dbgpt.serve.agent.team.layout.agent_operator import AgentOperator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AwelLayoutChatManager(ManagerAgent):
|
||||
profile: str = "AwelManager"
|
||||
goal: str = (
|
||||
"Promote and solve user problems according to the process arranged by Awel."
|
||||
)
|
||||
constraints: List[str] = []
|
||||
desc: str = goal
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
async def a_act(
|
||||
self,
|
||||
message: Optional[str],
|
||||
sender: Optional[ConversableAgent] = None,
|
||||
reviewer: Optional[ConversableAgent] = None,
|
||||
) -> Optional[ActionOutput]:
|
||||
try:
|
||||
# TODO Use programmed DAG
|
||||
last_node: AgentOperator = None
|
||||
with DAG(
|
||||
f"layout_agents_{self.agent_context.gpts_app_name}_{self.agent_context.conv_id}"
|
||||
) as dag:
|
||||
for agent in self.agents:
|
||||
now_node = AgentOperator(agent=agent)
|
||||
if not last_node:
|
||||
last_node = now_node
|
||||
else:
|
||||
last_node >> now_node
|
||||
last_node = now_node
|
||||
|
||||
start_message_context: AgentGenerateContext = AgentGenerateContext(
|
||||
message={
|
||||
"content": message,
|
||||
"current_goal": message,
|
||||
},
|
||||
sender=self,
|
||||
reviewer=reviewer,
|
||||
)
|
||||
final_generate_context: AgentGenerateContext = await last_node.call(
|
||||
call_data=start_message_context
|
||||
)
|
||||
last_message = final_generate_context.rely_messages[-1]
|
||||
|
||||
last_agent = last_node.agent
|
||||
await last_agent.a_send(
|
||||
last_message, self, start_message_context.reviewer, False
|
||||
)
|
||||
|
||||
return ActionOutput(
|
||||
content=last_message.get("content", None),
|
||||
view=last_message.get("view", None),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(f"DAG run failed!{str(e)}")
|
||||
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content=f"Failed to complete goal! {str(e)}",
|
||||
)
|
@@ -1,96 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.actions.action import ActionOutput, T
|
||||
from dbgpt.agent.agents.agent_new import Agent, AgentContext, AgentGenerateContext
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.agents.base_team import ManagerAgent
|
||||
from dbgpt.core.awel import DAG
|
||||
from dbgpt.core.awel.dag.dag_manager import DAGManager
|
||||
from dbgpt.serve.agent.model import AwelTeamContext
|
||||
from dbgpt.serve.agent.team.layout.agent_operator import AwelAgentOperator
|
||||
from dbgpt.serve.flow.api.endpoints import get_service as get_flow_service
|
||||
from dbgpt.serve.flow.service.service import Service as FlowService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CFG = Config()
|
||||
|
||||
|
||||
class AwelLayoutChatNewManager(ManagerAgent):
|
||||
dag: AwelTeamContext = Field(...)
|
||||
profile: str = "AwelNewManager"
|
||||
goal: str = (
|
||||
"Promote and solve user problems according to the process arranged by Awel."
|
||||
)
|
||||
constraints: List[str] = []
|
||||
desc: str = goal
|
||||
|
||||
@validator("dag")
|
||||
def check_dag(cls, value):
|
||||
assert value is not None and value != "", "dag must not be empty"
|
||||
return value
|
||||
|
||||
async def _a_process_received_message(self, message: Optional[Dict], sender: Agent):
|
||||
pass
|
||||
|
||||
async def a_act(
|
||||
self,
|
||||
message: Optional[str],
|
||||
sender: Optional[ConversableAgent] = None,
|
||||
reviewer: Optional[ConversableAgent] = None,
|
||||
) -> Optional[ActionOutput]:
|
||||
try:
|
||||
flow_service: FlowService = get_flow_service()
|
||||
flow = flow_service.get({"uid": self.dag.uid})
|
||||
_dag_manager = DAGManager.get_instance(CFG.SYSTEM_APP)
|
||||
|
||||
dag_id = flow.dag_id
|
||||
|
||||
agent_dag = _dag_manager.dag_map[dag_id]
|
||||
if agent_dag is None:
|
||||
raise ValueError(
|
||||
f"The configured flow cannot be found![{self.dag.name}]"
|
||||
)
|
||||
last_node: AwelAgentOperator = agent_dag.leaf_nodes[0]
|
||||
|
||||
start_message_context: AgentGenerateContext = AgentGenerateContext(
|
||||
message={
|
||||
"content": message,
|
||||
"current_goal": message,
|
||||
},
|
||||
sender=sender,
|
||||
reviewer=reviewer,
|
||||
memory=self.memory,
|
||||
agent_context=self.agent_context,
|
||||
resource_loader=self.resource_loader,
|
||||
llm_client=self.llm_config.llm_client,
|
||||
)
|
||||
final_generate_context: AgentGenerateContext = await last_node.call(
|
||||
call_data=start_message_context
|
||||
)
|
||||
last_message = final_generate_context.rely_messages[-1]
|
||||
|
||||
last_agent = await last_node.get_agent(final_generate_context)
|
||||
last_agent.consecutive_auto_reply_counter = (
|
||||
final_generate_context.round_index
|
||||
)
|
||||
await last_agent.a_send(
|
||||
last_message, sender, start_message_context.reviewer, False
|
||||
)
|
||||
|
||||
return ActionOutput(
|
||||
content=last_message.get("content", None),
|
||||
view=last_message.get("view", None),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(f"DAG run failed!{str(e)}")
|
||||
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content=f"Failed to complete goal! {str(e)}",
|
||||
)
|
@@ -1,121 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from dbgpt.agent.actions.action import Action, ActionOutput, T
|
||||
from dbgpt.agent.agents.agent import AgentContext
|
||||
from dbgpt.agent.common.schema import Status
|
||||
from dbgpt.agent.memory.base import GptsPlan
|
||||
from dbgpt.agent.memory.gpts_memory import GptsPlansMemory
|
||||
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
|
||||
from dbgpt.vis.tags.vis_agent_plans import Vis, VisAgentPlans
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PlanInput(BaseModel):
|
||||
serial_number: int = Field(
|
||||
0,
|
||||
description="子任务的步骤编号",
|
||||
)
|
||||
agent: str = Field(..., description="用来完成当前步骤的智能代理")
|
||||
content: str = Field(..., description="当前步骤的任务内容,确保可以被智能代理执行")
|
||||
rely: str = Field(..., description="当前任务执行依赖的其他任务serial_number, 如:1,2,3, 无依赖为空")
|
||||
|
||||
|
||||
class PlanAction(Action[List[PlanInput]]):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._render_protocal = VisAgentPlans()
|
||||
|
||||
@property
|
||||
def resource_need(self) -> Optional[ResourceType]:
|
||||
return None
|
||||
|
||||
@property
|
||||
def render_protocal(self) -> Optional[Vis]:
|
||||
return self._render_protocal
|
||||
|
||||
@property
|
||||
def out_model_type(self):
|
||||
return List[PlanInput]
|
||||
|
||||
async def a_run(
|
||||
self,
|
||||
ai_message: str,
|
||||
context: AgentContext,
|
||||
plans_memory: GptsPlansMemory,
|
||||
resource: Optional[AgentResource] = None,
|
||||
rely_action_out: Optional[ActionOutput] = None,
|
||||
need_vis_render: bool = True,
|
||||
) -> ActionOutput:
|
||||
try:
|
||||
param: List[PlanInput] = self._input_convert(ai_message, List[PlanInput])
|
||||
except Exception as e:
|
||||
logger.exception((str(e)))
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content="The requested correctly structured answer could not be found.",
|
||||
)
|
||||
|
||||
try:
|
||||
rensponse_succ = True
|
||||
plan_objects = []
|
||||
try:
|
||||
for item in param:
|
||||
plan = GptsPlan(
|
||||
conv_id=context.conv_id,
|
||||
sub_task_num=item.serial_number,
|
||||
sub_task_content=item.content,
|
||||
)
|
||||
plan.resource_name = ""
|
||||
plan.max_retry_times = context.max_retry_round
|
||||
plan.sub_task_agent = item.agent
|
||||
plan.sub_task_title = item.content
|
||||
plan.rely = item.rely
|
||||
plan.retry_times = 0
|
||||
plan.status = Status.TODO.value
|
||||
plan_objects.append(plan)
|
||||
|
||||
plans_memory.remove_by_conv_id(context.conv_id)
|
||||
plans_memory.batch_save(plan_objects)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(str(e))
|
||||
fail_reason = f"The generated plan cannot be stored, reason: {str(e)}. Please check whether it is a problem with the plan content. If so, please regenerate the correct plan. If not, please return 'TERMINATE'."
|
||||
rensponse_succ = False
|
||||
|
||||
if rensponse_succ:
|
||||
plan_content = []
|
||||
mk_plans = []
|
||||
for item in param:
|
||||
plan_content.append(
|
||||
{
|
||||
"name": item.content,
|
||||
"num": item.serial_number,
|
||||
"status": Status.TODO.value,
|
||||
"agent": item.agent,
|
||||
"rely": item.rely,
|
||||
"markdown": "",
|
||||
}
|
||||
)
|
||||
mk_plans.append(
|
||||
f"- {item.serial_number}.{item.content}[{item.agent}]"
|
||||
)
|
||||
|
||||
# view = await self.render_protocal.display(content=plan_content)
|
||||
view = "\n".join(mk_plans)
|
||||
return ActionOutput(
|
||||
is_exe_success=True,
|
||||
content=ai_message,
|
||||
view=view,
|
||||
)
|
||||
else:
|
||||
raise ValueError(fail_reason)
|
||||
except Exception as e:
|
||||
logger.exception("Plan Action Run Failed!")
|
||||
return ActionOutput(
|
||||
is_exe_success=False, content=f"Plan action run failed!{str(e)}"
|
||||
)
|
@@ -1,93 +0,0 @@
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from dbgpt._private.config import Config
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.resource.resource_api import AgentResource
|
||||
|
||||
from .plan_action import PlanAction
|
||||
|
||||
CFG = Config()
|
||||
|
||||
|
||||
class PlannerAgent(ConversableAgent):
|
||||
"""Planner agent, realizing task goal planning decomposition through LLM"""
|
||||
|
||||
agents: List[ConversableAgent] = Field(default_factory=list)
|
||||
|
||||
profile: str = "Planner"
|
||||
goal: str = "理解下面每个智能代理和他们的能力,使用给出的资源,通过协调智能代理来解决用户问题。 请发挥你LLM的知识和理解能力,理解用户问题的意图和目标,生成一个可以在没有用户帮助下,由智能代理协作完成目标的任务计划。"
|
||||
expand_prompt: str = """可用智能代理:
|
||||
{agents}
|
||||
"""
|
||||
constraints: List[str] = [
|
||||
"任务计划的每个步骤都应该是为了推进解决用户目标而存在,不要生成无意义的任务步骤,确保每个步骤内目标明确内容完整。",
|
||||
"关注任务计划每个步骤的依赖关系和逻辑,被依赖步骤要考虑被依赖的数据,是否能基于当前目标得到,如果不能请在目标中提示要生成被依赖数据。",
|
||||
"每个步骤都是一个独立可完成的目标,一定要确保逻辑和信息完整,不要出现类似:'Analyze the retrieved issues data'这样目标不明确,不知道具体要分析啥内容的步骤",
|
||||
"请确保只使用上面提到的智能代理,并且可以只使用其中需要的部分,严格根据描述能力和限制分配给合适的步骤,每个智能代理都可以重复使用。",
|
||||
"根据用户目标的实际需要使用提供的资源来协助生成计划步骤,不要使用不需要的资源。",
|
||||
"每个步骤最好只使用一种资源完成一个子目标,如果当前目标可以分解为同类型的多个子任务,可以生成相互不依赖的并行任务。",
|
||||
"数据资源可以被合适的智能代理加载使用,不用考虑数据资源的加载链接问题",
|
||||
"尽量合并有顺序依赖的连续相同步骤,如果用户目标无拆分必要,可以生成内容为用户目标的单步任务。",
|
||||
"仔细检查计划,确保计划完整的包含了用户问题所涉及的所有信息,并且最终能完成目标,确认每个步骤是否包含了需要用到的资源信息,如URL、资源名等. ",
|
||||
]
|
||||
desc: str = "你是一个任务规划专家!可以协调智能代理,分配资源完成复杂的任务目标。"
|
||||
|
||||
examples = """
|
||||
user:help me build a sales report summarizing our key metrics and trends
|
||||
assisant:[
|
||||
{{
|
||||
"serial_number": "1",
|
||||
"agent": "DataScientist",
|
||||
"content": "Retrieve total sales, average sales, and number of transactions grouped by "product_category"'.",
|
||||
"rely": ""
|
||||
}},
|
||||
{{
|
||||
"serial_number": "2",
|
||||
"agent": "DataScientist",
|
||||
"content": "Retrieve monthly sales and transaction number trends.",
|
||||
"rely": ""
|
||||
}},
|
||||
{{
|
||||
"serial_number": "3",
|
||||
"agent": "Reporter",
|
||||
"content": "Integrate analytical data into the format required to build sales reports.",
|
||||
"rely": "1,2"
|
||||
}}
|
||||
]
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._init_actions([PlanAction])
|
||||
|
||||
def _init_reply_message(self, recive_message):
|
||||
reply_message = super()._init_reply_message(recive_message)
|
||||
reply_message["context"] = {
|
||||
"agents": "\n".join(
|
||||
[f"- {item.profile}:{item.desc}" for item in self.agents]
|
||||
),
|
||||
}
|
||||
return reply_message
|
||||
|
||||
@staticmethod
|
||||
def get_unique_resources_codes(resource: AgentResource) -> str:
|
||||
return resource.name + "_" + resource.type.value + "_" + resource.value
|
||||
|
||||
def bind_agents(self, agents: List[ConversableAgent]) -> ConversableAgent:
|
||||
self.agents = agents
|
||||
unique_resources = set()
|
||||
for agent in self.agents:
|
||||
if agent.resources and len(agent.resources) > 0:
|
||||
for resource in agent.resources:
|
||||
if (
|
||||
self.get_unique_resources_codes(resource)
|
||||
not in unique_resources
|
||||
):
|
||||
unique_resources.add(self.get_unique_resources_codes(resource))
|
||||
self.resources.append(resource)
|
||||
return self
|
||||
|
||||
def prepare_act_param(self) -> Optional[Dict]:
|
||||
return {"context": self.agent_context, "plans_memory": self.memory.plans_memory}
|
@@ -1,248 +0,0 @@
|
||||
import logging
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from dbgpt.agent.actions.action import ActionOutput
|
||||
from dbgpt.agent.agents.agent_new import Agent
|
||||
from dbgpt.agent.agents.agents_manage import mentioned_agents, participant_roles
|
||||
from dbgpt.agent.agents.base_agent_new import ConversableAgent
|
||||
from dbgpt.agent.agents.base_team import ManagerAgent
|
||||
from dbgpt.agent.common.schema import Status
|
||||
from dbgpt.agent.memory.base import GptsPlan
|
||||
from dbgpt.core.interface.message import ModelMessageRoleType
|
||||
|
||||
from .planner_agent import PlannerAgent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AutoPlanChatManager(ManagerAgent):
|
||||
"""(In preview) A chat manager agent that can manage a team chat of multiple agents."""
|
||||
|
||||
profile: str = "PlanManager"
|
||||
goal: str = "Advance the task plan generated by the planning agent. If the plan does not pre-allocate an agent, it needs to be coordinated with the appropriate agent to complete."
|
||||
constraints: List[str] = []
|
||||
desc: str = "Advance the task plan generated by the planning agent."
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
async def a_process_rely_message(
|
||||
self, conv_id: str, now_plan: GptsPlan, speaker: ConversableAgent
|
||||
):
|
||||
rely_prompt = None
|
||||
rely_messages: List[Dict] = []
|
||||
|
||||
if now_plan.rely and len(now_plan.rely) > 0:
|
||||
rely_tasks_list = now_plan.rely.split(",")
|
||||
rely_tasks = self.memory.plans_memory.get_by_conv_id_and_num(
|
||||
conv_id, rely_tasks_list
|
||||
)
|
||||
if rely_tasks:
|
||||
rely_prompt = "Read the result data of the dependent steps in the above historical message to complete the current goal:"
|
||||
for rely_task in rely_tasks:
|
||||
rely_messages.append(
|
||||
{
|
||||
"content": rely_task.sub_task_content,
|
||||
"role": ModelMessageRoleType.HUMAN,
|
||||
}
|
||||
)
|
||||
rely_messages.append(
|
||||
{
|
||||
"content": rely_task.result,
|
||||
"role": ModelMessageRoleType.AI,
|
||||
}
|
||||
)
|
||||
return rely_prompt, rely_messages
|
||||
|
||||
def select_speaker_msg(self, agents: List[Agent]):
|
||||
"""Return the message for selecting the next speaker."""
|
||||
return f"""You are in a role play game. The following roles are available:
|
||||
{participant_roles(agents)}.
|
||||
Read the following conversation.
|
||||
Then select the next role from {[agent.name for agent in agents]} to play. The role can be selected repeatedly.Only return the role."""
|
||||
|
||||
async def a_select_speaker(
|
||||
self,
|
||||
last_speaker: Agent,
|
||||
selector: ConversableAgent,
|
||||
now_goal_context: str = None,
|
||||
pre_allocated: str = None,
|
||||
):
|
||||
"""Select the next speaker."""
|
||||
|
||||
agents = self.agents
|
||||
|
||||
if pre_allocated:
|
||||
# Preselect speakers
|
||||
logger.info(f"Preselect speakers:{pre_allocated}")
|
||||
name = pre_allocated
|
||||
model = None
|
||||
else:
|
||||
# auto speaker selection
|
||||
# TODO selector a_thinking It has been overwritten and cannot be used.
|
||||
final, name, model = await selector.a_thinking(
|
||||
messages=[
|
||||
{
|
||||
"role": ModelMessageRoleType.HUMAN,
|
||||
"content": f"""Read and understand the following task content and assign the appropriate role to complete the task.
|
||||
Task content: {now_goal_context}
|
||||
select the role from: {[agent.name for agent in agents]},
|
||||
Please only return the role, such as: {agents[0].name}""",
|
||||
}
|
||||
],
|
||||
prompt=self.select_speaker_msg(agents),
|
||||
)
|
||||
if not final:
|
||||
raise ValueError("Unable to select next speaker!")
|
||||
|
||||
# If exactly one agent is mentioned, use it. Otherwise, leave the OAI response unmodified
|
||||
mentions = mentioned_agents(name, agents)
|
||||
if len(mentions) == 1:
|
||||
name = next(iter(mentions))
|
||||
else:
|
||||
logger.warning(
|
||||
f"GroupChat select_speaker failed to resolve the next speaker's name. This is because the speaker selection OAI call returned:\n{name}"
|
||||
)
|
||||
|
||||
# Return the result
|
||||
try:
|
||||
return self.agent_by_name(name), model
|
||||
except Exception as e:
|
||||
logger.exception(f"auto select speaker failed!{str(e)}")
|
||||
raise ValueError("Unable to select next speaker!")
|
||||
|
||||
async def a_act(
|
||||
self,
|
||||
message: Optional[str],
|
||||
sender: Optional[ConversableAgent] = None,
|
||||
reviewer: Optional[ConversableAgent] = None,
|
||||
) -> Optional[ActionOutput]:
|
||||
speaker = sender
|
||||
final_message = message
|
||||
for i in range(self.max_round):
|
||||
plans = self.memory.plans_memory.get_by_conv_id(self.agent_context.conv_id)
|
||||
|
||||
if not plans or len(plans) <= 0:
|
||||
if i > 3:
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content="Retrying 3 times based on current application resources still fails to build a valid plan!",
|
||||
)
|
||||
planner: ConversableAgent = (
|
||||
await PlannerAgent()
|
||||
.bind(self.memory)
|
||||
.bind(self.agent_context)
|
||||
.bind(self.llm_config)
|
||||
.bind(self.resource_loader)
|
||||
.bind_agents(self.agents)
|
||||
.build()
|
||||
)
|
||||
|
||||
is_success, plan_message = await planner.a_generate_reply(
|
||||
recive_message={"content": message}, sender=self, reviewer=reviewer
|
||||
)
|
||||
await planner.a_send(
|
||||
message=plan_message, recipient=self, request_reply=False
|
||||
)
|
||||
else:
|
||||
todo_plans = [
|
||||
plan
|
||||
for plan in plans
|
||||
if plan.state in [Status.TODO.value, Status.RETRYING.value]
|
||||
]
|
||||
if not todo_plans or len(todo_plans) <= 0:
|
||||
### The plan has been fully executed and a success message is sent to the user.
|
||||
# complete
|
||||
return ActionOutput(
|
||||
is_exe_success=True,
|
||||
content=final_message, # work results message
|
||||
)
|
||||
else:
|
||||
try:
|
||||
now_plan: GptsPlan = todo_plans[0]
|
||||
current_goal_message = {
|
||||
"content": now_plan.sub_task_content,
|
||||
"current_goal": now_plan.sub_task_content,
|
||||
"context": {
|
||||
"plan_task": now_plan.sub_task_content,
|
||||
"plan_task_num": now_plan.sub_task_num,
|
||||
},
|
||||
}
|
||||
# select the next speaker
|
||||
speaker, model = await self.a_select_speaker(
|
||||
speaker,
|
||||
self,
|
||||
now_plan.sub_task_content,
|
||||
now_plan.sub_task_agent,
|
||||
)
|
||||
# Tell the speaker the dependent history information
|
||||
rely_prompt, rely_messages = await self.a_process_rely_message(
|
||||
conv_id=self.agent_context.conv_id,
|
||||
now_plan=now_plan,
|
||||
speaker=speaker,
|
||||
)
|
||||
if rely_prompt:
|
||||
current_goal_message["content"] = (
|
||||
rely_prompt + current_goal_message["content"]
|
||||
)
|
||||
|
||||
await self.a_send(
|
||||
message=current_goal_message,
|
||||
recipient=speaker,
|
||||
reviewer=reviewer,
|
||||
request_reply=False,
|
||||
)
|
||||
is_success, reply_message = await speaker.a_generate_reply(
|
||||
recive_message=current_goal_message,
|
||||
sender=self,
|
||||
reviewer=reviewer,
|
||||
rely_messages=rely_messages,
|
||||
)
|
||||
await speaker.a_send(
|
||||
reply_message, self, reviewer, request_reply=False
|
||||
)
|
||||
|
||||
plan_result = ""
|
||||
final_message = reply_message["content"]
|
||||
if is_success:
|
||||
if reply_message:
|
||||
action_report = reply_message.get("action_report", None)
|
||||
if action_report:
|
||||
plan_result = action_report.get("content", "")
|
||||
final_message = action_report["view"]
|
||||
|
||||
### The current planned Agent generation verification is successful
|
||||
##Plan executed successfully
|
||||
self.memory.plans_memory.complete_task(
|
||||
self.agent_context.conv_id,
|
||||
now_plan.sub_task_num,
|
||||
plan_result,
|
||||
)
|
||||
else:
|
||||
plan_result = reply_message["content"]
|
||||
self.memory.plans_memory.update_task(
|
||||
self.agent_context.conv_id,
|
||||
now_plan.sub_task_num,
|
||||
Status.FAILED.value,
|
||||
now_plan.retry_times + 1,
|
||||
speaker.name,
|
||||
"",
|
||||
plan_result,
|
||||
)
|
||||
return ActionOutput(
|
||||
is_exe_success=False, content=plan_result
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"An exception was encountered during the execution of the current plan step.{str(e)}"
|
||||
)
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content=f"An exception was encountered during the execution of the current plan step.{str(e)}",
|
||||
)
|
||||
return ActionOutput(
|
||||
is_exe_success=False,
|
||||
content=f"Maximum number of dialogue rounds exceeded.{self.max_round}",
|
||||
)
|
@@ -1,6 +1,6 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from dbgpt._private.pydantic import BaseModel, Field
|
||||
|
||||
from ..config import SERVE_APP_NAME_HUMP
|
||||
|
||||
|
@@ -157,7 +157,7 @@ class Service(BaseService[ServeEntity, ServeRequest, ServerResponse]):
|
||||
try:
|
||||
if state == State.DEPLOYED:
|
||||
# Register the DAG
|
||||
self.dag_manager.register_dag(dag)
|
||||
self.dag_manager.register_dag(dag, request.uid)
|
||||
# Update state to RUNNING
|
||||
request.state = State.RUNNING
|
||||
request.error_message = ""
|
||||
@@ -199,7 +199,7 @@ class Service(BaseService[ServeEntity, ServeRequest, ServerResponse]):
|
||||
entity.version == "0.1.0" and entity.state == State.INITIALIZING
|
||||
):
|
||||
# Register the DAG
|
||||
self.dag_manager.register_dag(dag)
|
||||
self.dag_manager.register_dag(dag, entity.uid)
|
||||
# Update state to RUNNING
|
||||
entity.state = State.RUNNING
|
||||
entity.error_message = ""
|
||||
|
@@ -1,8 +1,8 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import File, UploadFile
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from dbgpt._private.pydantic import BaseModel, Field
|
||||
from dbgpt.rag.chunk_manager import ChunkParameters
|
||||
|
||||
from ..config import SERVE_APP_NAME_HUMP
|
||||
|
Reference in New Issue
Block a user