6.6 C
New York
Thursday, April 2, 2026
Array

Spring AI tutorial: How to develop AI agents with Spring




[
    {
        "category": "Clothing",
        "description": "Lightweight mesh running sneakers",
        "id": 24,
        "name": "Running Shoes",
        "price": 109.99
    },
    {
        "category": "Clothing",
        "description": "Cross-training athletic shoes",
        "id": 83,
        "name": "Training Shoes",
        "price": 109.99
    }
]

So, the agent effectively determined what I meant by “sports shoes,” selected some relevant keywords to search for, filtered the products based on price, and returned a list of two options for me. Because LLMs are not deterministic, your results may be different from mine. For example, in other runs with the same query, the agent searched for different keywords and returned a larger list. But being able to translate a natural language query into a set of database queries and find relevant results is impressive!

Spring AI’s built-in support for developing agents

Now that you understand what an agent loop is, what it does, and how to handle tool executions, let’s look at Spring AI’s built-in support for managing its own agent loop and tool execution. Here is our updated ProductSearchAgent code:

package com.infoworld.springagentdemo.ai.agent;

import java.util.ArrayList;
import java.util.List;

import com.infoworld.springagentdemo.ai.tools.ProductSearchTools;
import com.infoworld.springagentdemo.model.Product;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.stereotype.Component;

@Component
public class ProductSearchAgent {

    private final ChatClient chatClient;
    private final ProductSearchTools productSearchTools;

    public ProductSearchAgent(ChatClient.Builder chatClientBuilder, ProductSearchTools productSearchTools) {
        this.chatClient =  chatClientBuilder.build();
        this.productSearchTools = productSearchTools;
    }

    public List<Product> run(String userRequest) {

        Prompt prompt = buildPrompt(userRequest);

        AgentResponse response = chatClient
                .prompt(prompt)
                .toolCallbacks(
                        MethodToolCallbackProvider.builder().toolObjects(productSearchTools).build()
                )
                .call()
                .entity(AgentResponse.class);

        System.out.println(response.answer());
        return response.products();
    }

    private Prompt buildPrompt(String userRequest) {

        List<Message> messages = new ArrayList<>();

        // 1. System message: defines the agent
        messages.add(new SystemMessage("""
You are a product search agent.

Your responsibility is to help users find relevant products using the available tools.

Guidelines:
- Use the provided tools whenever product data is required.
- You may call tools multiple times to refine or expand the search.
- If the request is vague, make reasonable assumptions and attempt a search.
- Do not ask follow-up questions.
- Continue using tools until you are confident you have the best possible results.

If the user asks about products in a certain price range, first search for the products and then filter
the results based on the price. Each product is defined with a price.

When you have completed the search process, return a structured JSON response in this format:

{
  "answer": "...",
  "products": [...]
}

Do not return conversational text.
Return only valid JSON.
"""));

        // Add the user's request
        messages.add(new UserMessage(userRequest));

        return new Prompt(messages);
    }
}

As I mentioned earlier, the ProductSearchToolssearchProducts() method is annotated with the @Tool annotation. This annotation has special meaning for Spring AI if we add a toolCallbacks() method call to our LLM call. In this case, we autowire the ProductSearchTools into our constructor and then invoke the toolCallbacks() method in our LLM call, passing it a list of all the classes containing tools we want to give the LLM access to in a MethodToolCallbackProvider.builder().toolObjects() call. Spring AI will see this list of tools and do a few things:

  1. Introspect all methods annotated with the @Tool annotation in the provided classes.
  2. Build the tool specification and pass it to the LLM for us, including the description of the tool and the method signature, which means that we no longer need to explicitly define the tool specification in our SystemPrompt.
  3. Because it has access to call the tools, the ChatClient’s call() method will run in its own agent loop and invoke the tools it needs for us.

Therefore, the response we receive will be the final response from the LLM with our list of products, so we do not need to build an agent loop ourselves. We build our prompt with a system prompt (which again does not have the tool specification) and the user’s request. We then make a single call to the call() method, which performs all the actions it needs to arrive at a conclusion.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Stay Connected

0FansLike
0FollowersFollow
0FollowersFollow
0SubscribersSubscribe
- Advertisement -spot_img

CATEGORIES & TAGS

- Advertisement -spot_img

LATEST COMMENTS

Most Popular

WhatsApp