Spring AI 框架实战:Java 后端集成大模型的架构设计与工程落地
Spring AI 框架实战Java 后端集成大模型的架构设计与工程落地一、API 调用之外的工程挑战Java 应用集成 LLM 的真实痛点当团队决定在 Java 后端系统中集成大语言模型时最常见的起点是直接用RestTemplate或WebClient调用 OpenAI 的 HTTP API。这种做法在 Demo 阶段看似可行但一旦进入生产环境问题便接踵而至不同模型供应商的 API 格式各异OpenAI、Azure OpenAI、Anthropic、国内模型切换供应商需要重写调用逻辑流式响应SSE的处理缺乏统一抽象Prompt 模板散落在业务代码中难以版本管理和 A/B 测试调用失败时的重试与降级策略缺失导致级联故障。Spring AI 正是为了解决这些工程痛点而诞生的框架。它提供了一套统一的模型调用抽象、Prompt 管理机制、向量存储接口和 RAG检索增强生成管道让 Java 开发者能够以 Spring 风格的方式集成大模型而非在业务代码中堆砌 HTTP 调用。本文将从架构视角出发完整演示 Spring AI 在生产环境中的集成方案覆盖模型调用、Prompt 管理、RAG 管道和可观测性四个核心环节。二、从 ChatModel 到 VectorStoreSpring AI 的核心抽象与协作机制Spring AI 的架构设计遵循 Spring 的一贯哲学通过接口抽象屏蔽实现差异通过自动装配降低集成成本。flowchart TB subgraph 应用层 A[业务服务] -- B[ChatClient] A -- C[PromptTemplate] A -- D[RAG 管道] end subgraph Spring AI 核心抽象 B -- E[ChatModel 接口] C -- F[Prompt 管理] D -- G[DocumentReader] D -- H[DocumentTransformer] D -- I[VectorStore 接口] end subgraph 模型实现 E -- J[OpenAI ChatModel] E -- K[Azure OpenAI] E -- L[Ollama ChatModel] E -- M[其他供应商] end subgraph 向量存储实现 I -- N[PgVector] I -- O[Chroma] I -- P[Milvus] I -- Q[Redis] end subgraph 可观测性 B -- R[Micrometer 指标] E -- S[Token 用量统计] B -- T[OpenTelemetry Tracing] endChatModel 接口是 Spring AI 的核心抽象定义了与 LLM 交互的统一契约。无论是 OpenAI、Azure OpenAI 还是本地部署的 Ollama都通过实现ChatModel接口来适配。应用代码只依赖接口切换模型供应商只需修改配置无需改动业务逻辑。PromptTemplate提供了参数化的 Prompt 管理能力。Prompt 不再硬编码在 Java 代码中而是以模板文件的形式外部化管理支持变量替换和条件逻辑。这使得 Prompt 的版本管理、A/B 测试和跨团队协作成为可能。VectorStore 接口统一了向量存储的访问方式。无论是 PgVector、Chroma 还是 Milvus应用代码都通过VectorStore接口进行向量写入和相似度检索底层实现可随时替换。三、生产级 Spring AI 集成从模型调用到 RAG 管道下面给出一个完整的生产级 Spring AI 集成方案包含模型调用、Prompt 管理、RAG 管道和异常处理。Maven 依赖配置dependencies !-- Spring AI BOM 统一版本管理 -- dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement !-- OpenAI 模型适配器 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency !-- PgVector 向量存储适配器 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store-spring-boot-starter/artifactId /dependency /dependencies应用配置spring: ai: openai: api-key: ${OPENAI_API_KEY} base-url: ${OPENAI_BASE_URL:https://api.openai.com} chat: options: model: gpt-4o temperature: 0.7 max-tokens: 2048 vectorstore: pgvector: index-type: HNSW distance-type: COSINE dimensions: 1536Prompt 模板管理外部化文件!-- src/main/resources/prompts/customer-support.st -- 你是一个专业的客户支持助手。请根据以下知识库内容回答用户问题。 知识库内容 {{knowledge}} 用户问题{{question}} 回答要求 1. 仅基于知识库内容回答不要编造信息 2. 如果知识库中没有相关信息明确告知用户 3. 回答需简洁、准确、专业RAG 管道服务实现/** * RAG 增强的客户支持服务 * 将用户问题与知识库向量检索结合生成有依据的回答 */ Service Slf4j public class CustomerSupportRagService { private final ChatClient chatClient; private final VectorStore vectorStore; private final PromptTemplate promptTemplate; public CustomerSupportRagService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { this.chatClient chatClientBuilder.build(); this.vectorStore vectorStore; // 加载外部化 Prompt 模板 var promptResource new ClassPathResource(prompts/customer-support.st); this.promptTemplate new PromptTemplate(promptResource); } /** * RAG 查询主流程检索 - 构建 Prompt - 调用模型 * * param question 用户问题 * return 基于知识库的回答 */ public String query(String question) { // 1. 向量检索从知识库中找到与问题最相关的文档片段 ListDocument relevantDocs retrieveRelevantDocuments(question); // 2. 拼接知识库上下文 String knowledge relevantDocs.stream() .map(Document::getContent) .collect(Collectors.joining(\n\n)); // 3. 构建 Prompt Prompt prompt promptTemplate.create(Map.of( knowledge, knowledge, question, question )); // 4. 调用模型带重试与降级 try { ChatResponse response chatClient.prompt(prompt) .call() .chatResponse(); // 记录 Token 用量用于成本监控 logTokenUsage(response); return response.getResult().getOutput().getContent(); } catch (ApiException e) { log.error(LLM API 调用失败状态码: {}, 原因: {}, e.getStatusCode(), e.getMessage()); return generateFallbackAnswer(question, relevantDocs); } } /** * 向量检索基于问题语义查找最相关的知识库片段 * 限制 Top-K 和相似度阈值避免无关内容干扰 */ private ListDocument retrieveRelevantDocuments(String question) { SearchRequest searchRequest SearchRequest.builder() .query(question) .topK(5) // 返回最相关的 5 个片段 .similarityThreshold(0.7) // 相似度阈值过滤低质量结果 .build(); return vectorStore.similaritySearch(searchRequest); } /** * 降级策略当 LLM 调用失败时返回检索到的原始文档片段 * 保证服务可用性避免因模型不可用导致完全无响应 */ private String generateFallbackAnswer(String question, ListDocument relevantDocs) { if (relevantDocs.isEmpty()) { return 抱歉当前无法获取回答请稍后重试。; } StringBuilder sb new StringBuilder(以下是与您问题相关的知识库内容\n\n); for (int i 0; i relevantDocs.size(); i) { sb.append([).append(i 1).append(] ) .append(relevantDocs.get(i).getContent()).append(\n\n); } sb.append(系统暂时无法生成总结以上为原始检索结果); return sb.toString(); } /** * 记录 Token 用量用于成本监控与预算控制 */ private void logTokenUsage(ChatResponse response) { Usage usage response.getMetadata().getUsage(); log.info(Token 用量 - Prompt: {}, Completion: {}, Total: {}, usage.getPromptTokens(), usage.getGenerationTokens(), usage.getTotalTokens()); } }知识库文档导入服务/** * 知识库文档导入服务 * 将文档分块、生成向量嵌入并写入 VectorStore */ Service public class KnowledgeBaseIngestionService { private final VectorStore vectorStore; public KnowledgeBaseIngestionService(VectorStore vectorStore) { this.vectorStore vectorStore; } /** * 导入文本文档到知识库 * 分块策略按段落分割每块不超过 500 Token重叠 50 Token */ public void ingestDocument(String documentId, String content) { // 文档分块控制块大小以平衡检索精度与上下文完整性 TextSplitter splitter new TokenTextSplitter( 500, // 每块最大 Token 数 50, // 块间重叠 Token 数 5, // 最小块大小 10000, // 最大块大小 true // 保留分隔符 ); Document originalDoc new Document(documentId, content, Map.of(source, documentId, ingestedAt, Instant.now().toString())); ListDocument chunks splitter.split(originalDoc); // 写入向量存储Spring AI 自动调用 Embedding 模型生成向量 vectorStore.add(chunks); } }四、Token 成本与延迟抖动Spring AI 集成的架构权衡Spring AI 显著降低了 Java 应用集成 LLM 的工程复杂度但引入大模型调用链路后系统面临的新约束必须被正视。第一Token 成本的不可预测性。LLM 的计费基于 Token 数量而用户输入的长度和模型的输出长度在请求前无法精确预估。一个看似简单的 RAG 查询可能因为检索到大量知识库片段而导致 Prompt Token 数飙升。在 Spring AI 的配置中max-tokens参数可以限制输出长度但无法限制输入长度。必须在业务层实现 Token 预算控制在调用模型前估算 Prompt Token 数超出预算时截断知识库片段或拒绝请求。第二模型调用的延迟不确定性。LLM 的推理延迟从数百毫秒到数十秒不等取决于输入长度、输出长度和模型负载。Spring AI 默认使用同步调用模式一个慢请求会阻塞业务线程。对于延迟敏感的接口必须使用stream()方法进行流式调用或设置合理的超时时间。Spring AI 的ChatClient支持通过RestClient.Builder自定义超时配置。第三向量检索的精度与召回率权衡。RAG 管道的质量高度依赖向量检索的效果。相似度阈值设置过高会导致召回不足知识库中有答案但未被检索到设置过低则引入噪声检索到不相关的内容干扰模型回答。similarityThreshold参数需要根据业务场景反复调优没有通用的最优值。适用边界Spring AI 适合需要集成多种模型供应商、管理复杂 Prompt、构建 RAG 管道的企业级 Java 应用。对于只需要调用单一模型 API 的简单场景直接使用 HTTP 客户端可能更轻量。Spring AI 的抽象层引入了一定的学习成本和框架约束团队需要评估其收益是否大于引入成本。五、总结Spring AI 为 Java 后端集成大模型提供了一套完整的工程化方案ChatModel 接口统一了模型调用抽象PromptTemplate 实现了 Prompt 的外部化管理VectorStore 接口屏蔽了向量存储的实现差异RAG 管道将检索与生成串联为端到端流程。降级策略和 Token 用量监控则是生产环境不可或缺的保障机制。然而Token 成本的不可预测性、推理延迟的不确定性、向量检索的精度与召回率权衡都是架构师在设计 LLM 集成方案时必须纳入考量的约束条件。Spring AI 提供的是工具和抽象而非银弹。落地路线建议第一步以单一模型供应商如 OpenAI为起点验证 Spring AI 的基本调用链路第二步将硬编码的 Prompt 迁移为外部化模板建立 Prompt 版本管理流程第三步引入 VectorStore 和 RAG 管道构建知识库增强的问答能力第四步实现 Token 预算控制和延迟监控将 LLM 调用纳入系统的可观测性体系。