Java AI Agent开发实战:基于Spring AI与LangChain4j构建RAG系统

Java AI Agent开发实战:基于Spring AI与LangChain4j构建RAG系统
对于有 Java 背景的开发者来说直接转向 Python AI 工程师岗位并不是唯一选择。更现实的路径是保持 Java 工程优势同时学习如何将 AI 能力集成到现有系统中。下面是一个结合 Spring AI 和 LangChain4j 的完整项目示例展示如何用 Java 技术栈构建具备 RAG 和基础 Agent 能力的生产级应用。1. 理解 AI Agent 的技术栈和 Java 的定位AI Agent 不是单一技术而是多种能力的组合。一个完整的 Agent 系统通常包含以下层次基础模型层大语言模型 API 调用OpenAI、通义千问、智谱等增强层RAG检索增强生成、Function Calling工具调用控制层Agent 工作流、状态管理、错误恢复工程层权限控制、成本统计、监控告警、部署运维Java 开发者的优势恰恰在工程层。国内大量存量业务系统基于 Java 技术栈企业引入 AI 能力时优先考虑的是如何安全、稳定地接入现有系统而不是重写整套架构。1.1 为什么 Java AI 是务实的选择从搜索材料中的实际案例来看企业级 AI 应用的典型架构是前端/App → Java/Go 后端权限、并发、业务逻辑、数据库 → Python/Java AI 服务 → 模型 API/向量库/外部系统在这种架构中Java 后端承担着关键的网关角色处理登录态、权限控制、业务规则、数据库操作然后再调用 AI 服务完成推理、检索或工具编排。这意味着 Java 开发者不需要变成 Python 专家但要懂得如何设计 AI 友好的接口和交互协议。2. 环境准备与项目结构2.1 技术选型与版本要求组件版本说明JDK17Spring AI 需要 JDK 17 或更高版本Spring Boot3.2提供完整的依赖管理Spring AI2.0.0官方 AI 集成框架LangChain4j0.28补充 Agent 和工具调用能力PostgreSQL15业务数据存储Redis7.0缓存和会话管理2.2 Maven 依赖配置?xml version1.0 encodingUTF-8? project parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.0/version /parent dependencies !-- Spring Boot 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency !-- Spring AI 核心 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version2.0.0/version /dependency !-- LangChain4j 增强 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version0.28.0/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version0.28.0/version /dependency /dependencies /project2.3 项目目录结构src/main/java/com/example/aiagent/ ├── config/ # 配置类 │ ├── AiConfig.java # AI 相关配置 │ └── SecurityConfig.java # 权限配置 ├── controller/ # 控制器层 │ ├── AgentController.java # Agent 接口 │ └── KnowledgeController.java # 知识库接口 ├── service/ # 业务层 │ ├── AgentService.java # Agent 核心逻辑 │ ├── KnowledgeService.java # 知识库管理 │ └── ToolService.java # 工具服务 ├── entity/ # 实体类 │ ├── Conversation.java # 会话实体 │ └── Document.java # 文档实体 └── repository/ # 数据访问层 ├── ConversationRepository.java └── DocumentRepository.java3. 核心实现从 RAG 到基础 Agent3.1 配置 AI 模型连接首先配置与大模型的基础连接这里以 OpenAI 兼容接口为例Configuration public class AiConfig { Value(${ai.openai.base-url}) private String baseUrl; Value(${ai.openai.api-key}) private String apiKey; Value(${ai.openai.model}) private String model; Bean Primary public OpenAiChatModel openAiChatModel() { return OpenAiChatModel.builder() .baseUrl(baseUrl) .apiKey(apiKey) .model(model) .temperature(0.7) .maxTokens(2000) .build(); } }对应的application.yml配置ai: openai: base-url: https://api.openai.com/v1 api-key: ${OPENAI_API_KEY} model: gpt-3.5-turbo spring: datasource: url: jdbc:postgresql://localhost:5432/ai_agent username: postgres password: password jpa: hibernate: ddl-auto: update show-sql: true3.2 实现基础的 RAG 功能RAG检索增强生成是 AI Agent 的核心能力之一让模型能够基于特定知识库回答问题。Service public class KnowledgeService { private final DocumentRepository documentRepository; private final EmbeddingModel embeddingModel; private final ChatLanguageModel chatModel; // 简单的内存向量存储生产环境用专业的向量数据库 private final MapString, Listfloat[] vectorStore new ConcurrentHashMap(); public KnowledgeService(DocumentRepository documentRepository, EmbeddingModel embeddingModel, ChatLanguageModel chatModel) { this.documentRepository documentRepository; this.embeddingModel embeddingModel; this.chatModel chatModel; } public void addDocument(String title, String content) { // 1. 保存到数据库 Document doc new Document(); doc.setTitle(title); doc.setContent(content); documentRepository.save(doc); // 2. 生成嵌入向量 float[] embedding embeddingModel.embed(content).content().vector(); vectorStore.computeIfAbsent(title, k - new ArrayList()).add(embedding); } public String queryWithRAG(String question) { // 1. 检索相关文档 ListString relevantDocs retrieveRelevantDocuments(question, 3); // 2. 构建增强的提示词 String context String.join(\n\n, relevantDocs); String prompt String.format( 基于以下上下文信息回答问题。如果上下文不足以回答问题请如实说明。 上下文 %s 问题%s 请提供准确、有用的回答 , context, question); // 3. 调用模型生成答案 return chatModel.generate(prompt); } private ListString retrieveRelevantDocuments(String query, int topK) { // 简化的余弦相似度计算生产环境用专业向量数据库 float[] queryEmbedding embeddingModel.embed(query).content().vector(); return vectorStore.entrySet().stream() .flatMap(entry - entry.getValue().stream() .map(vec - new AbstractMap.SimpleEntry( entry.getKey(), cosineSimilarity(queryEmbedding, vec)))) .sorted((a, b) - Float.compare(b.getValue(), a.getValue())) .limit(topK) .map(entry - 文档 entry.getKey()) .collect(Collectors.toList()); } private float cosineSimilarity(float[] a, float[] b) { // 实现余弦相似度计算 float dotProduct 0; float normA 0; float normB 0; for (int i 0; i a.length; i) { dotProduct a[i] * b[i]; normA a[i] * a[i]; normB b[i] * b[i]; } return (float) (dotProduct / (Math.sqrt(normA) * Math.sqrt(normB))); } }3.3 构建基础 Agent 系统Agent 的核心是工具调用和工作流管理。下面实现一个能够调用外部工具的基础 AgentService public class AgentService { private final ChatLanguageModel chatModel; private final KnowledgeService knowledgeService; private final ToolService toolService; // 会话状态管理 private final MapString, ConversationState sessionStates new ConcurrentHashMap(); public AgentService(ChatLanguageModel chatModel, KnowledgeService knowledgeService, ToolService toolService) { this.chatModel chatModel; this.knowledgeService knowledgeService; this.toolService toolService; } public AgentResponse processMessage(String sessionId, String userMessage) { // 获取或创建会话状态 ConversationState state sessionStates.computeIfAbsent(sessionId, k - new ConversationState()); // 更新对话历史 state.addUserMessage(userMessage); // 判断是否需要工具调用 ToolSelection toolSelection decideToolUsage(userMessage, state); if (toolSelection.needsTool()) { return handleWithTools(sessionId, userMessage, state, toolSelection); } else { return handleWithRAG(sessionId, userMessage, state); } } private ToolSelection decideToolUsage(String message, ConversationState state) { // 基于规则或模型判断是否需要工具调用 String lowerMessage message.toLowerCase(); if (lowerMessage.contains(天气) || lowerMessage.contains(weather)) { return new ToolSelection(weather, true); } if (lowerMessage.contains(计算) || lowerMessage.contains(calculate)) { return new ToolSelection(calculator, true); } if (lowerMessage.contains(时间) || lowerMessage.contains(time)) { return new ToolSelection(time, true); } // 复杂场景可以用模型判断 return new ToolSelection(rag, false); } private AgentResponse handleWithTools(String sessionId, String userMessage, ConversationState state, ToolSelection selection) { try { String toolResult switch (selection.toolName()) { case weather - toolService.getWeather(北京); // 简化处理 case calculator - toolService.calculate(userMessage); case time - toolService.getCurrentTime(); default - 工具暂不可用; }; // 将工具结果融入对话 String enhancedPrompt String.format( 用户问题%s 工具执行结果%s 请基于工具结果给出友好的回答 , userMessage, toolResult); String aiResponse chatModel.generate(enhancedPrompt); state.addAssistantMessage(aiResponse); return new AgentResponse(aiResponse, true, selection.toolName()); } catch (Exception e) { // 工具调用失败时的降级处理 return handleWithRAG(sessionId, userMessage, state); } } private AgentResponse handleWithRAG(String sessionId, String userMessage, ConversationState state) { String response knowledgeService.queryWithRAG(userMessage); state.addAssistantMessage(response); return new AgentResponse(response, false, rag); } // 内部状态类 private static class ConversationState { private final ListString messageHistory new ArrayList(); private static final int MAX_HISTORY 10; public void addUserMessage(String message) { messageHistory.add(用户: message); trimHistory(); } public void addAssistantMessage(String message) { messageHistory.add(助手: message); trimHistory(); } private void trimHistory() { if (messageHistory.size() MAX_HISTORY) { messageHistory.subList(0, messageHistory.size() - MAX_HISTORY).clear(); } } public String getConversationContext() { return String.join(\n, messageHistory); } } private record ToolSelection(String toolName, boolean needsTool) {} }3.4 工具服务实现Agent 依赖的工具服务需要具备容错和监控能力Service public class ToolService { private static final Logger logger LoggerFactory.getLogger(ToolService.class); public String getWeather(String city) { try { // 模拟外部 API 调用 logger.info(查询天气信息城市: {}, city); Thread.sleep(100); // 模拟网络延迟 // 实际项目中这里调用真实天气 API return String.format(城市%s的天气晴温度25°C, city); } catch (Exception e) { logger.error(天气查询失败, e); return 天气服务暂时不可用; } } public String calculate(String expression) { try { // 简单的数学表达式计算 logger.info(计算表达式: {}, expression); // 提取数学表达式简化处理 String mathExpr expression.replaceAll([^0-9\\-*/()], ); ScriptEngineManager mgr new ScriptEngineManager(); ScriptEngine engine mgr.getEngineByName(JavaScript); Object result engine.eval(mathExpr); return String.format(计算结果: %s %s, mathExpr, result); } catch (Exception e) { logger.error(计算失败, e); return 无法计算该表达式; } } public String getCurrentTime() { DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); return 当前时间: LocalDateTime.now().format(formatter); } }4. 控制器层和 API 设计提供 RESTful API 供前端调用RestController RequestMapping(/api/agent) public class AgentController { private final AgentService agentService; public AgentController(AgentService agentService) { this.agentService agentService; } PostMapping(/chat) public ResponseEntityAgentResponse chat( RequestHeader(X-Session-Id) String sessionId, RequestBody ChatRequest request) { try { AgentResponse response agentService.processMessage(sessionId, request.getMessage()); return ResponseEntity.ok(response); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new AgentResponse(系统繁忙请稍后重试, false, error)); } } PostMapping(/knowledge) public ResponseEntityString addDocument(RequestBody DocumentRequest request) { try { agentService.getKnowledgeService().addDocument(request.getTitle(), request.getContent()); return ResponseEntity.ok(文档添加成功); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(文档添加失败); } } // 请求响应DTO public record ChatRequest(String message) {} public record DocumentRequest(String title, String content) {} } // 统一响应结构 public record AgentResponse(String content, boolean usedTool, String toolType) {}5. 运行验证和测试5.1 启动应用创建主启动类SpringBootApplication public class AiAgentApplication { public static void main(String[] args) { SpringApplication.run(AiAgentApplication.class, args); } Bean public CommandLineRunner demo(KnowledgeService knowledgeService) { return args - { // 初始化示例知识库 knowledgeService.addDocument(公司介绍, 我们是一家专注于企业级软件开发的科技公司成立于2010年...); knowledgeService.addDocument(产品信息, 主要产品包括CRM系统、ERP解决方案和AI助手平台...); System.out.println(AI Agent 服务启动完成知识库初始化成功); }; } }5.2 测试 API使用 curl 或 Postman 测试接口# 添加知识文档 curl -X POST http://localhost:8080/api/agent/knowledge \ -H Content-Type: application/json \ -d {title:技术支持, content:我们的技术支持团队工作日9:00-18:00在线} # 发起对话 curl -X POST http://localhost:8080/api/agent/chat \ -H Content-Type: application/json \ -H X-Session-Id: test-session-001 \ -d {message:你们的技术支持时间是什么}预期响应{ content: 根据知识库信息我们的技术支持团队在工作日的9:00到18:00在线为您服务。, usedTool: false, toolType: rag }6. 生产环境注意事项6.1 性能与稳定性优化向量检索优化使用专业向量数据库PgVector、Milvus、Chroma实现增量索引和批量更新设置合理的索引刷新策略模型调用优化Bean public OpenAiChatModel openAiChatModel() { return OpenAiChatModel.builder() .apiKey(apiKey) .model(model) .temperature(0.7) .maxTokens(2000) .timeout(Duration.ofSeconds(30)) // 设置超时 .logRequests(true) // 生产环境谨慎开启 .logResponses(true) .build(); }6.2 错误处理和降级策略Service public class ResilientAgentService { private final CircuitBreaker circuitBreaker; public ResilientAgentService() { this.circuitBreaker CircuitBreaker.ofDefaults(aiService); } public String fallbackResponse(Throwable ex) { logger.warn(AI服务降级返回默认响应, ex); return 当前服务繁忙请稍后重试。; } public AgentResponse processMessageWithResilience(String sessionId, String message) { return circuitBreaker.executeSupplier(() - agentService.processMessage(sessionId, message)); } }6.3 监控和可观测性关键指标监控Token 使用量和成本统计请求响应时间和慢查询工具调用成功率和错误率会话活跃度和用户行为Component public class AgentMetrics { private final MeterRegistry meterRegistry; private final Counter tokenCounter; private final Timer requestTimer; public AgentMetrics(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; this.tokenCounter Counter.builder(ai.token.usage) .description(Token使用量) .register(meterRegistry); this.requestTimer Timer.builder(ai.request.duration) .description(请求处理时间) .register(meterRegistry); } public void recordTokenUsage(int tokens) { tokenCounter.increment(tokens); } public Timer.Sample startTiming() { return Timer.start(meterRegistry); } public void stopTiming(Timer.Sample sample, String operation) { sample.stop(requestTimer.tag(operation, operation)); } }7. 常见问题排查7.1 模型连接问题问题现象API 调用超时或认证失败排查步骤检查网络连接和代理设置验证 API Key 是否正确且有余额确认模型名称和服务端点查看详细错误日志和异常堆栈解决方案# 调整超时配置 spring: ai: openai: base-url: https://api.openai.com/v1 api-key: ${API_KEY} connect-timeout: 10s read-timeout: 30s7.2 内存泄漏问题问题现象服务运行一段时间后内存占用持续增长排查步骤使用 JProfiler 或 VisualVM 分析内存使用检查会话状态管理是否及时清理验证向量存储的内存增长情况解决方案Scheduled(fixedRate 300000) // 5分钟清理一次 public void cleanupExpiredSessions() { sessionStates.entrySet().removeIf(entry - isSessionExpired(entry.getKey())); }7.3 性能瓶颈排查瓶颈点现象优化方案向量检索响应慢CPU 占用高使用专业向量数据库建立索引模型调用API 延迟高超时多实现缓存批量处理设置超时工具调用外部服务不稳定增加重试机制降级处理8. 学习路径和进阶方向8.1 Java 开发者的 AI 学习清单第一阶段1-2周基础概念理解 LLM 基本原理和 API 调用掌握 Prompt Engineering 基础学习 Token 计算和成本控制第二阶段2-4周RAG 实践文档解析和分块策略向量嵌入和相似度计算检索质量评估和优化第三阶段2-4周Agent 开发工具调用和工作流设计状态管理和会话持久化错误处理和降级策略第四阶段持续工程化性能监控和成本统计安全权限和审计日志部署运维和扩缩容8.2 项目进阶建议从当前基础项目出发可以逐步扩展集成真实向量数据库替换内存存储使用 PgVector 或 Milvus实现复杂工作流多 Agent 协作、人工审核节点加入评估体系自动化的回答质量评估和反馈循环优化用户体验流式输出、实时编辑、多轮对话管理这个完整的 Java AI Agent 项目展示了如何将传统 Java 工程能力与新兴 AI 技术结合既保持了系统的稳定性和可维护性又具备了现代 AI 应用的智能交互能力。实际项目中需要根据具体业务需求调整架构设计和功能实现。