GitHub · LinkedIn · About · YouTube
Last updated by Kindson Munonye — July 1, 2026
📚 Tutorial hubs:
AI Developer Tutorials ·
Spring Boot ·
Angular ·
CRUD + REST guide
Source code: munonye-ai-chat-spring-angular on GitHub
Estimated reading time: 12–15 minutes · Last updated: July 1, 2026
Agentic AI is the defining backend trend of 2026. If you already ship Spring Boot REST APIs and Angular frontends, the next feature users expect is an AI assistant that acts on your data — look up orders, summarize tickets, recommend products — using tool calling, not prompt hacks.
This hands-on guide is part of the AI Developer Tutorials hub. You will build a tool-calling AI agent with Spring AI, step by step, in about 60 minutes.
Prerequisites: Spring AI first REST endpoint (M7-A), Java 17+, OpenAI API key, basic Spring Boot.
Table of contents

What is agentic AI? {#what-is-agentic-ai}
A Spring AI agent combines:
| Component | Role |
|---|---|
| LLM | Plans steps and interprets user intent |
| @Tool methods | Your Java functions the model can invoke |
| ChatClient | Orchestrates the tool-calling loop |
Unlike a basic chat endpoint (M7-A tutorial), an agent may execute multiple tool calls before answering — e.g. “What’s the status of order 1042 and when will it ship?” → getOrder(1042) → getShippingEstimate(1042) → natural language summary.

Architecture overview {#architecture}
User prompt
│
▼
POST /api/agent
│
▼
ChatClient (Spring AI)
├──► LLM plans tool use
├──► @Tool getOrderStatus(orderId)
├──► @Tool listLowStockProducts()
└──► Final answer to user
Stack alignment: This agent sits beside your existing FriendsAPI-style CRUD backend from the complete CRUD guide. Add AI without rewriting your domain layer.

Step 1 — Define @Tool methods {#step-1-tools}
@Component
public class StoreTools {
private final OrderRepository orders;
private final ProductRepository products;
public StoreTools(OrderRepository orders, ProductRepository products) {
this.orders = orders;
this.products = products;
}
@Tool(description = "Get order status and customer name by order ID")
public OrderSummary getOrderStatus(String orderId) {
return orders.findById(orderId)
.map(o -> new OrderSummary(o.getId(), o.getStatus(), o.getCustomerName()))
.orElse(new OrderSummary(orderId, "NOT_FOUND", "Unknown"));
}
@Tool(description = "List product names with stock below the threshold")
public List<String> listLowStockProducts(int threshold) {
return products.findByStockLessThan(threshold)
.stream()
.map(Product::getName)
.toList();
}
public record OrderSummary(String orderId, String status, String customer) {}
}
Tools must have clear descriptions — the LLM chooses based on name + description text.
Step 2 — Configure the agent ChatClient {#step-2-agent}
@Configuration
public class AgentConfig {
@Bean
ChatClient agentChatClient(ChatClient.Builder builder, StoreTools storeTools) {
return builder
.defaultSystem("""
You are a store operations assistant.
Use tools for factual data. Never invent order IDs or stock levels.
If a tool returns NOT_FOUND, say so clearly.
""")
.defaultTools(storeTools)
.build();
}
}
Dependencies (if not already added):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.options.model=gpt-4o-mini
For local models, swap to Ollama — Ollama + Spring Boot tutorial.
Step 3 — REST API for the agent {#step-3-api}
@RestController
@RequestMapping("/api/agent")
@CrossOrigin(origins = "http://localhost:4200")
public class AgentController {
private final ChatClient agentChatClient;
public AgentController(@Qualifier("agentChatClient") ChatClient agentChatClient) {
this.agentChatClient = agentChatClient;
}
@PostMapping
public AgentResponse run(@RequestBody AgentRequest req) {
String answer = agentChatClient.prompt()
.user(req.message())
.call()
.content();
return new AgentResponse(answer);
}
public record AgentRequest(String message) {}
public record AgentResponse(String answer) {}
}
Step 4 — Test multi-step prompts {#step-4-test}
export OPENAI_API_KEY=sk-...
./mvnw spring-boot:run
curl -s -X POST http://localhost:8080/api/agent \
-H "Content-Type: application/json" \
-d '{"message":"Check order 1042 and list products with stock under 5"}' | jq
The model should call both tools and synthesize one answer.
| Test prompt | Expected tool behavior |
|---|---|
| “Status of order 9999” | getOrderStatus → NOT_FOUND message |
| “What’s low on stock?” | Ask for threshold or assume default |
| “Compare order 1042 and 1043” | Two getOrderStatus calls |
Step 5 — Angular UI (optional) {#step-5-angular-ui}
Point your Angular 19 chat UI at /api/agent instead of /api/chat. Same HttpClient service — swap the URL. For structured UI cards, parse tool results via function calling from Angular.
Use Angular signals to track agent steps in the UI (optional advanced).
Production checklist {#production}
| Item | Resource |
|---|---|
| API keys server-side only | Secure AI tutorial |
| Ground answers with RAG | RAG Spring Boot guide |
| Event-driven long workflows | Axon + Spring AI agents |
| External tool protocol | MCP developer tutorial |
| Full learning path | Full-stack AI starter kit |
FAQ {#faq}
How is this different from LangChain4j?
Spring AI is the native Spring integration — same DI, config, and observability as your existing Spring Boot tutorials.
Can agents call external HTTP APIs?
Yes — implement @Tool methods that wrap RestClient or your existing services.
Source code: munonye-ai-chat-spring-angular on GitHub
- ← Start: Spring AI first REST endpoint
- → Advanced: RAG with Spring Boot
- → Hub: All AI Developer Tutorials
Related:
AI Developer Tutorials hub ·
Angular CRUD Part 1 ·
Spring AI overview
