Manager agent delegates task to wrong agent in a hierarchical process

I am working on the use case of a customer support system. This crew will helps to Categorizes the ticket as either a technical issue or a billing issue and assigns the ticket to the appropriate support agent and resolves the issue and provides solutions and finally reviews the solutions to ensure quality, trying to implement the hierarchical process, but here the manager agent is also executing the agent that is not related, in this case it suppose to execute only Techinical agent, but also exiecuting billing agent. For example if the input is I tried logging into my account today, but I keep receiving an ‘Invalid credentials’ error message., the manager able to categorize it as a technical issue and assign it to the technical support agent and resolve it, but the billing support agent is also executing it, but it is not related to this ticket. Below is the code snippet, please assist on how to make sure the manager agent delegates the task to the appropriate agent and not execute the billing support agent.


from crewai import Agent, Crew, Process, Task
from dotenv import load_dotenv

load_dotenv()

# Define the manager agent
manager = Agent(
    role="Customer Support Manager",
    goal="Oversee the support team to ensure timely and effective resolution of customer inquiries",
    backstory=(
        "You are a seasoned customer support manager with extensive experience in leading support teams. "
        "Your primary responsibility is to coordinate the efforts of support agents, ensuring that customer issues "
        "are addressed promptly and satisfactorily. You excel in task delegation, performance monitoring, and "
        "maintaining high standards of customer service."
    ),
    allow_delegation=True,
    verbose=True,
)

# Define the technical support agent
technical_support_agent = Agent(
    role="Technical Support Specialist",
    goal="Resolve technical issues reported by customers promptly and effectively",
    backstory=(
        "You are a highly skilled technical support specialist with a strong background in troubleshooting software and hardware issues. "
        "Your primary responsibility is to assist customers in resolving technical problems, ensuring their satisfaction and the smooth operation of their products."
    ),
    allow_delegation=False,
    verbose=True,
)

# Define the billing support agent
billing_support_agent = Agent(
    role="Billing Support Specialist",
    goal="Address customer inquiries related to billing, payments, and account management",
    backstory=(
        "You are an experienced billing support specialist with expertise in handling customer billing inquiries. "
        "Your main objective is to provide clear and accurate information regarding billing processes, resolve payment issues, and assist with account management to ensure customer satisfaction."
    ),
    allow_delegation=False,
    verbose=True,
)

# Define tasks
categorize_tickets = Task(
    description="Categorize the incoming customer support ticket: '{ticket} based on its content to determine if it is technical or billing-related.",
    expected_output="A categorized ticket labeled as 'Technical' or 'Billing'.",
    agent=manager,
)

resolve_technical_issues = Task(
    description="Resolve technical issues described in the ticket: '{ticket}'",
    expected_output="Detailed solutions provided to each technical issue.",
    agent=technical_support_agent,
)

resolve_billing_issues = Task(
    description="Resolve billing issues described in the ticket: '{ticket}'",
    expected_output="Comprehensive responses to each billing-related inquiry.",
    agent=billing_support_agent,
)

quality_assurance_review = Task(
    description="Review the resolutions provided for both technical and billing issues to ensure accuracy and customer satisfaction.",
    expected_output="A report confirming the quality and accuracy of the resolutions.",
    agent=manager,
)

# Instantiate your crew with a custom manager and hierarchical process
crew_q = Crew(
    agents=[technical_support_agent, billing_support_agent],
    tasks=[categorize_tickets, resolve_technical_issues, resolve_billing_issues, quality_assurance_review],
    manager_agent=manager,
    process=Process.hierarchical,
    verbose=True,
)

Thank you,
Siva Kumar

3 Likes

hi @sivakumarl were you able to solve your issue? We are facing similar issue when working with Chat agent. Manager agent is not delegating tasks to specific agent but executing all the agent at the same time.

Hope anyone else can assist us as well.

@sivakumarl, after carefully rereading the documentation on defining a custom manager agent, it’s clear that an agent is created, but this agent doesn’t have a task explicitly declared. My guess is that the system will internally construct the task for this agent, taking into account the details of the other agents. Also, you created a task called categorize_tickets, assigned this task to your manager agent, and also listed this task in the tasks parameter of your crew.

I suggest you follow the documentation, excluding the task manually assigned to your manager agent, and try your code again. If you succeed, please come back here and report it, so others can benefit from your progress.

Update - Functional Version of the Code

Folks, dealing with hierarchical processes requires some care, especially ensuring that your prompts don’t create an infinite loop. Try to clearly define the responses to facilitate the manager’s work.

I corrected and adapted the code presented above. I removed the task that was assigned to the manager, as it already assumes this responsibility internally. In my code, I’m using Gemini as the LLM, but you can certainly adapt it to your needs. The code below was executed successfully, hope it helps:

import os
from crewai import Agent, Task, Crew, LLM, Process

os.environ["GEMINI_API_KEY"] = "<YOUR_KEY>"

# LLM
gemini_flash_llm = LLM(
    model="gemini/gemini-2.0-flash",
    temperature=0.6,
    timeout=90,
    max_tokens=500
)

# Agents
technical_support_agent = Agent(
    role="Technical Support Specialist",
    goal="Resolve technical issues reported by customers promptly and "
    "effectively",
    backstory=(
        "You are a highly skilled technical support specialist with a "
        "strong background in troubleshooting software and hardware "
        "issues. Your primary responsibility is to assist customers "
        "in resolving technical problems, ensuring their satisfaction "
        "and the smooth operation of their products."
    ),
    llm=gemini_flash_llm,
    allow_delegation=False,
    verbose=True
)

billing_support_agent = Agent(
    role="Billing Support Specialist",
    goal="Address customer inquiries related to billing, payments, "
    "and account management",
    backstory=(
        "You are an experienced billing support specialist with "
        "expertise in handling customer billing inquiries. Your main "
        "objective is to provide clear and accurate information "
        "regarding billing processes, resolve payment issues, and "
        "assist with account management to ensure customer "
        "satisfaction."
    ),
    llm=gemini_flash_llm,
    allow_delegation=False,
    verbose=True
)

# Tasks
resolve_technical_issues = Task(
    description="Resolve only technical issues described in the "
    "ticket: {ticket}",
    expected_output="Provide a solution in up to 400 characters if "
    "it is something technical, otherwise state that it is not "
    "something technical.",
    agent=technical_support_agent
)

resolve_billing_issues = Task(
    description="Resolve only billing issues described in the ticket: "
    "{ticket}",
    expected_output="Provide a solution in up to 400 characters if "
    "it is something related to billing, otherwise inform that it "
    "is not something related to billing.",
    agent=billing_support_agent
)

# Manager
manager = Agent(
    role="Customer Support Manager",
    goal="Oversee the support team to ensure timely and effective "
    "resolution of customer inquiries",
    backstory=(
        "You are a seasoned customer support manager with extensive "
        "experience in leading support teams. Your primary "
        "responsibility is to coordinate the efforts of support "
        "agents, ensuring that customer issues are addressed "
        "promptly and satisfactorily. You excel in task delegation, "
        "performance monitoring, and maintaining high standards of "
        "customer service."
    ),
    llm=gemini_flash_llm,
    allow_delegation=True,
    verbose=True
)

# Crew
crew = Crew(
    agents=[technical_support_agent, billing_support_agent],
    tasks=[resolve_technical_issues, resolve_billing_issues],
    manager_agent=manager,
    process=Process.hierarchical
)

When executed with:

# Start the crew's work
result = crew.kickoff(
    inputs={
        'ticket': "My modem is not working. No LEDs are on either."
    }
)
print(f'\n[🤖 Final Answer]\n{result.raw}\n')

The result was:

[🤖 Final Answer]
Not a billing issue. Check power outlet, adapter, and cord. Power cycle the modem. If still no LEDs, the modem likely failed and needs replacement.

When executed with:

# Start the crew's work
result = crew.kickoff(
    inputs={
        'ticket': "My modem isn't working. My bill was due 1 week ago."
    }
)
print(f'\n[🤖 Final Answer]\n{result.raw}\n')

The result was:

[🤖 Final Answer]
Your modem service is suspended due to a past-due bill. Please make a payment to restore service. Service should resume within an hour after payment.
2 Likes

Update 2 - Functional Code Using Flows

I’d like to contribute to the discussion by presenting an alternative approach to solving the proposed problem. As in many real-world scenarios, there are multiple ways to tackle an issue.

Personally, I’m not a fan of using Process.hierarchical. To me (and I emphasize, this is just my personal opinion), it feels more like a chaotic situation with a bunch of people trying to solve the same problem simultaneously, much like my group projects back in fifth grade. :sweat_smile:

In this draft I’m presenting, I’ve adopted the flows paradigm from CrewAI. I created a step where it’s decided who will participate in the decision-making process, and only those chosen entities will influence the final answer.

Of course, there’s a lot that can (and should!) be improved in the code presented: the meeting with each participant could be asynchronous for speed; better software engineering practices could be adopted, such as isolating agents and their tasks in separate files, using docstrings, using blank lines to improve readability, etc. I hope you understand that the idea was to have a self-contained, simple, didactic, and functional draft. I’m using OpenRouter, but feel free to use the LLM provider of your choice.

from crewai import Agent, Task, LLM
from crewai.flow.flow import Flow, listen, start
from pydantic import BaseModel
from typing import cast, List
import os

os.environ["OPENROUTER_API_KEY"] = ""

class MeetingPlan(BaseModel):
    chosen_specialists: List[str] = []

class CustomerServiceState(BaseModel):
    ticket: str = ""
    chosen_specialists: List[str] = []
    opinions: List[str] = []
    response: str = ""

specialists_llm = LLM(
    model='openrouter/google/gemini-2.0-flash',
    temperature=0.5,
	max_tokens=512
)

manager_llm = LLM(
    model='openrouter/google/gemini-2.0-flash',
    temperature=0.7,
	max_tokens=512
)

def technical_support_specialist(question: str) -> str:
    agent = Agent(
        role="Technical Support Specialist",
        goal="Provide expert technical support to users.",
        backstory="You are a highly skilled technical support "
                  "specialist with years of experience in resolving "
                  "complex technical issues.",
        llm=specialists_llm,
        allow_delegation=False,
        max_iter=2,
        max_execution_time=30,
        verbose=True
    )
    task = Task(
        description="Analyze the user question and provide your "
                    "opinion only on technical issues. If the question "
                    "is not related to your expertise, state 'no opinion'. "
                    "Answer objectively in 400 characters or less.\n\n"
                    f"User question: {question}",
        expected_output="Objective technical opinion on the user "
                        "question or 'no opinion'.",
        agent=agent
    )
    opinion = task.execute_sync()
    return opinion.raw

def billing_support_specialist(question: str) -> str:
    agent = Agent(
        role="Billing Support Specialist",
        goal="Provide expert opinion on billing issues.",
        backstory="You are an experienced billing support "
                  "specialist adept at resolving billing "
                  "inquiries and providing clear opinions.",
        llm=specialists_llm,
        allow_delegation=False,
        max_iter=2,
        max_execution_time=30,
        verbose=True
    )
    task = Task(
        description="Analyze the user question and provide "
                    "your opinion only on billing issues. If "
                    "the question is not related to billing, "
                    "state 'no opinion'. Answer objectively in "
                    "400 characters or less.\n\n"
                    f"User question: {question}",
        expected_output="Objective billing opinion or 'no opinion'.",
        agent=agent
    )
    opinion = task.execute_sync()
    return opinion.raw

def manager(team: str, ticket: str) -> List[str]:
    agent = Agent(
        role="Customer Support Manager",
        goal="Select the best team members for customer "
            "support meetings to resolve customer issues efficiently.",
        backstory="You are an experienced customer support "
                "manager with a proven track record of leading "
                "high-performing support teams and resolving "
                "complex customer issues effectively.",
        llm=manager_llm,
        allow_delegation=False,
        max_iter=3,
        max_execution_time=40,
        verbose=True
    )
    task = Task(
        description="Analyze the customer's ticket and determine "
                    "which specialists from your team should attend "
                    "a meeting to address the issue. Provide only a "
                    "list of names. Exclude experts who are not "
                    "relevant. If no specialist is needed, return an "
                    "empty list.\n\n"
                    f"Team: {team}\nTicket: {ticket}",
        expected_output="List of names of relevant experts from "
                        "the team or an empty list if no expert is needed.",
        agent=agent,
        output_pydantic=MeetingPlan,
    )
    meeting = task.execute_sync()
    if meeting.pydantic:
        meeting.pydantic = cast(MeetingPlan, meeting.pydantic)
        return meeting.pydantic.chosen_specialists
    else:
        raise ValueError("Invalid list of specialists: {meeting.raw}")

def client_outcome_architect(ticket: str, opinions: str) -> str:
    agent = Agent(
        role="Customer Response Architect",
        goal="Craft helpful customer responses from expert input.",
        backstory="Expert in customer service and synthesizing "
                  "information to create clear, friendly replies.",
        llm=specialists_llm,
        allow_delegation=False,
        max_iter=2,
        max_execution_time=30,
        verbose=True
    )

    task = Task(
        description="Generate customer response from ticket and "
                    "expert opinions. Be friendly and accurate. "
                    "Rely on expert input only. Answer in "
                    "500 characters or less. If no input, say: "
                    "I'm sorry, but our team can't help you.\n\n"
                    f"Ticket: {ticket}\nSpecialists opinions: {opinions}",
        expected_output="Friendly, accurate customer response or "
                        "I'm sorry, but our team can't help you.",
        agent=agent
    )
    outcome = task.execute_sync()
    return outcome.raw

class CustomerServiceFlow(Flow[CustomerServiceState]):
    available_specialists = {
        'technical_support_specialist': technical_support_specialist,
        'billing_support_specialist': billing_support_specialist,
    }

    @start()
    def schedule_meeting(self):
        team = ', '.join(
            [repr(name) for name in self.available_specialists.keys()]
        )
        ticket = self.state.ticket
        chosen_specialists = manager(team, ticket)
        self.state.chosen_specialists = chosen_specialists

    @listen(schedule_meeting)
    def conduct_meeting(self):
        opinions: List[str] = []
        for specialist in self.state.chosen_specialists:
            try:
                opinion = self.available_specialists[specialist](
                    self.state.ticket
                )
                opinions.append(f"{specialist} stated: {opinion}")
            except:
                print(f"\nError: '{specialist}' key is not available.\n")
                continue
        self.state.opinions = opinions

    @listen(conduct_meeting)
    def generate_client_response(self):
        opinions = '; '.join(self.state.opinions)
        client_response = client_outcome_architect(
            self.state.ticket, opinions
        )
        self.state.response = client_response
        return client_response

if __name__ == '__main__':
    question = input("[🤖 Help Desk]: Hi! How can I help you today?\n")
    flow = CustomerServiceFlow()
    result = flow.kickoff(
        inputs={
            "ticket": question
        }
    )
    print(f"\n[🤖 Final Answer]:\n{result}")
    print(f"\n[🤖 Flow State]:\n{flow.state}\n")
2 Likes

After executing this does the manager agent was delegating task to only one agent based on the query? or it was delegating task to billing agent as well even if the prompt is technical? what you found in the logs?

I had never used Process.hierarchical before answering in this thread. So, I took a look at the code that coordinates the hierarchical process, and, from what I understand, it simply merges the tasks (and their respective agents) into a single, unified tool. IMHO, this can lead to a pretty chaotic process, which you can see in the console output (feel free to run the code, it’s small and self-contained enough).

Precisely because I believe this approach isn’t robust, I proposed the second solution, where there’s a clear step defining which tasks/agents should be part of the final decision-making process. This approach makes more sense to me. And, in my view, it makes the solution more robust and provides better control.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.