My hands-on Spring AI course is now live on JetBrains Academy
▲ 16 r/SpringAIDev+3 crossposts

My hands-on Spring AI course is now live on JetBrains Academy

Hi everyone!

My Spring AI course is now available on JetBrains Academy.

The course is designed around practical, real-world tasks completed directly in IntelliJ IDEA using the JetBrains Academy plugin. The project, dependencies, and configuration are already prepared, so you can focus on learning Spring AI and writing code instead of spending time on setup.

I honestly wish I’d had this kind of learning experience when I was starting out: clear tasks, a ready-to-use project, and immediate feedback—all inside the same IDE used for professional development.

I’d be glad to hear your feedback, especially which Spring AI topics or practical use cases you’d like to see covered next.

Course link: https://academy.jetbrains.com/course/32882

u/Proof-Possibility-54 — 10 days ago

Spring AI structured output: how to make a model correct itself

Spring AI structured output: how to make a model correct itself

​

If you use an LLM for something else than just a free-form chatting, you might probably want it to return data in a structured form, e.g. JSON

​

Spring AI allows to \[soft\] force a model to do that. But sometimes LLM fails to do that. The simplier the model is, the more chances that it will fail. Morover, any such a failure could be devided into 2 categories: incorrect schema and correct schema with incorrect data burned in. For example, some fields of the desired schema are missing. Or all fields are present, but field type, for example, is incorrect or a required filed missies a value

​

​

​

The POC i built validates not only schema as such, but also field types and ranges (e.g. Min-Max, NotNull, etc.) using validation package **spring-boot-starter-validation**

​

If any of the checks doesn't pass, this is feded back to model:

​

prompt = """

Your previous response was invalid.

Problem(s): %s

Your previous output was:

%s

Return corrected JSON that fixes these problems and matches the

schema exactly. Output ONLY JSON, no prose.

%s

""".formatted(lastError, lastOutput, format);

​

So we give a feedback to the model, not just asking to redo/re-think. By providing a detailed feedback we increase chances that the next reply will satisfy our expectations

​

As always, all code is available in the github repo: [https://github.com/DmitryFinashkin/spring-ai\](https://github.com/DmitryFinashkin/spring-ai)

​

You might also like to watch a detailed video walk-through on YouTube: [https://youtu.be/59kcnTLVu0Q\](https://youtu.be/59kcnTLVu0Q)

u/Proof-Possibility-54 — 2 months ago

Spring AI structured output: how to make a model correct itself

If you use an LLM for something else than just a free-form chatting, you might probably want it to return data in a structured form, e.g. JSON

Spring AI allows to [soft] force a model to do that. But sometimes LLM fails to do that. The simplier the model is, the more chances that it will fail. Morover, any such a failure could be devided into 2 categories: incorrect schema and correct schema with incorrect data burned in. For example, some fields of the desired schema are missing. Or all fields are present, but field type, for example, is incorrect or a required filed missies a value

The POC i built validates not only schema as such, but also field types and ranges (e.g. Min-Max, NotNull, etc.) using validation package spring-boot-starter-validation

If any of the checks doesn't pass, this is feded back to model:

prompt = """
        Your previous response was invalid.

        Problem(s): %s

        Your previous output was:
        %s

        Return corrected JSON that fixes these problems and matches the
        schema exactly. Output ONLY JSON, no prose.

        %s
        """.formatted(lastError, lastOutput, format);

So we give a feedback to the model, not just asking to redo/re-think. By providing a detailed feedback we increase chances that the next reply will satisfy our expectations

As always, all code is available in the github repo: https://github.com/DmitryFinashkin/spring-ai

You might also like to watch a detailed video walk-through on YouTube: https://youtu.be/59kcnTLVu0Q

reddit.com
u/Proof-Possibility-54 — 2 months ago
▲ 1 r/SpringAIDev+1 crossposts

Multi model setup using Spring AI

Last Friday I shared what I learned from swapping providers in a Spring AI app. This Friday: what happens when you stop swapping and start routing dynamically.

Same project as previous videos. Same ChatClient code. The only new piece is a dispatcher that looks at each request before choosing which provider handles it.

The pattern in one method:

public RoutedResponse route(String prompt) {

RoutingDecision decision = router.route(prompt);

ChatClient client = (decision.tier() == ModelTier.LOCAL)

? localClient

: cloudClient;

ChatResponse response = client.prompt(prompt).call().chatResponse();

long[] tokens = extractTokens(response, prompt, text);

tracker.record(decision, tokens[0], tokens[1]);

return new RoutedResponse(decision, text);

}

Two ChatClient beans — one autoconfigured against LM Studio (local), one explicit @Configuration for Anthropic (cloud). Spring's qualifier mechanism handles disambiguation. The dispatch is a ternary expression.

The router itself is intentionally simple — length check + keyword check. Not embeddings, not a classifier model. Just transparent rules you can debug by reading the code.

Result from the demo: 10 code review requests, 7 routed local, 3 routed cloud. Routed total $0.25 vs all-cloud baseline $0.48 — 48% lower, with identical-quality answers on the easy questions (verified by side-by-side comparison).

The data point worth flagging: those 7 routed-away queries would have cost ~$0.23 collectively on cloud, almost matching the $0.25 from the 3 cloud queries. The cheap-individually queries collectively rival the expensive ones. Routing the long tail away from cloud is where the real savings come from, not avoiding premium prices on premium queries.

A few practical notes that aren't obvious until you actually ship this:

  1. Anthropic's API requires max_tokens on every request. Without it, Spring AI's default truncates Opus responses mid-sentence. Set it explicitly to 4096 on cloud options.

  2. Claude Opus regularly takes 15-45 seconds per call. Spring AI's underlying Reactor Netty client has a default response timeout shorter than that. You'll see ReadTimeoutException in the Spring log if you don't extend it. Custom RestClient.Builder with responseTimeout(Duration.ofSeconds(300)).

  3. Don't refactor the original endpoints. The /chat, /review, /chat-with-tools endpoints from Model Switching keep running on the autoconfigured local ChatClient unchanged. The new multi-model controller lives in a separate package under /ai. Less surgery, less narrative debt.

Recorded the full walkthrough including a live cost dashboard demo: https://youtu.be/ziMzlY9Szvs

Has anyone here implemented cost-based routing in production? Curious how teams are handling the "is this request hard enough to escalate" decision — keyword rules, embeddings, confidence scoring, or something else.

As always, latest code in the repo: https://github.com/DmitryFinashkin/spring-ai

u/Proof-Possibility-54 — 3 months ago
▲ 11 r/SpringBoot+1 crossposts

Spring AI with local model through LM Studio

Couple of days ago I shared what I learned about Spring AI's chat memory. Today, here's what happened when I swapped the model behind it entirely.

Same Spring AI app. Same Java code. Same ChatClient, same @Tool annotations, same BeanOutputConverter for structured output. The only thing that changed: which model handled the requests.

OpenAI (GPT-4o) → Anthropic Claude Opus 4→ local Gemma 4 2B running through LM Studio.

The OpenAI → Claude switch was expected to work. Swap the starter dependency, update the config block, ship. Spring AI's provider abstraction is designed for this.

The local Gemma 4 2B switch was the interesting part. Same Anthropic starter dependency, just pointed at localhost:1234:

spring:

application:

name: spring-ai

ai:

anthropic:

api-key: ${LM_STUDIO_API_KEY}

base-url: http://127.0.0.1:1234

chat:

options:

model: google/gemma-4-e2b

memory:

repository:

jdbc:

initialize-schema: always

That's the entire config delta. LM Studio implements the Anthropic protocol, so Spring AI treats it as just another Anthropic-compatible endpoint. No separate "spring-ai-local" starter. No conditional Java code paths.

What I didn't expect — the 2B local model handled:

- Chat with memory (the same ChatMemoryAdvisor + JDBC repository setup from yesterday's post)

- Structured JSON output matching strict schemas

- Tool calling with proper parameter dispatch

- Code review (correctly identified a == vs .equals() bug in a real Java example)

Quality wasn't quite GPT-4o level, but it was meaningful enough that for what's probably 70% of business AI use cases — classification, summarization, structured extraction, simple agent loops — this would work in production. With zero per-request cost and full offline operation.

Recorded a walkthrough showing all three providers running the same demos (chat, memory, structured output, tool calling, code review) if you prefer video: https://youtu.be/lW0FMjDUzik

Repo with code: https://github.com/DmitryFinashkin/spring-ai

Has anyone here shipped multi-provider Spring AI in production yet? Curious how teams are handling provider routing — cost-based, latency-based, quality fallback, regional compliance — and what failure modes you're watching for.

reddit.com
u/Proof-Possibility-54 — 3 months ago
▲ 1 r/SpringAIDev+1 crossposts

Spring AI with local model through LM Studio

Couple of days ago I shared what I learned about Spring AI's chat memory. Today, here's what happened when I swapped the model behind it entirely.

Same Spring AI app. Same Java code. Same ChatClient, same @Tool annotations, same BeanOutputConverter for structured output. The only thing that changed: which model handled the requests.

OpenAI (GPT-4o) → Anthropic Claude Opus 4→ local Gemma 4 2B running through LM Studio.

The OpenAI → Claude switch was expected to work. Swap the starter dependency, update the config block, ship. Spring AI's provider abstraction is designed for this.

The local Gemma 4 2B switch was the interesting part. Same Anthropic starter dependency, just pointed at localhost:1234:

spring:

application:

name: spring-ai

ai:

anthropic:

api-key: ${LM_STUDIO_API_KEY}

base-url: http://127.0.0.1:1234

chat:

options:

model: google/gemma-4-e2b

memory:

repository:

jdbc:

initialize-schema: always

That's the entire config delta. LM Studio implements the Anthropic protocol, so Spring AI treats it as just another Anthropic-compatible endpoint. No separate "spring-ai-local" starter. No conditional Java code paths.

What I didn't expect — the 2B local model handled:

- Chat with memory (the same ChatMemoryAdvisor + JDBC repository setup from yesterday's post)

- Structured JSON output matching strict schemas

- Tool calling with proper parameter dispatch

- Code review (correctly identified a == vs .equals() bug in a real Java example)

Quality wasn't quite GPT-4o level, but it was meaningful enough that for what's probably 70% of business AI use cases — classification, summarization, structured extraction, simple agent loops — this would work in production. With zero per-request cost and full offline operation.

Recorded a walkthrough showing all three providers running the same demos (chat, memory, structured output, tool calling, code review) if you prefer video: https://youtu.be/lW0FMjDUzik

Repo with code: https://github.com/DmitryFinashkin/spring-ai

Has anyone here shipped multi-provider Spring AI in production yet? Curious how teams are handling provider routing — cost-based, latency-based, quality fallback, regional compliance — and what failure modes you're watching for.

reddit.com
u/Proof-Possibility-54 — 3 months ago
▲ 40 r/SpringAIDev+1 crossposts

Built my first AI app entirely in Java using Spring AI

Built my first AI app entirely in Java using Spring AI — no Python involved

I've been experimenting with Spring AI (the official Spring project for AI integration) and was surprised how little code it takes to get something working.

The whole setup is one Maven dependency and a few lines of YAML config. From there I built three things on top of the same project:

  • A simple chat endpoint using ChatClient — literally prompt(), call(), content()
  • Structured output that maps AI responses directly to Java records (no JSON parsing)
  • Tool calling where the AI invokes Java methods to get real data

The tool calling part was the most interesting — you annotate a method with @Tool and Spring AI handles the function-calling protocol with the model. The AI decides when to call your code and uses the result in its response.

I recorded the whole process if anyone wants to see the code in action: https://youtu.be/SiPq1i_0YgY

Anyone else using Spring AI in production or side projects? Curious what use cases people are finding beyond chat endpoints.

u/Proof-Possibility-54 — 3 months ago