Routing Workflow Agent
To use this agent, add the Maven dependency to your project, replacing ${agentic-patterns-version}
with the latest version.
Latest version:
<dependency>
<groupId>com.javaaidev.agenticpatterns</groupId>
<artifactId>routing-workflow</artifactId>
<version>${agentic-patterns-version}</version>
</dependency>
RoutingWorkflowAgent
RoutingWorkflowAgent
routes the request to a selected route for handling.
Routing choices are added using the addRoutingChoice
method. A RoutingChoice
has a name
, a description
, and an agent
to handle the request.
package com.javaaidev.agenticpatterns.routingworkflow;
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
/**
* A routing choice
*
* @param name Name of the route
* @param description Description of the route
* @param agent Task of the route
* @param <Request> Task input type
* @param <Response> Task output type
*/
public record RoutingChoice<Request, Response>(String name,
String description,
TaskExecutionAgent<Request, Response> agent) {
}
RoutingWorkflowAgent
uses a default prompt template to instruct an LLM to select the routing target. This default prompt template has two variables:
choices
, routing choices formatted from the list ofRoutingChoice
s.input
, the task input formatted using theformatRoutingInput
method.
You can provide your own routing prompt template by overriding the getRoutingPromptTemplate
method. In this case, you also need to override getRoutingPromptContext
method to provide values of variables used in your prompt template.
Example
The example is an agent for customer support. This agent has three routes, payment
, shipping
and general
. The agent for each route is a TaskExecutionAgent
using system text to simulate as a customer support agent. These routes are added in the initRoutes
method using addRoutingChoice
.
package com.javaaidev.agenticpatterns.examples.routingworkflow;
import com.javaaidev.agenticpatterns.core.AgentUtils;
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportRequest;
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportResponse;
import com.javaaidev.agenticpatterns.routingworkflow.RoutingChoice;
import com.javaaidev.agenticpatterns.routingworkflow.RoutingWorkflowAgent;
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
import io.micrometer.observation.ObservationRegistry;
import java.lang.reflect.Type;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.ChatClient.ChatClientRequestSpec;
/**
* A routing workflow agent for customer support
*/
public class CustomerSupportAgent extends
RoutingWorkflowAgent<CustomerSupportRequest, CustomerSupportResponse> {
public CustomerSupportAgent(ChatClient chatClient,
@Nullable Type responseType, @Nullable ObservationRegistry observationRegistry) {
super(chatClient, responseType, observationRegistry);
initRoutes();
}
public CustomerSupportAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
initRoutes();
}
private void initRoutes() {
addRoutingChoice(new RoutingChoice<>("payment", "Handle queries about payment and refund",
new PaymentSupportAgent(chatClient, observationRegistry)));
addRoutingChoice(new RoutingChoice<>("shipping", "Handle queries about shipping",
new ShippingSupportAgent(chatClient, observationRegistry)));
addRoutingChoice(new RoutingChoice<>("general", "Handle general queries",
new GeneralSupportAgent(chatClient, observationRegistry)));
}
@Override
protected String formatRoutingInput(@Nullable CustomerSupportRequest request) {
return AgentUtils.safeGet(request, CustomerSupportRequest::question, "");
}
public record CustomerSupportRequest(String question) {
}
public record CustomerSupportResponse(String answer) {
}
private static class PaymentSupportAgent extends
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
protected PaymentSupportAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return "{question}";
}
@Override
protected @Nullable Map<String, Object> getPromptContext(
@Nullable CustomerSupportRequest request) {
return Map.of(
"question",
AgentUtils.safeGet(request, CustomerSupportRequest::question, "")
);
}
@Override
protected void updateChatClientRequest(ChatClientRequestSpec spec) {
spec.system("You are a customer support agent for payment, be polite and helper");
}
}
private static class ShippingSupportAgent extends
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
protected ShippingSupportAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return "{question}";
}
@Override
protected Map<String, Object> getPromptContext(
@Nullable CustomerSupportRequest request) {
return Map.of(
"question",
AgentUtils.safeGet(request, CustomerSupportRequest::question, "")
);
}
@Override
protected void updateChatClientRequest(ChatClientRequestSpec spec) {
spec.system("You are a customer support agent for shipping, be polite and helper");
}
}
private static class GeneralSupportAgent extends
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
protected GeneralSupportAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return "{question}";
}
@Override
protected @Nullable Map<String, Object> getPromptContext(
@Nullable CustomerSupportRequest request) {
return Map.of(
"question",
AgentUtils.safeGet(request, CustomerSupportRequest::question, "")
);
}
@Override
protected void updateChatClientRequest(ChatClientRequestSpec spec) {
spec.system(
"You are a customer support agent for general questions, be polite and helper");
}
}
}
Given a task input, the input will be sent to an LLM first to select the routing target, then the agent for selected route is called to handle the input.