Spring AI vs LangChain4j:Java 后端接大模型,两条路线怎么选

Java 世界里接入大模型,现在有两个正经选择:Spring 官方的 Spring AI,和社区驱动的 LangChain4j。前者 8795 star,后者 12113 star。数字差不多,但设计哲学完全两回事。
这篇文章不讲 Hello World,从核心抽象、记忆管理、RAG、Function Calling 几个维度掰开看,帮你搞清楚什么时候用哪个。


目录


一、为什么 Spring 要自己做 AI?

LangChain4j 比 Spring AI 早出来不少,Star 数也更高。那 Spring 为什么还要单开一局?

三个原因。

第一,Spring 生态有自己的做事方式。 Spring 开发者习惯了依赖注入、自动配置、Actuator 端点、Micrometer 指标。LangChain4j 虽然也能集成 Spring Boot,但它不是 Spring 原生设计的,集成的"Spring 味"始终差一点。

第二,Spring AI 想覆盖的不止是 LLM 调用。 它的范围更接近"AI 工程化的 Spring 答案":模型接入、向量存储、ETL 管道、MCP 协议支持、可观测性——这些在 LangChain4j 里要么没有,要么靠社区补。

第三,企业级需求。 Spring 在企业市场根深蒂固。Spring AI 自带 Actuator health check、Micrometer 埋点、AOT 编译支持,这些东西 LangChain4j 做起来要额外踩坑。

一句话:LangChain4j 是"让 Java 能用 LangChain",Spring AI 是"让 Spring 开发者用 AI 跟用数据库一样自然"。 定位不同。


二、核心抽象:ChatClient vs AiServices

这是两个框架最根本的分歧点。

Spring AI:ChatClient

Spring AI 的核心入口是 ChatClient,一个 fluent builder 风格的 Bean。用法跟 RestClient 和 JdbcClient 一脉相承:

@Bean
ChatClient chatClient(ChatModel chatModel) {
    return ChatClient.builder(chatModel).build();
}

// 使用
String answer = chatClient.prompt()
    .user("这个项目用什么构建工具?")
    .call()
    .content();

思路很 Spring:你拿到一个注入好的 Bean,链式调用,请求返回。Advisors(类似拦截器)在调用链路中插入逻辑:

chatClient.prompt()
    .user("这个项目用什么构建工具?")
    .advisors(new MessageChatMemoryAdvisor(chatMemory))
    .call()
    .content();

你的代码里自始至终只有一个对象:ChatClient。记忆、RAG、日志这些横切关注点通过 Advisors() 链上去。接触过 Spring AOP 的人一秒就能理解。

LangChain4j:AiServices

LangChain4j 的核心入口是 AiServices,走接口代理路线。你声明一个接口,框架给你生成实现:

interface ProjectAssistant {
    @SystemMessage("你是项目技术顾问,回答基于项目实际配置")
    String chat(String userMessage);
}

ProjectAssistant assistant = AiServices.builder(ProjectAssistant.class)
    .chatLanguageModel(model)
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .build();

String answer = assistant.chat("这个项目用什么构建工具?");

你围绕业务语义组织代码:ProjectAssistant、CodeReviewer、DocWriter,每个接口代表一个角色。LangChain4j 在背后生成代理对象,把记忆、工具调用都绑在接口上。

核心差异

维度 Spring AI ChatClient LangChain4j AiServices
编程模型 Fluent Builder + Bean 注入 接口声明 + 动态代理
组织方式 围绕调用链组织代码 围绕业务角色组织接口
横切关注点 Advisors 链(类 AOP) 绑定在 Builder 上
学习曲线 Spring 开发者零门槛 需要适应接口代理思维
灵活性 每次调用都可以动态组合 Advisors 接口定义时就绑定了行为

选哪个更多是口味问题。习惯 Spring 的 xxxClient 系列就用 ChatClient,喜欢按业务角色拆分接口就用 AiServices。


三、记忆管理:Advisors vs ChatMemory

对话记忆是 AI 应用里最容易做砸的地方。两个框架给出了完全不同的解法。

Spring AI:Advisors 模式

Spring AI 把记忆当作一个横切关注点,通过 Advisors 注入:

// 基于消息数量的窗口记忆
ChatMemory chatMemory = new InMemoryChatMemory();
MessageChatMemoryAdvisor memoryAdvisor =
    new MessageChatMemoryAdvisor(chatMemory);

// 基于 Token 数量的窗口记忆
TokenStreamChatMemory tokenMemory =
    new TokenStreamChatMemory(4096);
MessageChatMemoryAdvisor tokenAdvisor =
    new MessageChatMemoryAdvisor(tokenMemory);

chatClient.prompt()
    .user("帮我查一下昨天的订单")
    .advisors(memoryAdvisor)  // 记忆作为一个 Advisor 插入
    .call()
    .content();

Advisors 是按顺序执行的。你可以链上多个:

chatClient.prompt()
    .user("帮我查一下昨天的订单")
    .advisors(memoryAdvisor, loggingAdvisor, ragAdvisor)
    .call()
    .content();

顺序敏感,排在前面的先执行。这个设计的优势是组合自由:记忆、RAG、日志、权限检查可以任意排列,每个 Advisors 职责单一。

LangChain4j:ChatMemory 接口

LangChain4j 把记忆绑在 AiServices Builder 上,或者手动管理:

// 方式一:绑在 AiServices 上
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);

ProjectAssistant assistant = AiServices.builder(ProjectAssistant.class)
    .chatLanguageModel(model)
    .chatMemory(memory)
    .build();

// 方式二:手动管理
List<ChatMessage> history = new ArrayList<>();
history.add(SystemMessage.from("你是项目技术顾问"));
history.add(UserMessage.from("这个项目用什么构建工具?"));
history.add(AiMessage.from("用 Maven"));
history.add(UserMessage.from("为什么不是 Gradle?"));

Response<AiMessage> response = model.generate(history);

LangChain4j 提供了多种记忆实现:

// 按消息数量截断
MessageWindowChatMemory.withMaxMessages(10);

// 按 Token 数量截断
TokenWindowChatMemory.withMaxTokens(2000, new OpenAiTokenizer());

差异点

Spring AI 的 Advisors 模式给了你一个插拔式的记忆管道,记忆只是管道中的一个切面。LangChain4j 把记忆作为 ChatModel 调用的一部分,更直接但组合性稍弱。

如果说 Spring AI 的记忆像 Spring Security 的 Filter Chain,LangChain4j 的记忆更像一个带截断的消息列表。


四、RAG 方案对比

RAG 是 AI 应用里最实用的模式:把私有文档喂给 AI,让它基于你的数据回答问题。两个框架都有完整方案,但实现路径不同。

Spring AI:ETL 管道

Spring AI 把文档处理拆成标准的 ETL 三步:

DocumentReader → DocumentTransformer → DocumentWriter
// 1. 读取文档
DocumentReader reader = new JsonReader(fileSystemResourceLoader);
List<Document> documents = reader.read();

// 2. 切分文档
DocumentTransformer splitter =
    new TokenTextSplitter(800, 100, 5, 10000, true);
List<Document> chunks = splitter.transform(documents);

// 3. 写入向量数据库
VectorStore vectorStore =
    new PgVectorStore(dataSource, new JdbcTemplate(dataSource));
vectorStore.add(chunks);

查询时用 QuestionAnswerAdvisor:

chatClient.prompt()
    .user("Spring AI 怎么配置向量数据库?")
    .advisors(new QuestionAnswerAdvisor(vectorStore))
    .call()
    .content();

优点是把 ETL 拆成独立步骤,替换任意一环都很方便。换一个文档格式?只换 Reader。换一种切分策略?只换 Transformer。换一个向量数据库?只换 VectorStore。

LangChain4j:EmbeddingStore + ContentRetriever

LangChain4j 没有显式的 ETL 管线概念,而是通过组件拼装:

// 文档摄入
EmbeddingStore<TextSegment> embeddingStore =
    new InMemoryEmbeddingStore<>();

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
    .documentSplitter(DocumentSplitters.recursive(800, 100))
    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();

ingestor.ingest(document);

// 检索
ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
    .embeddingStore(embeddingStore)
    .embeddingModel(embeddingModel)
    .maxResults(5)
    .minScore(0.75)
    .build();

// 连上 AiServices
ProjectAssistant assistant = AiServices.builder(ProjectAssistant.class)
    .chatLanguageModel(model)
    .contentRetriever(retriever)
    .build();

如果你不想手动拼,LangChain4j 还有 EasyRag:

EasyRag easyRag = EasyRag.builder()
    .documentsPath("/data/docs")
    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .chatLanguageModel(model)
    .build();

// 直接问
String answer = easyRag.chat("Spring AI 怎么配置向量数据库?");

差异

维度 Spring AI LangChain4j
文档处理 显式 ETL 管线 (Reader/Transformer/Writer) 内聚的 Ingestor
检索接入 Advisors 注入 AiServices Builder 绑定 / EasyRag
替换灵活性 每步独立 Bean,替换方便 组件粒度高,替换也不难
最简路径 需要配三步 EasyRag 一行配置

Spring AI 的 ETL 模型更适合对文档处理流程有精细控制的场景。LangChain4j 的 EasyRag 适合快速原型。


五、Function Calling:AI 怎么调用你的代码

让 AI 调用你的业务代码,而不是光聊天。这是 Agent 的入口。两个框架都支持,写法也很像,但背后的注册机制不同。

Spring AI:Bean 方法注册

定义 @Tool,注入 ChatClient,自动发现:

@Component
public class OrderTools {

    @Tool(description = "根据订单号查询订单状态")
    public OrderStatus getOrderStatus(
            @ToolParam(description = "订单号") String orderId) {
        return orderService.findStatus(orderId);
    }

    @Tool(description = "取消指定订单")
    public String cancelOrder(
            @ToolParam(description = "订单号") String orderId) {
        orderService.cancel(orderId);
        return "已取消";
    }
}

// ChatClient 自动发现所有 @Tool Bean
chatClient.prompt()
    .user("帮我查一下 ORD-2024001 的状态")
    .tools(orderTools)  // 指定工具 Bean
    .call()
    .content();

AI 模型收到用户问题后,判断需要调用 getOrderStatus,提取参数 ORD-2024001,Spring AI 帮你调用 Bean 方法,把返回结果送回模型生成最终回复。

LangChain4j:接口方法上的 @Tool

class OrderTools {

    @Tool("根据订单号查询订单状态")
    public OrderStatus getOrderStatus(
            @P("订单号") String orderId) {
        return orderService.findStatus(orderId);
    }

    @Tool("取消指定订单")
    public String cancelOrder(@P("订单号") String orderId) {
        orderService.cancel(orderId);
        return "已取消";
    }
}

// 绑定到 AiServices
ProjectAssistant assistant = AiServices.builder(ProjectAssistant.class)
    .chatLanguageModel(model)
    .tools(new OrderTools())
    .build();

String result = assistant.chat("帮我查一下 ORD-2024001 的状态");

差异点

维度 Spring AI LangChain4j
工具注册 Spring Bean 自动发现 手动传入 tools()
参数注解 @ToolParam @P
执行模型 框架自动调用 Bean 框架反射调用对象方法
与 DI 集成 天然集成,工具可以是注入的 Bean 需手动传实例

这个维度差距不大。Spring AI 对 Spring 开发者更顺手(工具就是 Bean),LangChain4j 对非 Spring 场景更友好(不依赖 DI 容器)。


六、生态与可观测性

Spring AI:Spring 全家桶

Spring AI 的最大优势不在 AI 功能本身,在它跟 Spring 生态的无缝衔接:

# 一行配置接入模型
spring:
  ai:
    openai:
      api-key: 
      chat:
        options:
          model: gpt-4o

自动配置好了。要监控?

management:
  endpoints:
    web:
      exposure:
        include: health,metrics

Spring AI 的 ChatClient 调用自动接入 Micrometer,token 消耗、调用延迟、错误率直接出现在 Grafana 面板上。AOT 编译支持意味着可以编译成 GraalVM native image,启动速度从秒级压到毫秒级。

MCP(Model Context Protocol)支持也是 Spring AI 先做的。把一个外部 MCP Server 注册成 Spring Bean,ChatClient 直接调用它的工具,不用写一行胶水代码。

LangChain4j:社区驱动

LangChain4j 支持的模型厂商更多(30+),包括一些 Spring AI 还没覆盖的国内模型。Quarkus 和 Micronaut 集成也是 LangChain4j 的优势区。

但可观测性弱一些。Micrometer 集成存在但不成熟,没有原生的 AOT 支持,Actuator 端点需要自己实现。

维度 Spring AI LangChain4j
模型接入数 20+ (OpenAI、Azure、Ollama…) 30+ (覆盖面更广)
Spring Boot 集成 原生 (Starter + AutoConfig) 通过 langchain4j-spring-boot-starter
可观测性 原生 Micrometer + Actuator 有集成但不够成熟
AOT / Native Image ✅ 支持 ❌ 暂无
MCP 协议 ✅ 原生支持 ⚠️ 社区插件
其他框架 仅 Spring Quarkus、Micronaut

七、选型建议

没标准答案,看场景。

选 Spring AI 的情况:

  • 项目已经在 Spring Boot 生态里
  • 团队习惯了 Spring 那套 DI + AutoConfig 的开发方式
  • 需要 Actuator 监控、Micrometer 指标、GraalVM native image
  • 想做 MCP 集成(让 AI 调用外部工具服务)
  • 需要细粒度控制文档 ETL 管道

选 LangChain4j 的情况:

  • 项目不是 Spring 生态(Quarkus、Micronaut、纯 Java)
  • 需要接的模型 Spring AI 还没支持
  • 团队偏好 AiServices 的接口代理风格
  • 想做快速原型,EasyRag 一行配置搞定 RAG
  • 看重社区活跃度和模型覆盖面

两个一起用:

技术上完全可行。LangChain4j 接入模型 + Spring AI 做可观测和部署,或者 Spring AI 做核心链路 + LangChain4j 补缺失的模型适配器。但增加了版本管理和调试成本,不推荐一开始就这么搞。

我个人倾向于:Spring 项目用 Spring AI,非 Spring 项目用 LangChain4j。 简单粗暴,但管用。


参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐