2026年智能问答系统开发入门基础教程(非常详细),用Spring AI看这篇就够!

智能问答系统开发入门基础教程(非常详细),用Spring AI看这篇就够!svg xmlns http www w3 org 2000 svg style display none svg

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



 
  
    
     
      
     
  
    
    

摘要:很多文章停留在“大模型接上了”“接口跑通了”这一层,但企业真正上线一个智能问答系统,难点从来不只是调用模型,而是如何在准确性、吞吐、成本、可观测性、安全性与可扩展性之间取得平衡。本文从架构设计、核心原理、工程化治理、生产级代码、真实业务案例五个维度,系统讲清如何基于 Spring AI Alibaba 与通义千问构建一个可持续演进的企业级智能问答平台。


对于 Java 技术团队而言,企业级 AI 应用通常面临四类问题:

  1. 模型接入碎片化,不同厂商 SDK、协议、鉴权方式差异明显。
  2. 业务系统需要的不只是“对话”,还包括 RAG、工具调用、流式返回、结构化输出、会话上下文管理。
  3. 真正上线后,问题会变成限流、超时、幂等、审计、安全脱敏、成本治理,而不是 prompt 是否优雅。
  4. 企业需要与现有 Spring Boot、Redis、MQ、数据库、监控体系无缝集成,而非另起一套技术栈。

Spring AI Alibaba 的价值就在于,它把“模型能力”纳入 Spring 工程体系,用统一抽象接住 Chat Model、Embedding、VectorStore、Advisor、Tool Calling、Workflow 等核心能力。通义千问则适合中文企业场景,尤其是在知识问答、客服辅助、工单分析、运营 Copilot 这类任务中,能够提供较好的中文理解与生成表现。

简单来说:

维度 Spring AI Alibaba 的价值 开发效率 统一 API,减少自定义 HTTP 封装与协议适配 工程集成 与 Spring Boot、Spring WebFlux、Spring Data、Actuator 结合自然 AI 能力 支持同步/流式对话、结构化输出、RAG、工具调用、工作流编排 企业落地 更容易接入缓存、限流、审计、监控、降级与治理体系

如果你的目标是“做一个 Demo”,直接调模型 API 即可;如果你的目标是“做一个能上线的企业服务”,Spring AI Alibaba 是更稳妥的工程入口。


很多团队把“问答系统”理解成一个聊天框加一个模型接口,这会导致后续架构快速失控。

企业级问答系统本质上是一个多层协同系统:

  1. 接入层:Web、App、企微、钉钉、内部工作台。
  2. 编排层:请求鉴权、路由、限流、对话会话管理、Prompt 组装。
  3. 检索层:知识切片、向量检索、元数据过滤、混合检索、重排。
  4. 生成层:大模型推理、工具调用、结构化输出、答案约束。
  5. 治理层:日志、追踪、Token 统计、缓存、熔断、审计、风控。

它不是一个“模型项目”,而是一个“AI 中台能力 + 业务服务”的组合体。


3.1 目标架构

┌──────────────────────────┐ │ Client / Portal │ │ Web / App / Feishu / IM │ └────────────┬─────────────┘ │ ▼ ┌──────────────────────────┐ │ API Gateway │ │ Auth / RateLimit / Trace │ └────────────┬─────────────┘ │ ┌─────────────────────┴─────────────────────┐ ▼ ▼ ┌──────────────────────────┐ ┌──────────────────────────┐ │ Conversation Service │ │ Knowledge Ingestion │ │ Chat / Stream / Tool / │ │ Parse / Chunk / Embed / │ │ Session / Prompt / RAG │ │ Index / Versioning │ └────────────┬─────────────┘ └────────────┬─────────────┘ │ │ ┌───────────────┼────────────────┐ ┌────────┼────────┐ ▼ ▼ ▼ ▼ ▼┌──────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ ┌──────────────┐│ DashScope│ │ VectorStore │ │ Redis │ │ Object │ │ Metadata DB ││ Qwen │ │ Milvus/Redis│ │ Cache/Session│ │ Storage │ │ MySQL/PG │└──────────┘ └────────────┘ └─────────────┘ └────────────┘ └──────────────┘ │ ▼ ┌─────────────────────┐ │ Observability Stack │ │ Logs / Metrics / │ │ Tracing / Audit │ └─────────────────────┘ 

3.2 核心职责拆分

建议至少拆成两个服务,而不是所有逻辑堆在一个 Controller:

  1. conversation-service
    负责用户问答、SSE 流式输出、RAG 检索编排、工具调用、会话管理、限流与降级。

  2. knowledge-ingestion-service
    负责文档上传、解析、切片、向量化、索引构建、知识版本管理、重建索引。

这样拆分的原因很现实:

  1. 在线问答链路追求低延迟。
  2. 文档入库链路通常是 CPU/IO 密集型异步任务。
  3. 两条链路扩容策略不同,生命周期也不同。

3.3 一条请求的完整执行链路

用户发起问题后,系统建议按照以下顺序执行:

  1. 鉴权与租户识别。
  2. 敏感词与越权问题预检测。
  3. 命中缓存则直接返回。
  4. 判断是否需要 RAG 检索。
  5. 检索召回 TopK 文档。
  6. 执行重排与片段压缩。
  7. 构造系统 Prompt、检索上下文、用户问题。
  8. 调用通义千问生成答案。
  9. 必要时触发工具调用。
  10. 对答案做引用、脱敏、审计、落库。
  11. 返回最终结果或以 SSE 方式流式返回。

这条链路决定了系统的上限。很多线上问题,如响应慢、答案漂移、成本飙升、上下文污染,本质都是链路设计不完整造成的。


推荐的项目结构如下:

enterprise-qa-system/├── src/main/java/com/example/aiqa/│ ├── AiQaApplication.java│ ├── config/│ │ ├── AiModelConfig.java│ │ ├── WebFluxConfig.java│ │ ├── RedisConfig.java│ │ ├── ResilienceConfig.java│ │ └── JacksonConfig.java│ ├── controller/│ │ ├── ChatController.java│ │ ├── KnowledgeController.java│ │ └── AdminController.java│ ├── service/│ │ ├── ChatApplicationService.java│ │ ├── RagService.java│ │ ├── PromptService.java│ │ ├── CitationService.java│ │ ├── SessionMemoryService.java│ │ ├── DocumentIngestionService.java│ │ └── ToolOrchestrationService.java│ ├── domain/│ │ ├── chat/│ │ ├── knowledge/│ │ └── audit/│ ├── repository/│ ├── tool/│ ├── workflow/│ ├── advisor/│ ├── dto/│ └── support/├── src/main/resources/│ ├── application.yml│ ├── application-prod.yml│ └── prompt/│ ├── qa-system.st│ ├── citation.st│ └── tool-system.st└── pom.xml 

核心原则只有一个:让业务逻辑围绕“问答应用服务”收口,不要让 Controller 同时处理检索、Prompt 拼装、调用模型、结果组装、缓存、审计。


5.1 Maven 依赖

 
       
    
          
          
            17 
           
          
            3.3.2 
           
          
            1.0.0.2 
           
          
            2.2.0 
           
          
       
    
          
           
            
            
              org.springframework.boot 
             
            
              spring-boot-dependencies 
             
            
              ${spring.boot.version} 
             
            
              pom 
             
            
              import 
             
            
            
            
              com.alibaba.cloud.ai 
             
            
              spring-ai-alibaba-bom 
             
            
              ${spring-ai-alibaba.version} 
             
            
              pom 
             
            
              import 
             
            
           
          
       
    
          
           
           
             org.springframework.boot 
            
           
             spring-boot-starter-webflux 
            
           
           
           
             org.springframework.boot 
            
           
             spring-boot-starter-validation 
            
           
           
           
             org.springframework.boot 
            
           
             spring-boot-starter-data-redis 
            
           
           
           
             org.springframework.boot 
            
           
             spring-boot-starter-actuator 
            
           
           
           
             com.alibaba.cloud.ai 
            
           
             spring-ai-alibaba-starter-dashscope 
            
           
           
           
             org.springframework.ai 
            
           
             spring-ai-starter-vector-store-redis 
            
           
           
           
             io.github.resilience4j 
            
           
             resilience4j-spring-boot3 
            
           
             ${resilience4j.version} 
            
           
           
           
             org.projectlombok 
            
           
             lombok 
            
           
             true 
            
           
           
           
             org.springframework.boot 
            
           
             spring-boot-starter-test 
            
           
             test 
            
           
          

5.2 配置文件

server: port: 8080spring: application: name: enterprise-qa-system data: redis: host: 127.0.0.1 port: 6379 timeout: 2s ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus temperature: 0.2 max-tokens: 1500 vectorstore: redis: initialize-schema: true index-name: enterprise_knowledge_idxmanagement: endpoints: web: exposure: include: health,info,prometheus,metrics tracing: sampling: probability: 1.0app: qa: retrieval: top-k: 6 threshold: 0.75 session: ttl-minutes: 30 cache: answer-ttl-minutes: 15 guardrail: max-question-length: 2000 

生产环境请注意三点:

  1. API Key 必须来自环境变量或密钥中心,不要写入仓库。
  2. temperature 对问答类系统应尽量低,减少答案漂移。
  3. max-tokens 要与上下文长度、召回片段数、成本预算一同设计,不能单独看。

6.1 请求与响应模型

package com.example.aiqa.dto;import jakarta.validation.constraints.NotBlank;import jakarta.validation.constraints.Size;public record ChatRequest( @NotBlank(message = "sessionId 不能为空") String sessionId, @NotBlank(message = "question 不能为空") @Size(max = 2000, message = "问题长度不能超过 2000") String question, boolean enableRag, boolean enableTool) {} plaintext package com.example.aiqa.dto;import java.util.List;public record ChatAnswer( String sessionId, String answer, List 
        
    
          
            citations, Usage usage, boolean fromCache) { public record Citation(String documentId, String title, String snippet) {} public record Usage(Long promptTokens, Long completionTokens, Long totalTokens) {}} 
          

这样的设计有几个好处:

  1. 便于后续扩展引用来源、耗时、Token、命中缓存标记。
  2. 前后端契约稳定,避免接口一开始就返回裸字符串,后期无法兼容演进。
  3. 审计、监控与问题定位时,结构化响应更容易落库和检索。

6.2 Controller 层

package com.example.aiqa.controller;import com.example.aiqa.dto.ChatAnswer;import com.example.aiqa.dto.ChatRequest;import com.example.aiqa.service.ChatApplicationService;import jakarta.validation.Valid;import lombok.RequiredArgsConstructor;import org.springframework.http.MediaType;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;import reactor.core.publisher.Flux;@RestController@RequestMapping("/api/v1/chat")@RequiredArgsConstructorpublic class ChatController { private final ChatApplicationService chatApplicationService; @PostMapping public ChatAnswer chat(@Valid @RequestBody ChatRequest request) { return chatApplicationService.chat(request); } @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux 
        
    
          
            stream(@Valid @RequestBody ChatRequest request) { return chatApplicationService.stream(request); }} 
          

Controller 的职责到此为止,不做任何模型编排细节。


7.1 统一 ChatClient 配置

package com.example.aiqa.config;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.model.ChatModel;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class AiModelConfig { @Bean public ChatClient chatClient(ChatModel chatModel) { return ChatClient.builder(chatModel) .defaultSystem(""" 你是一名企业知识助手,请遵守以下规则: 1. 优先依据提供的知识库上下文回答。 2. 如果上下文不足,请明确说明“不确定”。 3. 不要编造制度、价格、流程、联系方式。 4. 答案优先简洁、准确、可执行。 """) .build(); }} 

这里看似简单,实际上很关键。企业问答系统最怕的不是“答不上来”,而是“编造一个看起来很像真的答案”。因此系统 Prompt 的目标不是让模型更会说,而是让它更守边界。

7.2 Prompt 模板服务

package com.example.aiqa.service;import java.util.List;import org.springframework.ai.document.Document;import org.springframework.stereotype.Service;@Servicepublic class PromptService return """ 请基于给定知识片段回答用户问题。 要求: 1. 只能依据知识片段作答。 2. 如果知识不足,直接回答“根据当前知识库无法确认”。 3. 先给结论,再给依据。 知识片段: %s 用户问题: %s """.formatted(contextBuilder, question); }} 

把 Prompt 构造收敛到独立服务,而不是散落在 Controller、Service、Workflow 各处,这样后续做 A/B Prompt 实验、版本化管理、按租户切换模板都会容易很多。


8.1 RAG 的本质

RAG 的目标不是让模型“知道更多”,而是让模型“在回答当前问题时临时拿到更相关、更新、更可信的上下文”。

一个高质量 RAG 系统至少要解决五件事:

  1. 文档怎么切,既保留语义完整性,又避免 chunk 太大。
  2. 检索怎么召回,避免错召、漏召、脏召。
  3. 如何按租户、业务线、文档类型做元数据过滤。
  4. 是否需要重排,提升最终送给模型的片段质量。
  5. 最终答案如何回溯来源,降低幻觉风险。

8.2 文档切片策略

工程上不建议简单按固定字符数切片。更合理的做法是:

  1. 先按标题、段落、表格、列表进行语义分段。
  2. 再对超长段落做二次切片。
  3. 保留 10% 到 20% 重叠窗口,减少上下文断裂。
  4. 给每个 chunk 附带元数据,如 documentIdtitletenantIdcategoryversion

示例:

package com.example.aiqa.service;import java.util.ArrayList;import java.util.List;import java.util.Map;import org.springframework.ai.document.Document;import org.springframework.stereotype.Service;@Servicepublic class DocumentChunkService start = end - overlap; chunkNo++; } return chunks; }} 

8.3 文档入库链路

package com.example.aiqa.service;import java.util.List;import lombok.RequiredArgsConstructor;import org.springframework.ai.document.Document;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class DocumentIngestionService { private final DocumentChunkService documentChunkService; private final VectorStore vectorStore; public void ingest(String documentId, String title, String content, String tenantId) { List 
          
    
            
              chunks = documentChunkService.split(documentId, title, content, tenantId); vectorStore.add(chunks); }} 
            

生产环境这里还需要补上:

  1. 文件解析异步化,避免上传线程阻塞。
  2. 文档版本管理,支持索引重建与回滚。
  3. 重复文档去重。
  4. 大文件分批向量化,防止单次任务过大。
  5. 入库状态机,如 PENDINGEMBEDDINGDONEFAILED

8.4 检索服务

package com.example.aiqa.service;import java.util.List;import lombok.RequiredArgsConstructor;import org.springframework.ai.document.Document;import org.springframework.ai.vectorstore.SearchRequest;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class RagService { private final VectorStore vectorStore; public List 
          
    
            
              retrieve(String tenantId, String question, int topK, double threshold) { SearchRequest request = SearchRequest.builder() .query(question) .topK(topK) .similarityThreshold(threshold) .filterExpression("tenantId == '" + tenantId + "'") .build(); return vectorStore.similaritySearch(request); }} 
            

这段代码足够演示,但企业场景还要继续升级。

8.5 从“能检索”升级到“检得准”

建议按下面的路线逐步增强:

  1. 基础版:向量检索 + 元数据过滤。
  2. 进阶版:关键词检索与向量检索混合召回。
  3. 增强版:引入重排模型,对 TopK 结果二次排序。
  4. 企业版:按租户、部门、知识权限做隔离与裁剪。

一个常见误区是把 topK 一路调大,试图“多给模型一些材料”。这通常会带来三个副作用:

  1. 噪声变多,答案反而更差。
  2. Prompt 变长,成本上升。
  3. 响应变慢,SSE 首包时间拉长。

经验上,问答系统通常先召回 10 到 20 个片段,再经重排压缩到 3 到 6 个送入模型,更容易兼顾质量与成本。


9.1 应用服务实现

package com.example.aiqa.service;import com.example.aiqa.dto.ChatAnswer;import com.example.aiqa.dto.ChatRequest;import java.time.Duration;import java.util.List;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.document.Document;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;import reactor.core.publisher.Flux;@Slf4j@Service@RequiredArgsConstructorpublic class ChatApplicationService List 
           
    
             
               documents = request.enableRag() ? ragService.retrieve(currentTenant(), request.question(), 6, 0.75) : List.of(); String prompt = documents.isEmpty() ? request.question() : promptService.buildRagPrompt(request.question(), documents); ChatResponse response = chatClient.prompt() .system(sessionMemoryService.systemPrompt(request.sessionId())) .user(prompt) .call() .chatResponse(); String answer = response.getResult().getOutput().getText(); sessionMemoryService.append(request.sessionId(), request.question(), answer); redisTemplate.opsForValue().set(cacheKey, answer, Duration.ofMinutes(15)); return new ChatAnswer( request.sessionId(), answer, citationService.build(documents), new ChatAnswer.Usage( response.getUsage().getPromptTokens(), response.getUsage().getCompletionTokens(), response.getUsage().getTotalTokens() ), false ); } public Flux 
              
                stream(ChatRequest request) { validateQuestion(request.question()); List 
               
                 documents = request.enableRag() ? ragService.retrieve(currentTenant(), request.question(), 6, 0.75) : List.of(); String prompt = documents.isEmpty() ? request.question() : promptService.buildRagPrompt(request.question(), documents); return chatClient.prompt() .system(sessionMemoryService.systemPrompt(request.sessionId())) .user(prompt) .stream() .content() .doOnComplete(() -> log.info("stream completed, sessionId={}", request.sessionId())); } private void validateQuestion(String question) if (question.length() > 2000) { throw new IllegalArgumentException("问题长度超限"); } } private String currentTenant() { return "default"; }} 
                
               
             

9.2 为什么这样设计

这段代码比“Controller 里一句 chatClient.prompt().user(input).call().content()”复杂很多,但复杂是有意义的:

  1. 缓存减少高频重复问题成本。
  2. 会话服务隔离多轮上下文,不污染全局 Prompt。
  3. RAG 服务独立,后续便于替换为混合检索或接入重排。
  4. 引用服务独立,便于输出可解释答案。
  5. 流式与同步共用编排逻辑,避免双份维护。

9.3 会话记忆服务

package com.example.aiqa.service;import java.time.Duration;import java.util.ArrayList;import java.util.List;import lombok.RequiredArgsConstructor;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class SessionMemoryService { private final StringRedisTemplate redisTemplate; public void append(String sessionId, String question, String answer) { String key = "qa:session:" + sessionId; redisTemplate.opsForList().rightPush(key, "Q: " + question); redisTemplate.opsForList().rightPush(key, "A: " + answer); redisTemplate.expire(key, Duration.ofMinutes(30)); } public String systemPrompt(String sessionId) List 
           
    
             
               limited = history.size() > 10 ? new ArrayList<>(history.subList(history.size() - 10, history.size())) : history; return """ 你是一名企业知识助手,请结合最近对话上下文回答问题。 最近会话: %s """.formatted(String.join(" ", limited)); }} 
             

这里故意只截取最近 10 条,目的是控制上下文窗口,防止对话无限膨胀。


真正有价值的企业应用,往往不是“生成一段话”,而是“输出结构化结果并驱动后续业务动作”。

比如客服质检、工单分类、风险识别、运营摘要,都更适合结构化输出。

10.1 结构化 DTO

package com.example.aiqa.dto;import java.util.List;public record TicketAnalysis( String sentiment, String severity, String category, List 
            
    
              
                actions) {} 
              

10.2 结构化调用

package com.example.aiqa.service;import com.example.aiqa.dto.TicketAnalysis;import lombok.RequiredArgsConstructor;import org.springframework.ai.chat.client.ChatClient;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class TicketAnalysisService { private final ChatClient chatClient; public TicketAnalysis analyze(String ticketContent) { return chatClient.prompt() .system(""" 你是一名客服工单分析助手。 请严格返回结构化结果,不要输出多余解释。 sentiment 只能是 positive、neutral、negative。 severity 只能是 low、medium、high、critical。 """) .user("请分析以下工单内容: " + ticketContent) .call() .entity(TicketAnalysis.class); }} 

这类能力一旦稳定下来,就可以直接驱动:

  1. 工单自动分派。
  2. 升级告警。
  3. 质检评分。
  4. 客服话术建议。

这也是“AI 功能”真正变成“业务能力”的关键一步。


仅靠知识库,系统只能回答“已知事实”;接入工具后,系统才能处理“实时事实”。

典型场景:

  1. 查询订单状态。
  2. 查询库存与价格。
  3. 获取工单最新进展。
  4. 触发 CRM、ERP、OA 内部接口。

11.1 工具定义

package com.example.aiqa.tool;import org.springframework.ai.tool.annotation.Tool;import org.springframework.ai.tool.annotation.ToolParam;import org.springframework.stereotype.Service;@Servicepublic class OrderToolService { @Tool(description = "根据订单号查询订单状态") public String queryOrder( @ToolParam(description = "订单号") String orderId) { return """ { "orderId": "%s", "status": "DELIVERING", "carrier": "SF", "expectedArrival": "2026-03-26" } """.formatted(orderId); } @Tool(description = "根据商品编码查询库存") public String queryInventory( @ToolParam(description = "商品编码") String skuCode) { return """ { "skuCode": "%s", "available": 128, "warehouse": "HZ-01" } """.formatted(skuCode); }} 

11.2 工具注册

package com.example.aiqa.config;import com.example.aiqa.tool.OrderToolService;import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.ai.tool.method.MethodToolCallbackProvider;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class ToolConfig { @Bean public ToolCallbackProvider toolCallbackProvider(OrderToolService orderToolService) { return MethodToolCallbackProvider.builder() .toolObjects(orderToolService) .build(); }} 

11.3 带工具能力的 ChatClient

@Beanpublic ChatClient agentChatClient(ChatModel chatModel, ToolCallbackProvider toolCallbackProvider) 

11.4 工具调用的工程边界

工具调用在企业里非常有价值,但也非常危险。必须至少控制以下边界:

  1. 工具只开放白名单接口,不允许模型访问任意内网资源。
  2. 工具参数必须校验,防止越权查询。
  3. 写操作必须幂等,最好要求二次确认。
  4. 每次工具调用都要记录审计日志。
  5. 对外部接口设置超时、重试与熔断。

换句话说,工具调用不是“让模型更聪明”,而是“把模型纳入受控执行框架”。


这一部分是文章升级的重点,因为很多 Demo 一到线上就暴露问题。

12.1 并发瓶颈在哪

企业级 AI 问答系统的瓶颈通常不是 JVM 本身,而是以下几个外部依赖:

  1. 模型推理接口延迟。
  2. 向量检索响应时间。
  3. SSE 长连接占用。
  4. Redis/数据库热点 Key。
  5. 文档入库时的 embedding 计算与批量写入。

因此,高并发设计要围绕“隔离、削峰、缓存、降级”展开。

12.2 基础策略

建议至少落地以下策略:

  1. Web 层使用 WebFlux,适合处理流式响应和高连接数。
  2. 对同步问答与流式问答做线程池和舱壁隔离。
  3. 使用 Redis 做热点问题缓存与会话缓存。
  4. 对知识入库采用 MQ 异步化。
  5. 对模型调用增加超时、重试、熔断、限流。

12.3 Resilience4j 配置

resilience4j: ratelimiter: instances: chatRateLimiter: limit-for-period: 100 limit-refresh-period: 1s timeout-duration: 0 timelimiter: instances: chatTimeLimiter: timeout-duration: 8s circuitbreaker: instances: chatCircuitBreaker: sliding-window-size: 20 minimum-number-of-calls: 10 failure-rate-threshold: 50 wait-duration-in-open-state: 30s bulkhead: instances: chatBulkhead: max-concurrent-calls: 50 max-wait-duration: 100ms 

12.4 服务降级示例

package com.example.aiqa.service;import com.example.aiqa.dto.ChatAnswer;import com.example.aiqa.dto.ChatRequest;import io.github.resilience4j.bulkhead.annotation.Bulkhead;import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;import io.github.resilience4j.ratelimiter.annotation.RateLimiter;import io.github.resilience4j.timelimiter.annotation.TimeLimiter;import java.util.List;import java.util.concurrent.CompletableFuture;import lombok.RequiredArgsConstructor;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class ProtectedChatFacade { private final ChatApplicationService chatApplicationService; @RateLimiter(name = "chatRateLimiter") @Bulkhead(name = "chatBulkhead", type = Bulkhead.Type.SEMAPHORE) @CircuitBreaker(name = "chatCircuitBreaker", fallbackMethod = "fallback") @TimeLimiter(name = "chatTimeLimiter") public CompletableFuture 
              
    
                
                  protectedChat(ChatRequest request) { return CompletableFuture.supplyAsync(() -> chatApplicationService.chat(request)); } public CompletableFuture 
                 
                   fallback(ChatRequest request, Throwable throwable) { ChatAnswer answer = new ChatAnswer( request.sessionId(), "当前 AI 服务繁忙,请稍后重试,或改用知识库检索模式。", List.of(), new ChatAnswer.Usage(0L, 0L, 0L), false ); return CompletableFuture.completedFuture(answer); }} 
                  
                

12.5 缓存设计建议

缓存不要只缓存最终答案,还可以缓存三个层次:

  1. 问题标准化结果。
  2. 检索结果。
  3. 最终答案。

对于稳定知识问答场景,这能显著降低成本。典型做法:

  1. 对用户问题先做清洗与归一化。
  2. 用归一化后的 Query 作为缓存 Key。
  3. 按租户、版本、模型维度隔离缓存空间。

例如:

qa:answer:{tenantId}:{knowledgeVersion}:{model}:{questionHash} 

12.6 SSE 流式场景的注意事项

SSE 是问答系统体验提升最大的手段之一,但它对网关、连接数、超时配置很敏感:

  1. 网关要允许长连接,不要过早超时。
  2. 服务端需要定期发送心跳,防止中间层断流。
  3. 前端要处理重连与中断恢复。
  4. 不要把超长答案毫无节制地一直流式输出,要设置 token 上限。

12.7 水平扩展原则

如果系统预计进入百 QPS 以上规模,建议做到:

  1. 应用无状态化,会话存在 Redis。
  2. 知识入库与在线问答彻底分离部署。
  3. 向量库单独扩缩容。
  4. Prometheus + Grafana 监控 Token、耗时、命中率、错误率。

企业 AI 架构的扩展,不是先买更大的机器,而是先把状态外置,把链路拆开,把热点缓存起来。


这一节给一个完整业务案例,把前面技术点串起来。

13.1 场景定义

某电商企业希望建设内部知识助手,服务对象包括客服、运营、仓配人员。问题主要分三类:

  1. 制度与 SOP 查询。
  2. 商品、仓储、物流规则问答。
  3. 实时订单与库存查询。

13.2 技术拆解

对应的能力映射如下:

业务场景 技术能力 SOP、制度问答 RAG 检索增强 多轮澄清问答 Session 会话记忆 订单、库存查询 Tool Calling 热门固定问题 Redis 缓存 高峰时段客服问答 WebFlux + 限流 + 熔断 合规审计 请求日志、工具调用日志、敏感词过滤

13.3 一条实际问答链路

用户提问:

客户问 3 月 20 日下单的订单 A8 为什么还没到,另外生鲜商品超时赔付规则是什么?

系统应该分解为两段:

  1. 订单时效是实时信息,调用订单工具。
  2. 超时赔付规则是制度知识,从知识库检索。

最终模型在统一上下文中整合输出:

  1. 给出当前订单状态和预计送达时间。
  2. 说明赔付规则依据。
  3. 引用对应制度文档片段。

这类“实时数据 + 静态知识”混合回答,是企业助手最常见也最有价值的模式。

13.4 混合问答编排思路

package com.example.aiqa.service;import lombok.RequiredArgsConstructor;import org.springframework.ai.chat.client.ChatClient;import org.springframework.stereotype.Service;@Service@RequiredArgsConstructorpublic class HybridQaService { private final ChatClient agentChatClient; private final RagService ragService; private final PromptService promptService; public String answer(String question) { var docs = ragService.retrieve("default", question, 4, 0.75); String ragPrompt = promptService.buildRagPrompt(question, docs); return agentChatClient.prompt() .system(""" 你需要综合知识库和工具结果回答用户问题。 对于实时信息请调用工具,对于制度规则请引用知识库。 """) .user(ragPrompt) .call() .content(); }} 

如果想再进一步升级,可以增加一个“问题路由器”,先判断当前问题属于:

  1. 纯知识问答。
  2. 纯实时查询。
  3. 混合型问题。

再决定是否需要 RAG、是否需要工具、是否需要多阶段执行。


企业系统上线后,最怕的不是 bug,而是“出了问题没人知道为什么”。

14.1 至少监控这些指标

  1. 问答请求量、成功率、P95/P99 延迟。
  2. 模型调用耗时、失败率、超时率。
  3. 检索耗时、召回数、重排耗时。
  4. 平均 Prompt Token、Completion Token、总 Token。
  5. 缓存命中率。
  6. 工具调用次数、失败次数、平均耗时。
  7. SSE 中断率。

14.2 日志建议

不要打印完整敏感数据,但要保留可排障字段:

log.info( "chat request, traceId={}, sessionId={}, enableRag={}, enableTool={}, questionLength={}", traceId, request.sessionId(), request.enableRag(), request.enableTool(), request.question().length()); 

工具调用日志建议单独审计:

log.info( "tool invoked, traceId={}, toolName={}, tenantId={}, resultSize={}", traceId, "queryOrder", tenantId, result.length()); 

14.3 Token 成本治理

问答系统如果不做 Token 治理,成本会失控。建议至少做这些事:

  1. 控制检索片段数。
  2. 控制对话历史长度。
  3. 对长问题先做压缩或重写。
  4. 对命中缓存的问题直接返回。
  5. 对简单 FAQ 路由到低成本模型。
  6. 对复杂推理场景再升级到更强模型。

也就是说,模型路由和上下文压缩,本质上都是成本治理手段。


企业级 AI 系统必须把安全设计提前,而不是上线后亡羊补牢。

15.1 需要重点控制的风险

  1. Prompt Injection:用户诱导模型忽略系统约束。
  2. 数据越权:跨租户、跨部门召回知识。
  3. 敏感信息泄露:模型把内部信息输出给无权限用户。
  4. 工具滥用:模型调用不该调用的内部接口。
  5. 审计缺失:谁问了什么、调用了什么工具、返回了什么结果无法追踪。

15.2 实践建议

  1. 检索前先做租户隔离和权限过滤。
  2. 系统 Prompt 中明确禁止输出未确认信息。
  3. 对输入和输出做敏感词、PII 脱敏。
  4. 工具按租户、用户角色做鉴权。
  5. 审计日志落库并保留 traceId。

15.3 一个简单的输入防护示例

package com.example.aiqa.service;import java.util.Set;import org.springframework.stereotype.Service;@Servicepublic class GuardrailService } }} 

这当然不是完整防护体系,但至少说明一点:AI 应用安全,绝不能只靠模型“自觉”。


企业 AI 应用的测试要分层设计。

16.1 单元测试

适合测试:

  1. PromptService 是否正确拼装上下文。
  2. CitationService 是否正确组装引用。
  3. GuardrailService 是否正确拦截风险输入。
package com.example.aiqa.service;import static org.assertj.core.api.Assertions.assertThat;import java.util.List;import org.junit.jupiter.api.Test;import org.springframework.ai.document.Document;class PromptServiceTest { private final PromptService promptService = new PromptService(); @Test void shouldBuildRagPrompt() { List 
                  
    
                    
                      docs = List.of( new Document("退款时效为 1 到 3 个工作日"), new Document("生鲜品类不支持无理由退货") ); String prompt = promptService.buildRagPrompt("退款多久到账?", docs); assertThat(prompt).contains("退款时效为 1 到 3 个工作日"); assertThat(prompt).contains("退款多久到账"); }} 
                    

16.2 集成测试

适合测试:

  1. RAG 检索链路是否通。
  2. Redis 会话与缓存是否生效。
  3. 工具调用是否符合预期。

16.3 评测测试

这是 AI 项目区别于普通 CRUD 项目的关键。

建议建立一套问答评测集,覆盖:

  1. FAQ 标准问题。
  2. 同义改写问题。
  3. 多轮上下文问题。
  4. 极端长问题。
  5. 敏感问题与越权问题。

然后定期跑自动评测,关注:

  1. 准确率。
  2. 引用命中率。
  3. 幻觉率。
  4. 平均成本。
  5. 平均延迟。

没有评测体系的 AI 系统,迭代很容易“看起来变聪明了,实际上更不稳定了”。


17.1 推荐部署方式

  1. conversation-service 单独部署,多副本扩容。
  2. knowledge-ingestion-service 独立部署,异步消费任务。
  3. Redis 用于会话、缓存、限流计数。
  4. 向量库独立资源池。
  5. 监控、日志、追踪统一接入企业平台。

17.2 上线前检查清单

上线前至少确认:

  1. 是否配置限流与熔断。
  2. 是否控制上下文长度与 Token 成本。
  3. 是否做租户隔离和权限过滤。
  4. 是否记录审计日志。
  5. 是否有缓存与降级策略。
  6. 是否构建了基础评测集。
  7. 是否验证了高峰期 SSE 稳定性。

17.3 常见上线事故

最常见的三类事故如下:

  1. 文档更新后没有同步重建向量索引,导致答案引用旧制度。
  2. 会话上下文无限累积,响应越来越慢,成本越来越高。
  3. 工具调用缺少权限控制,出现越权查询。

这些问题本质都不是模型能力问题,而是工程治理问题。


如果你要从零搭建一个企业级问答项目,建议第一版就至少具备以下最小能力:

  1. 同步问答接口。
  2. SSE 流式接口。
  3. RAG 检索链路。
  4. Redis 会话与缓存。
  5. 工具调用白名单。
  6. 限流、熔断、超时。
  7. 监控、日志、审计。

换句话说,真正的 MVP 不是“能回答”,而是“能稳定回答、能治理、能定位问题”。


回到文章开头,Spring AI Alibaba + 通义千问的组合,真正的价值并不只是“快速接模型”,而是它能让 Java 团队用熟悉的 Spring 工程体系构建 AI 应用,把模型能力纳入标准化、可治理、可演进的企业架构。

从架构视角看,这类系统要解决的是:

  1. 如何用 RAG 提升事实准确性。
  2. 如何用工具调用补足实时能力。
  3. 如何在高并发下稳定服务。
  4. 如何做成本、权限、安全与审计治理。
  5. 如何把问答能力真正嵌入业务流程。

如果只做 Demo,重点在“模型能不能回答”;如果要做企业系统,重点一定转向:

  1. 链路是否可观测。
  2. 知识是否可维护。
  3. 服务是否可扩展。
  4. 输出是否可信。
  5. 系统是否可治理。

这才是企业级智能问答系统的真正分水岭。


如果你准备继续把这套系统做深,建议优先考虑以下演进路线:

  1. 引入混合检索与重排,进一步提升 RAG 准确率。
  2. 引入多模型路由,按问题复杂度与成本自动选择模型。
  3. 引入评测平台,形成 Prompt、检索、模型的持续优化闭环。
  4. 引入工作流编排,把问答扩展为任务执行型 Agent。
  5. 引入知识版本管理与灰度发布,支持文档迭代与回滚。

最终目标不是“做一个聊天机器人”,而是“构建企业的智能知识与执行平台”。

🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!

有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!

就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

在这里插入图片描述

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇

学习路线:

以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!

我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

小讯
上一篇 2026-04-12 16:49
下一篇 2026-04-12 16:47

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/252706.html