# Spring AI Spring AI is a comprehensive framework for building AI-powered applications using Spring Boot. It provides a Spring-friendly API and abstractions that apply familiar Spring ecosystem design principles (portability, modular design, POJO-based development) to AI domain challenges. The project addresses the fundamental challenge of AI integration by connecting enterprise Data and APIs with AI Models through a unified, provider-agnostic interface. The framework supports all major AI model providers including OpenAI, Anthropic, Azure OpenAI, Google Vertex AI, Amazon Bedrock, Mistral AI, and Ollama. It offers support for various AI capabilities including chat completion, embeddings, text-to-image generation, audio transcription, and text-to-speech. Spring AI also provides integration with vector databases (PGVector, Pinecone, Milvus, Chroma, Weaviate, etc.), implements Retrieval Augmented Generation (RAG) patterns, and supports the Model Context Protocol (MCP) for tool calling and function execution. ## ChatClient API - Fluent Chat Interface The ChatClient provides a fluent API for communicating with AI chat models, similar to Spring's WebClient and RestClient patterns. It supports synchronous calls, streaming responses, system/user prompts, advisors for cross-cutting concerns, and structured output conversion. ```java import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; // Create ChatClient from a ChatModel OpenAiApi openAiApi = OpenAiApi.builder() .apiKey(System.getenv("OPENAI_API_KEY")) .build(); ChatModel chatModel = OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(OpenAiChatOptions.builder() .model("gpt-4o") .temperature(0.7) .build()) .build(); ChatClient chatClient = ChatClient.create(chatModel); // Simple call with user prompt String response = chatClient.prompt() .user("What is the capital of France?") .call() .content(); // Returns: "The capital of France is Paris." // Using system and user messages with parameters String story = chatClient.prompt() .system("You are a creative storyteller who writes {genre} stories.") .user("Write a short story about {subject}") .system(s -> s.param("genre", "science fiction")) .user(u -> u.param("subject", "a robot learning to paint")) .call() .content(); // Streaming response chatClient.prompt() .user("Explain quantum computing in simple terms") .stream() .content() .subscribe(chunk -> System.out.print(chunk)); // Builder pattern with default configuration ChatClient configuredClient = ChatClient.builder(chatModel) .defaultSystem("You are a helpful assistant specialized in Java programming.") .defaultOptions(OpenAiChatOptions.builder() .model("gpt-4o") .temperature(0.3) .build()) .build(); String javaHelp = configuredClient.prompt() .user("How do I create a thread-safe singleton in Java?") .call() .content(); ``` ## ChatModel API - Direct Model Interaction The ChatModel interface provides direct access to AI models for chat completion. It supports both synchronous calls and streaming responses, with model-specific options available through implementations like OpenAiChatModel, AnthropicChatModel, etc. ```java import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.List; // Initialize OpenAI ChatModel OpenAiApi openAiApi = OpenAiApi.builder() .apiKey(System.getenv("OPENAI_API_KEY")) .build(); ChatModel chatModel = OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(OpenAiChatOptions.builder() .model("gpt-4o") .temperature(0.7) .maxTokens(1000) .build()) .build(); // Simple string call String response = chatModel.call("What is Spring Boot?"); // Using Prompt with messages Prompt prompt = new Prompt(List.of( new SystemMessage("You are an expert Java developer."), new UserMessage("Explain dependency injection in Spring.") )); ChatResponse chatResponse = chatModel.call(prompt); Generation generation = chatResponse.getResult(); String content = generation.getOutput().getText(); // Access metadata String model = chatResponse.getMetadata().getModel(); long promptTokens = chatResponse.getMetadata().getUsage().getPromptTokens(); long completionTokens = chatResponse.getMetadata().getUsage().getCompletionTokens(); // Streaming response Flux streamResponse = chatModel.stream(prompt); streamResponse.subscribe(chunk -> { if (chunk.getResult() != null) { System.out.print(chunk.getResult().getOutput().getText()); } }); // Runtime options override ChatResponse customResponse = chatModel.call(new Prompt( "Write a haiku about programming", OpenAiChatOptions.builder() .model("gpt-4o-mini") .temperature(0.9) .build() )); ``` ## Structured Output - Converting AI Responses to POJOs The BeanOutputConverter transforms AI model output into strongly-typed Java objects using JSON schema generation. It validates responses against the schema and handles the conversion automatically. ```java import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.core.ParameterizedTypeReference; import java.util.List; // Define your output types record Person(String name, int age, String occupation) {} record MovieReview( String title, int rating, String summary, List pros, List cons ) {} record BookRecommendation( String title, String author, String genre, String description ) {} // Using ChatClient with entity() for automatic conversion ChatClient chatClient = ChatClient.create(chatModel); // Single object conversion Person person = chatClient.prompt() .user("Generate a fictional person profile with name, age, and occupation") .call() .entity(Person.class); // Returns: Person[name=John Smith, age=34, occupation=Software Engineer] // Complex object with nested collections MovieReview review = chatClient.prompt() .user("Write a review for the movie 'Inception' with rating (1-10), summary, pros and cons") .call() .entity(MovieReview.class); // List of objects using ParameterizedTypeReference List books = chatClient.prompt() .user("Recommend 3 science fiction books") .call() .entity(new ParameterizedTypeReference>() {}); // Using BeanOutputConverter directly for custom configuration BeanOutputConverter converter = new BeanOutputConverter<>(Person.class); String format = converter.getFormat(); // Returns JSON schema instruction String rawResponse = chatClient.prompt() .user("Generate a fictional person. " + format) .call() .content(); Person converted = converter.convert(rawResponse); // ResponseEntity for accessing both response metadata and entity var responseEntity = chatClient.prompt() .user("Generate a person profile") .call() .responseEntity(Person.class); Person entity = responseEntity.entity(); var chatResponse = responseEntity.response(); String modelUsed = chatResponse.getMetadata().getModel(); ``` ## EmbeddingModel API - Text to Vector Conversion The EmbeddingModel interface converts text into vector representations for semantic search and similarity comparisons. It supports single text, batch processing, and document embedding with configurable batching strategies. ```java import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.EmbeddingRequest; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.ai.embedding.EmbeddingOptions; import org.springframework.ai.document.Document; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.OpenAiEmbeddingOptions; import org.springframework.ai.openai.api.OpenAiApi; import java.util.List; import java.util.Map; // Initialize OpenAI Embedding Model OpenAiApi openAiApi = OpenAiApi.builder() .apiKey(System.getenv("OPENAI_API_KEY")) .build(); EmbeddingModel embeddingModel = new OpenAiEmbeddingModel( openAiApi, OpenAiEmbeddingOptions.builder() .model("text-embedding-3-small") .build() ); // Embed a single text float[] embedding = embeddingModel.embed("Spring AI makes AI integration easy"); System.out.println("Embedding dimensions: " + embedding.length); // Output: Embedding dimensions: 1536 // Embed multiple texts (batch) List texts = List.of( "Machine learning basics", "Deep learning with neural networks", "Natural language processing techniques" ); List embeddings = embeddingModel.embed(texts); // Get full response with metadata EmbeddingResponse response = embeddingModel.embedForResponse(texts); int totalTokens = response.getMetadata().getUsage().getTotalTokens(); // Embed Documents (for vector store integration) List documents = List.of( new Document("Spring Boot simplifies Java development", Map.of("topic", "spring", "type", "intro")), new Document("Spring AI provides AI abstractions", Map.of("topic", "ai", "type", "overview")) ); // Using EmbeddingRequest for fine-grained control EmbeddingRequest request = new EmbeddingRequest( List.of("Custom embedding request"), EmbeddingOptions.builder().build() ); EmbeddingResponse customResponse = embeddingModel.call(request); float[] vector = customResponse.getResults().get(0).getOutput(); // Get embedding dimensions int dimensions = embeddingModel.dimensions(); System.out.println("Model dimensions: " + dimensions); ``` ## VectorStore API - Semantic Search and Document Storage The VectorStore interface manages document storage and semantic similarity search in vector databases. It supports adding documents, similarity search with metadata filtering, and deletion operations. ```java import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.pgvector.PgVectorStore; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; import java.util.Map; // Initialize PgVector Store (requires PostgreSQL with pgvector extension) VectorStore vectorStore = PgVectorStore.builder(jdbcTemplate, embeddingModel) .dimensions(1536) .distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE) .build(); // Create and add documents with metadata List documents = List.of( new Document("Spring Boot auto-configuration simplifies setup", Map.of("category", "spring-boot", "difficulty", "beginner", "year", 2024)), new Document("Spring AI integrates with OpenAI, Anthropic, and more", Map.of("category", "spring-ai", "difficulty", "intermediate", "year", 2024)), new Document("Vector databases store embeddings for semantic search", Map.of("category", "vector-db", "difficulty", "advanced", "year", 2023)) ); vectorStore.add(documents); // Simple similarity search List results = vectorStore.similaritySearch("How does Spring Boot work?"); results.forEach(doc -> System.out.println(doc.getText())); // Search with SearchRequest builder List filtered = vectorStore.similaritySearch( SearchRequest.builder() .query("AI integration frameworks") .topK(5) .similarityThreshold(0.7) .build() ); // Search with metadata filter expression (SQL-like syntax) List filteredByMeta = vectorStore.similaritySearch( SearchRequest.builder() .query("Spring framework features") .topK(10) .filterExpression("category == 'spring-boot' && year >= 2024") .build() ); // Complex filter with OR and IN operators List complexFilter = vectorStore.similaritySearch( SearchRequest.builder() .query("Programming tutorials") .filterExpression("difficulty IN ['beginner', 'intermediate'] && year == 2024") .topK(5) .build() ); // Delete documents by ID vectorStore.delete(List.of("doc-id-1", "doc-id-2")); // Delete by filter expression vectorStore.delete("category == 'outdated' && year < 2020"); ``` ## Advisors - Cross-Cutting Concerns for Chat Advisors implement the interceptor pattern for chat operations, enabling features like conversation memory, RAG, logging, and content safety. They can modify requests before and responses after model calls. ```java import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.ai.chat.client.advisor.SafeGuardAdvisor; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor; import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever; import org.springframework.ai.vectorstore.VectorStore; import java.util.List; // Initialize chat memory ChatMemory chatMemory = new InMemoryChatMemory(); // Create ChatClient with multiple advisors ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors( // Logging advisor for debugging new SimpleLoggerAdvisor(), // Memory advisor for conversation history MessageChatMemoryAdvisor.builder(chatMemory) .conversationId("user-123-session") .build(), // SafeGuard advisor to filter sensitive content SafeGuardAdvisor.builder() .sensitiveWords(List.of("password", "secret", "api-key")) .build() ) .build(); // Multi-turn conversation with memory String response1 = chatClient.prompt() .user("My name is Alice and I'm learning Spring Boot") .call() .content(); String response2 = chatClient.prompt() .user("What's my name and what am I learning?") .call() .content(); // Model remembers: "Your name is Alice and you're learning Spring Boot" // RAG Advisor for retrieval-augmented generation RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder() .documentRetriever(VectorStoreDocumentRetriever.builder() .vectorStore(vectorStore) .similarityThreshold(0.7) .topK(5) .build()) .build(); ChatClient ragClient = ChatClient.builder(chatModel) .defaultAdvisors(ragAdvisor) .build(); // Query is automatically augmented with relevant documents String ragResponse = ragClient.prompt() .user("How do I configure Spring Security?") .call() .content(); // Per-request advisor configuration String customResponse = chatClient.prompt() .advisors(advisor -> advisor .param("chat_memory_conversation_id", "custom-session-456")) .user("Start a new conversation about Docker") .call() .content(); ``` ## Tool Calling - Function Execution by AI Models Tools enable AI models to execute functions to access real-time data or perform actions. Spring AI supports both annotation-based tool definitions and programmatic ToolCallback implementations. ```java import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.stereotype.Component; // Annotation-based tool definition @Component public class WeatherTools { @Tool(name = "get_weather", description = "Get current weather for a city") public String getWeather( @ToolParam(description = "City name") String city, @ToolParam(description = "Temperature unit: celsius or fahrenheit") String unit ) { // Simulated weather API call return String.format("Weather in %s: 22 degrees %s, partly cloudy", city, unit.equals("celsius") ? "C" : "F"); } @Tool(name = "get_forecast", description = "Get 5-day weather forecast") public String getForecast(String city) { return String.format("5-day forecast for %s: Mon 20C, Tue 22C, Wed 19C, Thu 21C, Fri 23C", city); } } // Using tools with ChatClient @Autowired private WeatherTools weatherTools; ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(weatherTools) .build(); String response = chatClient.prompt() .user("What's the weather like in Paris today?") .call() .content(); // Model calls get_weather("Paris", "celsius") and returns formatted response // Programmatic ToolCallback implementation ToolCallback calculatorTool = new ToolCallback() { @Override public ToolDefinition getToolDefinition() { return ToolDefinition.builder() .name("calculator") .description("Perform mathematical calculations") .inputSchema(""" { "type": "object", "properties": { "expression": { "type": "string", "description": "Mathematical expression to evaluate" } }, "required": ["expression"] } """) .build(); } @Override public String call(String toolInput) { // Parse JSON input and evaluate expression var json = new ObjectMapper().readTree(toolInput); String expression = json.get("expression").asText(); // Evaluate and return result return "Result: " + evaluateExpression(expression); } }; // Register tools at runtime String mathResponse = chatClient.prompt() .tools(calculatorTool) .user("What is 15 * 23 + 47?") .call() .content(); // Multiple tools in a single request String complexResponse = chatClient.prompt() .tools(weatherTools, calculatorTool) .user("If it's 22C in Paris, what's that in Fahrenheit? Also, what's the forecast?") .call() .content(); ``` ## MCP Integration - Model Context Protocol Spring AI supports the Model Context Protocol (MCP) for integrating external tools and services. MCP enables standardized communication between AI applications and tool providers. ```java import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider; import org.springframework.ai.tool.ToolCallback; import io.modelcontextprotocol.client.McpSyncClient; import io.modelcontextprotocol.client.McpAsyncClient; // Configure MCP client for tool discovery McpSyncClient mcpClient = McpSyncClient.builder() .transport(new StdioClientTransport( "npx", List.of("-y", "@modelcontextprotocol/server-filesystem"))) .build(); mcpClient.initialize(); // Create tool callback provider from MCP client SyncMcpToolCallbackProvider mcpProvider = SyncMcpToolCallbackProvider.builder() .mcpClients(mcpClient) .toolFilter((connection, tool) -> tool.name().startsWith("file_")) .build(); // Get discovered tools ToolCallback[] mcpTools = mcpProvider.getToolCallbacks(); // Use MCP tools with ChatClient ChatClient chatClient = ChatClient.builder(chatModel) .defaultToolCallbacks(mcpTools) .build(); String response = chatClient.prompt() .user("List the files in the current directory") .call() .content(); // Async MCP client for non-blocking operations McpAsyncClient asyncMcpClient = McpAsyncClient.builder() .transport(webFluxTransport) .build(); AsyncMcpToolCallbackProvider asyncProvider = AsyncMcpToolCallbackProvider.builder() .mcpClients(asyncMcpClient) .build(); // MCP Server annotations for creating tool servers @Component public class FileSystemMcpTools { @McpTool(name = "read_file", description = "Read contents of a file") public String readFile(@McpToolParam(description = "File path") String path) { return Files.readString(Path.of(path)); } @McpTool(name = "list_directory", description = "List directory contents") public List listDirectory(String path) { return Files.list(Path.of(path)) .map(Path::getFileName) .map(Path::toString) .toList(); } } ``` ## Document Readers - ETL for AI Applications Spring AI provides document readers for loading various file formats into Documents for embedding and RAG workflows. Supported formats include PDF, Markdown, HTML, and general file types via Apache Tika. ```java import org.springframework.ai.reader.pdf.PagePdfDocumentReader; import org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader; import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig; import org.springframework.ai.reader.markdown.MarkdownDocumentReader; import org.springframework.ai.reader.markdown.config.MarkdownDocumentReaderConfig; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.reader.jsoup.JsoupDocumentReader; import org.springframework.ai.document.Document; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.UrlResource; import java.util.List; // PDF Reader - page by page PagePdfDocumentReader pdfReader = new PagePdfDocumentReader( new ClassPathResource("documents/spring-guide.pdf"), PdfDocumentReaderConfig.builder() .withPagesPerDocument(1) .withPageExtractedTextFormatter(text -> text.trim()) .build() ); List pdfDocs = pdfReader.get(); // PDF Reader - by paragraphs (better for semantic search) ParagraphPdfDocumentReader paragraphReader = new ParagraphPdfDocumentReader( new ClassPathResource("documents/manual.pdf"), PdfDocumentReaderConfig.builder() .withPageTopMargin(50) .withPageBottomMargin(50) .build() ); List paragraphs = paragraphReader.get(); // Markdown Reader with header-based splitting MarkdownDocumentReader mdReader = new MarkdownDocumentReader( new ClassPathResource("docs/README.md"), MarkdownDocumentReaderConfig.builder() .withHorizontalRuleCreateDocument(true) .withIncludeCodeBlock(true) .withIncludeBlockquote(true) .build() ); List mdDocs = mdReader.get(); // HTML Reader using Jsoup JsoupDocumentReader htmlReader = new JsoupDocumentReader( new UrlResource("https://docs.spring.io/spring-boot/docs/current/reference/html/") ); List htmlDocs = htmlReader.get(); // Tika Reader for various formats (Word, Excel, PowerPoint, etc.) TikaDocumentReader tikaReader = new TikaDocumentReader( new ClassPathResource("documents/report.docx") ); List tikaDocs = tikaReader.get(); // Add documents to vector store after reading vectorStore.add(pdfDocs); vectorStore.add(mdDocs); // Full ETL pipeline example List allDocuments = new ArrayList<>(); allDocuments.addAll(pdfReader.get()); allDocuments.addAll(mdReader.get()); // Apply text splitter if needed TextSplitter splitter = new TokenTextSplitter(500, 100); List chunks = splitter.split(allDocuments); // Store in vector database vectorStore.add(chunks); ``` ## ChatMemory - Conversation History Management ChatMemory provides persistence for conversation history, enabling multi-turn conversations and context-aware AI interactions. Spring AI supports various backends including in-memory, JDBC, Redis, MongoDB, and more. ```java import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.messages.AssistantMessage; import java.util.List; // In-memory chat memory (for development/testing) ChatMemory memory = new InMemoryChatMemory(); // Add messages to a conversation String conversationId = "user-123-session-abc"; memory.add(conversationId, new UserMessage("Hi, I'm learning Java")); memory.add(conversationId, new AssistantMessage("Hello! Java is a great choice. What would you like to learn about?")); memory.add(conversationId, new UserMessage("Tell me about inheritance")); // Retrieve conversation history List history = memory.get(conversationId); history.forEach(msg -> System.out.println(msg.getMessageType() + ": " + msg.getText())); // Clear conversation memory.clear(conversationId); // Message window memory (keeps last N messages) ChatMemory windowMemory = MessageWindowChatMemory.builder() .chatMemory(new InMemoryChatMemory()) .maxMessages(10) .build(); // Using with ChatClient and MessageChatMemoryAdvisor ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors( MessageChatMemoryAdvisor.builder(memory) .conversationId("default") .build() ) .build(); // Multi-turn conversation chatClient.prompt().user("My favorite color is blue").call().content(); chatClient.prompt().user("What's my favorite color?").call().content(); // Returns: "Your favorite color is blue." // JDBC-backed memory for production (auto-configured with starter) // spring.ai.chat.memory.repository.jdbc.enabled=true // Uses conversation_id as key in database table // Redis-backed memory for distributed systems // spring.ai.chat.memory.repository.redis.enabled=true ``` ## Spring Boot Auto-Configuration Spring AI provides comprehensive Spring Boot starters for easy integration. Each AI provider and vector store has dedicated starters with sensible defaults and configuration properties. ```yaml # application.yml - OpenAI Configuration spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: options: model: gpt-4o temperature: 0.7 max-tokens: 2000 embedding: options: model: text-embedding-3-small # PgVector Configuration vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1536 # Chat Memory Configuration chat: memory: repository: jdbc: enabled: true ``` ```java import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class AiApplication { public static void main(String[] args) { SpringApplication.run(AiApplication.class, args); } // ChatClient.Builder is auto-configured @Bean public ChatClient chatClient(ChatClient.Builder builder) { return builder .defaultSystem("You are a helpful assistant.") .build(); } } // In your service, inject auto-configured beans @Service public class AiService { private final ChatClient chatClient; private final EmbeddingModel embeddingModel; private final VectorStore vectorStore; public AiService(ChatClient chatClient, EmbeddingModel embeddingModel, VectorStore vectorStore) { this.chatClient = chatClient; this.embeddingModel = embeddingModel; this.vectorStore = vectorStore; } public String chat(String message) { return chatClient.prompt() .user(message) .call() .content(); } public void indexDocument(String content, Map metadata) { Document doc = new Document(content, metadata); vectorStore.add(List.of(doc)); } public List search(String query) { return vectorStore.similaritySearch( SearchRequest.builder() .query(query) .topK(5) .build() ); } } ``` ## Summary Spring AI provides a comprehensive, Spring-native approach to building AI-powered applications. The primary use cases include building conversational AI chatbots with memory, implementing semantic search and RAG systems, creating AI agents with tool calling capabilities, and processing documents for knowledge bases. The framework's strength lies in its provider-agnostic design, allowing developers to switch between AI providers (OpenAI, Anthropic, Azure, Google, AWS) with minimal code changes while maintaining Spring's familiar programming model. Key integration patterns include using the ChatClient fluent API for chat interactions, combining VectorStore with RetrievalAugmentationAdvisor for RAG workflows, implementing custom tools via @Tool annotation or ToolCallback interface, and leveraging MCP for standardized tool integration. The auto-configuration capabilities enable rapid prototyping while the modular architecture supports production-grade deployments with observability (Micrometer), retry policies, and multiple persistence backends for conversation memory.