Java开发者转型AI应用开发:Spring AI+RAG+MCP实战指南

发布时间:2026/8/21 10:37:36
Java开发者转型AI应用开发:Spring AI+RAG+MCP实战指南 如果你是一名Java开发者最近在招聘网站上看到“智能体工程师”、“AI应用开发”等岗位要求时是否感到一丝焦虑Spring Boot、MyBatis玩得再熟面对“RAG”、“Agent”、“MCP”这些新词是不是觉得像在看天书别担心这种感觉很正常。AI应用开发正在经历一场“框架化”的革命其核心不再是炼丹般的模型调参而是如何用你熟悉的Java工程化思维去构建可靠、可扩展的AI能力。这恰恰是Java开发者的巨大机会。本文要解决的正是这个核心痛点如何让你已有的Java技能平滑迁移到AI应用开发而不是从零学习Python或深度学习。我们将聚焦于当前企业级AI开发中最实用、最落地的技术栈组合Spring AI Spring AI Alibaba RAG MCP FastAPI。这不是一个简单的工具介绍而是一套完整的、面向生产的解决方案拆解。读完本文你将彻底理解为什么是这套组合它们各自解决了什么问题组合起来又形成了怎样的能力闭环。作为Java开发者你的学习路径是什么如何避开概念陷阱直击工程实现。从零到一的完整实践。我们将构建一个具备私有知识库问答RAG和外部工具调用MCP能力的AI智能体后端。我们的判断是未来的AI应用开发工程化能力的重要性将远超对单一模型的精通。Spring生态提供的标准化、模块化、可观测性正是将AI能力“工业化”的关键。现在让我们开始这场面向2026年的技术储备之旅。1. 重新定义问题Java开发者转型AI到底在学什么很多教程一上来就讲Prompt工程、讲向量数据库这容易让开发者迷失。我们必须先厘清一个根本问题作为Java后端开发者你的核心价值在于构建稳定、高效、易维护的服务而不是成为大模型专家。因此转型AI应用开发本质是学习如何将大模型作为一种新的“计算资源”或“服务组件”集成到你已有的微服务架构中。传统后端 vs. AI增强型后端传统后端接收请求 → 业务逻辑CRUD、规则计算→ 访问数据库MySQL/Redis→ 返回响应。AI增强型后端接收请求 →AI路由决策用哪个模型/工具→调用大模型完成理解、生成、推理→可能调用工具搜索、数据库、API→ 组织结果 → 返回响应。你会发现数据库访问变成了“向量数据库传统数据库”的混合查询业务逻辑中嵌入了对模型响应的解析与校验。Spring AI的出现就是为了标准化这个“调用大模型”的环节就像Spring Data标准化了数据库访问一样。本教程技术栈角色解析Spring AI核心抽象层。它定义了一套统一的API如ChatClientEmbeddingClient来对接OpenAI、Azure OpenAI、Anthropic、本地模型等。你用一套代码就能灵活切换底层模型提供商。Spring AI Alibaba阿里云生态集成。它在Spring AI基础上提供了对阿里云灵积模型服务平台DashScope的深度集成包括千问、通义等模型以及符合国内监管要求的便捷访问方式。这是国内项目落地的重要选项。RAG检索增强生成解决模型“知识陈旧”和“幻觉”问题的核心模式。通过将私有文档向量化存储在提问时先检索相关片段再连同片段和问题一起交给模型生成答案极大提升答案的准确性和专业性。MCPModel Context Protocol工具调用与扩展协议。由Anthropic提出用于标准化AI模型与外部工具如数据库、搜索引擎、业务系统之间的交互方式。你可以理解为AI版的“JDBC”或“RPC”协议。通过MCP Server暴露工具AI Agent就能安全、可控地使用它们。FastAPI为什么是Python在AI生态中很多先进的库如LangChain的某些组件、专门的向量数据库客户端、模型微调工具仍是Python首选。FastAPI常用于快速构建轻量级、高性能的AI服务或工具端点。在我们的架构中它可能作为独立的工具服务或MCP Server存在与Java主服务通过HTTP或gRPC通信。Java开发者需要学会与之协作而非取代。这套组合拳让你能用JavaSpring构建AI应用的主干和核心业务逻辑同时又能灵活集成Python生态的前沿工具兼顾了稳定性与前沿性。2. 环境准备搭建跨语言开发环境由于涉及Java和Python生态我们需要一个清晰的开发环境设置。请确保你已安装以下基础软件Java开发环境JDK 17 或 21Spring AI 推荐使用LTS版本。Maven 3.6 或 Gradle 7.x本文以Maven为例。IDEIntelliJ IDEA推荐或 Eclipse with STS。可选Docker Docker Compose用于运行向量数据库等中间件。Python开发环境用于FastAPI工具服务Python 3.9 - 3.11建议使用3.10以保证兼容性。创建虚拟环境这是最佳实践避免包冲突。# 在项目工具服务目录下 python -m venv venv # 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate安装基础包我们稍后会给出完整的requirements.txt。关键中间件向量数据库我们选择PgVectorPostgreSQL扩展作为示例因为它结合了成熟的PostgreSQL和向量检索能力适合生产环境。使用Docker快速启动docker run -d \ --name pgvector \ -e POSTGRES_USERai_user \ -e POSTGRES_PASSWORDai_password \ -e POSTGRES_DBai_knowledge \ -p 5432:5432 \ pgvector/pgvector:pg163. 核心流程拆解构建一个AI知识库问答助手我们的目标是构建一个服务用户提问服务先从私有知识库RAG中查找相关资料再结合资料让大模型生成回答并且模型在需要时可以调用一个查询天气的模拟工具通过MCP。整体架构图文字描述用户请求 - [Spring Boot 应用] - (RAG流程) - [向量检索] - [组合Prompt] - [Spring AI ChatClient] | | [PgVector] [MCP Client] | | [文档向量] [FastAPI工具服务] | [模拟天气API]核心步骤知识库入库将PDF、Word等文档切分、向量化存入PgVector。问答接口接收用户问题将其向量化在PgVector中进行相似性检索得到相关文本片段。提示词工程将问题、检索到的片段、系统指令组合成最终的Prompt。模型调用与工具决策通过Spring AI调用大模型。如果模型在回答过程中决定需要查询天气它会输出一个结构化的工具调用请求。工具调用执行Spring Boot应用通过MCP Client将工具调用请求转发给FastAPI工具服务获取结果。结果整合与返回将工具执行结果返回给模型模型生成最终回答返回给用户。接下来我们分模块实现。4. 模块一Spring AI Spring AI Alibaba 基础集成首先我们搭建一个能简单对话的Spring Boot应用。第1步创建Spring Boot项目使用 start.spring.io 或IDE创建项目。Project:MavenLanguage:JavaSpring Boot:3.2.x (Spring AI 当前稳定支持)Dependencies:Spring Web,Lombok(可选简化代码)第2步添加Spring AI依赖在pom.xml中添加Spring AI的BOM和具体提供商依赖。这里我们以OpenAI和阿里云通义千问为例。!-- pom.xml -- project !-- ... 其他配置 ... -- properties spring-ai.version0.8.1/spring-ai.version !-- 请使用最新稳定版 -- /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- Spring Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI OpenAI Starter -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency !-- 或者使用 Spring AI Alibaba (DashScope) -- !-- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-ai-alibaba-spring-boot-starter/artifactId version2023.0.1.0/version !-- 版本需对应Spring Cloud Alibaba -- /dependency -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies /project第3步配置API密钥在application.yml中配置。切记不要将密钥提交到代码仓库# application.yml spring: ai: openai: api-key: ${OPENAI_API_KEY:sk-your-key-here} # 从环境变量读取更安全 chat: options: model: gpt-3.5-turbo temperature: 0.7 # 如果使用Spring AI Alibaba # alibaba: # dashscope: # api-key: ${DASHSCOPE_API_KEY} # chat: # options: # model: qwen-max第4步创建简单的对话控制器// 文件路径src/main/java/com/example/aidemo/controller/ChatController.java package com.example.aidemo.controller; import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Map; RestController RequiredArgsConstructor public class ChatController { private final ChatClient chatClient; // 基础对话 GetMapping(/ai/chat) public String chat(RequestParam(value message, defaultValue 你好) String message) { return chatClient.call(message); } // 使用PromptTemplate进行结构化对话 GetMapping(/ai/chat/adv) public String advancedChat(RequestParam String topic) { PromptTemplate promptTemplate new PromptTemplate( 请用简洁的语言为一位软件工程师解释一下什么是{ topic }。 回答不超过三句话。 ); Prompt prompt promptTemplate.create(Map.of(topic, topic)); ChatResponse response chatClient.call(prompt); return response.getResult().getOutput().getContent(); } }启动应用访问http://localhost:8080/ai/chat?message你好世界和http://localhost:8080/ai/chat/adv?topic微服务你应该能收到模型的回复。至此Spring AI的基础集成完成。它抽象了底层API调用让你像使用JdbcTemplate一样使用大模型。5. 模块二构建RAG知识库系统这是核心。我们将实现文档上传、向量化存储和检索。第1步添加向量数据库和Embedding依赖!-- pom.xml 新增依赖 -- dependencies !-- Spring AI Embedding (用于文本向量化) -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai/artifactId !-- 或 spring-ai-alibaba -- /dependency !-- Spring Data JPA 和 PostgreSQL 驱动 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency !-- PgVector 支持 (需要对应方言) -- dependency groupIdcom.pgvector/groupId artifactIdpgvector/artifactId version0.1.5/version /dependency /dependencies第2步配置数据源和JPA# application.yml 新增配置 spring: datasource: url: jdbc:postgresql://localhost:5432/ai_knowledge username: ai_user password: ai_password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update # 生产环境请使用validate或none配合迁移工具 properties: hibernate: dialect: com.example.aidemo.config.PgVectorDialect # 自定义方言见下文 show-sql: true第3步自定义PgVector方言和实体由于JPA默认不支持vector类型我们需要自定义方言。// 文件路径src/main/java/com/example/aidemo/config/PgVectorDialect.java package com.example.aidemo.config; import org.hibernate.dialect.PostgreSQLDialect; import java.sql.Types; public class PgVectorDialect extends PostgreSQLDialect { public PgVectorDialect() { super(); registerColumnType(Types.OTHER, vector); } }// 文件路径src/main/java/com/example/aidemo/entity/DocumentChunk.java package com.example.aidemo.entity; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; import java.util.List; Entity Table(name document_chunks, indexes { Index(name idx_chunk_embedding, columnList embedding vector_l2_ops) // 为向量检索创建索引 }) Data public class DocumentChunk { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(nullable false) private String documentId; // 原文档ID Column(columnDefinition TEXT, nullable false) private String content; // 文本分块内容 Column(columnDefinition vector(1536)) // OpenAI text-embedding-3-small 维度为1536 JdbcTypeCode(SqlTypes.OTHER) private ListFloat embedding; // 向量数组 private Integer chunkIndex; // 块序号 private String metadata; // 可存储来源、页码等JSON }第4步实现文档处理与向量化服务这里涉及文件解析如Apache Tika、文本分块如langchain4j或简单按句/词分块、向量化调用。为简化我们假设文本已分好块。// 文件路径src/main/java/com/example/aidemo/service/RagService.java package com.example.aidemo.service; import com.example.aidemo.entity.DocumentChunk; import com.example.aidemo.repository.DocumentChunkRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.embedding.EmbeddingRequest; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; Service Slf4j RequiredArgsConstructor public class RagService { private final EmbeddingClient embeddingClient; private final DocumentChunkRepository chunkRepository; /** * 将文本列表向量化并存储 */ Transactional public void ingestDocuments(String documentId, ListString chunks) { ListDocumentChunk documentChunks new ArrayList(); for (int i 0; i chunks.size(); i) { String chunkText chunks.get(i); // 1. 生成向量 ListDouble embeddingList embeddingClient.embed(chunkText); // 转换为Float列表存储PgVector常用Float ListFloat floatEmbedding embeddingList.stream().map(Double::floatValue).toList(); // 2. 构建实体 DocumentChunk chunk new DocumentChunk(); chunk.setDocumentId(documentId); chunk.setContent(chunkText); chunk.setEmbedding(floatEmbedding); chunk.setChunkIndex(i); chunk.setMetadata({\source\:\manual_upload\}); documentChunks.add(chunk); } // 3. 批量保存 chunkRepository.saveAll(documentChunks); log.info(已入库文档 {} 的 {} 个分块, documentId, chunks.size()); } /** * 检索与问题最相关的文本块 */ public ListString retrieveRelevantChunks(String query, int topK) { // 1. 将问题向量化 ListDouble queryEmbedding embeddingClient.embed(query); ListFloat floatQueryEmbedding queryEmbedding.stream().map(Double::floatValue).toList(); // 2. 调用自定义Repository进行向量相似性搜索 ListDocumentChunk chunks chunkRepository.findTopKNearest(floatQueryEmbedding, topK); // 3. 提取文本内容 return chunks.stream().map(DocumentChunk::getContent).toList(); } }第5步自定义Repository实现向量搜索// 文件路径src/main/java/com/example/aidemo/repository/DocumentChunkRepository.java package com.example.aidemo.repository; import com.example.aidemo.entity.DocumentChunk; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; Repository public interface DocumentChunkRepository extends JpaRepositoryDocumentChunk, Long { /** * 使用PgVector的 运算符欧氏距离进行相似性搜索。 * 注意需要确保embedding字段已建立向量索引如ivfflat或hnsw以提高性能。 */ Query(value SELECT * FROM document_chunks ORDER BY embedding CAST(:queryVector AS vector) LIMIT :topK , nativeQuery true) ListDocumentChunk findTopKNearest(Param(queryVector) ListFloat queryVector, Param(topK) int topK); }第6步创建RAG问答控制器// 文件路径src/main/java/com/example/aidemo/controller/RagController.java package com.example.aidemo.controller; import com.example.aidemo.service.RagService; import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; RestController RequestMapping(/rag) RequiredArgsConstructor public class RagController { private final RagService ragService; private final ChatClient chatClient; PostMapping(/ingest) public String ingest(RequestParam String docId, RequestBody ListString chunks) { ragService.ingestDocuments(docId, chunks); return 文档分块已向量化存储; } GetMapping(/ask) public String ask(RequestParam String question) { // 1. 检索相关上下文 ListString relevantChunks ragService.retrieveRelevantChunks(question, 3); // 取前3个最相关的块 String context String.join(\n---\n, relevantChunks); // 2. 构建增强后的Prompt PromptTemplate promptTemplate new PromptTemplate( 请基于以下上下文信息回答问题。如果上下文信息不足以回答问题请直接说“根据已有信息无法回答”。 上下文 {context} 问题{question} 请给出专业、准确的回答 ); MapString, Object variables Map.of(context, context, question, question); Prompt prompt promptTemplate.create(variables); // 3. 调用模型生成答案 return chatClient.call(prompt).getResult().getOutput().getContent(); } }现在你可以通过/rag/ingest接口注入知识例如公司产品手册然后通过/rag/ask进行基于知识的问答。RAG的核心流程已经打通。6. 模块三集成MCP与FastAPI工具服务MCP的核心思想是让模型能安全地调用外部工具。我们将创建一个简单的天气查询工具作为示例。第1步创建FastAPI工具服务Python在项目根目录下创建tool-service文件夹并添加以下文件# tool-service/requirements.txt fastapi0.104.0 uvicorn0.24.0 pydantic2.5.0 mcp[cli]1.0.0 # 安装MCP SDK# tool-service/main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional import uvicorn app FastAPI(titleMCP Tool Server) # 模拟一个天气数据库 fake_weather_db { beijing: {city: Beijing, temperature: 22°C, condition: Sunny}, shanghai: {city: Shanghai, temperature: 25°C, condition: Cloudy}, hangzhou: {city: Hangzhou, temperature: 24°C, condition: Light Rain}, } class WeatherQuery(BaseModel): city: str class WeatherResponse(BaseModel): city: str temperature: str condition: str app.post(/weather, response_modelWeatherResponse) async def get_weather(query: WeatherQuery): 根据城市名查询天气模拟 city_key query.city.lower() weather fake_weather_db.get(city_key) if not weather: raise HTTPException(status_code404, detailfWeather data for {query.city} not found.) return WeatherResponse(**weather) app.get(/health) async def health_check(): return {status: healthy} if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)启动工具服务cd tool-service python main.py。现在有一个运行在http://localhost:8000的天气查询API。第2步在Java中创建MCP Client简化版Spring AI 社区正在积极集成MCP。目前我们可以先实现一个简单的HTTP客户端来调用工具。未来可以使用官方的spring-ai-mcp模块。// 文件路径src/main/java/com/example/aidemo/service/ToolService.java package com.example.aidemo.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; Service Slf4j public class ToolService { private final RestClient restClient; public ToolService(Value(${tool.service.base-url:http://localhost:8000}) String baseUrl) { this.restClient RestClient.builder() .baseUrl(baseUrl) .defaultHeader(Content-Type, application/json) .build(); } public String callWeatherTool(String cityName) { try { String requestBody String.format({\city\: \%s\}, cityName); JsonNode response restClient.post() .uri(/weather) .body(requestBody) .retrieve() .body(JsonNode.class); // 格式化结果 return String.format(城市%s温度%s天气状况%s, response.get(city).asText(), response.get(temperature).asText(), response.get(condition).asText()); } catch (Exception e) { log.error(调用天气工具失败, e); return 无法获取该城市的天气信息。; } } }第3步实现支持工具调用的AI对话这需要利用大模型的“Function Calling”或“Tool Use”能力。我们以OpenAI的gpt-3.5-turbo或gpt-4为例它们支持在Prompt中定义工具并返回结构化的工具调用请求。首先定义一个工具调用的DTO// 文件路径src/main/java/com/example/aidemo/dto/ToolCall.java package com.example.aidemo.dto; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; Data public class ToolCall { private String id; private String type function; private Function function; Data public static class Function { private String name; private String arguments; // JSON字符串 } }然后创建一个更复杂的对话服务它能够解析模型的工具调用请求执行工具并将结果返回给模型进行最终生成。// 文件路径src/main/java/com/example/aidemo/service/AgentChatService.java package com.example.aidemo.service; import com.example.aidemo.dto.ToolCall; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; Service Slf4j RequiredArgsConstructor public class AgentChatService { private final ChatClient chatClient; private final ToolService toolService; private final ObjectMapper objectMapper; public String chatWithTools(String userMessage) throws JsonProcessingException { // 1. 系统消息定义工具和能力 String systemPrompt 你是一个有帮助的AI助手可以回答用户问题并且在需要时使用工具。 你拥有的工具 1. get_weather: 查询指定城市的天气情况。 使用工具的规则 - 当用户询问某个城市的天气时你必须使用get_weather工具。 - 工具调用结果返回后结合结果生成最终回复。 ; // 2. 构建消息历史简化版单轮 ListMessage messages new ArrayList(); messages.add(new SystemMessage(systemPrompt)); messages.add(new UserMessage(userMessage)); // 3. 第一次调用模型期待它可能返回工具调用 ChatResponse response chatClient.call(new Prompt(messages)); AssistantMessage assistantMessage (AssistantMessage) response.getResult().getOutput(); String assistantContent assistantMessage.getContent(); // 4. 检查是否有工具调用这里简化处理实际需解析模型返回的特定格式如OpenAI的tool_calls // 注意Spring AI 0.8.x 对工具调用的原生支持在完善中。以下为概念性代码。 // 实际开发中应使用 chatClient.call(Prompt) 并配置 ChatOptions 来启用 function calling。 // 此处假设模型返回的JSON中包含工具调用信息。 // 模拟解析工具调用 if (assistantContent ! null assistantContent.contains(\tool_calls\)) { JsonNode root objectMapper.readTree(assistantContent); JsonNode toolCalls root.path(tool_calls); if (toolCalls.isArray()) { ListMessage newMessages new ArrayList(messages); newMessages.add(assistantMessage); // 添加助手的请求 for (JsonNode toolCallNode : toolCalls) { String functionName toolCallNode.path(function).path(name).asText(); String arguments toolCallNode.path(function).path(arguments).asText(); // 5. 执行工具 String toolResult ; if (get_weather.equals(functionName)) { JsonNode args objectMapper.readTree(arguments); String city args.path(city).asText(); toolResult toolService.callWeatherTool(city); } // 6. 将工具执行结果作为新的消息加入对话 newMessages.add(new ToolMessage(toolResult, toolCallNode.path(id).asText())); } // 7. 再次调用模型让它基于工具结果生成最终回复 ChatResponse finalResponse chatClient.call(new Prompt(newMessages)); return finalResponse.getResult().getOutput().getContent(); } } // 如果没有工具调用直接返回模型回复 return assistantContent ! null ? assistantContent : 未收到有效回复; } }注意上述工具调用解析是高度简化的。在实际项目中你需要根据所选模型提供商OpenAI、Anthropic、DashScope的官方工具调用格式使用Spring AI提供的相应ChatOptions进行配置和解析。Spring AI 正在不断完善对Function Calling和MCP的支持。7. 运行、测试与效果验证启动所有服务PgVector数据库docker run ...(已在环境准备中启动)FastAPI工具服务cd tool-service python main.pySpring Boot应用在IDE中运行AIDemoApplication或使用mvn spring-boot:run测试RAG功能注入知识使用Postman或curl向POST http://localhost:8080/rag/ingest?docIdhandbook1发送JSON body例如[ Spring AI 是Spring官方推出的AI应用开发框架。, RAG通过检索外部知识来增强大模型生成答案的准确性。, MCP协议用于标准化AI模型与外部工具的交互。 ]进行问答访问GET http://localhost:8080/rag/ask?question什么是RAG。你应该能得到一个基于注入知识的回答而不是模型的通用知识。测试工具调用功能访问GET http://localhost:8080/ai/chat?message北京天气怎么样。在配置了正确的工具调用后模型应该会尝试调用天气工具。你需要根据实际的模型响应格式调整AgentChatService中的解析逻辑。验证关键点RAG是否生效问一个知识库中明确记载的问题答案应包含你注入的原文片段。工具调用流程是否通查看Spring Boot应用日志确认是否发起了对http://localhost:8000/weather的HTTP调用。服务是否健壮尝试传入空问题、不存在的城市等观察异常处理。8. 常见问题与排查思路问题现象可能原因排查方式解决方案应用启动失败BeanCreationExceptionSpring AI 版本与 Spring Boot 版本不兼容检查pom.xml中spring-ai.version与Spring Boot版本的兼容性矩阵。使用Spring AI官方文档推荐的版本组合。调用/ai/chat接口超时或报错401API密钥错误或网络不通1. 检查application.yml中的api-key是否正确。2. 检查是否能访问对应的API端点如api.openai.com。1. 使用环境变量传递密钥。2. 检查网络代理设置。向量检索速度极慢PgVector未创建向量索引连接数据库检查document_chunks表是否有embedding列的索引。在数据入库后执行SQL创建索引CREATE INDEX ON document_chunks USING ivfflat (embedding vector_l2_ops) WITH (lists 100);RAG回答与知识库无关1. 向量化模型不匹配2. 检索到的topK值太小或太大3. Prompt指令不清晰1. 确认入库和检索使用相同的EmbeddingClient。2. 调整retrieveRelevantChunks中的topK参数。3. 检查系统Prompt是否强调“基于上下文”。1. 确保使用相同的模型。2. 尝试topK3~5。3. 强化Prompt例如“你必须且只能使用以下上下文。”工具调用未被触发1. 模型不支持工具调用2. Prompt中工具描述不清晰3. 模型返回格式解析错误1. 确认使用的模型如gpt-3.5-turbo-1106及以上支持function calling。2. 查看模型返回的原始消息检查是否有tool_calls字段。3. 调试AgentChatService打印模型原始响应。1. 更换支持工具调用的模型。2. 参考OpenAI等官方文档编写清晰的工具描述。3. 使用Spring AI未来版本对工具调用的原生支持。java.lang.ClassNotFoundException: pgvector.PGvectorPgVector JDBC驱动未正确加载或版本冲突检查pom.xml中pgvector依赖版本并确保其与PostgreSQL JDBC驱动兼容。尝试使用其他版本或检查是否有其他依赖覆盖了类路径。9. 最佳实践与工程建议环境与配置分离API密钥、数据库密码等敏感信息必须通过环境变量或配置中心如Apollo, Nacos管理绝不入库。为开发、测试、生产环境准备不同的application-{profile}.yml文件。向量数据库优化索引选择PgVector支持ivfflat和hnsw索引。对于海量数据100万条hnsw通常查询性能更好但创建更慢。根据数据量和查询模式选择。分块策略文本分块大小chunk size和重叠overlap对检索质量影响巨大。建议根据文档类型技术文档、合同、对话记录进行实验调整。元数据过滤在findTopKNearest查询中增加元数据过滤如文档来源、日期可以大幅提升检索精度。提示词工程系统指令固化将RAG的系统指令、工具描述等固化在代码或配置文件中便于管理和A/B测试。少样本学习Few-Shot在Prompt中提供几个高质量的问答示例能显著提升模型在特定任务上的表现。输出结构化要求模型以JSON等格式输出便于后端解析和后续处理。服务治理与可观测性限流与降级对ChatClient和EmbeddingClient的调用必须添加限流Resilience4j和熔断降级防止因模型服务不稳定导致雪崩。链路追踪集成Micrometer和Zipkin对一次用户问答的完整链路检索、模型调用、工具调用进行追踪便于性能分析和问题定位。日志与监控详细记录每次问答的原始问题、检索到的上下文、模型请求/响应、工具调用详情。这不仅是调试的需要更是后续优化RAG和评估模型效果的数据基础。架构演进方向异步化对于长文本处理、复杂工作流将向量化、模型调用等耗时操作异步化通过消息队列如RabbitMQ, Kafka解耦提升接口响应速度。缓存策略对常见问题FAQ的问答结果进行缓存减少对模型和向量数据库的重复调用。多路召回与重排序成熟的RAG系统不会只依赖向量检索。可以结合关键词检索如Elasticsearch、知识图谱查询等多路召回结果再用一个轻量级模型进行重排序Rerank选出最相关的片段。通过以上步骤我们完成了一个融合了Spring AI基础对话、RAG知识库增强和MCP工具调用概念的AI应用后端。虽然MCP的集成部分为了简化使用了直接的HTTP调用但它清晰地展示了AI Agent的工作流程感知用户输入→ 规划决定调用工具→ 执行调用工具→ 反思整合结果并输出。对于Java开发者而言掌握Spring AI意味着掌握了以熟悉的方式接入AI能力的钥匙理解RAG和MCP则意味着你能构建真正有用、可控、可扩展的AI应用。这套技术栈的学习曲线是平滑的它复用的是你已有的分布式系统、数据库、API设计经验只是增加了一层“模型交互”的抽象。接下来的学习方向可以聚焦于深入Spring AI的高级特性如流式响应、多模态、探索更专业的向量数据库如Milvus, Weaviate、研究Agentic RAG的复杂工作流设计以及关注Spring AI社区对MCP协议的原生支持进展。AI工程化的时代正是后端开发者大展身手的舞台。