Spring AI Recipe: Building a Text-Based Chat Loop Around ChatClient
When working with Spring AI, it’s often useful to have a quick way to experiment — trying prompts, testing tools, or observing how conversational behavior evolves.
Instead of writing one-off test code, a better approach is to create a simple interactive chat loop around ChatClient.
This recipe shows how to build a lightweight textual UI and extend it with conversational memory.
NOTE: This recipe has been updated since it’s original publication to reflect changes in Spring AI 2.0.0.
Setting up the project
Start with a typical Spring AI application and include a model provider starter.
For example, with OpenAI:
implementation 'org.springframework.ai:spring-ai-starter-model-openai'Configure your API key using an environment variable:
spring.ai.openai.api-key=${OPENAI_API_KEY}Using environment variables avoids committing secrets to source control.
Creating a basic ChatClient
@Bean
ChatClient chatClient(ChatClient.Builder chatClientBuilder) {
return chatClientBuilder.build();
}Spring Boot auto-configures a ChatClient.Builder, which you can use to create a ChatClient.
Adding a simple chat loop
To interact with the model, create an ApplicationRunner that reads input from the console:
@Bean
ApplicationRunner go(ChatClient chatClient) {
return args -> {
System.out.println("How can I help?\n");
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.print("> ");
System.out.println("\n - " +
chatClient.prompt(scanner.nextLine())
.call().content());
}
}
};
}Example interaction:
> Tell me a joke
- Why don't scientists trust atoms? Because they make up everything.This provides a simple but effective way to interact with ChatClient.
Adding conversational memory
At this point, each interaction is independent — no context is retained between messages.
To enable conversational memory, you can configure a MessageChatMemoryAdvisor.Rather than adding it directly in the ChatClient bean, this is a good use case for ChatClientBuilderCustomizer:
@Bean
ChatClientBuilderCustomizer chatMemoryCustomizer() {
return builder -> {
builder.defaultAdvisors(
MessageChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder()
.maxMessages(500)
.build())
.build());
};
}This configures a rolling window of up to 500 messages and applies it to all ChatClient instances.
You’ll also need to modify the chat loop to provide a conversation ID, which is now required in Spring AI 2.0.0. For our purposes, setting the conversation ID to “DEMO” should suffice:
@Bean
ApplicationRunner go(ChatClient chatClient) {
return args -> {
System.out.println("How can I help?\n");
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.print("> ");
System.out.println("\n - " +
chatClient.prompt(scanner.nextLine())
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, "DEMO"))
.call().content());
}
}
};
}In a real world application, you would probably set the conversation ID to the user’s username, session ID, or something unique to that particular user and conversation.
Trying it out
Now you can ask a question and follow it up:
> Why is the sky blue?
- The sky is blue due to a phenomenon known as Rayleigh Scattering.
> Is it ever green?
- There are several circumstances in which the sky may appear green, including...Because memory is enabled, the model understands that “it” refers to the sky.
Why this matters
Although this example is relatively simple, it provides a powerful foundation.
This chat loop becomes a reusable harness for:
- experimenting with prompts
- testing tools and agents
- observing conversational behavior
- validating memory and advisor configurations
Instead of repeatedly writing test code, you can interact with your system as you add components and make changes to configuration.
Takeaway
A text-based chat loop around ChatClient is more than a convenience—it’s a practical way to develop and explore Spring AI applications interactively.
As your applications grow more complex, this kind of harness becomes an essential tool for rapid iteration and experimentation. In fact, I intend to use it as the foundation for future recipes that I share. Stay tuned.
Get the code for this recipe (and all future recipes) at https://github.com/habuma/spring-ai-recipes