Skip to content

Commit

Permalink
refactor(agent): Agent modular refactoring (#1487)
Browse files Browse the repository at this point in the history
  • Loading branch information
fangyinc authored May 7, 2024
1 parent 2a418f9 commit 863b540
Show file tree
Hide file tree
Showing 86 changed files with 4,506 additions and 960 deletions.
9 changes: 6 additions & 3 deletions dbgpt/agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""DB-GPT Multi-Agents Module."""

from .actions.action import Action, ActionOutput # noqa: F401
from .core.action import * # noqa: F401, F403
from .core.agent import ( # noqa: F401
Agent,
AgentContext,
Expand All @@ -13,12 +13,15 @@
initialize_agent,
)
from .core.base_agent import ConversableAgent # noqa: F401
from .core.llm.llm import LLMConfig # noqa: F401
from .core.memory import * # noqa: F401, F403
from .core.memory.gpts.gpts_memory import GptsMemory # noqa: F401
from .core.plan import * # noqa: F401, F403
from .core.profile import * # noqa: F401, F403
from .core.schema import PluginStorageType # noqa: F401
from .core.user_proxy_agent import UserProxyAgent # noqa: F401
from .memory.gpts_memory import GptsMemory # noqa: F401
from .resource.resource_api import AgentResource, ResourceType # noqa: F401
from .resource.resource_loader import ResourceLoader # noqa: F401
from .util.llm.llm import LLMConfig # noqa: F401

__ALL__ = [
"Agent",
Expand Down
1 change: 0 additions & 1 deletion dbgpt/agent/actions/__init__.py

This file was deleted.

23 changes: 22 additions & 1 deletion dbgpt/agent/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
"""Core Module for the Agent."""
"""Core Module for the Agent.
There are four modules in DB-GPT agent core according the paper
`A survey on large language model based autonomous agents
<https://link.springer.com/article/10.1007/s11704-024-40231-1>`
by `Lei Wang, Chen Ma, Xueyang Feng, et al.`:
1. Profiling Module: The profiling module aims to indicate the profiles of the agent
roles.
2. Memory Module: It stores information perceived from the environment and leverages
the recorded memories to facilitate future actions.
3. Planning Module: When faced with a complex task, humans tend to deconstruct it into
simpler subtasks and solve them individually. The planning module aims to empower the
agents with such human capability, which is expected to make the agent behave more
reasonably, powerfully, and reliably
4. Action Module: The action module is responsible for translating the agent’s
decisions into specific outcomes. This module is located at the most downstream
position and directly interacts with the environment.
"""
20 changes: 20 additions & 0 deletions dbgpt/agent/core/action/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Action Module.
The action module is responsible for translating the agent’s decisions into specific
outcomes. This module is located at the most downstream position and directly interacts
with the environment. It is influenced by the profile, memory, and planning modules.
The Goal Of The Action Module:
--------
1. Task Completion: Complete specific tasks, write a function in software development,
and make an iron pick in the game.
2. Communication: Communicate with other agents.
3. Environment exploration: Explore unfamiliar environments to expand its perception
and strike a balance between exploring and exploiting.
"""

from .base import Action, ActionOutput # noqa: F401
from .blank_action import BlankAction # noqa: F401
22 changes: 19 additions & 3 deletions dbgpt/agent/actions/action.py → dbgpt/agent/core/action/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Base Action class for defining agent actions."""

import json
from abc import ABC, abstractmethod
from typing import (
Expand All @@ -21,12 +22,13 @@
field_description,
model_fields,
model_to_dict,
model_validator,
)
from dbgpt.util.json_utils import find_json_objects
from dbgpt.vis.base import Vis

from ...vis.base import Vis
from ..resource.resource_api import AgentResource, ResourceType
from ..resource.resource_loader import ResourceLoader
from ...resource.resource_api import AgentResource, ResourceType
from ...resource.resource_loader import ResourceLoader

T = TypeVar("T", bound=Union[BaseModel, List[BaseModel], None])

Expand All @@ -41,6 +43,20 @@ class ActionOutput(BaseModel):
view: Optional[str] = None
resource_type: Optional[str] = None
resource_value: Optional[Any] = None
action: Optional[str] = None
thoughts: Optional[str] = None
observations: Optional[str] = None

@model_validator(mode="before")
@classmethod
def pre_fill(cls, values: Any) -> Any:
"""Pre-fill the values."""
if not isinstance(values, dict):
return values
is_exe_success = values.get("is_exe_success", True)
if not is_exe_success and "observations" not in values:
values["observations"] = values.get("content")
return values

@classmethod
def from_dict(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import logging
from typing import Optional

from ..resource.resource_api import AgentResource
from .action import Action, ActionOutput
from ...resource.resource_api import AgentResource
from .base import Action, ActionOutput

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -33,4 +33,8 @@ async def run(
Just return the AI message.
"""
return ActionOutput(is_exe_success=True, content=ai_message, view=ai_message)
return ActionOutput(
is_exe_success=True,
content=ai_message,
view=ai_message,
)
27 changes: 18 additions & 9 deletions dbgpt/agent/core/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from dbgpt.core import LLMClient
from dbgpt.util.annotations import PublicAPI

from ..actions.action import ActionOutput
from ..memory.gpts_memory import GptsMemory
from ..resource.resource_loader import ResourceLoader
from .action.base import ActionOutput
from .memory.agent_memory import AgentMemory


class Agent(ABC):
Expand Down Expand Up @@ -160,17 +160,20 @@ async def verify(
verification result.
"""

@property
@abstractmethod
def get_name(self) -> str:
"""Return name of the agent."""
def name(self) -> str:
"""Return the name of the agent."""

@property
@abstractmethod
def get_profile(self) -> str:
"""Return profile of the agent."""
def role(self) -> str:
"""Return the role of the agent."""

@property
@abstractmethod
def get_describe(self) -> str:
"""Return describe of the agent."""
def desc(self) -> Optional[str]:
"""Return the description of the agent."""


@dataclasses.dataclass
Expand Down Expand Up @@ -204,7 +207,7 @@ class AgentGenerateContext:
rely_messages: List[AgentMessage] = dataclasses.field(default_factory=list)
final: Optional[bool] = True

memory: Optional[GptsMemory] = None
memory: Optional[AgentMemory] = None
agent_context: Optional[AgentContext] = None
resource_loader: Optional[ResourceLoader] = None
llm_client: Optional[LLMClient] = None
Expand Down Expand Up @@ -302,3 +305,9 @@ def copy(self) -> "AgentMessage":
role=self.role,
success=self.success,
)

def get_dict_context(self) -> Dict[str, Any]:
"""Return the context as a dictionary."""
if isinstance(self.context, dict):
return self.context
return {}
14 changes: 7 additions & 7 deletions dbgpt/agent/core/agent_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def participant_roles(agents: List[Agent]) -> str:
# Default to all agents registered
roles = []
for agent in agents:
roles.append(f"{agent.get_name()}: {agent.get_describe()}")
roles.append(f"{agent.name}: {agent.desc}")
return "\n".join(roles)


Expand All @@ -34,13 +34,13 @@ def mentioned_agents(message_content: str, agents: List[Agent]) -> Dict:
mentions = dict()
for agent in agents:
regex = (
r"(?<=\W)" + re.escape(agent.get_name()) + r"(?=\W)"
r"(?<=\W)" + re.escape(agent.name) + r"(?=\W)"
) # Finds agent mentions, taking word boundaries into account
count = len(
re.findall(regex, " " + message_content + " ")
) # Pad the message to help with matching
if count > 0:
mentions[agent.get_name()] = count
mentions[agent.name] = count
return mentions


Expand Down Expand Up @@ -84,7 +84,7 @@ def register_agent(
) -> str:
"""Register an agent."""
inst = cls()
profile = inst.get_profile()
profile = inst.role
if profile in self._agents and (
profile in self._core_agents or not ignore_duplicate
):
Expand All @@ -110,13 +110,13 @@ def get_by_name(self, name: str) -> Type[ConversableAgent]:

def get_describe_by_name(self, name: str) -> str:
"""Return the description of an agent by name."""
return self._agents[name][1].desc
return self._agents[name][1].desc or ""

def all_agents(self) -> Dict[str, str]:
"""Return a dictionary of all registered agents and their descriptions."""
result = {}
for name, value in self._agents.items():
result[name] = value[1].desc
result[name] = value[1].desc or ""
return result

def list_agents(self):
Expand All @@ -125,7 +125,7 @@ def list_agents(self):
for name, value in self._agents.items():
result.append(
{
"name": value[1].profile,
"name": value[1].role,
"desc": value[1].goal,
}
)
Expand Down
Loading

0 comments on commit 863b540

Please sign in to comment.