Skip to main content

Run LLMs Locally with Spring AI and Ollama: No API Key, Testcontainers Included

Every other post in this series calls out to a hosted model. That means an API key in an environment variable, a network round trip on every request, and a per-token bill running in the background even while you're just trying things out. This article is the exception: spring-ai-starter-model-ollama talks to Ollama, a server that runs models on your own machine, over plain HTTP, with nothing to sign up for. No api-key property appears anywhere in this article's companion module -- not because it's hidden, but because there's genuinely nothing to put there.

Structured Output in Spring AI 2.0: Records, JSON Schema and Self-Correcting Responses

Every post in this series so far has ended a ChatClient call with .content() -- a string, handed back to whatever prints it. Most real code doesn't want a string. It wants a Java record it can pass to a repository, a List it can iterate, a Map it can look a key up in. This article covers ChatClient.entity(), the API that maps a model's raw JSON text straight into one of those -- and StructuredOutputValidationAdvisor, which is what happens when the model's JSON doesn't actually fit the shape you asked for and something has to decide whether to give up or try again. Nothing below calls a real LLM. Every test in this article's companion repo drives the real entity() machinery and the real StructuredOutputValidationAdvisor against a hand-written ChatModel that returns a queued, pre-programmed response instead of calling an API -- the same technique, and the same confirmed-by-javap reasoning for why it's legitimate, that the tool-calling article in this series used. Versions. Spring Boot 4.1.1 and Spring AI 2.0.1, on Java 25 (LTS) — the same baseline as the rest of this series. StructuredOutputValidationAdvisor lives in org.springframework.ai.chat.client.advisor, inside the same spring-ai-client-chat artifact as ToolCallingAdvisor — unlike the Tool Search Advisor pieces in this series' tool-calling article, it needs no separate Maven Central artifact or version pin. It also confirms something worth knowing on its own: Spring AI 2.0's JSON stack is Jackson 3 (tools.jackson.databind), not the classic com.fasterxml.jackson package every older tutorial assumes — visible directly in this advisor's constructor and field signatures.

Tool Calling in Spring AI 2.0: @Tool, ToolCallingAdvisor and the Tool Search Advisor

The ChatClient in this series has already called a couple of tools in passing. This article is the one that actually explains how tool calling works in Spring AI 2.0: the plain case, where a tool answers a question the model then paraphrases; returnDirect, where the tool's own answer skips that paraphrase entirely; ToolContext, for data a tool needs that the model must never see or supply; and Tool Search Advisor, which stops a 230-tool library from putting all 230 definitions in front of the model on every single call. Nothing below calls a real LLM. Every test in this article's companion repo drives the real Spring AI advisor classes — the same ToolCallingAdvisor and ToolSearchToolCallingAdvisor a production app would use — against a hand-written ChatModel that returns a queued, pre-programmed response instead of calling an API. That sounds like it should be a weaker kind of proof. It's the opposite: it means every number and every tool count in this article was read directly off the real advisor's real behavior, not off documentation describing what it's supposed to do. Versions. Spring Boot 4.1.1 and Spring AI 2.0.1, on Java 25 (LTS) — the same baseline as the rest of this series. The Tool Search Advisor pieces (ToolSearchToolCallingAdvisor, RegexToolIndex) live in two separately versioned Maven Central artifacts, spring-ai-tool-search-advisor and spring-ai-tool-search-tool, both at 2.0.1 — confirmed against Maven Central's own metadata, since neither is covered by spring-ai-bom and both need pinning explicitly in pom.xml.

Spring AI MCP Client: Calling External MCP Servers from ChatClient

Connect ChatClient to two real external MCP servers over stdio -- the official filesystem and git servers -- with defaultToolCallbacks, contrasted with a local @Tool method. Real tool calls driven through ToolCallingManager with no LLM involved, a double-logging trap in the Observation API, what isError:true really does client-side, and what a too-short request-timeout actually throws.

Build an MCP Server with Spring AI 2.0: @McpTool, @McpResource, and Streamable HTTP

Expose an existing Spring service as an MCP server with @McpTool, @McpResource, and @McpPrompt, over Streamable HTTP -- Spring AI 2.0's default transport. Real JSON-RPC request/response traffic, a 404 gotcha in the protocol property, a duplicated-error-message quirk found by disassembling the jar, and what it actually takes to reach the server from MCP Inspector and Claude Desktop.

Top 60 8086 Viva Questions and Answers (2026)

60 8086 microprocessor viva questions and answers for Sunday-evening exam prep, covering architecture, addressing modes, arithmetic and flags, the stack, interrupts and DOS/BIOS calls, each answer linked to a worked program or reference post.