Spring AI Concept and Project
Spring AI Learning Roadmap
Spring AI
│
┌──────────────┼───────────────┐
▼ ▼ ▼
Prompt ChatClient ChatModel
│
▼
Message Roles
│
▼
Advisors
│
▼
Prompt Templates
│
▼
Chat History (Memory)
│
▼
RAG
│
▼
Function Calling
│
▼
MCP (AI Agents)
1. Message Roles ⭐⭐⭐⭐⭐
LLMs don't just receive text; they receive messages with roles.
There are three common roles:
| Role | Purpose |
|---|---|
| System | Tells the AI how to behave |
| User | The user's question |
| Assistant | Previous AI responses |
Example:
System: You are a Java expert. User: Explain Spring Boot. Assistant: Spring Boot is...
The model understands who said what.
In Your Project
System: You are ASK AI. User: How to create scheduler? Assistant: Follow these steps...
2. Advisors ⭐⭐⭐⭐⭐
An Advisor intercepts or enriches a request before it reaches the model.
Think of it like a Spring MVC interceptor or a filter.
User ↓ Advisor ↓ ChatModel ↓ LLM
Examples:
- Add company policies
- Add user information
- Add chat history
- Add RAG context
- Log prompts
For example, every prompt could automatically get:
You are AskAI. Answer only from Confluence.
without repeating it in your controller.
3. Prompt Templates ⭐⭐⭐⭐☆
Instead of hardcoding prompts:
"You are a Java expert. Explain " + topic
Use placeholders:
You are a Java expert. Explain: {topic}
At runtime:
topic = Spring Boot
becomes:
You are a Java expert. Explain: Spring Boot
This keeps prompts reusable and easier to maintain.
4. Chat History (Memory) ⭐⭐⭐⭐⭐
Without memory:
User:
Who is James Gosling?
AI answers.
Then:
Where was he born?
The AI may not know "he" refers to James Gosling.
With memory:
User: Who is James Gosling? AI: Creator of Java. User: Where was he born?
The previous conversation is included, so the AI understands the reference.
This is essential for chatbots.
5. RAG (Retrieval-Augmented Generation) ⭐⭐⭐⭐⭐⭐
This is the most important topic for your project.
Without RAG:
Question ↓ GPT ↓ General Internet Knowledge
With RAG:
Question ↓ Vector Search ↓ Confluence ↓ Relevant Documents ↓ GPT ↓ Answer
This is exactly how you'll build your DBAssist AI Assistant.
6. Function Calling (Tool Calling) ⭐⭐⭐⭐⭐
Suppose the user asks:
Show failed schedulers.
The AI doesn't know your database.
Instead:
User ↓ AI ↓ Calls Java Method ↓ Database ↓ Result ↓ AI Explains
For example, your application could have:
schedulerService.getFailedSchedulers()
The AI invokes that method, gets live data, and answers using the result.
7. MCP (Model Context Protocol) ⭐⭐⭐⭐⭐⭐
MCP is a standard protocol that lets AI communicate with external systems.
Instead of hardcoding integrations:
AI ↓ Database ↓ Confluence ↓ GitHub ↓ Jira
the AI talks to MCP servers.
An MCP server can expose:
- Confluence
- Jira
- GitHub
- Databases
- File systems
The AI discovers available tools and uses them when appropriate.
Your Future Project
This is how your final enterprise application could work:
User ↓ Spring Boot ↓ ChatClient ↓ Advisor (Add company policy) ↓ Memory (Previous conversation) ↓ RAG (Search Confluence) ↓ Function Calling (Database/API) ↓ OpenAI / Ollama ↓ Answer
ChatModel and ChatClient.
Think of it like Spring Data JPA:
- DataSource → Connection to the database
- JdbcTemplate/JPA Repository → Easy way to query the database
Similarly:
- ChatModel → Connection to the AI model
- ChatClient → Easy way to chat with the AI
1. ChatModel
ChatModel is the low-level interface that communicates directly with the LLM (OpenAI, Gemini, Claude, etc.).
It is responsible for:
- Sending requests to the AI provider
- Receiving responses
- Managing authentication
- Converting Java objects to API requests
- Parsing the API response
Think of it as:
@RestController
@RequestMapping("/api")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt(message)
.call()
.content();
}
}
Test with these URLs
Test 1
http://localhost:8080/api/chat?message=Hello
Test 2
http://localhost:8080/api/chat?message=What is Java?
Test 3
http://localhost:8080/api/chat?message=Who are you?
Building a “Hello World” App with Spring AI & Ollama
What is Ollama? Ollama is a developer-friendly tool that allows you to run large language models (LLMs) locally on your machine — easily and efficiently. Built for privacy, speed, and offline use.
Key features of Ollama:
Local execution: Run AI models entirely on your machine without internet dependency.
Model library: Access to popular open-source models through a simple download system
API access: Provides REST API endpoints so you can integrate models into your applications
Cross-platform: Works on macOS, Linux, and Windows
To get started with Ollama, visit https://ollama.com/
1. Install model in local using ollama - We can install a model with a simple command like mentioned below,
ollama run llama3.2:1b
2. Add Required Dependencies - Enables Spring Boot web and ollama integration
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>openai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name/>
<description/>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. configure properties in Spring AI app
Configure below properties in application.properties or application.yml.
API key is not required as the model is available in local.
spring.ai.model.chat=ollama
spring.ai.ollama.chat.model=llama3.2:1
4. Create the Chat Controller
package com.example.openai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/apis")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt(message)
.call()
.content();
}
}
You now have a minimal working Spring AI app that connects to ollama and responds to your prompts.
Working with Multiple Chat Models in Spring AI
Multiple Chat Models means your application can use more than one AI model at the same time.
For example:
- OpenAI for general questions
- Ollama for local/private questions
- Gemini for image analysis
Instead of using only one model:
User
│
▼
OpenAI
You can have:
User
│
┌──────────┼──────────┐
▼ ▼ ▼
OpenAI Ollama Gemini
Why use Multiple Models?
Suppose you're building Application-
| Task | Model |
|---|---|
| General Chat | OpenAI |
| Company Documents | Ollama |
| Screenshot Analysis | Gemini |
.The application chooses the best model for each task.
Why Companies Use Multiple Models
A common pattern is:
- OpenAI → Best quality for customer-facing conversations.
- Ollama → Keeps sensitive company data on internal infrastructure.
- Gemini → Image understanding.
- Claude → Long-document analysis.
The application routes each request to the most appropriate model.
In real-world applications, it's common to integrate multiple chat models for better flexibility, performance, and
user experience. Here are some practical scenarios:
Why Use Multiple Chat Models?
Task-Based Model Selection
Use a powerful model for complex reasoning and a lightweight model for basic queries.
Fallback Strategy
Automatically switch to another model if the primary one is unavailable.
A/B Testing
Test different models or configurations to compare accuracy, latency, and cost.
User Preference
Allow users to choose their preferred model for interaction.
Specialized Models
Combine models with different strengths (e.g., one for code, another for creative writing).
Case 1: Single Model (Default Behavior)
Suppose your application.properties contains:
spring.ai.openai.api-key=xxxx
Spring Boot sees only one AI model (OpenAI).
During startup, it creates:
OpenAiChatModel Bean
│
▼
ChatClient.Builder Bean
Internally, it's similar to:
@Bean
ChatClient.Builder chatClientBuilder(ChatModel chatModel) {
return ChatClient.builder(chatModel);
}
Since there is only one ChatModel, Spring knows exactly which one to use.
Now your code works:
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
Everything is automatic.
Case 2: Multiple Models
Now suppose you add both:
spring.application.name=openai
logging.pattern.console=%green(%d{HH:mm:ss.SSS}) %blue(%-5level) %red([%thread]) %yellow(%logger{15}) - %msg%n
spring.ai.ollama.chat=ollama
spring.ai.ollama.chat.model=llama3.2
spring.ai.chat.client.enabled=false
spring.ai.openai.api-key=${OPENAI_API_KEY}
package com.example.openai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class MultiModelChatController {
private final ChatClient openAiChatClient;
private final ChatClient ollamaChatClient;
public MultiModelChatController(@Qualifier("openAiChatClient") ChatClient openAiChatClient,
@Qualifier("ollamaChatClient") ChatClient ollamaChatClient) {
this.openAiChatClient = openAiChatClient;
this.ollamaChatClient = ollamaChatClient;
}
@GetMapping("/openai/chat")
public String openAIChat(@RequestParam ("message") String message)
{
return openAiChatClient.prompt(message).call().content();
}
@GetMapping("/ollama/chat")
public String ollamaChat(@RequestParam("message") String message) {
return ollamaChatClient.prompt(message).call().content();
}
}
package com.example.openai.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient openAiChatClient(OpenAiChatModel openAiChatModel) {
return ChatClient.create(openAiChatModel);
}
@Bean
public ChatClient ollamaChatClient(OllamaChatModel ollamaChatModel) {
ChatClient.Builder chatClientBuilder = ChatClient.builder(ollamaChatModel);
return chatClientBuilder.build();
}
}
Spring creates:
OpenAiChatModel Bean
OllamaChatModel Bean
Now Spring has a problem.
Which ChatModel should be used?
When Spring tries to auto-create:
@Bean
ChatClient.Builder builder(ChatModel model)
it asks:
Which
ChatModel?
There are now two:
ChatModel
├── OpenAiChatModel
└── OllamaChatModel
Result
You'll get an error like:
No qualifying bean of type ChatModel
expected single matching bean
but found 2
This is exactly the same problem you see with multiple DataSource beans or multiple JdbcTemplate beans.
Why Disable Auto Configuration?
Spring AI normally creates:
ChatClient.Builderfor you.
But now you don't want one builder.
You want
OpenAI Builderfor you.
But now you don't want one builder.
You want
OpenAI Builder
AND Ollama BuilderTherefore you tell Spring:
Therefore you tell Spring:
spring.ai.chat.client.enabled=falseThis means:
"Spring, don't create the default
ChatClient.Builder. I'll create my own."Then You Create Them Yourself.
Why Does Spring AI Recommend This?
Because only you know your business logic.
For example:
User asks about company documentation │ ▼ Ollama ---------------------------- User asks a general question │ ▼ OpenAI
Spring cannot infer this routing automatically,
so it lets you create separate builders and choose the appropriate one in your application.
What You've Built So Far • A simple Spring AI app that sends prompts and receives responses • Great start—but it only scratches the surface
====================================Part2============================================
Understanding Message Roles in LLMs
System → Instructions to AI User → Question from the user Assistant → AI's previous response Tool (Function) → Result returned by a tool/function
Comments
Post a Comment