Native data AI application framework based on AWEL+AGENT (#1152)

Co-authored-by: Fangyin Cheng <staneyffer@gmail.com>
Co-authored-by: lcx01800250 <lcx01800250@alibaba-inc.com>
Co-authored-by: licunxing <864255598@qq.com>
Co-authored-by: Aralhi <xiaoping0501@gmail.com>
Co-authored-by: xuyuan23 <643854343@qq.com>
Co-authored-by: aries_ckt <916701291@qq.com>
Co-authored-by: hzh97 <2976151305@qq.com>
This commit is contained in:
明天
2024-02-07 17:43:27 +08:00
committed by GitHub
parent dbb9ac83b1
commit d5afa6e206
328 changed files with 22606 additions and 3282 deletions

View File

@@ -4,7 +4,7 @@ import logging
import uuid
from abc import ABC
from collections import defaultdict
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Type
from fastapi import APIRouter, Body
from fastapi.responses import StreamingResponse
@@ -12,9 +12,12 @@ from fastapi.responses import StreamingResponse
from dbgpt._private.config import Config
from dbgpt.agent.agents.agent 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.memory.gpts_memory import GptsMemory
from dbgpt.agent.resource.resource_loader import ResourceLoader
from dbgpt.app.openapi.api_view_model import Result
from dbgpt.app.scene.base import ChatScene
from dbgpt.component import BaseComponent, ComponentType, SystemApp
@@ -26,12 +29,15 @@ 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 ..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 import AwelLayoutChatManager
from ..team.layout.team_awel_layout_new import AwelLayoutChatNewManager
from .db_gpts_memory import MetaDbGptsMessageMemory, MetaDbGptsPlansMemory
from .dbgpts import DbGptsInstance
CFG = Config()
@@ -70,8 +76,9 @@ class MultiAgents(BaseComponent, ABC):
system_app.app.include_router(router, prefix="/api", tags=["Multi-Agents"])
def __init__(self):
self.gpts_intance = GptsInstanceDao()
self.gpts_conversations = GptsConversationsDao()
self.gpts_app = GptsAppDao()
self.memory = GptsMemory(
plans_memory=MetaDbGptsPlansMemory(),
message_memory=MetaDbGptsMessageMemory(),
@@ -81,68 +88,10 @@ class MultiAgents(BaseComponent, ABC):
self.gpts_intance.add(entity)
def get_dbgpts(self, user_code: str = None, sys_code: str = None):
return self.gpts_intance.get_by_user(user_code, sys_code)
async def _build_agent_context(
self,
name: str,
conv_id: str,
) -> AgentContext:
gpts_instance: GptsInstanceEntity = self.gpts_intance.get_by_name(name)
if gpts_instance is None:
raise ValueError(f"can't find dbgpts!{name}")
agents_names = json.loads(gpts_instance.gpts_agents)
llm_models_priority = json.loads(gpts_instance.gpts_models)
resource_db = (
json.loads(gpts_instance.resource_db) if gpts_instance.resource_db else None
apps = self.gpts_app.app_list(
GptsAppQuery(user_code=user_code, sys_code=sys_code)
)
resource_knowledge = (
json.loads(gpts_instance.resource_knowledge)
if gpts_instance.resource_knowledge
else None
)
resource_internet = (
json.loads(gpts_instance.resource_internet)
if gpts_instance.resource_internet
else None
)
### init chat param
worker_manager = CFG.SYSTEM_APP.get_component(
ComponentType.WORKER_MANAGER_FACTORY, WorkerManagerFactory
).create()
llm_task = DefaultLLMClient(worker_manager, auto_convert_message=True)
context: AgentContext = AgentContext(conv_id=conv_id, llm_provider=llm_task)
context.gpts_name = gpts_instance.gpts_name
context.resource_db = resource_db
context.resource_internet = resource_internet
context.resource_knowledge = resource_knowledge
context.agents = agents_names
context.llm_models = await llm_task.models()
context.model_priority = llm_models_priority
return context, gpts_instance.team_mode
async def _build_chat_manager(
self, context: AgentContext, mode: TeamMode, agents: List[Agent]
):
if mode == TeamMode.SINGLE_AGENT:
manager = agents[0]
else:
if TeamMode.AUTO_PLAN == mode:
manager = AutoPlanChatManager(
agent_context=context,
memory=self.memory,
)
elif TeamMode.AWEL_LAYOUT == mode:
manager = AwelLayoutChatManager(
agent_context=context,
memory=self.memory,
)
else:
raise ValueError(f"Unknown Agent Team Mode!{mode}")
manager.hire(agents)
return manager
return apps
async def agent_chat(
self,
@@ -152,7 +101,8 @@ class MultiAgents(BaseComponent, ABC):
user_code: str = None,
sys_code: str = None,
):
context, team_mode = await self._build_agent_context(gpts_name, agent_conv_id)
gpt_app: GptsApp = self.gpts_app.app_detail(gpts_name)
gpts_conversation = self.gpts_conversations.get_by_conv_id(agent_conv_id)
is_retry_chat = True
if not gpts_conversation:
@@ -162,8 +112,9 @@ class MultiAgents(BaseComponent, ABC):
conv_id=agent_conv_id,
user_goal=user_query,
gpts_name=gpts_name,
team_mode=gpt_app.team_mode,
state=Status.RUNNING.value,
max_auto_reply_round=context.max_chat_round,
max_auto_reply_round=0,
auto_reply_count=0,
user_code=user_code,
sys_code=sys_code,
@@ -171,26 +122,11 @@ class MultiAgents(BaseComponent, ABC):
)
asyncio.create_task(
multi_agents.agent_team_chat(user_query, context, team_mode, is_retry_chat)
multi_agents.agent_team_chat_new(
user_query, agent_conv_id, gpt_app, is_retry_chat
)
)
# def task_done_callback(task):
# if task.cancelled():
# logger.warning("The task was cancelled!")
# elif task.exception():
# logger.exception(f"The task raised an exception: {task.exception()}")
# else:
# logger.info(f"Callback: {task.result()}")
# loop = asyncio.get_event_loop()
# future = asyncio.run_coroutine_threadsafe(
# self.stable_message(agent_conv_id), loop
# )
#
# current_message.add_view_message(future.result())
# current_message.end_current_round()
# current_message.save_to_storage()
#
# task.add_done_callback(task_done_callback)
async for chunk in multi_agents.chat_messages(agent_conv_id):
if chunk:
logger.info(chunk)
@@ -244,68 +180,96 @@ class MultiAgents(BaseComponent, ABC):
current_message.end_current_round()
current_message.save_to_storage()
async def agent_team_chat(
async def agent_team_chat_new(
self,
user_query: str,
context: AgentContext,
team_mode: TeamMode,
conv_uid: str,
gpts_app: GptsApp,
is_retry_chat: bool = False,
):
"""Initiate an Agent-based conversation
Args:
user_query:
context:
team_mode:
is_retry_chat:
employees: List[Agent] = []
# Prepare resource loader
resource_loader = ResourceLoader()
plugin_hub_loader = PluginHubLoadClient()
resource_loader.register_resesource_api(plugin_hub_loader)
datasource_loader = DatasourceLoadClient()
resource_loader.register_resesource_api(datasource_loader)
knowledge_space_loader = KnowledgeSpaceLoadClient()
resource_loader.register_resesource_api(knowledge_space_loader)
context: AgentContext = AgentContext(
conv_id=conv_uid,
gpts_app_name=gpts_app.app_name,
language=gpts_app.language,
)
Returns:
# init llm provider
### init chat param
worker_manager = CFG.SYSTEM_APP.get_component(
ComponentType.WORKER_MANAGER_FACTORY, WorkerManagerFactory
).create()
self.llm_provider = DefaultLLMClient(worker_manager, auto_convert_message=True)
"""
try:
agents = []
for name in context.agents:
cls = agent_manage.get_by_name(name)
agent = cls(
agent_context=context,
memory=self.memory,
)
agents.append(agent)
manager = await self._build_chat_manager(
context, TeamMode(team_mode), agents
for record in gpts_app.details:
cls: Type[ConversableAgent] = agent_manage.get_by_name(record.agent_name)
llm_config = LLMConfig(
llm_client=self.llm_provider,
llm_strategy=LLMStrategyType(record.llm_strategy),
strategy_context=record.llm_strategy_value,
)
user_proxy = UserProxyAgent(memory=self.memory, agent_context=context)
if not is_retry_chat:
## dbgpts conversation save
try:
await user_proxy.a_initiate_chat(
recipient=manager,
message=user_query,
memory=self.memory,
)
except Exception as e:
logger.error(f"chat abnormal termination{str(e)}", e)
self.gpts_conversations.update(context.conv_id, Status.FAILED.value)
agent = (
await cls()
.bind(context)
.bind(self.memory)
.bind(llm_config)
.bind(record.resources)
.bind(resource_loader)
.build()
)
employees.append(agent)
team_mode = TeamMode(gpts_app.team_mode)
if team_mode == TeamMode.SINGLE_AGENT:
recipient = employees[0]
else:
llm_config = LLMConfig(llm_client=self.llm_provider)
if TeamMode.AUTO_PLAN == team_mode:
manager = AutoPlanChatManager()
elif TeamMode.AWEL_LAYOUT == team_mode:
manager = AwelLayoutChatNewManager(dag=gpts_app.team_context)
else:
# retry chat
self.gpts_conversations.update(context.conv_id, Status.RUNNING.value)
try:
await user_proxy.a_retry_chat(
recipient=manager,
memory=self.memory,
)
except Exception as e:
logger.error(f"chat abnormal termination{str(e)}", e)
self.gpts_conversations.update(context.conv_id, Status.FAILED.value)
raise ValueError(f"Unknown Agent Team Mode!{team_mode}")
manager = (
await manager.bind(context)
.bind(self.memory)
.bind(llm_config)
.bind(resource_loader)
.build()
)
manager.hire(employees)
recipient = manager
self.gpts_conversations.update(context.conv_id, Status.COMPLETE.value)
return context.conv_id
user_proxy: UserProxyAgent = (
await UserProxyAgent()
.bind(context)
.bind(self.memory)
.bind(resource_loader)
.build()
)
if is_retry_chat:
# retry chat
self.gpts_conversations.update(conv_uid, Status.RUNNING.value)
try:
await user_proxy.a_initiate_chat(
recipient=recipient,
message=user_query,
)
except Exception as e:
logger.exception("new chat compeletion failed!")
self.gpts_conversations.update(context.conv_id, Status.FAILED.value)
raise ValueError(f"Add chat failed!{str(e)}")
logger.error(f"chat abnormal termination{str(e)}", e)
self.gpts_conversations.update(conv_uid, Status.FAILED.value)
self.gpts_conversations.update(conv_uid, Status.COMPLETE.value)
return conv_uid
async def chat_messages(
self, conv_id: str, user_code: str = None, system_app: str = None
@@ -369,33 +333,6 @@ async def agents_list():
return Result.failed(code="E30001", msg=str(e))
@router.post("/v1/dbgpts/create", response_model=Result[str])
async def create_dbgpts(gpts_instance: DbGptsInstance = Body()):
logger.info(f"create_dbgpts:{gpts_instance}")
try:
multi_agents.gpts_create(
GptsInstanceEntity(
gpts_name=gpts_instance.gpts_name,
gpts_describe=gpts_instance.gpts_describe,
team_mode=gpts_instance.team_mode,
resource_db=json.dumps(gpts_instance.resource_db.to_dict()),
resource_internet=json.dumps(gpts_instance.resource_internet.to_dict()),
resource_knowledge=json.dumps(
gpts_instance.resource_knowledge.to_dict()
),
gpts_agents=json.dumps(gpts_instance.gpts_agents),
gpts_models=json.dumps(gpts_instance.gpts_models),
language=gpts_instance.language,
user_code=gpts_instance.user_code,
sys_code=gpts_instance.sys_code,
)
)
return Result.succ(None)
except Exception as e:
logger.error(f"create_dbgpts failed:{str(e)}")
return Result.failed(msg=str(e), code="E300002")
@router.get("/v1/dbgpts/list", response_model=Result[str])
async def get_dbgpts(user_code: str = None, sys_code: str = None):
logger.info(f"get_dbgpts:{user_code},{sys_code}")

View File

@@ -5,31 +5,6 @@ from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, List, Optional
from dbgpt.agent.agents.agent import AgentResource
from dbgpt.serve.agent.team.base import TeamMode
class AgentMode(Enum):
PLAN_EXCUTE = "plan_excute"
@dataclass
class DbGptsInstance:
gpts_name: str
gpts_describe: str
gpts_agents: list[str]
team_mode: TeamMode
resource_db: Optional[AgentResource] = None
resource_internet: Optional[AgentResource] = None
resource_knowledge: Optional[AgentResource] = None
gpts_models: Optional[Dict[str, List[str]]] = None
language: str = "en"
user_code: str = None
sys_code: str = None
def to_dict(self) -> Dict[str, Any]:
return dataclasses.asdict(self)
@dataclass
class DbGptsMessage:

View File

View File

@@ -0,0 +1,173 @@
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.resource.resource_api import ResourceType
from dbgpt.app.knowledge.api import knowledge_space_service
from dbgpt.app.knowledge.request.request import KnowledgeSpaceRequest
from dbgpt.app.openapi.api_view_model import Result
from dbgpt.serve.agent.app.gpts_server import available_llms
from dbgpt.serve.agent.db.gpts_app import (
GptsApp,
GptsAppCollectionDao,
GptsAppDao,
GptsAppQuery,
)
from dbgpt.serve.agent.hub.plugin_hub import plugin_hub
from dbgpt.serve.agent.team.base import TeamMode
CFG = Config()
router = APIRouter()
logger = logging.getLogger(__name__)
gpts_dao = GptsAppDao()
collection_dao = GptsAppCollectionDao()
@router.post("/v1/app/create")
async def create(gpts_app: GptsApp):
try:
return Result.succ(gpts_dao.create(gpts_app))
except Exception as ex:
return Result.failed(code="E000X", msg=f"create app error: {ex}")
@router.post("/v1/app/list")
async def app_list(query: GptsAppQuery):
try:
return Result.succ(gpts_dao.app_list(query))
except Exception as ex:
return Result.failed(code="E000X", msg=f"query app error: {ex}")
@router.post("/v1/app/detail")
async def app_list(gpts_app: GptsApp):
try:
return Result.succ(gpts_dao.app_detail(gpts_app.app_code))
except Exception as ex:
return Result.failed(code="E000X", msg=f"query app error: {ex}")
@router.post("/v1/app/edit")
async def edit(gpts_app: GptsApp):
try:
return Result.succ(gpts_dao.edit(gpts_app))
except Exception as ex:
return Result.failed(code="E000X", msg=f"edit app error: {ex}")
@router.get("/v1/agents/list")
async def all_agents():
try:
return Result.succ(agent_manage.list_agents())
except Exception as ex:
return Result.failed(code="E000X", msg=f"query agents error: {ex}")
@router.post("/v1/app/remove", response_model=Result[str])
async def delete(gpts_app: GptsApp):
try:
gpts_dao.delete(gpts_app.app_code, gpts_app.user_code, gpts_app.sys_code)
return Result.succ([])
except Exception as ex:
return Result.failed(code="E000X", msg=f"delete app error: {ex}")
@router.post("/v1/app/collect", response_model=Result[str])
async def collect(gpts_app: GptsApp):
try:
collection_dao.collect(gpts_app.app_code, gpts_app.user_code, gpts_app.sys_code)
return Result.succ([])
except Exception as ex:
return Result.failed(code="E000X", msg=f"collect app error: {ex}")
@router.post("/v1/app/uncollect", response_model=Result[str])
async def uncollect(gpts_app: GptsApp):
try:
collection_dao.uncollect(
gpts_app.app_code, gpts_app.user_code, gpts_app.sys_code
)
return Result.succ([])
except Exception as ex:
return Result.failed(code="E000X", msg=f"uncollect app error: {ex}")
@router.get("/v1/team-mode/list")
async def team_mode_list():
try:
return Result.succ([mode.value for mode in TeamMode])
except Exception as ex:
return Result.failed(code="E000X", msg=f"query team mode list error: {ex}")
@router.get("/v1/resource-type/list")
async def team_mode_list():
try:
return Result.succ([type.value for type in ResourceType])
except Exception as ex:
return Result.failed(code="E000X", msg=f"query resource type list error: {ex}")
@router.get("/v1/llm-strategy/list")
async def llm_strategies():
try:
return Result.succ([type.value for type in LLMStrategyType])
except Exception as ex:
return Result.failed(
code="E000X", msg=f"query llm strategy type list error: {ex}"
)
@router.get("/v1/llm-strategy/value/list")
async def llm_strategy_values(type: str):
try:
results = []
match type:
case LLMStrategyType.Priority.value:
results = await available_llms()
return Result.succ(results)
except Exception as ex:
return Result.failed(
code="E000X", msg=f"query llm strategy type list error: {ex}"
)
@router.get("/v1/app/resources/list", response_model=Result[str])
async def app_resources(
type: str, name: str = None, user_code: str = None, sys_code: str = None
):
"""
Get agent resources, such as db, knowledge, internet, plugin.
"""
try:
results = []
match type:
case ResourceType.DB.value:
dbs = CFG.LOCAL_DB_MANAGE.get_db_list()
results = [db["db_name"] for db in dbs]
if name:
results = [r for r in results if name in r]
case ResourceType.Knowledge.value:
knowledge_spaces = knowledge_space_service.get_knowledge_space(
KnowledgeSpaceRequest()
)
results = [ks.name for ks in knowledge_spaces]
if name:
results = [r for r in results if name in r]
case ResourceType.Plugin.value:
plugins = plugin_hub.get_my_plugin(user_code)
results = [plugin.name for plugin in plugins]
if name:
results = [r for r in results if name in r]
case ResourceType.Internet.value:
return Result.succ(None)
case ResourceType.File.value:
return Result.succ(None)
return Result.succ(results)
except Exception as ex:
return Result.failed(code="E000X", msg=f"query app resources error: {ex}")

View File

@@ -0,0 +1,22 @@
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
CFG = Config()
collection_dao = GptsAppCollectionDao()
gpts_dao = GptsAppDao()
async def available_llms(worker_type: str = "llm"):
controller = CFG.SYSTEM_APP.get_component(
ComponentType.MODEL_CONTROLLER, BaseModelController
)
types = set()
models = await controller.get_all_instances(healthy_only=True)
for model in models:
worker_name, wt = model.model_name.split("@")
if wt == worker_type:
types.add(worker_name)
return list(types)

View File

@@ -0,0 +1,562 @@
import json
import logging
import uuid
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.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
logger = logging.getLogger(__name__)
class GptsAppDetail(BaseModel):
app_code: Optional[str] = None
app_name: Optional[str] = None
agent_name: Optional[str] = None
node_id: Optional[str] = None
resources: Optional[list[AgentResource]] = None
prompt_template: Optional[str] = None
llm_strategy: Optional[str] = None
llm_strategy_value: Optional[str] = None
created_at: datetime = datetime.now()
updated_at: datetime = datetime.now()
class Config:
"""Configuration for this pydantic object."""
arbitrary_types_allowed = True
def to_dict(self):
return {k: self._serialize(v) for k, v in self.__dict__.items()}
def _serialize(self, value):
if isinstance(value, BaseModel):
return value.to_dict()
elif isinstance(value, list):
return [self._serialize(item) for item in value]
elif isinstance(value, dict):
return {k: self._serialize(v) for k, v in value.items()}
else:
return value
@classmethod
def from_dict(cls, d: Dict[str, Any]):
return cls(
app_code=d["app_code"],
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)),
prompt_template=d.get("prompt_template", None),
llm_strategy=d.get("llm_strategy", None),
llm_strategy_value=d.get("llm_strategy_value", None),
created_at=d.get("created_at", None),
updated_at=d.get("updated_at", None),
)
@classmethod
def from_entity(cls, entity):
resources = AgentResource.from_josn_list_str(entity.resources)
return cls(
app_code=entity.app_code,
app_name=entity.app_name,
agent_name=entity.agent_name,
node_id=entity.node_id,
resources=resources,
prompt_template=entity.prompt_template,
llm_strategy=entity.llm_strategy,
llm_strategy_value=entity.llm_strategy_value,
created_at=entity.created_at,
updated_at=entity.updated_at,
)
class GptsApp(BaseModel):
app_code: Optional[str] = None
app_name: Optional[str] = None
app_describe: Optional[str] = None
team_mode: Optional[str] = None
language: Optional[str] = None
team_context: Optional[Union[str, AwelTeamContext]] = None
user_code: Optional[str] = None
sys_code: Optional[str] = None
is_collected: Optional[str] = None
icon: Optional[str] = None
created_at: datetime = datetime.now()
updated_at: datetime = datetime.now()
details: List[GptsAppDetail] = []
class Config:
"""Configuration for this pydantic object."""
arbitrary_types_allowed = True
def to_dict(self):
return {k: self._serialize(v) for k, v in self.__dict__.items()}
def _serialize(self, value):
if isinstance(value, BaseModel):
return value.to_dict()
elif isinstance(value, list):
return [self._serialize(item) for item in value]
elif isinstance(value, dict):
return {k: self._serialize(v) for k, v in value.items()}
else:
return value
@classmethod
def from_dict(cls, d: Dict[str, Any]):
return cls(
app_code=d.get("app_code", None),
app_name=d["app_name"],
language=d["language"],
app_describe=d["app_describe"],
team_mode=d["team_mode"],
team_context=d.get("team_context", None),
user_code=d.get("user_code", None),
sys_code=d.get("sys_code", None),
is_collected=d.get("is_collected", None),
created_at=d.get("created_at", None),
updated_at=d.get("updated_at", None),
details=d.get("details", None),
)
class GptsAppQuery(GptsApp):
page_size: int = 100
page_no: int = 1
is_collected: Optional[str] = None
class GptsAppCollection(BaseModel):
app_code: Optional[str] = None
user_code: Optional[str] = None
sys_code: Optional[str] = None
def to_dict(self):
return {k: self._serialize(v) for k, v in self.__dict__.items()}
def _serialize(self, value):
if isinstance(value, BaseModel):
return value.to_dict()
elif isinstance(value, list):
return [self._serialize(item) for item in value]
elif isinstance(value, dict):
return {k: self._serialize(v) for k, v in value.items()}
else:
return value
@classmethod
def from_dict(cls, d: Dict[str, Any]):
return cls(
app_code=d.get("app_code", None),
user_code=d.get("user_code", None),
sys_code=d.get("sys_code", None),
created_at=d.get("created_at", None),
updated_at=d.get("updated_at", None),
)
class GptsAppCollectionEntity(Model):
__tablename__ = "gpts_app_collection"
id = Column(Integer, primary_key=True, comment="autoincrement id")
app_code = Column(String(255), nullable=False, comment="Current AI assistant code")
user_code = Column(String(255), nullable=True, comment="user code")
sys_code = Column(String(255), nullable=True, comment="system app code")
created_at = Column(DateTime, default=datetime.utcnow, comment="create time")
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
comment="last update time",
)
class GptsAppEntity(Model):
__tablename__ = "gpts_app"
id = Column(Integer, primary_key=True, comment="autoincrement id")
app_code = Column(String(255), nullable=False, comment="Current AI assistant code")
app_name = Column(String(255), nullable=False, comment="Current AI assistant name")
icon = Column(String(1024), nullable=True, comment="app icon, url")
app_describe = Column(
String(2255), nullable=False, comment="Current AI assistant describe"
)
language = Column(String(100), nullable=False, comment="gpts language")
team_mode = Column(String(255), nullable=False, comment="Team work mode")
team_context = Column(
Text,
nullable=True,
comment="The execution logic and team member content that teams with different working modes rely on",
)
user_code = Column(String(255), nullable=True, comment="user code")
sys_code = Column(String(255), nullable=True, comment="system app code")
created_at = Column(DateTime, default=datetime.utcnow, comment="create time")
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
comment="last update time",
)
__table_args__ = (UniqueConstraint("app_name", name="uk_gpts_app"),)
class GptsAppDetailEntity(Model):
__tablename__ = "gpts_app_detail"
id = Column(Integer, primary_key=True, comment="autoincrement id")
app_code = Column(String(255), nullable=False, comment="Current AI assistant code")
app_name = Column(String(255), nullable=False, comment="Current AI assistant name")
agent_name = Column(String(255), nullable=False, comment=" Agent name")
node_id = Column(
String(255), nullable=False, comment="Current AI assistant Agent Node id"
)
resources = Column(Text, nullable=True, comment="Agent bind resource")
prompt_template = Column(Text, nullable=True, comment="Agent bind template")
llm_strategy = Column(String(25), nullable=True, comment="Agent use llm strategy")
llm_strategy_value = Column(
Text, nullable=True, comment="Agent use llm strategy value"
)
created_at = Column(DateTime, default=datetime.utcnow, comment="create time")
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
comment="last update time",
)
__table_args__ = (
UniqueConstraint(
"app_name", "agent_name", "node_id", name="uk_gpts_app_agent_node"
),
)
def to_dict(self):
return {k: self._serialize(v) for k, v in self.__dict__.items()}
def _serialize(self, value):
if isinstance(value, BaseModel):
return value.to_dict()
elif isinstance(value, list):
return [self._serialize(item) for item in value]
elif isinstance(value, dict):
return {k: self._serialize(v) for k, v in value.items()}
else:
return value
class GptsAppCollectionDao(BaseDao):
def collect(
self,
app_code: str,
user_code: Optional[str] = None,
sys_code: Optional[str] = None,
):
with self.session() as session:
app_qry = session.query(GptsAppCollectionEntity)
if user_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.user_code == user_code)
if sys_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.sys_code == sys_code)
if app_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.app_code == app_code)
app_entity = app_qry.one_or_none()
if app_entity is not None:
raise f"current app has been collected!"
app_entity = GptsAppCollectionEntity(
app_code=app_code,
user_code=user_code,
sys_code=sys_code,
)
session.add(app_entity)
def uncollect(
self,
app_code: str,
user_code: Optional[str] = None,
sys_code: Optional[str] = None,
):
with self.session() as session:
app_qry = session.query(GptsAppCollectionEntity)
if user_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.user_code == user_code)
if sys_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.sys_code == sys_code)
if app_code:
app_qry = app_qry.filter(GptsAppCollectionEntity.app_code == app_code)
app_entity = app_qry.one_or_none()
if app_entity:
session.delete(app_entity)
session.commit()
def list(self, query: GptsAppCollection):
with self.session() as session:
app_qry = session.query(GptsAppCollectionEntity)
if query.user_code:
app_qry = app_qry.filter(
GptsAppCollectionEntity.user_code == query.user_code
)
if query.sys_code:
app_qry = app_qry.filter(
GptsAppCollectionEntity.sys_code == query.sys_code
)
if query.app_code:
app_qry = app_qry.filter(
GptsAppCollectionEntity.app_code == query.app_code
)
res = app_qry.all()
session.close()
return res
class GptsAppDao(BaseDao):
def app_list(self, query: GptsAppQuery):
collection_dao = GptsAppCollectionDao()
gpts_collections = collection_dao.list(
GptsAppCollection.from_dict(
{"sys_code": query.sys_code, "user_code": query.user_code}
)
)
app_codes = [gc.app_code for gc in gpts_collections]
with self.session() as session:
app_qry = session.query(GptsAppEntity)
if query.app_name:
app_qry = app_qry.filter(
GptsAppEntity.app_name.like(f"%{query.app_name}%")
)
if query.user_code:
app_qry = app_qry.filter(GptsAppEntity.user_code == query.user_code)
if query.sys_code:
app_qry = app_qry.filter(GptsAppEntity.sys_code == query.sys_code)
if query.is_collected and query.is_collected.lower() in ("true", "false"):
app_qry = app_qry.filter(GptsAppEntity.app_code.in_(app_codes))
app_qry = app_qry.order_by(GptsAppEntity.id.desc())
app_qry = app_qry.offset((query.page_no - 1) * query.page_size).limit(
query.page_size
)
results = app_qry.all()
result_app_codes = [res.app_code for res in results]
app_details_group = self._group_app_details(result_app_codes, session)
apps = []
for app_info in results:
app_details = app_details_group.get(app_info.app_code, [])
apps.append(
GptsApp.from_dict(
{
"app_code": app_info.app_code,
"app_name": app_info.app_name,
"language": app_info.language,
"app_describe": app_info.app_describe,
"team_mode": app_info.team_mode,
"team_context": _load_team_context(
app_info.team_mode, app_info.team_context
),
"user_code": app_info.user_code,
"sys_code": app_info.sys_code,
"is_collected": "true"
if app_info.app_code in app_codes
else "false",
"created_at": app_info.created_at,
"updated_at": app_info.updated_at,
"details": [
GptsAppDetail.from_dict(item.to_dict())
for item in app_details
],
}
)
)
return apps
def _group_app_details(self, app_codes, session):
app_detail_qry = session.query(GptsAppDetailEntity).filter(
GptsAppDetailEntity.app_code.in_(app_codes)
)
app_details = app_detail_qry.all()
app_details.sort(key=lambda x: x.app_code)
app_details_group = {
key: list(group)
for key, group in groupby(app_details, key=lambda x: x.app_code)
}
return app_details_group
def app_detail(self, app_code: str):
with self.session() as session:
app_qry = session.query(GptsAppEntity).filter(
GptsAppEntity.app_code == app_code
)
app_info = app_qry.first()
app_detail_qry = session.query(GptsAppDetailEntity).filter(
GptsAppDetailEntity.app_code == app_code
)
app_details = app_detail_qry.all()
app = GptsApp.from_dict(
{
"app_code": app_info.app_code,
"app_name": app_info.app_name,
"language": app_info.language,
"app_describe": app_info.app_describe,
"team_mode": app_info.team_mode,
"team_context": _load_team_context(
app_info.team_mode, app_info.team_context
),
"user_code": app_info.user_code,
"sys_code": app_info.sys_code,
"created_at": app_info.created_at,
"updated_at": app_info.updated_at,
"details": [
GptsAppDetail.from_dict(item.to_dict()) for item in app_details
],
}
)
return app
def delete(
self,
app_code: str,
user_code: Optional[str] = None,
sys_code: Optional[str] = None,
):
"""
To delete the application, you also need to delete the corresponding plug-ins and collections.
"""
if app_code is None:
raise f"cannot delete app when app_code is None"
with self.session() as session:
app_qry = session.query(GptsAppEntity)
app_qry = app_qry.filter(GptsAppEntity.app_code == app_code)
app_qry.delete()
app_detail_qry = session.query(GptsAppDetailEntity).filter(
GptsAppDetailEntity.app_code == app_code
)
app_detail_qry.delete()
app_collect_qry = session.query(GptsAppCollectionEntity).filter(
GptsAppCollectionEntity.app_code == app_code
)
app_collect_qry.delete()
def create(self, gpts_app: GptsApp):
with self.session() as session:
app_entity = GptsAppEntity(
app_code=str(uuid.uuid1()),
app_name=gpts_app.app_name,
app_describe=gpts_app.app_describe,
team_mode=gpts_app.team_mode,
team_context=_parse_team_context(gpts_app.team_context),
language=gpts_app.language,
user_code=gpts_app.user_code,
sys_code=gpts_app.sys_code,
created_at=gpts_app.created_at,
updated_at=gpts_app.updated_at,
icon=gpts_app.icon,
)
session.add(app_entity)
app_details = []
for item in gpts_app.details:
resource_dicts = [resource.to_dict() for resource in item.resources]
if item.agent_name is None:
raise f"agent name cannot be None"
app_details.append(
GptsAppDetailEntity(
app_code=app_entity.app_code,
app_name=app_entity.app_name,
agent_name=item.agent_name,
node_id=str(uuid.uuid1()),
resources=json.dumps(resource_dicts, ensure_ascii=False),
prompt_template=item.prompt_template,
llm_strategy=item.llm_strategy,
llm_strategy_value=item.llm_strategy_value,
created_at=item.created_at,
updated_at=item.updated_at,
)
)
session.add_all(app_details)
gpts_app.app_code = app_entity.app_code
return gpts_app
def edit(self, gpts_app: GptsApp):
with self.session() as session:
app_qry = session.query(GptsAppEntity)
if gpts_app.app_code is None:
raise f"app_code is None, don't allow to edit!"
app_qry = app_qry.filter(GptsAppEntity.app_code == gpts_app.app_code)
app_entity = app_qry.one()
app_entity.app_name = gpts_app.app_name
app_entity.app_describe = gpts_app.app_describe
app_entity.language = gpts_app.language
app_entity.team_mode = gpts_app.team_mode
app_entity.icon = gpts_app.icon
app_entity.team_context = _parse_team_context(gpts_app.team_context)
session.merge(app_entity)
old_details = session.query(GptsAppDetailEntity).filter(
GptsAppDetailEntity.app_code == gpts_app.app_code
)
old_details.delete()
session.commit()
app_details = []
for item in gpts_app.details:
resource_dicts = [resource.to_dict() for resource in item.resources]
app_details.append(
GptsAppDetailEntity(
app_code=gpts_app.app_code,
app_name=gpts_app.app_name,
agent_name=item.agent_name,
node_id=str(uuid.uuid1()),
resources=json.dumps(resource_dicts, ensure_ascii=False),
prompt_template=item.prompt_template,
llm_strategy=item.llm_strategy,
llm_strategy_value=item.llm_strategy_value,
created_at=item.created_at,
updated_at=item.updated_at,
)
)
session.add_all(app_details)
return True
def _parse_team_context(team_context: Optional[Union[str, AwelTeamContext]] = None):
"""
parse team_context to str
"""
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]:
"""
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))
return awel_team_ctx
except Exception as ex:
logger.info(
f"_load_team_context error, team_mode={team_mode}, team_context={team_context}, {ex}"
)
return team_context

View File

@@ -25,6 +25,9 @@ class GptsConversationsEntity(Model):
user_goal = Column(Text, nullable=False, comment="User's goals content")
gpts_name = Column(String(255), nullable=False, comment="The gpts name")
team_mode = Column(
String(255), nullable=False, comment="The conversation team mode"
)
state = Column(String(255), nullable=True, comment="The gpts state")
max_auto_reply_round = Column(

View File

@@ -28,6 +28,7 @@ class GptsInstanceEntity(Model):
default=False,
comment="Applications for sustainable dialogue",
)
resource_db = Column(
Text,
nullable=True,

View File

@@ -1,73 +0,0 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, Integer, String, Text, UniqueConstraint
from dbgpt.storage.metadata import BaseDao, Model
class GptsInstanceEntity(Model):
__tablename__ = "gpts_instance"
id = Column(Integer, primary_key=True, comment="autoincrement id")
gpts_name = Column(String(255), nullable=False, comment="Current AI assistant name")
gpts_describe = Column(
String(2255), nullable=False, comment="Current AI assistant describe"
)
resource_db = Column(
Text,
nullable=True,
comment="List of structured database names contained in the current gpts",
)
resource_internet = Column(
Text,
nullable=True,
comment="Is it possible to retrieve information from the internet",
)
resource_knowledge = Column(
Text,
nullable=True,
comment="List of unstructured database names contained in the current gpts",
)
gpts_agents = Column(
String(1000),
nullable=True,
comment="List of agents names contained in the current gpts",
)
gpts_models = Column(
String(1000),
nullable=True,
comment="List of llm model names contained in the current gpts",
)
language = Column(String(100), nullable=True, comment="gpts language")
user_code = Column(String(255), nullable=False, comment="user code")
sys_code = Column(String(255), nullable=True, comment="system app code")
created_at = Column(DateTime, default=datetime.utcnow, comment="create time")
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
comment="last update time",
)
__table_args__ = (UniqueConstraint("gpts_name", name="uk_gpts"),)
class GptsInstanceDao(BaseDao):
def add(self, engity: GptsInstanceEntity):
session = self.get_raw_session()
session.add(engity)
session.commit()
id = engity.id
session.close()
return id
def get_by_name(self, name: str) -> GptsInstanceEntity:
session = self.get_raw_session()
gpts_instance = session.query(GptsInstanceEntity)
if name:
gpts_instance = gpts_instance.filter(GptsInstanceEntity.gpts_name == name)
result = gpts_instance.first()
session.close()
return result

View File

@@ -36,7 +36,9 @@ class GptsMessagesEntity(Model):
)
model_name = Column(String(255), nullable=True, comment="message generate model")
rounds = Column(Integer, nullable=False, comment="dialogue turns")
content = Column(Text, nullable=True, comment="Content of the speech")
content = Column(
Text(length=2**31 - 1), nullable=True, comment="Content of the speech"
)
current_gogal = Column(
Text, nullable=True, comment="The target corresponding to the current message"
)
@@ -45,7 +47,9 @@ class GptsMessagesEntity(Model):
Text, nullable=True, comment="Current conversation review info"
)
action_report = Column(
Text, nullable=True, comment="Current conversation action report"
Text(length=2**31 - 1),
nullable=True,
comment="Current conversation action report",
)
role = Column(

View File

@@ -10,7 +10,7 @@ from dbgpt.app.openapi.api_view_model import Result
from dbgpt.component import BaseComponent, ComponentType, SystemApp
from dbgpt.configs.model_config import PLUGINS_DIR
from dbgpt.serve.agent.db.plugin_hub_db import PluginHubEntity
from dbgpt.serve.agent.hub.agent_hub import AgentHub
from dbgpt.serve.agent.hub.plugin_hub import plugin_hub
from dbgpt.serve.agent.model import (
PagenationFilter,
PagenationResult,
@@ -22,8 +22,8 @@ router = APIRouter()
logger = logging.getLogger(__name__)
class ModuleAgent(BaseComponent, ABC):
name = ComponentType.AGENT_HUB
class ModulePlugin(BaseComponent, ABC):
name = ComponentType.PLUGIN_HUB
def __init__(self):
# load plugins
@@ -48,14 +48,13 @@ class ModuleAgent(BaseComponent, ABC):
return generator
module_agent = ModuleAgent()
module_plugin = ModulePlugin()
@router.post("/v1/agent/hub/update", response_model=Result[str])
async def agent_hub_update(update_param: PluginHubParam = Body()):
logger.info(f"agent_hub_update:{update_param.__dict__}")
async def plugin_hub_update(update_param: PluginHubParam = Body()):
logger.info(f"plugin_hub_update:{update_param.__dict__}")
try:
agent_hub = AgentHub(PLUGINS_DIR)
branch = (
update_param.branch
if update_param.branch is not None and len(update_param.branch) > 0
@@ -67,7 +66,7 @@ async def agent_hub_update(update_param: PluginHubParam = Body()):
else None
)
# TODO change it to async
agent_hub.refresh_hub_from_git(update_param.url, branch, authorization)
plugin_hub.refresh_hub_from_git(update_param.url, branch, authorization)
return Result.succ(None)
except Exception as e:
logger.error("Agent Hub Update Error!", e)
@@ -77,14 +76,13 @@ async def agent_hub_update(update_param: PluginHubParam = Body()):
@router.post("/v1/agent/query", response_model=Result[str])
async def get_agent_list(filter: PagenationFilter[PluginHubFilter] = Body()):
logger.info(f"get_agent_list:{filter.__dict__}")
agent_hub = AgentHub(PLUGINS_DIR)
filter_enetity: PluginHubEntity = PluginHubEntity()
if filter.filter:
attrs = vars(filter.filter) # 获取原始对象的属性字典
for attr, value in attrs.items():
setattr(filter_enetity, attr, value) # 设置拷贝对象的属性值
datas, total_pages, total_count = agent_hub.hub_dao.list(
datas, total_pages, total_count = plugin_hub.hub_dao.list(
filter_enetity, filter.page_index, filter.page_size
)
result: PagenationResult[PluginHubEntity] = PagenationResult[PluginHubEntity]()
@@ -100,8 +98,7 @@ async def get_agent_list(filter: PagenationFilter[PluginHubFilter] = Body()):
@router.post("/v1/agent/my", response_model=Result[str])
async def my_agents(user: str = None):
logger.info(f"my_agents:{user}")
agent_hub = AgentHub(PLUGINS_DIR)
agents = agent_hub.get_my_plugin(user)
agents = plugin_hub.get_my_plugin(user)
agent_dicts = []
for agent in agents:
agent_dicts.append(agent.__dict__)
@@ -113,10 +110,9 @@ async def my_agents(user: str = None):
async def agent_install(plugin_name: str, user: str = None):
logger.info(f"agent_install:{plugin_name},{user}")
try:
agent_hub = AgentHub(PLUGINS_DIR)
agent_hub.install_plugin(plugin_name, user)
plugin_hub.install_plugin(plugin_name, user)
module_agent.refresh_plugins()
module_plugin.refresh_plugins()
return Result.succ(None)
except Exception as e:
@@ -128,10 +124,9 @@ async def agent_install(plugin_name: str, user: str = None):
async def agent_uninstall(plugin_name: str, user: str = None):
logger.info(f"agent_uninstall:{plugin_name},{user}")
try:
agent_hub = AgentHub(PLUGINS_DIR)
agent_hub.uninstall_plugin(plugin_name, user)
plugin_hub.uninstall_plugin(plugin_name, user)
module_agent.refresh_plugins()
module_plugin.refresh_plugins()
return Result.succ(None)
except Exception as e:
logger.error("Plugin Uninstall Error!", e)
@@ -142,9 +137,8 @@ async def agent_uninstall(plugin_name: str, user: str = None):
async def personal_agent_upload(doc_file: UploadFile = File(...), user: str = None):
logger.info(f"personal_agent_upload:{doc_file.filename},{user}")
try:
agent_hub = AgentHub(PLUGINS_DIR)
await agent_hub.upload_my_plugin(doc_file, user)
module_agent.refresh_plugins()
await plugin_hub.upload_my_plugin(doc_file, user)
module_plugin.refresh_plugins()
return Result.succ(None)
except Exception as e:
logger.error("Upload Personal Plugin Error!", e)

View File

@@ -10,6 +10,7 @@ from fastapi import UploadFile
from dbgpt.agent.common.schema import PluginStorageType
from dbgpt.agent.plugin.plugins_util import scan_plugins, update_from_git
from dbgpt.configs.model_config import PLUGINS_DIR
from ..db.my_plugin_db import MyPluginDao, MyPluginEntity
from ..db.plugin_hub_db import PluginHubDao, PluginHubEntity
@@ -20,7 +21,7 @@ DEFAULT_PLUGIN_REPO = "https://github.com/eosphoros-ai/DB-GPT-Plugins.git"
TEMP_PLUGIN_PATH = ""
class AgentHub:
class PluginHub:
def __init__(self, plugin_dir) -> None:
self.hub_dao = PluginHubDao()
self.my_plugin_dao = MyPluginDao()
@@ -207,3 +208,6 @@ class AgentHub:
if not user:
user = Default_User
return self.my_plugin_dao.get_by_user(user)
plugin_hub = PluginHub(PLUGINS_DIR)

View File

@@ -32,6 +32,59 @@ 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

View File

@@ -0,0 +1,64 @@
import logging
from typing import Any, Dict, List, Optional, Tuple, Union
from dbgpt._private.config import Config
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
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
CFG = Config()
logger = logging.getLogger(__name__)
class DatasourceLoadClient(ResourceDbClient):
def __init__(self):
super().__init__()
# The executor to submit blocking function
self._executor = CFG.SYSTEM_APP.get_component(
ComponentType.EXECUTOR_DEFAULT, ExecutorFactory
).create()
def get_data_type(self, resource: AgentResource) -> str:
conn = CFG.LOCAL_DB_MANAGE.get_connect(resource.value)
return conn.db_type
async def a_get_schema_link(self, db: str, question: Optional[str] = None) -> str:
try:
from dbgpt.rag.summary.db_summary_client import DBSummaryClient
except ImportError:
raise ValueError("Could not import DBSummaryClient. ")
client = DBSummaryClient(system_app=CFG.SYSTEM_APP)
table_infos = None
try:
with root_tracer.start_span("ChatWithDbAutoExecute.get_db_summary"):
table_infos = await blocking_func_to_async(
self._executor,
client.get_db_summary,
db,
question,
CFG.KNOWLEDGE_SEARCH_TOP_SIZE,
)
except Exception as e:
print("db summary find error!" + str(e))
if not table_infos:
conn = CFG.LOCAL_DB_MANAGE.get_connect(db)
table_infos = await blocking_func_to_async(
self._executor, conn.table_simple_info
)
return table_infos
async def a_query_to_df(self, db: str, sql: str):
conn = CFG.LOCAL_DB_MANAGE.get_connect(db)
return conn.run_to_df(sql)
async def a_query(self, db: str, sql: str):
conn = CFG.LOCAL_DB_MANAGE.get_connect(db)
return conn.query_ex(sql)
async def a_run_sql(self, db: str, sql: str):
conn = CFG.LOCAL_DB_MANAGE.get_connect(db)
return conn.run(sql)

View File

@@ -0,0 +1,43 @@
import json
import logging
from typing import Any, Dict, List, Optional, Tuple, Union
from dbgpt._private.config import Config
from dbgpt.agent.plugin.generator import PluginPromptGenerator
from dbgpt.agent.resource.resource_api import AgentResource, ResourceType
from dbgpt.agent.resource.resource_knowledge_api import ResourceKnowledgeClient
from dbgpt.component import ComponentType
from dbgpt.rag.chunk import Chunk
from dbgpt.serve.agent.hub.controller import ModulePlugin
from dbgpt.serve.rag.retriever.knowledge_space import KnowledgeSpaceRetriever
from dbgpt.util.executor_utils import ExecutorFactory, blocking_func_to_async
from dbgpt.util.tracer import root_tracer, trace
CFG = Config()
logger = logging.getLogger(__name__)
class KnowledgeSpaceLoadClient(ResourceKnowledgeClient):
async def a_get_space_desc(self, space_name) -> str:
pass
async def a_get_kn(
self, space_name: str, question: Optional[str] = None
) -> List[Chunk]:
kn_retriver = KnowledgeSpaceRetriever(space_name=space_name)
chunks: List[Chunk] = kn_retriver.retrieve(question)
return chunks
async def add_kn(
self, space_name: str, kn_name: str, type: str, content: Optional[Any]
):
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)
return "\n".join([doc.content for doc in docs])

View File

@@ -0,0 +1,40 @@
import json
import logging
from typing import Any, Dict, List, Optional, Tuple, Union
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
CFG = Config()
logger = logging.getLogger(__name__)
class PluginHubLoadClient(ResourcePluginClient):
def __init__(self):
super().__init__()
# The executor to submit blocking function
self._executor = CFG.SYSTEM_APP.get_component(
ComponentType.EXECUTOR_DEFAULT, ExecutorFactory
).create()
async def a_load_plugin(
self, value: str, plugin_generator: Optional[PluginPromptGenerator] = None
) -> PluginPromptGenerator:
logger.info(f"PluginHubLoadClient load plugin:{value}")
plugins_prompt_generator = PluginPromptGenerator()
plugins_prompt_generator.command_registry = CFG.command_registry
agent_module = CFG.SYSTEM_APP.get_component(
ComponentType.PLUGIN_HUB, ModulePlugin
)
plugins_prompt_generator = agent_module.load_select_plugin(
plugins_prompt_generator, json.dumps(value)
)
return plugins_prompt_generator

View File

@@ -2,8 +2,23 @@ from abc import ABC
from typing import Dict, List, Optional
from dbgpt.agent.agents.agent 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:
@@ -12,7 +27,7 @@ class BaseAgentOperator:
SHARE_DATA_KEY_MODEL_NAME = "share_data_key_agent_name"
def __init__(self, agent: Optional[Agent] = None):
self._agent = agent
self._agent: ConversableAgent = agent
@property
def agent(self) -> Agent:
@@ -32,9 +47,11 @@ class AgentOperator(
async def map(self, input_value: AgentGenerateContext) -> AgentGenerateContext:
now_rely_messages: List[Dict] = []
# input_value.message["current_gogal"] = (
# self._agent.name + ":" + input_value.message["current_gogal"]
# )
# Isolate the message delivery mechanism and pass it to the operator
input_value.message["current_gogal"] = (
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
@@ -48,39 +65,20 @@ class AgentOperator(
now_message, self._agent, input_value.reviewer, False
)
### Retry on failure
current_message = input_value.message
final_sucess = False
final_message = None
while self.agent.current_retry_counter < self.agent.max_retry_count:
verify_paas, reply_message = await self._agent.a_generate_reply(
message=current_message,
sender=input_value.sender,
reviewer=input_value.reviewer,
silent=input_value.silent,
rely_messages=input_value.rely_messages,
)
final_message = reply_message
if verify_paas:
final_sucess = True
break
else:
# retry
current_message = {
"content": reply_message["content"],
"current_gogal": input_value.message.get("current_gogal", None),
}
await input_value.sender.a_send(
current_message, self.agent, input_value.reviewer, False
)
self.agent.current_retry_counter += 1
if not final_sucess:
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"After trying {self.agent.current_retry_counter} times, I still can't generate a valid answer. The current problem is:{final_message['content']}!"
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 = final_message
ai_message = reply_message
ai_message["role"] = ModelMessageRoleType.AI
now_rely_messages.append(ai_message)
@@ -92,3 +90,160 @@ class AgentOperator(
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_rely_messages: List[Dict] = []
agent = await self.get_agent(input_value)
# Isolate the message delivery mechanism and pass it to the operator
input_value.message["current_gogal"] = (
f"[{agent.name if agent.name else 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, 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,
)
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)
agent = (
await agent_cls(name=self.awel_agent.role_name)
.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) -> None:
"""Trigger the DAG. Not used in HttpTrigger."""
pass

View File

@@ -0,0 +1,182 @@
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.base_agent_new import ConversableAgent
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 (
IOField,
OperatorCategory,
OperatorType,
OptionValue,
Parameter,
ResourceCategory,
ViewMetadata,
register_resource,
)
from dbgpt.core.interface.operators.prompt_operator import CommonChatPromptTemplate
@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
@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=[
OptionValue(label=item["name"], name=item["name"], value=item["name"])
for item in agent_manage.list_agents()
],
),
Parameter.build_from(
label="Role Name",
name="role_name",
type=str,
optional=True,
default=None,
description="The agent role name.",
),
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)
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

View File

@@ -1,10 +1,10 @@
import logging
import sys
from typing import Any, Optional
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.agent.memory.gpts_memory import GptsMemory
from dbgpt.core.awel import DAG
from dbgpt.serve.agent.team.layout.agent_operator import AgentOperator
@@ -12,44 +12,27 @@ logger = logging.getLogger(__name__)
class AwelLayoutChatManager(ManagerAgent):
NAME = "layout_manager"
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,
memory: GptsMemory,
agent_context: AgentContext,
# unlimited consecutive auto reply by default
max_consecutive_auto_reply: Optional[int] = sys.maxsize,
human_input_mode: Optional[str] = "NEVER",
describe: Optional[str] = "layout chat manager.",
**kwargs,
):
super().__init__(
name=self.NAME,
describe=describe,
memory=memory,
max_consecutive_auto_reply=max_consecutive_auto_reply,
human_input_mode=human_input_mode,
agent_context=agent_context,
**kwargs,
)
# Allow async chat if initiated using a_initiate_chat
self.register_reply(
Agent,
AwelLayoutChatManager.a_run_chat,
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
async def a_run_chat(
async def a_act(
self,
message: Optional[str] = None,
sender: Optional[Agent] = None,
reviewer: Agent = None,
config: Optional[Any] = None,
):
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_name}_{self.agent_context.conv_id}"
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)
@@ -59,12 +42,13 @@ class AwelLayoutChatManager(ManagerAgent):
last_node >> now_node
last_node = now_node
start_message = {
"content": message,
"current_gogal": message,
}
start_message_context: AgentGenerateContext = AgentGenerateContext(
message=start_message, sender=self, reviewer=reviewer
message={
"content": message,
"current_gogal": message,
},
sender=self,
reviewer=reviewer,
)
final_generate_context: AgentGenerateContext = await last_node.call(
call_data=start_message_context
@@ -76,14 +60,14 @@ class AwelLayoutChatManager(ManagerAgent):
last_message, self, start_message_context.reviewer, False
)
return True, {
"is_exe_success": True,
"content": last_message.get("content", None),
"view": last_message.get("view", None),
}
return ActionOutput(
content=last_message.get("content", None),
view=last_message.get("view", None),
)
except Exception as e:
logger.exception("DAG run failed!")
return True, {
"content": f"Failed to complete goal! {str(e)}",
"is_exe_success": False,
}
logger.exception(f"DAG run failed!{str(e)}")
return ActionOutput(
is_exe_success=False,
content=f"Failed to complete goal! {str(e)}",
)

View File

@@ -0,0 +1,90 @@
import json
import logging
from typing import Any, 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 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_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_gogal": message,
},
sender=self,
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)
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)}",
)

View File

@@ -0,0 +1,121 @@
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.disply(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)}"
)

View File

@@ -1,12 +1,11 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
from pydantic import Field
from dbgpt._private.config import Config
from dbgpt.agent.agents.agent import Agent, AgentContext
from dbgpt.agent.agents.base_agent import ConversableAgent
from dbgpt.agent.common.schema import Status
from dbgpt.agent.memory.base import GptsPlan
from dbgpt.agent.memory.gpts_memory import GptsMemory
from dbgpt.util.json_utils import find_json_objects
from dbgpt.agent.agents.base_agent_new import ConversableAgent
from .plan_action import PlanAction
CFG = Config()
@@ -14,26 +13,27 @@ CFG = Config()
class PlannerAgent(ConversableAgent):
"""Planner agent, realizing task goal planning decomposition through LLM"""
DEFAULT_SYSTEM_MESSAGE = """
你是一个任务规划专家!您需要理解下面每个智能代理和他们的能力,却确保在没有用户帮助下,使用给出的资源,通过协调下面可用智能代理来回答用户问题。
请发挥你LLM的知识和理解能力理解用户问题的意图和目标生成一个可用智能代理协作的任务计划解决用户问题。
可用资源:
{all_resources}
可用智能代理:
{agents}
agents: List[ConversableAgent] = Field(default_factory=list)
*** 重要的提醒 ***
- 充分理解用户目标然后进行必要的步骤拆分,拆分需要保证逻辑顺序和精简,尽量把可以一起完成的内容合并再一个步骤,拆分后每个子任务步骤都将是一个需要智能代理独立完成目标, 请确保每个子任务目标内容简洁明了
- 请确保只使用上面提到的智能代理,并且可以只使用其中需要的部分,严格根据描述能力和限制分配给合适的步骤,每个智能代理都可以重复使用
- 给子任务分配智能代理是需要考虑整体计划,确保和前后依赖步骤的关系,数据可以被传递使用
- 根据用户目标的实际需要使用提供的资源来协助生成计划步骤,不要使用不需要的资源
- 每个步骤最好是使用一种资源完成一个子目标,如果当前目标可以分解为同类型的多个子任务,可以生成相互不依赖的并行任务
- 数据库资源只需要使用结构生成SQL数据获取交给用户执行
- 尽量合并有顺序依赖的连续相同步骤,如果用户目标无拆分必要,可以生成内容为用户目标的单步任务
- 仔细检查计划,确保计划完整的包含了用户问题所涉及的所有信息,并且最终能完成目标,确认每个步骤是否包含了需要用到的资源信息,如URL、资源名等.
具体任务计划的生成可参考如下例子:
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:[
{{
@@ -50,152 +50,32 @@ class PlannerAgent(ConversableAgent):
}},
{{
"serial_number": "3",
"agent": "DataScientist",
"content": "Count the number of transactions with "pay_status" as "paid" among all transactions to retrieve the sales conversion rate.",
"rely": ""
}},
{{
"serial_number": "4",
"agent": "Reporter",
"content": "Integrate analytical data into the format required to build sales reports.",
"rely": "1,2,3"
"rely": "1,2"
}}
]
请一步步思考并以如下json格式返回你的行动计划内容:
[{{
"serial_number":"0",
"agent": "用来完成当前步骤的智能代理",
"content": "当前步骤的任务内容,确保可以被智能代理执行",
"rely":"当前任务执行依赖的其他任务serial_number, 如:1,2,3, 无依赖为空"
}}]
确保回答的json可以被Python代码的json.loads函数加载解析.
"""
REPAIR_SYSTEM_MESSAGE = """
You are a planning expert! Now you need to use your professional knowledge to carefully check the generated plan, re-evaluate and analyze it, and ensure that each step of the plan is clear and complete and can be understood by the intelligent agent to solve the current plan Problems encountered! and return new program content as requested.
"""
NAME = "Planner"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._init_actions([PlanAction])
def __init__(
self,
memory: GptsMemory,
agent_context: AgentContext,
agents: Optional[List[Agent]] = None,
max_consecutive_auto_reply: Optional[int] = None,
human_input_mode: Optional[str] = "NEVER",
**kwargs,
):
super().__init__(
name=self.NAME,
memory=memory,
system_message=self.DEFAULT_SYSTEM_MESSAGE,
max_consecutive_auto_reply=max_consecutive_auto_reply,
human_input_mode=human_input_mode,
agent_context=agent_context,
**kwargs,
)
self._agents = agents
### register planning funtion
self.register_reply(Agent, PlannerAgent._a_planning)
def build_param(self, agent_context: AgentContext):
resources = []
if agent_context.resource_db is not None:
db_connect = CFG.LOCAL_DB_MANAGE.get_connect(
agent_context.resource_db.get("name")
)
resources.append(
f"{agent_context.resource_db.get('type')}:{agent_context.resource_db.get('name')}\n{db_connect.get_table_info()}"
)
if agent_context.resource_knowledge is not None:
resources.append(
f"{agent_context.resource_knowledge.get('type')}:{agent_context.resource_knowledge.get('name')}\n{agent_context.resource_knowledge.get('introduce')}"
)
if agent_context.resource_internet is not None:
resources.append(
f"{agent_context.resource_internet.get('type')}:{agent_context.resource_internet.get('name')}\n{agent_context.resource_internet.get('introduce')}"
)
return {
"all_resources": "\n".join([f"- {item}" for item in resources]),
def _init_reply_message(self, recive_message):
reply_message = super()._init_reply_message(recive_message)
reply_message["context"] = {
"agents": "\n".join(
[f"- {item.name}:{item.describe}" for item in self._agents]
[f"- {item.profile}:{item.desc}" for item in self.agents]
),
}
return reply_message
async def a_system_fill_param(self):
params = self.build_param(self.agent_context)
self.update_system_message(self.DEFAULT_SYSTEM_MESSAGE.format(**params))
def bind_agents(self, agents: List[ConversableAgent]) -> ConversableAgent:
self.agents = agents
for agent in self.agents:
if agent.resources and len(agent.resources) > 0:
self.resources.extend(agent.resources)
return self
async def a_generate_reply(
self,
message: Optional[Dict],
sender: Agent,
reviewer: Agent,
silent: Optional[bool] = False,
rely_messages: Optional[List[Dict]] = None,
):
final, reply_message = await super().a_generate_reply(
message, sender, reviewer, silent, rely_messages
)
reply_message["is_termination"] = True
return final, reply_message
async def _a_planning(
self,
message: Optional[str] = None,
sender: Optional[Agent] = None,
reviewer: Optional[Agent] = None,
config: Optional[Any] = None,
) -> Tuple[bool, Union[str, Dict, None]]:
json_objects = find_json_objects(message)
plan_objects = []
fail_reason = (
"Please recheck your answerno usable plans generated in correct format"
)
json_count = len(json_objects)
rensponse_succ = True
if json_count != 1:
### Answer failed, turn on automatic repair
fail_reason += f"There are currently {json_count} json contents"
rensponse_succ = False
else:
try:
for item in json_objects[0]:
plan = GptsPlan(
conv_id=self.agent_context.conv_id,
sub_task_num=item.get("serial_number"),
sub_task_content=item.get("content"),
)
plan.resource_name = item.get("resource")
plan.max_retry_times = self.agent_context.max_retry_round
plan.sub_task_agent = item.get("agent")
plan.sub_task_title = item.get("content")
plan.rely = item.get("rely")
plan.retry_times = 0
plan.status = Status.TODO.value
plan_objects.append(plan)
except Exception as e:
fail_reason += f"Return json structure error and cannot be converted to a usable plan{str(e)}"
rensponse_succ = False
if rensponse_succ:
if len(plan_objects) > 0:
### Delete the old plan every time before saving it
self.memory.plans_memory.remove_by_conv_id(self.agent_context.conv_id)
self.memory.plans_memory.batch_save(plan_objects)
content = "\n".join(
[
"{},{}".format(index + 1, item.get("content"))
for index, item in enumerate(json_objects[0])
]
)
else:
content = fail_reason
return True, {
"is_exe_success": rensponse_succ,
"content": content,
"view": content,
}
def prepare_act_param(self) -> Optional[Dict]:
return {"context": self.agent_context, "plans_memory": self.memory.plans_memory}

View File

@@ -1,14 +1,14 @@
import logging
import sys
from typing import Any, List, Optional
from typing import Any, Dict, List, Optional
from dbgpt.agent.agents.agent import Agent, AgentContext
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 import ConversableAgent
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.agent.memory.gpts_memory import GptsMemory
from dbgpt.core.interface.message import ModelMessageRoleType
from .planner_agent import PlannerAgent
@@ -19,53 +19,39 @@ logger = logging.getLogger(__name__)
class AutoPlanChatManager(ManagerAgent):
"""(In preview) A chat manager agent that can manage a team chat of multiple agents."""
NAME = "plan_manager"
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,
memory: GptsMemory,
agent_context: AgentContext,
# unlimited consecutive auto reply by default
max_consecutive_auto_reply: Optional[int] = sys.maxsize,
human_input_mode: Optional[str] = "NEVER",
describe: Optional[str] = "Plan chat manager.",
**kwargs,
):
super().__init__(
name=self.NAME,
describe=describe,
memory=memory,
max_consecutive_auto_reply=max_consecutive_auto_reply,
human_input_mode=human_input_mode,
agent_context=agent_context,
**kwargs,
)
# Order of register_reply is important.
# Allow async chat if initiated using a_initiate_chat
self.register_reply(Agent, AutoPlanChatManager.a_run_chat)
def __init__(self, **kwargs):
super().__init__(**kwargs)
async def a_process_rely_message(
self, conv_id: str, now_plan: GptsPlan, speaker: ConversableAgent
):
rely_prompt = ""
speaker.reset_rely_message()
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
)
rely_tasks = self.memory.plans_memory.get_by_conv_id_and_num(conv_id, [])
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:
speaker.append_rely_message(
{"content": rely_task.sub_task_content},
ModelMessageRoleType.HUMAN,
rely_messages.append(
{
"content": rely_task.sub_task_content,
"role": ModelMessageRoleType.HUMAN,
}
)
speaker.append_rely_message(
{"content": rely_task.result}, ModelMessageRoleType.AI
rely_messages.append(
{
"content": rely_task.result,
"role": ModelMessageRoleType.AI,
}
)
return rely_prompt
return rely_prompt, rely_messages
def select_speaker_msg(self, agents: List[Agent]):
"""Return the message for selecting the next speaker."""
@@ -92,10 +78,9 @@ class AutoPlanChatManager(ManagerAgent):
model = None
else:
# auto speaker selection
selector.update_system_message(self.select_speaker_msg(agents))
final, name, model = await selector.a_reasoning_reply(
self.messages
+ [
# 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.
@@ -103,7 +88,8 @@ class AutoPlanChatManager(ManagerAgent):
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!")
@@ -124,44 +110,38 @@ class AutoPlanChatManager(ManagerAgent):
logger.exception(f"auto select speaker failed!{str(e)}")
raise ValueError("Unable to select next speaker!")
async def a_generate_speech_process(
async def a_act(
self,
message: Optional[str],
reviewer: Agent,
agents: Optional[List[Agent]] = None,
) -> None:
planner = PlannerAgent(
agent_context=self.agent_context,
memory=self.memory,
agents=agents,
)
await self.a_initiate_chat(
message=message, recipient=planner, reviewer=reviewer
)
async def a_run_chat(
self,
message: Optional[str] = None,
sender: Optional[Agent] = None,
reviewer: Agent = None,
config: Optional[Any] = None,
):
"""Run a team chat asynchronously."""
sender: Optional[ConversableAgent] = None,
reviewer: Optional[ConversableAgent] = None,
) -> Optional[ActionOutput]:
speaker = sender
last_message = None
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:
error_report = {
"content": f"Retrying 3 times based on current application resources still fails to build a valid plan",
"is_exe_success": False,
}
return True, error_report
###Have no plan, generate a new plan
await self.a_generate_speech_process(message, reviewer, self.agents)
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
@@ -171,51 +151,21 @@ class AutoPlanChatManager(ManagerAgent):
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
print(f"fDEBUG:[{last_message}]")
return (
True,
last_message.get("action_report") if last_message else None,
return ActionOutput(
is_exe_success=True,
content=f"{plans[-1].result}", # work results message
)
else:
now_plan: GptsPlan = todo_plans[0]
# There is no need to broadcast the message to other agents, it will be automatically obtained from the collective memory according to the dependency relationship.
try:
if Status.RETRYING.value == now_plan.state:
if now_plan.retry_times <= now_plan.max_retry_times:
current_goal_message = {
"content": now_plan.result,
"current_gogal": now_plan.sub_task_content,
"context": {
"plan_task": now_plan.sub_task_content,
"plan_task_num": now_plan.sub_task_num,
},
}
else:
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,
)
faild_report = {
"content": f"ReTask [{now_plan.sub_task_content}] was retried more than the maximum number of times and still failed.{now_plan.result}",
"is_exe_success": False,
}
return True, faild_report
else:
current_goal_message = {
"content": now_plan.sub_task_content,
"current_gogal": now_plan.sub_task_content,
"context": {
"plan_task": now_plan.sub_task_content,
"plan_task_num": now_plan.sub_task_num,
},
}
now_plan: GptsPlan = todo_plans[0]
current_goal_message = {
"content": now_plan.sub_task_content,
"current_gogal": 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,
@@ -224,7 +174,7 @@ class AutoPlanChatManager(ManagerAgent):
now_plan.sub_task_agent,
)
# Tell the speaker the dependent history information
rely_prompt = await self.a_process_rely_message(
rely_prompt, rely_messages = await self.a_process_rely_message(
conv_id=self.agent_context.conv_id,
now_plan=now_plan,
speaker=speaker,
@@ -234,27 +184,26 @@ class AutoPlanChatManager(ManagerAgent):
rely_prompt + current_goal_message["content"]
)
is_recovery = False
if message == current_goal_message["content"]:
is_recovery = True
await self.a_send(
message=current_goal_message,
recipient=speaker,
reviewer=reviewer,
request_reply=False,
is_recovery=is_recovery,
)
verify_pass, reply = await speaker.a_generate_reply(
current_goal_message, self, reviewer
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
)
last_message = reply
print(f"DEBUG2:[{last_message}]")
plan_result = ""
if verify_pass:
if reply:
action_report = reply.get("action_report", None)
if is_success:
if reply_message:
action_report = reply_message.get("action_report", None)
if action_report:
plan_result = action_report.get("content", "")
### The current planned Agent generation verification is successful
@@ -264,31 +213,30 @@ class AutoPlanChatManager(ManagerAgent):
now_plan.sub_task_num,
plan_result,
)
await speaker.a_send(
reply, self, reviewer, request_reply=False
)
else:
plan_result = reply["content"]
plan_result = reply_message["content"]
self.memory.plans_memory.update_task(
self.agent_context.conv_id,
now_plan.sub_task_num,
Status.RETRYING.value,
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)}"
)
error_report = {
"content": f"An exception was encountered during the execution of the current plan step.{str(e)}",
"is_exe_success": False,
}
return True, error_report
return True, {
"content": f"Maximum number of dialogue rounds exceeded.{self.MAX_CONSECUTIVE_AUTO_REPLY}",
"is_exe_success": False,
}
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}",
)