· 王思腾 · 大模型工程 · 10 分钟阅读

Spring AI 介绍与实践:用 Java 生态的方式接入大模型

Java 团队想接大模型,不必硬套 Python 的 LangChain。Spring AI 把 ChatClient、Prompt 模板、结构化输出、RAG(向量库)、函数调用、对话记忆这些能力做成了 Spring 风格的抽象和 Starter,配几行 yml、写几行链式调用就能跑。这篇从核心抽象讲起,带你用 Spring Boot 一步步实现聊天、结构化映射、RAG 知识库和工具调用,并聊聊生产里要注意的事。

Java 团队想接大模型,不必硬套 Python 的 LangChain。Spring AI 把 ChatClient、Prompt 模板、结构化输出、RAG(向量库)、函数调用、对话记忆这些能力做成了 Spring 风格的抽象和 Starter,配几行 yml、写几行链式调用就能跑。这篇从核心抽象讲起,带你用 Spring Boot 一步步实现聊天、结构化映射、RAG 知识库和工具调用,并聊聊生产里要注意的事。

大模型应用这两年,Python 生态(LangChain、LlamaIndex)声量最大,但大量企业的后端是 Java、是 Spring Boot。为了接个 AI 能力再维护一套 Python 服务,成本不低。Spring AI 就是来填这个坑的:它是 Spring 官方的 AI 应用框架,理念和 Spring 一贯的风格一致——提供统一抽象 + 自动装配 + Starter,让你用熟悉的 @Bean、依赖注入、链式 API 接大模型,底层模型可替换。

这篇从核心概念讲起,写几个能跑的实践:聊天、结构化输出、RAG 知识库、函数调用,最后说生产注意事项。

一、Spring AI 是什么

一句话:Spring AI 是 Java 版的“大模型应用脚手架”,对标 LangChain,但深度贴合 Spring 生态。

  • 统一模型抽象:ChatModel、EmbeddingModel 一套接口,底层可切换 OpenAI、Anthropic、Azure OpenAI、Ollama(本地)、通义/智谱等,换模型只改配置;
  • 高层 API:ChatClient 用流式链式调用完成对话、RAG、工具调用;
  • RAG 全套:文档读取(ETL)、切分、向量化、VectorStore 抽象(PGVector、Redis、Milvus、Chroma 等都有实现);
  • 结构化输出:直接把模型返回映射成 Java 对象;
  • 函数调用(Function Calling):把 Spring Bean 方法暴露给模型当工具;
  • Advisor(顾问)机制:把对话记忆、RAG 检索这类横切逻辑像拦截器一样织入;
  • 可观测性:原生接 Micrometer,token、延迟、调用链直接进 Actuator/Prometheus。

二、核心抽象速览

flowchart TB
    App["你的业务代码"] --> CC["ChatClient(高层门面)"]
    CC --> Adv["Advisors(横切:记忆 / RAG / 日志)"]
    Adv --> CM["ChatModel(统一模型接口)"]
    CM --> Prov["具体实现<br/>OpenAI / Ollama / 通义 ..."]
    CC --> VS["VectorStore(向量库抽象)"]
    CC --> EM["EmbeddingModel(向量化)"]
    CC --> Fn["Function Calling(工具/Bean)"]
  • ChatModel / EmbeddingModel:最底层的可移植抽象,对应“聊天模型”和“向量模型”;
  • ChatClient:日常用得最多的门面 API,提供 fluent 链式调用;
  • Prompt / Message:消息抽象(SystemMessage、UserMessage、AssistantMessage);
  • VectorStore:向量库统一接口,add() 写入、similaritySearch() 检索;
  • Advisor:请求/响应的拦截增强层,记忆和 RAG 都靠它织入,不用污染业务代码。

三、快速上手:一个聊天接口

引入 Starter(以 OpenAI 兼容接口为例,用国内模型换 base-url 即可):

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

配置 application.yml:

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: https://api.deepseek.com # 指向任何 OpenAI 兼容网关
      chat:
        options:
          model: deepseek-chat
          temperature: 0.3

写一个聊天接口:

@RestController
class ChatController {

    private final ChatClient chatClient;

    // 自动装配的 Builder 已带上全局默认配置
    ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultSystem("你是一个严谨的技术助手,回答简洁。")
            .build();
    }

    @GetMapping("/chat")
    String chat(@RequestParam String q) {
        return chatClient.prompt()
            .user(q)
            .call()
            .content();          // 直接拿文本结果
    }
}

要流式输出(打字机效果),把 .call() 换成 .stream().content() 返回 Flux<String> 即可。

四、结构化输出:直接映射成 Java 对象

让模型返回可被程序消费的结果,是生产里的高频需求。Spring AI 可以直接把响应映射成一个 record/类:

record Sentiment(String sentiment, int score, String reason) {}

Sentiment result = chatClient.prompt()
    .user("分析这条评价的情感倾向:「用了三天就坏了,客服还不理人」")
    .call()
    .entity(Sentiment.class);   // 自动得到结构化对象

框架会在底层用函数调用/JSON Schema 约束模型输出并反序列化,比自己拼 JSON、写正则稳得多。这对应 Prompt 工程里讲的“结构化输出”技巧,Spring 把它做成了一行 API。

五、RAG:几行代码搭知识库问答

RAG 的离线建库 + 在线检索,Spring AI 都有现成组件。整体流程和原理篇讲的一致:

flowchart TB
    subgraph Off["离线"]
        Doc["文档 PDF/TXT/MD"] --> Reader["DocumentReader 读取"]
        Reader --> Split["TokenTextSplitter 切分"]
        Split --> Embed["EmbeddingModel 向量化"]
        Embed --> VS[("VectorStore")]
    end
    subgraph On["在线"]
        Q["用户提问"] --> QA["QuestionAnswerAdvisor<br/>自动检索 + 拼上下文"]
        QA --> CC["ChatClient"] --> Ans["带依据的回答"]
    end
    VS --> QA

离线:文档入库

@Service
class IngestService {
    private final VectorStore vectorStore;

    IngestService(VectorStore vectorStore) { this.vectorStore = vectorStore; }

    void ingest(Resource pdf) {
        var docs = new PagePdfDocumentReader(pdf).get();   // 读 PDF
        var split = new TokenTextSplitter().apply(docs);   // 切分
        vectorStore.add(split);                           // 内部自动 embedding 后写入
    }
}

向量库选 PGVector 就引入对应 starter 并配数据源,VectorStore Bean 自动创建,业务代码不关心底层是 Postgres 还是 Milvus。

在线:用 Advisor 自动做 RAG

String answer = chatClient.prompt()
    .user(q)
    .advisors(new QuestionAnswerAdvisor(vectorStore))  // 自动检索 Top-K 并注入上下文
    .call()
    .content();

QuestionAnswerAdvisor 把“检索相关片段 → 拼进 Prompt”这步全包了。想控制 topK、相似度阈值、检索到的元数据过滤,可以传 SearchRequest。生产上别忘了 RAG 实战篇强调的几件事:切分参数、混合检索、Rerank、相似度兜底(让模型在没找到时说“不知道”)。

六、函数调用:让模型调用你的 Java 方法

模型本身不能查订单、不能发消息,函数调用让它“长出手脚”。在 Spring AI 里,一个工具就是一个带描述的 Bean:

@Configuration
class Tools {

    @Bean
    @Description("根据订单号查询订单物流状态")
    Function<OrderRequest, OrderStatus> orderStatus() {
        return req -> orderService.queryStatus(req.orderId());
    }

    record OrderRequest(String orderId) {}
    record OrderStatus(String orderId, String status, String eta) {}
}

调用时声明可用函数,模型会自己决定何时、用什么参数调用:

String reply = chatClient.prompt()
    .user("帮我查一下订单 A20260409 到哪了")
    .functions("orderStatus")     // 注册给模型的工具名(Bean 方法名)
    .call()
    .content();

模型识别出需要查订单 → 生成 OrderRequest 参数 → Spring 执行你的 Bean → 把结果回喂模型 → 模型组织成自然语言。参数的 schema、调用编排框架都处理了,你只写业务逻辑。

七、对话记忆:多轮上下文

默认每次调用是无状态的,多轮对话靠 ChatMemory + Advisor 织入:

@Bean
ChatMemory chatMemory() {
    return new MessageWindowChatMemory();   // 窗口式记忆,也可用 JdbcChatMemory 持久化
}

// 请求时带上会话 id,并加记忆 Advisor
chatClient.prompt()
    .user("我刚才问的那个问题再展开讲讲")
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
    .advisors(new MessageChatMemoryAdvisor(chatMemory))
    .call().content();

框架按会话 id 自动加载历史消息塞进上下文、并把本轮对话存回。生产上要注意窗口长度(太长既费 token 又超上下文),通常配合摘要压缩,这点和 Agent 工程篇讲的记忆管理一致。

八、生产实践要注意的事

  • 模型可移植但别过度依赖:统一抽象很好,但各厂商的高级特性(推理模式、特殊参数)抽象层未必暴露,必要时下沉到具体模型 API;
  • 成本与超时:为每次调用设超时、重试和 token 上限;接入成本优化篇讲的模型分级路由——简单意图走小模型,难例再升级;
  • 可观测性:开启 Spring AI 的 observation,把 token 消耗、延迟、错误率接进 Actuator + Prometheus,否则上量后账单和故障都是黑盒;
  • RAG 数据更新:文档变更要能增量重建向量,别只建一次就不管;
  • 函数调用的安全:模型能调你的方法,就要做权限校验和参数校验,高危操作(退款、删除)加人工确认,别让模型直接触发;
  • 版本:Spring AI 迭代较快,1.0 正式版后 API 趋于稳定,升级时留意包名和 starter 命名变化。

小结

  1. Spring AI 是 Spring 官方的大模型应用框架,用统一抽象 + Starter 把聊天、Embedding、向量库、函数调用、记忆做成可替换组件,Java 团队无需另起 Python 服务;
  2. 日常开发围绕 ChatClient 的链式 API:.prompt().user().call().content(),流式用 .stream();
  3. 结构化输出用 .entity(Class),RAG 用 VectorStore + QuestionAnswerAdvisor,工具调用用 @Bean Function + .functions(),多轮记忆用 ChatMemory + MessageChatMemoryAdvisor;
  4. 换模型只改 yml,业务代码不动;
  5. 生产重点仍是那几件通用的事:超时/重试/成本控制、可观测、RAG 数据更新、函数调用的权限与人工卡点。

对 Java 后端来说,Spring AI 最大的价值是让接大模型这件事回到了熟悉的节奏:加依赖、写配置、注入 Bean、调方法。模型在变,但你的工程体系和团队技能栈不用推倒重来。

本文由 AI 辅助创作,所有内容仅供交流与参考。
分享:
返回博客
大模型应用架构进阶:从 Prompt、工作流、RAG 到 Agent 的选型与演进

大模型应用架构进阶:从 Prompt、工作流、RAG 到 Agent 的选型与演进

“Prompt、RAG、Agent 三选一”是个过于粗糙的说法。真实系统是一条从“全确定”到“全自主”的连续光谱,RAG 本质是给模型接知识的能力,Agent 也分固定编排和自主循环。这篇往深里聊:工作流模式、RAG 的三个深浅层级、什么时候才该让模型自主决策,以及成本、延迟和失败模式这些入门文章很少算的账。

动手实战:从零搭一个 RAG 知识库问答系统(含代码与生产化清单)

动手实战:从零搭一个 RAG 知识库问答系统(含代码与生产化清单)

原理篇讲了 RAG 是什么,这篇带你亲手搭一个能跑的。从文档切分、向量化入库,到检索、重排、拼上下文、生成答案,给出最小可运行代码;然后把 Demo 推向生产:混合检索、Rerank、查询改写、相似度兜底、引用溯源、评测集和增量更新,附一份逐项可落地的调优清单。

RAG 原理详解:让大模型用上私有知识的检索增强生成

RAG 原理详解:让大模型用上私有知识的检索增强生成

大模型知识冻结、会幻觉、不知道企业内部数据。RAG(检索增强生成)通过“先检索、再生成”把外部知识注入上下文,是当前落地最广的大模型应用范式。本文带你走通 RAG 的完整链路:分块、嵌入、检索与重排,以及工程上最常踩的坑。

LLM 推理优化:大模型为什么慢,以及如何提速降本

LLM 推理优化:大模型为什么慢,以及如何提速降本

大模型推理是自回归逐 token 生成,显存带宽是瓶颈。本文讲清 Prefill/Decode 两阶段、KV Cache 的作用与开销,再逐个聊连续批处理、PagedAttention(vLLM)、量化(INT8/INT4)、投机解码、FlashAttention 与并行策略,以及延迟与吞吐怎么权衡。