Skip to main content

为什么不使用SpringAI或LangChain4j

为什么 Ragent 选择手搓 RAG,而非 Spring AI

很多同学看到项目里没有用 Spring AI,也没有用 LangChain4j,第一反应是——为什么要自己造轮子?

回答这个问题,得先回到做决策的那个时间点。

时间线:Spring AI 刚刚发布 1.0

Ragent 项目启动于 2025 年 6 月。这个时间点很关键——Spring AI 1.0.0 GA 是 2025 年 5 月 20 日发布的,距离 Ragent 开始做只有不到一个月

换句话说,当 Ragent 做技术选型的时候,Spring AI 刚刚拥有自己的第一个正式发行版。

时间节点事件
2023 年中Spring AI 项目启动,开始发布里程碑版本(M1、M2……)
2025 年 5 月 13 日Spring AI 1.0.0-RC1 发布
2025 年 5 月 20 日Spring AI 1.0.0 GA 发布(第一个正式版)
2025 年 6 月Ragent 项目着手启动
2025 年 10 月Spring AI 1.0.3 发布(补丁版)
2025 年 11 月Spring AI 1.1.0 GA 发布

一个框架的 1.0 版本意味着什么?意味着 API 刚刚稳定下来。在此之前的里程碑阶段,Spring AI 的核心 API 经历了多次重构——ChatClient 被完全重新设计过,FunctionCallback 整体更名为 ToolCallback,对话记忆模块也做了架构级调整(InMemoryChatMemory 被删除,拆分为 MessageWindowChatMemory + InMemoryChatMemoryRepository 的双层设计),M7 到 M8 之间还出现过旧 API 静默失效的破坏性变更。1.0 GA 的发布标志着 API 进入稳定期,但一个刚稳定的框架和一个经过生产验证的成熟框架之间,差距不小。

Spring AI 1.0 给了什么

站在 2025 年中这个时间点看,Spring AI 1.0 已经提供了相当完整的 RAG 基础设施:

  • ChatClient:统一的模型调用接口,1.0 GA 时官方宣称可对接 20 个 AI Model 集成
  • QuestionAnswerAdvisor:开箱即用的简易 RAG,查向量库 → 拼进 Prompt → 调模型
  • RetrievalAugmentationAdvisor:模块化 RAG 架构,提供了 QueryTransformerQueryExpanderDocumentRetrieverDocumentPostProcessor 等扩展点
  • ETL Pipeline:文档提取 → 转换 → 加载的管道
  • VectorStore 抽象:支持 Milvus、PGVector、Redis 等 14+ 向量数据库
  • ChatMemory:对话记忆管理
  • Structured Output:模型输出直接映射到 Java 对象

如果你要做一个 Demo,或者做一个查知识库回答问题的简单场景,这些足够了。QuestionAnswerAdvisor 几行代码就能跑起来,体验确实很好。但它更适合快速起步,而不是直接承载高定制、长生命周期的企业项目。

API 变更:升级一次,重构一次

这是我选择手搓最直接的原因之一。

我当时用 Spring AI 1.0 写了一个小的 RAG Demo,功能不复杂。后来升级到 1.0.2(还是 1.0.3 记不太清了),结果代码大面积爆红——不是改几个方法名的事,而是整体的类结构、接口定义、包路径都变了,跟重构没区别。

你不希望自己在公司里做的项目,在未来升级的时候,和重写一遍没什么两样吧。谁都不敢赌这件事,除非你做的项目功能单一,未来不存在大的功能迭代。

1. 对话记忆 API 的大幅重构

这是最典型的案例。Spring AI 的 ChatMemory 相关 API 在版本迭代过程中经历了明显的重构,迁移成本不低。以下变更均可在 官方升级说明 中查证:

  • 核心常量全部重命名:CHAT_MEMORY_CONVERSATION_ID_KEYCONVERSATION_ID(同时从 AbstractChatMemoryAdvisor 移到了 ChatMemory 接口),CHAT_MEMORY_RETRIEVE_SIZE_KEYTOP_KDEFAULT_CHAT_MEMORY_RESPONSE_SIZE(值为 100)→ DEFAULT_TOP_K(值改为 20)
  • AbstractChatMemoryAdvisor 被替换为 BaseChatMemoryAdvisor 接口,所有直接继承它的代码全部失效
  • VectorStoreChatMemoryAdvisor 的构造函数改为 private,强制使用 Builder;chatMemoryRetrieveSize() 方法改名为 defaultTopK()
  • Chat Memory 相关模块的 artifact ID、包名也加上了 repository 后缀(如 spring-ai-autoconfigure-model-chat-memory-cassandraspring-ai-autoconfigure-model-chat-memory-repository-cassandra

之前写的代码长这样:

new MessageChatMemoryAdvisor(new InMemoryChatMemory())

现在推荐的写法变成了:

ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();

你说这个改动合不合理?从设计角度来说,新的 API 确实更清晰。但问题不在于新 API 好不好,而在于——对一个刚进入 GA 的框架来说,这类核心 API 的迁移成本偏高。从团队长期投资的角度看,我当时不愿意把项目绑死在这条演进曲线上。

2. 工具调用 API 的静默失效

还有一个更坑的。M7 到 M8 升级时,tools() 方法被改名为 toolCallbacks(),但旧的 tools() 方法没有报错,也没有标记 @Deprecated——它就是静默地不生效了。你的代码编译通过、运行正常、没有任何异常日志,但工具就是不会被调用。官方升级说明里明确写了这个问题(对应 commit):使用 deprecated tools() 的代码会导致 tool calling silently fail。

这种悄悄坏掉的问题比编译报错还可怕。编译报错至少告诉你哪里需要改,静默失效可能让你排查好几个小时才发现问题出在框架升级上。

Spring AI 团队后来专门出了一份 FunctionCallback 到 ToolCallback 的迁移指南,社区甚至搞了一个 OpenRewrite 自动迁移工具(Arconia Migrations)来帮助开发者批量重构代码。一个框架需要专门的自动化迁移工具来应对版本升级——这本身就说明迁移成本不是小事。

3. 关于 API 稳定性的体感

哪怕是现在的 2.0.0-M2,在 API 稳定性上甚至都不如 Spring Boot 1.5。Spring Boot 从 1.x 到 2.x 是一次大版本升级,改动当然大,但它在小版本之间的兼容性做得非常好。Spring AI 的问题是:小版本之间就能给你来一次较大的破坏性变更。这对生产项目来说太不友好了。

工具调用:从设计到实现都有坑

解锁付费内容,👉 戳