How to Build a Manager-Workers of Agents#
Prerequisites
This guide assumes familiarity with Agents.
With the advent of increasingly powerful Large Language Models (LLMs), multi-agent systems are becoming more relevant and are expected to be particularly valuable in scenarios requiring high-levels of autonomy and/or processing of diverse sources of information.
There are various types of multi-agent systems, each serving different purposes and applications. Some notable examples include hierarchical structures, agent swarms, and mixtures of agents.
This guide demonstrates an example of a hierarchical multi-agent system (also known as manager-workers pattern) and will show you how to:
Build expert agents equipped with tools and a manager agent;
Test the expert agents individually;
Build a ManagerWorkers using the defined agents;
Execute the ManagerWorkers of agents;
Diagram: Multi-agent system shown in this how-to guide, comprising a manager agent (customer service agent) and two expert agents equipped with tools (refund specialist and satisfaction surveyor).
See also
To access short code snippets demonstrating how to use other agentic patterns in WayFlow, refer to the Reference Sheet.
To follow this guide, you need an LLM. WayFlow supports several LLM API providers. Select an LLM from the options below:
from wayflowcore.models import OCIGenAIModel
if __name__ == "__main__":
llm = OCIGenAIModel(
model_id="provider.model-id",
service_endpoint="https://url-to-service-endpoint.com",
compartment_id="compartment-id",
auth_type="API_KEY",
)
from wayflowcore.models import VllmModel
llm = VllmModel(
model_id="model-id",
host_port="VLLM_HOST_PORT",
)
from wayflowcore.models import OllamaModel
llm = OllamaModel(
model_id="model-id",
)
Building and testing expert Agents#
In this guide you will use the following helper function to print messages:
def print_messages(messages):
from wayflowcore.messagelist import MessageType
for message in messages:
message_type = message.message_type
prefix = (
f"{message_type}"
if message_type == MessageType.USER
else f"{message_type} ({message.sender})"
)
content = (
f"{message.content}"
if message_type != MessageType.TOOL_REQUEST
else f"{message.content.strip()}\n{message.tool_requests}"
)
print(f"{prefix} >>> {content}")
API Reference: MessageType
Refund specialist agent#
The refund specialist agent is equipped with two tools.
from wayflowcore.tools import tool
@tool
def check_refund_eligibility(
order_id: Annotated[str, "The unique identifier for the order."],
customer_id: Annotated[str, "The unique identifier for the customer."],
) -> Dict[str, Union[bool, float, str]]:
"""
Checks if a given order is eligible for a refund based on company policy.
Returns:
A dictionary containing eligibility status and details.
Example: {"eligible": True, "max_refundable_amount": 50.00, "reason": "Within return window"}
{"eligible": False, "reason": "Order past 30-day return window"}
"""
# Simulate checking eligibility (e.g., database lookup, policy check)
# In a real system, this would interact with backend systems.
if "123" in order_id: # Simulate eligible order
return {
"eligible": True,
"max_refundable_amount": 50.00,
"reason": "Within 30-day return window and item is returnable.",
}
elif "999" in order_id: # Simulate ineligible order
return {"eligible": False, "reason": "Order past 30-day return window."}
else: # Simulate item not found or other issue
return {"eligible": False, "reason": "Order ID not found or invalid."}
@tool
def process_refund(
order_id: Annotated[str, "The unique identifier for the order to be refunded."],
amount: Annotated[float, "The amount to be refunded."],
reason: Annotated[str, "The reason for the refund."],
) -> Dict[str, Union[bool, str]]:
"""
Processes a refund for a specific order and amount.
Returns:
A dictionary confirming the refund status.
Example: {"success": True, "refund_id": "REF_789XYZ", "message": "Refund processed successfully."}
{"success": False, "message": "Refund processing failed due to payment gateway error."}
"""
# Simulate refund processing (e.g., calling a payment gateway API)
# In a real system, this would trigger financial transactions.
if float(amount) > 0:
refund_id = f"REF_{order_id[:3]}{int(amount * 100)}" # Generate a pseudo-unique ID
return {
"success": True,
"refund_id": refund_id,
"message": f"Refund of ${amount:.2f} processed successfully.",
}
else:
return {"success": False, "message": "Refund amount must be greater than zero."}
API Reference: tool
The first tool is used to check whether a given order is eligible for a refund, while the second is used to process the specific refund.
System prompt#
REFUND_SPECIALIST_SYSTEM_PROMPT = """
You are a Refund Specialist agent whose objective is to process customer refund requests accurately and efficiently based on company policy.
# Instructions
- Receive the refund request details (e.g., order ID, customer ID, reason) from the 'CustomerServiceManager'.
- Use the `check_refund_eligibility` tool to verify if the request meets the refund policy criteria using the provided order and customer IDs.
- If the check indicates eligibility, determine the correct refund amount (up to the maximum allowed from the eligibility check).
- If eligible, use the `process_refund` tool to execute the refund for the determined amount, providing order ID and reason.
- If ineligible based on the check, clearly note the reason provided by the tool.
- Report the final outcome (e.g., "Refund processed successfully, Refund ID: [ID], Amount: [Amount]", or "Refund denied: [Reason from eligibility check]") back to the 'CustomerServiceManager'.
- Do not engage in general conversation; focus solely on the refund process.
""".strip()
Important
The quality of the system prompt is paramount to ensuring proper behaviour of the multi-agent system, because slight deviations in the behaviour can lead to cascading unintended effects as the number of agents scales up.
Building the Agent#
from wayflowcore.agent import Agent
refund_specialist_agent = Agent(
name="RefundSpecialist",
description="Specializes in processing customer refund requests by verifying eligibility and executing the refund transaction using available tools.",
llm=llm,
custom_instruction=REFUND_SPECIALIST_SYSTEM_PROMPT,
tools=[check_refund_eligibility, process_refund],
agent_id="RefundSpecialist", # for the `print_messages` utility function
)
API Reference: Agent
Testing the Agent#
refund_conversation = refund_specialist_agent.start_conversation()
refund_conversation.append_user_message(
"Please handle a refund request. Details: Order ID='123', Customer ID='CUST456', Reason='Item arrived damaged'."
# "Please handle a refund request. Details: Order ID='999', Customer ID='CUST789', Reason='No longer needed'."
# "Please handle a refund request. Details: Order ID='INVALID_ID', Customer ID='CUST101', Reason='Item defective'."
)
refund_conversation.execute()
print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
print_messages(refund_conversation.get_messages())
# USER >>> Please handle a refund request. Details: Order ID='123', Customer ID='CUST456', Reason='Item arrived damaged'.
# TOOL_REQUEST (RefundSpecialist) >>> To process the refund request, we first need to check if the order is eligible for a refund based on company policy.
# [ToolRequest(name='check_refund_eligibility', args={'order_id': '123', 'customer_id': 'CUST456'}, tool_request_id='42d8f215-e80e-4426-b8d1-9c18d6d8059e')]
# TOOL_RESULT (RefundSpecialist) >>> {'eligible': True, 'max_refundable_amount': 50.0, 'reason': 'Within 30-day return window and item is returnable.'}
# TOOL_REQUEST (RefundSpecialist) >>> The order is eligible for a refund. Now, we need to process the refund for the maximum refundable amount.
# [ToolRequest(name='process_refund', args={'order_id': '123', 'amount': 50.0, 'reason': 'Item arrived damaged'}, tool_request_id='6b8f07eb-2caa-4551-9ea3-dcd2b53916b8')]
# TOOL_RESULT (RefundSpecialist) >>> {'success': True, 'refund_id': 'REF_1235000', 'message': 'Refund of $50.00 processed successfully.'}
# AGENT (RefundSpecialist) >>> Refund processed successfully. Refund ID: REF_1235000, Amount: $50.00
Test the agents individually to ensure they perform as expected.
Statisfaction surveyor agent#
Tools#
The statisfaction surveyor agent is equipped with one tool.
@tool
def record_survey_response(
customer_id: Annotated[str, "The unique identifier for the customer."],
satisfaction_score: Annotated[
Optional[int], "The customer's satisfaction rating (e.g., 1-5), if provided."
] = None,
comments: Annotated[
Optional[str], "Any additional comments provided by the customer, if provided."
] = None,
) -> Dict[str, Union[bool, str]]:
"""
Records the customer's satisfaction survey response.
Returns:
A dictionary confirming the recording status.
Example: {"success": True, "message": "Survey response recorded."}
{"success": False, "message": "Failed to record survey response."}
"""
# Simulate storing the response (e.g., writing to a database or logging system)
# In a real system, this would persist the feedback data.
if customer_id:
return {"success": True, "message": "Survey response recorded successfully."}
else:
return {"success": False, "message": "Customer ID is required to record response."}
The record_survey_response tool is simulating the recording of
user feedback data.
System prompt#
SURVEYOR_SYSTEM_PROMPT = """
You are a Satisfaction Surveyor agent tasked with collecting customer feedback about their recent service experience in a friendly manner.
# Instructions
- Receive the trigger to conduct a survey from the 'CustomerServiceManager', including context like the customer ID and the nature of the interaction if provided.
- Politely ask the customer if they have a moment to provide feedback on their recent interaction.
- If the customer agrees, ask 1-2 concise questions about their satisfaction (e.g., "On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?", "Is there anything else you'd like to share about your experience?").
- Use the `record_survey_response` tool to log the customer's feedback, including the satisfaction score and any comments provided. Ensure you pass the correct customer ID.
- If the customer declines to participate, thank them for their time anyway. Do not pressure them. Use the `record_survey_response` tool to log the declination if possible (e.g., score=None, comments="Declined survey").
- Thank the customer for their participation if they provided feedback.
- Report back to the 'CustomerServiceManager' confirming that the survey was attempted and whether it was completed or declined.
""".strip()
Building the Agent#
surveyor_agent = Agent(
name="SatisfactionSurveyor",
description="Conducts brief surveys to gather feedback on customer satisfaction following service interactions.",
llm=llm,
custom_instruction=SURVEYOR_SYSTEM_PROMPT,
tools=[record_survey_response],
agent_id="SatisfactionSurveyor", # for the `print_messages` utility function
)
Testing the Agent#
surveyor_conversation = surveyor_agent.start_conversation()
surveyor_conversation.append_user_message(
"Engage customer for feedback. Details: Customer ID='CUST456', Context='Recent successful refund'."
)
surveyor_conversation.execute()
print_messages([surveyor_conversation.get_last_message()])
# AGENT (SatisfactionSurveyor) >>> Hi there, I'm reaching out on behalf of our Customer Service team. We're glad to hear that your recent refund was successful. If you have a moment, we'd greatly appreciate any feedback you can share about your experience. It will help us improve our services. Would you be willing to answer a couple of quick questions?
surveyor_conversation.append_user_message("yes")
surveyor_conversation.execute()
print_messages([surveyor_conversation.get_last_message()])
# AGENT (SatisfactionSurveyor) >>> On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?
surveyor_conversation.append_user_message("5")
surveyor_conversation.execute()
print_messages([surveyor_conversation.get_last_message()])
# AGENT (SatisfactionSurveyor) >>> Is there anything else you'd like to share about your experience?
surveyor_conversation.append_user_message("Very quick!")
surveyor_conversation.execute()
print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
print_messages(surveyor_conversation.get_messages())
# USER >>> Engage customer for feedback. Details: Customer ID='CUST456', Context='Recent successful refund'.
# AGENT (SatisfactionSurveyor) >>> Hi there, I'm reaching out on behalf of our Customer Service team. We're glad to hear that your recent refund was successful. If you have a moment, we'd greatly appreciate any feedback you can share about your experience. It will help us improve our services. Would you be willing to answer a couple of quick questions?
# USER >>> yes
# AGENT (SatisfactionSurveyor) >>> On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?
# USER >>> 5
# AGENT (SatisfactionSurveyor) >>> Is there anything else you'd like to share about your experience?
# USER >>> Very quick!
# TOOL_REQUEST (SatisfactionSurveyor) >>> Now that we have the customer's feedback, we can record it using the record_survey_response tool.
# [ToolRequest(name='record_survey_response', args={'customer_id': 'CUST456', 'satisfaction_score': 5, 'comments': 'Very quick!'}, tool_request_id='3d7ad387-13b0-465c-a425-42b50b7cfdd4')]
# TOOL_RESULT (SatisfactionSurveyor) >>> {'success': True, 'message': 'Survey response recorded successfully.'}
# TOOL_REQUEST (SatisfactionSurveyor) >>> Now that we have the customer's feedback, we can record it using the record_survey_response tool.
# [ToolRequest(name='record_survey_response', args={'customer_id': 'CUST456', 'satisfaction_score': 5, 'comments': 'Very quick!'}, tool_request_id='e1d00a28-ecbe-4c6f-91fc-a75c13e2e467')]
# TOOL_RESULT (SatisfactionSurveyor) >>> {'success': True, 'message': 'Survey response recorded successfully.'}
# AGENT (SatisfactionSurveyor) >>> Thank you so much for taking the time to share your feedback with us! Your input is invaluable in helping us improve our services. Have a great day!
Again, the expert agent behaves as intended.
Manager Agent#
In the our built-in ManagerWorkers component, we allow passing an Agent as the group manager. Therefore, we just need to define an agent as usual.
In this example, our manager agent will be a Customer Service Manager.
System prompt#
MANAGER_SYSTEM_PROMPT = """
You are a Customer Service Manager agent tasked with handling incoming customer interactions and orchestrating the resolution process efficiently.
# Instructions
- Greet the customer politely and acknowledge their message.
- Analyze the customer's message to understand their core need (e.g., refund request, general query, feedback).
- Answer common informational questions (e.g., about shipping times, return policy basics) directly if you have the knowledge, before delegating.
- If the request is clearly about a refund, gather necessary details (like Order ID) if missing, and then assign the task to the 'RefundSpecialist' agent. Provide all relevant context.
- If the interaction seems successfully concluded (e.g., refund processed, query answered) and requesting feedback is appropriate, assign the task to the 'SatisfactionSurveyor' agent. Provide customer context.
- For general queries you cannot handle directly and that don't fit the specialist agents, state your limitations clearly and politely.
- Await responses or status updates from specialist agents you have assigned to.
- Summarize the final outcome or confirmation for the customer based on specialist agent reports.
- Maintain a helpful, empathetic, and professional tone throughout the interaction.
# Additional Context
Customer ID: {{customer_id}}
Company policies: {{company_policy_info}}
""".strip()
Building the manager Agent#
customer_service_manager = Agent(
name="CustomerServiceManager",
description="Acts as the primary contact point for customer inquiries, analyzes the request, routes tasks to specialized agents (Refund Specialist, Satisfaction Surveyor), and ensures resolution.",
llm=llm,
custom_instruction=MANAGER_SYSTEM_PROMPT,
agent_id="CustomerServiceManager", # for the `print_messages` utility function
)
Building and testing ManagerWorkers of Agents#
Building the ManagerWorkers of Agents#
from wayflowcore.managerworkers import ManagerWorkers
group = ManagerWorkers(
group_manager=customer_service_manager,
workers=[refund_specialist_agent, surveyor_agent],
)
API Reference: ManagerWorkers
The ManagerWorkers has two main parameters:
group_managerThis can be either an Agent or an LLM.If an LLM is provided, a manager agent will automatically be created using that LLM along with the default
custom_instructionfor group managers.In this example, we explicitly pass an Agent (the Customer Service Manager Agent) so we can use our own defined
custom_instruction.
workers- List of Agents These agents serve as the workers within the group and are coordinated by the manager agent.Worker agents cannot interact with the end user directly.
When invoked, each worker can leverage its equipped tools to complete the assigned task and report the result back to the group manager.
Executing the ManagerWorkers#
The power of mult-agent systems is their high adaptiveness. In the following example, it is demonstrated how the manager can decide not to call the expert agents for simple user queries.
main_conversation = group.start_conversation(
inputs={
"customer_id": "CUST456",
"company_policy_info": "Shipping times: 3-5 business days in the US",
}
)
main_conversation.append_user_message(
"Hi, I was wondering what your standard shipping times are for orders within the US?"
)
main_conversation.execute()
"""
last_message = main_conversation.get_last_message()
print(f"{last_message.message_type} >>> {last_message.content or last_message.tool_requests}")
# AGENT >>> Hello! Thank you for reaching out to us. Our standard shipping times for orders within the US are 3-5 business days. Is there anything else I can help you with?
main_conversation.append_user_message("Okay, thanks! Does that include weekends?")
main_conversation.execute()
last_message = main_conversation.get_last_message()
print(f"{last_message.message_type} >>> {last_message.content or last_message.tool_requests}")
# AGENT >>> Our standard shipping times of 3-5 business days are for weekdays only (Monday through Friday). Weekends and holidays are not included in that timeframe. If you have any other questions or need further assistance, feel free to ask!
print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
print_messages(main_conversation.get_messages())
# USER >>> Hi, I was wondering what your standard shipping times are for orders within the US?
# AGENT >>> Hello! Thank you for reaching out to us. Our standard shipping times for orders within the US are 3-5 business days. Is there anything else I can help you with?
# USER >>> Okay, thanks! Does that include weekends?
# AGENT >>> Our standard shipping times of 3-5 business days are for weekdays only (Monday through Friday). Weekends and holidays are not included in that timeframe. If you have any other questions or need further assistance, feel free to ask!
"""
However, the manager is explicitly prompted to assign to the specialized agents for more complex tasks. This is demonstrated in the following example.
main_conversation = group.start_conversation(inputs={
"customer_id": "CUST456",
"company_policy_info": "Shipping times: 3-5 business days in the US"
})
main_conversation.execute()
"""
print_messages([main_conversation.get_last_message()])
main_conversation.append_user_message("Thank you")
main_conversation.execute()
print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
print_messages(main_conversation.get_messages())
# ------------------------------
# FULL CONVERSATION:
# ------------------------------
# USER >>> Hi, I need to request a refund for order #123. The item wasn't what I expected.
# TOOL_REQUEST (CustomerServiceManager) >>> The user is requesting a refund for order #123 because the item did not meet their expectations. I will forward this request to the Refund Specialist to verify the eligibility and process the refund accordingly.
# [ToolRequest(name='send_message', args={'message': 'A customer is requesting a refund for order #123 due to the item not meeting their expectations. Please verify the eligibility for the refund and proceed with the necessary steps.', 'recipient': 'RefundSpecialist'}, tool_request_id='19c3cad0-683f-4aa5-9047-57f446405932')]
# TOOL_RESULT (None) >>> The refund for order #123 has been processed successfully. The refund amount is $50.00. Refund ID: REF_789XYZ
# AGENT (CustomerServiceManager) >>> Your refund for order #123 has been processed successfully. The refund amount of $50.00 has been issued to you. If there's anything else you need, please feel free to ask!
# USER >>> Thank you
# TOOL_REQUEST (CustomerServiceManager) >>> --- MESSAGE: From: CustomerServiceManager ---
# I will now initiate a brief satisfaction survey with the user regarding their refund experience to ensure complete service quality.
# [ToolRequest(name='send_message', args={'message': 'Please conduct a brief satisfaction survey with the customer regarding their recent refund experience for order #123.', 'recipient': 'SatisfactionSurveyor'}, tool_request_id='b2368dd2-4bab-447d-9bf3-a6483b403beb')]
# TOOL_RESULT (None) >>> Hi there, I hope you’re doing well. My name is John, and I’m a Satisfaction Surveyor. I’m reaching out because you recently had a refund experience with us for order #123. I wanted to take a moment to thank you for being our customer, and I was wondering if you might have a moment to provide some feedback on that experience. Would you be willing to answer a couple of quick questions about your recent interaction?
# AGENT (CustomerServiceManager) >>> Hi there, I hope you’re doing well. My name is John, and I’m a Satisfaction Surveyor. I’m reaching out because you recently had a refund experience with us for order #123. I wanted to take a moment to thank you for being our customer, and I was wondering if you might have a moment to provide some feedback on that experience. Would you be willing to answer a couple of quick questions about your recent interaction?
# ... can continue similarly to the example above with the satisfaction surveyor
"""
Agent Spec Exporting/Loading#
You can export the assistant configuration to its Agent Spec configuration using the AgentSpecExporter.
from wayflowcore.agentspec import AgentSpecExporter
serialized_group = AgentSpecExporter().to_yaml(group)
Here is what the Agent Spec representation will look like ↓
Click here to see the assistant configuration.
{
"component_type": "ManagerWorkers",
"id": "3a3ac4d1-5b2e-4f5d-8e8e-2ef3f447651c",
"name": "managerworkers_a4ce9181__auto",
"description": "",
"metadata": {
"__metadata_info__": {}
},
"inputs": [],
"outputs": [],
"group_manager": {
"component_type": "Agent",
"id": "CustomerServiceManager",
"name": "CustomerServiceManager",
"description": "Acts as the primary contact point for customer inquiries, analyzes the request, routes tasks to specialized agents (Refund Specialist, Satisfaction Surveyor), and ensures resolution.",
"metadata": {
"__metadata_info__": {}
},
"inputs": [
{
"description": "\"customer_id\" input variable for the template",
"type": "string",
"title": "customer_id"
},
{
"description": "\"company_policy_info\" input variable for the template",
"type": "string",
"title": "company_policy_info"
}
],
"outputs": [],
"llm_config": {
"$component_ref": "8ac9179d-eec5-45da-bb77-500265cb830f"
},
"system_prompt": "You are a Customer Service Manager agent tasked with handling incoming customer interactions and orchestrating the resolution process efficiently.\n\n# Instructions\n- Greet the customer politely and acknowledge their message.\n- Analyze the customer's message to understand their core need (e.g., refund request, general query, feedback).\n- Answer common informational questions (e.g., about shipping times, return policy basics) directly if you have the knowledge, before delegating.\n- If the request is clearly about a refund, gather necessary details (like Order ID) if missing, and then assign the task to the 'RefundSpecialist' agent. Provide all relevant context.\n- If the interaction seems successfully concluded (e.g., refund processed, query answered) and requesting feedback is appropriate, assign the task to the 'SatisfactionSurveyor' agent. Provide customer context.\n- For general queries you cannot handle directly and that don't fit the specialist agents, state your limitations clearly and politely.\n- Await responses or status updates from specialist agents you have assigned to.\n- Summarize the final outcome or confirmation for the customer based on specialist agent reports.\n- Maintain a helpful, empathetic, and professional tone throughout the interaction.\n\n# Additional Context\nCustomer ID: {{customer_id}}\nCompany policies: {{company_policy_info}}",
"tools": []
},
"workers": [
{
"component_type": "Agent",
"id": "RefundSpecialist",
"name": "RefundSpecialist",
"description": "Specializes in processing customer refund requests by verifying eligibility and executing the refund transaction using available tools.",
"metadata": {
"__metadata_info__": {}
},
"inputs": [],
"outputs": [],
"llm_config": {
"$component_ref": "8ac9179d-eec5-45da-bb77-500265cb830f"
},
"system_prompt": "You are a Refund Specialist agent whose objective is to process customer refund requests accurately and efficiently based on company policy.\n\n# Instructions\n- Receive the refund request details (e.g., order ID, customer ID, reason) from the 'CustomerServiceManager'.\n- Use the `check_refund_eligibility` tool to verify if the request meets the refund policy criteria using the provided order and customer IDs.\n- If the check indicates eligibility, determine the correct refund amount (up to the maximum allowed from the eligibility check).\n- If eligible, use the `process_refund` tool to execute the refund for the determined amount, providing order ID and reason.\n- If ineligible based on the check, clearly note the reason provided by the tool.\n- Report the final outcome (e.g., \"Refund processed successfully, Refund ID: [ID], Amount: [Amount]\", or \"Refund denied: [Reason from eligibility check]\") back to the 'CustomerServiceManager'.\n- Do not engage in general conversation; focus solely on the refund process.",
"tools": [
{
"component_type": "ServerTool",
"id": "89a5b809-f66f-4206-83d3-ab46debb83c2",
"name": "check_refund_eligibility",
"description": "Checks if a given order is eligible for a refund based on company policy.\n\nReturns:\n A dictionary containing eligibility status and details.\n Example: {\"eligible\": True, \"max_refundable_amount\": 50.00, \"reason\": \"Within return window\"}\n {\"eligible\": False, \"reason\": \"Order past 30-day return window\"}",
"metadata": {
"__metadata_info__": {}
},
"inputs": [
{
"description": "The unique identifier for the order.",
"type": "string",
"title": "order_id"
},
{
"description": "The unique identifier for the customer.",
"type": "string",
"title": "customer_id"
}
],
"outputs": [
{
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "number"
},
{
"type": "string"
}
]
},
"title": "tool_output"
}
]
},
{
"component_type": "ServerTool",
"id": "958dfe5b-7124-4186-a2ac-0687e09db652",
"name": "process_refund",
"description": "Processes a refund for a specific order and amount.\n\nReturns:\n A dictionary confirming the refund status.\n Example: {\"success\": True, \"refund_id\": \"REF_789XYZ\", \"message\": \"Refund processed successfully.\"}\n {\"success\": False, \"message\": \"Refund processing failed due to payment gateway error.\"}",
"metadata": {
"__metadata_info__": {}
},
"inputs": [
{
"description": "The unique identifier for the order to be refunded.",
"type": "string",
"title": "order_id"
},
{
"description": "The amount to be refunded.",
"type": "number",
"title": "amount"
},
{
"description": "The reason for the refund.",
"type": "string",
"title": "reason"
}
],
"outputs": [
{
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
}
]
},
"title": "tool_output"
}
]
}
]
},
{
"component_type": "Agent",
"id": "SatisfactionSurveyor",
"name": "SatisfactionSurveyor",
"description": "Conducts brief surveys to gather feedback on customer satisfaction following service interactions.",
"metadata": {
"__metadata_info__": {}
},
"inputs": [],
"outputs": [],
"llm_config": {
"$component_ref": "8ac9179d-eec5-45da-bb77-500265cb830f"
},
"system_prompt": "You are a Satisfaction Surveyor agent tasked with collecting customer feedback about their recent service experience in a friendly manner.\n\n# Instructions\n- Receive the trigger to conduct a survey from the 'CustomerServiceManager', including context like the customer ID and the nature of the interaction if provided.\n- Politely ask the customer if they have a moment to provide feedback on their recent interaction.\n- If the customer agrees, ask 1-2 concise questions about their satisfaction (e.g., \"On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?\", \"Is there anything else you'd like to share about your experience?\").\n- Use the `record_survey_response` tool to log the customer's feedback, including the satisfaction score and any comments provided. Ensure you pass the correct customer ID.\n- If the customer declines to participate, thank them for their time anyway. Do not pressure them. Use the `record_survey_response` tool to log the declination if possible (e.g., score=None, comments=\"Declined survey\").\n- Thank the customer for their participation if they provided feedback.\n- Report back to the 'CustomerServiceManager' confirming that the survey was attempted and whether it was completed or declined.",
"tools": [
{
"component_type": "ServerTool",
"id": "95652709-278d-4f78-9ecf-dcf47d095b2d",
"name": "record_survey_response",
"description": "Records the customer's satisfaction survey response.\n\nReturns:\n A dictionary confirming the recording status.\n Example: {\"success\": True, \"message\": \"Survey response recorded.\"}\n {\"success\": False, \"message\": \"Failed to record survey response.\"}",
"metadata": {
"__metadata_info__": {}
},
"inputs": [
{
"description": "The unique identifier for the customer.",
"type": "string",
"title": "customer_id"
},
{
"description": "The customer's satisfaction rating (e.g., 1-5), if provided.",
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "satisfaction_score",
"default": null
},
{
"description": "Any additional comments provided by the customer, if provided.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "comments",
"default": null
}
],
"outputs": [
{
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
}
]
},
"title": "tool_output"
}
]
}
]
}
],
"$referenced_components": {
"8ac9179d-eec5-45da-bb77-500265cb830f": {
"component_type": "VllmConfig",
"id": "8ac9179d-eec5-45da-bb77-500265cb830f",
"name": "llm_65f5df68__auto",
"description": null,
"metadata": {
"__metadata_info__": {}
},
"default_generation_parameters": null,
"url": "VLLM_HOST_PORT",
"model_id": "model-id"
}
},
"agentspec_version": "25.4.2"
}
component_type: ManagerWorkers
id: 92d09dcd-0eb4-43d7-8626-e9879a7e0ec2
name: managerworkers_ec2b1679__auto
description: ''
metadata:
__metadata_info__: {}
inputs: []
outputs: []
group_manager:
component_type: Agent
id: CustomerServiceManager
name: CustomerServiceManager
description: Acts as the primary contact point for customer inquiries, analyzes
the request, routes tasks to specialized agents (Refund Specialist, Satisfaction
Surveyor), and ensures resolution.
metadata:
__metadata_info__: {}
inputs:
- description: '"customer_id" input variable for the template'
type: string
title: customer_id
- description: '"company_policy_info" input variable for the template'
type: string
title: company_policy_info
outputs: []
llm_config:
$component_ref: 6cc05856-534f-4684-bbd0-b9c98021ab8b
system_prompt: 'You are a Customer Service Manager agent tasked with handling incoming
customer interactions and orchestrating the resolution process efficiently.
# Instructions
- Greet the customer politely and acknowledge their message.
- Analyze the customer''s message to understand their core need (e.g., refund
request, general query, feedback).
- Answer common informational questions (e.g., about shipping times, return policy
basics) directly if you have the knowledge, before delegating.
- If the request is clearly about a refund, gather necessary details (like Order
ID) if missing, and then assign the task to the ''RefundSpecialist'' agent. Provide
all relevant context.
- If the interaction seems successfully concluded (e.g., refund processed, query
answered) and requesting feedback is appropriate, assign the task to the ''SatisfactionSurveyor''
agent. Provide customer context.
- For general queries you cannot handle directly and that don''t fit the specialist
agents, state your limitations clearly and politely.
- Await responses or status updates from specialist agents you have assigned to.
- Summarize the final outcome or confirmation for the customer based on specialist
agent reports.
- Maintain a helpful, empathetic, and professional tone throughout the interaction.
# Additional Context
Customer ID: {{customer_id}}
Company policies: {{company_policy_info}}'
tools: []
workers:
- component_type: Agent
id: RefundSpecialist
name: RefundSpecialist
description: Specializes in processing customer refund requests by verifying eligibility
and executing the refund transaction using available tools.
metadata:
__metadata_info__: {}
inputs: []
outputs: []
llm_config:
$component_ref: 6cc05856-534f-4684-bbd0-b9c98021ab8b
system_prompt: 'You are a Refund Specialist agent whose objective is to process
customer refund requests accurately and efficiently based on company policy.
# Instructions
- Receive the refund request details (e.g., order ID, customer ID, reason) from
the ''CustomerServiceManager''.
- Use the `check_refund_eligibility` tool to verify if the request meets the refund
policy criteria using the provided order and customer IDs.
- If the check indicates eligibility, determine the correct refund amount (up
to the maximum allowed from the eligibility check).
- If eligible, use the `process_refund` tool to execute the refund for the determined
amount, providing order ID and reason.
- If ineligible based on the check, clearly note the reason provided by the tool.
- Report the final outcome (e.g., "Refund processed successfully, Refund ID: [ID],
Amount: [Amount]", or "Refund denied: [Reason from eligibility check]") back to
the ''CustomerServiceManager''.
- Do not engage in general conversation; focus solely on the refund process.'
tools:
- component_type: ServerTool
id: 7df19064-1b82-4e9e-9168-2edd052b07ad
name: check_refund_eligibility
description: "Checks if a given order is eligible for a refund based on company\
\ policy.\n\nReturns:\n A dictionary containing eligibility status and details.\n\
\ Example: {\"eligible\": True, \"max_refundable_amount\": 50.00, \"reason\"\
: \"Within return window\"}\n {\"eligible\": False, \"reason\":\
\ \"Order past 30-day return window\"}"
metadata:
__metadata_info__: {}
inputs:
- description: The unique identifier for the order.
type: string
title: order_id
- description: The unique identifier for the customer.
type: string
title: customer_id
outputs:
- type: object
additionalProperties:
anyOf:
- type: boolean
- type: number
- type: string
title: tool_output
- component_type: ServerTool
id: 4fe2caf8-eacf-40b6-853c-6763477748d3
name: process_refund
description: "Processes a refund for a specific order and amount.\n\nReturns:\n\
\ A dictionary confirming the refund status.\n Example: {\"success\":\
\ True, \"refund_id\": \"REF_789XYZ\", \"message\": \"Refund processed successfully.\"\
}\n {\"success\": False, \"message\": \"Refund processing failed\
\ due to payment gateway error.\"}"
metadata:
__metadata_info__: {}
inputs:
- description: The unique identifier for the order to be refunded.
type: string
title: order_id
- description: The amount to be refunded.
type: number
title: amount
- description: The reason for the refund.
type: string
title: reason
outputs:
- type: object
additionalProperties:
anyOf:
- type: boolean
- type: string
title: tool_output
- component_type: Agent
id: SatisfactionSurveyor
name: SatisfactionSurveyor
description: Conducts brief surveys to gather feedback on customer satisfaction
following service interactions.
metadata:
__metadata_info__: {}
inputs: []
outputs: []
llm_config:
$component_ref: 6cc05856-534f-4684-bbd0-b9c98021ab8b
system_prompt: 'You are a Satisfaction Surveyor agent tasked with collecting customer
feedback about their recent service experience in a friendly manner.
# Instructions
- Receive the trigger to conduct a survey from the ''CustomerServiceManager'',
including context like the customer ID and the nature of the interaction if provided.
- Politely ask the customer if they have a moment to provide feedback on their
recent interaction.
- If the customer agrees, ask 1-2 concise questions about their satisfaction (e.g.,
"On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with
the resolution provided today?", "Is there anything else you''d like to share
about your experience?").
- Use the `record_survey_response` tool to log the customer''s feedback, including
the satisfaction score and any comments provided. Ensure you pass the correct
customer ID.
- If the customer declines to participate, thank them for their time anyway. Do
not pressure them. Use the `record_survey_response` tool to log the declination
if possible (e.g., score=None, comments="Declined survey").
- Thank the customer for their participation if they provided feedback.
- Report back to the ''CustomerServiceManager'' confirming that the survey was
attempted and whether it was completed or declined.'
tools:
- component_type: ServerTool
id: b25b7b4b-a880-47f2-af61-68cf232841a8
name: record_survey_response
description: "Records the customer's satisfaction survey response.\n\nReturns:\n\
\ A dictionary confirming the recording status.\n Example: {\"success\"\
: True, \"message\": \"Survey response recorded.\"}\n {\"success\"\
: False, \"message\": \"Failed to record survey response.\"}"
metadata:
__metadata_info__: {}
inputs:
- description: The unique identifier for the customer.
type: string
title: customer_id
- description: The customer's satisfaction rating (e.g., 1-5), if provided.
anyOf:
- type: integer
- type: 'null'
title: satisfaction_score
default: null
- description: Any additional comments provided by the customer, if provided.
anyOf:
- type: string
- type: 'null'
title: comments
default: null
outputs:
- type: object
additionalProperties:
anyOf:
- type: boolean
- type: string
title: tool_output
$referenced_components:
6cc05856-534f-4684-bbd0-b9c98021ab8b:
component_type: VllmConfig
id: 6cc05856-534f-4684-bbd0-b9c98021ab8b
name: llm_9112faef__auto
description: null
metadata:
__metadata_info__: {}
default_generation_parameters: null
url: VLLM_HOST_PORT
model_id: model-id
agentspec_version: 25.4.2
You can load it back using the AgentSpecLoader.
from wayflowcore.agentspec import AgentSpecLoader
TOOL_REGISTRY = {
"record_survey_response": record_survey_response,
"check_refund_eligibility": check_refund_eligibility,
"process_refund": process_refund,
}
deserialized_group: ManagerWorkers = AgentSpecLoader(
tool_registry=TOOL_REGISTRY
).load_yaml(serialized_group)
Next steps#
Now that you have learned how to define a ManagerWorkers, you may proceed to Build a Swarm of Agents.
Full code#
Click on the card at the top of this page to download the full code for this guide or copy the code below.
1# Copyright © 2025 Oracle and/or its affiliates.
2#
3# This software is under the Apache License 2.0
4# %%[markdown]
5# Code Example - Build a ManagerWorkers of Agents
6# -----------------------------------------------
7
8# How to use:
9# Create a new Python virtual environment and install the latest WayFlow version.
10# ```bash
11# python -m venv venv-wayflowcore
12# source venv-wayflowcore/bin/activate
13# pip install --upgrade pip
14# pip install "wayflowcore==26.1"
15# ```
16
17# You can now run the script
18# 1. As a Python file:
19# ```bash
20# python howto_managerworkers.py
21# ```
22# 2. As a Notebook (in VSCode):
23# When viewing the file,
24# - press the keys Ctrl + Enter to run the selected cell
25# - or Shift + Enter to run the selected cell and move to the cell below# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
26# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.
27
28from typing import Annotated, Dict, Optional, Union
29
30from wayflowcore.agent import Agent
31from wayflowcore.models import VllmModel
32from wayflowcore.tools import tool
33
34llm = VllmModel(
35 model_id="model-id",
36 host_port="VLLM_HOST_PORT",
37)
38
39
40
41# %%[markdown]
42## Helper method for printing conversation messages
43
44# %%
45def print_messages(messages):
46 from wayflowcore.messagelist import MessageType
47
48 for message in messages:
49 message_type = message.message_type
50 prefix = (
51 f"{message_type}"
52 if message_type == MessageType.USER
53 else f"{message_type} ({message.sender})"
54 )
55 content = (
56 f"{message.content}"
57 if message_type != MessageType.TOOL_REQUEST
58 else f"{message.content.strip()}\n{message.tool_requests}"
59 )
60 print(f"{prefix} >>> {content}")
61
62
63
64
65# %%[markdown]
66## Specialist tools
67
68# %%
69from wayflowcore.tools import tool
70
71@tool
72def check_refund_eligibility(
73 order_id: Annotated[str, "The unique identifier for the order."],
74 customer_id: Annotated[str, "The unique identifier for the customer."],
75) -> Dict[str, Union[bool, float, str]]:
76 """
77 Checks if a given order is eligible for a refund based on company policy.
78
79 Returns:
80 A dictionary containing eligibility status and details.
81 Example: {"eligible": True, "max_refundable_amount": 50.00, "reason": "Within return window"}
82 {"eligible": False, "reason": "Order past 30-day return window"}
83 """
84 # Simulate checking eligibility (e.g., database lookup, policy check)
85 # In a real system, this would interact with backend systems.
86 if "123" in order_id: # Simulate eligible order
87 return {
88 "eligible": True,
89 "max_refundable_amount": 50.00,
90 "reason": "Within 30-day return window and item is returnable.",
91 }
92 elif "999" in order_id: # Simulate ineligible order
93 return {"eligible": False, "reason": "Order past 30-day return window."}
94 else: # Simulate item not found or other issue
95 return {"eligible": False, "reason": "Order ID not found or invalid."}
96
97@tool
98def process_refund(
99 order_id: Annotated[str, "The unique identifier for the order to be refunded."],
100 amount: Annotated[float, "The amount to be refunded."],
101 reason: Annotated[str, "The reason for the refund."],
102) -> Dict[str, Union[bool, str]]:
103 """
104 Processes a refund for a specific order and amount.
105
106 Returns:
107 A dictionary confirming the refund status.
108 Example: {"success": True, "refund_id": "REF_789XYZ", "message": "Refund processed successfully."}
109 {"success": False, "message": "Refund processing failed due to payment gateway error."}
110 """
111 # Simulate refund processing (e.g., calling a payment gateway API)
112 # In a real system, this would trigger financial transactions.
113 if float(amount) > 0:
114 refund_id = f"REF_{order_id[:3]}{int(amount * 100)}" # Generate a pseudo-unique ID
115 return {
116 "success": True,
117 "refund_id": refund_id,
118 "message": f"Refund of ${amount:.2f} processed successfully.",
119 }
120 else:
121 return {"success": False, "message": "Refund amount must be greater than zero."}
122
123
124
125# %%[markdown]
126## Specialist prompt
127
128# %%
129REFUND_SPECIALIST_SYSTEM_PROMPT = """
130You are a Refund Specialist agent whose objective is to process customer refund requests accurately and efficiently based on company policy.
131
132# Instructions
133- Receive the refund request details (e.g., order ID, customer ID, reason) from the 'CustomerServiceManager'.
134- Use the `check_refund_eligibility` tool to verify if the request meets the refund policy criteria using the provided order and customer IDs.
135- If the check indicates eligibility, determine the correct refund amount (up to the maximum allowed from the eligibility check).
136- If eligible, use the `process_refund` tool to execute the refund for the determined amount, providing order ID and reason.
137- If ineligible based on the check, clearly note the reason provided by the tool.
138- Report the final outcome (e.g., "Refund processed successfully, Refund ID: [ID], Amount: [Amount]", or "Refund denied: [Reason from eligibility check]") back to the 'CustomerServiceManager'.
139- Do not engage in general conversation; focus solely on the refund process.
140""".strip()
141
142
143# %%[markdown]
144## Specialist agent
145
146# %%
147from wayflowcore.agent import Agent
148
149refund_specialist_agent = Agent(
150 name="RefundSpecialist",
151 description="Specializes in processing customer refund requests by verifying eligibility and executing the refund transaction using available tools.",
152 llm=llm,
153 custom_instruction=REFUND_SPECIALIST_SYSTEM_PROMPT,
154 tools=[check_refund_eligibility, process_refund],
155 agent_id="RefundSpecialist", # for the `print_messages` utility function
156)
157
158
159
160# %%[markdown]
161## Specialist test
162
163# %%
164refund_conversation = refund_specialist_agent.start_conversation()
165refund_conversation.append_user_message(
166 "Please handle a refund request. Details: Order ID='123', Customer ID='CUST456', Reason='Item arrived damaged'."
167 # "Please handle a refund request. Details: Order ID='999', Customer ID='CUST789', Reason='No longer needed'."
168 # "Please handle a refund request. Details: Order ID='INVALID_ID', Customer ID='CUST101', Reason='Item defective'."
169)
170refund_conversation.execute()
171print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
172print_messages(refund_conversation.get_messages())
173
174# USER >>> Please handle a refund request. Details: Order ID='123', Customer ID='CUST456', Reason='Item arrived damaged'.
175# TOOL_REQUEST (RefundSpecialist) >>> To process the refund request, we first need to check if the order is eligible for a refund based on company policy.
176# [ToolRequest(name='check_refund_eligibility', args={'order_id': '123', 'customer_id': 'CUST456'}, tool_request_id='42d8f215-e80e-4426-b8d1-9c18d6d8059e')]
177# TOOL_RESULT (RefundSpecialist) >>> {'eligible': True, 'max_refundable_amount': 50.0, 'reason': 'Within 30-day return window and item is returnable.'}
178# TOOL_REQUEST (RefundSpecialist) >>> The order is eligible for a refund. Now, we need to process the refund for the maximum refundable amount.
179# [ToolRequest(name='process_refund', args={'order_id': '123', 'amount': 50.0, 'reason': 'Item arrived damaged'}, tool_request_id='6b8f07eb-2caa-4551-9ea3-dcd2b53916b8')]
180# TOOL_RESULT (RefundSpecialist) >>> {'success': True, 'refund_id': 'REF_1235000', 'message': 'Refund of $50.00 processed successfully.'}
181# AGENT (RefundSpecialist) >>> Refund processed successfully. Refund ID: REF_1235000, Amount: $50.00
182
183
184# %%[markdown]
185## Surveyor tools
186
187# %%
188@tool
189def record_survey_response(
190 customer_id: Annotated[str, "The unique identifier for the customer."],
191 satisfaction_score: Annotated[
192 Optional[int], "The customer's satisfaction rating (e.g., 1-5), if provided."
193 ] = None,
194 comments: Annotated[
195 Optional[str], "Any additional comments provided by the customer, if provided."
196 ] = None,
197) -> Dict[str, Union[bool, str]]:
198 """
199 Records the customer's satisfaction survey response.
200
201 Returns:
202 A dictionary confirming the recording status.
203 Example: {"success": True, "message": "Survey response recorded."}
204 {"success": False, "message": "Failed to record survey response."}
205 """
206 # Simulate storing the response (e.g., writing to a database or logging system)
207 # In a real system, this would persist the feedback data.
208 if customer_id:
209 return {"success": True, "message": "Survey response recorded successfully."}
210 else:
211 return {"success": False, "message": "Customer ID is required to record response."}
212
213
214
215
216# %%[markdown]
217## Surveyor prompt
218
219# %%
220SURVEYOR_SYSTEM_PROMPT = """
221You are a Satisfaction Surveyor agent tasked with collecting customer feedback about their recent service experience in a friendly manner.
222
223# Instructions
224- Receive the trigger to conduct a survey from the 'CustomerServiceManager', including context like the customer ID and the nature of the interaction if provided.
225- Politely ask the customer if they have a moment to provide feedback on their recent interaction.
226- If the customer agrees, ask 1-2 concise questions about their satisfaction (e.g., "On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?", "Is there anything else you'd like to share about your experience?").
227- Use the `record_survey_response` tool to log the customer's feedback, including the satisfaction score and any comments provided. Ensure you pass the correct customer ID.
228- If the customer declines to participate, thank them for their time anyway. Do not pressure them. Use the `record_survey_response` tool to log the declination if possible (e.g., score=None, comments="Declined survey").
229- Thank the customer for their participation if they provided feedback.
230- Report back to the 'CustomerServiceManager' confirming that the survey was attempted and whether it was completed or declined.
231""".strip()
232
233
234# %%[markdown]
235## Surveyor agent
236
237# %%
238surveyor_agent = Agent(
239 name="SatisfactionSurveyor",
240 description="Conducts brief surveys to gather feedback on customer satisfaction following service interactions.",
241 llm=llm,
242 custom_instruction=SURVEYOR_SYSTEM_PROMPT,
243 tools=[record_survey_response],
244 agent_id="SatisfactionSurveyor", # for the `print_messages` utility function
245)
246
247
248
249# %%[markdown]
250## Surveyor test
251
252# %%
253surveyor_conversation = surveyor_agent.start_conversation()
254surveyor_conversation.append_user_message(
255 "Engage customer for feedback. Details: Customer ID='CUST456', Context='Recent successful refund'."
256)
257surveyor_conversation.execute()
258print_messages([surveyor_conversation.get_last_message()])
259# AGENT (SatisfactionSurveyor) >>> Hi there, I'm reaching out on behalf of our Customer Service team. We're glad to hear that your recent refund was successful. If you have a moment, we'd greatly appreciate any feedback you can share about your experience. It will help us improve our services. Would you be willing to answer a couple of quick questions?
260surveyor_conversation.append_user_message("yes")
261surveyor_conversation.execute()
262print_messages([surveyor_conversation.get_last_message()])
263# AGENT (SatisfactionSurveyor) >>> On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?
264surveyor_conversation.append_user_message("5")
265surveyor_conversation.execute()
266print_messages([surveyor_conversation.get_last_message()])
267# AGENT (SatisfactionSurveyor) >>> Is there anything else you'd like to share about your experience?
268surveyor_conversation.append_user_message("Very quick!")
269surveyor_conversation.execute()
270print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
271print_messages(surveyor_conversation.get_messages())
272# USER >>> Engage customer for feedback. Details: Customer ID='CUST456', Context='Recent successful refund'.
273# AGENT (SatisfactionSurveyor) >>> Hi there, I'm reaching out on behalf of our Customer Service team. We're glad to hear that your recent refund was successful. If you have a moment, we'd greatly appreciate any feedback you can share about your experience. It will help us improve our services. Would you be willing to answer a couple of quick questions?
274# USER >>> yes
275# AGENT (SatisfactionSurveyor) >>> On a scale of 1 to 5, where 5 is highly satisfied, how satisfied were you with the resolution provided today?
276# USER >>> 5
277# AGENT (SatisfactionSurveyor) >>> Is there anything else you'd like to share about your experience?
278# USER >>> Very quick!
279# TOOL_REQUEST (SatisfactionSurveyor) >>> Now that we have the customer's feedback, we can record it using the record_survey_response tool.
280# [ToolRequest(name='record_survey_response', args={'customer_id': 'CUST456', 'satisfaction_score': 5, 'comments': 'Very quick!'}, tool_request_id='3d7ad387-13b0-465c-a425-42b50b7cfdd4')]
281# TOOL_RESULT (SatisfactionSurveyor) >>> {'success': True, 'message': 'Survey response recorded successfully.'}
282# TOOL_REQUEST (SatisfactionSurveyor) >>> Now that we have the customer's feedback, we can record it using the record_survey_response tool.
283# [ToolRequest(name='record_survey_response', args={'customer_id': 'CUST456', 'satisfaction_score': 5, 'comments': 'Very quick!'}, tool_request_id='e1d00a28-ecbe-4c6f-91fc-a75c13e2e467')]
284# TOOL_RESULT (SatisfactionSurveyor) >>> {'success': True, 'message': 'Survey response recorded successfully.'}
285# AGENT (SatisfactionSurveyor) >>> Thank you so much for taking the time to share your feedback with us! Your input is invaluable in helping us improve our services. Have a great day!
286
287
288# %%[markdown]
289## Manager prompt
290
291# %%
292MANAGER_SYSTEM_PROMPT = """
293You are a Customer Service Manager agent tasked with handling incoming customer interactions and orchestrating the resolution process efficiently.
294
295# Instructions
296- Greet the customer politely and acknowledge their message.
297- Analyze the customer's message to understand their core need (e.g., refund request, general query, feedback).
298- Answer common informational questions (e.g., about shipping times, return policy basics) directly if you have the knowledge, before delegating.
299- If the request is clearly about a refund, gather necessary details (like Order ID) if missing, and then assign the task to the 'RefundSpecialist' agent. Provide all relevant context.
300- If the interaction seems successfully concluded (e.g., refund processed, query answered) and requesting feedback is appropriate, assign the task to the 'SatisfactionSurveyor' agent. Provide customer context.
301- For general queries you cannot handle directly and that don't fit the specialist agents, state your limitations clearly and politely.
302- Await responses or status updates from specialist agents you have assigned to.
303- Summarize the final outcome or confirmation for the customer based on specialist agent reports.
304- Maintain a helpful, empathetic, and professional tone throughout the interaction.
305
306# Additional Context
307Customer ID: {{customer_id}}
308Company policies: {{company_policy_info}}
309""".strip()
310
311
312# %%[markdown]
313## Manager agent
314
315# %%
316customer_service_manager = Agent(
317 name="CustomerServiceManager",
318 description="Acts as the primary contact point for customer inquiries, analyzes the request, routes tasks to specialized agents (Refund Specialist, Satisfaction Surveyor), and ensures resolution.",
319 llm=llm,
320 custom_instruction=MANAGER_SYSTEM_PROMPT,
321 agent_id="CustomerServiceManager", # for the `print_messages` utility function
322)
323
324
325# %%[markdown]
326## Managerworkers pattern
327
328# %%
329from wayflowcore.managerworkers import ManagerWorkers
330
331group = ManagerWorkers(
332 group_manager=customer_service_manager,
333 workers=[refund_specialist_agent, surveyor_agent],
334)
335
336
337# %%[markdown]
338## Managerworkers answers without expert
339
340# %%
341main_conversation = group.start_conversation(
342 inputs={
343 "customer_id": "CUST456",
344 "company_policy_info": "Shipping times: 3-5 business days in the US",
345 }
346)
347main_conversation.append_user_message(
348 "Hi, I was wondering what your standard shipping times are for orders within the US?"
349)
350main_conversation.execute()
351"""
352last_message = main_conversation.get_last_message()
353print(f"{last_message.message_type} >>> {last_message.content or last_message.tool_requests}")
354# AGENT >>> Hello! Thank you for reaching out to us. Our standard shipping times for orders within the US are 3-5 business days. Is there anything else I can help you with?
355main_conversation.append_user_message("Okay, thanks! Does that include weekends?")
356main_conversation.execute()
357last_message = main_conversation.get_last_message()
358print(f"{last_message.message_type} >>> {last_message.content or last_message.tool_requests}")
359# AGENT >>> Our standard shipping times of 3-5 business days are for weekdays only (Monday through Friday). Weekends and holidays are not included in that timeframe. If you have any other questions or need further assistance, feel free to ask!
360print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
361print_messages(main_conversation.get_messages())
362# USER >>> Hi, I was wondering what your standard shipping times are for orders within the US?
363# AGENT >>> Hello! Thank you for reaching out to us. Our standard shipping times for orders within the US are 3-5 business days. Is there anything else I can help you with?
364# USER >>> Okay, thanks! Does that include weekends?
365# AGENT >>> Our standard shipping times of 3-5 business days are for weekdays only (Monday through Friday). Weekends and holidays are not included in that timeframe. If you have any other questions or need further assistance, feel free to ask!
366"""
367
368
369# %%[markdown]
370## Managerworkers answers with expert
371
372# %%
373main_conversation = group.start_conversation(inputs={
374 "customer_id": "CUST456",
375 "company_policy_info": "Shipping times: 3-5 business days in the US"
376})
377main_conversation.execute()
378"""
379print_messages([main_conversation.get_last_message()])
380
381main_conversation.append_user_message("Thank you")
382main_conversation.execute()
383
384print(f"{'-'*30}\nFULL CONVERSATION:\n{'-'*30}\n")
385print_messages(main_conversation.get_messages())
386# ------------------------------
387# FULL CONVERSATION:
388# ------------------------------
389# USER >>> Hi, I need to request a refund for order #123. The item wasn't what I expected.
390# TOOL_REQUEST (CustomerServiceManager) >>> The user is requesting a refund for order #123 because the item did not meet their expectations. I will forward this request to the Refund Specialist to verify the eligibility and process the refund accordingly.
391# [ToolRequest(name='send_message', args={'message': 'A customer is requesting a refund for order #123 due to the item not meeting their expectations. Please verify the eligibility for the refund and proceed with the necessary steps.', 'recipient': 'RefundSpecialist'}, tool_request_id='19c3cad0-683f-4aa5-9047-57f446405932')]
392# TOOL_RESULT (None) >>> The refund for order #123 has been processed successfully. The refund amount is $50.00. Refund ID: REF_789XYZ
393# AGENT (CustomerServiceManager) >>> Your refund for order #123 has been processed successfully. The refund amount of $50.00 has been issued to you. If there's anything else you need, please feel free to ask!
394# USER >>> Thank you
395# TOOL_REQUEST (CustomerServiceManager) >>> --- MESSAGE: From: CustomerServiceManager ---
396# I will now initiate a brief satisfaction survey with the user regarding their refund experience to ensure complete service quality.
397# [ToolRequest(name='send_message', args={'message': 'Please conduct a brief satisfaction survey with the customer regarding their recent refund experience for order #123.', 'recipient': 'SatisfactionSurveyor'}, tool_request_id='b2368dd2-4bab-447d-9bf3-a6483b403beb')]
398# TOOL_RESULT (None) >>> Hi there, I hope you’re doing well. My name is John, and I’m a Satisfaction Surveyor. I’m reaching out because you recently had a refund experience with us for order #123. I wanted to take a moment to thank you for being our customer, and I was wondering if you might have a moment to provide some feedback on that experience. Would you be willing to answer a couple of quick questions about your recent interaction?
399# AGENT (CustomerServiceManager) >>> Hi there, I hope you’re doing well. My name is John, and I’m a Satisfaction Surveyor. I’m reaching out because you recently had a refund experience with us for order #123. I wanted to take a moment to thank you for being our customer, and I was wondering if you might have a moment to provide some feedback on that experience. Would you be willing to answer a couple of quick questions about your recent interaction?
400
401# ... can continue similarly to the example above with the satisfaction surveyor
402"""
403
404
405# %%[markdown]
406## Export config to Agent Spec
407
408# %%
409from wayflowcore.agentspec import AgentSpecExporter
410
411serialized_group = AgentSpecExporter().to_yaml(group)
412
413
414# %%[markdown]
415## Load Agent Spec config
416
417# %%
418from wayflowcore.agentspec import AgentSpecLoader
419
420TOOL_REGISTRY = {
421 "record_survey_response": record_survey_response,
422 "check_refund_eligibility": check_refund_eligibility,
423 "process_refund": process_refund,
424}
425
426deserialized_group: ManagerWorkers = AgentSpecLoader(
427 tool_registry=TOOL_REGISTRY
428).load_yaml(serialized_group)