Hierarchical process (#206)

* Hierarchical process +  Docs
Co-authored-by: João Moura <joaomdmoura@gmail.com>
This commit is contained in:
Gui Vieira
2024-02-02 13:56:35 -03:00
committed by GitHub
parent a3af73b593
commit 7efecd10ea
12 changed files with 1358 additions and 74 deletions

View File

@@ -1,13 +1,12 @@
import uuid
from typing import Any, List, Optional
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.agents.agent import RunnableAgent
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.memory import ConversationSummaryMemory
from langchain.tools.render import render_text_description
from langchain_core.runnables.config import RunnableConfig
from langchain_openai import ChatOpenAI
from langchain_core.language_models import BaseLanguageModel
from pydantic import (
UUID4,
BaseModel,
@@ -171,7 +170,7 @@ class Agent(BaseModel):
"""
self.cache_handler = cache_handler
self.tools_handler = ToolsHandler(cache=self.cache_handler)
self.__create_agent_executor()
self._create_agent_executor()
def set_rpm_controller(self, rpm_controller: RPMController) -> None:
"""Set the rpm controller for the agent.
@@ -181,9 +180,9 @@ class Agent(BaseModel):
"""
if not self._rpm_controller:
self._rpm_controller = rpm_controller
self.__create_agent_executor()
self._create_agent_executor()
def __create_agent_executor(self) -> None:
def _create_agent_executor(self) -> None:
"""Create an agent executor for the agent.
Returns:

View File

@@ -142,18 +142,26 @@ class Crew(BaseModel):
agent.i18n = I18N(language=self.language)
if self.process == Process.sequential:
return self._sequential_loop()
else:
raise NotImplementedError(
f"The process '{self.process}' is not implemented yet."
)
return self._run_sequential_process()
if self.process == Process.hierarchical:
return self._run_hierarchical_process()
def _sequential_loop(self) -> str:
raise NotImplementedError(
f"The process '{self.process}' is not implemented yet."
)
def _run_sequential_process(self) -> str:
"""Executes tasks sequentially and returns the final output."""
task_output = ""
for task in self.tasks:
self._prepare_and_execute_task(task)
task_output = task.execute(task_output)
if task.agent is not None and task.agent.allow_delegation:
task.tools += AgentTools(agents=self.agents).tools()
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"Working Agent: {role}")
self._logger.log("info", f"Starting Task: {task.description}")
task_output = task.execute(context=task_output)
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"[{role}] Task output: {task_output}\n\n")
@@ -163,15 +171,29 @@ class Crew(BaseModel):
return task_output
def _prepare_and_execute_task(self, task: Task) -> None:
"""Prepares and logs information about the task being executed.
def _run_hierarchical_process(self) -> str:
"""Creates and assigns a manager agent to make sure the crew completes the tasks."""
Args:
task: The task to be executed.
"""
if task.agent is not None and task.agent.allow_delegation:
task.tools += AgentTools(agents=self.agents).tools()
i18n = I18N(language=self.language)
manager = Agent(
role=i18n.retrieve("hierarchical_manager_agent", "role"),
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
tools=AgentTools(agents=self.agents).tools(),
verbose=True,
)
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"Working Agent: {role}")
self._logger.log("info", f"Starting Task: {task.description}")
task_output = ""
for task in self.tasks:
self._logger.log("info", f"Starting Task: {task.description}")
task_output = task.execute(agent=manager, context=task_output)
self._logger.log(
"debug", f"[{manager.role}] Task output: {task_output}\n\n"
)
if self.max_rpm:
self._rpm_controller.stop_rpm_counter()
return task_output

View File

@@ -7,5 +7,5 @@ class Process(str, Enum):
"""
sequential = "sequential"
hierarchical = "hierarchical"
# TODO: consensual = 'consensual'
# TODO: hierarchical = 'hierarchical'

View File

@@ -19,7 +19,7 @@ class Task(BaseModel):
description="Callback to be executed after the task is completed.", default=None
)
agent: Optional[Agent] = Field(
description="Agent responsible for executiong the task.", default=None
description="Agent responsible for execution the task.", default=None
)
expected_output: Optional[str] = Field(
description="Clear definition of expected output for the task.",
@@ -53,18 +53,20 @@ class Task(BaseModel):
self.tools.extend(self.agent.tools)
return self
def execute(self, context: Optional[str] = None) -> str:
def execute(self, agent: Agent | None = None, context: Optional[str] = None) -> str:
"""Execute the task.
Returns:
Output of the task.
"""
if not self.agent:
agent = agent or self.agent
if not agent:
raise Exception(
f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, either consensual or hierarchical."
f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, like hierarchical."
)
result = self.agent.execute_task(
result = agent.execute_task(
task=self._prompt(), context=context, tools=self.tools
)

View File

@@ -33,13 +33,13 @@ class AgentTools(BaseModel):
def delegate_work(self, command):
"""Useful to delegate a specific task to a coworker."""
return self.__execute(command)
return self._execute(command)
def ask_question(self, command):
"""Useful to ask a question, opinion or take from a coworker."""
return self.__execute(command)
return self._execute(command)
def __execute(self, command):
def _execute(self, command):
"""Execute the command."""
try:
agent, task, context = command.split("|")

View File

@@ -1,4 +1,9 @@
{
"hierarchical_manager_agent": {
"role": "Διευθυντής Ομάδας",
"goal": "Διαχειρίσου την ομάδα σου για να ολοκληρώσει την εργασία με τον καλύτερο δυνατό τρόπο.",
"backstory": "Είσαι ένας έμπειρος διευθυντής με την ικανότητα να βγάζεις το καλύτερο από την ομάδα σου.\nΕίσαι επίσης γνωστός για την ικανότητά σου να αναθέτεις εργασίες στους σωστούς ανθρώπους και να κάνεις τις σωστές ερωτήσεις για να πάρεις το καλύτερο από την ομάδα σου.\nΑκόμα κι αν δεν εκτελείς εργασίες μόνος σου, έχεις πολλή εμπειρία στον τομέα, που σου επιτρέπει να αξιολογείς σωστά τη δουλειά των μελών της ομάδας σου."
},
"slices": {
"observation": "\nΠαρατήρηση",
"task": "Αρχή! Αυτό είναι ΠΟΛΥ σημαντικό για εσάς, η δουλειά σας εξαρτάται από αυτό!\n\nΤρέχουσα εργασία: {input}",

View File

@@ -1,4 +1,9 @@
{
"hierarchical_manager_agent": {
"role": "Crew Manager",
"goal": "Manage the team to complete the task in the best way possible.",
"backstory": "You are a seasoned manager with a knack for getting the best out of your team.\nYou are also known for your ability to delegate work to the right people, and to ask the right questions to get the best out of your team.\nEven though you don't perform tasks by yourself, you have a lot of experience in the field, which allows you to properly evaluate the work of your team members."
},
"slices": {
"observation": "\nObservation",
"task": "Begin! This is VERY important to you, your job depends on it!\n\nCurrent Task: {input}",
@@ -18,4 +23,4 @@
"delegate_work": "Useful to delegate a specific task to one of the following co-workers: {coworkers}.\nThe input to this tool should be a pipe (|) separated text of length 3 (three), representing the co-worker you want to ask it to (one of the options), the task and all actual context you have for the task.\nFor example, `coworker|task|context`.",
"ask_question": "Useful to ask a question, opinion or take from on of the following co-workers: {coworkers}.\nThe input to this tool should be a pipe (|) separated text of length 3 (three), representing the co-worker you want to ask it to (one of the options), the question and all actual context you have for the question.\n For example, `coworker|question|context`."
}
}
}