You will understand how to tell an LLM the exact shape of the data you want back, so your Java code never has to guess at the output again.
Picture this: you're building the EngineerPrep lesson pipeline. The planner service calls Claude via Amazon Bedrock and asks it to return a list of chapter topics. Claude responds beautifully — in flowing English prose. "The first chapter should cover the concept itself, followed by a visual walkthrough, and then a hands-on exercise..." Your Java code expected a List<ChapterTopic . Instead it got a paragraph. Your first instinct is reasonable: add a sentence to the prompt that says "Reply in JSON." So you do. Claude now returns JSON — sometimes. Other times it wraps it in a markdown code fence. Sometimes it adds a friendly sentence before the opening brace. Every new response format breaks your ObjectMapper.readValue() call in a different way. The question the rest of this lesson answers: how do you make the model return exactly the shape your Java code expects…
Think about ordering food at a restaurant using a printed form instead of talking to a waiter. When you fill in a form — tick a box for size, write a number for quantity, circle your choice — the kitchen gets information in a predictable shape. No ambiguity. No back-and-forth. Schema-first generation works the same way. Instead of asking the model to "reply in JSON," you hand it a schema (a blueprint that describes exactly which fields should exist, what type each field is, and which ones are required). The model API uses that schema as a constraint on the output format — it cannot reply in prose, add unexpected fields, or skip required ones. A schema here means a JSON Schema — a small document that says things like: "this object must have a field called difficulty that is one of EASY , MEDIUM , or HARD ." Think of it as the blank form the model must fill out.…
Step 1 — You define the shape in Java. Imagine a simple record: PracticeQuestion with three fields: id (a number), difficulty (one of three values), and text (a string). That's your form template. PracticeQuestion ├── id : integer ├── difficulty: "EASY" | "MEDIUM" | "HARD" └── text : string (required) Step 2 — Spring AI converts it to a JSON Schema. Behind the scenes, Spring AI reads your Java class and produces a JSON Schema document — the blank form. You never write this by hand; it's generated automatically. { "type": "object", "properties": { "id": { "type": "integer" }, "difficulty": { "enum": "EASY","MEDIUM","HARD" }, "text": { "type": "string" } }, "required": "id", "difficulty", "text" } Step 3 — The schema travels to the model with the request. Spring AI attaches this schema to the API call it sends to Amazon Bedrock (Claude). Claude now knows: "I must fill in this form…