Building Effective Agents with Spring AI
Create intelligent AI agents that can reason, plan, use tools, and execute complex multi-step tasks autonomously
1What are AI Agents?
An AI Agent is an autonomous system that uses a Large Language Model (LLM) as its "brain" to reason about problems, make decisions, and take actions. Unlike simple chatbots that just respond to queries, agents can:
Reason & Plan
Break down complex tasks into smaller steps and determine the best approach
Use Tools
Call external APIs, databases, and services to gather information or perform actions
Execute Autonomously
Carry out multi-step workflows without constant human intervention
Achieve Goals
Work towards specific objectives, adapting their approach as needed
Agent vs Chatbot
A chatbot responds to individual messages. An agent takes a goal like "Book me a flight to Paris next week" and autonomously searches flights, compares prices, selects options, and completes the booking.
2Agent Architecture in Spring AI
Agent Loop Architecture
User Goal
"Analyze sales data"
LLM Reasoning
Plan & decide action
Tool Execution
Call APIs/functions
Result/Loop
Complete or continue
Core Components
1. ChatClient
The main interface to interact with the LLM. Handles prompts, responses, and tool calling.
2. Tools (Functions)
Java methods annotated with @Tool that the agent can invoke. These extend the agent's capabilities.
3. Memory/Chat History
Maintains conversation context so the agent remembers previous interactions and tool results.
4. System Prompt
Defines the agent's persona, capabilities, and behavioral guidelines.
3Building Your First Agent
Step 1: Define Tools
Create tools that your agent can use. Each tool is a method annotated with @Tool.
importorg.springframework.ai.tool.annotation.Tool;importorg.springframework.ai.tool.annotation.ToolParam;importorg.springframework.stereotype.Component;@ComponentpublicclassWeatherTools{@Tool(description ="Get the current weather for a specific city")publicStringgetCurrentWeather(@ToolParam(description ="The city name, e.g., 'London'")String city){// In production, call a real weather APIreturn"The weather in "+ city +" is 22°C and sunny.";}@Tool(description ="Get the weather forecast for the next N days")publicStringgetWeatherForecast(@ToolParam(description ="The city name")String city,@ToolParam(description ="Number of days (1-7)")int days){return"Weather forecast for "+ city +" for the next "+ days +" days: Mostly sunny with temperatures between 18-25°C.";}}Step 2: Create the Agent Service
importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;importorg.springframework.ai.chat.memory.InMemoryChatMemory;importorg.springframework.stereotype.Service;@ServicepublicclassWeatherAgentService{privatefinalChatClient chatClient;publicWeatherAgentService(ChatClient.Builder chatClientBuilder,WeatherTools weatherTools){this.chatClient = chatClientBuilder
.defaultSystem("""
You are a helpful weather assistant agent.
You can check current weather and forecasts for any city.
Always be friendly and provide helpful weather advice.
If asked about activities, suggest based on the weather.
""").defaultTools(weatherTools)// Register tools.defaultAdvisors(newMessageChatMemoryAdvisor(newInMemoryChatMemory())).build();}publicStringchat(String userMessage){return chatClient.prompt().user(userMessage).call().content();}}Step 3: Agent Controller
@RestController@RequestMapping("/api/agent")@RequiredArgsConstructorpublicclassWeatherAgentController{privatefinalWeatherAgentService agentService;@PostMapping("/chat")publicResponseEntity<AgentResponse>chat(@RequestBodyChatRequest request){String response = agentService.chat(request.message());returnResponseEntity.ok(newAgentResponse(response));}}recordChatRequest(String message){}recordAgentResponse(String response){}How It Works
When you ask "What's the weather in Paris?", the LLM recognizes it needs weather data, calls the getCurrentWeather tool, receives the result, and formulates a natural response.
4Advanced Agent Patterns
Multi-Tool Agent Example
Build agents with multiple specialized tools for complex tasks:
@ComponentpublicclassTravelAgentTools{@Tool(description ="Search for available flights between two cities")publicStringsearchFlights(@ToolParam(description ="Departure city")String from,@ToolParam(description ="Destination city")Stringto,@ToolParam(description ="Date in YYYY-MM-DD format")String date){return"Found 5 flights from "+ from +" to "+to+" on "+ date +". Prices range from $250 to $450.";}@Tool(description ="Search for hotels in a city")publicStringsearchHotels(@ToolParam(description ="City name")String city,@ToolParam(description ="Check-in date")String checkIn,@ToolParam(description ="Check-out date")String checkOut){return"Found 10 hotels in "+ city +" from "+ checkIn +" to "+ checkOut +". Prices from $80/night to $300/night.";}@Tool(description ="Book a flight with the given details")publicStringbookFlight(@ToolParam(description ="Flight ID")String flightId,@ToolParam(description ="Passenger name")String passengerName){return"Flight "+ flightId +" booked for "+ passengerName +". Confirmation: TRV-"+System.currentTimeMillis();}@Tool(description ="Get current exchange rate between currencies")publicStringgetExchangeRate(@ToolParam(description ="Source currency code")String from,@ToolParam(description ="Target currency code")Stringto){return"1 "+ from +" = 0.85 "+to;}}ReAct Pattern (Reasoning + Acting)
Implement the ReAct pattern where the agent explicitly reasons before taking actions:
@ServicepublicclassReActAgentService{privatefinalChatClient chatClient;publicReActAgentService(ChatClient.Builder builder,TravelAgentTools tools){this.chatClient = builder
.defaultSystem("""
You are an intelligent travel planning agent using the ReAct pattern.
For each user request, follow this process:
1. THOUGHT: Analyze what the user needs and plan your approach
2. ACTION: Use the appropriate tool to gather information
3. OBSERVATION: Review the tool result
4. Repeat steps 1-3 until you have enough information
5. ANSWER: Provide a comprehensive response to the user
Always explain your reasoning before taking actions.
""").defaultTools(tools).defaultAdvisors(newMessageChatMemoryAdvisor(newInMemoryChatMemory())).build();}publicStringplanTrip(String userRequest){return chatClient.prompt().user(userRequest).call().content();}}Example Interaction
User: "Plan a trip from NYC to London next month"
Agent: THOUGHT: I need to search for flights first, then find hotels...
ACTION: searchFlights("NYC", "London", "2024-02-15")
5Best Practices
Clear Tool Descriptions
Write detailed descriptions for each tool so the LLM knows when and how to use them.
Limit Tool Count
Keep the number of tools manageable (5-10). Too many tools can confuse the model.
Handle Errors Gracefully
Tools should return helpful error messages that the agent can interpret and act upon.
Set Execution Limits
Limit the number of tool calls per request to prevent infinite loops and runaway costs.
Security Considerations
- Validate all tool inputs before execution
- Implement rate limiting to prevent abuse
- Use proper authentication for sensitive operations
- Log all agent actions for auditing and debugging
What You've Learned
Agent Fundamentals
What makes agents different from chatbots
Architecture
The agent loop and core components
Tool Development
Creating tools with @Tool annotation
Agent Service
Building agents with ChatClient
ReAct Pattern
Explicit reasoning before actions
Best Practices
Security and production considerations