Skip to content

Commit ecb59d8

Browse files
committed
add routing workflow
1 parent 2022d26 commit ecb59d8

File tree

16 files changed

+385
-10
lines changed

16 files changed

+385
-10
lines changed

core/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</parent>
1111

1212
<artifactId>agentic-patterns-core</artifactId>
13+
<name>Agentic Patterns :: Core</name>
1314

1415
<properties>
1516
<maven.compiler.source>21</maven.compiler.source>

core/src/main/java/com/javaaidev/agenticpatterns/core/PromptTemplateHelper.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
import java.io.IOException;
44
import java.nio.charset.StandardCharsets;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Optional;
8+
import java.util.function.Function;
9+
import org.jspecify.annotations.Nullable;
510
import org.springframework.core.io.ClassPathResource;
611

712
public class PromptTemplateHelper {
@@ -13,4 +18,20 @@ public static String loadPromptTemplateFromClasspath(String resource) {
1318
throw new AgentExecutionException("Prompt template not found: " + resource, e);
1419
}
1520
}
21+
22+
public static Map<String, Object> mergeMap(@Nullable Map<String, Object> map1,
23+
@Nullable Map<String, Object> map2) {
24+
var m1 = Optional.ofNullable(map1)
25+
.orElseGet(HashMap::new);
26+
var m2 = Optional.ofNullable(map2)
27+
.orElseGet(HashMap::new);
28+
var result = new HashMap<String, Object>();
29+
result.putAll(m1);
30+
result.putAll(m2);
31+
return result;
32+
}
33+
34+
public static <T, R> R safeGet(@Nullable T obj, Function<T, R> extractor, R defaultValue) {
35+
return Optional.ofNullable(obj).map(extractor).orElse(defaultValue);
36+
}
1637
}

evaluator-optimizer/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</parent>
1111

1212
<artifactId>agentic-patterns-evaluator-optimizer</artifactId>
13+
<name>Agentic Patterns :: Evaluator-Optimizer</name>
1314

1415
<properties>
1516
<maven.compiler.source>21</maven.compiler.source>

evaluator-optimizer/src/main/java/com/javaaidev/agenticpatterns/evaluatoroptimizer/Evaluation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
import org.jspecify.annotations.Nullable;
44

5+
/**
6+
* Evaluation result
7+
*
8+
* @param passed Passed or not passed
9+
* @param feedback Feedback if not passed
10+
*/
511
public record Evaluation(boolean passed, @Nullable String feedback) {
612

713
}

evaluator-optimizer/src/main/java/com/javaaidev/agenticpatterns/evaluatoroptimizer/EvaluatorOptimizerAgent.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import org.slf4j.LoggerFactory;
77

88
/**
9+
* Evaluator-Optimizer Agent, refer to the <a
10+
* href="https://javaaidev.com/docs/agentic-patterns/patterns/evaluator-optimizer">pattern</a>
11+
*
912
* @param <Request>
1013
* @param <Response>
1114
*/
@@ -19,14 +22,34 @@ public abstract class EvaluatorOptimizerAgent<Request, Response> {
1922

2023
private static final Logger LOGGER = LoggerFactory.getLogger(EvaluatorOptimizerAgent.class);
2124

25+
/**
26+
* The maximum number of evaluation iterations, default to 3
27+
*
28+
* @return Maximum number of iterations
29+
*/
2230
protected int getMaxIterations() {
2331
return 3;
2432
}
2533

34+
/**
35+
* Build the agent to generation initial result
36+
*
37+
* @return the agent, see {@linkplain TaskExecutionAgent}
38+
*/
2639
protected abstract TaskExecutionAgent<Request, Response> buildInitialResultAgent();
2740

41+
/**
42+
* Build the agent to evaluate the result
43+
*
44+
* @return the agent, see {@linkplain TaskExecutionAgent}
45+
*/
2846
protected abstract TaskExecutionAgent<Response, Evaluation> buildEvaluationAgent();
2947

48+
/**
49+
* Build the agent to optimize the result
50+
*
51+
* @return the agent, see {@linkplain TaskExecutionAgent}
52+
*/
3053
protected abstract TaskExecutionAgent<OptimizationInput<Response>, Response> buildOptimizationAgent();
3154

3255
public Response call(@Nullable Request request) {

evaluator-optimizer/src/main/java/com/javaaidev/agenticpatterns/evaluatoroptimizer/PromptBasedEvaluatorOptimizerAgent.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
import org.jspecify.annotations.Nullable;
99
import org.springframework.ai.chat.client.ChatClient;
1010

11+
/**
12+
* A {@linkplain EvaluatorOptimizerAgent} implementation uses prompts for subtask agents
13+
*
14+
* @param <Request>
15+
* @param <Response>
16+
*/
1117
public abstract class PromptBasedEvaluatorOptimizerAgent<Request, Response> extends
1218
EvaluatorOptimizerAgent<Request, Response> {
1319

examples/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
<version>3.4.1</version>
1010
<relativePath/>
1111
</parent>
12+
1213
<artifactId>agentic-patterns-examples</artifactId>
14+
<name>Agentic Patterns :: Examples</name>
1315

1416
<properties>
1517
<java.version>21</java.version>
@@ -48,6 +50,11 @@
4850
<artifactId>agentic-patterns-parallelization-workflow</artifactId>
4951
<version>${agentic-patterns.version}</version>
5052
</dependency>
53+
<dependency>
54+
<groupId>com.javaaidev</groupId>
55+
<artifactId>agentic-patterns-routing-workflow</artifactId>
56+
<version>${agentic-patterns.version}</version>
57+
</dependency>
5158
<dependency>
5259
<groupId>org.springframework.ai</groupId>
5360
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.javaaidev.agenticpatterns.examples.routingworkflow;
2+
3+
import com.javaaidev.agenticpatterns.core.PromptTemplateHelper;
4+
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportRequest;
5+
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportResponse;
6+
import com.javaaidev.agenticpatterns.routingworkflow.RoutingWorkflowAgent;
7+
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
8+
import java.lang.reflect.Type;
9+
import java.util.Map;
10+
import org.jspecify.annotations.Nullable;
11+
import org.springframework.ai.chat.client.ChatClient;
12+
import org.springframework.ai.chat.client.ChatClient.ChatClientRequestSpec;
13+
14+
public class CustomerSupportAgent extends
15+
RoutingWorkflowAgent<CustomerSupportRequest, CustomerSupportResponse> {
16+
17+
protected CustomerSupportAgent(ChatClient chatClient,
18+
@Nullable Type responseType) {
19+
super(chatClient, responseType);
20+
initRoutes();
21+
}
22+
23+
protected CustomerSupportAgent(ChatClient chatClient) {
24+
super(chatClient);
25+
initRoutes();
26+
}
27+
28+
private void initRoutes() {
29+
addRoutingChoice(new RoutingChoice<>("payment", "Handle requests about payment and refund",
30+
new PaymentSupportAgent(chatClient)));
31+
addRoutingChoice(new RoutingChoice<>("shipping", "Handle requests about shipping",
32+
new ShippingSupportAgent(chatClient)));
33+
addRoutingChoice(new RoutingChoice<>("general", "Handle general requests",
34+
new GeneralSupportAgent(chatClient)));
35+
}
36+
37+
public record CustomerSupportRequest(String question) {
38+
39+
}
40+
41+
public record CustomerSupportResponse(String answer) {
42+
43+
}
44+
45+
private static class PaymentSupportAgent extends
46+
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
47+
48+
protected PaymentSupportAgent(ChatClient chatClient) {
49+
super(chatClient);
50+
}
51+
52+
@Override
53+
protected String getPromptTemplate() {
54+
return "{question}";
55+
}
56+
57+
@Override
58+
protected @Nullable Map<String, Object> getPromptContext(
59+
@Nullable CustomerSupportRequest customerSupportRequest) {
60+
return Map.of(
61+
"question",
62+
PromptTemplateHelper.safeGet(customerSupportRequest, CustomerSupportRequest::question, "")
63+
);
64+
}
65+
66+
@Override
67+
protected void updateRequest(ChatClientRequestSpec spec) {
68+
spec.system("Pretend to be a customer support agent for payment, be polite and helper");
69+
}
70+
}
71+
72+
private static class ShippingSupportAgent extends
73+
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
74+
75+
protected ShippingSupportAgent(ChatClient chatClient) {
76+
super(chatClient);
77+
}
78+
79+
@Override
80+
protected String getPromptTemplate() {
81+
return "{question}";
82+
}
83+
84+
@Override
85+
protected @Nullable Map<String, Object> getPromptContext(
86+
@Nullable CustomerSupportRequest customerSupportRequest) {
87+
return Map.of(
88+
"question",
89+
PromptTemplateHelper.safeGet(customerSupportRequest, CustomerSupportRequest::question, "")
90+
);
91+
}
92+
93+
@Override
94+
protected void updateRequest(ChatClientRequestSpec spec) {
95+
spec.system("Pretend to be a customer support agent for shipping, be polite and helper");
96+
}
97+
}
98+
99+
private static class GeneralSupportAgent extends
100+
TaskExecutionAgent<CustomerSupportRequest, CustomerSupportResponse> {
101+
102+
protected GeneralSupportAgent(ChatClient chatClient) {
103+
super(chatClient);
104+
}
105+
106+
@Override
107+
protected String getPromptTemplate() {
108+
return "{question}";
109+
}
110+
111+
@Override
112+
protected @Nullable Map<String, Object> getPromptContext(
113+
@Nullable CustomerSupportRequest customerSupportRequest) {
114+
return Map.of(
115+
"question",
116+
PromptTemplateHelper.safeGet(customerSupportRequest, CustomerSupportRequest::question, "")
117+
);
118+
}
119+
120+
@Override
121+
protected void updateRequest(ChatClientRequestSpec spec) {
122+
spec.system(
123+
"Pretend to be a customer support agent for general questions, be polite and helper");
124+
}
125+
}
126+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.javaaidev.agenticpatterns.examples.routingworkflow;
2+
3+
import org.springframework.ai.chat.client.ChatClient;
4+
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
@Configuration
9+
public class CustomerSupportConfiguration {
10+
11+
@Bean
12+
public CustomerSupportAgent customerSupportAgent(
13+
ChatClient.Builder chatClientBuilder,
14+
SimpleLoggerAdvisor simpleLoggerAdvisor) {
15+
return new CustomerSupportAgent(
16+
chatClientBuilder.defaultAdvisors(simpleLoggerAdvisor).build());
17+
}
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.javaaidev.agenticpatterns.examples.routingworkflow;
2+
3+
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportRequest;
4+
import com.javaaidev.agenticpatterns.examples.routingworkflow.CustomerSupportAgent.CustomerSupportResponse;
5+
import org.springframework.web.bind.annotation.PostMapping;
6+
import org.springframework.web.bind.annotation.RequestBody;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RestController;
9+
10+
@RestController
11+
@RequestMapping("/customer_support")
12+
public class CustomerSupportController {
13+
14+
private final CustomerSupportAgent customerSupportAgent;
15+
16+
public CustomerSupportController(CustomerSupportAgent customerSupportAgent) {
17+
this.customerSupportAgent = customerSupportAgent;
18+
}
19+
20+
@PostMapping
21+
public CustomerSupportResponse customerSupport(@RequestBody CustomerSupportRequest request) {
22+
return customerSupportAgent.call(request);
23+
}
24+
}

0 commit comments

Comments
 (0)