ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring AI Alibaba停更不可怕,Java开发者做AI的出路在集成与RAG

Spring AI Alibaba停更不可怕,Java开发者做AI的出路在集成与RAG

Spring AI Alibaba 停更了的消息,这几天在 Java 圈子里讨论得不算少。我身边几个做后端的朋友,第一反应都是同一个问题:Java 还有希望吗?我的回答也很直接:先别急着把手里用得顺手的锤子扔掉,得先看清眼前那颗钉子到底是什么形状。

先说结论:某个适配层项目停更,和 Java 这门语言在 AI 时代有没有未来,中间至少隔着一整个技术生态的距离。真正要紧的不是盯着一个仓库的提交记录叹气,而是想清楚 Java 开发者手里那套积累了十几年的工程能力,到底在 AI 应用里能变成什么实实在在的东西。

这篇文章我不打算讲太多虚的。会先从 Spring AI Alibaba 的定位说起,再聊 Java 在 AI 生态里的真实位置,最后给几条可以直接上手的路线和一个完整跑通的案例。如果你是做 Java 后端、最近因为这条消息有点慌的,这篇文章应该能帮你把心放到肚子里。

1. 先弄清停的是哪座桥,别把整条河都当成没路走

1.1 Spring AI Alibaba 在技术栈里的真实定位

很多人把 Spring AI Alibaba 理解成了"Java 做 AI 的唯一入口",这个误解是焦虑的源头。实际上,它更像是 Spring AI 生态里的一座桥:一端连着 Spring AI 这套 Java 侧的 AI 抽象框架,另一端连着某个云厂商的模型服务。有了它,你在配置里写几行参数,就能让 Spring Boot 应用直接调用云端的大模型接口,省去自己封装 HTTP 请求、鉴权、消息格式转换这些重复工作。

但要注意,这座桥解决的是"方便"的问题,不是"能不能"的问题。哪怕这座桥不存在,你依然可以用 Spring AI 主项目对接模型服务,甚至完全不用任何框架,直接拿 Java 的 HttpClient 去调用模型接口。打个比方,Spring AI Alibaba 停更,相当于一条商业街里某个品牌奶茶店关门了,你不能因此认为整个奶茶行业完了,更不能认为自己以后只能喝白开水。

从技术结构上看,Spring AI Alibaba 属于适配层。它的主要工作是跟随上游 Spring AI 的变化同步调整配置属性和自动装配逻辑。这种项目有一个天然痛点:只要上游版本一更新,它就得跟着改,维护成本高、收益却不太明显。一旦项目方觉得投入产出不划算,停更往往是大概率事件。

1.2 适配层项目停更,背后的原因不外乎这几类

我翻过不少类似的适配项目,停更原因基本逃不出三类:

一是上游迭代太快,跟不上。Spring AI 本身正处于快速演进期,版本号涨得快,API 说变就变。适配项目如果团队规模小,很难做到同步更新。时间一长,版本落后太多,再想追就难了。

二是厂商阶段性的资源调整。开源项目往往依赖公司内部的资源和战略方向。如果项目方把人力调去别的方向,或者觉得这个适配层不再需要单独维护,停更就是自然结果。这不代表 Java 生态出问题,只代表某个具体团队的业务优先级变了。

三是官方能力已经覆盖。有时上游主项目直接内置了同类能力,适配层就没有存在必要了。这在开源世界里是好事,说明生态是在整合,不是在萎缩。

想验证一个项目是不是真的停更,不用听传言,直接看三点:GitHub 上的最近提交时间、Issue 的响应情况、以及有没有新版本发布。我当前看到的是,Spring AI Alibaba 近期确实长时间没有活跃更新,但 Spring AI 主项目本身仍然在正常迭代。两者是完全不同层面的东西。

1.3 停更的直接影响:谁该慌,谁不用慌

为了让你判断得更准确,我把受影响人群分成三类,用表格直接看清楚:

人群实际影响我的建议
已经在生产环境用了 Spring AI Alibaba功能已经固定,只要不过度追求新特性,风险可控暂时不用动,评估后续有没有必要迁移到标准 Spring AI 接口
正准备启动新项目不建议再选它作为集成层,避免刚起步就背上维护包袱直接改用 Spring AI 主项目或下面要说的其他路线
还没开始用 Java 做 AI 的开发者几乎没有影响正好跳过这段历史包袱,从当前稳定的生态直接起步

说到底,技术选型里真正值钱的不是某座桥,而是你过桥之后能不能把事情做成。Java 的能力沉淀在桥的对岸,不在桥本身。

2. Java 做 AI 真的没戏吗?先把基本盘看清楚

2.1 AI 应用的真正战场,不在训练,在集成

聊到 AI,很多人先想到 Python、PyTorch、训练大模型。这些确实是 AI 前端科技最密集的地方,但它们是属于少数团队的战场。绝大多数公司做 AI 项目,做的不是从零训练一个模型,而是把现成的大模型能力接进自己的产品:客服机器人、文档分析、代码辅助、智能审核、流程自动化。这些东西的本质是集成,是把模型变成业务系统里一个可靠的服务。

而集成恰恰是 Java 最有话语权的地方。一个典型的 Java 后端工程师,日常干的活就是对接数据库、调用第三方接口、处理消息队列、保证事务一致性、做好权限控制。这些能力在做 AI 应用时一个都不少,反而因为大模型带入了很多不可控因素,对工程化的要求更高了。你可以把大模型想象成一个能力很强但经常情绪不稳定的新同事,Java 后端的工作就是给他配好流程、设好边界、管好上下文,让他稳定地输出结果。

训练一个能写诗的大模型是模型团队的事。但在自己的业务系统里稳定地调用这个模型,把它的回答接进工单系统、加上权限校验、记录审计日志,这一整套活儿,Java 工程师闭着眼都能干。

2.2 企业级落地的硬约束,恰恰站在 Java 这边

企业级 AI 应用有几个绕不开的硬约束:合规审计、系统对接、稳定性保障、事务一致性、监控告警。这些东西不是"加分项",是"一票否决项"。

做个智能客服,回答对了问题只是开始。你得知道这个回答是哪个模型给的、当时的提示词是什么、上下文中包含哪些业务数据、用户最终看到了什么。这一整套可追溯链路,Java 生态里的日志框架、链路追踪、审计组件基本是现成的。换成一个临时起意的 Python 脚本,这些能力要从零搭,而且要搭好不容易。

再说系统对接。很多公司的核心业务跑在 Java 技术栈里,Spring Boot 就是事实上的标准。AI 能力要进生产,就得跟现有系统做同一个容器、同一套注册中心、同一个监控大盘。Java 版本的 AI 应用可以顺滑地融进这套基础设施,Python 服务还得单独维护一套部署和监控链路。我见过不少团队最后把 Python 推理服务外面再套一层 Java 网关,本质上还是为了接入这个体系。

所以说,Java 不是被 AI 抛弃了,恰恰是因为 AI 要落地到企业业务里,Java 的工程能力反而变成了刚需。大模型负责聪明,Java 负责靠谱,这两件事在现实项目里是分工合作关系。

2.3 别只盯一个仓库,Java AI 生态比你想的活跃

经常有人问我:Java 做 AI 是不是只有 Spring AI 一条路?真不是。我简单列几个方向:

  • Spring AI 主项目:Spring 官方主导的 AI 应用框架,提供 ChatClient、Prompt Template、VectorStore、Function Calling 等抽象,模型服务商适配还在持续扩展中。
  • LangChain4j:专门为 Java 设计的 LLM 应用框架,思路参考 LangChain,但 API 完全是 Java 风格,中文文档也维护得不错。
  • DJL 和 ONNX Runtime Java API:适合在 Java 进程里直接跑本地推理模型,用于隐私场景、离线场景或低延迟场景。
  • 直接调用模型服务商的标准 REST 接口:连框架都不用,一个 HttpClient 就能干活。

从社区活跃度看,这些项目里有不少还在持续发版本。说"Java 生态在 AI 时代彻底没戏",更像是情绪化表达,而不是对技术现状的客观判断。

3. 真要在 Java 里搞 AI,这几条路线我都替你趟过

3.1 路线一:继续守好 Spring AI 主项目

Spring AI 主项目是 Spring 官方团队在维护的,定位是给 Java 开发者提供一套统一的 AI 应用开发抽象。它已经走出了早期"做个聊天 Demo"的阶段,开始覆盖对话、提示词模板、向量检索、函数调用、多轮记忆这些真实项目里绕不开的能力。

我用它接本地模型的时候,最小配置是这样的(以本地 Ollama 服务为例,换成其他模型服务商只需调整对应的 starter 依赖和配置前缀):

spring.ai.ollama.base-url=http://localhost:11434 spring.ai.ollama.chat.model=qwen2.5:7b spring.ai.ollama.embedding.model=nomic-embed-text

然后直接注入 ChatClient 就能用:

@RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/chat") public String chat(@RequestParam(defaultValue = "你好") String message) { return chatClient.prompt() .system("你是一个乐于助人的中文助手。") .user(message) .call() .content(); } }

如果你选这条路,核心收益是:保持 Spring 生态的统一编程体验,事务、配置、监控这些基础设施都是现成的。核心成本是:需要花时间熟悉 Spring AI 自己的抽象概念,而且它本身 API 还在演进,升级版本时要留意兼容性。

3.2 路线二:用 LangChain4j 这类更纯粹的 Java AI 库

LangChain4j 从一开始就是按 LLM 应用的思路设计的,对链式调用、消息记忆、AI Service 这类抽象支持得很直接。如果你的项目不是一个典型的 Spring MVC 应用,或者你想少一点 Spring AI 的魔法、多一点自己掌控的参数,LangChain4j 会更顺手。

var model = OpenAiChatModel.builder() .baseUrl(System.getenv("LLM_BASE_URL")) .apiKey(System.getenv("LLM_API_KEY")) .modelName("qwen2.5:7b") .build(); String answer = model.chat("用一句话解释什么是RAG"); System.out.println(answer);

注意,代码里的接线方式是示例,具体版本的方法签名略有差异,以你依赖的版本为准。但整体上,LangChain4j 对 Java 开发者的心智负担比较小,尤其适合喜欢显式组装每一步流程的人。

3.3 路线三:直接裸调模型接口,清爽又可控

如果项目里只是偶尔调用一次模型,比如做一个内部小工具,那我建议别上框架。Java 自带的 HttpClient 足够完成整个调用,好处是没有额外依赖、逻辑一目了然、出了问题时排查路径最短。

HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); String json = """ { "model": "qwen2.5:7b", "messages": [ {"role": "user", "content": "用一句话解释RAG"} ], "temperature": 0.7 } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://localhost:11434/v1/chat/completions")) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + System.getenv("LLM_API_KEY")) .POST(HttpRequest.BodyPublishers.ofString(json)) .timeout(Duration.ofSeconds(60)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());

我把调用地址写成了本地兼容端点,正式接入时换成实际服务的地址即可。用裸调的方式,你会更清楚每次请求到底往外面发了什么、返回了什么。我有一个感受:裸调几次之后,再回去用框架,你会对框架帮你做的那些事情有完全不同的理解。

3.4 路线四:本地推理留给 DJL 和 ONNX Runtime

还有一类场景,数据不能出内网、或者需要极低延迟响应,这时候就得在 Java 进程内直接跑模型推理。DJL 让 Java 开发者能加载 PyTorch、TensorFlow、ONNX 格式的模型,ONNX Runtime 的 Java API 也相当成熟,加载模型后可以直接跑推理,不依赖外部推理服务。

这类方案的代价是,你需要处理模型本身的体积、显存占用、输入输出的张量转换等一堆底层细节,上手门槛比调用 API 高不少。但从架构角度看,它把 AI 能力变成了一个纯 Java 组件,部署形态跟在项目里嵌一个缓存组件差不多。

如果只是想快速出活,优先走前三条路线。如果业务对数据安全、延迟有硬指标,再考虑本地推理。这个顺序能帮你少走很多弯路。

4. 手把手实操:用 Java 跑通一个文档问答(RAG)应用

前面讲的是路线,这一段我给一个完整的落地案例。RAG(检索增强生成)是目前 Java 后端最容易切入、也最能体现工程价值的 AI 场景:先把自己的文档切块存进向量库,用户提问时先检索相关片段,再把片段和问题一起交给模型生成回答。这样答案能锚定在你的文档内容上,大幅减少幻觉。

4.1 场景设定与准备工作

我用的是一个模拟项目 X:内部售后文档问答系统,用户问"退款流程是什么",系统应该基于已有的售后手册回答,而不是模型凭空编一个流程。

准备清单:

  • JDK 17+,Maven 项目
  • Spring Boot 3.x 项目
  • PostgreSQL 数据库,启用 pgvector 扩展
  • 本地模型服务或远程模型服务,需要一个支持 Chat 和 Embedding 的接口

为了演示方便,下面的代码以 Spring AI 主项目为基础。

4.2 依赖与配置

在 pom.xml 里引入 Spring AI 的 Ollama starter(用本地模型演示)和 pgvector starter(作为向量存储):

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-ollama</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-pgvector</artifactId> <version>1.0.0</version> </dependency>

配置里指定模型和数据库连接:

spring.ai.ollama.base-url=http://localhost:11434 spring.ai.ollama.chat.model=qwen2.5:7b spring.ai.ollama.embedding.model=nomic-embed-text spring.datasource.url=jdbc:postgresql://localhost:5432/rag_demo spring.datasource.username=postgres spring.datasource.password=postgres

Spring AI 的自动装配会帮你创建 ChatModel、EmbeddingModel 和 VectorStore 这些 Bean,接下来的代码里直接注入就行。

4.3 文档加载、切分与向量化

把 docs 目录下的 Markdown 售后文档读进来,切分成小块,向量化之后存入向量库。这是 RAG 的"入库"阶段,也是决定检索质量的第一步。

@Service public class DocumentIndexService { private final VectorStore vectorStore; public DocumentIndexService(VectorStore vectorStore) { this.vectorStore = vectorStore; } public void indexDocuments(String path) { List<Document> docs = new ArrayList<>(); File dir = new File(path); for (File file : dir.listFiles((d, name) -> name.endsWith(".md"))) { Document doc = new Document(file.toURI().toString(), new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); docs.add(doc); } // 切分:每块约 500 字,块与块之间保留 100 字重叠 TextSplitter splitter = new TokenTextSplitter(500, 100); List<Document> chunks = splitter.apply(docs); vectorStore.add(chunks); } }

切分参数不是拍脑袋定的。块太小,检索到的内容缺乏上下文,模型很难给出完整答案;块太大,检索粒度太粗,可能有大量无关内容混进来,既浪费 token 也稀释注意力。我在实际项目里一般从 400 到 600 开始试,再根据问答效果调整。

调用一次indexDocuments("./docs"),文档就进向量库了。这一步可以在应用启动时执行,也可以做成管理接口手动触发。

4.4 检索问答接口:把相关文档片段喂给模型

用户提问时,先做相似度检索,找出向量库里最相关的几块文档,拼进提示词,再让模型基于这些材料回答。

@RestController public class QaController { private final VectorStore vectorStore; private final ChatClient chatClient; public QaController(VectorStore vectorStore, ChatClient chatClient) { this.vectorStore = vectorStore; this.chatClient = chatClient; } @GetMapping("/qa") public String ask(@RequestParam String question) { List<Document> matches = vectorStore.similaritySearch( SearchRequest.query(question).withTopK(3)); String context = matches.stream() .map(Document::getText) .collect(Collectors.joining("\n---\n")); return chatClient.prompt() .system("你是售后文档助手,只能基于提供的文档内容回答。" + "如果文档中找不到答案,直接说不知道。") .user("相关文档:\n" + context + "\n\n问题:" + question) .call() .content(); } }

到这里,一个最简的 Java RAG 应用已经通了。调用/qa?question=退款流程是什么,系统会先检索到售后手册里关于退款的段落,再让模型根据这些段落组织回答。

4.5 调优的三个旋钮

跑通只是开始,效果好不好要看三个旋钮怎么调。

第一个是检索数量 topK。K 太小,可能漏掉关键内容;K 太大,无关内容会干扰模型。先用 3 到 5 起步,观察回答里有没有出现不相关内容,再决定增减。

第二个是切分尺寸。500 字是一般起点。如果文档本身是按章节组织的,可以优先按标题切分,保持语义完整;如果文档是长段落,再考虑按 token 切分,避免切断核心表述。

第三个是提示词模板。不要只把检索结果和问题简单拼在一起,最好明确告诉模型"只能基于文档回答""不要推理文档之外的内容""如果文档信息不足就承认不知道"。加不加这两句话,回答质量差距非常明显。

5. 避坑清单:这些坑我是用时间换来的

5.1 网络超时:默认超时一定会坑你一次

模型接口的响应时间通常比普通 REST 接口长得多,动辄十几秒、几十秒。Java HTTP 客户端默认连接超时很容易触发,实际开发里要显式设置连接超时和请求超时。更隐蔽的是,就算你设置了 60 秒超时,遇到长文本生成依然可能被中断。

解法是:能流式就不等全量,能用异步就用异步。Spring AI 里流式调用写着很简单:

Flux<String> stream = chatClient.prompt() .user("讲一个关于程序员的故事") .stream() .content();

前端通过 SSE 接收,一边生成一边展示,体验好,也不容易撞上超时墙。

5.2 上下文管理:别把整个聊天记录全塞进去

多轮对话是最容易出问题的场景。很多新手直接把所有历史消息拼进请求,结果聊几轮后 token 数爆掉,模型开始无视你最新的指示,或者直接报错。

正确的做法是:只保留最近几轮的消息,并且把过长的历史做摘要。Spring AI 提供 ChatMemory 抽象,LangChain4j 也有对应的消息窗口实现。自己实现的话,最简单的方式是维护一个最近十轮的滑动窗口,超出部分压缩成一段摘要,再拼进消息列表。

5.3 API Key 安全:正经项目别把 Key 写进代码

本地 Demo 怎么都行,一旦项目要上线,API Key 必须走环境变量或配置中心,绝对不能硬编码进代码或者提交进 Git 仓库。更稳妥的做法是经过一个网关服务统一代理模型调用,由网关集中管控限流、审计和成本,业务侧拿不到真正的 Key。这样既能防泄露,也方便做配额管理。

5.4 结构化输出:别让模型自由发挥

业务系统里经常需要模型输出固定格式的数据,比如"从用户反馈里提取客户名、产品名、情绪倾向"。如果让模型自由输出,你会发现它今天给你 JSON,明天给你 Markdown,后天带上一堆废话。

解法有两个:一是用 Spring AI 的实体映射,让模型直接返回 Java 对象,例如.call().entity(MyRecord.class);二是用 Function Calling 方式,把"提取结构化信息"当成一个工具调用,让模型自己决定用哪个参数去调用。

5.5 检索质量差,先别急着换大模型

RAG 项目效果差,十有八九是检索阶段出了问题,而不是模型能力不够。先检查切分是否合理,再检查检索 score 是否符合预期,然后再看拼进提示词的内容是否和张冠李戴。我见过不少团队花大力气换更强的大模型,结果问题出在向量库数据没清理,检索结果全是过期文档。基础工作做扎实,比迷信模型能力重要得多。

写在最后的一点感受

Spring AI Alibaba 停更这事,我个人的看法是:它提醒我们,选型的时候不要把整个生态的安危押在一个适配项目上。真正值钱的,是你对 AI 应用本身的理解——怎么检索、怎么设计提示词、怎么管好上下文、怎么保证输出可靠。这些能力在任何一个框架里都是通用的,不会因为某个仓库停止更新就贬值。

Java 在 AI 时代的希望,不在一座桥上,而在业务集成的主战场上。模型提供能力,Java 提供秩序。只要企业还在用 Java 写业务系统,Java 开发者做 AI 就有天然的入场券。把手里那套工程能力真正用到 AI 项目里,你会发现路比你想象的宽得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进