Skip to content

Commit ef6d2a3

Browse files
committed
chain flow
1 parent 17b1884 commit ef6d2a3

File tree

11 files changed

+356
-1
lines changed

11 files changed

+356
-1
lines changed

chain-workflow/pom.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.javaaidev</groupId>
8+
<artifactId>agentic-patterns</artifactId>
9+
<version>0.1.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>agentic-patterns-chain-workflow</artifactId>
13+
<name>Agentic Patterns :: Chain Workflow</name>
14+
15+
<properties>
16+
<maven.compiler.source>21</maven.compiler.source>
17+
<maven.compiler.target>21</maven.compiler.target>
18+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>com.javaaidev</groupId>
24+
<artifactId>agentic-patterns-task-execution</artifactId>
25+
<version>${project.version}</version>
26+
</dependency>
27+
</dependencies>
28+
29+
</project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.javaaidev.agenticpatterns.chainworkflow;
2+
3+
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
4+
import java.lang.reflect.Type;
5+
import java.util.Map;
6+
import org.jspecify.annotations.Nullable;
7+
import org.springframework.ai.chat.client.ChatClient;
8+
import org.springframework.core.Ordered;
9+
10+
public abstract class ChainStepAgent<Req, Res> extends TaskExecutionAgent<Req, Res> implements
11+
Ordered {
12+
13+
protected ChainStepAgent(ChatClient chatClient) {
14+
super(chatClient);
15+
}
16+
17+
protected ChainStepAgent(ChatClient chatClient, @Nullable Type responseType) {
18+
super(chatClient, responseType);
19+
}
20+
21+
protected abstract Res call(Req request, Map<String, Object> context,
22+
WorkflowChain<Req, Res> chain);
23+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.javaaidev.agenticpatterns.chainworkflow;
2+
3+
import com.javaaidev.agenticpatterns.taskexecution.NoLLMTaskExecutionAgent;
4+
import java.lang.reflect.Type;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import org.jspecify.annotations.Nullable;
8+
import org.springframework.ai.chat.client.ChatClient;
9+
10+
public class ChainWorkflowAgent<Request, Response> extends
11+
NoLLMTaskExecutionAgent<Request, Response> {
12+
13+
private final List<ChainStepAgent<Request, Response>> stepAgents = new ArrayList<>();
14+
15+
protected ChainWorkflowAgent(ChatClient chatClient) {
16+
super(chatClient);
17+
}
18+
19+
public ChainWorkflowAgent(ChatClient chatClient,
20+
@Nullable Type responseType) {
21+
super(chatClient, responseType);
22+
}
23+
24+
public void addStep(ChainStepAgent<Request, Response> stepAgent) {
25+
stepAgents.add(stepAgent);
26+
}
27+
28+
@Override
29+
public Response call(@Nullable Request request) {
30+
var chain = new WorkflowChain<>(stepAgents);
31+
return chain.callNext(request, null);
32+
}
33+
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.javaaidev.agenticpatterns.chainworkflow;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Deque;
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
import org.jspecify.annotations.Nullable;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
import org.springframework.core.OrderComparator;
12+
13+
public class WorkflowChain<Request, Response> {
14+
15+
private final Deque<ChainStepAgent<Request, Response>> agents;
16+
private final Map<String, Object> context = new HashMap<>();
17+
18+
private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowChain.class);
19+
20+
public WorkflowChain(List<ChainStepAgent<Request, Response>> agents) {
21+
this.agents = new ArrayDeque<>(agents.stream().sorted(OrderComparator.INSTANCE).toList());
22+
LOGGER.info("Added {} agents to the chain", this.agents.size());
23+
}
24+
25+
public Response callNext(Request request, @Nullable Response lastResponse) {
26+
if (agents.isEmpty()) {
27+
return lastResponse;
28+
}
29+
var agent = agents.pop();
30+
return agent.call(request, context, this);
31+
}
32+
}

examples/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
<artifactId>agentic-patterns-routing-workflow</artifactId>
5656
<version>${agentic-patterns.version}</version>
5757
</dependency>
58+
<dependency>
59+
<groupId>com.javaaidev</groupId>
60+
<artifactId>agentic-patterns-chain-workflow</artifactId>
61+
<version>${agentic-patterns.version}</version>
62+
</dependency>
5863
<dependency>
5964
<groupId>org.springframework.ai</groupId>
6065
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package com.javaaidev.agenticpatterns.examples.chainworkflow;
2+
3+
import com.javaaidev.agenticpatterns.chainworkflow.ChainStepAgent;
4+
import com.javaaidev.agenticpatterns.chainworkflow.ChainWorkflowAgent;
5+
import com.javaaidev.agenticpatterns.chainworkflow.WorkflowChain;
6+
import com.javaaidev.agenticpatterns.core.Utils;
7+
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingRequest;
8+
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingResponse;
9+
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
10+
import java.util.List;
11+
import java.util.Map;
12+
import org.jspecify.annotations.Nullable;
13+
import org.springframework.ai.chat.client.ChatClient;
14+
15+
public class ArticleWritingAgent extends
16+
TaskExecutionAgent<ArticleWritingRequest, ArticleWritingResponse> {
17+
18+
private final ArticleGenerationAgent articleGenerationAgent;
19+
private final ArticleImprovementChainAgent articleImprovementChainAgent;
20+
21+
protected ArticleWritingAgent(ChatClient chatClient) {
22+
super(chatClient, ArticleWritingResponse.class);
23+
articleGenerationAgent = new ArticleGenerationAgent(chatClient);
24+
articleImprovementChainAgent = new ArticleImprovementChainAgent(chatClient);
25+
}
26+
27+
@Override
28+
protected String getPromptTemplate() {
29+
return "";
30+
}
31+
32+
@Override
33+
public ArticleWritingResponse call(@Nullable ArticleWritingRequest articleWritingRequest) {
34+
var initialArticle = articleGenerationAgent.call(articleWritingRequest);
35+
var improved = articleImprovementChainAgent.call(
36+
new ArticleImprovementRequest(initialArticle.article()));
37+
return new ArticleWritingResponse(improved.article());
38+
}
39+
40+
public record ArticleWritingRequest(String topic) {
41+
42+
}
43+
44+
public record ArticleWritingResponse(String article) {
45+
46+
}
47+
48+
private record ArticleImprovementRequest(String article) {
49+
50+
}
51+
52+
private record ArticleImprovementResponse(String article) {
53+
54+
}
55+
56+
private static class ArticleGenerationAgent extends
57+
TaskExecutionAgent<ArticleWritingRequest, ArticleWritingResponse> {
58+
59+
protected ArticleGenerationAgent(ChatClient chatClient) {
60+
super(chatClient, ArticleWritingResponse.class);
61+
}
62+
63+
@Override
64+
protected String getPromptTemplate() {
65+
return """
66+
Write an article about {topic}
67+
""";
68+
}
69+
70+
@Override
71+
protected @Nullable Map<String, Object> getPromptContext(
72+
@Nullable ArticleWritingRequest articleWritingRequest) {
73+
return Map.of(
74+
"topic", Utils.safeGet(articleWritingRequest, ArticleWritingRequest::topic, "")
75+
);
76+
}
77+
}
78+
79+
private static class ArticleImprovementChainAgent extends
80+
ChainWorkflowAgent<ArticleImprovementRequest, ArticleImprovementResponse> {
81+
82+
protected ArticleImprovementChainAgent(ChatClient chatClient) {
83+
super(chatClient, ArticleImprovementResponse.class);
84+
initStepAgents();
85+
}
86+
87+
private void initStepAgents() {
88+
var instructions = List.of(
89+
"""
90+
Review the Structure
91+
- Ensure the article has a clear introduction, body, and conclusion.
92+
- Check if ideas flow logically from one section to another.
93+
- Ensure paragraphs are well-organized and each one has a clear purpose.
94+
""",
95+
"""
96+
Improve Clarity and Conciseness
97+
- Remove unnecessary words and redundant phrases.
98+
- Simplify complex sentences for better readability.
99+
- Use active voice where possible.
100+
""",
101+
"""
102+
Enhance Readability
103+
- Break long paragraphs into shorter ones.
104+
- Use bullet points or subheadings for easier scanning.
105+
- Vary sentence length to maintain reader interest.
106+
"""
107+
);
108+
for (int i = 0; i < instructions.size(); i++) {
109+
addStep(new ArticleImprovementAgent(chatClient, instructions.get(i), i));
110+
}
111+
}
112+
}
113+
114+
115+
private static class ArticleImprovementAgent extends
116+
ChainStepAgent<ArticleImprovementRequest, ArticleImprovementResponse> {
117+
118+
private final String instruction;
119+
private final int order;
120+
121+
protected ArticleImprovementAgent(ChatClient chatClient, String instruction, int order) {
122+
super(chatClient, ArticleImprovementResponse.class);
123+
this.instruction = instruction;
124+
this.order = order;
125+
}
126+
127+
@Override
128+
protected String getPromptTemplate() {
129+
return """
130+
Goal: Improve an article by following the instruction:
131+
132+
{instruction}
133+
134+
Article content:
135+
{article}
136+
""";
137+
}
138+
139+
@Override
140+
protected ArticleImprovementResponse call(ArticleImprovementRequest request,
141+
Map<String, Object> context,
142+
WorkflowChain<ArticleImprovementRequest, ArticleImprovementResponse> chain) {
143+
var response = this.call(request);
144+
return chain.callNext(new ArticleImprovementRequest(response.article()), response);
145+
}
146+
147+
@Override
148+
protected @Nullable Map<String, Object> getPromptContext(
149+
@Nullable ArticleImprovementRequest articleImprovementRequest) {
150+
return Map.of(
151+
"instruction", instruction,
152+
"article",
153+
Utils.safeGet(articleImprovementRequest, ArticleImprovementRequest::article, "")
154+
);
155+
}
156+
157+
@Override
158+
public int getOrder() {
159+
return order;
160+
}
161+
}
162+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.javaaidev.agenticpatterns.examples.chainworkflow;
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 ArticleWritingConfiguration {
10+
11+
@Bean
12+
public ArticleWritingAgent articleWritingAgent(ChatClient.Builder chatClientBuilder,
13+
SimpleLoggerAdvisor simpleLoggerAdvisor) {
14+
return new ArticleWritingAgent(chatClientBuilder.defaultAdvisors(simpleLoggerAdvisor).build());
15+
}
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.javaaidev.agenticpatterns.examples.chainworkflow;
2+
3+
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingRequest;
4+
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingResponse;
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("/article_writing")
12+
public class ArticleWritingController {
13+
14+
private final ArticleWritingAgent agent;
15+
16+
public ArticleWritingController(ArticleWritingAgent agent) {
17+
this.agent = agent;
18+
}
19+
20+
@PostMapping
21+
public ArticleWritingResponse articleWrite(@RequestBody ArticleWritingRequest request) {
22+
return agent.call(request);
23+
}
24+
}

examples/src/main/resources/application.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ spring:
77
api-key: ${OPENAI_API_KEY:}
88
chat:
99
options:
10-
model: gpt-4o
10+
model: gpt-3.5-turbo
1111
temperature: 0.
1212
logging:
1313
level:

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<module>evaluator-optimizer</module>
1818
<module>parallelization-workflow</module>
1919
<module>routing-workflow</module>
20+
<module>chain-workflow</module>
2021
</modules>
2122

2223
<properties>

0 commit comments

Comments
 (0)