Skip to main content

Chain Workflow Agent

To use this agent, add the Maven dependency to your project, replacing ${agentic-patterns-version} with the latest version.

Latest version: Maven Central Version

<dependency>
<groupId>com.javaaidev.agenticpatterns</groupId>
<artifactId>chain-workflow</artifactId>
<version>${agentic-patterns-version}</version>
</dependency>

ChainWorkflowAgent

A ChainWorkflowAgent has multiple steps. Each step is a ChainStepAgent. Steps are added using the addStep method.

ChainStepAgent

A ChainStepAgent implements the Spring Ordered interface. This order determines its position in the chain. A ChainStepAgent must implement the call method. In the call method, a ChainStepAgent can return the task output directly, or use the callNext method of WorkflowChain to call the next ChainStepAgent in the chain.

The call method has a parameter context of type Map<String, Object>. This Map can be used to share data between different steps.

Example

The example is an agent to write an article about a topic. It first uses an agent to generate the initial content, then it uses a chain workflow to improve the content from different aspects.

ArticleGenerationAgent is a simple TaskExecutionAgent to generate content by given topic.

ArticleImprovementChainAgent is a ChainWorkflowAgent to improve content using multiple steps. It has three steps. Each step is an ArticleImprovementAgent. Each step calls the next step using the improved content.

Article writing agent
package com.javaaidev.agenticpatterns.examples.chainworkflow;

import com.javaaidev.agenticpatterns.chainworkflow.ChainStepAgent;
import com.javaaidev.agenticpatterns.chainworkflow.ChainWorkflowAgent;
import com.javaaidev.agenticpatterns.chainworkflow.WorkflowChain;
import com.javaaidev.agenticpatterns.core.AgentUtils;
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingRequest;
import com.javaaidev.agenticpatterns.examples.chainworkflow.ArticleWritingAgent.ArticleWritingResponse;
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
import io.micrometer.observation.ObservationRegistry;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.ai.chat.client.ChatClient;

public class ArticleWritingAgent extends
TaskExecutionAgent<ArticleWritingRequest, ArticleWritingResponse> {

private final ArticleGenerationAgent articleGenerationAgent;
private final ArticleImprovementChainAgent articleImprovementChainAgent;

protected ArticleWritingAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, ArticleWritingResponse.class, observationRegistry);
articleGenerationAgent = new ArticleGenerationAgent(chatClient, observationRegistry);
articleImprovementChainAgent = new ArticleImprovementChainAgent(chatClient,
observationRegistry);
}

@Override
protected String getPromptTemplate() {
return "";
}

@Override
public ArticleWritingResponse call(@Nullable ArticleWritingRequest articleWritingRequest) {
var initialArticle = articleGenerationAgent.call(articleWritingRequest);
var improved = articleImprovementChainAgent.call(
new ArticleImprovementRequest(initialArticle.article()));
return new ArticleWritingResponse(improved.article());
}

public record ArticleWritingRequest(String topic) {

}

public record ArticleWritingResponse(String article) {

}

private record ArticleImprovementRequest(String article) {

}

private record ArticleImprovementResponse(String article) {

}

private static class ArticleGenerationAgent extends
TaskExecutionAgent<ArticleWritingRequest, ArticleWritingResponse> {

protected ArticleGenerationAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, ArticleWritingResponse.class, observationRegistry);
}

@Override
protected String getPromptTemplate() {
return """
Write an article about {topic}
""";
}

@Override
protected Map<String, Object> getPromptContext(
@Nullable ArticleWritingRequest request) {
return Map.of(
"topic", AgentUtils.safeGet(request, ArticleWritingRequest::topic, "")
);
}
}

private static class ArticleImprovementChainAgent extends
ChainWorkflowAgent<ArticleImprovementRequest, ArticleImprovementResponse> {

protected ArticleImprovementChainAgent(
ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, ArticleImprovementResponse.class, observationRegistry);
initStepAgents();
}

private void initStepAgents() {
var instructions = List.of(
"""
Review the Structure
- Ensure the article has a clear introduction, body, and conclusion.
- Check if ideas flow logically from one section to another.
- Ensure paragraphs are well-organized and each one has a clear purpose.
""",
"""
Improve Clarity and Conciseness
- Remove unnecessary words and redundant phrases.
- Simplify complex sentences for better readability.
- Use active voice where possible.
""",
"""
Enhance Readability
- Break long paragraphs into shorter ones.
- Use bullet points or subheadings for easier scanning.
- Vary sentence length to maintain reader interest.
"""
);
for (int i = 0; i < instructions.size(); i++) {
addStep(
new ArticleImprovementAgent(chatClient, observationRegistry, instructions.get(i), i));
}
}
}


private static class ArticleImprovementAgent extends
ChainStepAgent<ArticleImprovementRequest, ArticleImprovementResponse> {

private final String instruction;
private final int order;

protected ArticleImprovementAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry, String instruction, int order) {
super(chatClient, ArticleImprovementResponse.class, observationRegistry);
this.instruction = instruction;
this.order = order;
}

@Override
protected String getPromptTemplate() {
return """
Goal: Improve an article by following the instruction:

{instruction}

Article content:
{article}
""";
}

@Override
protected ArticleImprovementResponse call(ArticleImprovementRequest request,
Map<String, Object> context,
WorkflowChain<ArticleImprovementRequest, ArticleImprovementResponse> chain) {
var response = this.call(request);
return chain.callNext(new ArticleImprovementRequest(response.article()), response);
}

@Override
protected Map<String, Object> getPromptContext(
@Nullable ArticleImprovementRequest request) {
return Map.of(
"instruction", instruction,
"article",
AgentUtils.safeGet(request, ArticleImprovementRequest::article, "")
);
}

@Override
public int getOrder() {
return order;
}
}
}