Order from Chaos — LLMs, Java and the Power of Annotations

In the book Applied AI for Enterprise Java Development, I came across this really awesome paradigm for extracting data from unstructured-sources using LLMs. The concept is obviously nothing new but I love the way LangChain4j is exposing it.

You simply annotate POJOs and allow the model to fill in the details. Here's a quick example demonstrating the power of this approach.

package com.wininger.cli_image_labeler.dto.story;

import java.util.List;

import dev.langchain4j.model.output.structured.Description;

public record StoryInfo(
    @Description("The main character of the story")
    String mainCharacterName,
    @Description("The main character's hair color")
    String mainCharacterHairColor,
    @Description("The main character's occupation")
    String mainCharacterOccupation,
    @Description("A list of short tags that describe the story")
    List<String> storyTags
) {}
package com.wininger.cli_image_labeler.dto.story;

import dev.langchain4j.service.UserMessage;

public interface Story {
    @UserMessage("Extract information about the story, identify the main character and their attributes {{it}}")
    StoryInfo extract(String message);
}
        final String storyText = """
            Bob Vance's Best Day
        Bob Vance ran his hand through his brown hair as he unlocked the showroom. As a refrigerator salesman at Vance Refrigeration, he’d learned that the best sales come from understanding what people need, not just what they want.
        That morning, Mrs. Peterson came in looking frustrated. Her old fridge had broken, and she needed a replacement quickly.
        "Tell me about your family," Bob said, pushing aside his brown hair with a practiced gesture.
        "Three kids, always hungry," she laughed.
        Bob showed her a model that was efficient, spacious, and within her budget. He explained the features clearly, and within an hour, she was signing the paperwork.
        "Thank you, Bob. You actually listened to what I needed," she said.
        As she left, Bob smiled. Being a good refrigerator salesman wasn’t about moving units—it was about solving problems, one customer at a time.        
                    """;

        final ChatModel chatModel = OllamaChatModel.builder()
        .modelName("gemma3:4b")
        .baseUrl("http://localhost:11434/")
        .build();

        final Story tx = AiServices.builder(Story.class)
        .chatModel(chatModel)
        .build();

        final StoryInfo storyInfo = tx.extract(storyText);

        System.out.println("Extracted Story Info: " + storyInfo);
        System.out.println(storyInfo);
    }

You should see output along these lines:

Extracted Story Info: StoryInfo[mainCharacterName=Bob Vance, mainCharacterHairColor=brown, mainCharacterOccupation=refrigerator salesman, storyTags=[customer service, sales, problem solving, family]]

It's that simple! This abstraction really demonstrates the future of AI. It exposes it in a way that makes it extremely easy to integrate with traditional business logic. LLMs are not magic bullets, but they can bring new capabilities to our applications. Abstraction layers like this will make it possible and accelerate adoption.

Full source code: https://github.com/chriswininger/langchain4j-quarkus-structured-data-example