图片
Spring AI与Redis的结合为Java开发者提供了构建高性能RAG系统的理想方案。通过本文介绍的架构设计和实现方案,企业可以快速搭建属于自己的智能问答系统,显著提升知识管理效率。

引言:RAG技术为何成为企业AI应用首选

实现成本降低千倍、响应速度秒级的企业级知识库解决方案

在当前AI技术飞速发展的背景下,企业面临着一个核心挑战:如何让大语言模型(LLM)准确掌握企业内部知识并避免产生幻觉(Hallucination)?检索增强生成(Retrieval-Augmented Generation,RAG)技术应运而生,它通过将信息检索与生成模型相结合,有效解决了这一难题。

Spring AI作为Spring官方推出的AI开发框架,为Java开发者提供了构建AI应用的标准化方案。结合Redis这一高性能向量数据库,我们可以构建出响应迅速、成本可控、易于维护的RAG问答系统。本文将深入探讨这一技术组合的架构设计、核心实现和优化策略。

一、RAG技术架构设计

1.1 系统整体架构

基于Spring AI和Redis的RAG系统主要包含以下组件:

图片图片

1.2 技术栈选型依据

  • Spring AI:提供统一的AI应用开发接口,支持多种大模型和向量数据库
  • Redis Stack:具备向量搜索能力的高性能内存数据库,适合实时检索场景
  • OpenAI API/本地模型:平衡性能与成本的需求

二、环境准备与核心配置

2.1 项目依赖配置

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>
    <!-- Redis 向量存储 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-vector-store-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-redis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pdf-document-reader</artifactId>
    </dependency>
    <!-- 文档解析(支持 Word、Excel 等) -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-tika-document-reader</artifactId>
    </dependency>
</dependencies>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

2.2 应用配置文件

# application.yml
spring:
  ai:
    openai:
      embedding:
        options:
          model: text-embedding-v4 # 使用百炼平台的嵌入模型
    vectorstore:
      redis:
        uri: redis://localhost:6379
        index: knowledge-base
        prefix: "doc:"
        initialize-schema: true


server:
  port: 8080
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

三、核心实现源码解析

3.1 数据加载服务实现

知识库的初始化是RAG系统的基础,需要将文档转换为向量并存储到Redis中。

@Service
@Slf4j
public class DataLoaderService {


    @Value("classpath:knowledge/*.pdf")
    private Resource[] knowledgeResources;


    @Autowired
    private VectorStore vectorStore;


    @PostConstruct
    public void initializeKnowledgeBase() {
        log.info("开始初始化知识库...");


        for (Resource resource : knowledgeResources) {
            try {
                // 使用PDF文档阅读器
                PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(
                    resource,
                    PdfDocumentReaderConfig.builder()
                        .withPagesPerDocument(1)
                        .build()
                );


                // 文本分割器,确保文档块大小合适
                TokenTextSplitter textSplitter = new TokenTextSplitter(
                    1000,  // 最大token数
                    200,   // 重叠token数
                    true   // 分段存储
                );


                // 读取、分割并存储文档
                List<Document> documents = pdfReader.get();
                List<Document> chunks = textSplitter.apply(documents);


                vectorStore.add(chunks);
                log.info("已加载文档: {},分割为 {} 个块", 
                    resource.getFilename(), chunks.size());


            } catch (Exception e) {
                log.error("加载文档失败: {}", resource.getFilename(), e);
            }
        }
        log.info("知识库初始化完成");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

3.2 RAG服务核心逻辑

RAG服务的核心在于实现检索与生成的协同工作。

@Service
@Slf4j
public class RagService {


    @Autowired
    private VectorStore vectorStore;


    @Autowired
    private ChatClient chatClient;


    // 相似度搜索配置
    private static final int TOP_K = 5;
    private static final double SIMILARITY_THRESHOLD = 0.7;


    public Generation retrieve(String userQuery) {
        // 1. 向量相似度搜索
        SearchRequest searchRequest = SearchRequest.query(userQuery)
            .withTopK(TOP_K)
            .withSimilarityThreshold(SIMILARITY_THRESHOLD);


        List<Document> relevantDocs = vectorStore.similaritySearch(searchRequest);


        if (relevantDocs.isEmpty()) {
            return new Generation("未找到相关信息,请尝试其他问题。");
        }


        // 2. 构建增强提示
        String context = buildContext(relevantDocs);
        String enhancedPrompt = buildEnhancedPrompt(userQuery, context);


        // 3. 调用LLM生成回答
        Prompt prompt = new Prompt(enhancedPrompt);
        ChatResponse response = chatClient.call(prompt);


        return response.getResult();
    }


    private String buildContext(List<Document> documents) {
        StringBuilder contextBuilder = new StringBuilder();
        contextBuilder.append("相关参考信息:\n\n");


        for (int i = 0; i < documents.size(); i++) {
            Document doc = documents.get(i);
            contextBuilder.append(String.format("[%d] %s\n\n", i + 1, doc.getText()));
        }


        return contextBuilder.toString();
    }


    private String buildEnhancedPrompt(String userQuery, String context) {
        return String.format("""
            你是一个专业的客服助手,请根据以下参考信息回答问题。
            如果参考信息不足以回答问题,请明确说明。
            不要编造信息,保持回答准确、简洁。


            %s


            用户问题:%s


            请根据以上信息提供回答:
            """, context, userQuery);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.

3.3 控制器层实现

@RestController
@RequestMapping("/api/rag")
@Slf4j
public class RagController {


    @Autowired
    private RagService ragService;


    @PostMapping("/chat")
    public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
        try {
            long startTime = System.currentTimeMillis();


            Generation generation = ragService.retrieve(request.getQuestion());


            long responseTime = System.currentTimeMillis() - startTime;
            log.info("问题处理完成: 问题长度={}, 响应时间={}ms", 
                request.getQuestion().length(), responseTime);


            ChatResponse response = new ChatResponse(
                generation.getOutput().getContent(),
                responseTime
            );


            return ResponseEntity.ok(response);


        } catch (Exception e) {
            log.error("处理问题时发生错误", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ChatResponse("系统繁忙,请稍后重试", -1));
        }
    }


    // DTO类
    @Data
    @AllArgsConstructor
    public static class ChatRequest {
        private String question;
    }


    @Data
    @AllArgsConstructor
    public static class ChatResponse {
        private String answer;
        private long responseTimeMs;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

四、高级特性与优化策略

4.1 使用QuestionAnswerAdvisor优化RAG流程

Spring AI提供了Advisor接口来标准化RAG流程的实现。

@Configuration
@Slf4j
public class RagAdvisorConfig {


    @Bean
    public QuestionAnswerAdvisor questionAnswerAdvisor(
            VectorStore vectorStore, 
            ChatClient chatClient) {


        return new QuestionAnswerAdvisor(vectorStore, chatClient) {
            @Override
            public Prompt before(String question) {
                // 自定义检索逻辑
                SearchRequest request = SearchRequest.query(question)
                    .withTopK(5)
                    .withSimilarityThreshold(0.75)
                    .withFilterExpression("category == 'technical'");


                List<Document> docs = vectorStore.similaritySearch(request);


                // 构建系统消息
                SystemMessage systemMessage = new SystemMessage(
                    "你是一个技术专家,请根据以下文档回答问题:\n" + 
                    docs.stream()
                        .map(Document::getText)
                        .collect(Collectors.joining("\n\n"))
                );


                UserMessage userMessage = new UserMessage(question);


                return new Prompt(List.of(systemMessage, userMessage));
            }


            @Override
            public String after(ChatResponse response) {
                // 后处理:添加引用和验证
                String answer = response.getResult().getOutput().getContent();
                return answer + "\n\n*以上信息仅供参考*";
            }
        };
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.

4.2 性能优化实践

向量索引优化

spring:
  ai:
    vectorstore:
      redis:
        index-type: HNSW  # 使用分层导航小世界算法
        distance-metric: COSINE  # 余弦相似度
        index-options: |
          {
            "EF_CONSTRUCTION": 200,
            "M": 16
          }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

缓存策略实现

@Service
@Slf4j
public class CachingRagService {


    @Autowired
    private RagService ragService;


    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    private static final long CACHE_TTL = 3600; // 1小时


    public Generation retrieveWithCache(String userQuery) {
        // 生成查询指纹作为缓存键
        String cacheKey = generateCacheKey(userQuery);


        // 尝试从缓存获取
        String cachedAnswer = redisTemplate.opsForValue().get(cacheKey);
        if (cachedAnswer != null) {
            log.debug("缓存命中: {}", cacheKey);
            return new Generation(cachedAnswer);
        }


        // 缓存未命中,执行RAG流程
        Generation generation = ragService.retrieve(userQuery);


        // 缓存结果
        if (shouldCache(generation)) {
            redisTemplate.opsForValue().set(
                cacheKey, 
                generation.getOutput().getContent(),
                Duration.ofSeconds(CACHE_TTL)
            );
        }


        return generation;
    }


    private String generateCacheKey(String query) {
        return "rag:cache:" + Integer.toHexString(query.hashCode());
    }


    private boolean shouldCache(Generation generation) {
        // 只缓存高质量的回答
        String content = generation.getOutput().getContent();
        return !content.contains("不确定") && !content.contains("无法回答");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.

五、实战案例:企业知识库问答系统

5.1 系统特色功能

基于Spring AI和Redis的RAG系统在实际应用中表现出色:

  1. 精准问答:针对”公司请假流程是什么?”等问题,能直接从员工手册中检索相关信息生成准确回答
  2. 多文档支持:支持PDF、Word、HTML等多种格式文档的自动处理和向量化
  3. 实时更新:知识库更新后,系统能够立即感知并提供最新信息

5.2 性能对比数据

图片

六、总结与展望

Spring AI与Redis的结合为Java开发者提供了构建高性能RAG系统的理想方案。通过本文介绍的架构设计和实现方案,企业可以快速搭建属于自己的智能问答系统,显著提升知识管理效率。

未来,随着Spring AI生态的不断完善,我们可以期待更多高级特性的出现:

  • 多模态RAG:支持图像、表格等非文本内容的检索与生成
  • 自适应学习:系统能够根据用户反馈自动优化检索策略
  • 边缘部署:支持在资源受限环境中运行轻量级RAG系统

文章来自:51CTO

Loading

作者 yinhua

发表回复