Artificial Intelligence 43 min read

Build AI‑Native Apps Quickly with Spring AI: From Chat Models to RAG

This guide explains what an AI‑native application is, compares AI‑native and AI‑based approaches, and walks through Spring AI’s core features—including chat models, prompt templates, function calling, structured output, image generation, embedding, and vector stores—showing step‑by‑step code examples and how to assemble a complete AI‑native app with RAG support.

Architecture & Thinking
Architecture & Thinking
Architecture & Thinking
Build AI‑Native Apps Quickly with Spring AI: From Chat Models to RAG

Summary

AI‑native applications embed artificial intelligence as the core functionality; without AI the app would not exist. They differ from AI‑based apps where AI is merely an add‑on.

Ways to Add AI

Replace existing modules with AI‑powered ones.

Add a brand‑new AI module.

Introduce an AI‑driven module that orchestrates traditional components.

Spring AI Project Overview

Spring AI simplifies the development of applications that use large language models (LLMs). It provides abstractions, auto‑configuration, and starter modules for a wide range of providers (OpenAI, Azure, Google, HuggingFace, etc.) and supports chat, text‑to‑image, function calling, and more.

Core Features

3.1 Conversation Model

The Chat Completion API lets developers integrate conversational AI. Spring AI registers OpenAiChatClient as a bean that can be autowired.

<code>@RestController
public class AiController {
    @Autowired
    private OpenAiChatClient chatClient;

    @GetMapping("/ai/generate")
    public Response generate(@RequestParam(value="message", defaultValue="Tell me a joke") String message) {
        return Response.ok(chatClient.call(message));
    }

    @GetMapping("/ai/generateStream")
    public Flux<ChatResponse> generateStream(@RequestParam(value="message", defaultValue="Tell me a joke") String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return chatClient.stream(prompt);
    }
}
</code>

Two output modes are supported: call (non‑streaming) and stream (character‑by‑character).

3.2 Prompt Templates

Prompt templates allow placeholders that are replaced at runtime, similar to JdbcTemplate .

<code>PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");
Prompt prompt = promptTemplate.create(Map.of("adjective", "funny", "topic", "cats"));
return chatClient.call(prompt).getResult();
</code>

3.3 Function Calling

Function calling lets LLMs invoke external tools. Example services:

<code>public class WeatherService implements Function<WeatherService.Request, WeatherService.Response> {
    public record Request(@JsonProperty(required=true, value="位置") @JsonPropertyDescription("城市,例如: 广州") String location) {}
    public record Response(String weather) {}
    @Override
    public Response apply(Request request) {
        // simple mock implementation
        return new Response("小雨转阴 13~19°C");
    }
}
</code>

Functions are registered as beans and referenced in the prompt options.

3.4 Structured Output (OutputParser)

OutputParser maps LLM string output to Java objects.

<code>@Data
public class ActorsFilms {
    private String actor;
    private List<String> movies;
}

@Bean
public Response getActorFilms(@RequestParam String actor) {
    BeanOutputParser<ActorsFilms> parser = new BeanOutputParser<>(ActorsFilms.class);
    PromptTemplate tmpl = new PromptTemplate("为演员{actor}生成电影作品年表。{format}");
    Prompt prompt = tmpl.create(Map.of("actor", actor, "format", parser.getFormat()));
    Generation gen = chatClient.call(prompt).getResult();
    ActorsFilms result = parser.parse(gen.getOutput().getContent());
    return Response.ok(JacksonUtil.toJson(result));
}
</code>

3.5 Image Generation

<code>@RestController
public class AiController {
    @Autowired
    private ImageClient imageClient;

    @GetMapping("/ai/image")
    public Response image(@RequestParam String description) {
        ImageResponse resp = imageClient.call(new ImagePrompt(description,
            OpenAiImageOptions.builder().withQuality("hd").withN(1).withHeight(1024).withWidth(1024).build()));
        return Response.ok(resp.getResults().get(0).getOutput().getUrl());
    }
}
</code>

3.6 Embedding (Vectorization)

<code>@RestController
public class AiController {
    @Autowired
    private EmbeddingClient embeddingClient;

    @GetMapping("/ai/embedding")
    public Response embed(@RequestParam(defaultValue="Tell me a joke") String message) {
        EmbeddingResponse resp = embeddingClient.embedForResponse(List.of(message));
        return Response.ok(resp);
    }
}
</code>

3.7 Vector Database Integration

Spring AI supports many vector stores (Azure, Chroma, Milvus, Neo4j, PGVector, Pinecone, Qdrant, Redis, Weaviate, SimpleVectorStore).

<code>@Configuration
public class ToolConfig {
    @Bean
    public VectorStore createVectorStore(EmbeddingClient embeddingClient) {
        return new SimpleVectorStore(embeddingClient);
    }
}

@RestController
public class AiController {
    @Autowired
    private VectorStore vectorStore;

    @GetMapping("/ai/vectorStore/add")
    public Response add(@RequestParam String content) {
        Document doc = new Document(content);
        vectorStore.add(List.of(doc));
        return Response.ok();
    }

    @GetMapping("/ai/vectorStore/search")
    public Response search(@RequestParam String query) {
        List<Document> docs = vectorStore.similaritySearch(query);
        return Response.ok(JacksonUtil.toJson(docs));
    }
}
</code>

Retrieval‑Augmented Generation (RAG)

RAG combines external data with LLM prompts to improve factuality. The workflow: extract text → split into chunks → embed → store in a vector DB → retrieve relevant chunks at query time and inject them into the prompt.

4.3 Creating a RAG Knowledge Base

<code>@RestController
public class AiController {
    @Autowired
    private VectorStore vectorStore;

    @GetMapping("/ai/rag/create")
    public Response ragCreate() {
        String filePath = "resume.txt";
        TextReader reader = new TextReader(filePath);
        List<Document> docs = reader.get();
        TokenTextSplitter splitter = new TokenTextSplitter(1200, 350, 5, 100, true);
        docs = splitter.apply(docs);
        vectorStore.add(docs);
        return Response.ok();
    }
}
</code>

Building a Simple AI‑Native Recruitment Assistant

The assistant combines a system prompt (personality), a vector knowledge base of candidate resumes, and a function that returns a candidate’s applied position.

<code>public class RecruitService implements Function<RecruitService.Request, RecruitService.Response> {
    public record Request(@JsonProperty(required=true, value="人名") @JsonPropertyDescription("投递简历人姓名,例如: 张真源") String name) {}
    public record Response(String position) {}
    @Override
    public Response apply(Request req) {
        String pos = "未知";
        if (req.name().contains("张真源")) pos = "算法工程师";
        return new Response(pos);
    }
}

@RestController
public class AiController {
    @Autowired
    private VectorStore vectorStore;
    @Autowired
    private OpenAiChatClient chatClient;

    @GetMapping("/ai/agent")
    public Response rag(@RequestParam String query) {
        List<Document> docs = vectorStore.similaritySearch(query);
        String info = docs.isEmpty() ? "" : docs.get(0).getContent();
        String systemPrompt = "角色与目标:你是一个招聘助手...请你跟进数据参考与工具返回结果回复用户的请求。";
        String userPrompt = "给你提供一些数据参考: {info},请回答我的问题:{query}";
        Message systemMsg = new SystemMessage(systemPrompt);
        PromptTemplate tmpl = new PromptTemplate(userPrompt);
        Message userMsg = tmpl.createMessage(Map.of("info", info, "query", query));
        Prompt prompt = new Prompt(List.of(userMsg, systemMsg), OpenAiChatOptions.builder().withFunctions(Set.of("recruitPosition")).build());
        List<Generation> resp = chatClient.call(prompt).getResults();
        StringBuilder sb = new StringBuilder();
        for (Generation g : resp) sb.append(g.getOutput().getContent());
        return Response.ok(sb.toString());
    }
}
</code>

Example request: GET /ai/agent?query=刘磊是否有资格参与面试 returns a detailed assessment based on the resume knowledge base and the recruitment function.

JavaPrompt EngineeringRAGfunction callingSpring AIAI native application
Architecture & Thinking
Written by

Architecture & Thinking

🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.