AgentClient API
The AgentClient
offers a fluent API for communicating with autonomous agents. It provides a familiar interface for Spring developers, following the same patterns as Spring AI’s ChatClient
.
The fluent API allows you to build up goal requests that are passed to autonomous agents. Unlike traditional chat models that process conversational prompts, autonomous agents execute goals that can involve reading files, running commands, and making code changes.
1. Quick Start with Spring Boot
The fastest way to get started is using a Spring Boot starter:
<dependency>
<groupId>org.springaicommunity.agents</groupId>
<artifactId>spring-ai-starter-agent-claude</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
With the starter, Spring Boot automatically configures AgentClient.Builder
(prototype scope):
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springaicommunity.agents.client.AgentClient;
import org.springaicommunity.agents.client.AgentClientResponse;
import java.nio.file.Path;
@SpringBootApplication
public class HelloAgentWorld {
public static void main(String[] args) {
SpringApplication.run(HelloAgentWorld.class, args);
}
@Bean
CommandLineRunner demo(AgentClient.Builder agentClientBuilder) {
return args -> {
// Use the autoconfigured builder
AgentClientResponse response = agentClientBuilder.build()
.goal("Create a simple Hello World Java class")
.workingDirectory(Path.of(System.getProperty("user.dir")))
.run();
System.out.println("Goal completed: " + response.isSuccessful());
System.out.println("Result: " + response.getResult());
};
}
}
See Spring Boot Autoconfiguration for complete setup details.
2. Standalone Usage (No Spring Boot)
Without Spring Boot, create AgentClient
manually:
import org.springaicommunity.agents.client.AgentClient;
import org.springaicommunity.agents.client.AgentClientResponse;
import org.springaicommunity.agents.claude.ClaudeAgentModel;
import org.springaicommunity.agents.claude.ClaudeAgentOptions;
import org.springaicommunity.agents.claude.sdk.ClaudeAgentClient;
public class StandaloneExample {
public static void main(String[] args) {
// 1. Create the Claude CLI client
ClaudeAgentClient claudeClient = ClaudeAgentClient.create();
// 2. Configure agent options
ClaudeAgentOptions options = ClaudeAgentOptions.builder()
.model("claude-sonnet-4.5")
.yolo(true)
.build();
// 3. Create the agent model
ClaudeAgentModel agentModel = new ClaudeAgentModel(claudeClient, options);
// 4. Create AgentClient
AgentClient agentClient = AgentClient.create(agentModel);
// 5. Execute a goal
AgentClientResponse response = agentClient.run(
"Create a simple Hello World Java class"
);
System.out.println("Goal completed: " + response.isSuccessful());
}
}
3. Basic Usage
3.1. Simple Goal Execution
The simplest way to run a goal:
// Assuming you have an AgentClient created as shown above
AgentClientResponse response = agentClient.run("Create a Hello World Java class");
System.out.println("Result: " + response.getResult());
System.out.println("Success: " + response.isSuccessful());
4. Configuration Options
4.1. Working Directory
Set the directory where the agent operates:
// Using string path
agentClient.goal("Generate unit tests")
.workingDirectory("/home/user/my-project")
.run();
// Using Path object
Path projectPath = Paths.get(System.getProperty("user.dir"));
agentClient.goal("Generate unit tests")
.workingDirectory(projectPath)
.run();
5. Response Handling
5.1. AgentClientResponse
The response object provides access to results and metadata:
AgentClientResponse response = agentClient.run("Generate a README file");
// Check if goal completed successfully
if (response.isSuccessful()) {
String result = response.getResult();
System.out.println("Agent completed: " + result);
} else {
System.err.println("Goal failed: " + response.getResult());
}
// Access metadata
AgentResponseMetadata metadata = response.getMetadata();
Duration duration = metadata.getDuration();
String model = metadata.getModel();
5.2. Error Handling
Handle various error conditions:
try {
AgentClientResponse response = agentClient.run("Complex refactoring goal");
if (!response.isSuccessful()) {
// Goal completed but failed
System.err.println("Agent reported failure: " + response.getResult());
}
} catch (AgentExecutionException e) {
// Agent process failed to start or crashed
System.err.println("Execution error: " + e.getMessage());
} catch (AgentTimeoutException e) {
// Goal exceeded timeout
System.err.println("Goal timed out after: " + e.getTimeout());
}
6. Spring Boot Integration
6.1. Using Spring Boot Starters
Add the Claude or Gemini starter to your project:
<!-- Claude Agent -->
<dependency>
<groupId>org.springaicommunity.agents</groupId>
<artifactId>spring-ai-starter-agent-claude</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<!-- OR Gemini Agent -->
<dependency>
<groupId>org.springaicommunity.agents</groupId>
<artifactId>spring-ai-starter-agent-gemini</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
Spring Boot automatically configures:
-
AgentClient.Builder
(prototype scope) -
AgentModel
(ClaudeAgentModel or GeminiAgentModel) -
Sandbox
(Docker or Local)
Simply inject AgentClient.Builder
into your components:
@Service
public class CodeGenerationService {
private final AgentClient.Builder agentClientBuilder;
public CodeGenerationService(AgentClient.Builder agentClientBuilder) {
this.agentClientBuilder = agentClientBuilder;
}
public String generateController(String entityName) {
AgentClientResponse response = agentClientBuilder.build()
.goal("Generate REST controller for " + entityName)
.workingDirectory(Path.of(System.getProperty("user.dir")))
.run();
return response.getResult();
}
}
See Spring Boot Autoconfiguration for configuration properties and customization options
6.2. Controller Usage
Use AgentClient.Builder
in REST controllers:
@RestController
public class DevelopmentController {
private final AgentClient.Builder agentClientBuilder;
public DevelopmentController(AgentClient.Builder agentClientBuilder) {
this.agentClientBuilder = agentClientBuilder;
}
@PostMapping("/generate-tests")
public ResponseEntity<String> generateTests(@RequestBody GenerateTestsRequest request) {
try {
AgentClientResponse response = agentClientBuilder.build()
.goal("Generate unit tests for " + request.getClassName())
.workingDirectory(Path.of(request.getProjectPath()))
.run();
if (response.isSuccessful()) {
return ResponseEntity.ok(response.getResult());
} else {
return ResponseEntity.badRequest().body(response.getResult());
}
} catch (Exception e) {
return ResponseEntity.status(500).body("Goal execution failed: " + e.getMessage());
}
}
}
7. Best Practices
7.1. Goal Formulation
Write clear, specific goals:
// Good: Specific and actionable
client.run("Add input validation to the UserController.createUser() method");
// Poor: Vague and ambiguous
client.run("Make the code better");
7.2. Working Directory Management
Always set appropriate working directories:
// For multi-module projects
client.goal("Generate integration tests")
.workingDirectory(projectRoot.resolve("service-module"))
.run();
7.3. Resource Management
AgentClient.Builder
is prototype-scoped. Build new clients as needed:
@Component
public class CodeGenerationService {
private final AgentClient.Builder agentClientBuilder;
public CodeGenerationService(AgentClient.Builder agentClientBuilder) {
this.agentClientBuilder = agentClientBuilder;
}
public String generateController(String entityName, Path projectPath) {
AgentClientResponse response = agentClientBuilder.build()
.goal("Generate REST controller for " + entityName)
.workingDirectory(projectPath)
.run();
return response.getResult();
}
public String generateTests(String className, Path projectPath) {
AgentClientResponse response = agentClientBuilder.build()
.goal("Generate unit tests for " + className)
.workingDirectory(projectPath)
.run();
return response.getResult();
}
}
8. Advanced Features
8.1. Timeout Configuration
Configure execution timeouts:
AgentOptions options = AgentOptions.builder()
.timeout(Duration.ofMinutes(15)) // Long-running refactoring goal
.build();
client.goal("Refactor entire codebase to use reactive patterns")
.options(options)
.run();
8.2. Result Streaming
For long-running tasks, some agents support progress updates:
// Note: Streaming support varies by agent implementation
AgentClientResponse response = client
.goal("Generate comprehensive test suite")
.options(AgentOptions.builder().streaming(true).build())
.run();
// Implementation-specific streaming access
if (response instanceof StreamingAgentResponse streaming) {
streaming.getProgressUpdates().forEach(System.out::println);
}
9. Working with Advisors
Agent Advisors provide powerful interception points for augmenting agent execution, following the same pattern as Spring AI’s ChatClient advisors.
9.1. Configuring Advisors
Register advisors at build time using the builder’s defaultAdvisor()
or defaultAdvisors()
methods:
AgentClient client = AgentClient.builder(agentModel)
.defaultAdvisor(new SimpleLoggerAdvisor())
.defaultAdvisor(new WorkspaceContextAdvisor())
.build();
9.2. Common Use Cases
9.2.1. Logging and Observability
Monitor agent execution with custom metrics and logging:
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
public class MetricsAdvisor implements AgentCallAdvisor {
private final MeterRegistry meterRegistry;
public MetricsAdvisor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public AgentClientResponse adviseCall(AgentClientRequest request, AgentCallAdvisorChain chain) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
AgentClientResponse response = chain.nextCall(request);
sample.stop(Timer.builder("agent.execution")
.tag("success", String.valueOf(response.isSuccessful()))
.register(meterRegistry));
return response;
} catch (Exception e) {
sample.stop(Timer.builder("agent.execution")
.tag("success", "false")
.register(meterRegistry));
throw e;
}
}
@Override
public String getName() {
return "Metrics";
}
@Override
public int getOrder() {
return 0;
}
}
9.2.2. Context Engineering
Inject additional context before agent execution (see Context Engineering):
-
Clone git repositories
-
Sync vendor dependencies
-
Gather project metadata
-
Prepare test fixtures
Example:
public class WorkspaceContextAdvisor implements AgentCallAdvisor {
@Override
public AgentClientResponse adviseCall(AgentClientRequest request, AgentCallAdvisorChain chain) {
// Inject workspace info into context before execution
Path workspace = request.workingDirectory();
String workspaceInfo = analyzeWorkspace(workspace);
request.context().put("workspace_info", workspaceInfo);
// Continue the chain
AgentClientResponse response = chain.nextCall(request);
// Add execution metrics to response context
response.context().put("files_modified", countModifiedFiles(response));
return response;
}
private String analyzeWorkspace(Path workspace) {
// Implementation details...
return "Workspace analyzed";
}
private int countModifiedFiles(AgentClientResponse response) {
// Implementation details...
return 0;
}
@Override
public String getName() {
return "WorkspaceContext";
}
@Override
public int getOrder() {
return 100;
}
}
9.2.3. Post-Execution Evaluation
Validate agent output and enforce quality standards (see Judge Concept):
-
Verify file existence
-
Run test suites
-
Check code quality metrics
-
Validate against schemas
Example:
public class TestExecutionAdvisor implements AgentCallAdvisor {
@Override
public AgentClientResponse adviseCall(AgentClientRequest request, AgentCallAdvisorChain chain) {
// Execute the agent goal
AgentClientResponse response = chain.nextCall(request);
// Post-execution: run tests to validate the changes
if (response.isSuccessful()) {
boolean testsPass = runTests(request.workingDirectory());
response.context().put("tests_passed", testsPass);
if (!testsPass) {
// Optionally modify response to indicate failure
System.err.println("WARNING: Tests failed after agent execution");
}
}
return response;
}
private boolean runTests(Path workingDirectory) {
// Run test suite and return result
return true; // Placeholder
}
@Override
public String getName() {
return "TestExecution";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100; // Run near the end
}
}
9.2.4. Security and Validation
Enforce security policies before execution:
public class GoalValidationAdvisor implements AgentCallAdvisor {
private final List<String> bannedOperations = List.of("rm -rf", "DROP DATABASE");
@Override
public AgentClientResponse adviseCall(AgentClientRequest request, AgentCallAdvisorChain chain) {
String goal = request.goal().getContent().toLowerCase();
// Block dangerous operations
for (String banned : bannedOperations) {
if (goal.contains(banned.toLowerCase())) {
// Return failure response without executing
return new AgentClientResponse(
createBlockedResponse("Goal blocked: dangerous operation")
);
}
}
return chain.nextCall(request);
}
private AgentResponse createBlockedResponse(String reason) {
// Create blocked response
return new AgentResponse(List.of(
new AgentGeneration(reason, new AgentGenerationMetadata("BLOCKED", Map.of()))
));
}
@Override
public String getName() {
return "GoalValidation";
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // Execute first for security
}
}
9.3. Spring Boot Integration
Register advisors as Spring beans and configure them with AgentClient.Builder
:
@Configuration
public class AgentAdvisorConfiguration {
@Bean
public AgentCallAdvisor metricsAdvisor(MeterRegistry meterRegistry) {
return new MetricsAdvisor(meterRegistry);
}
@Bean
public AgentCallAdvisor validationAdvisor() {
return new GoalValidationAdvisor();
}
@Bean
@Scope("prototype")
public AgentClient.Builder agentClientBuilder(
AgentModel agentModel,
List<AgentCallAdvisor> advisors) {
return AgentClient.builder(agentModel)
.defaultAdvisors(advisors);
}
}
9.4. Learn More
For complete advisor documentation, see Agent Advisors API.
10. Next Steps
-
Master the Agent Advisors API for advanced execution control
-
Learn how AgentClient compares to ChatClient in AgentClient vs ChatClient
-
Explore agent-specific features in Claude Code SDK
-
See real-world examples in Sample Agents