ChatClient.CallResponseSpec.entity() mapping LLM JSON to a record (TicketTriage, with a real enum-constrained Priority field), a List<ActionItem>, and a Map<String,Object> -- every case driven by a hand-written ScriptedChatModel with no live LLM anywhere. Key findings, all confirmed by disassembling spring-ai-client-chat-2.0.1.jar and spring-ai-model-2.0.1.jar rather than trusting docs: - StructuredOutputValidationAdvisor lives in org.springframework.ai.chat.client.advisor, in the same spring-ai-client-chat artifact as ToolCallingAdvisor -- unlike the tool-calling module's Tool Search Advisor pieces, it needs no separate Maven Central artifact or version pin. - entity(Class, spec -> spec.validateSchema()) is sugar: DefaultCallResponseSpec.resolveAdvisorChain builds a real StructuredOutputValidationAdvisor from the same JSON schema BeanOutputConverter uses to parse the response, and pushes it onto the advisor chain for that one call. - The schema/format instructions are baked into the user message once, up front, by entity() itself, before the advisor chain runs at all. A validation retry's only contribution is one appended line: "Output JSON validation failed because of: <the real schema-validator error>" -- each retry re-augments the ORIGINAL request, not the previous attempt's, so corrections never stack. - Default maxRepeatAttempts is 3 (4 total attempts); default advisorOrder is 2147481647, near Ordered.LOWEST_PRECEDENCE. - Exhausting every retry does NOT throw -- adviseCall's loop just returns the last (still invalid) response to the caller. Plain entity() with no validation, by contrast, throws immediately on the same bad JSON, since BeanOutputConverter.convert() is a separate Jackson deserialization step with no retry loop of its own. Both behaviors are captured from real runs (output/02, output/06). - Spring AI 2.0's JSON stack is Jackson 3 (tools.jackson.databind), not classic com.fasterxml.jackson -- visible directly in every one of this advisor's constructor and field signatures. Companion module for "Structured Output in Spring AI 2.0: Records, JSON Schema and Self-Correcting Responses" on ankurm.com. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01FtpJvZfg4nvLvtzgJTDWpB
3.2 KiB
3.2 KiB
spring-ai
Runnable companion code for the Spring AI articles on ankurm.com. One directory per module; each module is one commit and carries its own README, tests and captured output.
| Module | What it is | Article |
|---|---|---|
getting-started/ |
One ChatClient bean, three endpoints (plain call, templated system prompt, streaming), and a test proving spring.ai.model.chat switches providers with zero code change. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Spring AI 2.0 in 10 Minutes: ChatClient on Spring Boot 4.1 |
rag/ |
Ingest PDFs, chunk, retrieve from pgvector, rerank, answer, check the answer. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. | Production-grade RAG with Spring AI and the complete example |
mcp-server/ |
An order-lookup service exposed as MCP tools, a resource, and a prompt with @McpTool/@McpResource/@McpPrompt, served over Streamable HTTP (Spring AI 2.0's default MCP server transport). Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Build an MCP Server with Spring AI 2.0 |
mcp-client/ |
ChatClient calling tools from two real external MCP servers (filesystem, git) over stdio via defaultToolCallbacks(ToolCallbackProvider...), contrasted with a local @Tool method, with every call logged through one Micrometer ObservationHandler. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Spring AI MCP Client: Calling External MCP Servers from ChatClient |
mcp-secure/ |
The mcp-server article's order-lookup tools behind a real OAuth2 resource server: JWT validation, one scope per tool via @PreAuthorize, unauthenticated tool discovery rejected outright, and every call audit-logged through MDC -- denials included. Spring Boot 4.1.1, Spring AI 2.0.1, Spring Security 7.1.1, Java 25. |
Securing an MCP Server with Spring Security 7 |
tool-calling/ |
@Tool methods, ToolCallingAdvisor (the advisor-layer replacement for Spring AI 1.x's per-model tool loop), returnDirect, ToolContext, and ToolSearchToolCallingAdvisor for progressive disclosure across a 230-tool synthetic library -- every test driven by a hand-written ScriptedChatModel, no live model anywhere. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Tool Calling in Spring AI 2.0 |
structured-output/ |
ChatClient.entity() mapping LLM responses to Java records, lists and maps; StructuredOutputValidationAdvisor retrying non-conforming JSON with a real enum-constrained schema, including a captured run that exhausts every retry without throwing. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Structured Output in Spring AI 2.0 |
Upgrading from Spring AI 1.x: migration guide.