Exploring AI with Groovy™

Author:  Paul King
PMC Member

Published: 2025-10-15 07:06AM


Introduction

In this post, we’ll look at several ways to integrate Groovy with AI tools, including ollama4j, langchain4j, Spring AI, and Embabel.

Bulcock Beach at Sunset looking towards Pumicestone Passage

We’ll use a simple chat example, perhaps similar to what you might have done yourself when trying out your favourite LLM. We’ll ask for activities to do while on vacation.

To make the examples as accessible as possible, we’ll use Ollama’s open-source models, which can be run locally. So, no need to get keys, or use up your token limits. The libraries we use here can also connect to remote models and services.

The examples mostly use the mistral:7b model, which you’ll need to download to run the examples unchanged, but feel free to try other models and see what results you get. We also used Groovy 5 and JDK 25, but the examples should work on other Groovy and Java versions.

Using Ollama4j

Since we are using Ollama, we’ll start with a library aimed directly at that tool. Ollama4j provides a client for interacting with local or remote Ollama models.

We first create an instance of the Ollama class. We set a generous timeout to allow for longer-running models but otherwise leave the defaults as is. While not strictly necessary, we can call the ping method to verify that the Ollama server is reachable.

var ollama = new Ollama(requestTimeoutSeconds: 300)
println "Found ollama: ${ollama.ping()}"

Which gives this output:

Found ollama: true

Now we can send our prompt to the model:

var prompt = 'What are 4 interesting things to do while I am on vacation in Caloundra?'
var builder = OllamaChatRequestBuilder.builder()
    .withModel('mistral:7b')

var request = builder
    .withMessage(OllamaChatMessageRole.USER, prompt)
    .build()

var result = ollama.chat(request, null)
println "Four things:\n$result.responseModel.message.response"

It will respond with something like:

Four things:
 1. Visit the beautiful beaches: Caloundra is famous for its stunning beaches, including Kings Beach, Moffat Beach, and Bulcock Beach. Spend your days soaking up the sun, swimming, or surfing.

2. Explore the UnderWater World SeaLife Aquarium: This marine attraction offers a unique opportunity to interact with various sea creatures. You can even have a close encounter with sharks and turtles!

3. Visit the Glastonbury Estate: For those who love history, this beautiful estate is worth a visit. It was built in the 1920s and features a variety of artifacts and memorabilia from World War I and II.

4. Take a day trip to the Australia Zoo: Made famous by the Crocodile Hunter, Steve Irwin, the Australia Zoo is just a short drive from Caloundra. It's home to a wide variety of Australian wildlife, including kangaroos, koalas, and crocodiles. Don't miss the daily wildlife shows!

We can continue the conversation by including the previous chat history in the next request:

var prompt2 = 'If I had half a day and can only go to one, which would you recommend?'
request = builder
    .withMessages(result.chatHistory)
    .withMessage(OllamaChatMessageRole.USER, prompt2)
    .build()

result = ollama.chat(request, null)
println "Best thing:\n$result.responseModel.message.response"

The output might be:

Best thing:
 If you only have half a day and can choose just one activity, I would recommend visiting the beautiful Kings Beach in Caloundra. It offers a lovely stretch of sandy beach, perfect for swimming, sunbathing, or simply taking a leisurely stroll along the shoreline. The beach also has various amenities like picnic areas, BBQ facilities, and a playground for children. Plus, it provides stunning views of the Pacific Ocean and the Glass House Mountains in the distance. It's the perfect place to relax and soak up the sun on your vacation!

Using LangChain4j

LangChain4j brings LangChain’s composable AI approach to the JVM. Its unified API might be a good option if you want to experiment with different models and providers later.

Here’s the same initial prompt using its OllamaChatModel interface:

var chatModel = OllamaChatModel.builder()
    .baseUrl("http://localhost:11434")
    .timeout(Duration.ofMinutes(5))
    .modelName("mistral:7b")
    .build()

String prompt = 'What are 4 interesting things to do while I am on vacation in Caloundra?'
println "Response: " + chatModel.chat(prompt)

The output might look something like:

Response:
 1. Visit the beautiful beaches: Caloundra is known for its stunning beaches, with Kings Beach and Moffat Beach being some of the most popular ones. You can spend your days sunbathing, swimming, or surfing.

2. Explore the underwater world: Take a trip to the UnderWater World Sea Life Mooloolaba, an aquarium that houses a variety of marine life including sharks, turtles, and seahorses. It's a great way to learn about and appreciate the ocean's wonders.

3. Visit the Glastonbury Estate: This historic homestead offers a glimpse into Australia's past. The estate features beautiful gardens, a tea room, and often hosts various events throughout the year.

4. Take a day trip to the Glass House Mountains: Just a short drive from Caloundra, these iconic volcanic plugs offer breathtaking views and hiking trails for all levels of fitness. You can also visit the Kondalilla National Park for waterfalls and rainforest walks.

Using Spring AI

Spring AI provides first-class integration with the Spring ecosystem. This would be a good option if you are already using Spring Boot or need its integration and deployment capabilities.

In Groovy, it’s simple to embed AI capabilities into a Spring Boot app. Our entire script is shown below:

@SpringBootApplication
void main() {
    try(var context = SpringApplication.run(Holiday)) {
        var chatClient = context.getBean(ChatClient.Builder).build()
        var response = chatClient
                .prompt("What are some interesting things to do while I am on vacation in Caloundra?")
                .call()
        println "Response:\n" + response.content()
    }
}

We also need to set up a few properties, e.g. in application.properties, to tell Spring AI to use Ollama and our chosen model.

The output might look something like:

Response:
 Caloundra, located on the Sunshine Coast of Australia, offers a variety of activities that cater to different interests. Here are some suggestions for an enjoyable vacation:

1. Beaches: Caloundry has several beautiful beaches, including Kings Beach, Moffat Beach, and Bulcock Beach. You can swim, sunbathe, surf, or just enjoy the stunning views.

2. Visit the Underwater World SEA LIFE Mooloolaba: This aquarium is home to a diverse range of marine life, including sharks, turtles, and seahorses. It's a great place for both children and adults to learn about and interact with marine creatures.

3. Explore the Glass House Mountains: These are a series of 12 granite peaks that offer stunning views of the surrounding area. You can hike, picnic, or simply enjoy the panoramic vistas.

4. Visit the Eumundi Markets: Open on Saturdays and Wednesdays, these markets feature over 600 stalls selling art, crafts, produce, and food. It's a great place to pick up unique souvenirs and sample local delicacies.

5. Go for a scenic flight: For a truly unforgettable experience, consider taking a scenic flight over the Sunshine Coast. You'll get breathtaking views of the coastline, hinterland, and the Glass House Mountains.

6. Visit Australia Zoo: Made famous by the Crocodile Hunter, Steve Irwin, this zoo is home to a wide variety of Australian wildlife. It's a great place for families and animal lovers.

7. Enjoy local cuisine: Caloundra has a vibrant food scene with numerous restaurants offering everything from fresh seafood to international cuisines. Be sure to try some local favorites like Barramundi, Moreton Bay bugs, and mangoes.

8. Visit the Powerboat Park: If you're a fan of powerboats, this park is a must-visit. It features a museum dedicated to the history of powerboating in Australia.

9. Relax at a day spa: After a day of exploring, treat yourself to a relaxing massage or beauty treatment at one of Caloundra's many day spas.

10. Go fishing: Whether you prefer deep-sea fishing or casting a line from the shore, Caloundra offers numerous opportunities for anglers. You can even hire a charter boat if you don't have your own equipment.

Spring AI also supports structured outputs — where responses are deserialized into domain objects.

Let’s create a small domain model for describing activities and lists of activities (itineraries).

@ToString(includePackage = false)
record Activity(String activity, String location, String day, String time) {
}

record Itinerary(List<Activity> itinerary) {
}

These simple records let the AI models return structured data that Groovy can manipulate easily. With our domain records defined, our earlier example can be tweaked as follows:

@SpringBootApplication
void main() {
    try(var context = SpringApplication.run(Holiday)) {
        var chatClient = context.getBean(ChatClient.Builder).build()
        var response = chatClient
                .prompt("What are some interesting things to do while I am on vacation in Caloundra?")
                .call()
                .entity(Itinerary)
        println "Response:\n" + response.itinerary.join('\n')
    }
}

The output might look something like:

Response:
Activity(Visit Kings Beach, Caloundra, Day 1, Morning)
Activity(Explore Bulcock Beach, Caloundra, Day 1, Afternoon)
Activity(Sunset at Moffat Headland, Caloundra, Day 1, Evening)
Activity(Visit the Australian Zoo, Beerwah, Day 2, Whole Day)
Activity(Relax at Shelly Beach, Caloundra, Day 3, Morning)
Activity(Explore Pumicestone Passage by boat tour, Caloundra, Day 3, Afternoon)

Using Embabel

Embabel is a newer JVM library that provides agent orchestration and LLM integration through a declarative approach.

A simple text-generation example:

@SpringBootApplication
@EnableAgents(loggingTheme = LoggingThemes.STAR_WARS)
void main() {
    try(var context = SpringApplication.run(Holiday)) {
        println context.getBean(OperationContext)
            .ai()
            .withDefaultLlm()
            .generateText('What are some interesting things to do while I am on vacation in Caloundra?')
    }
}

The output might look something like:

Caloundra, located on the Sunshine Coast in Queensland, Australia, offers a variety of activities for tourists. Here are some suggestions for an enjoyable vacation:

1. Visit the beautiful beaches: Caloundra has several beautiful beaches, such as Kings Beach, Shelly Beach, and Moffat Beach, where you can swim, sunbathe, or surf.

2. Explore the Coastal Walk: Take a leisurely stroll along the Coastal Walk, which offers stunning views of the ocean, coastal cliffs, and nearby islands.

3. Visit the Underwater World SEA LIFE Mooloolaba Aquarium: Discover an amazing underwater world filled with sea turtles, sharks, seahorses, and more.

4. Spend a day at Australia Zoo: Home to over 1,200 animals and the late Steve Irwin's family, this iconic zoo offers up-close encounters with some of Australia's most famous wildlife.

5. Visit the Glastonbury Estate Winery: Taste locally produced wines at this picturesque winery, which also features a restaurant and beautiful gardens.

6. Explore the Bulcock Beach Esplanade: This vibrant area offers a variety of shops, cafes, and restaurants, as well as regular markets on weekends.

7. Take a day trip to Fraser Island: Known for its stunning beaches, crystal-clear waters, and rainforests, Fraser Island is just a short boat ride away from Caloundra.

8. Enjoy the Pumicestone Passage: Go boating, kayaking, or fishing in this beautiful waterway that separates Bribie Island from the mainland.

9. Visit the Powerboat Park: Watch high-speed powerboats compete in various races at this popular watersports venue.

10. Relax at a spa or wellness center: Pamper yourself with a massage, beauty treatment, or yoga class at one of Caloundra's many wellness centers.

Similarly to Spring AI, Embabel also supports structured data generation:

@SpringBootApplication
@EnableAgents(loggingTheme = LoggingThemes.STAR_WARS)
void main() {
    try(var context = SpringApplication.run(Structured)) {
        println context.getBean(OperationContext)
            .ai()
            .withDefaultLlm()
            .createObject('What are some interesting things to do while I am on vacation in Caloundra?', Itinerary)
            .itinerary
            .join('\n')
    }
}

The output might look something like:

Activity(Visit the Kings Beach, Kings Beach, Caloundra, Day 1, Morning)
Activity(Explore Bulcock Beach, Bulcock Beach, Caloundra, Day 1, Afternoon)
Activity(Dine at Mooloolaba Seafood Market, Mooloolaba Seafood Market, Mooloolaba, Day 1, Evening)
Activity(Visit the Aussie World Theme Park, Aussie World, Palmview, Day 2, Whole day)
Activity(Relax at Shelly Beach, Shelly Beach, Caloundra, Day 3, Morning)
Activity(Try Surfing at Currimundi Beach, Currimundi Beach, Currimundi, Day 3, Afternoon)
Activity(Explore the Eumundi Markets, Eumundi Markets, Eumundi, Day 4, Morning to Afternoon)

Autonomous Agents with Embabel

As a final example, let’s look at how Embabel can orchestrate multiple AI calls using its agent model.

Let’s first extend our domain model to be able to support a set of alternative itineraries and allow them to be rated.

record Alternatives(Set<Itinerary> content) {
}

record RatedAlternatives(List<RatedItinerary> content) {
}

record RatedItinerary(Itinerary itinerary, Rating rating) {
}

record Rating(double percentage) { }

Our example uses an @Agent class to coordinate multiple AI actions — generating, rating, and selecting itineraries. The @Action methods that make up the agent’s capabilities are simple to write, with our domain records as inputs and outputs.

@Agent(description = "Creates and ranks itineraries for a holiday at a given location")
class ItineraryAgent {
    @Action
    Alternatives generateItineraries(UserInput userInput, OperationContext context) {
        context.ai()
            .withLlm('mistral:7b')
            .createObject("Generate 5 sets of: An itinerary of things to do while on $userInput.content?", Alternatives)
    }

    @Action
    RatedAlternatives rateItineraries(Alternatives alternatives, OperationContext context) {
        new RatedAlternatives(alternatives.content.collect { itinerary ->
            var rating = context.ai()
                .withLlm('qwen3:8b')
                .createObject("Rate this itinerary on variety and number of activities: $itinerary?", Rating)
            new RatedItinerary(itinerary, rating)
        })
    }

    @Action
    @AchievesGoal(description = 'Best itinerary')
    RatedItinerary bestItinerary(RatedAlternatives ratedAlternatives) {
        ratedAlternatives.content.max { it.rating.percentage }
    }
}

@SpringBootApplication
@EnableAgents(loggingTheme = LoggingThemes.STAR_WARS)
void main() {
    try(var context = SpringApplication.run(Rated)) {
        println context.getBean(Autonomy)
            .chooseAndRunAgent('A long-weekend holiday in Caloundra', ProcessOptions.DEFAULT).output
    }
}

Here we are using two different models for different tasks — mistral:7b for generating itineraries and qwen3:8b for rating them. Embabel provides richer ways to control and configure the models, but this simple approach works well for our example. The @AchievesGoal annotation makes use of Embabel’s goal-oriented action planning (GOAP) capabilities.

The output might look something like (slightly formatted here for readability):

RatedItinerary[
    itinerary=Itinerary[itinerary=[
        Activity(Visit Kings Beach, Caloundra, Friday, All day),
        Activity(Sunset at Mooloolaba Beach, Mooloolaba, Friday, Evening),
        Activity(Explore Bulcock Beach Markets, Caloundra, Saturday, Morning to Early Afternoon),
        Activity(Snorkeling at Dicky Beach, Dicky Beach, Saturday, Afternoon),
        Activity(Relax at Shelly Beach, Caloundra, Sunday, All day)]],
    rating=Rating[percentage=80.0]]

This demonstrates how Embabel’s agent model and Groovy’s expressive syntax can work together to orchestrate multiple AI calls with minimal boilerplate.

Conclusion

Groovy’s interoperability, concise syntax, and powerful DSL capabilities make it an excellent language for prototyping and composing AI workflows on the JVM. Whether you’re chatting with Ollama, integrating via Spring, or orchestrating agents with Embabel, Groovy keeps your code clear and compact. Feel free to experiment with different models and prompts to see what interesting results you can achieve!

You can find the full source for all these examples at:
https://github.com/paulk-asert/groovy-ai
Other examples of using Groovy with Spring AI can be found at:
https://github.com/danvega/groovyai