06、RAG

RAG技术全面解析与应用

LLM的知识仅限于它所接受到的训练数据。如果我们希望让它了解特定领域的专有知识,则可以使用下面的方式操作:

  • 使用RAG
  • 使用专有数据对LLM进行微调
  • RAG与数据微调方式结合使用

什么是RAG

简单地说,RAG就是把数据发送给LLM之前从数据中查找相关信息片段并把它注入到提示符的方法。这样的话LLM获得相关信息,并可以使用这些信息进行回复。

那么最为重要的是我们要可以检索到信息,可以使用下面的一些方法:

  1. 全文(关键字)检索,这咱方法使用IF-IDF和BM25之类的技术,通过在文档数据库中匹配查询关键字(用户询问的内容)来检索文档。它会根据这些关键字在每个文档中的频率和相关性对结果进行排序。
  2. 向量搜索,有称为“语义搜索”。使用嵌入模型把长文本文档转换为数字向量。然后,根据查询向量和文档向量之间的余弦相似度或其他相似度,来查找和排序文档,从而获得更深层次的语义。
  3. 混合检索,结合多种搜索方法(如:全文 + 向量),通常这样子可以提高检索的效率。

RAG的两阶段处理

RAG的过程是分为两个阶段的:索引和检索

索引

索引阶段就是为了后一阶段检索而进行的对文档的预处理

在这个阶段会根据所使用的信息检索方法而有变化。对于向量检索,通常包含清理文档、用额外的数据和元数据丰富它们、把它们分割为更小的段/块,最后把它们存储到存储区(向量数据库)中。

索引阶段通常是脱机进行的,这意味着它不需要最终用户等待它的操作完成。这样的话我们就可以使用定时任务在固定时间进行重新索引知识库文档,负责进行索引的代码也可以是一个单独的代码也可以是一个单独的应用程序,专门只用来处理索引任务。

在有些情况下,最终用户可能希望上传他们自己的文档,便于LLM访问到它们。在这种情况下索引阶段应该是在线执行的,并且是主应用程序的一部分。

关于索引阶段官方示意图如下:

检索

检索阶段通常是在线进行的,它处于用户提交应该使用索引文档回答问题时。

在这个过程中会根据所使用的信息检索方法而变化。对于向量搜索,通常来说涉及到用户的查询并在嵌入存储中地相似性搜索。然后把这个片段(原始文档的片段)注入提示并发送给LLM。

关于检索的官方示意图如下:

三种不同的RGA

  • Easy RAG:使用RAG最简单的方法
  • Native RAG:一个使用矢量搜索的RAG基本实现
  • Advanced RAG:一个模块化的RAG框架,它允许额的步骤,如查询转换、多个源检索和重新排序

Easy RAG

LangChain4j有一个简单的RAG实现,使用这个不需要额外去了解嵌入、矢量存储、嵌入模型以及了解如何解析和分割文档等等,我们只需要指向指定的文档,LangChain4j就会去处理。

注意:这种简单的RAG在质量上肯定是会低于定制RAG的

使用步骤

第一步:我们要使用Easy RAG功能,必须要添加相对应的依赖

<dependency>    
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-easy-rag</artifactId>
</dependency>

第二步:加载指定文档

List<Document> documents = FileSystemDocumentLoader.loadDocuments("E://myDocument");

第二步会加载指定目录下的所有文档,它的实现依赖于Apache Tika库支持,Tika库支持多种类型文档的解析,由于这里没有指定具体的DocumentParser,那么FileSystemDocumentLoader会加载一个ApacheTikaDocumentParser,这是由langchain4j-easy-rag依赖项通过SPI提供的。

我们了可以自定义需要解析哪些个文档,可以给定一个匹配规则,然后解析目录下满足规则的文档

PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:**" + ".txt");
FileSystemDocumentLoader.loadDocuments("E://myDocument", pathMatcher);

上面的话给定了一个规则,则只会解.txt的文档

第三步:我们需要一个专门的嵌入存储(矢量数据库)对文档进行预处理和存储

InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(documents, embeddingStore);

EmbeddingStoreIngestor通过SPI从langchain4j-easy-rag依赖项加载一个DocumentSplitter。每个文档被分割为更小的部分,每个部分由不超过300个令牌组成,并有30个令牌的重叠。

EmbeddingStoreIngestor通过SPI从langchain4j-easy-rag依赖项加载一个EmbeddingModel,使用这个EmbeddingModel把每个TextSegment转为嵌入

最终所有的TextSegment-Embedding对都会存储到EmbeddingStore

第四步:定义和创建AiService

具体示例

1、pom依赖项添加

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-easy-rag</artifactId>
</dependency>

2、创建一个Document的工具类用来封装加载文档的方法

public class DocumentUtils {

    /**
     * 根据指定的目录加载目录下所有文档
     * @param dir 目录
     * @return 文档列表
     */
    public static List<Document> loadDocuments(String dir) {
        return FileSystemDocumentLoader.loadDocuments(dir);
    }

    /**
     * 根据指定的目录加载目录下指定类型的文件
     * @param dir 指定要加载的目录
     * @param filter 文件类型
     * @return 文档列表
     */
    public static List<Document> loadDocuments(String dir, String filter) {
        PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:**" + filter);
        return FileSystemDocumentLoader.loadDocuments(dir, pathMatcher);
    }
    
    /**
     * 根据指定的目录加载目录下所有文档
     * @param dir 目录
     * @param containsSubDir 是否包含子目录
     * @return 文档列表
     */
    public static List<Document> loadDocuments(String dir,Boolean containsSubDir) {
        if (containsSubDir) {
            return FileSystemDocumentLoader.loadDocumentsRecursively(dir);
        }
        return FileSystemDocumentLoader.loadDocuments(dir);
    }


    /**
     * 根据指定的目录加载目录下指定类型的文件
     * @param dir 指定要加载的目录
     * @param filter 文件类型
     * @param containsSubDir 是否包含子目录
     * @return 文档列表
     */
    public static List<Document> loadDocuments(String dir,String filter,Boolean containsSubDir) {
        // 多级目录时写**
        PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:**" + filter);
        if (containsSubDir) {
            return FileSystemDocumentLoader.loadDocumentsRecursively(dir, pathMatcher);
        }
        return FileSystemDocumentLoader.loadDocuments(dir, pathMatcher);
    }
}

3、创建AiService接口

public interface Assistant {
    Flux<String> chat(@MemoryId String memoryId, @UserMessage String message);
}

4、创建AiService实例Bean

@Configuration
public class AssistantConfig {

    @Resource
    private StreamingChatLanguageModel streamingChatLanguageModel;


    @Bean
    public Assistant assistant() {
        InMemoryEmbeddingStore<TextSegment> embeddingStore = getInMemoryEmbeddingStore("E:\\project\\IdeaProjects\\AI\\LangChain4j\\rag\\src\\main\\resources\\documents", ".txt");
        return AiServices.builder(Assistant.class)
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
                .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
                .streamingChatLanguageModel(streamingChatLanguageModel).build();

    }

    private InMemoryEmbeddingStore<TextSegment> getInMemoryEmbeddingStore(String path, String filter) {
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        List<Document> documents = DocumentUtils
### 强化学习与RAG技术的关系及应用 强化学习(Reinforcement Learning, RL)与检索增强生成(Retrieval-Augmented Generation, RAG)的结合,为自然语言处理任务提供了更高效、更智能的解决方案。以下是两者关系及应用的详细分析: #### 1. RAG技术的核心功能 RAG是一种结合了检索和生成的模型架构,其通过引入外部知识库,在生成文本时能够参考大量额外信息,从而提升生成内容的质量和准确性[^1]。RAG模型的关键在于其架构流程、实现方式以及与数据仓库的同步机制,这些因素共同决定了其在实际应用中的效果。 #### 2. 强化学习在RAG中的作用 强化学习通过设计合理的奖励函数,使得RAG系统能够通过不断的交互学习,找到最优的决策策略,以生成更加优质的对话回复[^4]。具体来说,强化学习在RAG中的应用主要体现在以下几个方面: - **优化生成质量**:通过评估回复的信息丰富性、语义相关性、情感共情性等指标,引导RAG系统更好地利用检索结果。 - **提升交互性能**:强化学习可以帮助RAG系统在实时交互场景中快速适应用户需求,生成更符合预期的响应。 - **多模态融合**:在复杂的多模态场景中,强化学习可以驱动RAG系统进行智能升级,进一步提高性能和效率[^5]。 #### 3. RAG与强化学习的对比 尽管RAG和强化学习在目标上存在一定的重叠,但它们的应用侧重点有所不同: - **RAG的核心优势**:RAG专注于通过检索增强生成能力,能够在生成过程中引入外部知识,提升内容的连贯性和信息量[^3]。 - **强化学习的独特价值**:强化学习则侧重于通过试错机制优化策略,帮助系统在动态环境中不断改进[^2]。这种特性使得强化学习特别适合用于优化RAG系统的决策过程。 #### 4. 应用实例 强化学习与RAG的结合已经在多个领域得到了成功应用,例如: - **对话系统优化**:通过强化学习调整RAG模型的参数,使其生成的对话回复更具信息量和相关性。 - **多模态任务**:在涉及图像、文本等多种数据类型的场景中,强化学习驱动的RAG系统能够更好地整合不同模态的信息。 ```python # 示例代码:基于强化学习优化RAG模型的简单框架 class RLAGOptimizer: def __init__(self, rag_model, reward_function): self.rag_model = rag_model self.reward_function = reward_function def train(self, data): for episode in range(num_episodes): state = data[episode] action = self.rag_model.generate(state) reward = self.reward_function.evaluate(action, state) self.rag_model.update_parameters(reward) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值