Spring AI 与 LangChain4j 对比

随着大型语言模型(LLM)的崛起,软件工程领域正在经历一场范式转变,从确定性的逻辑编程转向概率性的生成式交互。在 Java 生态系统中,Spring AILangChain4j 已成为连接企业级应用与 AI 模型的主流框架。虽然两者的最终目标一致------即简化 Java 应用与 AI 的集成------但它们源于截然不同的设计哲学,导致了在实现同一功能时存在显著的机制差异 。

1.1Spring AI:便携式服务抽象 (PSA) 与依赖注入

Spring AI 由 Spring 工程团队开发,其核心设计理念是便携式服务抽象(Portable Service Abstraction, PSA)。正如 Spring Data 抽象了 SQL 与 NoSQL 数据库的差异,Spring AI 致力于抽象不同 AI 模型提供商(OpenAI, Azure, Bedrock, Ollama 等)之间的接口差异,使其成为 Spring 生态系统中的标准组件 。

  • 实现机制 :Spring AI 深度依赖 Spring 的控制反转(IoC)容器。核心组件如 ChatModel(聊天模型)、VectorStore(向量存储)和 EmbeddingModel(嵌入模型)均被设计为 Spring Bean。这意味着开发者可以通过修改 application.propertiesapplication.yaml 配置文件来切换底层 AI 提供商,而无需大幅修改代码。例如,通过引入 spring-ai-starter-openai 依赖,框架会自动配置基于 OpenAI 的 ChatModel 实现 。

  • 设计影响 :这种设计使得 Spring AI 非常适合已经在使用 Spring Boot 的企业级项目。它利用了 Spring 的资源抽象(Resource)来加载提示词模板,利用 Spring 的事件机制进行错误处理,并利用 Spring Boot Actuator 进行可观测性集成。然而,这也意味着 Spring AI 具有较强的框架侵入性,且在非 Spring 环境下使用较为繁琐 。

1.2LangChain4j:统一 API 与模块化代理

LangChain4j 虽然深受 Python 版 LangChain 的启发,但并非简单的移植,而是针对 Java 语言特性进行了重新设计。其核心理念是提供一个统一且模块化的 API,旨在成为 Java 领域的 LLM 标准库,且不强制绑定任何特定的应用框架(如 Spring 或 Quarkus) 。

  • 实现机制 :LangChain4j 采用了显式的构建者模式(Builder Pattern) 。几乎所有的核心组件(如 OpenAiChatModelEmbeddingStoreIngestor)都通过流畅的 API 进行实例化和配置。这种设计使得依赖关系显式化,不依赖于自动装配的魔法 。

  • 声明式服务 :LangChain4j 最具创新性的特性是**AiServices** ,这是一种基于 Java 动态代理的高级抽象。开发者只需定义一个 Java 接口 ,框架便会在运行时自动生成该接口的实现,负责编排提示词构建、模型调用、RAG 检索、记忆管理及结果解析。这种"代码优先"的方法极大地降低了样板代码的数量,提供了类似 Python 的开发效率,同时保留了 Java 的类型安全 。

特性维度 Spring AI LangChain4j
核心哲学 Spring 生态原生,强调配置与 Bean 管理 库无关性(Library-Agnostic),强调 API 统一与模块化
实例化方式 依赖注入 (DI) 与自动配置 (Auto-configuration) 显式构建者模式 (Builder Pattern) 或 工厂方法
抽象层次 侧重于底层的客户端抽象 (ChatModel) 提供分层抽象:底层 (ChatLanguageModel) + 高层 (AiServices)
运行时依赖 强依赖 Spring Framework / Spring Boot 无强制依赖,可独立运行于 Java SE,亦有 Quarkus/Spring 扩展

2. 核心功能对比一:聊天客户端与模型交互

最基础的 AI 功能是向模型发送文本并接收响应。尽管看似简单,两个框架在 API 设计和请求处理流程上展现了完全不同的思路。

2.1 Spring AI 的实现:流式接口与 Advisor 模式

Spring AI 的交互核心围绕 ChatModel 接口展开,并在近期版本中引入了更高层的 ChatClient 流式 API。

2.1.1 接口定义与请求封装

在 Spring AI 中,交互的基本单元是 Prompt 对象

  • 请求对象Prompt 封装了 Message 列表(包含 UserMessage, SystemMessage 等)和 ChatOptions。这种设计将"内容"与"配置"严格分离。ChatOptions 利用 PSA 模式,既提供了跨平台的通用选项(如 temperature),也允许访问特定提供商的参数 。

  • 响应对象ChatResponse 包含了 Generation 列表(模型的候选回复)以及 ChatResponseMetadata(元数据,如 Token 使用量)。

2.1.2 ChatClient Builder 与 Advisor 链

Spring AI 推荐使用 ChatClient 进行交互,它模仿了 WebClientRestClient 的设计风格。

Java

java 复制代码
// Spring AI ChatClient 示例
String response = chatClient.prompt()
   .system("你是一个乐于助人的助手")
   .user(u -> u.text("给我讲一个关于{topic}的笑话").param("topic", "Java"))
   .advisors(new MessageChatMemoryAdvisor(chatMemory)) // 挂载记忆增强器
   .call()
   .content();
  • 实现细节ChatClient 实际上是一个请求构建器。关键的架构创新在于 .advisors() 方法。Spring AI 采用了责任链模式(Chain of Responsibility) ,将日志记录、对话记忆(Chat Memory)、RAG 检索等功能封装为 Advisor。当调用 .call() 时,请求会依次通过这些 Advisor,每个 Advisor 都有机会修改 Prompt(例如将历史记录填充进 Prompt)或处理 Response。这种拦截器机制是典型的 Spring 设计风格,具有极高的扩展性 。

  • 流式处理 :Spring AI 原生支持响应式编程,其流式接口返回**Flux<ChatResponse>**,利用 Project Reactor 实现非阻塞 I/O。这对于构建高并发的 AI 网关至关重要 。

3.2 LangChain4j 的实现:双层抽象与动态代理

LangChain4j 提供了两层 API:底层的 ChatLanguageModel 和高层的 AiServices

3.2.1 底层模型接口
  • 接口定义ChatLanguageModel 提供了 generate(String userMessage)generate(List<ChatMessage> messages) 等重载方法。

  • 响应对象 :返回 Response<AiMessage>。LangChain4j 的 AiMessage 代表 AI 的回复,它是 ChatMessage 接口的一个实现。

  • 流式处理 :LangChain4j 使用 StreamingChatLanguageModel 接口,它依赖于回调机制(StreamingResponseHandler),包含 onNext(String token), onError(Throwable t) 等方法。这种设计不绑定特定的反应式库(如 Reactor 或 RxJava),保持了核心库的轻量级 。

3.2.2 AiServices:声明式 AI 服务

这是 LangChain4j 与 Spring AI 最大的体验差异点。

Java

java 复制代码
// LangChain4j AiServices 示例
interface Assistant {
    @SystemMessage("你是一个乐于助人的助手")
    String chat(@UserMessage String message);
}

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

String response = assistant.chat("你好");
  • 实现细节AiServices.builder() 使用 Java 的 动态代理(Dynamic Proxy) 技术。在运行时,框架会拦截 assistant.chat() 方法的调用,解析方法参数和注解,构建 ChatMessage 列表,自动从 ChatMemory 中拉取历史记录,调用底层的 ChatLanguageModel,最后将结果解析为方法定义的返回类型(String 或 POJO)。

  • 优势:这种"魔法"完全隐藏了底层的 Prompt 构建过程,使得业务代码极其整洁,非常接近于传统的 RPC 调用体验 。

3.3 差异总结

  • 控制权 :Spring AI 的 ChatClient 提供了更细粒度的控制,开发者显式地构建请求链;LangChain4j 的 AiServices 提供了更高层次的抽象,隐藏了细节。

  • 扩展性 :Spring AI 通过 Advisor 链扩展功能;LangChain4j 通过 AiServices 构建器中的组件(如 ContentRetriever, ChatMemory)组合功能。

检索增强生成 (RAG) 实现

RAG 是将私有数据注入模型上下文的关键技术。两个框架在 RAG 的实现上都采用了模块化设计,但在数据流转的抽象上有所不同。

4.1 数据摄取 (Ingestion) 流程

数据从源头进入向量数据库的过程,通常被称为 ETL(Extract, Transform, Load)。

Spring AI:ETL 管道模式 Spring AI 借用了 Spring Batch 或 Spring Integration 的术语。

  • 提取 (Extract) :使用 DocumentReader 接口。提供了 JsonReader, TikaDocumentReader (用于 PDF/Word), TextReader 等实现。开发者通常通过 reader.get() 获取 List<Document>

  • 转换 (Transform) :使用 DocumentTransformer 接口。最常见的实现是 TokenTextSplitter,用于将长文档切分为片段。

  • 加载 (Load)VectorStore 接口本身充当 Writer。调用 vectorStore.add(documents) 完成写入。

  • 实现特点:Spring AI 将这三个步骤视为独立的组件,开发者需要手动编写代码将它们串联起来,或者利用 Spring Cloud Function 进行编排。

LangChain4j:一体化摄取器 LangChain4j 提供了一个封装度更高的组件 EmbeddingStoreIngestor

  • 组件构成 :它内部封装了 DocumentLoader (加载), DocumentParser (解析), DocumentSplitter (切分), 和 EmbeddingModel (嵌入)。

  • 递归切分 :LangChain4j 的 DocumentSplitters.recursive(...) 是一个非常强大的切分器,它尝试根据语义结构(段落、换行、句子)递归地切分文本,这在处理代码或 Markdown 时比简单的字符切分更有效 。

  • 流程控制 :开发者只需配置好 Ingestor,然后调用 ingest(document),框架会自动处理切分、嵌入计算和存储的全部流程。

4.2 检索与增强 (Retrieval & Augmentation)

在运行时,如何根据用户查询检索数据并注入 Prompt。

Spring AI:Advisor 拦截机制

  • 核心组件QuestionAnswerAdvisor

  • 工作流 :这是一个 ChatClient 的 Advisor。当请求发起时,Advisor 拦截用户消息,调用 VectorStore.similaritySearch() 进行检索。检索到的文档被填充到 System Prompt 的特定占位符中(Prompt Stuffing)。

  • 过滤器 :Spring AI 提供了一套强大的 Filter Expression DSL (如 builder.eq("genre", "tech").and(...))。这套 DSL 是跨数据库移植的,Spring AI 负责将其翻译为特定向量数据库(如 PGVector, Milvus, Redis)的查询语法。这是 Spring AI PSA 哲学的典型体现 。

LangChain4j:检索增强器架构

  • 核心组件RetrievalAugmentor 接口及其默认实现 DefaultRetrievalAugmentor

  • 模块化设计:LangChain4j 将检索过程细分为:

    • QueryRouter(查询路由):决定查询哪个存储,或是否需要分发到多个存储 。

    • QueryTransformer(查询转换):优化查询,例如压缩对话历史或扩展查询词。

    • ContentRetriever(内容检索):执行实际的检索。

    • ContentInjector(内容注入):决定如何将内容插入 Prompt。

  • 高级 RAG :得益于这种细粒度的模块化,LangChain4j 原生支持许多高级 RAG 模式,如查询路由 (Query Routing)重排序 (Re-ranking) 等,且配置相对简单 。

功能对比四:工具调用 (Function Calling)

工具调用允许 LLM 请求执行 Java 代码。这是构建 Agent 的基础。

6.1 Spring AI:基于 Bean 的注册与 @Tool

Spring AI 最初依赖于标准的 Spring Bean 注册机制。

  • 实现方式 1(传统) :定义一个返回 java.util.function.Function 的 Bean。

    Java

    java 复制代码
    @Bean
    @Description("查询某地的天气")
    public Function<WeatherRequest, WeatherResponse> weatherService() {
        return request ->...;
    }

    这种方式利用了 Java 8 的函数式接口,类型安全,但每个工具需要一个独立的类或 Bean 定义。

  • 实现方式 2(新版 @Tool) :为了简化开发,Spring AI 引入了 @Tool 注解。开发者可以在 Service 的方法上添加此注解。框架通过 ToolCallbackResolver 在 Spring 上下文中扫描并注册这些方法。尽管如此,它仍然深度依赖 Spring Context 来管理生命周期 。

  • 调用流程:模型返回 tool call 指令 -> Spring AI 解析 JSON -> 查找对应的 Function Bean -> 执行 -> 将结果序列化回 JSON -> 发送回模型。

6.2 LangChain4j:灵活的 @Tool 与动态执行

LangChain4j 从一开始就采用了基于注解的方法,且更加灵活。

  • 实现方式 :在任何 Java 对象的任何方法上添加 @Tool 注解。

    Java

    java 复制代码
    @Tool("查询 {{location}} 的天气")
    public String getWeather(String location) {... }
  • 参数映射 :LangChain4j 的 ToolExecutor 能够处理更复杂的参数签名。它不强制要求单一的 Request 对象,而是可以直接映射多个参数(如 int id, String name)。这使得直接复用现有的业务方法变得更加容易,无需专门编写 Wrapper 类 。

  • 动态工具 :LangChain4j 支持 ToolProvider 接口,允许在运行时动态提供工具(例如根据当前用户的权限动态决定其可以使用哪些工具)。这对于构建多租户或权限敏感的 Agent 系统非常关键 。

  • 执行与错误控制 :在 AiServices 中,如果工具执行失败,LangChain4j 允许配置特定的策略(如将异常信息作为结果返回给 LLM 让其重试,或者直接抛出异常中断对话),提供了更细粒度的控制力 。

三大功能对比总结

第一部分:聊天客户端与模型交互 (Chat Client & Model Interaction)

"关于如何与 AI 模型对话,这两个框架的思路完全不同,可以概括为'显式构建 '与'声明式代理'的区别。

首先是 Spring AI 。它的交互方式非常像我们在写 Web 请求。就像我们用 WebClient 调用 API 一样,你需要显式地创建一个 ChatClient ,然后链式 地把系统提示词、用户消息、参数 一个一个拼上去,最后调用 call() 方法拿到结果。它的特点是过程非常透明,你控制着请求的每一个细节 ,而且它通过一种叫 Advisor 的拦截器机制 ,允许你在请求发出 或响应回来插入逻辑,比如加个日志或者把历史记录塞进去。这对于习惯 Spring 开发的同学来说,感觉非常亲切和可控。

Spring AI:显式构建 + 链式调用(你手动拼 Prompt,然后 call())

java 复制代码
// Spring AI(显式 ChatClient)
// 典型:ChatClient.builder(chatModel).build(); 然后链式 prompt().system().user().call()

ChatClient chatClient = ChatClient.builder(chatModel).build();

String result = chatClient.prompt()
        .system("你是一个严谨的助理,用中文回答。")
        .user("一句话解释什么是 RAG?")
        .call()
        .content();

System.out.println(result);

反观 LangChain4j ,它的核心体验是'声明式服务 ',也就是所谓的**AiServices** 。你只需要定义一个普通的 Java 接口 ,写一个类似 String chat(String msg) 的方法,框架就会利用 Java 动态代理技术,在运行时自动帮你生成这个接口的实现。你调用这个接口就像调用本地方法一样简单,底层的 Prompt 构建、参数传递全都被框架隐藏了。这种方式开发效率极高,代码看起来非常干净,有点像是在用 Feign Client 调用远程服务。"

LangChain4j:声明式接口 + 动态代理(像调用本地方法一样调用 AI)

java 复制代码
// LangChain4j(AiServices 代理接口)
// 你定义接口,框架生成实现:assistant.chat("...")

interface Assistant {
    String chat(String message);
}

Assistant assistant = AiServices.builder(Assistant.class)
        .chatLanguageModel(chatModel)  // 你的 ChatLanguageModel
        .build();

System.out.println(assistant.chat("一句话解释什么是 RAG?"));

第二部分:检索增强生成 (RAG 实现)

"在让 AI 读取私有数据的 RAG 环节,两者的区别在于'数据管道的组装方式'。

Spring AI 把 RAG 的数据处理看作是一个标准的 ETL 流程 (也就是提取、转换、加载)。你需要分别创建'文档读取器 '、'文本切分器 '和'向量写入器 ',然后自己写代码或者用 Spring Cloud Function 把这三步串联起来。它的好处是模块化程度高,每一步你都可以换成自己的实现,非常适合需要精细控制数据处理流程的企业级场景。

java 复制代码
// Spring AI RAG(最小版)

// ========== 1) 摄取:读文档 -> 切分 -> 写入向量库 ==========
TikaDocumentReader reader =
        new TikaDocumentReader(new FileSystemResource("docs/manual.pdf"));

List<Document> docs = reader.get();

// Token 切分(你也可以换成自己的 splitter/transformer)
TokenTextSplitter splitter = new TokenTextSplitter();
vectorStore.add(splitter.apply(docs));   // 写入向量库(embedding + store)

// ========== 2) 查询:Advisor 拦截,自动 similaritySearch 并注入上下文 ==========
ChatClient ragClient = ChatClient.builder(chatModel)
        .defaultAdvisors(
                QuestionAnswerAdvisor.builder(vectorStore)
                        .searchRequest(SearchRequest.builder().topK(4).build())
                        .build()
        )
        .build();

String answer = ragClient.prompt()
        .user("根据文档,退货政策是什么?")
        .call()
        .content();

System.out.println(answer);

LangChain4j 则提供了一种更'开箱即用 '的体验。它封装了一个叫 Ingestor (摄取器)的组件,你把读取、切分、嵌入模型配给它 ,一行代码就能完成从文档到向量数据库的全部工作。而在检索时,LangChain4j 的设计更加先进,它原生支持'查询路由 '和'重排序'等高级 RAG 模式。简单来说,Spring AI 让你自己搭积木,而 LangChain4j 直接给了一套功能强大的组合拳,特别是在处理复杂检索逻辑时,LangChain4j 会显得更得心应手。"

java 复制代码
// LangChain4j RAG(最小版:摄取 + 检索增强器)

// ========== 1) 摄取:Ingestor 自动切分/嵌入/写入 ==========
List<Document> documents =
        FileSystemDocumentLoader.loadDocumentsRecursively(Path.of("docs"));

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
        .embeddingStore(embeddingStore)
        .embeddingModel(embeddingModel)
        .documentSplitter(DocumentSplitters.recursive(500, 0)) // 递归切分
        .build();

ingestor.ingest(documents);

// ========== 2) 查询:RetrievalAugmentor = retriever + inject ==========
EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
        .embeddingModel(embeddingModel)
        .embeddingStore(embeddingStore)
        .maxResults(3)
        .build();

RetrievalAugmentor augmentor = DefaultRetrievalAugmentor.builder()
        .contentRetriever(retriever)
        .build();

// ========== 3) 把 augmentor 接到 AiService(不同集成方式略有差异)==========
// 方式A(概念演示,纯 LangChain4j 场景):
// Assistant assistant = AiServices.builder(Assistant.class)
//        .chatLanguageModel(chatModel)
//        .retrievalAugmentor(augmentor) // 具体方法名以你版本为准/或由框架集成提供
//        .build();

// 方式B(Quarkus 场景):用 @RegisterAiService(retrievalAugmentor = Xxx.class) 自动接入(文档推荐)
// 这里不展开注解代码了,核心就是:你把 augmentor 作为 CDI Bean 提供给 AI service。

第三部分:工具调用 (Function Calling)

"最后是关于让 AI 执行 Java 代码的工具调用功能,这里的核心差异是'依赖注入 '与'灵活映射'。

Spring AI 的实现严格遵循 Spring 的哲学,也就是基于 Bean 的管理 。你通常需要把你的工具函数注册成 Spring 容器里的 Bean(比如一个 Function 类型的 Bean)。虽然现在也支持了 @Tool 注解,但它本质上还是依赖 Spring 上下文来发现和管理这些工具。这种做法安全性高,生命周期管理规范,但有时候为了写一个小工具,得配置不少样板代码。

java 复制代码
// Spring AI 工具:方法上加 @Tool,然后注册到 ChatClient.tools(...)

class WeatherTools {

    @Tool(description = "查询某城市天气,返回简短文本")
    public String getWeather(String city, ToolContext ctx) {
        // ctx 里的信息可以只给工具用(比如 tenantId / userId),不发给模型
        Object tenantId = ctx.getContext().get("tenantId");
        return "城市=" + city + ", tenant=" + tenantId + ", 天气=晴 25℃";
    }
}

ChatClient toolClient = ChatClient.builder(chatModel).build();

String resp = toolClient.prompt()
        .user("帮我查一下北京天气")
        .tools(new WeatherTools())
        .toolContext(Map.of("tenantId", "acme")) // 给工具的上下文
        .call()
        .content();

System.out.println(resp);

LangChain4j 在这方面则灵活得多。它允许你在任何 Java 对象的任何方法直接加 @Tool 注解,不需要这个对象非得是 Spring Bean。更厉害的是,它对参数的映射非常智能,哪怕你的方法有多个参数,它也能自动把 AI 返回的 JSON 拆解并填进去。甚至,它还支持在运行时动态地给 AI 提供工具列表(比如根据不同用户的权限提供不同工具)。所以在构建复杂的、需要灵活调用工具的 AI Agent 时,LangChain4j 会让你感觉更自由。"

java 复制代码
// LangChain4j 工具:任何对象的任何方法都能 @Tool,然后 AiServices 注册

class WeatherTools {

    @Tool("查询 {city} 的天气")
    public String getWeather(String city) {
        return city + ":晴 25℃";
    }

    // 多参数也可以(框架会把模型生成的 JSON 自动映射进来)
    @Tool("把两个整数相加")
    public int add(int a, int b) {
        return a + b;
    }
}

interface Assistant {
    String chat(String message);
}

Assistant assistant = AiServices.builder(Assistant.class)
        .chatLanguageModel(chatModel)
        .tools(new WeatherTools())
        .build();

System.out.println(assistant.chat("北京天气怎么样?顺便算一下 7+5"));
相关推荐
杜子不疼.11 小时前
计算机视觉热门模型手册:Spring Boot 3.2 自动装配新机制:@AutoConfiguration 使用指南
人工智能·spring boot·计算机视觉
无心水13 小时前
【分布式利器:腾讯TSF】7、TSF高级部署策略全解析:蓝绿/灰度发布落地+Jenkins CI/CD集成(Java微服务实战)
java·人工智能·分布式·ci/cd·微服务·jenkins·腾讯tsf
北辰alk18 小时前
RAG索引流程详解:如何高效解析文档构建知识库
人工智能
九河云18 小时前
海上风电“AI偏航对风”:把发电量提升2.1%,单台年增30万度
大数据·人工智能·数字化转型
wm104318 小时前
机器学习第二讲 KNN算法
人工智能·算法·机器学习
沈询-阿里19 小时前
Skills vs MCP:竞合关系还是互补?深入解析Function Calling、MCP和Skills的本质差异
人工智能·ai·agent·ai编程
xiaobai17819 小时前
测试工程师入门AI技术 - 前序:跨越焦虑,从优势出发开启学习之旅
人工智能·学习
盛世宏博北京19 小时前
云边协同・跨系统联动:智慧档案馆建设与功能落地
大数据·人工智能
TGITCIC19 小时前
讲透知识图谱Neo4j在构建Agent时到底怎么用(二)
人工智能·知识图谱·neo4j·ai agent·ai智能体·大模型落地·graphrag