Getting Started with Spring AI and Chat Model
This article will teach you how to use the Spring AI project to build applications based on different chat models. The Spring AI Chat Model is a simple and portable interface that allows us to interact with these models. Our sample Spring Boot application will switch between three popular chat models provided by OpenAI, Mistral AI, and Ollama. This article is the first in a series explaining AI concepts with Spring Boot. Look for more on my blog in this area soon.
If you are interested in Spring Boot, read my article about tips, tricks, and techniques for this framework here.
Source Code
If you would like to try it by yourself, you may always take a look at my source code. To do that, you must clone my sample GitHub repository. Then you should only follow my instructions.
Problem
Whenever I create a new article or example related to AI, I like to define the problem I’m trying to solve. The problem this example solves is very trivial. I publish a lot of small demo apps to explain technology concepts. These apps usually need data to show a demo output. Usually, I add demo data by myself or use a library like Datafaker to do it for me. This time, we can leverage AI Chat Models API for that. Let’s begin!
Dependencies
The Spring AI project is still under active development. Currently, we are waiting for the 1.0 GA release. Until then, we will switch to the milestone releases of the project. The current milestone is 1.0.0-M5
. So let’s add the Spring Milestones repository to our Maven pom.xml
file.
<repositories>
<repository>
<id>central</id>
<name>Central</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
XMLThen we should include the Maven BOM with a specified version of the Spring AI project.
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0-M5</spring-ai.version>
</properties>
<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>
XMLSince our sample application exposes some REST endpoints, we should include the Spring Boot Web Starter. We can include the Spring Boot Test Starter to create some JUnit tests. The Spring AI modules are included in the Maven profiles
section. There are three different profiles for each chat model provider. By default, our application uses Open AI, and thus it activates the open-ai
profile, which includes the spring-ai-openai-spring-boot-starter
library. We should activate the mistral-ai
profile to switch to Mistral AI. The third option is the ollama-ai
profile including the spring-ai-ollama-spring-boot-starter
dependency. Here’s a full list of dependencies. That’ll make it a breeze to switch between different chat model AI providers — we’ll only need to set the profile parameter in the Maven running command.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>open-ai</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>mistral-ai</id>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mistral-ai-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>ollama-ai</id>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
XMLConnect to AI Chat Model Providers
Configure OpenAI
Before we proceed with a source code, we should prepare chat model AI tools. Let’s begin with OpenAI. We must have an account on the OpenAI Platform portal. After signing in we should access the API Keys page to generate an API token. Once we set its name, we can click the “Create secret key” button. Don’t forget to copy the key after creation.
The value of the generated token should be saved as an environment variable. Our sample Spring Boot application read its value from the OPEN_AI_TOKEN
variable.
export OPEN_AI_TOKEN=<YOUR_TOKEN_VALUE>
ShellSessionConfigure Mistral AI
Then, we should repeat a very similar action for Mistral AI. We must have an account on the Mistral AI Platform portal. After signing in we should access the API Keys page to generate an API token. Both the name and expiration date fields are optional. Once we generate a token by clicking the “Create key” button, we should copy it.
The value of the generated token should be saved as an environment variable. Our sample Spring Boot application read its value for Mistral AI from the MISTRAL_AI_TOKEN
variable.
export MISTRAL_AI_TOKEN=<YOUR_TOKEN_VALUE>
ShellSessionRun and Configure Ollama
Opposite to OpenAI or Mistral AI, Ollama is built to allow to run large language models (LLMs) directly on our workstations. This means we don’t have any connection to the remote API to access it. First, we must download the Ollama binary dedicated to our OS from the following page. After installation, we can interact with it using the ollama
CLI. First, we should choose the model to run. The full list of available models can be found here. By default, Spring AI expects the mistral
model for the Ollama. Let’s choose llama3.2
.
ollama run llama3.2
ShellSessionAfter running Ollama locally we can interact with it using the CLI terminal.
Configure Spring Boot Properties
Ollama exposes port over localhost
and does not require an API token. Fortunately, all necessary URLs for our APIs come with the Spring AI auto-configuration. After choosing the llama3.2
model, we should provide the change in Spring Boot application properties respectively. We can also set the gpt-4o-mini
model for OpenAI to decrease API costs.
spring.ai.openai.api-key = ${OPEN_AI_TOKEN}
spring.ai.openai.chat.options.model = gpt-4o-mini
spring.ai.mistralai.api-key = ${MISTRAL_AI_TOKEN}
spring.ai.ollama.chat.options.model = llama3.2
PlaintextSpring AI Chat Model API
Prompting and Structured Output
Here is our model class. It contains the id
field and several other fields that best describe each person.
public class Person {
private Integer id;
private String firstName;
private String lastName;
private int age;
private Gender gender;
private String nationality;
//... GETTERS/SETTERS
}
public enum Gender {
MALE, FEMALE;
}
JavaThe @RestController
class injects auto-configured ChatClient.Builder
to create an instance of ChatClient
. PersonController
implements a method for returning a list of persons from the GET /persons
endpoint. The main goal is to generate a list of 10 objects with the fields defined in the Person
class. The id field should be auto-incremented. The PromptTemplate
object defines a message, that will be sent to the chat model AI API. It doesn’t have to specify the exact fields that should be returned. This part is handled automatically by the Spring AI library after we invoke the entity()
method on the ChatClient
instance. The ParameterizedTypeReference
object inside the entity method tells Spring AI to generate a list of objects.
@RestController
@RequestMapping("/persons")
public class PersonController {
private final ChatClient chatClient;
public PersonController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping
List<Person> findAll() {
PromptTemplate pt = new PromptTemplate("""
Return a current list of 10 persons if exists or generate a new list with random values.
Each object should contain an auto-incremented id field.
Do not include any explanations or additional text.
""");
return this.chatClient.prompt(pt.create())
.call()
.entity(new ParameterizedTypeReference<>() {});
}
}
JavaAssuming you exported the OpenAI token to the OPEN_AI_TOKEN
environment variable, you can run the application using the following command:
mvn spring-boot:run
ShellSessionThen, let’s call the http://localhost:8080/persons
endpoint. It returns a list of 10 people with different nationalities. It
Now, we can change the PromptTemplate
content and add the word “famous” before persons. Just for fun.
The results are not surprising at all – “Elon Musk” enters the list 🙂 However, the list will be slightly different the second time you call the same endpoint. According to our prompt, a chat client should “return a current list of 10 persons”. So, I expected to get the same list as before. In this case, the problem is that the chat client doesn’t remember a previous conversation.
Advisors and Chat Memory
Let’s try to change it. First, we should define the implementation of the ChatMemory
interface. InMemoryChatMemory
is good enough for our tests.
@SpringBootApplication
public class SpringAIShowcase {
public static void main(String[] args) {
SpringApplication.run(SpringAIShowcase.class, args);
}
@Bean
InMemoryChatMemory chatMemory() {
return new InMemoryChatMemory();
}
}
JavaTo enable conversation history for a chat client we should define an advisor. The Spring AI Advisors API lets us intercept, modify, and enhance AI-driven interactions handled by Spring applications. Spring AI offers API to create custom advisors, but we can also leverage several built-in advisors. It can be e.g. PromptChatMemoryAdvisor
that enables chat memory and adds it to the prompt’s system text or SimpleLoggerAdvisor
which enables request/response logging. Let’s take a look at the latest implementation of the PersonController
class. I highlighted the added lines of code. Besides advisors, it contains a new GET /persons/{id}
endpoint implementation. This endpoint takes a previously returned list of persons and seeks the object with a specified id
. The PromptTemplate
object specifies the id parameter filled with the value read from the context path.
@RestController
@RequestMapping("/persons")
public class PersonController {
private final ChatClient chatClient;
public PersonController(ChatClient.Builder chatClientBuilder,
ChatMemory chatMemory) {
this.chatClient = chatClientBuilder
.defaultAdvisors(
new PromptChatMemoryAdvisor(chatMemory),
new SimpleLoggerAdvisor())
.build();
}
@GetMapping
List<Person> findAll() {
PromptTemplate pt = new PromptTemplate("""
Return a current list of 10 persons if exists or generate a new list with random values.
Each object should contain an auto-incremented id field.
Do not include any explanations or additional text.
""");
return this.chatClient.prompt(pt.create())
.call()
.entity(new ParameterizedTypeReference<>() {});
}
@GetMapping("/{id}")
Person findById(@PathVariable String id) {
PromptTemplate pt = new PromptTemplate("""
Find and return the object with id {id} in a current list of persons.
""");
Prompt p = pt.create(Map.of("id", id));
return this.chatClient.prompt(p)
.call()
.entity(Person.class);
}
}
JavaNow, let’s make a final test. After the application restarts, we can call the endpoint that generates a list of persons. Then, we will call the GET /persons/{id}
endpoint to display only a single person by ID. Spring application reads the value from the list of persons stored in the chat memory. Finally, we can repeat the call to the GET /persons
endpoint to verify if it returns the same list.
Different Chat AI Models
Assuming you exported the Mistral AI token to the MISTRAL_AI_TOKEN
environment variable, you can run the application using the following command. It activates the mistral-ai
Maven profile and includes the starter with the Mistral AI support.
mvn spring-boot:run -Pmistral-ai
ShellSessionIt returns responses similar to OpenAI’s, but some small differences exist. It always returns 0 in the age field and a 3-letter shortcut as a country name.
Let’s tweak our template to get Mistrai AI to generate an accurate age number. Here’s the fixed prompt template:
Now, it looks quite better. Even so, the names don’t match up with the countries they’re from, so there’s room for improvement.
The last test is for Ollama. Let’s run our application once again. This time we should activate the ollama-ai
Maven profile.
mvn spring-boot:run -Pollama-ai
ShellSessionThen, we can repeat the same requests to check out the responses from Ollama AI. You can check out the responses by yourself.
$ curl http://localhost:8080/persons
$ curl http://localhost:8080/persons/2
ShellSessionFinal Thoughts
This example doesn’t do anything unusual but only shows some basic features offered by Spring AI Chat Models API. We quickly reviewed features like prompts, structured output, chat memory, and built-in advisors. We also switched between some popular AI Chat Models API providers. You can expect more articles in this area soon.
2 COMMENTS