[
{
"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, filtered the products based on price, and returned me a list of two options. LLM is not deterministic, so your results may differ from mine. For example, in other runs using the same query, the agent searched for different keywords and returned a larger list. But it’s great to be able to translate a natural language query into a set of database queries and find relevant results.
Spring AI built-in agent development support
Now that you understand what an agent loop is, how it works, and how it handles tool executions, let’s take a look at Spring AI’s built-in support for managing your own agent loops and tool executions. This is our updated version 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 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 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, ProductSearchTools‘ searchProducts() The method is annotated with the following @Tool Notes. This annotation has special meaning to Spring AI. toolCallbacks() Add method calls to LLM calls. in this case, ProductSearchTools After including it in the constructor, toolCallbacks() Pass the method in the LLM call and pass LLM a list of all the classes that contain the tools you want to give access to. MethodToolCallbackProvider.builder().toolObjects() phone. Spring AI looks at this list of tools and does a few things.
- Introspect all annotated methods.
@ToolAnnotations for the provided class. - Create a tool specification and pass it to LLM, including the tool description and method signature. This means that you no longer need to explicitly define tool specifications.
SystemPrompt. - Because you have access to call the tool,
ChatClient‘scall()The method runs in its own agent loop and calls the necessary tools.
Therefore, you don’t need to build an agent loop yourself because the response you receive will be the final response from LLM containing the product list. Builds prompts using system prompts (again, no tool spec) and user requests. next, call() method. Perform all necessary actions to reach your conclusion.
