Skip to content

Commit 01a54cc

Browse files
committed
refactor routing workflow
1 parent 28c3879 commit 01a54cc

File tree

9 files changed

+256
-124
lines changed

9 files changed

+256
-124
lines changed

examples/src/main/java/com/javaaidev/agenticpatterns/examples/routingworkflow/CustomerSupportAgent.java

Lines changed: 0 additions & 112 deletions
This file was deleted.
Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
11
package com.javaaidev.agenticpatterns.examples.routingworkflow;
22

3+
import com.javaaidev.agenticpatterns.routingworkflow.DefaultRoutingSelector;
4+
import com.javaaidev.agenticpatterns.routingworkflow.RoutingChoice;
5+
import com.javaaidev.agenticpatterns.routingworkflow.RoutingWorkflow;
6+
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
37
import io.micrometer.observation.ObservationRegistry;
8+
import java.util.List;
49
import org.springframework.ai.chat.client.ChatClient;
510
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
11+
import org.springframework.beans.factory.annotation.Qualifier;
612
import org.springframework.context.annotation.Bean;
713
import org.springframework.context.annotation.Configuration;
814

915
@Configuration
1016
public class CustomerSupportConfiguration {
1117

1218
@Bean
13-
public CustomerSupportAgent customerSupportAgent(
19+
@Qualifier("customerSupportWorkflow")
20+
public RoutingWorkflow<CustomerSupportRequest, CustomerSupportResponse> customerSupportWorkflow(
1421
ChatClient.Builder chatClientBuilder,
1522
SimpleLoggerAdvisor simpleLoggerAdvisor,
16-
ObservationRegistry observationRegistry) {
17-
return new CustomerSupportAgent(
18-
chatClientBuilder.defaultAdvisors(simpleLoggerAdvisor).build(),
19-
observationRegistry);
23+
ObservationRegistry observationRegistry
24+
) {
25+
var chatClient = chatClientBuilder.defaultAdvisors(simpleLoggerAdvisor).build();
26+
var routes = List.of(
27+
new CustomerSupportRoute("payment", "Handle queries about payment and refund",
28+
"You are a customer support agent for payment, be polite and helpful"),
29+
new CustomerSupportRoute("shipping", "Handle queries about shipping",
30+
"You are a customer support agent for shipping, be polite and helpful"),
31+
new CustomerSupportRoute("general", "Handle general queries",
32+
"You are a customer support agent for general questions, be polite and helpful")
33+
);
34+
var routingChoices = routes.stream().map(route -> new RoutingChoice<>(
35+
route.name(),
36+
route.description(),
37+
TaskExecutionAgent.<CustomerSupportRequest, CustomerSupportResponse>defaultBuilder()
38+
.chatClient(chatClient)
39+
.promptTemplate("{question}")
40+
.responseType(CustomerSupportResponse.class)
41+
.chatClientRequestSpecUpdater(spec -> spec.system(route.agentSystemText()))
42+
.name("CustomerSupport_" + route.name)
43+
.observationRegistry(observationRegistry)
44+
.build()
45+
)).toList();
46+
return RoutingWorkflow.<CustomerSupportRequest, CustomerSupportResponse>builder()
47+
.addRoutingChoices(routingChoices)
48+
.routingSelector(
49+
DefaultRoutingSelector.<CustomerSupportRequest, CustomerSupportResponse>builder()
50+
.chatClient(chatClient)
51+
.name("RoutingSelector")
52+
.observationRegistry(observationRegistry)
53+
.build())
54+
.name("CustomerSupportWorkflow")
55+
.observationRegistry(observationRegistry)
56+
.build();
57+
}
58+
59+
record CustomerSupportRoute(String name, String description, String agentSystemText) {
60+
2061
}
2162
}
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.javaaidev.agenticpatterns.examples.routingworkflow;
22

3-
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportRequest;
4-
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportResponse;
3+
import com.javaaidev.agenticpatterns.routingworkflow.RoutingWorkflow;
4+
import org.springframework.beans.factory.annotation.Qualifier;
55
import org.springframework.web.bind.annotation.PostMapping;
66
import org.springframework.web.bind.annotation.RequestBody;
77
import org.springframework.web.bind.annotation.RequestMapping;
@@ -11,14 +11,16 @@
1111
@RequestMapping("/customer_support")
1212
public class CustomerSupportController {
1313

14-
private final CustomerSupportAgent customerSupportAgent;
14+
private final RoutingWorkflow<CustomerSupportRequest, CustomerSupportResponse> customerSupportWorkflow;
1515

16-
public CustomerSupportController(CustomerSupportAgent customerSupportAgent) {
17-
this.customerSupportAgent = customerSupportAgent;
16+
public CustomerSupportController(
17+
@Qualifier("customerSupportWorkflow") RoutingWorkflow<CustomerSupportRequest, CustomerSupportResponse> customerSupportWorkflow) {
18+
this.customerSupportWorkflow = customerSupportWorkflow;
1819
}
1920

21+
2022
@PostMapping
2123
public CustomerSupportResponse customerSupport(@RequestBody CustomerSupportRequest request) {
22-
return customerSupportAgent.call(request);
24+
return customerSupportWorkflow.execute(request);
2325
}
2426
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.javaaidev.agenticpatterns.examples.routingworkflow;
2+
3+
public record CustomerSupportRequest(String question) {
4+
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.javaaidev.agenticpatterns.examples.routingworkflow;
2+
3+
public record CustomerSupportResponse(String answer) {
4+
5+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.javaaidev.agenticpatterns.routingworkflow;
2+
3+
import com.javaaidev.agenticpatterns.taskexecution.AbstractTaskExecutionAgentBuilder;
4+
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
5+
import io.micrometer.observation.ObservationRegistry;
6+
import java.util.Map;
7+
import java.util.Objects;
8+
import java.util.function.Function;
9+
import java.util.stream.Collectors;
10+
import org.jspecify.annotations.Nullable;
11+
import org.springframework.ai.chat.client.ChatClient;
12+
import org.springframework.util.Assert;
13+
14+
public class DefaultRoutingSelector<Request, Response> extends
15+
TaskExecutionAgent<RoutingRequest<Request, Response>, RoutingResponse> implements
16+
RoutingSelector<Request, Response> {
17+
18+
private final Function<Request, String> defaultRoutingInputFormatter = request -> Objects.toString(
19+
request, "");
20+
private Function<Request, String> routingInputFormatter = defaultRoutingInputFormatter;
21+
22+
public DefaultRoutingSelector(ChatClient chatClient,
23+
@Nullable ObservationRegistry observationRegistry) {
24+
super(chatClient, RoutingResponse.class, observationRegistry);
25+
}
26+
27+
public DefaultRoutingSelector(ChatClient chatClient,
28+
@Nullable Function<Request, String> routingInputFormatter,
29+
@Nullable ObservationRegistry observationRegistry) {
30+
super(chatClient, RoutingResponse.class, observationRegistry);
31+
this.routingInputFormatter = Objects.requireNonNullElse(routingInputFormatter,
32+
defaultRoutingInputFormatter);
33+
}
34+
35+
@Override
36+
public RoutingResponse select(RoutingRequest<Request, Response> request) {
37+
return this.call(request);
38+
}
39+
40+
@Override
41+
protected String getPromptTemplate() {
42+
return """
43+
Goal: Select the best target to handle the input from a list of choices
44+
45+
Choices:
46+
{choices}
47+
48+
Input:
49+
{input}
50+
""";
51+
}
52+
53+
@Override
54+
protected @Nullable Map<String, Object> getPromptContext(
55+
@Nullable RoutingRequest<Request, Response> routingRequest) {
56+
Assert.notNull(routingRequest, "routing request cannot be null");
57+
var choices = routingRequest.choices().stream().map(choice ->
58+
"- %s: %s".formatted(choice.name(), choice.description()))
59+
.collect(Collectors.joining("\n"));
60+
return Map.of(
61+
"choices", choices,
62+
"input", routingInputFormatter.apply(routingRequest.request())
63+
);
64+
}
65+
66+
public static <Request, Response> Builder<Request, Response> builder() {
67+
return new Builder<>();
68+
}
69+
70+
public static class Builder<Request, Response> extends
71+
AbstractTaskExecutionAgentBuilder<RoutingRequest<Request, Response>, RoutingResponse, Builder<Request, Response>> {
72+
73+
private Function<Request, String> routingInputFormatter;
74+
75+
public Builder<Request, Response> routingInputFormatter(
76+
Function<Request, String> routingInputFormatter) {
77+
this.routingInputFormatter = routingInputFormatter;
78+
return this;
79+
}
80+
81+
@Override
82+
public DefaultRoutingSelector<Request, Response> build() {
83+
return new DefaultRoutingSelector<>(
84+
chatClient,
85+
routingInputFormatter,
86+
observationRegistry);
87+
}
88+
}
89+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.javaaidev.agenticpatterns.routingworkflow;
2+
3+
public interface RoutingSelector<Request, Response> {
4+
5+
RoutingResponse select(RoutingRequest<Request, Response> request);
6+
}

0 commit comments

Comments
 (0)