大家好我是专注于Java后端与AI应用落地的技术博主。最近在规划一个智能航空客服项目时面对琳琅满目的AI技术栈从Spring AI、LangChain4j到各种Agent框架着实经历了一番选型纠结。网上资料要么是概念科普要么是单一框架的“Hello World”缺乏一个从Java开发者视角出发、贯穿业务场景的完整技术选型与实战指南。本文正是为了解决这个问题。我将以“智能航空客服”为业务背景系统性地拆解JavaAI技术栈的选型逻辑并手把手带你完成一个融合了RAG、AI Agent和Spring AI Alibaba的核心模块实战。无论你是想将AI能力集成到现有Java系统还是从零开始构建AI应用这篇文章都能为你提供清晰的路径和可复用的代码。1. 背景与核心概念为什么Java开发者需要关注AI技术栈在传统的Java开发领域我们擅长处理高并发、复杂业务逻辑和稳定的事务。然而当业务需求扩展到智能问答、内容生成、意图理解时传统的规则引擎和模板匹配就显得力不从心。大语言模型LLM的出现为这些场景提供了新的解决方案。对于Java团队而言直接调用OpenAI等云端API是最快的方式但会带来数据安全、网络延迟、成本不可控和模型能力固化等问题。因此一套能够灵活集成、支持本地模型、且符合Java工程化规范的技术栈变得至关重要。核心概念解析Spring AI Spring官方推出的AI应用开发框架。它抽象了不同AI提供商如OpenAI、Azure OpenAI、Ollama的接口让开发者通过统一的ChatClient、EmbeddingClient等与AI模型交互极大地简化了集成工作。其核心理念是“为AI工程带来Spring生态的便利性”。LangChain4j Java版的LangChain。它提供了一套更高层次的抽象用于构建由LLM驱动的应用程序。其核心价值在于“链”Chain和“工具”Tool的概念能轻松编排多个LLM调用、工具执行和记忆管理是构建复杂AI Agent的理想选择。AI Agent智能体 一个能够感知环境、进行决策并执行动作以完成目标的系统。在本文语境下它指的是一个能调用工具如查询数据库、执行代码、拥有记忆和规划能力的LLM增强程序。例如一个航空客服Agent可以理解用户问题自动查询航班信息、政策并组织语言回复。RAG检索增强生成 解决LLM“幻觉”生成虚假信息和知识滞后问题的关键技术。其原理是先将外部知识库如航空手册、政策文档向量化存储当用户提问时先从向量库中检索出最相关的文档片段然后将这些片段作为上下文连同问题一起提交给LLM生成答案。这能确保答案基于事实且实时。Spring AI Alibaba 阿里巴巴基于Spring AI生态的扩展提供了与阿里云百炼、灵积等平台模型服务的深度集成同时也包含了一些针对国内开发环境的优化和额外组件。为什么是“Java AI”Java拥有庞大的企业级应用存量其稳定性、可维护性和丰富的中间件生态是企业核心系统的基石。将AI能力无缝融入现有Java体系而不是推倒重来或用Python另起炉灶是成本最低、效益最高的技术演进路径。上述技术栈正是为此而生。2. 环境准备与版本说明在开始实战前我们需要搭建好开发环境。本文将使用最通用的Spring Boot 3.x架构进行演示。基础环境JDK: 17 或 21推荐17长期支持版本Maven: 3.6IDE: IntelliJ IDEA 或 VS Code模型服务:选项A云端快速上手OpenAI API Key 或 阿里云通义千问API Key。选项B本地数据安全使用Ollama在本地运行开源模型如qwen2.5:7b,llama3.2:3b。需要安装Docker或直接下载Ollama。项目初始化使用 Spring Initializr 创建一个Spring Boot项目。Project: MavenLanguage: JavaSpring Boot: 3.2.x (建议选择当前稳定版)Dependencies: 先选择Spring Web。生成项目后用IDE打开。接下来的依赖我们将手动添加到pom.xml。3. 技术栈选型深度解析与对比面对众多选择如何决策下图清晰地展示了各组件在智能航空客服系统中的角色与协作关系flowchart TD A[用户提问br“明天北京飞上海的航班有哪些”] -- B(航空客服AI Agent) subgraph B[智能体决策与执行] B1{Agent分析用户意图} --|需查询实时信息| B2[调用“航班查询工具”] B1 --|需依据政策回答| B3[调用“RAG知识库检索工具”] B1 --|简单对话| B4[直接调用LLM生成回复] B2 -- B5[获取结构化航班数据] B3 -- B6[获取相关政策文档片段] end B5 B6 -- C{Agent合成最终答案} C -- D[回复用户br“明天共有X班航班最早一班是...”] subgraph E[支撑技术栈] E1[Spring AIbr统一模型接口] E2[LangChain4jbrAgent与链编排] E3[向量数据库br存储知识库] E4[本地/云端LLMbr大语言模型] end E1 E2 E3 E4 -- 为智能体提供能力 -- B3.1 Spring AI vs. LangChain4j定位与选择这是选型中最常见的困惑。两者并非互斥而是互补。Spring AI模型交互层的抽象。它解决的是“如何用统一的方式调用不同模型”的问题。如果你的应用核心是简单地发送Prompt、获取Completion或生成Embedding那么引入Spring AI就足够了。它轻量、与Spring生态无缝集成。LangChain4j应用编排层的框架。它解决的是“如何构建复杂的、多步骤的AI应用”问题。当你需要构建一个能自动选择工具、管理对话历史、进行条件判断的Agent时LangChain4j提供了更强大的抽象。选型建议简单集成场景 仅需调用模型API -首选Spring AI。复杂Agent场景 需要工具调用、记忆、复杂链式逻辑 -使用LangChain4j它底层可以集成Spring AI的客户端。组合使用最佳实践是使用Spring AI 作为模型访问层同时使用LangChain4j 作为应用编排层。这样既能享受Spring的配置管理、依赖注入又能利用LangChain4j强大的Agent能力。3.2 RAG技术栈选型构建RAG系统需要三个核心组件文本嵌入模型Embedding Model 将文本转换为向量。可选Spring AI的EmbeddingClient或LangChain4j的嵌入接口。向量数据库Vector Database 存储和检索向量。Java生态中Redis通过RediSearch模块、PostgreSQL通过pgvector扩展、Milvus、Weaviate都是常见选择。检索器Retriever 执行相似性搜索。选型建议针对Java项目轻量级/快速验证 使用Redis。很多项目已在使用Redis增加RediSearch模块成本低且Spring Data Redis支持良好。已有PostgreSQL 使用pgvector。无需引入新组件利用现有数据库管理简单。大规模、高并发向量检索 考虑Milvus或Weaviate等专业向量数据库。本文为简化演示将使用内存向量存储但这仅适用于演示生产环境务必替换为上述持久化方案。3.3 AI Agent框架选型除了LangChain4j还有agentscope-java等框架。LangChain4j社区活跃文档相对完善且与LangChainPython概念对齐学习成本较低是目前Java生态中构建Agent的主流选择。Spring AI Alibaba也提供了Agent相关的初步支持但成熟度和生态丰富度仍在发展中。结论对于智能航空客服这类需要工具调用查航班、查政策的复杂场景LangChain4j是更成熟和强大的选择。4. 完整实战构建智能航空客服核心模块我们将构建一个具备以下能力的客服Agent原型RAG知识库问答 回答关于行李规定、退改签政策的问题。工具调用 查询“模拟”的航班信息。意图识别与路由 自动判断用户问题属于哪一类并调用相应能力。4.1 项目结构与依赖引入首先在pom.xml中添加关键依赖。!-- Spring Boot 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI - 核心抽象支持OpenAI/Ollama等 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId !-- 请查看Spring AI官方文档获取最新版本 -- version0.8.1/version /dependency !-- LangChain4j - 核心库 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version0.31.0/version /dependency !-- LangChain4j 与 Spring AI 集成 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-ai/artifactId version0.31.0/version /dependency !-- LangChain4j 内存向量库仅用于演示 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-embeddings-all-minilm-l6-v2/artifactId !-- 内置的轻量级嵌入模型 -- version0.31.0/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-store-embedding-in-memory/artifactId version0.31.0/version /dependency4.2 配置模型连接在application.yml中配置Spring AI。这里以本地Ollama运行qwen2.5:7b模型为例确保数据安全。spring: ai: openai: # 如果使用本地Ollamabase-url指向本地服务 base-url: http://localhost:11434/v1 api-key: ollama # Ollama不需要真实的key但属性需要存在 chat: options: model: qwen2.5:7b # 指定Ollama中已拉取的模型名称 temperature: 0.74.3 实现RAG知识库第一步创建知识库服务用于加载政策文档并支持检索。// 文件路径src/main/java/com/example/ai航空客服/service/KnowledgeBaseService.java package com.example.ai航空客服.service; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import jakarta.annotation.PostConstruct; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.stereotype.Service; import dev.langchain4j.store.embedding.EmbeddingMatch; import java.util.List; import java.util.stream.Collectors; Service public class KnowledgeBaseService { private final EmbeddingClient embeddingClient; // Spring AI的嵌入客户端 private final EmbeddingStoreTextSegment embeddingStore; public KnowledgeBaseService(EmbeddingClient embeddingClient) { this.embeddingClient embeddingClient; // 生产环境请替换为 RedisEmbeddingStore 或 PgVectorEmbeddingStore this.embeddingStore new InMemoryEmbeddingStore(); } PostConstruct public void init() { // 模拟加载航空公司的政策文档 ListString policyDocuments List.of( 经济舱旅客的免费托运行李额为20公斤尺寸三边之和不超过158厘米。, 商务舱旅客的免费托运行李额为30公斤尺寸三边之和不超过158厘米。, 航班起飞前2小时以上可以免费办理退票起飞前2小时内退票收取20%手续费。, 随身携带的液体物品每件容器容积不得超过100毫升并需放入透明塑料袋中。, 婴儿票2周岁以下不占座票价为成人全价票的10%。 ); // 使用LangChain4j的Ingestor来分割文本、生成向量并存储 EmbeddingStoreIngestor ingestor EmbeddingStoreIngestor.builder() .embeddingModel(new SpringAiEmbeddingModel(embeddingClient)) // 适配Spring AI客户端 .embeddingStore(embeddingStore) .textSegmenter(/* 使用默认分割器 */) .build(); // 将文档转换为TextSegment并摄入向量库 ListTextSegment segments policyDocuments.stream() .map(TextSegment::from) .collect(Collectors.toList()); ingestor.ingest(segments); System.out.println(知识库初始化完成已加载 policyDocuments.size() 条政策。); } /** * 根据用户问题检索最相关的政策片段 */ public String retrieveRelevantPolicy(String userQuestion) { // 1. 将问题转换为向量 float[] queryEmbedding embeddingClient.embed(userQuestion).getOutput(); // 2. 在向量库中搜索最相似的3个片段 ListEmbeddingMatchTextSegment relevantMatches embeddingStore.findRelevant( dev.langchain4j.data.embedding.Embedding.from(queryEmbedding), 3, 0.6 // 相似度阈值 ); // 3. 合并检索结果作为上下文 if (relevantMatches.isEmpty()) { return 未找到相关政策信息。; } return relevantMatches.stream() .map(match - match.embedded().text()) .collect(Collectors.joining(\n\n)); } }4.4 定义Agent工具Tool工具是Agent扩展能力的关键。这里定义两个工具航班查询工具和知识库查询工具。// 文件路径src/main/java/com/example/ai航空客服/tool/FlightQueryTool.java package com.example.ai航空客服.tool; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.util.List; import java.util.stream.Collectors; Component public class FlightQueryTool { // 模拟一个航班数据库 private static final ListFlight mockFlights List.of( new Flight(CA1501, 北京, 上海, LocalDate.now().plusDays(1), 08:00, 10:20, 1200, 50), new Flight(MU5102, 北京, 上海, LocalDate.now().plusDays(1), 10:30, 12:45, 1100, 120), new Flight(CZ6166, 广州, 上海, LocalDate.now().plusDays(1), 14:00, 16:10, 900, 30) ); /** * 根据条件查询航班信息 * Tool 注解让LangChain4j能识别这是一个可供Agent调用的工具 */ Tool(根据出发地、目的地和日期查询可用的航班信息。) public String queryFlights(String departureCity, String arrivalCity, String date) { LocalDate queryDate LocalDate.parse(date); ListFlight result mockFlights.stream() .filter(f - f.departureCity.equals(departureCity)) .filter(f - f.arrivalCity.equals(arrivalCity)) .filter(f - f.date.equals(queryDate)) .collect(Collectors.toList()); if (result.isEmpty()) { return String.format(未找到%s从%s飞往%s的航班。, date, departureCity, arrivalCity); } return result.stream() .map(f - String.format(航班号%s 时间%s-%s 价格%d 余票%d, f.flightNumber, f.departureTime, f.arrivalTime, f.price, f.seatsAvailable)) .collect(Collectors.joining(\n)); } // 内部航班数据类 static class Flight { String flightNumber; String departureCity; String arrivalCity; LocalDate date; String departureTime; String arrivalTime; int price; int seatsAvailable; // 构造函数、getter/setter 省略... } }// 文件路径src/main/java/com/example/ai航空客服/tool/PolicyQueryTool.java package com.example.ai航空客服.tool; import com.example.ai航空客服.service.KnowledgeBaseService; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; Component public class PolicyQueryTool { private final KnowledgeBaseService knowledgeBaseService; public PolicyQueryTool(KnowledgeBaseService knowledgeBaseService) { this.knowledgeBaseService knowledgeBaseService; } Tool(查询航空公司的行李、退改签、乘机等政策规定。) public String queryPolicy(String question) { // 调用RAG知识库服务获取相关信息 String context knowledgeBaseService.retrieveRelevantPolicy(question); return 根据相关政策信息如下\n context; } }4.5 构建并运行AI Agent这是最核心的部分我们将使用LangChain4j创建一个具备工具调用能力的AI Agent。// 文件路径src/main/java/com/example/ai航空客服/service/AirlineCustomerServiceAgent.java package com.example.ai航空客服.service; import dev.langchain4j.agent.tool.ToolSpecification; import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.TokenStream; import com.example.ai航空客服.tool.FlightQueryTool; import com.example.ai航空客服.tool.PolicyQueryTool; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import java.util.List; Service public class AirlineCustomerServiceAgent { private final Assistant assistant; // 定义Agent的交互接口 interface Assistant { String chat(String userMessage); FluxString chatStream(String userMessage); // 流式响应可选 } public AirlineCustomerServiceAgent(ChatClient.Builder chatClientBuilder, FlightQueryTool flightTool, PolicyQueryTool policyTool) { // 1. 通过Spring AI创建LangChain4j可用的模型 ChatLanguageModel model new SpringAiChatModel(chatClientBuilder.build()); // 2. 创建聊天记忆保留最近10轮对话 ChatMemory chatMemory MessageWindowChatMemory.withMaxMessages(10); // 3. 使用AiServices.builder()构建Agent this.assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemory(chatMemory) .tools(flightTool, policyTool) // 注入工具 .build(); } /** * 与客服Agent交互 */ public String chat(String userMessage) { return assistant.chat(userMessage); } }4.6 创建REST API控制器最后暴露一个HTTP接口供前端或测试调用。// 文件路径src/main/java/com/example/ai航空客服/controller/ChatController.java package com.example.ai航空客服.controller; import com.example.ai航空客服.service.AirlineCustomerServiceAgent; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/chat) public class ChatController { private final AirlineCustomerServiceAgent agent; public ChatController(AirlineCustomerServiceAgent agent) { this.agent agent; } PostMapping public String chat(RequestBody ChatRequest request) { if (request null || request.getMessage() null || request.getMessage().trim().isEmpty()) { return 请输入您的问题。; } return agent.chat(request.getMessage()); } // 简单的请求体 public static class ChatRequest { private String message; // getter/setter 省略... } }4.7 运行与验证启动你的Spring Boot应用。使用curl、Postman或任何HTTP客户端测试。curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 我明天从北京飞上海有什么航班}预期输出示例为您查询到明天从北京飞往上海的航班如下 航班号CA1501 时间08:00-10:20 价格1200 余票50 航班号MU5102 时间10:30-12:45 价格1100 余票120测试政策查询curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 经济舱的行李额是多少}预期输出示例根据相关政策信息如下 经济舱旅客的免费托运行李额为20公斤尺寸三边之和不超过158厘米。测试复杂意图Agent自动判断并组合工具curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 我想订明天北京到上海的航班另外想问下退票政策}Agent可能会先调用FlightQueryTool查询航班再调用PolicyQueryTool查询退票政策最后将两者信息整合成一段连贯的回答。5. 常见问题与排查思路在集成过程中你可能会遇到以下典型问题问题现象常见原因解决思路启动报错No qualifying bean of type EmbeddingClient未正确引入Spring AI starter或配置有误。1. 检查pom.xml中spring-ai-openai-spring-boot-starter依赖是否正确。2. 检查application.yml中spring.ai.openai.*配置特别是base-url和api-key。Agent不调用工具直接回答“我不知道”1. 工具描述不清晰。2. 模型能力不足特别是小参数本地模型。3. Prompt未引导模型使用工具。1. 优化Tool注解中的描述确保清晰、具体。2. 尝试使用能力更强的模型如GPT-4、DeepSeek-V3。3. 在构建AiServices时可以通过.promptTemplate()自定义系统提示词明确要求其使用工具。RAG检索结果不相关1. 嵌入模型不适合中文。2. 文本分割Chunk策略不合理。3. 相似度阈值设置不当。1. 更换为针对中文优化的嵌入模型如BAAI/bge-small-zh。2. 调整文本分割的大小和重叠度。3. 调整findRelevant方法中的相似度阈值和返回数量。调用本地Ollama模型超时或失败1. Ollama服务未启动。2. 模型未正确拉取。3. 网络端口被占用。1. 运行ollama serve启动服务。2. 运行ollama pull qwen2.5:7b拉取模型。3. 确认application.yml中的base-url默认http://localhost:11434是否正确。OutOfMemoryError: Insufficient memory本地模型特别是7B以上或向量操作消耗内存过大。1. 为JVM增加堆内存-Xmx8g -Xms4g。2. 考虑使用量化版本模型如qwen2.5:7b-q4_K_M。3. 生产环境使用云端API或专用推理服务器。6. 最佳实践与工程建议将AI能力投入生产环境远不止让代码跑通那么简单。以下是一些关键的工程化考量6.1 配置与安全密钥管理 绝对不要将API Key硬编码在代码或配置文件中。使用Spring Cloud Config、Apollo、环境变量或云厂商的密钥管理服务。配置分离 将模型配置如base-url, model-name放在application-{profile}.yml中便于不同环境dev/test/prod切换。网络与代理 国内访问OpenAI等可能需要配置网络代理可在RestTemplate或WebClient层面统一处理。6.2 性能与稳定性超时与重试 为AI模型调用配置合理的连接超时、读取超时并实现重试机制可使用Spring Retry或Resilience4j。限流与熔断 AI服务可能不稳定或昂贵。使用Sentinel或Resilience4j对模型调用进行限流和熔断防止雪崩。异步与非阻塞 对于耗时的AI生成或RAG检索考虑使用Async或WebFlux进行异步处理避免阻塞主线程。6.3 RAG优化高质量知识库 RAG的效果严重依赖原始文档质量。需对文档进行清洗、去重、结构化。智能分块Chunking 不要简单按固定字数分割。尝试按段落、标题或使用语义分割算法保证块的完整性。元数据过滤 在检索时除了向量相似度还可结合文档类型、更新时间等元数据进行过滤提升精度。重排序Re-ranking 在初步向量检索后使用一个更精细的交叉编码器模型对结果进行重排序进一步提升Top1答案的相关性。6.4 Agent设计工具设计原则 工具应职责单一、接口明确。工具的描述Tool注解要足够详细让LLM能准确理解其用途。验证与回退 Agent调用的工具如航班查询返回的结果在展示给用户前应进行基本的逻辑或格式验证。对于工具调用失败要有明确的回退话术。可控性与审计 记录Agent的完整思考过程Chain-of-Thought和工具调用日志这对于调试、优化和满足合规性要求至关重要。6.5 监控与可观测性关键指标 监控AI调用的延迟、成功率、Token消耗量、费用。链路追踪 集成Micrometer、SkyWalking等将一次用户问答背后的RAG检索、LLM调用、工具执行串联起来便于排查问题。效果评估 建立人工评估或自动化评估机制定期检查问答准确率、相关性持续迭代模型、提示词和知识库。通过本文的梳理你应该对Java生态下的AI技术栈选型有了清晰的认识并成功搭建了一个具备RAG和工具调用能力的智能航空客服原型。从简单的模型调用到复杂的Agent编排Java开发者完全有能力在熟悉的生态内构建强大的AI应用。接下来的方向可以是接入真实的航班API、丰富知识库、优化提示词工程并将向量存储和模型服务部署到生产环境。