当前位置: 首页 > news >正文

Spring AI 入门到实战:我如何用它让系统具备“理解能力”

我向来对“整合大模型进 Java 应用”这件事持谨慎态度。在 GPT 火了之后,我们团队最初是用 HTTP 手动调 OpenAI 接口,把它当成一个 JSON API 用。但随着业务交互变复杂,我意识到:我们需要的是一个语义系统,而不是一个封装 prompt 的 util 类。

这时,Spring AI 出现了。

它不是框架革命,而是语义层和 Spring 编程模型之间的桥梁。你可以像写 Spring Boot Controller 那样调用大模型,像写 Bean 那样组织提示词和语义模板

以下是我亲身踩过的坑与总结。


Spring AI 是什么?一句话版本

Spring AI 是 Spring 推出的 AI 集成框架,它封装了与 OpenAI、Azure、HuggingFace、Anthropic 等主流 LLM 提供商的连接逻辑,并基于 Spring 编程模型提供统一的:

  • Prompt 模板机制
  • Function 调用能力(Function calling)
  • Embedding 向量搜索整合(如与 Redis、PGVector、Milvus 等)
  • ChatMemory 管理
  • RAG(检索增强生成)支持

第一步:快速跑通 OpenAI 接入

引入依赖(当前主流版本建议使用 0.8.1 或以上):

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>0.8.1</version>
</dependency>

application.yml 配置如下:

spring:ai:openai:api-key: ${OPENAI_API_KEY}base-url: https://api.openai.com/v1chat:model: gpt-4

然后写个最基础的服务类:

@Service
public class AiAssistant {private final OpenAiChatClient chatClient;public AiAssistant(OpenAiChatClient chatClient) {this.chatClient = chatClient;}public String ask(String question) {ChatResponse response = chatClient.call(question);return response.getResult().getOutput().getContent();}
}

你会惊讶于它的“Spring 味儿”有多重 —— ChatClient 就像 RestTemplate,但背后驱动的是 LLM。


第二步:PromptTemplate 的强大之处

Spring AI 真正让我惊艳的,是 PromptTemplatePromptTemplateModel

举个例子,我们想做一个发票解析助手:

String template = """
你是一个发票专家,请从以下文本中提取字段:
{{text}}返回格式:
{"invoiceCode": "","invoiceNumber": "","amount": "","date": ""
}
""";PromptTemplate promptTemplate = new PromptTemplate(template);
promptTemplate.add("text", ocrResult);Prompt prompt = promptTemplate.create();
ChatResponse response = chatClient.call(prompt);

为什么说它强大?因为它把 prompt 当作模板文件处理,变量和代码解耦,可调试、可重构、可测试 —— 这比我们以前把 prompt 字符串硬编码在 Java 里强太多。


第三步:函数调用(Function Call)集成

Spring AI 支持大模型的函数调用(Function Calling)能力。你可以像注册 Spring Bean 一样注册 AI 能“调用”的 Java 方法:

@Component
public class InvoiceFunction {@AiFunctionpublic String parseInvoice(@AiFunctionParameter(name = "text") String text) {// 实际是把这个方法暴露给大模型,让它决定是否调用return someService.extractInvoiceJson(text);}
}

这意味着你可以让 LLM 成为业务流程中的“指挥员”,它不再只是吐字,而是能决定调用哪个函数、传什么参数。


第四步:向量检索 + Embedding(RAG 模式)

Spring AI 原生支持嵌入向量生成和搜索。以下是一个 Redis + Embedding 的组合示例:

  1. 首先配置 Redis 向量存储(需使用 Redis Stack):
spring:ai:vectorstore:redis:host: localhostport: 6379
  1. 注入向量存储和 embedding 模型:
@Autowired
private VectorStore vectorStore;@Autowired
private EmbeddingClient embeddingClient;
  1. 索引知识库:
EmbeddingResponse embedding = embeddingClient.embed("这是发票的开票代码含义说明…");
vectorStore.add(List.of(new Vector("invoice-001", embedding.getEmbedding(), metadata)));
  1. 在对话中执行 RAG 检索:
List<Document> docs = vectorStore.similaritySearch("什么是发票代码", 3);
String context = docs.stream().map(Document::getContent).collect(Collectors.joining("\n"));
  1. 拼接到 Prompt 中:
String prompt = "基于以下知识回答问题:\n" + context + "\n问题:什么是发票代码?";

这样你就完成了一个端到端的 RAG 系统,模型可以“读”你自己喂的文档。


项目实战技巧总结

  1. Prompt 模板一定要版本管理:我们用 YAML + Git 存储每个 PromptTemplate,避免代码污染和可追踪。
  2. 用注解暴露函数给大模型,是一种未来的编排模式:特别适合 Workflow 逻辑。
  3. LLM 的不可控性,需要用 ChatMemory 管住上下文:Spring AI 提供了 MemoryChatClient 封装 chat history,避免上下文漂移。
  4. 别忽视异常处理:OpenAI 接口 rate limit、timeout 经常发生,Spring AI 建议用 RetryTemplate 或自定义 fallback。

写在最后:Spring AI 更像一个“语言中间件”

Spring AI 不是大模型的封装器,而是让 Spring 应用具备“语言交互能力”的中间件

它就像当年的 Spring Data,让我们用接口操作数据库;现在我们也可以用 Spring 风格操作语义接口。


你不再需要理解 token、role、chat-completion payload,只需要关注一件事:

“业务中哪块逻辑,可以更好地用语言来处理?”

从接口设计、代码结构,到 Prompt 管理和知识检索,Spring AI 都在逐渐成为一种“主流选型”。

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 什么是 Cookie?简单介绍与使用方法

  • 什么是 Session?如何应用?

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • 如何理解应用 Java 多线程与并发编程?

  • 把握Java泛型的艺术:协变、逆变与不可变性一网打尽

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 如何理解线程安全这个概念?

  • 理解 Java 桥接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加载 SpringMVC 组件

  • “在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”

  • “避免序列化灾难:掌握实现 Serializable 的真相!(二)”

  • 如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)

  • 解密 Redis:如何通过 IO 多路复用征服高并发挑战!

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”

  • Java 中消除 If-else 技巧总结

  • 线程池的核心参数配置(仅供参考)

  • 【人工智能】聊聊Transformer,深度学习的一股清流(13)

  • Java 枚举的几个常用技巧,你可以试着用用

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)

http://www.lqws.cn/news/529957.html

相关文章:

  • 【机器学习第一期(Python)】梯度提升决策树 GBDT
  • Pycharm无法运行Vue项目的解决办法
  • Java 泛型详解:从入门到实战
  • jdbc实现跨库分页查询demo
  • 人力资源管理系统
  • Spring Cloud Config动态刷新实战指南
  • 用户统计-01.需求分析和设计
  • GNSS位移监测站在大坝安全中的用处
  • 渗透实战:使用隐式转换覆盖toString的反射型xss
  • Day43 复习日 图像数据集——CNN
  • 【PX4-AutoPilot教程-TIPS】PX4系统命令行控制台ConsolesShells常用命令(持续更新)
  • ES文件管理器v4.4.3(ES文件浏览器)
  • 鸿蒙 FoldSplitContainer 解析:折叠屏布局适配与状态管理
  • MySQL之存储函数与触发器详解
  • 多相机人脸扫描设备如何助力高效打造数字教育孪生体?
  • ethers.js express vue2 定时任务每天凌晨2点监听合约地址数据同步到Mysql整理
  • ASIO 避坑指南:高效、安全与稳健的异步网络编程
  • 微服务架构下面临的安全、合规审计挑战
  • Python打卡:Day37
  • 使用 Python 自动化文件获取:从 FTP 到 API 的全面指南
  • 【Bluedroid】蓝牙启动之 btm_acl_device_down 流程源码解析
  • 稳定币技术全解:从货币锚定机制到区块链金融基础设施
  • Java底层原理:深入理解线程与并发机制
  • GEO生成式引擎优化发展迅猛:热点数智化传播是GEO最佳路径
  • 人大金仓Kingbase数据库KSQL 常用命令指南
  • 【论文】云原生事件驱动架构在智能风控系统中的实践与思考
  • 小孙学变频学习笔记(八)变频器的输入电流(下)
  • RPC(Remote Procedure Call)技术解析
  • 计算机网络 网络层:控制平面(二)
  • WPF中Converter基础用法