Step 04 - Supervisor Pattern for Autonomous Agentic Orchestration
Supervisor Pattern for Autonomous Agentic Orchestration
In the previous step, you created nested workflows that combined sequential, parallel, and conditional patterns to build sophisticated multi-level orchestration.
However, those workflows used fixed, deterministic routing - the conditions were hardcoded and predictable, which is great for maintaining control over the agentic workflow. But what if outcomes are less predictable and you need dynamic, context-aware orchestration where an AI agent decides which sub-agents to invoke based on the current situation?
In this step, you’ll learn about the Supervisor Pattern - a powerful approach where a supervisor agent autonomously orchestrates other agents based on runtime context and business conditions.
New Requirement from Miles of Smiles Management: Intelligent Disposition Decisions
The Miles of Smiles management team has identified a new challenge: they need to make intelligent decisions about vehicle disposition when cars return with severe damage.
The system needs to:
- Detect severe damage that might make a car uneconomical to repair
- Estimate vehicle value to inform disposition decisions
- Decide disposition strategy (SCRAP, SELL, DONATE, or KEEP) based on:
- Car value
- Age of the vehicle
- Severity of damage
- Repair cost estimates
- Let an AI supervisor orchestrate the entire decision-making process
What You’ll Learn
In this step, you will:
- Understand the Supervisor Pattern and when to use it
- Implement a supervisor agent using
@SupervisorAgentannotation - Create specialized agents for feedback analysis and action execution
- Build a PricingAgent to estimate vehicle market values
- Create a DispositionAgent to make SCRAP/SELL/DONATE/KEEP decisions
- See how supervisors provide autonomous, adaptive orchestration
Understanding the Supervisor Pattern
What is a Supervisor Agent?
A supervisor agent is an AI agent that:
- Autonomously coordinates other (sub-)agents
- Makes runtime decisions about which agents to invoke
- Adapts to context using business rules and current conditions
- Provides autonomous orchestration without hardcoded logic
Supervisor vs. Conditional Workflows
| Aspect | Conditional Workflow | Supervisor Agent |
|---|---|---|
| Decision Logic | Hardcoded conditions | AI-driven decisions |
| Flexibility | Fixed rules | Adapts to context |
| Complexity | Simple boolean checks | Complex reasoning |
| Maintenance | Update code for changes | Update prompts/context |
When to Use Supervisors
Use supervisor agents when you need:
- Context-aware routing where decisions are based on multiple factors that are hard to predict
- Business rule flexibility that are easier to adjust without code changes
- Complex orchestration with multiple agents that have interdependencies
What is Being Added?
We’re going to enhance our car management system with:
- a FleetSupervisorAgent that will orchestrate feedback and action agents autonomously
- a DispositionFeedbackAgent that detects severe damage requiring disposition evaluation
- a PricingAgent that estimates vehicle market value
- a DispositionAgent which will decide to SCRAP/SELL/DONATE/KEEP based on value and condition
- an Updated workflow with two-phase processing (feedback → supervisor → actions)
The New Architecture
graph TB
Start([Car Return]) --> A[CarProcessingWorkflow<br/>Sequential]
A --> B[Step 1: FeedbackWorkflow<br/>Parallel Analysis]
B --> B1[CleaningFeedbackAgent]
B --> B2[MaintenanceFeedbackAgent]
B --> B3[DispositionFeedbackAgent<br/>NEW]
B1 --> BEnd[All feedback complete]
B2 --> BEnd
B3 --> BEnd
BEnd --> C[Step 2: FleetSupervisorAgent<br/>Autonomous Orchestration]
C --> C1{AI Supervisor<br/>Analyzes Feedback}
C1 -->|Severe Damage| C2[PricingAgent<br/>Estimate Value]
C2 --> C3[DispositionAgent<br/>SCRAP/SELL/DONATE/KEEP]
C1 -->|Repairable| C4[MaintenanceAgent]
C1 -->|Minor Issues| C5[CleaningAgent]
C3 --> CEnd[Supervisor Decision]
C4 --> CEnd
C5 --> CEnd
CEnd --> D[Step 3: CarConditionFeedbackAgent<br/>Final Summary]
D --> End([Updated Car with Status])
style A fill:#90EE90
style B fill:#87CEEB
style C fill:#FFB6C1
style D fill:#90EE90
style C1 fill:#FFA07A
style B3 fill:#FFD700
style C2 fill:#FFD700
style C3 fill:#FFD700
style Start fill:#E8E8E8
style End fill:#E8E8E8
The Key Innovation:
The FleetSupervisorAgent receives feedback from three parallel agents and then autonomously decides:
- If severe damage detected → invoke PricingAgent → DispositionAgent
- If repairable damage → invoke MaintenanceAgent
- If only cleaning needed → invoke CleaningAgent
Implementing the Supervisor Pattern
Let’s build the new autonomous dispositioning system step by step.
Prerequisites
Before starting:
- Completed Step 03 (or have the
section-2/step-03code available) - Application from Step 03 is stopped (Ctrl+C)
If you want to continue building on your Step 03 code, copy the updated UI files from step-04:
cd section-2/step-03
cp ../step-04/src/main/resources/META-INF/resources/css/styles.css ./src/main/resources/META-INF/resources/css/styles.css
cp ../step-04/src/main/resources/META-INF/resources/js/app.js ./src/main/resources/META-INF/resources/js/app.js
cp ../step-04/src/main/resources/META-INF/resources/index.html ./src/main/resources/META-INF/resources/index.html
cd section-2\step-03
copy ..\step-04\src\main\resources\META-INF\resources\css\styles.css .\src\main\resources\META-INF\resources\css\styles.css
copy ..\step-04\src\main\resources\META-INF\resources\js\app.js .\src\main\resources\META-INF\resources\js\app.js
copy ..\step-04\src\main\resources\META-INF\resources\index.html .\src\main\resources\META-INF\resources\index.html
Create the DispositionFeedbackAgent
Create an agent that detects severe damage requiring disposition evaluation.
In src/main/java/com/carmanagement/agentic/agents, create DispositionFeedbackAgent.java:
package com.carmanagement.agentic.agents;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
/**
* Agent that analyzes feedback to determine if a car should be considered for disposition.
*/
public interface DispositionFeedbackAgent {
@SystemMessage("""
You are a disposition analyzer for a car rental company. Your job is to determine if a car should be considered for disposition (removal from fleet).
Analyze the feedback for SEVERE issues that would make the car uneconomical to keep:
- Major accidents: "wrecked", "totaled", "destroyed", "crashed", "collision"
- Severe damage: "frame damage", "structural damage", "major damage"
- Safety concerns: "unsafe", "not drivable", "inoperable", "dangerous"
- Catastrophic mechanical failure: "engine blown", "transmission failed", "major mechanical failure"
If you detect ANY of these severe issues, respond with:
"DISPOSITION_REQUIRED: [brief description of the severe issue]"
If the car has only minor or moderate issues that can be repaired, respond with:
"DISPOSITION_NOT_REQUIRED"
Keep your response concise.
""")
@UserMessage("""
Car Information:
Make: {carMake}
Model: {carModel}
Year: {carYear}
Previous Condition: {carCondition}
Feedback:
Rental Feedback: {rentalFeedback}
Cleaning Feedback: {cleaningFeedback}
Maintenance Feedback: {maintenanceFeedback}
""")
@Agent(description = "Disposition analyzer. Determines if a car has severe damage requiring disposition evaluation.",
outputKey = "dispositionRequest")
String analyzeForDisposition(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String rentalFeedback,
String cleaningFeedback,
String maintenanceFeedback);
}
This agent runs in parallel with the other feedback agents, looking for keywords like “wrecked”, “totaled”, or “crashed” that indicate severe damage.
Create the PricingAgent
The Miles & Smiles management feels comfortable using AI to determine the value of their cars. A wise decision? That remains to be seen 😉.
Either way, let’s create the agent. We’ll add some prompt engineering in the system message to ‘train’ the AI model on how to calculate the value based on the brand, its state and its age. As a reminder, the agent will be invoked by the supervisor when pricing is needed.
In src/main/java/com/carmanagement/agentic/agents, create PricingAgent.java:
package com.carmanagement.agentic.agents;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
/**
* Agent that estimates the market value of a vehicle.
* Used by the supervisor to make disposition decisions.
*/
public interface PricingAgent {
@SystemMessage("""
You are a vehicle pricing specialist with expertise in market valuations.
Today is {current_date}. Use this to calculate the current year and vehicle age.
Use these pricing guidelines:
Brand Base Values (new current-year models):
- Luxury brands (Mercedes-Benz, BMW, Audi): $50,000-$70,000
- Premium trucks (Ford F-150): $45,000-$60,000
- Mainstream brands (Toyota, Honda, Chevrolet): $28,000-$42,000
- Economy brands (Nissan): $22,000-$35,000
Depreciation (calculate age as: current year - vehicle year):
- Age 1 year (nearly new): -12% from base value
- Age 2 years: -15% additional (27% total depreciation)
- Age 3 years: -12% additional (39% total depreciation)
- Age 4 years: -10% additional (49% total depreciation)
- Age 5+ years: -8% per additional year
Condition Adjustments (apply after depreciation):
- Excellent/Like new: +5% to depreciated value
- Good/Recently serviced: No adjustment
- Fair/Minor issues: -10% from depreciated value
- Poor/Needs work: -20% from depreciated value
Provide:
1. Estimated market value (single dollar amount with comma separator)
2. Brief justification (2-3 sentences explaining age, condition, and brand factors)
Format your response as:
Estimated Value: $XX,XXX
Justification: [Your reasoning including vehicle age]
""")
@UserMessage("""
Estimate the current market value of this vehicle:
- Make: {carMake}
- Model: {carModel}
- Year: {carYear}
- Condition: {carCondition}
""")
@Agent(
outputKey = "carValue",
description = "Pricing specialist that estimates vehicle market value based on make, model, year, and condition"
)
String estimateValue(String carMake, String carModel, Integer carYear, String carCondition);
}
Create the DispositionAgent
Management also feels comfortable letting an AI model decide whether to SCRAP, SELL, DONATE, or KEEP the vehicle based on repair economics.
Create an agent that makes disposition decisions based on the value outcome of the PricingAgent’s value estimate as well as the car’s age and condition.
In src/main/java/com/carmanagement/agentic/agents, create DispositionAgent.java:
package com.carmanagement.agentic.agents;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
/**
* Agent that determines how to dispose of a car based on value, condition, and damage.
*/
public interface DispositionAgent {
@SystemMessage("""
You are a car disposition specialist for a car rental company.
Your job is to determine the best disposition action based on the car's value, condition, age, and damage.
Disposition Options:
- SCRAP: Car is beyond economical repair or has severe safety concerns
- SELL: Car has value but is aging out of the fleet or has moderate damage
- DONATE: Car has minimal value but could serve a charitable purpose
- KEEP: Car is worth keeping in the fleet
Decision Criteria:
- If estimated repair cost > 50% of car value: Consider SCRAP or SELL
- If car is over 5 years old with significant damage: SCRAP
- If car is 3-5 years old in fair condition: SELL
- If car has low value (<$5,000) but functional: DONATE
- If car is valuable and damage is minor: KEEP
Provide your recommendation with a clear explanation of the reasoning.
""")
@UserMessage("""
Determine the disposition for this vehicle:
- Make: {carMake}
- Model: {carModel}
- Year: {carYear}
- Car Number: {carNumber}
- Current Condition: {carCondition}
- Estimated Value: {carValue}
- Damage/Feedback: {rentalFeedback}
Provide your disposition recommendation (SCRAP/SELL/DONATE/KEEP) and explanation.
""")
@Agent(outputKey = "dispositionAction", description = "Car disposition specialist. Determines how to dispose of a car based on value and condition.")
String processDisposition(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String carValue,
String rentalFeedback);
}
Create the FleetSupervisorAgent
Now create the supervisor agent that orchestrates everything. What’s absolutely key here is to make the prompt as clear as possible about the workflow and the agents that are available to it. The more precise you are, the better the supervisor will be at deciding which agents to invoke.
In src/main/java/com/carmanagement/agentic/agents, create FleetSupervisorAgent.java:
package com.carmanagement.agentic.agents;
import dev.langchain4j.agentic.declarative.SupervisorAgent;
import dev.langchain4j.agentic.declarative.SupervisorRequest;
/**
* Supervisor agent that orchestrates the entire car processing workflow.
* Coordinates feedback analysis agents and action agents based on car condition.
*/
public interface FleetSupervisorAgent {
@SupervisorAgent(
outputKey = "supervisorDecision",
subAgents = {
PricingAgent.class,
DispositionAgent.class,
MaintenanceAgent.class,
CleaningAgent.class
}
)
String superviseCarProcessing(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String rentalFeedback,
String cleaningFeedback,
String maintenanceFeedback,
String cleaningRequest,
String maintenanceRequest,
String dispositionRequest
);
@SupervisorRequest
static String request(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String cleaningRequest,
String maintenanceRequest,
String dispositionRequest,
String rentalFeedback
) {
boolean dispositionRequired = dispositionRequest != null &&
dispositionRequest.toUpperCase().contains("DISPOSITION_REQUIRED");
String noDispositionMessage = """
No disposition has been requested.
INSTRUCTIONS:
- DO NOT invoke PricingAgent
- DO NOT invoke DispositionAgent
- Only invoke MaintenanceAgent if maintenance needed
- Only invoke CleaningAgent if cleaning needed
""";
// Disposition required - complex path
String dispositionMessage = """
The car has to be disposed.
STEP 1: Invoke PricingAgent to get car value
STEP 2: Invoke DispositionAgent to decide disposition action (SCRAP/SELL/DONATE/KEEP)
STEP 3: If DispositionAgent decides KEEP:
- Invoke MaintenanceAgent if maintenance needed
- Invoke CleaningAgent if cleaning needed
IMPORTANT: When invoking DispositionAgent:
- Pass carValue as a STRING with dollar sign (e.g., "$10,710" not 10710)
- Use the EXACT format from PricingAgent's response
Follow the decision logic in your system message carefully.
""";
return String.format("""
You are a fleet supervisor for a car rental company. You coordinate action agents based on feedback analysis.
The feedback has already been analyzed and you have these inputs:
- cleaningRequest: What cleaning is needed (or "CLEANING_NOT_REQUIRED")
- maintenanceRequest: What maintenance is needed (or "MAINTENANCE_NOT_REQUIRED")
- dispositionRequest: Whether severe damage requires disposition (or "DISPOSITION_NOT_REQUIRED")
Your job is to invoke the appropriate ACTION agents for this car
Car: %d %s %s (#%d)
Current Condition: %s
Rental Feedback: %s
Cleaning Request: %s
Maintenance Request: %s
In particular, your have to follow these steps
%s
""",
carYear, carMake, carModel, carNumber, carCondition, rentalFeedback,
cleaningRequest, maintenanceRequest, dispositionRequired ? dispositionMessage : noDispositionMessage);
}
}
Key Points:
- The
@SupervisorAgentannotation enables autonomous orchestration. - The supervisor receives feedback analysis results and decides which action agents to invoke.
- Notice how the
subAgentsparameter lists only action agents (not feedback agents) - the supervisor coordinates actions after feedback is complete. - The prompt clearly explains the workflow and available agents.
@SupervisorRequeststatic method provides the initial user request to the supervisor- This method constructs a message that tells the supervisor what to do
- It receives all the method parameters and formats them into a clear request
- Without this, the supervisor would not know what action to take
Understanding @SupervisorRequest:
The @SupervisorRequest annotation is crucial for supervisor agents to function. It provides the initial context and instructions that the supervisor needs to make decisions.
The implementation uses conditional logic to provide different instructions based on whether disposition is required:
@SupervisorRequest
static String request(...) {
// Determine if disposition is required
boolean dispositionRequired = dispositionRequest != null &&
dispositionRequest.toUpperCase().contains("DISPOSITION_REQUIRED");
if (!dispositionRequired) {
// Simple path: clear instructions to avoid unnecessary agent invocations
return String.format("""
═══════════════════════════════════════════════════════════════════════════
✅ NO DISPOSITION REQUIRED
═══════════════════════════════════════════════════════════════════════════
Car: %d %s %s (#%d)
Current Condition: %s
INSTRUCTIONS:
- DO NOT invoke PricingAgent
- DO NOT invoke DispositionAgent
- Only invoke MaintenanceAgent if maintenance needed
- Only invoke CleaningAgent if cleaning needed
""", ...);
}
// Complex path: step-by-step workflow for disposition
return String.format("""
═══════════════════════════════════════════════════════════════════════════
⚠️ DISPOSITION REQUIRED - FOLLOW WORKFLOW
═══════════════════════════════════════════════════════════════════════════
STEP 1: Invoke PricingAgent to get car value
STEP 2: Invoke DispositionAgent to decide disposition action (SCRAP/SELL/DONATE/KEEP)
STEP 3: If DispositionAgent decides KEEP:
- Invoke MaintenanceAgent if maintenance needed
- Invoke CleaningAgent if cleaning needed
IMPORTANT: When invoking DispositionAgent:
- Pass carValue as a STRING with dollar sign (e.g., "$10,710" not 10710)
- Use the EXACT format from PricingAgent's response
""", ...);
}
Key Benefits of This Approach:
- Explicit guidance: The supervisor receives clear, formatted instructions that reduce ambiguity
- Prevents unnecessary invocations: When no disposition is needed, explicitly tells the supervisor NOT to invoke PricingAgent/DispositionAgent
- Step-by-step workflow: For complex disposition cases, provides a numbered sequence to follow
- Format specifications: Includes important details like how to pass the carValue parameter
This method is automatically called by the framework before the supervisor begins orchestration, providing it with the necessary context to make informed decisions about which sub-agents to invoke.
Updated FeedbackWorkflow
We need to update the FeedbackWorkflow and add the DispositionFeedbackAgent to the parallel feedback workflow.
Update src/main/java/com/carmanagement/agentic/workflow/FeedbackWorkflow.java:
package com.carmanagement.agentic.workflow;
import com.carmanagement.agentic.agents.CleaningFeedbackAgent;
import com.carmanagement.agentic.agents.MaintenanceFeedbackAgent;
import com.carmanagement.agentic.agents.DispositionFeedbackAgent;
import dev.langchain4j.agentic.declarative.ParallelAgent;
/**
* Workflow for processing car feedback in parallel.
* Analyzes feedback for cleaning, maintenance, and disposition needs.
*/
public interface FeedbackWorkflow {
/**
* Runs multiple feedback agents in parallel to analyze different aspects of car feedback.
*/
@ParallelAgent(outputKey = "feedbackResult",
subAgents = { CleaningFeedbackAgent.class, MaintenanceFeedbackAgent.class, DispositionFeedbackAgent.class })
String analyzeFeedback(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String rentalFeedback,
String cleaningFeedback,
String maintenanceFeedback);
}
Update the CarProcessingWorkflow
Since we want the supervisor to determine which agents need to be called, we will replace the previous conditional workflow with the supervisor agent, and add the additional car assignment logic based on the output of the supervisor agent.
Update src/main/java/com/carmanagement/agentic/workflow/CarProcessingWorkflow.java:
package com.carmanagement.agentic.workflow;
import com.carmanagement.agentic.agents.CarConditionFeedbackAgent;
import com.carmanagement.agentic.agents.FleetSupervisorAgent;
import com.carmanagement.model.CarConditions;
import dev.langchain4j.agentic.declarative.Output;
import dev.langchain4j.agentic.declarative.SequenceAgent;
import io.quarkus.logging.Log;
/**
* Workflow for processing car returns using a supervisor agent for complete orchestration.
* The supervisor coordinates both feedback analysis and action agents.
*/
public interface CarProcessingWorkflow {
/**
* Processes a car return by first analyzing feedback, then using supervisor to coordinate actions.
* FeedbackWorkflow produces cleaningRequest, maintenanceRequest, and dispositionRequest.
* FleetSupervisorAgent then uses these to coordinate action agents.
* CarConditionFeedbackAgent determines the final car assignment and condition.
*/
@SequenceAgent(outputKey = "carProcessingAgentResult",
subAgents = { FeedbackWorkflow.class, FleetSupervisorAgent.class, CarConditionFeedbackAgent.class })
CarConditions processCarReturn(
String carMake,
String carModel,
Integer carYear,
Integer carNumber,
String carCondition,
String rentalFeedback,
String cleaningFeedback,
String maintenanceFeedback);
@Output
static CarConditions output(CarConditions carConditions) {
// CarConditionFeedbackAgent handles all logic for determining
// the final car assignment and condition description.
Log.debug("CarConditions: " + carConditions.generalCondition() + " → " + carConditions.carAssignment());
return carConditions;
}
}
Why both check dispositionRequest and supervisorDecision?
Both checks serve different purposes in the workflow:
- First check (dispositionRequest): Catches severe damage from initial feedback analysis
- Second check (supervisorDecision): Catches final disposition decision (SCRAP/SELL/DONATE) after pricing evaluation
While there’s logical overlap (severe damage → supervisor invokes DispositionAgent → outputs SCRAP/SELL/DONATE), both checks provide defense in depth. The first ensures immediate routing for catastrophic damage, while the second catches the supervisor’s final action decision.
Update the Service Layer
Finally, we need to add the new disposition status to the processCarReturn method.
In src/main/java/com/carmanagement/service/CarManagementService.java, add disposition handling in the processCarReturn method:
// Add after existing status checks:
if (carConditions.carAssignment() == CarAssignment.DISPOSITION) {
carInfo.status = CarStatus.PENDING_DISPOSITION;
}
Try the Supervisor Pattern
Nicely done! You’ve implemented the supervisor pattern to autonomously orchestrate agents. Let’s test it:
Start the application:
Test Disposition Scenarios
Try these scenarios to see how the supervisor pattern autonomously orchestrates agents:
Scenario 1: Severe Damage - Disposition Required
Enter the following text in the feedback field for the Honda Civic:
What happens:
flowchart TD
Start([Input: Car was in serious collision<br/>Front end destroyed, airbags deployed])
Start --> FW[FeedbackWorkflow<br/>Parallel Analysis]
FW --> DFA[DispositionFeedbackAgent]
FW --> MFA[MaintenanceFeedbackAgent]
FW --> CFA[CleaningFeedbackAgent]
DFA --> DR[Detects: DISPOSITION_REQUIRED ✓<br/>Keywords: collision, destroyed]
MFA --> MR[Detects: maintenance issues ✓]
CFA --> CR[Detects: cleaning needed ✓]
DR --> FSA{FleetSupervisorAgent<br/>Autonomous Orchestration}
MR --> FSA
CR --> FSA
FSA -->|Disposition has highest priority| PA[Invoke PricingAgent]
PA --> PV[Estimate: $8,500<br/>2020 Honda Civic with severe damage]
PV --> DA[Invoke DispositionAgent]
DA --> DD[Decision: SCRAP<br/>Repair cost > 50% of value]
DD --> Result([Result: PENDING_DISPOSITION<br/>Condition: SCRAP - severe damage])
style FW fill:#FAE5D3
style FSA fill:#D5F5E3
style PA fill:#F9E79F
style DA fill:#F9E79F
style Result fill:#D2B4DE
Expected Result:
- Status: PENDING_DISPOSITION
- Condition includes disposition decision (e.g., “SCRAP - severe damage, repair cost exceeds value”)
- PricingAgent estimated the car’s value
- DispositionAgent made a SCRAP decision based on economics
Scenario 2: Total Loss
Enter the following text in the Ford F-150 feedback field from the Maintenance Returns tab:
In this scenario, the car had already been sent to maintenance by the returns team, but the maintenance team is not able to repair the car. Our autonomous system can handle this scenario as well.
What happens:
flowchart TD
Start([Input: Car is totaled<br/>completely inoperable])
Start --> FW[FeedbackWorkflow<br/>Parallel Analysis]
FW --> DFA[DispositionFeedbackAgent]
FW --> MFA[MaintenanceFeedbackAgent]
FW --> CFA[CleaningFeedbackAgent]
DFA --> DR[Detects: DISPOSITION_REQUIRED ✓<br/>Keywords: totaled, inoperable]
MFA --> MR[Analysis complete]
CFA --> CR[Analysis complete]
DR --> FSA{FleetSupervisorAgent<br/>Autonomous Orchestration}
MR --> FSA
CR --> FSA
FSA -->|Severe damage detected| PA[Invoke PricingAgent]
PA --> PV[Estimate: $12,000<br/>2019 Toyota Camry, totaled]
PV --> DA[Invoke DispositionAgent]
DA --> DD[Decision: SCRAP or SELL<br/>Beyond economical repair]
DD --> Result([Result: PENDING_DISPOSITION<br/>Condition: SCRAP/SELL - totaled])
style FW fill:#FAE5D3
style FSA fill:#D5F5E3
style PA fill:#F9E79F
style DA fill:#F9E79F
style Result fill:#D2B4DE
Expected Result:
- Status: PENDING_DISPOSITION
- Disposition decision: SCRAP or SELL (beyond economical repair)
- PricingAgent estimated value before total loss
- DispositionAgent determined vehicle is not worth repairing
Scenario 3: Repairable Damage
Enter the following text in the Mercedes Benz feedback field:
What happens:
flowchart TD
Start([Input: Engine making noise<br/>needs inspection])
Start --> FW[FeedbackWorkflow<br/>Parallel Analysis]
FW --> DFA[DispositionFeedbackAgent]
FW --> MFA[MaintenanceFeedbackAgent]
FW --> CFA[CleaningFeedbackAgent]
DFA --> DR[Detects: no severe damage ✗]
MFA --> MR[Detects: maintenance needed ✓]
CFA --> CR[Detects: no cleaning needed ✗]
DR --> FSA{FleetSupervisorAgent<br/>Autonomous Orchestration}
MR --> FSA
CR --> FSA
FSA -->|No disposition, maintenance is priority| MA[Invoke MaintenanceAgent]
MA --> Result([Result: IN_MAINTENANCE<br/>Condition: Engine inspection required])
style FW fill:#FAE5D3
style FSA fill:#D5F5E3
style MA fill:#F9E79F
style Result fill:#D2B4DE
Expected Result:
- Status:
IN_MAINTENANCE - Condition describes the maintenance issue
- Supervisor routed to MaintenanceAgent (not disposition)
Scenario 4: Minor Issues
Enter the following text in the BMW X5 feedback field (in the Maintenance Return tab):
What happens:
flowchart TD
Start([Input: Car is dirty<br/>needs cleaning])
Start --> FW[FeedbackWorkflow<br/>Parallel Analysis]
FW --> DFA[DispositionFeedbackAgent]
FW --> MFA[MaintenanceFeedbackAgent]
FW --> CFA[CleaningFeedbackAgent]
DFA --> DR[Detects: no severe damage ✗]
MFA --> MR[Detects: no maintenance needed ✗]
CFA --> CR[Detects: cleaning needed ✓]
DR --> FSA{FleetSupervisorAgent<br/>Autonomous Orchestration}
MR --> FSA
CR --> FSA
FSA -->|No disposition or maintenance| CA[Invoke CleaningAgent]
CA --> Result([Result: IN_CLEANING<br/>Condition: Requires thorough cleaning])
style FW fill:#FAE5D3
style FSA fill:#D5F5E3
style CA fill:#F9E79F
style Result fill:#D2B4DE
Expected Result:
- Status:
IN_CLEANING - Condition describes cleaning needs
- Supervisor routed to CleaningAgent only
Experiment Further
1. Add More Disposition Criteria
Enhance the DispositionAgent to consider:
- Repair history (frequent repairs → disposition)
- Market demand for specific models
- Seasonal factors (convertibles in winter)
- Fleet composition (too many of same model)
2. Implement Multi-Tier Pricing
Create different pricing strategies:
- Wholesale value (for SCRAP decisions)
- Retail value (for SELL decisions)
- Donation value (for tax purposes)
3. Add Disposition Workflow
Create a separate workflow for cars marked PENDING_DISPOSITION:
- Get multiple price quotes
- Check auction values
- Evaluate donation options
- Final disposition decision
4. Test Edge Cases
What happens when:
- Luxury car with severe damage?
- Old car in excellent condition?
- Multiple disposition-worthy issues?
- Repair cost estimate unavailable?
Troubleshooting
Supervisor not invoking DispositionAgent
- Check that DispositionFeedbackAgent is detecting severe damage keywords
- Verify “DISPOSITION_REQUIRED” is in the output
- Review supervisor’s system message for disposition routing logic
- Add logging to see feedback values
Cars not getting PENDING_DISPOSITION status
- Check the output logic in CarProcessingWorkflow
- Verify
dispositionRequestparameter is being passed - Ensure
isDisposition()method checks for correct keywords - Check CarManagementService status mapping
PricingAgent returning unexpected values
- Review the pricing guidelines in the
@SystemMessage - Check that car information (make, model, year) is passed correctly
- Verify the LLM is following the output format
- Test with different car makes/models/years
What’s Next?
You’ve implemented the Supervisor Pattern for autonomous, context-aware orchestration with intelligent disposition decisions!
The supervisor agent can now:
- Detect severe damage requiring disposition
- Estimate vehicle value
- Make economically sound SCRAP/SELL/DONATE/KEEP decisions
- Route to appropriate action agents based on feedback
In Step 05, you’ll learn about Agent-to-Agent (A2A) communication — converting the local DispositionAgent you just built into a remote service that runs in a separate system, demonstrating how to distribute agent workloads across multiple applications!