Task Execution 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>task-execution</artifactId>
<version>${agentic-patterns-version}</version>
</dependency>
Usage
TaskExecutionAgent
To create a Task Execution agent, you can create a subclass of TaskExecutionAgent
. When constructing a TaskExecutionAgent
, parameters in the table below can be provided.
Parameter | Required? | Type | Description |
---|---|---|---|
chatClient | yes | ChatClient | Spring AI ChatClient |
responseType | no | Type | Type of response, only required when auto-detection fails |
observationRegistry | no | ObservationRegistry | Micrometer ObservationRegistry for tracing |
TaskExecutionAgent
is a parameterized type with request type and response type.
When subclassing TaskExecutionAgent
, the getPromptTemplate
method must be implemented. This method returns the prompt template sent to an LLM.
If the prompt template contains variables, then the getPromptContext
method may be overridden to provide values for these variables. The request object is provided to build the Map<String, Object>
context. The default implementation uses Jackson ObjectMapper
to convert the input object to JSON string first, then convert the JSON string to a Map<String, Object>
.
If you want to customize the request sent to an LLM, you can override updateChatClientRequest
method to modify the ChatClientRequestSpec
object. For example, you can add a system text.
@Override
protected void updateChatClientRequest(ChatClientRequestSpec spec) {
spec.system(
"You are a customer support agent for general questions, be polite and helper");
}
The call
method is used to send a request to an LLM and receive the response.
NoLLMTaskExecutionAgent
If you agent doesn't use LLM to execute a task, you can extends from NoLLMTaskExecutionAgent
. Then you must override call
method, as the default implementation of call
method uses ChatClient
to send request to an LLM.
Example
Let's see an sample agent to generate test users.
UserGenerationAgent
extends from TaskExecutionAgent
. The request type is UserGenerationRequest
, which contains the number of users to generate. The response type is List<User>
, which is a list of User
s.
In the getPromptTemplate
method, the prompt template is loaded from a classpath resource. Below is the content of this prompt template.
Goal: Generate {count} users
Requirements:
- Id should be a version 4 random UUID.
- Name should be using the format "$firstName $lastName".
- Email address should be using the format "$firstName.$lastName@$domain".
- For an address,
- Country or region must use ISO 3166 alpha-2 code.
- For province/state/city, they should be generated based on the country or region.
- Address line can be fake.
- Zip code should use the format based on the country or region.
- When generating multiple users, choose different countries or regions for those users.
- For a user, generate 1 to 3 addresses. At least one address has the type HOME.
In the getPromptContext
method, the UserGenerationRequest
object is used to set the value of variable count
in the prompt template.
package com.javaaidev.agenticpatterns.examples.taskexecution;
import com.javaaidev.agenticpatterns.core.AgentUtils;
import com.javaaidev.agenticpatterns.examples.taskexecution.UserGenerationAgent.UserGenerationRequest;
import com.javaaidev.agenticpatterns.taskexecution.TaskExecutionAgent;
import io.micrometer.observation.ObservationRegistry;
import java.util.List;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.ai.chat.client.ChatClient;
public class UserGenerationAgent extends
TaskExecutionAgent<UserGenerationRequest, List<User>> implements
Function<UserGenerationRequest, List<User>> {
public UserGenerationAgent(ChatClient chatClient,
@Nullable ObservationRegistry observationRegistry) {
super(chatClient, observationRegistry);
}
@Override
protected String getPromptTemplate() {
return AgentUtils.loadPromptTemplateFromClasspath("prompt_template/generate-user.st");
}
public record UserGenerationRequest(int count) {
}
}
Now we can expose a REST API to call this agent.
package com.javaaidev.agenticpatterns.examples.taskexecution;
import com.javaaidev.agenticpatterns.examples.taskexecution.UserGenerationAgent.UserGenerationRequest;
import java.util.List;
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("/users_generation")
public class UserGenerationAgentController {
private final UserGenerationAgent userGenerationAgent;
public UserGenerationAgentController(UserGenerationAgent userGenerationAgent) {
this.userGenerationAgent = userGenerationAgent;
}
@PostMapping
public List<User> generateUsers(@RequestBody UserGenerationRequest request) {
return userGenerationAgent.call(request);
}
}
When calling the REST API, we can get the test users in JSON. Below is the returned JSON when count
is set to 1
.
[
{
"id": "f3e3d3c0-6e4d-6e5d-0e3f-3c0e3d3c0e3d",
"name": "John Doe",
"email": "John.Doe@example.com",
"mobilePhone": "+1-416-555-1234",
"addresses": [
{
"id": "b1c1e1f0-4c2b-4c3b-8c1e-1f0b1c1e1f0b",
"addressType": "HOME",
"countryOrRegion": "CA",
"provinceOrState": "Ontario",
"city": "Toronto",
"addressLine": "123 Elm Street",
"zipCode": "M5A 1A1"
},
{
"id": "c2d2e2f0-5d3c-5d4c-9d2e-2f0c2d2e2f0c",
"addressType": "OFFICE",
"countryOrRegion": "CA",
"provinceOrState": "Ontario",
"city": "Toronto",
"addressLine": "456 Maple Avenue",
"zipCode": "M5B 2B2"
}
]
}
]