Java SpringAI

Agentic AI with Spring AI — Build Autonomous Agents That Plan and Act

Agentic AI with Spring AI — Build Autonomous Agents That Plan and Act

An AI agent is a program that autonomously decides what actions to take in order to achieve a goal. Unlike a simple chatbot that responds to each message in isolation, an agent uses an LLM as its reasoning engine to plan multiple steps, call tools, observe results, and adapt until the task is complete. Spring AI enables this with the tool-calling loop built into ChatClient.

The Agent Loop — ReAct Pattern

Goal: "Research Spring AI, check if our docs are up to date, and write a summary"

Loop iteration 1:
  Think: "I need to search the web for latest Spring AI news"
  Act:   searchWeb("Spring AI latest features 2025")
  Observe: "Spring AI 1.0 released with MCP support, new vector stores..."

Loop iteration 2:
  Think: "Now check our internal docs for Spring AI coverage"
  Act:   searchKnowledgeBase("Spring AI documentation")
  Observe: "Found 3 docs from 2024, no coverage of MCP or 1.0 features"

Loop iteration 3:
  Think: "I have enough information to write the summary"
  Act:   (no tool call — generate final response)
  Answer: "Summary: Spring AI 1.0 GA was released... Our docs are missing MCP..."

This loop runs automatically inside ChatClient until the LLM stops calling tools.

Building a Research Agent

@Component
public class ResearchTools {

    private final WebSearchService webSearch;
    private final VectorStore      knowledgeBase;

    public ResearchTools(WebSearchService webSearch, VectorStore knowledgeBase) {
        this.webSearch     = webSearch;
        this.knowledgeBase = knowledgeBase;
    }

    @Tool(description = "Search the web for current information about a topic. Returns top 3 results with URLs.")
    public String searchWeb(String query) {
        // In production: call SerpAPI, Google Custom Search, or Brave Search
        return """
               Result 1: Spring AI 1.0 GA Released — spring.io/blog (2025-06)
               Spring AI 1.0 introduces MCP support, new vector store backends...

               Result 2: Spring AI with GPT-4o — baeldung.com
               Tutorial on using ChatClient with GPT-4o for structured output...

               Result 3: Spring AI GitHub — github.com/spring-projects/spring-ai
               1,000+ commits, active development, 12k stars...
               """;
    }

    @Tool(description = "Search internal knowledge base for existing documentation on a topic.")
    public String searchKnowledgeBase(String query) {
        List<Document> results = knowledgeBase.similaritySearch(
                SearchRequest.query(query).withTopK(3));
        if (results.isEmpty()) return "No internal documentation found for: " + query;
        return results.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n---\n"));
    }

    @Tool(description = "Calculate estimated reading time for text content in minutes.")
    public String calculateReadingTime(String text) {
        int wordCount    = text.split("\\s+").length;
        int minutes      = wordCount / 200;   // avg 200 WPM
        return "Estimated reading time: " + Math.max(1, minutes) + " minutes (" + wordCount + " words)";
    }
}
@Service
public class ResearchAgentService {

    private final ChatClient chatClient;

    public ResearchAgentService(ChatClient.Builder builder, ResearchTools tools) {
        this.chatClient = builder
                .defaultSystem("""
                    You are a research agent. To complete tasks:
                    1. Break the goal into steps
                    2. Use available tools to gather information
                    3. Synthesize findings into a clear, structured report
                    Always verify information from multiple sources before concluding.
                    """)
                .defaultTools(tools)
                .build();
    }

    public String research(String goal) {
        return chatClient.prompt()
                .user(goal)
                .call()
                .content();
        // ChatClient automatically handles the tool-calling loop
        // until the LLM produces a final answer without calling more tools
    }
}

Controller and Demo

@RestController
@RequestMapping("/agent")
public class AgentController {

    private final ResearchAgentService agent;

    public AgentController(ResearchAgentService agent) {
        this.agent = agent;
    }

    @PostMapping("/research")
    public String research(@RequestBody String goal) {
        return agent.research(goal);
    }
}

Output

POST /agent/research
Body: Research Spring AI 1.0 and identify what our internal docs are missing

[Agent internally calls: searchWeb, searchKnowledgeBase, calculateReadingTime]

Response:
## Spring AI Research Summary

**Latest Spring AI Status (from web search):**
Spring AI 1.0 GA was released in June 2025 with:
- MCP (Model Context Protocol) server and client support
- New vector store backends: MongoDB Atlas, Cassandra
- Improved streaming API with backpressure
- @Tool annotation replacing FunctionCallback

**Internal Documentation Gaps:**
Our knowledge base has 3 Spring AI docs from 2024 covering basic ChatClient usage.
Missing coverage:
1. MCP server and client implementation
2. New vector store options (MongoDB Atlas, Cassandra)
3. Spring AI 1.0 migration guide from 0.x
4. Agent patterns and multi-step reasoning

**Recommendation:**
Priority: Write MCP tutorial first (highest developer interest based on web search volume).
Estimated content: ~2,000 words, 10 min read.

Controlling Agent Behavior

// Limit maximum tool calls to prevent infinite loops
import org.springframework.ai.chat.client.advisor.ReActAdvisor;

this.chatClient = builder
        .defaultSystem("You are a research agent...")
        .defaultTools(tools)
        .defaultAdvisors(ReActAdvisor.builder()
                .maxIterations(10)   // stop after 10 tool calls max
                .build())
        .build();

Agent with Confirmation Step

@Component
public class SafeActionTools {

    @Tool(description = "Send an email to a user. Requires email address and message body.")
    public String sendEmail(String recipientEmail, String subject, String body) {
        // In a real agent, you might want human-in-the-loop confirmation
        // before actually sending
        System.out.println("SEND EMAIL:");
        System.out.println("To     : " + recipientEmail);
        System.out.println("Subject: " + subject);
        System.out.println("Body   : " + body);
        return "Email queued for sending to " + recipientEmail;
    }

    @Tool(description = "Create a calendar event. Returns event ID.")
    public String createCalendarEvent(String title, String date, String time, String attendees) {
        return "Event created: ID=EVT-" + System.currentTimeMillis() +
               " Title=" + title + " Date=" + date + " Time=" + time;
    }
}

Key Points

  • The tool-calling loop runs automatically inside ChatClient — the LLM calls tools, Spring AI executes them, results go back to the LLM, repeat until no more tool calls
  • Use maxIterations to prevent runaway agents — a default of 5–10 iterations is safe for most tasks
  • Design tools to be idempotent where possible — agents may call a tool multiple times if they don't get a satisfying result
  • Add human-in-the-loop confirmation before irreversible actions (send email, delete records, deploy code)
  • The ReAct pattern (Reason, Act) is the foundation of most production agent systems — Spring AI implements it natively
Topics: Java SpringAI
← Newer Post Older Post →