ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java开发者AI应用实战:SpringAI与LangChain4j构建RAG与Agent系统

2026/9/4 13:07:12 拓冰建站 浏览量
Java开发者AI应用实战:SpringAI与LangChain4j构建RAG与Agent系统 你好我是专注于Java全栈技术分享的博主。最近在团队内部推动AI能力落地时发现很多有丰富CRUD和微服务开发经验的Java后端同学面对AI应用开发却不知从何下手。网上的资料要么是Python视角要么过于零散缺乏一个从Java开发者视角出发、能贯穿环境搭建、核心概念、代码实战到项目上线的完整路径。本文正是为了解决这个问题。我将为你系统梳理Java AI应用开发的核心技术栈涵盖SpringAI、LangChain4j、向量数据库、RAG以及Agent的实战应用。无论你是想为现有系统增加智能问答、文档分析能力还是想从零打造一个AI产品这篇教程都能提供从理论到代码的完整指导。学完后你将能独立完成一个具备知识库检索与智能对话能力的AI服务模块。1. 背景与核心概念为什么Java开发者需要关注AI在传统的Java开发领域我们擅长处理结构化数据、构建高并发微服务、设计复杂的业务逻辑。然而随着大语言模型LLM的爆发应用的核心价值正从“流程自动化”向“认知智能化”迁移。对于Java后端开发者而言这既是挑战也是巨大的机遇。1.1 AI原生应用的核心模式当前将AI能力集成到应用中有几种主流模式直接调用API最简单的方式直接调用OpenAI、通义千问等模型的Completion或Chat接口。但这种方式无法处理长文本、私有知识且成本可控性差。检索增强生成RAG这是当前企业级AI应用最主流的架构。其核心思想是将外部知识源如公司文档、产品手册进行处理存入向量数据库。当用户提问时先从向量库中检索出最相关的知识片段再将片段和问题一起提交给大模型生成答案。这解决了大模型的“幻觉”问题和知识更新滞后的问题。AI Agent智能体让AI具备使用工具如调用API、查询数据库、进行规划、执行多步骤任务的能力。这代表了更高级的自动化水平。1.2 Java生态的AI工具链对于Java开发者我们不必转向Python成熟的Java生态已经提供了强大的AI开发框架SpringAISpring官方项目旨在将AI能力无缝集成到Spring生态中。它提供了统一的API可以方便地切换不同的AI模型提供商如OpenAI、Ollama、Azure OpenAI并内置了Prompt模板、输出解析、数据ETL等常用功能。它的设计理念与Spring Boot一脉相承Java开发者几乎零成本上手。LangChain4jJava版的LangChain功能更为丰富和灵活。它提供了极其丰富的“链”Chain、“工具”Tool、“记忆”Memory等组件的实现是构建复杂Agent和RAG系统的利器。向量数据库用于存储和检索文本的向量化表示。常见的Java友好选择有Milvus、PgVectorPostgreSQL插件、Redis Stack支持向量搜索等。1.3 从CRUD到AI全栈的转型路径Java后端开发者转型AI应用开发优势在于深厚的工程化能力设计模式、并发、容错、监控需要补足的是对AI核心概念Embedding、Token、RAG流程、Agent工作流的理解以及熟悉对应的Java框架。本教程的目标就是帮你补齐这块拼图。2. 环境准备与版本说明我们将构建一个基于Spring Boot 3.x的AI应用集成SpringAI和LangChain4j使用Ollama本地运行大模型避免API密钥和网络问题并用内存向量库进行演示。2.1 基础环境JDK: 17 或 21推荐17长期支持版本Maven: 3.6IDE: IntelliJ IDEA 或 VS CodeOllama: 用于在本地运行开源大模型。请前往 Ollama官网 下载并安装。安装后在终端执行ollama pull qwen2.5:7b拉取一个适中的模型。2.2 创建Spring Boot项目使用 Spring Initializr 或IDE创建新项目Project: MavenLanguage: JavaSpring Boot: 3.2.xPackaging: JarJava: 17Dependencies: Spring Web, Spring Boot DevTools (可选用于热加载)2.3 关键依赖说明 (pom.xml)我们将混合使用SpringAI和LangChain4j因为它们各有侧重。SpringAI适合快速集成和标准化调用LangChain4j适合构建复杂逻辑。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version !-- 使用稳定版本 -- relativePath/ /parent groupIdcom.example/groupId artifactIdjava-ai-demo/artifactId version0.0.1-SNAPSHOT/version namejava-ai-demo/name descriptionDemo project for Java AI Stack/description properties java.version17/java.version spring-ai.version0.8.1/spring-ai.version !-- 注意版本 -- langchain4j.version0.31.0/langchain4j.version /properties dependencies !-- Spring Boot 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI - 核心与Ollama连接器 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency !-- LangChain4j - 核心与Ollama集成 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-ollama/artifactId version${langchain4j.version}/version /dependency !-- LangChain4j 内存向量库用于演示 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-embeddings-all-minilm-l6-v2/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-store-embedding-in-memory/artifactId version${langchain4j.version}/version /dependency !-- 工具类 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project注意SpringAI和LangChain4j版本迭代较快请根据官方文档调整至稳定版本。上述版本为撰写本文时的稳定选择。2.4 配置文件 (application.yml)配置Ollama连接信息。确保Ollama服务已启动默认在 http://localhost:11434。spring: application: name: java-ai-demo # Spring AI Ollama 配置 spring: ai: ollama: base-url: http://localhost:11434 # Ollama服务地址 chat: model: qwen2.5:7b # 使用的模型名称需与Ollama拉取的模型一致 embedding: model: qwen2.5:7b # 用于生成嵌入向量的模型可与chat模型相同 # 自定义配置 app: vector-store: in-memory: true # 演示使用内存向量库3. 核心组件与原理拆解在编写代码前理解以下几个核心概念至关重要。3.1 嵌入Embedding与向量搜索这是RAG的基石。一段文本无论是词、句还是段落通过嵌入模型Embedding Model可以转化为一个高维向量一组数字。语义相似的文本其向量在空间中的距离也更近。过程知识库文档 - 拆分文本块 - 每个块通过Embedding模型转为向量 - 存入向量数据库。检索用户问题 - 转为向量 - 在向量数据库中搜索距离最近的K个向量 - 找到对应的原始文本块。3.2 提示词工程Prompt Engineering如何有效地与大模型对话。一个典型的RAG提示词模板如下请根据以下上下文信息回答问题。如果上下文信息不足以回答问题请直接说“根据提供的信息无法回答该问题”。 上下文信息 {context} 问题{question} 请用中文回答在SpringAI和LangChain4j中我们可以将此类模板抽象成PromptTemplate方便管理和复用。3.3 链ChainLangChain4j的核心抽象将多个步骤组合成一个可执行的工作流。例如一个简单的RAG链可能包含用户输入 - 检索 - 构建提示词 - 调用模型 - 解析输出。3.4 智能体Agent与工具ToolAgent是一个具备自主决策能力的AI系统。它可以根据目标决定调用哪个“工具”Tool可以是一个函数如查询天气、计算器、搜索数据库来完成任务并根据工具返回的结果决定下一步动作直到达成目标或失败。4. 完整实战案例构建一个企业知识库问答系统RAG我们将分步骤实现一个完整的RAG系统包含文档嵌入、向量存储、检索和生成答案。4.1 项目结构src/main/java/com/example/jaiaidemo/ ├── JavaAiDemoApplication.java ├── config │ ├── AiConfig.java // AI Bean配置 │ └── EmbeddingConfig.java // 嵌入模型配置 ├── controller │ └── KnowledgeController.java // REST API ├── service │ ├── EmbeddingService.java // 文档嵌入服务 │ └── RagService.java // RAG核心服务 ├── store │ └── VectorStoreService.java // 向量存储服务抽象层 └── dto └── QuestionRequest.java // 请求/响应DTO4.2 配置AI模型Bean首先配置SpringAI和LangChain4j的核心组件。// File: src/main/java/com/example/jaiaidemo/config/AiConfig.java package com.example.jaiaidemo.config; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.model.embedding.all.minilm.AllMiniLmL6V2EmbeddingModel; import dev.langchain4j.model.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaChatClient; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class AiConfig { // 配置Spring AI的Ollama Chat Client Bean public OllamaChatClient ollamaChatClient() { OllamaApi ollamaApi new OllamaApi(http://localhost:11434); return new OllamaChatClient(ollamaApi, OllamaOptions.builder() .withModel(qwen2.5:7b) .withTemperature(0.7) // 创造性0-1越高越随机 .build()); } // 配置LangChain4j的Ollama Chat Model (用于更复杂的链式调用) Bean public OllamaChatModel langChainOllamaChatModel() { return OllamaChatModel.builder() .baseUrl(http://localhost:11434) .modelName(qwen2.5:7b) .temperature(0.7) .build(); } // 配置嵌入模型 (使用一个轻量级本地模型) Bean public EmbeddingModel embeddingModel() { return new AllMiniLmL6V2EmbeddingModel(); } }4.3 实现向量存储服务为了便于切换不同的向量数据库如内存、Milvus、PgVector我们定义一个抽象服务。// File: src/main/java/com/example/jaiaidemo/store/VectorStoreService.java package com.example.jaiaidemo.store; import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; public interface VectorStoreService { void addDocument(String text); ListTextSegment findRelevant(String query, int maxResults); } Service Slf4j RequiredArgsConstructor class InMemoryVectorStoreServiceImpl implements VectorStoreService { private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore new InMemoryEmbeddingStore(); Override public void addDocument(String text) { // 1. 将文本拆分为片段这里简化处理实际应用需更复杂的分块策略 TextSegment segment TextSegment.from(text); // 2. 为文本片段生成嵌入向量 Embedding embedding embeddingModel.embed(segment.text()).content(); // 3. 存储到向量库 embeddingStore.add(embedding, segment); log.info(文档已添加到向量库内容长度{}, text.length()); } Override public ListTextSegment findRelevant(String query, int maxResults) { // 1. 将查询问题转换为向量 Embedding queryEmbedding embeddingModel.embed(query).content(); // 2. 在向量库中搜索最相似的片段 ListEmbeddingMatchTextSegment relevantMatches embeddingStore.findRelevant(queryEmbedding, maxResults); // 3. 提取文本片段返回 return relevantMatches.stream() .map(EmbeddingMatch::embedded) .toList(); } }4.4 实现文档嵌入服务该服务负责处理原始文档如TXT、PDF进行分块、清洗并调用VectorStoreService存储。// File: src/main/java/com/example/jaiaidemo/service/EmbeddingService.java package com.example.jaiaidemo.service; import com.example.jaiaidemo.store.VectorStoreService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; Service Slf4j RequiredArgsConstructor public class EmbeddingService { private final VectorStoreService vectorStoreService; /** * 从文本文件加载知识库 * param filePath 文件路径 */ public void loadKnowledgeFromFile(String filePath) throws IOException { Path path Paths.get(filePath); String content Files.readString(path); // 简单按句号分块实际项目需用更智能的分块器如按语义、固定长度重叠分块 ListString chunks splitTextIntoChunks(content, 300); // 每块约300字符 for (String chunk : chunks) { if (StringUtils.hasText(chunk.trim())) { vectorStoreService.addDocument(chunk); } } log.info(知识库加载完成共处理 {} 个文本块。, chunks.size()); } /** * 直接添加文本到知识库 * param text 文本内容 */ public void addTextToKnowledge(String text) { vectorStoreService.addDocument(text); log.info(文本已添加到知识库。); } private ListString splitTextIntoChunks(String text, int chunkSize) { // 这是一个非常简单的分块实现仅用于演示。 // 生产环境应使用 LangChain4j 的 DocumentSplitter如 TokenSplitter, RecursiveCharacterTextSplitter return Arrays.asList(text.split((?[。.]))); } }4.5 实现RAG核心服务这是最核心的部分串联检索和生成。// File: src/main/java/com/example/jaiaidemo/service/RagService.java package com.example.jaiaidemo.service; import com.example.jaiaidemo.store.VectorStoreService; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.output.Response; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; Service Slf4j RequiredArgsConstructor public class RagService { // 使用Spring AI的ChatClient private final ChatClient chatClient; // 使用LangChain4j的向量存储服务 private final VectorStoreService vectorStoreService; // 定义RAG提示词模板 private static final String RAG_PROMPT_TEMPLATE 请严格根据以下提供的上下文信息来回答问题。上下文信息由三引号包裹。 如果上下文信息中没有明确答案请直接说“根据已知信息无法回答此问题”不要编造信息。 请用简洁、专业的中文回答。 上下文信息 \\\ {context} \\\ 问题{question} 答案 ; /** * 使用Spring AI进行RAG问答 */ public String answerWithSpringAI(String question) { // 1. 检索相关上下文 ListString relevantContexts vectorStoreService.findRelevant(question, 3) // 取最相关的3段 .stream() .map(segment - segment.text()) .collect(Collectors.toList()); String context String.join(\n\n, relevantContexts); log.info(检索到 {} 条相关上下文。, relevantContexts.size()); if (context.isEmpty()) { return 知识库中未找到相关信息。; } // 2. 构建Prompt PromptTemplate promptTemplate new PromptTemplate(RAG_PROMPT_TEMPLATE); MapString, Object model new HashMap(); model.put(context, context); model.put(question, question); Prompt prompt promptTemplate.create(model); // 3. 调用模型并返回结果 ChatResponse response chatClient.prompt(prompt).call().chatResponse(); return response.getResult().getOutput().getContent(); } /** * 使用LangChain4j进行RAG问答演示另一种方式 */ public String answerWithLangChain4j(String question, ChatLanguageModel chatModel) { // 1. 检索相关上下文 ListString relevantContexts vectorStoreService.findRelevant(question, 3) .stream() .map(segment - segment.text()) .collect(Collectors.toList()); String context String.join(\n\n, relevantContexts); if (context.isEmpty()) { return 知识库中未找到相关信息。; } // 2. 构建Prompt并调用模型 (LangChain4j方式) String prompt String.format(RAG_PROMPT_TEMPLATE, context, question); ResponseString response chatModel.generate(prompt); return response.content(); } }4.6 创建REST API控制器// File: src/main/java/com/example/jaiaidemo/controller/KnowledgeController.java package com.example.jaiaidemo.controller; import com.example.jaiaidemo.dto.QuestionRequest; import com.example.jaiaidemo.service.EmbeddingService; import com.example.jaiaidemo.service.RagService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.io.IOException; RestController RequestMapping(/api/knowledge) RequiredArgsConstructor public class KnowledgeController { private final RagService ragService; private final EmbeddingService embeddingService; PostMapping(/load) public String loadKnowledge(RequestParam String filePath) { try { embeddingService.loadKnowledgeFromFile(filePath); return 知识库加载成功; } catch (IOException e) { return 知识库加载失败: e.getMessage(); } } PostMapping(/add) public String addKnowledge(RequestBody String text) { embeddingService.addTextToKnowledge(text); return 知识添加成功; } PostMapping(/ask) public String askQuestion(RequestBody QuestionRequest request) { return ragService.answerWithSpringAI(request.getQuestion()); } } // File: src/main/java/com/example/jaiaidemo/dto/QuestionRequest.java package com.example.jaiaidemo.dto; import lombok.Data; Data public class QuestionRequest { private String question; }4.7 运行与验证启动Ollama服务并确保模型已下载。运行Spring Boot应用JavaAiDemoApplication。准备一个knowledge.txt文件里面放入一些公司制度、产品介绍等文本。使用curl或Postman调用API加载知识库POST http://localhost:8080/api/knowledge/load?filePath/path/to/your/knowledge.txt直接添加知识POST http://localhost:8080/api/knowledge/addBody (raw text):“我们公司的年假制度是工作满1年有5天年假。”提问POST http://localhost:8080/api/knowledge/askBody (JSON):{question: 公司的年假有多少天}你应该会收到一个基于你提供知识库的准确回答而不是模型自身的通用知识。5. 进阶实战构建一个简单的AI Agent天气查询RAG解决了知识问题Agent则解决了行动问题。我们构建一个能查询实时天气的简单Agent。5.1 定义工具ToolAgent通过调用工具来与外界交互。我们先定义一个查询天气的工具。// File: src/main/java/com/example/jaiaidemo/agent/WeatherTool.java package com.example.jaiaidemo.agent; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.time.LocalDate; Component // 注册为Spring Bean以便被Agent发现 public class WeatherTool { /** * 一个模拟的天气查询工具。 * Tool 注解让LangChain4j能识别这是一个可供Agent调用的工具。 */ Tool(根据城市名称和日期查询天气情况。日期格式为YYYY-MM-DD如果未提供日期则默认为今天。) public String getWeatherAtCity(Tool(城市名称例如北京、上海) String city, Tool(查询日期格式为YYYY-MM-DD) String date) { // 模拟实现真实场景应调用天气API LocalDate queryDate (date null || date.isEmpty()) ? LocalDate.now() : LocalDate.parse(date); return String.format(%s在%s的天气是晴朗气温20-25度。这是一个模拟结果。, city, queryDate); } }5.2 配置并运行Agent我们使用LangChain4j的AiServices来轻松创建一个Agent。// File: src/main/java/com/example/jaiaidemo/agent/Assistant.java package com.example.jaiaidemo.agent; // 定义Agent的接口 interface Assistant { String chat(String userMessage); } // File: src/main/java/com/example/jaiaidemo/agent/AgentService.java package com.example.jaiaidemo.agent; import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.service.AiServices; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; Service Slf4j RequiredArgsConstructor public class AgentService { private final ChatLanguageModel chatModel; // 注入之前配置的OllamaChatModel private final WeatherTool weatherTool; // 注入工具 private Assistant assistant; PostConstruct public void init() { // 创建ChatMemory让Agent有短期记忆记住最近的对话 ChatMemory chatMemory MessageWindowChatMemory.withMaxMessages(10); // 使用AiServices构建Agent它会自动发现并绑定Tool注解的Bean this.assistant AiServices.builder(Assistant.class) .chatLanguageModel(chatModel) .tools(weatherTool) // 注册工具 .chatMemory(chatMemory) .build(); log.info(AI Agent 初始化完成。); } public String chat(String userMessage) { log.info(用户提问: {}, userMessage); String response assistant.chat(userMessage); log.info(Agent 回复: {}, response); return response; } }5.3 创建Agent的API// File: src/main/java/com/example/jaiaidemo/controller/AgentController.java package com.example.jaiaidemo.controller; import com.example.jaiaidemo.agent.AgentService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/agent) RequiredArgsConstructor public class AgentController { private final AgentService agentService; PostMapping(/chat) public String chatWithAgent(RequestBody String userMessage) { return agentService.chat(userMessage); } }5.4 验证Agent启动应用后调用POST http://localhost:8080/api/agent/chatBody为“今天北京天气怎么样”。 观察日志你会看到类似的过程用户提问: 今天北京天气怎么样 [Agent思考] 我需要查询天气有一个工具叫getWeatherAtCity... [Agent调用工具] getWeatherAtCity(北京, 2024-05-27) Agent 回复: 北京在2024-05-27的天气是晴朗气温20-25度。这是一个模拟结果。Agent自动判断需要调用天气工具并正确传入了参数。6. 常见问题与排查思路在开发过程中你可能会遇到以下典型问题问题现象可能原因排查思路与解决方案启动报错No qualifying bean of type ‘ChatLanguageModel’1. LangChain4j的Ollama依赖未正确添加。2.OllamaChatModelBean未创建或扫描不到。1. 检查pom.xml中langchain4j-ollama依赖。2. 检查AiConfig配置类是否被SpringBootApplication主类扫描到。调用API超时或无响应1. Ollama服务未启动。2. 模型未下载。3. 模型太大本地硬件推理慢。1. 终端执行ollama serve确保服务运行并检查application.yml中的base-url。2. 执行ollama list确认模型存在或用ollama pull拉取。3. 尝试更小的模型如qwen2.5:3b或llama3.2:3b。RAG回答与知识库无关或“幻觉”严重1. 文本分块不合理丢失上下文。2. 检索到的相关片段太少或质量差。3. Prompt模板设计不佳。1. 优化EmbeddingService中的分块逻辑使用重叠分块。2. 调整findRelevant的maxResults参数尝试增加数量。3. 强化Prompt模板明确要求“仅根据上下文回答”。向量检索结果不准确1. 嵌入模型与任务不匹配。2. 文本未清洗包含大量噪音。1. 尝试不同的嵌入模型如bge-small-zh。2. 在嵌入前对文本进行预处理去除特殊字符、停用词。Agent不调用工具1. 工具方法未被Tool注解。2. 工具类未注册为Spring Bean或未注入到AiServices。3. 模型能力不足无法理解工具用途。1. 检查工具方法上的注解和描述是否清晰。2. 确保工具类有Component等注解并在AiServices.builder().tools(...)中传入。3. 尝试更换或微调模型或在Prompt中提供更详细的系统指令。Java版本不兼容错误Spring Boot 3.x 需要 JDK 17。检查环境变量JAVA_HOME和IDE中的项目SDK版本确保17。7. 最佳实践与工程建议将AI能力集成到生产级Java应用中需要考虑更多工程化因素。7.1 向量数据库选型与优化生产环境弃用内存向量库InMemoryEmbeddingStore仅用于演示。生产环境应选择Milvus、PgVector、Qdrant或Redis Stack。索引优化向量数据库支持HNSW、IVF-Flat等索引算法根据数据规模百万级以下用HNSW和查询精度要求进行选择和调参。元数据过滤存储向量时附带业务元数据如文档ID、章节、创建时间。检索时结合向量相似度和元数据过滤提高精度。7.2 文本预处理与分块策略高质量分块是RAG成功的一半不要简单按句号或固定长度分割。推荐使用递归字符分块优先按段落、标题分割并设置重叠窗口如200字符保证上下文连贯性。清洗与标准化去除HTML标签、无关字符统一编码格式。多模态支持对于PDF、PPT、Word使用Apache Tika、PDFBox等库提取高质量文本。7.3 提示词工程与管理模板化与外部化不要将Prompt硬编码在代码中。可以存储在数据库、配置中心如Apollo或模板文件里便于运营人员调整。结构化输出使用SpringAI的OutputParser或LangChain4j的OutputFormat让模型返回JSON等结构化数据方便后端处理。系统指令System Message为Agent设定明确的角色、职责和边界例如“你是一个严谨的客服助手只能回答知识库内明确信息”。7.4 性能、监控与容错异步与流式响应对于耗时的模型调用使用Spring WebFlux或CompletableFuture实现异步API或使用SSEServer-Sent Events返回流式响应提升用户体验。限流与降级对AI服务接口实施限流如Resilience4j防止被刷。当外部模型服务不稳定时要有降级策略如返回缓存答案或提示“服务繁忙”。链路追踪与监控集成Micrometer和Prometheus监控每次调用的耗时、Token使用量、费用如果调用云API。记录用户问题、检索到的上下文和模型答案用于后续效果分析和优化。重试与超时配置合理的连接超时和读取超时并对可重试的错误如网络抖动实现自动重试机制。7.5 安全与合规输入输出检查对用户输入进行严格的校验和过滤防止Prompt注入攻击。对模型输出内容进行安全检查过滤不当言论。数据隐私涉及敏感数据的知识库确保向量数据库访问权限可控Embedding过程最好在可信环境内完成。考虑使用可加密的向量数据库或对向量进行加密。审计日志记录所有用户查询和AI响应满足合规审计要求。8. 总结与学习路线通过本教程我们完成了一个Java AI应用从0到1的搭建从环境准备、依赖配置到实现核心的RAG知识库问答系统再到构建能调用外部工具的AI Agent。我们混合使用了SpringAI的便捷性和LangChain4j的强大灵活性。核心掌握点环境搭建基于Spring Boot集成SpringAI和LangChain4j连接本地Ollama模型。RAG全流程理解了文档加载、分块、嵌入、向量存储、检索、提示词构建和答案生成的完整闭环。Agent开发学会了如何通过Tool定义工具并使用AiServices快速构建一个能理解意图、使用工具的智能体。工程化思维了解了向量数据库选型、分块策略、提示词管理等生产级考量。下一步学习建议深入LangChain4j探索其丰富的Chain如转换链、归纳链、不同类型的Memory如Token窗口记忆、向量存储记忆以及更复杂的Agent架构如ReAct、Plan-and-Execute。接入真实数据源尝试从MySQL、Elasticsearch或Confluence中实时抓取数据构建知识库。替换生产级组件将内存向量库换成Milvus或PgVector将本地Ollama模型换成云端API如OpenAI、通义千问并处理鉴权和计费。前端集成使用Vue/React构建一个聊天界面通过WebSocket实现与后端AI服务的流式对话。评估与优化建立评估体系通过人工或自动化方式评估RAG回答的准确率、相关性持续迭代分块策略、检索算法和Prompt。AI应用开发是一个迭代和实验的过程。作为Java开发者你的工程化能力是巨大优势。从这个小项目开始不断尝试、踩坑、优化你就能逐步掌握交付完整AI智能产品的能力。本文的所有代码都已给出建议你亲手运行一遍并尝试修改参数、添加新功能这是最好的学习方式。如果在实践中遇到问题欢迎在评论区交流探讨。