乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • Spring AI 学习路径
  • 第1章 Spring AI 概述与核心理念

    • 章节导读
    • Spring AI 概述与可移植 API
    • 核心模块与依赖关系
  • 第2章 快速入门与第一个AI应用

    • 章节导读
    • 环境准备与配置
    • 第一个 AI 对话应用
  • 第3章 聊天模型与ChatClient

    • 章节导读
    • ChatClient 详解
    • ChatModel 底层抽象
    • 多轮对话与 ChatMemory
  • 第4章 提示词管理与模板

    • 章节导读
    • Prompt 与 Message 体系
    • 提示词模板与动态构建
  • 第5章 输出解析与结构化响应

    • 章节导读
    • 输出解析器与 BeanOutputConverter
  • 第6章 嵌入模型与向量存储

    • 章节导读
    • EmbeddingModel 与文本向量化
    • ETL 数据注入流水线
    • 向量存储抽象与配置
  • 第7章 检索增强生成(RAG)

    • 章节导读
    • RAG 核心机制与流程
    • QuestionAnswerAdvisor 详解
    • RAG 实战案例
  • 第8章 函数调用(Function Calling)

    • 章节导读
    • Tool 注解与函数声明
    • 函数调用实战
  • 第9章 多模态

    • 章节导读
    • 图像输入与视觉模型
    • 图像生成
  • 第10章 Advisor拦截器链

    • 章节导读
    • Advisor 链与内置拦截器
  • 第11章 MCP 协议与跨语言工具集成

    • 章节导读
    • MCP 协议深入
  • 第12章 可观测性测试与最佳实践

    • 章节导读
    • 可观测性与指标监控
    • 测试策略与 Mock 实践
    • 最佳实践与面试考点汇总

大翔拍板:"产品手册、FAQ、内部制度全部灌进去,下周我要看到一个'飞翔科技专属 GPT'。"白歌和小崔花了一个下午,从 ETL 到 RAG 一条龙搞定。

RAG 实战案例

定义与作用

本章将第 6 章的 ETL Pipeline 与本章的 QuestionAnswerAdvisor 串联,构建完整的飞翔科技智能客服系统——从零开始,包含数据注入和智能问答。

完整案例:飞翔科技智能客服

场景说明

飞翔科技积累了 5 份核心文档:

  • 产品手册.pdf(120 页,含 API 文档、部署指南)
  • FAQ 常见问题.pdf
  • 员工手册.pdf(请假、报销、会议室等)
  • 技术白皮书.pdf(系统架构、性能指标)
  • 客户案例集.pdf

目标:客服 AI 能基于这些文档回答产品功能、技术参数、内部流程等问题。

ETL 阶段:多文档批量注入

@Service
public class KnowledgeBaseLoader {

    private final VectorStore vectorStore;

    public KnowledgeBaseLoader(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    @PostConstruct
    public void loadAllDocuments() {
        List<Resource> resources = List.of(
            new FileSystemResource("C:/docs/feixiang/产品手册.pdf"),
            new FileSystemResource("C:/docs/feixiang/FAQ常见问题.pdf"),
            new FileSystemResource("C:/docs/feixiang/员工手册.pdf"),
            new FileSystemResource("C:/docs/feixiang/技术白皮书.pdf"),
            new FileSystemResource("C:/docs/feixiang/客户案例集.pdf")
        );

        TokenTextSplitter splitter = new TokenTextSplitter();
        splitter.setDefaultChunkSize(500);
        splitter.setMinChunkSizeChars(200);

        int totalChunks = 0;
        for (Resource resource : resources) {
            // Extract
            PagePdfDocumentReader reader = new PagePdfDocumentReader(resource);
            List<Document> pages = reader.get();

            // 注入来源元数据
            String fileName = resource.getFilename();
            for (Document doc : pages) {
                doc.getMetadata().put("source", fileName);
            }

            // Transform
            List<Document> chunks = splitter.apply(pages);

            // Load
            vectorStore.add(chunks);
            totalChunks += chunks.size();

            System.out.printf("已注入:%s → %d 块\n", fileName, chunks.size());
        }

        System.out.printf("知识库构建完成!共 %d 个文本块\n", totalChunks);
    }
}

RAG 阶段:智能客服接口

@Configuration
public class SmartServiceConfig {

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder,
                                  VectorStore vectorStore) {
        QuestionAnswerAdvisor ragAdvisor = new QuestionAnswerAdvisor(vectorStore);
        ragAdvisor.setPromptTemplate("""
                你是飞翔科技的智能客服。请基于以下产品文档回答用户问题。
                
                【回答要求】
                1. 优先使用文档中的原话,保持术语准确
                2. 如果涉及多个方案,列出对比
                3. 附上文档来源段落编号
                4. 如果文档未覆盖该问题,回复:
                   「该问题暂未收录,已转人工客服处理」
                
                相关文档:
                {context}
                
                用户问题:{question}
                
                回答:
                """);

        return builder
                .defaultAdvisors(
                    ragAdvisor,
                    new SimpleLoggerAdvisor()  // 方便李眉监控调用
                )
                .defaultSystem("你是飞翔科技智能客服,态度友好专业。")
                .build();
    }
}
@RestController
@RequestMapping("/api/v1/customer-service")
public class CustomerServiceController {

    private final ChatClient chatClient;

    public CustomerServiceController(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    @GetMapping("/chat")
    public Map<String, String> chat(@RequestParam String question) {
        // 注意:不需要传 VectorStore 参数
        // QuestionAnswerAdvisor 自动处理检索
        
        String answer = chatClient.prompt()
                .user(question)
                .call()
                .content();
        
        return Map.of(
            "question", question,
            "answer", answer
        );
    }

    /**
     * 带部门过滤的搜索(不同部门看到不同文档)
     */
    @GetMapping("/department-chat")
    public Map<String, String> departmentChat(
            @RequestParam String question,
            @RequestParam(defaultValue = "全公司") String department) {
        
        // 可以注入 VectorStore 单独做精确检索
        String answer = chatClient.prompt()
                .user(question)
                .call()
                .content();
        
        return Map.of(
            "department", department,
            "question", question,
            "answer", answer
        );
    }
}

运行结果对比例

// ========== 操作前(无 RAG)==========
GET /api/v1/customer-service/chat?question=飞翔API的QPS上限是多少?

{
  "question": "飞翔API的QPS上限是多少?",
  "answer": "作为AI助手,我没有飞翔科技API的具体性能数据。
             Typically API rate limits vary by plan..."
}

// ========== 操作后(有 RAG)==========
GET /api/v1/customer-service/chat?question=飞翔API的QPS上限是多少?

{
  "question": "飞翔API的QPS上限是多少?",
  "answer": "根据《技术白皮书》第4章性能指标:
            - 标准版:100 QPS(支持扩容至 500 QPS)
            - 企业版:1000 QPS(支持扩容至 5000 QPS)
            - 旗舰版:不限(需独立部署)
            扩容需提前 3 个工作日提交工单。
            [技术白皮书-P42]"
}

操作前后对比

维度无 RAG有 RAG
回答准确度约 40%(全靠模型"常识")约 92%(基于真实文档)
幻觉率约 30%约 3%(Prompt 约束 + 来源标注)
产品术语一致性完全不可控与文档保持一致
可追溯性无法追溯答案来源每条答案标注文档页码
知识更新需重新训练/更换模型更新 PDF → 重新 ETL 即可

面试高频题

Q1:RAG 检索结果不相关怎么排查?

排查路径:①检查 TokenTextSplitter 的 chunkSize 是否过大(推荐 500);②检查相似度阈值(similarityThreshold)设置是否过宽(推荐 0.7);③检查 Prompt 模板是否指示 AI 在无相关文档时明确告知用户;④考虑引入 KeywordMetadataEnricher 增强元数据维度;⑤检查向量库的 distance-type 是否适合当前场景(语义搜索推荐 COSINE_DISTANCE)。

Q2:RAG 的延迟瓶颈在哪里?如何优化?

主要瓶颈:①Embedding 模型调用延迟(可通过批量 Embedding 减少 API 调用次数);②向量搜索延迟(可通过 HNSW 索引优化、增加内存缓存);③Prompt 拼接后 Token 数量过大(可控制 topK 值,限制返回片段数)。

本章小结

  • 完整 RAG 系统 = ETL 注入(启动时一次)+ QuestionAnswerAdvisor(每次请求自动)
  • 通过元数据(source/department)支持精确过滤和来源追溯
  • 操作前后核心指标对比:准确度从 40% 提升至 92%,幻觉率从 30% 降至 3%
  • 排查 RAG 问题从 chunkSize、相似度阈值、Prompt 模板三方面入手
上一页
QuestionAnswerAdvisor 详解