Parallelization 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>parallelization-workflow</artifactId>
<version>${agentic-patterns-version}</version>
</dependency>
ParallelizationWorkflowAgent
ParallelizationWorkflowAgent
executes a list of subtasks in parallel. Subtasks are just other TaskExecutionAgent
s. Each subtask must have a unique id. This id is used to get its execution result.
There are two approaches to add subtasks.
The first approach is using the addSubtask
method. When adding a subtask, you should provide parameters shown below.
Parameter | Type | Description |
---|---|---|
taskId | String | Task id |
subtask | TaskExecutionAgent | Task agent |
requestTransformer | Function | Transform the request into task input |
The second approach is using the createTasks
method to create a list of subtasks from the request. The return value of createTasks
method is a List<SubtaskCreationRequest<Request>>
object. SubtaskCreationRequest
encapsulates taskId
, task
, and requestTransformer
used in the addSubtask
method.
Subtasks added by these two approaches are merged.
DirectAssembling
DirectAssembling
doesn't use an LLM to generate the result. It's useful for scenarios like voting.
Subclasses of DirectAssembling
must implement the Response assemble(TaskExecutionResults results)
method. This method assembles execution results of subtasks to produce the response.
PromptBasedAssembling
PromptBasedAssembling
uses a LLM to generate the final result. A subclass of PromptBasedAssembling
must provide the prompt template and values of template variables.
Values of template variables may come from two places.
- From task input, overrides the
getRequestPromptContext
method. - From subtask execution results, overrides the
getSubtasksPromptContext
method.
The final Map<String, Object>
values of template variables are merged from results of these two methods.
Example
The example is an agent to write articles about algorithms. Each article has code examples written in different programming languages. A parallelization workflow agent runs parallel subtasks to generate code examples, then generates an article using these code examples.
SampleCodeGenerationAgent
shown below is a TaskExecutionAgent
to generate code samples. It generates code samples based on a language and a description. For example, it can generate a code sample for quick sort using Java.
package com.javaaidev.agenticpatterns.examples.parallelizationworkflow;
import com.javaaidev.agenticpatterns.core.AgentUtils;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.SampleCodeGenerationAgent.SampleCodeGenerationRequest;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.SampleCodeGenerationAgent.SampleCodeGenerationResponse;
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
import io.micrometer.observation.ObservationRegistry;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.ai.chat.client.ChatClient;
public class SampleCodeGenerationAgent extends
TaskExecutionAgent<SampleCodeGenerationRequest, SampleCodeGenerationResponse> {
public SampleCodeGenerationAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return """
Write {language} code to meet the requirement.
{description}
""";
}
@Override
protected @Nullable Map<String, Object> getPromptContext(
@Nullable SampleCodeGenerationRequest request) {
return Map.of(
"language",
AgentUtils.safeGet(request, SampleCodeGenerationRequest::language, "java"),
"description",
AgentUtils.safeGet(request, SampleCodeGenerationRequest::description, "")
);
}
public record SampleCodeGenerationRequest(String language, String description) {
}
public record SampleCodeGenerationResponse(String code) {
}
}
AlgorithmArticleGenerationAgent
shown below creates a list of subtasks from the request. A SampleCodeGenerationAgent
is created for each algorithm in the AlgorithmArticleGenerationRequest
. The result article is generated using an LLM based on the execution results of SampleCodeGenerationAgent
s.
In the createTasks
method, each language is mapped to a SubtaskCreationRequest
.
- Task id is the language.
- Task is a new
SampleCodeGenerationAgent
. - Request transformer converts the
AlgorithmArticleGenerationRequest
to aSampleCodeGenerationRequest
used by the task.
AlgorithmArticleGenerationAgent
uses an LLM to generate the final result. In the prompt template, there are two variables, algorithm
and code_sample
.
- Value of
algorithm
comes from the request, populated in thegetRequestPromptContext
method. - Value of
code_sample
comes from subtask execution results, populated in thegetSubtasksPromptContext
method.
package com.javaaidev.agenticpatterns.examples.parallelizationworkflow;
import com.javaaidev.agenticpatterns.core.AgentUtils;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.AlgorithmArticleGenerationAgent.AlgorithmArticleGenerationRequest;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.AlgorithmArticleGenerationAgent.AlgorithmArticleGenerationResponse;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.SampleCodeGenerationAgent.SampleCodeGenerationRequest;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.SampleCodeGenerationAgent.SampleCodeGenerationResponse;
import com.javaaidev.agenticpatterns.parallelizationworkflow.PromptBasedAssembling;
import com.javaaidev.agenticpatterns.parallelizationworkflow.SubtaskCreationRequest;
import com.javaaidev.agenticpatterns.parallelizationworkflow.TaskExecutionResults;
import io.micrometer.observation.ObservationRegistry;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.util.CollectionUtils;
public class AlgorithmArticleGenerationAgent extends
PromptBasedAssembling<AlgorithmArticleGenerationRequest, AlgorithmArticleGenerationResponse> {
public AlgorithmArticleGenerationAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, AlgorithmArticleGenerationResponse.class, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return """
Goal: Write an article about {algorithm}.
Requirements:
- Start with a brief introduction.
- Include only sample code listed below.
- Output the article in markdown.
{sample_code}
""";
}
@Override
protected @Nullable Map<String, Object> getRequestPromptContext(
@Nullable AlgorithmArticleGenerationRequest request) {
return Map.of("algorithm",
AgentUtils.safeGet(request, AlgorithmArticleGenerationRequest::algorithm, ""));
}
@Override
protected @Nullable Map<String, Object> getSubtasksPromptContext(
TaskExecutionResults results) {
var sampleCode = results.allSuccessfulResults().entrySet().stream().map(entry -> """
Language: %s
Code:
%s
""".formatted(entry.getKey(), ((SampleCodeGenerationResponse) entry.getValue()).code()))
.collect(Collectors.joining("==========\n", "\n----------\n", "=========="));
return Map.of("sample_code", sampleCode);
}
@Override
protected List<SubtaskCreationRequest<AlgorithmArticleGenerationRequest>> createTasks(
@Nullable AlgorithmArticleGenerationRequest request) {
var languages = AgentUtils.safeGet(request,
AlgorithmArticleGenerationRequest::languages, List.<String>of());
if (CollectionUtils.isEmpty(languages)) {
return List.of();
}
var codeGenerationAgent = new SampleCodeGenerationAgent(chatClient, observationRegistry);
return languages.stream().map(
language -> new SubtaskCreationRequest<>(language,
codeGenerationAgent,
(AlgorithmArticleGenerationRequest req) -> new SampleCodeGenerationRequest(language,
"Implement algorithm " + req.algorithm()))).toList();
}
public record AlgorithmArticleGenerationRequest(String algorithm, List<String> languages) {
}
public record AlgorithmArticleGenerationResponse(String article) {
}
}
We can expose a REST API to test this agent.
package com.javaaidev.agenticpatterns.examples.parallelizationworkflow;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.AlgorithmArticleGenerationAgent.AlgorithmArticleGenerationRequest;
import com.javaaidev.agenticpatterns.examples.parallelizationworkflow.AlgorithmArticleGenerationAgent.AlgorithmArticleGenerationResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/algorithm_article")
public class AlgorithmArticleGenerationController {
private final AlgorithmArticleGenerationAgent agent;
public AlgorithmArticleGenerationController(AlgorithmArticleGenerationAgent agent) {
this.agent = agent;
}
@PostMapping
public AlgorithmArticleGenerationResponse generateAlgorithmArticle(
@RequestBody AlgorithmArticleGenerationRequest request) {
return agent.call(request);
}
}