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());

3.2. Fluent API Configuration

For more control over execution:

AgentClientResponse response = agentClient
    .goal("Refactor the UserService class to use dependency injection")
    .workingDirectory("/path/to/project")
    .yolo(true)
    .run();

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();

4.2. YOLO Mode

Enable or disable the agent’s ability to make changes without confirmation:

// Enable YOLO mode for development
agentClient.goal("Fix all compilation errors")
    .yolo(true)
    .run();

// Disable for safe analysis
agentClient.goal("Analyze code quality issues")
    .yolo(false)
    .run();

4.3. Agent-Specific Options

Configure agent-specific behavior:

ClaudeAgentOptions options = ClaudeAgentOptions.builder()
    .model("claude-sonnet-4-0")
    .maxTokens(8192)
    .timeout(Duration.ofMinutes(10))
    .build();

agentClient.goal("Generate comprehensive documentation")
    .options(options)
    .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