Spring AI is a powerful framework that seamlessly integrates AI capabilities into Spring Boot applications. It abstracts complex AI workflows and enables developers to design intelligent agents capable of reasoning, task automation, workflow orchestration, and parallelization. Its modular architecture makes it suitable for building Chain, Routing, Orchestrator, and Evaluator-Optimizer agents. Let us explore how building effective agents with Spring AI can contribute to creating smarter and more efficient solutions.
1. Building Effective Agents with Spring AI
In Spring AI, an agent represents an intelligent workflow unit designed to perform reasoning, execute tasks, and make decisions using Large Language Models (LLMs). Agents serve as the core abstraction that enables developers to build AI-powered applications capable of handling complex workflows, automating decision-making, and interacting with external systems.
1.1 Types of Agents in Spring AI
Spring AI supports several agent patterns that are commonly used to solve real-world problems. Each pattern addresses different use cases and can be combined to build sophisticated AI workflows.
1.1.1 Chain Agents
Chain Agents execute a series of tasks sequentially, where the output from one step serves as the input to the next. This pattern is useful for workflows that require step-by-step processing or incremental refinement. For example, a Chain Agent might first generate a summary of a document, then extract keywords from that summary, and finally produce a concise title based on those keywords. This approach ensures modularity and clear separation of concerns within the workflow, improving maintainability and extensibility. An example workflow could be: generate summary → extract keywords → provide title.
1.1.2 Routing Agents
Routing Agents make intelligent decisions about which workflow or sub-agent to invoke based on the input they receive. They are essential for handling diverse types of requests and directing them to the most appropriate processing path. For instance, if the input contains the keyword “summarize”, the Routing Agent invokes the summary Chain Agent. If the input contains the keyword “insight”, it routes the request to an insights agent designed to analyze and provide technical or market insights. For all other inputs, the agent falls back to a generic model handler that processes open-ended queries. This pattern allows building flexible and adaptable AI systems capable of handling multiple use cases dynamically.
1.1.3 Orchestrator Agents
Orchestrator Agents are responsible for coordinating and managing complex workflows that involve multiple agents running in combination. They handle chaining, routing, and parallel executions by orchestrating interactions between different agents to produce a unified outcome. For example, an Orchestrator Agent might trigger a routing agent to select the appropriate task, then execute several Chain Agents in parallel to gather different insights, and finally evaluate the combined output for quality and relevance. This pattern is particularly useful in enterprise environments where workflows often span multiple systems and require robust coordination and error handling.
1.1.4 Evaluator-Optimizer Agents
Evaluator-Optimizer Agents assess the quality of generated outputs and provide recommendations or corrections to improve them. These agents apply heuristics, scoring mechanisms, or additional model evaluations to refine responses and ensure they meet desired standards. For example, if the output exceeds a certain length, the Evaluator-Optimizer Agent suggests shortening the response to enhance clarity and user engagement. If the output quality is detected to be low based on predefined metrics (e.g., relevance, coherence), the agent triggers a re-generation or enhancement workflow. This feedback loop enables continuous improvement and optimization of AI-generated content, making the system more reliable and user-friendly over time.
2. Code Example
To get started, create a Spring Boot project and integrate the spring-ai-openai-spring-boot-starter dependency.
2.1 Add Dependencies
Add the following dependencies in your pom.xml file:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>latest__jar__version</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>latest__jar__version</version> </dependency> </dependencies>
The first dependency initializes the basic Spring Boot application setup, the second adds Spring AI’s OpenAI starter to let your application communicate with OpenAI models, and the third includes Spring AI Core, which provides the foundational classes and abstractions required for interacting with multiple AI model providers.
2.2 Adding properties to Property File
Create an environment variable or define your API key in application.properties.
To generate an OpenAI API key, you need to create an account on the OpenAI platform. After signing up and verifying your email, log in to the OpenAI dashboard and navigate to the API Keys section. Here, you can create a new secret key by clicking the “Create new secret key” button. Make sure to copy and securely store the generated key immediately, as it will only be shown once. This API key is essential for authenticating your application’s requests to OpenAI services and should be kept confidential to prevent unauthorized access. Once you have the key, you can set it in your application’s application.properties file or as an environment variable to enable your Spring AI application to communicate with OpenAI models securely.
# application.properties spring.ai.openai.api-key=your_openai_api_key spring.ai.openai.model=gpt-4
The application.properties file contains configuration settings for the Spring AI OpenAI integration. The property spring.ai.openai.api-key should be set to your actual OpenAI API key, which authenticates your application’s requests to the OpenAI service. The property spring.ai.openai.model specifies the default AI model to use for processing requests, in this case, gpt-4. These properties are injected into the Spring configuration classes (like OpenAiConfig) and enable flexible configuration management, allowing you to easily switch API keys or models without changing your application code. For security, it’s recommended to store sensitive keys in environment variables or secure vaults rather than hardcoding them directly in property files.
2.3 Bean Configuration for OpenAI Client
Create a configuration class that exposes an AiClient bean:
// OpenAiConfig.java
package com.example.aiagents.config;
import org.springframework.ai.client.openai.OpenAiClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenAiConfig {
@Value("${spring.ai.openai.api-key}")
private String openAiApiKey;
@Value("${spring.ai.openai.model:gpt-4}")
private String modelName;
@Bean
public OpenAiClient openAiClient() {
return new OpenAiClient(openAiApiKey, modelName);
}
}
2.3.1 Code Explanation
The OpenAiConfig class is a Spring configuration component responsible for creating and configuring the OpenAiClient bean, which is used to interact with OpenAI models. Annotated with @Configuration, it signals Spring to treat this class as a source of bean definitions. Two fields, openAiApiKey and modelName, are injected from application properties using @Value. The openAiApiKey holds the secret API key necessary for authenticating requests with the OpenAI service, while modelName specifies the default model to use (defaulting to “gpt-4” if not provided). The method openAiClient() is annotated with @Bean, indicating that it returns a Spring-managed bean. This method instantiates an OpenAiClient using the configured API key and model name, making it available for dependency injection throughout the application. By centralizing the client configuration, this class ensures that the AI client is consistently set up and easily maintainable, allowing other components like services and controllers to seamlessly use the OpenAI client without worrying about its initialization details.
2.4 Unified Multi-Agent Service
// UnifiedAgentService.java
package com.example.aiagents.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.client.openai.OpenAiClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
@EnableAsync
@Service
public class UnifiedAgentService {
@Autowired
private OpenAiClient openAiClient;
private final ObjectMapper objectMapper = new ObjectMapper();
// 1. LLM-based Routing Agent: Ask the LLM how to route this input
public String llmRouteDecision(String query) {
String routingPrompt =
"You are a routing agent. Analyze this user input and return only the NEXT step: " +
"'summarize', 'insight', or 'generic'. Input: " + query;
return openAiClient.prompt(routingPrompt)
.getResult()
.toLowerCase()
.trim();
}
// 2. Chain Agent (summary → title)
public String performChainedTask(String content) {
String summary = openAiClient.prompt("Summarize the following content: " + content)
.getResult();
String title = openAiClient.prompt("Generate a short, clear title for this summary: " + summary)
.getResult();
return "Title: " + title + "\nSummary: " + summary;
}
// 3. Parallel Agent: insights collected asynchronously
@Async
public CompletableFuture<String> fetchInsightA(String topic) {
String result = openAiClient.prompt("Provide a technical insight about: " + topic)
.getResult();
return CompletableFuture.completedFuture(result);
}
@Async
public CompletableFuture<String> fetchInsightB(String topic) {
String result = openAiClient.prompt("Provide a market insight about: " + topic)
.getResult();
return CompletableFuture.completedFuture(result);
}
public String performParallelInsights(String topic) {
CompletableFuture<String> i1 = fetchInsightA(topic);
CompletableFuture<String> i2 = fetchInsightB(topic);
return "Technical Insight: " + i1.join() + "\nMarket Insight: " + i2.join();
}
// 4. Orchestrator Agent: Uses routing agent's LLM decision to delegate workflow
public String orchestrateWorkflow(String query) {
String route = llmRouteDecision(query);
switch (route) {
case "summarize":
return performChainedTask(query);
case "insight":
return performParallelInsights(query);
default:
return openAiClient.prompt("Handle this request generically: " + query)
.getResult();
}
}
// 5. Recursive Evaluator Agent
/**
* Single evaluation step that returns a JSON with:
* {
* "decision": "Yes" or "No",
* "feedback": "evaluation + improved output"
* }
*/
private String rawEvaluationCall(String context, String output) {
String evaluationPrompt =
"You are an evaluator agent. Review the following output in the context of the previous instructions.\n" +
"If you think the output can be improved, improve it.\n\n" +
"Respond STRICTLY in RFC8259-compliant JSON with the following fields:\n" +
"{\n" +
" \"decision\": \"Yes\" or \"No\", // \"Yes\" = perform another evaluation iteration; \"No\" = stop.\n" +
" \"feedback\": \"Your evaluation comments and the improved final output in one coherent text.\"\n" +
"}\n\n" +
"Context: " + context + "\n" +
"CurrentOutput: " + output;
return openAiClient.prompt(evaluationPrompt).getResult();
}
/**
* Public recursive evaluator with cutoff.
*/
public String evaluateResponseRecursively(String context, String initialOutput) {
String currentOutput = initialOutput;
int maxIterations = 3; // failsafe cutoff
for (int i = 0; i < maxIterations; i++) {
String rawJson = rawEvaluationCall(context, currentOutput);
String decision = "No";
String feedback = currentOutput;
try {
JsonNode root = objectMapper.readTree(rawJson);
if (root.hasNonNull("decision")) {
decision = root.get("decision").asText("No");
}
if (root.hasNonNull("feedback")) {
feedback = root.get("feedback").asText(currentOutput);
}
} catch (IOException e) {
// If parsing fails, stop recursion and return the last good output
return currentOutput + "\n\nEvaluation (parsing error, stopping recursion): " + rawJson;
}
// Use feedback as the new "best" output
currentOutput = feedback;
// Stop if the model says no more re-evaluation
if (!"yes".equalsIgnoreCase(decision)) {
break;
}
}
return currentOutput;
}
// 6. Complete agentic workflow entrypoint: orchestrate then evaluate recursively
public String orchestrateCompleteWorkflow(String input) {
String agentOutput = orchestrateWorkflow(input);
String evaluatedOutput = evaluateResponseRecursively(input, agentOutput);
return evaluatedOutput;
}
}
2.4.1 Code Explanation
This class implements a unified multi-agent workflow in Spring Boot using OpenAI, where @EnableAsync enables parallel execution and OpenAiClient is injected for all LLM interactions; the routing agent llmRouteDecision() asks the LLM whether the user input should be summarized, analyzed for insights, or handled generically, after which the chain agent performChainedTask() runs two sequential prompts to generate a summary and then a title; the parallel agent methods fetchInsightA() and fetchInsightB() run asynchronously to gather technical and market insights which are merged in performParallelInsights(); the orchestrator orchestrateWorkflow() uses the routing decision to choose the correct agentic workflow; the evaluator agent evaluateResponse() uses the LLM to review and recursively critique the output; and finally orchestrateCompleteWorkflow() ties everything together by running the selected workflow and appending the evaluator’s feedback as the final unified response.
2.5 Controller
// AgentController.java
package com.example.aiagents.controller;
import com.example.aiagents.service.UnifiedAgentService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/ai")
public class AgentController {
private final UnifiedAgentService aiService;
public AgentController(UnifiedAgentService aiService) {
this.aiService = aiService;
}
@GetMapping("/process")
public String processQuery(@RequestParam String query) {
return aiService.orchestrateCompleteWorkflow(query);
}
}
2.5.1 Code Explanation
Once all components are configured, you can start the Spring Boot application and interact with the unified AI workflow through the exposed REST endpoint. Start the Spring Boot application using your IDE or by running the command mvn spring-boot:run. After the application starts, you can trigger the AI workflow using the following endpoint:
GET http://localhost:8080/ai/process?query=Summarize this content about Spring AI integration
The following is an example of the output returned by the chained agent workflow after passing through the recursive evaluator:
Title: Efficient Agentic Workflows with Spring AI Summary: Spring AI provides a flexible framework for integrating LLM-driven agents into Spring Boot applications, using patterns such as chaining, routing, orchestration, and evaluation to automate and optimize complex workflows through modular, reusable components.
If the query triggers the parallel insight agents, the output will look like this:
GET http://localhost:8080/ai/process?query=Provide an insight about agentic patterns --- Technical Insight: Agentic patterns in Spring AI enable developers to assemble modular and adaptive workflows that combine routing, parallel processing, and iterative evaluation so that different concerns—such as summarization, insight generation, and quality control—can evolve independently while still working together as a cohesive pipeline. Market Insight: Enterprise adoption of AI agent orchestration is accelerating as organizations look for scalable, context-aware automation capabilities that can plug into existing Spring-based systems and reduce the time to deliver production-ready AI features.
The AgentController class is a simple Spring REST controller that exposes an HTTP API endpoint for interacting with the AI workflows defined in UnifiedAgentService. Annotated with @RestController, it automatically handles HTTP requests and serializes responses as JSON or plain text. The base URL path for all endpoints in this controller is set to /ai via @RequestMapping. The controller uses constructor injection to receive an instance of UnifiedAgentService, promoting immutability and testability. It exposes a single GET endpoint at /ai/process which accepts a required query parameter named query. When a client sends a request to this endpoint with a query string, the controller delegates processing to the service’s orchestrateCompleteWorkflow method, which runs the full AI-driven workflow including routing, processing, and evaluation. The result from the service is returned directly as the HTTP response body. This controller acts as the entry point for external clients to trigger the intelligent agent workflows, providing a clean and straightforward API for integrating AI capabilities into other applications or user interfaces.
2.6 Code Run and Code Output
Once all components are configured, you can start the Spring Boot application and interact with the unified AI workflow through the exposed REST endpoint. Start the Spring Boot application using your IDE or by running the command mvn spring-boot:run. After the application starts, you can trigger the AI workflow using the following endpoint:
GET http://localhost:8080/ai/process?query=Summarize this content about Spring AI integration
The following is an example of the output returned by the chained agent workflow:
Title: Efficient Agentic Workflows with Spring AI Summary: Spring AI offers developers a flexible framework to integrate LLM-driven agents into Spring Boot applications. With patterns like chaining, routing, orchestration, and evaluation, complex workflows can be automated and optimized through modular AI components. Evaluation: The summary is concise and highlights the key functionality and benefits of Spring AI in workflow orchestration. No major improvements needed, but a concrete example could enhance clarity.
If the query triggers the parallel insight agents, the output will look like this:
GET http://localhost:8080/ai/process?query=Provide an insight about agentic patterns --- Technical Insight: Agentic patterns in Spring AI simplify the creation of modular and adaptive workflows, enabling dynamic routing, parallelized processing, and contextual evaluation powered by large language models. Market Insight: Enterprise adoption of AI agent orchestration is accelerating, driven by increased demand for efficient, scalable, and context-aware automations in digital transformation projects. Evaluation: Both insights are relevant and actionable. Consider expanding the technical insight by mentioning specific Spring AI design patterns for deeper value to the reader.
3. Conclusion
Spring AI provides a robust platform to create intelligent agents capable of executing complex workflows like chaining, parallelization, orchestration, and optimization. Using familiar Spring Boot abstractions, developers can now integrate AI natively into backend services, automate decisions, and build scalable, efficient AI-driven systems.
Thank you!
We will contact you soon.
Yatin BatraNovember 24th, 2025Last Updated: November 24th, 2025

This site uses Akismet to reduce spam. Learn how your comment data is processed.