阿里开源AgentScope多智能体框架解析系列(十二)第12章:RAG(检索增强生成)

本章导读

本章介绍AgentScope的RAG(Retrieval Augmented Generation,检索增强生成)系统,这是让Agent能够基于最新知识库回答问题的关键技术。

RAG通过检索外部知识库中的相关信息 ,为LLM提供上下文,从而显著提升回答的准确性和时效性

本章概览

  • What(是什么):理解RAG的工作原理和架构
  • Why(为什么):明确什么时候需要使用RAG
  • How(怎样做):完整的实现示例和生产可用案例

12.1 RAG设计理念

什么是RAG?

**RAG(检索增强生成)**是一种混合式的处理流程:

css 复制代码
用户问题
  ↓
[检索阶段] 在知识库中搜索相关文档
  ↓
[增强阶段] 将检索到的文档作为上下文
  ↓
[生成阶段] LLM基于上下文回答问题
  ↓
准确、可追溯的答案

RAG的核心优势

特性 传统LLM RAG增强
知识时效性 依赖训练数据(可能过时) 基于最新知识库 ✓
可追溯性 黑盒回答 可引用具体来源 ✓
领域专业性 通用知识 垂直领域专业知识 ✓
成本 需要大模型 可用小模型 ✓
隐私 数据上传到厂商 私有知识库 ✓

RAG的三个核心阶段

1. 检索(Retrieval)

css 复制代码
目标:从知识库中找到与用户问题最相关的文档

方法:
├─ 关键词匹配(BM25):快速但精度低
├─ 语义相似度(向量):精度高,需要Embedding
└─ 混合方法:结合两者优点

过程:
用户问题 → 向量化 → 在向量库中搜索
  ↓
返回top-k相关文档

2. 增强(Augmentation)

sql 复制代码
目标:将检索到的文档融入到LLM的输入中

方式:
├─ 追加到System Prompt
├─ 插入到上下文中
└─ 作为工具输入

结果:
LLM获得了额外的上下文信息,能做出更好的回答

3. 生成(Generation)

arduino 复制代码
目标:基于增强后的输入,生成准确的答案

特点:
├─ 可以引用具体来源
├─ 回答更专业和准确
└─ 避免"幻觉"(生成虚假信息)

12.2 RAG的工作原理

为什么需要RAG?

1. LLM知识的局限性

vbnet 复制代码
问题:LLM的训练数据有截止日期,无法回答最新的问题

示例:
Q: "2024年最新的AI法规是什么?"
A: 模型训练于2023年10月,无法回答

解决:RAG可以从最新的知识库中检索相关信息

2. 领域专业性要求

vbnet 复制代码
问题:通用LLM可能缺乏特定领域的专业知识

示例:
Q: "我们公司的产假政策是什么?"
A: 通用LLM只能给出通用政策,不知道公司具体规定

解决:将公司文件添加到知识库,RAG可以直接回答

3. 成本和隐私

diff 复制代码
问题:大模型贵,私有信息不能上传

示例:
- 用更小的模型也能获得准确的专业回答
- 私有知识保留在内部,不上传到云端

解决:结合RAG和小模型(如qwen-turbo),低成本高效率

RAG的核心价值

markdown 复制代码
┌──────────────────────────────┐
│    知识库 + 小模型           │
│ = 大模型的效果 + 低成本      │
└──────────────────────────────┘
                ↓
┌──────────────────────────────┐
│   专业、准确、可追溯         │
│   的智能问答系统             │
└──────────────────────────────┘

12.3 RAG的实现方式

基础RAG实现

java 复制代码
public class BasicRAGDemo {
    
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        // 1. 创建知识库
        System.out.println("=== 初始化知识库 ===\n");
        
        Knowledge knowledge = new SimpleEmbeddingKnowledge(
            new DashScopeEmbeddingService(),
            new InMemoryVectorStore()
        );
        
        // 2. 添加公司知识文档
        System.out.println("=== 添加知识文档 ===\n");
        
        List<Document> documents = List.of(
            new Document(
                "产假政策",
                "女性员工享受158天产假,其中:" +
                "- 产前30天\n" +
                "- 产后128天\n" +
                "符合条件的可申请延长30天"
            ),
            new Document(
                "年假政策",
                "年假计算:\n" +
                "- 入职1-10年:10天\n" +
                "- 入职10-20年:15天\n" +
                "- 入职20年以上:20天\n" +
                "年假可以跨年结转,最多3年"
            ),
            new Document(
                "远程工作政策",
                "申请条件:\n" +
                "- 入职满6个月\n" +
                "- 性能评级不低于B\n" +
                "频率:每周最多3天\n" +
                "需要主管批准"
            ),
            new Document(
                "技术栈",
                "后端:Java 17 + Spring Boot 3.0 + Spring Cloud\n" +
                "数据库:MySQL + Redis + Elasticsearch\n" +
                "部署:Kubernetes + Docker\n" +
                "监控:Prometheus + Grafana"
            )
        );
        
        knowledge.addDocuments(documents).block();
        System.out.println("已添加 " + documents.size() + " 个文档\n");
        
        // 3. 创建支持RAG的Agent
        System.out.println("=== 创建支持RAG的Agent ===\n");
        
        ReActAgent agent = ReActAgent.builder()
            .name("CompanyInfoAgent")
            .model(model)
            .memory(new InMemoryMemory())
            .systemPrompt("你是公司员工的HR助手,回答关于公司政策和技术的问题。" +
                         "如果使用了知识库中的信息,请明确引用来源。")
            .hooks(List.of(
                new RAGHook(knowledge)
            ))
            .build();
        
        // 4. 提问
        System.out.println("=== 提问环节 ===\n");
        
        String[] questions = {
            "我刚入职,想了解公司的年假政策",
            "能否告诉我们公司使用的技术栈?",
            "我需要申请产假,能享受多久?"
        };
        
        for (String question : questions) {
            System.out.println("Q: " + question);
            
            Msg response = agent.call(Msg.builder()
                .role(MsgRole.USER)
                .textContent(question)
                .build()).block();
            
            System.out.println("A: " + response.getTextContent() + "\n");
        }
    }
    
    /**
     * 简单的RAG Hook实现
     */
    public static class RAGHook implements Hook {
        private final Knowledge knowledge;
        
        public RAGHook(Knowledge knowledge) {
            this.knowledge = knowledge;
        }
        
        @Override
        public <T extends HookEvent> Mono<T> onEvent(T event) {
            if (!(event instanceof PreReasoningEvent pre)) {
                return Mono.just(event);
            }
            
            // 从用户问题中提取查询
            List<Msg> messages = pre.getInputMessages();
            if (messages.isEmpty()) {
                return Mono.just(event);
            }
            
            String query = messages.get(messages.size() - 1).getTextContent();
            
            // 检索相关文档
            return knowledge.retrieve(query, RetrieveConfig.builder()
                .limit(3)
                .scoreThreshold(0.5)
                .build())
            .flatMapMany(documents -> {
                // 将检索结果作为System消息插入
                String ragContext = "相关知识库信息:\n";
                for (Document doc : documents) {
                    ragContext += "\n【" + doc.getTitle() + "】\n" +
                                 doc.getContent() + "\n";
                }
                
                Msg ragMsg = Msg.builder()
                    .role(MsgRole.SYSTEM)
                    .textContent(ragContext)
                    .build();
                
                List<Msg> enhanced = new ArrayList<>();
                enhanced.add(ragMsg);
                enhanced.addAll(messages);
                
                pre.setInputMessages(enhanced);
                
                return Mono.just(event);
            })
            .thenReturn(event);
        }
        
        @Override
        public int getPriority() {
            return 20;
        }
    }
}

使用工具方式的RAG

java 复制代码
public class ToolBasedRAGDemo {
    
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        // 创建知识库
        Knowledge knowledge = new SimpleEmbeddingKnowledge(
            new DashScopeEmbeddingService(),
            new InMemoryVectorStore()
        );
        
        knowledge.addDocuments(getSampleDocuments()).block();
        
        // 创建Agent,通过工具而不是Hook使用RAG
        ReActAgent agent = ReActAgent.builder()
            .name("ToolBasedRAGAgent")
            .model(model)
            .toolkit(new Toolkit()
                .registerObject(new RAGTools(knowledge)))
            .systemPrompt("""
                你是一个信息助手。
                当需要查询信息时,使用searchKnowledgeBase工具。
                """)
            .build();
        
        // 提问时,Agent会主动调用RAG工具
        Msg response = agent.call(Msg.builder()
            .textContent("请告诉我产假的具体规定")
            .build()).block();
        
        System.out.println(response.getTextContent());
    }
    
    /**
     * 提供RAG功能的工具集
     */
    public static class RAGTools {
        private final Knowledge knowledge;
        
        public RAGTools(Knowledge knowledge) {
            this.knowledge = knowledge;
        }
        
        @Tool(description = "在知识库中搜索相关信息")
        public String searchKnowledgeBase(String query) {
            List<Document> results = knowledge.retrieve(query, 
                RetrieveConfig.builder()
                    .limit(5)
                    .scoreThreshold(0.5)
                    .build())
            .block();
            
            if (results.isEmpty()) {
                return "知识库中未找到相关信息";
            }
            
            StringBuilder sb = new StringBuilder("搜索结果:\n");
            for (Document doc : results) {
                sb.append("【").append(doc.getTitle()).append("】\n");
                sb.append(doc.getContent()).append("\n\n");
            }
            return sb.toString();
        }
    }
    
    private static List<Document> getSampleDocuments() {
        return List.of(
            new Document("产假政策", "女性员工享受158天产假..."),
            new Document("年假政策", "年假计算:入职1-10年:10天..."),
            new Document("远程工作政策", "申请条件:入职满6个月...")
        );
    }
}

12.4 生产场景:企业知识库QA系统

场景描述

markdown 复制代码
一个完整的企业知识库系统:
1. 用户提出各种问题(HR、IT、产品等)
2. 系统自动检索相关知识库文档
3. 基于检索结果给出准确的回答
4. 追踪未能回答的问题,不断完善知识库

实现代码

java 复制代码
public class EnterpriseKnowledgeQASystem {
    
    /**
     * 知识库管理系统
     */
    public static class KnowledgebaseManager {
        private final Knowledge knowledge;
        private final ReActAgent qaAgent;
        private final Map<String, Integer> unansweredQuestions = new ConcurrentHashMap<>();
        
        public KnowledgebaseManager(Model model) {
            this.knowledge = new SimpleEmbeddingKnowledge(
                new DashScopeEmbeddingService(),
                createPersistentVectorStore()
            );
            
            this.qaAgent = ReActAgent.builder()
                .name("EnterpriseQAAgent")
                .model(model)
                .memory(new InMemoryMemory())
                .toolkit(new Toolkit()
                    .registerObject(new KBTools(knowledge)))
                .hooks(List.of(
                    new RAGHook(knowledge),
                    new FeedbackHook(unansweredQuestions)
                ))
                .systemPrompt(getSystemPrompt())
                .build();
            
            // 初始化知识库
            initializeKnowledgebase();
        }
        
        /**
         * 回答用户问题
         */
        public String answerQuestion(String question) {
            System.out.println("\n用户问题:" + question);
            
            Msg response = qaAgent.call(Msg.builder()
                .role(MsgRole.USER)
                .textContent(question)
                .build()).block();
            
            String answer = response.getTextContent();
            System.out.println("系统回答:" + answer);
            
            return answer;
        }
        
        /**
         * 用户反馈
         */
        public void provideFeedback(String question, String feedback) {
            System.out.println("\n用户反馈:" + feedback);
            
            // 记录反馈,用于持续改进
            if (feedback.contains("不准确") || feedback.contains("不符合")) {
                System.out.println("⚠️ 记录为不准确回答,需要改进知识库");
            } else {
                System.out.println("✓ 感谢反馈,用于改进系统");
            }
        }
        
        /**
         * 添加新文档到知识库
         */
        public void addKnowledgeDocuments(List<Document> documents) {
            knowledge.addDocuments(documents).block();
            System.out.println("已添加 " + documents.size() + " 个新文档");
        }
        
        /**
         * 获取未能回答的问题统计
         */
        public void showUnansweredStatistics() {
            System.out.println("\n=== 未能回答的问题统计 ===");
            unansweredQuestions.entrySet().stream()
                .sorted((a, b) -> b.getValue().compareTo(a.getValue()))
                .forEach(entry -> 
                    System.out.println("「" + entry.getKey() + "」:" + 
                        entry.getValue() + "次"));
        }
        
        private void initializeKnowledgebase() {
            List<Document> docs = List.of(
                new Document("入职流程", "新员工入职流程:..."),
                new Document("办公系统", "公司使用:OA系统、钉钉、企业邮箱..."),
                new Document("差旅费规定", "国内出差:...,国际出差:..."),
                new Document("采购流程", "小于5000元:部门批准即可..."),
                new Document("绩效评估", "年度评估周期:...,晋升标准:...")
            );
            addKnowledgeDocuments(docs);
        }
        
        private String getSystemPrompt() {
            return """
                你是企业知识库的AI助手,帮助员工了解公司政策和流程。
                
                原则:
                1. 优先使用知识库信息回答问题
                2. 如果知识库中没有相关信息,明确说明
                3. 对于敏感问题(如薪资、个人隐私),建议联系HR
                4. 回答时明确引用知识库来源
                5. 鼓励用户反馈,帮助我们改进
                
                如果无法回答,请说:"抱歉,我在知识库中没有找到相关信息,
                请联系相应部门或HR。"
                """;
        }
        
        private VectorStore createPersistentVectorStore() {
            // 在生产中应该使用如Milvus、Weaviate等向量数据库
            return new InMemoryVectorStore();
        }
    }
    
    /**
     * 知识库工具集
     */
    public static class KBTools {
        private final Knowledge knowledge;
        
        public KBTools(Knowledge knowledge) {
            this.knowledge = knowledge;
        }
        
        @Tool(description = "在知识库中搜索")
        public String search(String keyword) {
            List<Document> results = knowledge.retrieve(keyword,
                RetrieveConfig.builder()
                    .limit(5)
                    .scoreThreshold(0.5)
                    .build())
            .block();
            
            return formatResults(results);
        }
        
        private String formatResults(List<Document> documents) {
            if (documents.isEmpty()) {
                return "未找到相关文档";
            }
            
            StringBuilder sb = new StringBuilder();
            for (Document doc : documents) {
                sb.append("【").append(doc.getTitle()).append("】\n")
                  .append(doc.getContent()).append("\n\n");
            }
            return sb.toString();
        }
    }
    
    /**
     * 反馈Hook:记录无法回答的问题
     */
    public static class FeedbackHook implements Hook {
        private final Map<String, Integer> unansweredQuestions;
        
        public FeedbackHook(Map<String, Integer> unansweredQuestions) {
            this.unansweredQuestions = unansweredQuestions;
        }
        
        @Override
        public <T extends HookEvent> Mono<T> onEvent(T event) {
            if (event instanceof PostReasoningEvent post) {
                // 检查回答质量
                String response = post.getReasoningResult();
                
                if (response.contains("无法") || response.contains("不清楚")) {
                    // 记录无法回答的问题
                    String question = extractQuestion(post.getInputMessages());
                    unansweredQuestions.put(question,
                        unansweredQuestions.getOrDefault(question, 0) + 1);
                }
            }
            return Mono.just(event);
        }
        
        private String extractQuestion(List<Msg> messages) {
            if (!messages.isEmpty()) {
                return messages.get(messages.size() - 1).getTextContent();
            }
            return "Unknown";
        }
        
        @Override
        public int getPriority() {
            return 100;
        }
    }
    
    /**
     * RAG Hook
     */
    public static class RAGHook implements Hook {
        private final Knowledge knowledge;
        
        public RAGHook(Knowledge knowledge) {
            this.knowledge = knowledge;
        }
        
        @Override
        public <T extends HookEvent> Mono<T> onEvent(T event) {
            if (!(event instanceof PreReasoningEvent pre)) {
                return Mono.just(event);
            }
            
            // 检索相关文档
            List<Msg> messages = pre.getInputMessages();
            String query = messages.isEmpty() ? "" : 
                messages.get(messages.size() - 1).getTextContent();
            
            if (query.isEmpty()) {
                return Mono.just(event);
            }
            
            return knowledge.retrieve(query, RetrieveConfig.builder()
                .limit(3)
                .scoreThreshold(0.5)
                .build())
            .flatMapMany(documents -> {
                String context = buildRAGContext(documents);
                
                Msg ragMsg = Msg.builder()
                    .role(MsgRole.SYSTEM)
                    .textContent(context)
                    .build();
                
                List<Msg> enhanced = new ArrayList<>();
                enhanced.add(ragMsg);
                enhanced.addAll(messages);
                
                pre.setInputMessages(enhanced);
                
                return Mono.just(event);
            })
            .thenReturn(event);
        }
        
        private String buildRAGContext(List<Document> documents) {
            if (documents.isEmpty()) {
                return "";
            }
            
            StringBuilder sb = new StringBuilder("【来自知识库的相关信息】\n");
            for (Document doc : documents) {
                sb.append("\n").append(doc.getTitle()).append(":\n")
                  .append(doc.getContent()).append("\n");
            }
            return sb.toString();
        }
        
        @Override
        public int getPriority() {
            return 20;
        }
    }
    
    /**
     * 测试主程序
     */
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        KnowledgebaseManager kb = new KnowledgebaseManager(model);
        
        // 回答一系列问题
        String[] questions = {
            "新员工入职有哪些流程?",
            "我要出国出差,有什么规定吗?",
            "怎样申请采购?",
            "无敌问题?"  // 故意问一个无法回答的问题
        };
        
        for (String question : questions) {
            kb.answerQuestion(question);
            kb.provideFeedback(question, "✓ 很有帮助");
        }
        
        // 显示统计信息
        kb.showUnansweredStatistics();
    }
}

12.5 支持的知识库后端

java 复制代码
// 1. 内存向量库(开发用)
Knowledge kb1 = new SimpleEmbeddingKnowledge(
    embeddingService,
    new InMemoryVectorStore()
);

// 2. 阿里云百炼
Knowledge kb2 = new BailianKnowledge(
    apiKey,
    workspaceId,
    datasetId
);

// 3. Dify
Knowledge kb3 = new DifyKnowledge(
    baseUrl,
    apiKey
);

// 4. RAGFlow
Knowledge kb4 = new RAGFlowKnowledge(
    baseUrl,
    apiKey
);

// 5. 自建向量库(Milvus、Weaviate等)
Knowledge kb5 = new CustomVectorKnowledge(
    customVectorStore,
    customEmbeddingService
);

本章总结

本章介绍了RAG(检索增强生成)系统,这是让Agent基于最新知识库回答问题的核心技术:

核心要点

  • RAG是检索+生成的混合模式
  • 支持最新知识、领域专业性、成本优化
  • 通过Hook或工具集成RAG功能

使用场景

  • 企业知识库QA系统
  • 文档基础的客服
  • 持续学习和知识积累
  • 私有信息的安全处理

下一章预告

第13章:结构化输出(Structured Output)

当Agent需要返回机器可解析的数据而不是自由文本时,结构化输出就变得至关重要。第13章将介绍如何确保LLM返回类型安全的、结构化的数据。

相关推荐
语落心生2 小时前
阿里开源AgentScope多智能体框架解析系列(十)第10章:中断机制
agent
语落心生7 小时前
阿里开源AgentScope多智能体框架解析系列(三)第3章:模型接口(Model)与适配器模式
agent
FreeCode7 小时前
智能体设计模式解析:交接模式(Handoffs)
langchain·agent·ai编程
语落心生7 小时前
阿里开源AgentScope多智能体框架解析系列(二)第2章:消息系统(Message)深入解析
agent
语落心生7 小时前
阿里开源AgentScope多智能体框架解析系列(一)第1章:AgentScope-Java 概述与快速入门
agent
语落心生7 小时前
阿里开源AgentScope多智能体框架解析系列(四)第4章:Agent 接口与 AgentBase 基类
agent
潘锦8 小时前
AI Agent 核心管理逻辑:工具的管理和调度
agent
Jay Kay9 小时前
Agent沙箱执行服务
agent·沙箱服务
小马过河R10 小时前
ReAct和Function Calling之间的纠葛与恩恩怨怨
人工智能·语言模型·agent·智能体