高级 RAG 技术图解概览

原文:Advanced RAG Techniques: an Illustrated Overview
作者:IVAN ILIN

本文全面研究了高级检索增强式生成技术 (RAG) 及其算法,系统地整理了各种方法。文章中还包含了我知识库中与提到的各种实现和研究相关的链接集。

引言

检索增强生成(Retrieval Augmented Generation,简称 RAG)为大语言模型 (LLMs) 提供了从数据源检索的信息,以此为基础生成回答。简而言之,RAG 结合了搜索技术和大语言模型的提示功能,即模型根据搜索算法找到的信息作为上下文来回答查询问题。无论是查询还是检索的上下文,都会被整合到发给大语言模型的提示中。

2023 年,基于 RAG 架构的大语言模型系统成为最受欢迎的技术。许多产品几乎全依赖 RAG 架构,这包括结合网络搜索引擎和大语言模型的问答服务,以及数以百计的数据交互应用程序。

即使是 向量搜索 领域也因 RAG 的热潮而得到推动,尽管基于嵌入式搜索引擎的技术早在 2019 年就已经运用了 faiss。例如 chroma、weaviate.io 和 pinecone 这样的向量数据库初创公司在现有的开源搜索索引基础上 — 主要是 faiss 和 nmslib — 构建了自己的系统,并在近期增加了输入文本的额外存储和其他工具。

两个最显著的开源库用于基于大语言模型的管道和应用程序 是 LangChain 和 LlamaIndex,它们分别在 2022 年 10 月和 11 月成立,相差一个月,受到 ChatGPT 发布的启发,并在 2023 年获得了广泛应用。

本文旨在系统地介绍高级 RAG 技术及其在 LlamaIndex 等平台的实现,以帮助开发者深入理解这项技术。

问题在于,大多数教程仅挑选个别技术进行详细讲解,而没有全面介绍所有可用的工具。

另一个问题是,LlamaIndex 和 LangChain 都是发展迅速的优秀开源项目,以至于它们的文档已经比 2016 年的机器学习教科书还要厚。

原始 RAG 方法

本文中所讨论的 RAG 流程起始于一系列文本文档——本文不涉及此之前的内容,而是将其交由能够连接到从 Youtube 到 Notion 等任何想象得到的来源的优秀开源数据加载器处理。

作者设计的一个方案,以及文中接下来的所有方案

作者设计的一个方案,以及文中接下来的所有方案

标准 RAG 方法 简而言之,是这样的:你将文本分割成小块,然后使用某种 Transformer Encoder 模型将这些小块转换为向量,把这些向量汇总到一个索引中,最后创建一个针对大语言模型的提示,指导模型根据我们在搜索步骤中找到的上下文回答用户的查询。
在实际运行中,我们用相同的 Encoder 模型将用户的查询转化为向量,然后对这个查询向量进行搜索,与索引进行匹配,找出最相关的前 k 个结果,从我们的数据库中提取相应的文本块,并将其作为上下文输入 LLM 进行处理。

这样的提示可以是:

def question_answering(context, query):
    prompt = f"""
Give the answer to the user query delimited by triple backticks ```{query}```\
using the information given in context delimited by triple backticks ```{context}```.\
If there is no relevant information in the provided context, try to answer yourself,
but tell user that you did not have any relevant context to base your answer on.
Be concise and output the answer of size less than 80 tokens.
"""

    response = get_completion(instruction, prompt, model="gpt-3.5-turbo")
    answer = response.choices[0].message["content"]
    return answer

一个 RAG 提示的例子

提示优化 是提升 RAG 流程性能的一种简便有效的方法。不要忘了查阅 OpenAI 提供的详尽的 提示优化指南。

虽然 OpenAI 是大语言模型提供商的市场领头羊,但还有其他不少选择,例如 Anthropic 的 Claude,Mistral 推出的最新潮流的小型但功能强大的模型 Mixtral,Microsoft 的 Phi-2,以及如 Llama2,OpenLLaMA,Falcon 等众多开源选择,供你为 RAG 流程选用不同的“大脑”。

高级 RAG 技术概览

接下来,我们将详细探讨高级 RAG 技术。

下面这幅图展示了涉及的核心步骤和算法。

为了使图解清晰易懂,我们省略了一些逻辑循环和复杂的多步骤智能体行为。

这是高级 RAG 架构中的一些关键组成部分,更像是一套可选工具的集合,而不是固定的设计蓝图。

这是高级 RAG 架构中的一些关键组成部分,更像是一套可选工具的集合,而不是固定的设计蓝图。

图中的绿色元素代表我们将进一步探讨的核心 RAG 技术,蓝色部分则是相关文本。并不是所有高级 RAG 概念都能在一张图上展示得一目了然,例如,我们省略了一些扩展上下文的方法 — 我们会在后续的讨论中逐步深入这些话题。

1 分块 (Chunking) & 向量化 (Vectorisation)

首先,我们需要为文档内容创建一个向量索引,然后在实时运行中,寻找这些向量与查询向量之间最小的余弦距离,这样可以找到与查询最接近的语义含义。

1.1 分块 (Chunking)

Transformer 模型的输入序列长度是固定的。即便输入的上下文窗口很大,相比于几页文本的平均向量,一句话或几句话的向量更能准确地代表其语义含义(这也取决于具体的模型,但一般都是这样)。因此,你需要将数据进行分块 —— 把初始文档分成一些块,每块大小适中,既不丢失原有的含义(比如,可以按句子或段落分割文本,而不是将单个句子切成两半)。有许多文本分割工具能够完成这个任务。

块的大小是一个需要深思熟虑的参数 —— 它取决于你所使用的嵌入模型以及它在 tokens 方面的容量。例如,标准的 Transformer 编码模型,如基于 BERT 的句子 Transformers 最多可以处理 512 个 tokens,而 OpenAI ada-002 能够处理更长的序列,例如 8191 个 tokens,但这里需要权衡的是,为 LLM 提供足够的上下文进行推理,同时确保文本嵌入足够具体,以便有效地执行搜索。在这里,你可以找到一项研究,展示了选择块大小时的各种考虑因素。在 LlamaIndex 中,这一问题通过 NodeParser 类 得到解决,它提供了定义自己的文本分割器、元数据、节点/块关系等高级选项。

1.2 向量化 (Vectorisation)

下一步是选择一个 模型来嵌入我们的块 —— 这里有很多选择。我个人倾向于使用 为搜索优化的模型,如 bge-large 或 E5 嵌入模型。你可以查看 MTEB 排行榜 了解最新的模型更新情况。

想了解如何在端到端系统中实现块处理和向量化步骤吗?不妨参考 LlamaIndex 中完整数据摄取管道的示例。

2 搜索索引

2.1 向量存储索引

在这个架构及后续内容中,为了简化描述,我们不考虑编码器部分,直接把查询内容送入索引。显然,查询内容会首先进行向量化。类似地,尽管索引是根据向量而不是具体的块来进行检索的,但最终我们还是以块的形式展现结果,因为获取这些块相对简单。

在这个架构及后续内容中,为了简化描述,我们不考虑编码器部分,直接把查询内容送入索引。显然,查询内容会首先进行向量化。类似地,尽管索引是根据向量而不是具体的块来进行检索的,但最终我们还是以块的形式展现结果,因为获取这些块相对简单。

RAG 管道的核心是搜索索引,它存储了我们在上一步骤中向量化的内容。最基本的实现方法是采用平面索引,即直接计算查询向量与所有块向量之间的距离。

为了实现高效检索,一个更高级的搜索索引应该采用向量索引,比如 faiss、nmslib 或 annoy。这些工具基于近似最近邻居算法,如聚类、树结构或 HNSW 算法。

此外,还有一些托管服务,如 OpenSearch、ElasticSearch 和向量数据库,它们自动处理前文提到的数据摄取流程,例如 Pinecone、Weaviate 和 Chroma。

根据您的索引选择、数据和搜索需求,您还可以存储元数据,并使用元数据过滤器来按照日期或来源等条件进行信息检索。

LlamaIndex 支持多种向量存储索引,同时也兼容其他简单的索引类型,如列表索引、树索引和关键词表索引。关于这些索引,我们会在后续的融合检索部分详细介绍。

2.2 分层索引

当需要从众多文档中检索信息时,高效的搜索、发现相关内容并整合成含有参考来源的答案变得尤为重要。在处理大型数据库时,一个高效的策略是构建两个索引:一个包含摘要,另一个包含文档的各个部分。这样的搜索分为两步:首先利用摘要来筛选出相关文档,然后只在这个筛选出的相关文档集中继续深入搜索。

2.3 假设性问题和 HyDE

另一种策略是让大语言模型为文档的每个部分产生一个问题,并把这些问题转换成数学上的向量。在实际操作中,这些问题向量构成一个索引,用于对用户的查询进行匹配搜索(这里是用问题向量而非原文档的内容向量来构成索引),检索到相应问题后,再链接回原始文档的相应部分,作为大语言模型提供答案的背景信息。

这种方法通过增强查询与假设问题之间的语义相似性,从而提升了搜索的精准度,相比直接使用文档内容的方法效果更佳。

此外,还有一种反向逻辑的方法,名为HyDE。这种方法中,你会让大语言模型针对一个查询生成一个假设性的回应,然后结合这个回应的向量和查询的向量,共同用于提升搜索的效果。

2.4 语境增强

本节的核心理念是通过检索更小的信息块来提高搜索质量,同时为大语言模型增加更多周围语境以便其进行推理。

我们有两种方法:一是通过增加检索到的小块周围的句子来扩大语境;二是递归地将文档分割成包含小块的大块。

2.4.1 句子窗口检索法

在这种方法中,文档的每个句子都被单独编码,这样可以极大提高查询与语境之间的余弦距离搜索的准确性。

为了更好地分析找到的语境,我们在检索到的最相关单句之前后各扩展 k 个句子,然后把这个扩展后的语境送给 LLM 进行推理。

图中绿色部分代表在索引搜索中找到的句子编码,而整个黑色和绿色的段落则是提供给 LLM 以扩大其推理语境的内容。

图中绿色部分代表在索引搜索中找到的句子编码,而整个黑色和绿色的段落则是提供给 LLM 以扩大其推理语境的内容。

2.4.2 自动合并检索法 (也称为 父文档检索法)

这里的思路与句子窗口检索法类似 —— 搜索更精细的信息片段,然后在将这些内容送给 LLM 进行推理前扩大语境窗口。文档被分割成小的子块,这些子块又与更大的父块相关联。

文档被分割成层次化的块结构,最小的叶子块被送至索引。在检索时,我们会找出 k 个叶子块,如果存在 n 个块都指向同一父块,我们就用这个父块替换它们,并把它送给 LLM 用于生成答案。

文档被分割成层次化的块结构,最小的叶子块被送至索引。在检索时,我们会找出 k 个叶子块,如果存在 n 个块都指向同一父块,我们就用这个父块替换它们,并把它送给 LLM 用于生成答案。

首先检索较小的数据块,如果在最初检索到的前 k 个数据块中有超过 n 个数据块与同一个父节点(即更大的数据块)相连,那么我们会用这个父节点来替换提供给大语言模型的上下文。这个过程类似于自动将几个检索到的小数据块合并成一个较大的父数据块,因此得名。值得一提的是,这种搜索只在子节点的索引中进行。想要深入了解,可以查阅 LlamaIndex 的教程,了解 递归检索器 + 节点引用。

2.5 融合检索或混合搜索

这是一个早已有之的想法:结合传统的基于关键词的搜索和现代的语义或向量搜索。传统搜索使用如 tf-idf 或行业标准的 BM25 等稀疏检索算法,而现代搜索则采用语义或向量方法。这两种方法的结合就能产生出色的检索结果。

其关键在于如何恰当地融合这两种不同相似度得分的检索结果。这个问题通常通过 互惠排名融合 (Reciprocal Rank Fusion) 算法来解决,该算法能有效地对检索结果进行重新排序,以得到最终的输出结果。

在 LangChain 中,这种方法是通过 合奏检索器 (Ensemble Retriever) 来实现的,该类将您定义的多个检索器结合起来,比如一个基于 faiss 的向量索引和一个基于 BM25 的检索器,并利用 RRF 算法进行结果的重新排序。

在 LlamaIndex 中,这一过程也是以类似的方式 实现 的。

混合或融合搜索通常能提供更优秀的检索结果,因为它结合了两种互补的搜索算法——既考虑了查询和存储文档之间的语义相似性,也考虑了关键词匹配。

3 重新排名与过滤

在应用了前述的检索算法后,我们得到了初步的搜索结果。下一步是通过过滤、重新排列或转换这些结果来进一步优化它们。在 LlamaIndex 中,你可以找到多种后处理器,这些处理器能够基于相似度分数、关键词、元数据进行过滤,或者使用其他模型如大语言模型、句子 - 转换器交叉编码器和 Cohere 的重新排名 接口来重新排列结果,甚至可以根据元数据的日期新近度来操作——基本上包括了你能想到的所有功能。

这一步是在将我们检索到的上下文送入大语言模型,以获得最终回答之前的最后环节。

现在,我们将深入探索更为高级的 RAG 技术,比如查询转换和路由。这些技术涉及到大语言模型的使用,代表了一种更复杂的逻辑思维——在 RAG 流程中融合了 LLM 的推理能力。

4 查询变换

查询变换是利用大语言模型作为推理引擎,对用户输入进行调整的一系列技术,目的是提升检索的质量。 有多种方法可以实现这一点。

查询变换的原理示意图

查询变换的原理示意图

对于复杂的查询,大语言模型能够将其拆分为多个子查询。 比如,当你问:
_— “在 Github 上,Langchain 和 LlamaIndex 这两个框架哪个更受欢迎?”,
_我们不太可能直接在语料库的文本中找到它们的比较,所以将这个问题分解为两个更简单、具体的子查询是合理的:
_— “Langchain 在 Github 上有多少星?”
— “Llamaindex 在 Github 上有多少星?”
_这些子查询会同时进行,检索到的信息随后被汇总到一个语句中,供大语言模型综合出对原始查询的最终答案。这两个功能分别在 Langchain 中以多查询检索器的形式和在 Llamaindex 中以子问题查询引擎的形式实现。

  1. 回溯提示 是指利用大语言模型来生成更广泛的查询,以此检索到更一般性或高层次的上下文,帮助我们更好地回答原始查询。 我们不仅对原始查询进行了检索,还将这两种上下文都输入到大语言模型中,以生成最终的答案。 这是 LangChain 的一个示例实现。
  2. 查询重写是指使用大语言模型来重新构造初始查询,从而提高检索的效果。LangChain 和 LlamaIndex 都提供了这样的功能,虽然它们的方法有所不同,但我认为 LlamaIndex 在这方面的解决方案更为强大。

参考引用

这部分没有单独编号,因为它更多地被视为一种工具,而非一个改进检索的技术,但它同样重要。 如果我们在生成答案时引用了多个不同的来源,不管是因为初步查询的复杂性(需要执行多个子查询并合并结果),还是因为我们在不同文档中找到了针对单一查询的相关上下文,我们就会面临一个问题:如何能够准确地标注我们所引用的各个来源。

实现这一点有几种方法:

  1. 在我们的提示中加入引用任务,请求大语言模型明确提及所用来源的 id。
  2. 将生成的答案的部分内容与我们索引中的原始文本进行匹配 - LlamaIndex 提供了一种高效的基于模糊匹配的方案。如果你不熟悉模糊匹配,这其实是一种非常强大的字符串匹配技术。

5 聊天引擎

构建一个能对同一查询多次响应的优秀 RAG(检索增强生成)系统的关键之一是 聊天逻辑,特别是要考虑到对话的上下文,这一点与 LLM(大语言模型)时代之前的传统聊天机器人类似。

这是为了支持对后续问题、代词指代或与先前对话上下文相关的用户指令的处理。这个问题通过 考虑聊天上下文的查询压缩技术 来解决,并结合了用户的查询。

处理这种上下文压缩有几种方法 —
一个受欢迎且相对简单的方法是使用 ContextChatEngine,它首先检索与用户查询相关的上下文,然后将其与来自 内存 缓冲区的聊天历史一起发送给大语言模型,让大语言模型在生成下一步答案时能够了解之前的上下文。

更为复杂的情况是 CondensePlusContextMode — 在这种模式下,每次互动时都将聊天历史和最新消息压缩成一个新的查询,然后这个查询被发送到索引中,并将检索到的上下文与原始用户消息一起传递给大语言模型,以生成答案。

值得一提的是,LlamaIndex 还支持基于 OpenAI 智能体的聊天引擎,提供了一种更灵活的聊天模式,而 Langchain 也 支持 OpenAI 的功能性 API。

不同聊天引擎类型和它们的工作原理示意图

不同聊天引擎类型和它们的工作原理示意图

还有像 ReAct 智能体 这样的其他聊天引擎类型,但我们接下来将直接跳转到第 7 节,讨论智能体本身。

6 查询路由

查询路由是指在接收到用户的查询后,由大语言模型决定接下来的操作步骤 - 常见的做法包括概述查询内容、对特定数据索引进行搜索,或尝试多个不同的处理方法,并将这些方法的结果合成一个答案。

查询路由器的另一个作用是选择数据存储位置来处理用户查询。这些数据存储位置可能是多样的,比如传统的向量存储、图形数据库或关系型数据库,或者是不同层级的索引系统。在处理多文档存储时,通常会用到摘要索引和文档块向量索引这两种不同的索引。

设置查询路由器包括决定它可以做出哪些选择。

选择特定路由的过程是通过大语言模型来实现的,其结果按照预定义的格式返回,以指导查询到达指定的索引。如果是涉及到关联操作,这些查询还可能被发送到子链或其他智能体,如下面的多文档智能体方案所展示的那样。

LlamaIndex 和 LangChain 都提供了对查询路由器的支持。

7 RAG 中的 AI 智能体

AI 智能体(得到了 Langchain 和 LlamaIndex 的支持)几乎从第一个大语言模型 API 发布之初就已经出现了——这个构想旨在赋予一个具备推理能力的大语言模型,配备各种工具和一个特定的任务。这些工具可能包括诸如代码功能、外部 API 或其他智能体等确定性功能——LangChain 正是由这种大语言模型链接的理念得名。

智能体本身非常复杂,而且不可能在一个 RAG 概览文章中对它进行深入探讨。因此,我将继续介绍基于智能体的多文档检索案例,并简要提及 OpenAI 助手,因为它是一个相对较新的概念,在最近的 OpenAI 开发者大会上作为 GPTs 呈现,并在下文将要介绍的 RAG 系统中发挥作用。

OpenAI 助手 基本上整合了我们以前在开源领域看到的许多围绕大语言模型的工具——如聊天历史、知识存储、文档上传界面,尤其是函数调用 API。这一功能可以将自然语言指令转换为对外部工具或数据库的 API 调用。

在 LlamaIndex 中,OpenAIAgent 类将这种先进的逻辑与 ChatEngine 和 QueryEngine 类结合在一起,实现了基于知识和上下文的智能聊天,以及在单次对话中调用多个 OpenAI 功能的能力,从而真正实现了智能的代理行为。

让我们来看一下多文档智能体的方案 —— 这是一个颇为复杂的配置,涉及到在每个文档上初始化一个智能体(OpenAIAgent),该智能体能进行文档摘要制作和传统问答机制的操作,还有一个顶层智能体,负责将查询分配到各个文档智能体,并综合形成最终的答案。

每个文档智能体配备了两种工具 —— 向量存储索引和摘要索引。它会根据被分配的查询来决定使用哪一个工具。 对顶层智能体而言,所有的文档智能体都是其工具。

这个方案展示了一种高级 RAG 架构,其中每个参与的智能体都需要做出众多的路由决策。这种方法的优势在于,它可以对描述于不同文档及其摘要中的不同解决方案或实体进行比较,同时还包括了传统的单文档摘要制作和问答机制 —— 这大致涵盖了与文档集合交互的常见用例。

图示展示了涉及查询路由和智能体行为模式的多文档智能体方案。

图示展示了涉及查询路由和智能体行为模式的多文档智能体方案。

这种复杂配置的缺点可以通过图片理解 —— 由于需要在智能体内部的大语言模型之间进行多次往返迭代,其运行速度较慢。顺便一提,LLM 调用通常是 RAG 管道中耗时最长的操作,而搜索则是出于设计考虑而优化了速度。因此,对于大型的多文档存储,我建议考虑对此方案进行简化,以便实现扩展。

8 响应合成阶段

这是任何 RAG 流程的最终步骤 —— 基于我们精心检索的所有上下文和用户的初始查询来生成答案。最简单的方式是将所有高相关性的检索上下文和查询一起直接输入到一个大语言模型 (大语言模型) 中。然而,还有其他更为复杂的方法,例如通过多次调用大语言模型来精细化检索的上下文,从而得出更优的答案。

**响应合成的主要方法包括:
**1. 通过逐块发送检索的上下文到大语言模型,以逐步优化答案2. 概括检索的上下文,使其适应提示条件 3. 根据不同的上下文块生成多个答案,然后将它们整合或概括。更多详情请参阅响应合成器模块文档。

编码器与大语言模型的微调

这个方法涉及对 RAG 流程中的两个深度学习模型之一进行微调 —— 或是负责嵌入质量和上下文检索质量的 Transformer 编码器,或是负责最有效利用提供上下文来回答用户查询的 大语言模型 —— 幸运的是,后者擅长于少样本学习。

如今,我们能够利用像 GPT-4 这样的高端大语言模型来创建高质量的合成数据集,这是一个巨大的优势。但是,我们也应当注意到,仅用小型合成数据集对专业研究团队开发的、基于大量精心收集、清洁和验证的数据集训练的开源模型进行快速调整,可能会降低模型的整体性能和能力。

编码器微调 (Encoder fine-tuning)

我曾对使用编码器微调来增强 Transformer 编码器的效率持有一些怀疑,毕竟最新的搜索优化 Transformer 编码器已经相当高效。然而,在 LlamaIndex 笔记本 中,我测试了对 bge-large-en-v1.5(在撰文时位于 MTEB 排行榜 前四)的微调效果。结果显示,这种微调使检索质量提升了 2%。虽然这个提升不是特别显著,但了解这种选择对于特定领域数据集的 RAG (Retrieval-Augmented Generation,检索增强生成) 构建来说是非常有价值的。

排序器微调 (Ranker fine-tuning)

另一个值得考虑的老方法是,在你不完全信任基础编码器的情况下,使用交叉编码器 (cross-encoder) 对检索到的结果进行重新排列。 这个过程是这样的:你把查询和每个前 k 个检索到的文本块一起送入交叉编码器,中间用 SEP (分隔符) Token 分隔,并对它进行微调,使其对相关的文本块输出 1,对不相关的输出 0。一个这种微调过程的优秀例子可以在这里找到,结果显示通过交叉编码器微调,成对比较得分提高了 4%。

大语言模型微调

OpenAI 最近推出了大语言模型微调的 API ,同时 LlamaIndex 也提供了一个教程,教你如何在 RAG(检索式问答生成)环境中对 GPT-3.5-turbo 进行微调,以便从 GPT-4 中获取知识。这个方法的核心思想是:先用 GPT-3.5-turbo 从一个文档中生成一系列问题,然后利用 GPT-4 根据文档内容来回答这些问题,通过这种方式构建基于 GPT-4 的 RAG 处理流程,接着再对这批问题和答案对进行微调。使用的 ragas 框架在 RAG 流程评估中表现出了显著的提升,微调后的 GPT 3.5-turbo 在生成答案时,相比原始模型更好地利用了所给的上下文,忠实度提高了 5%。

Meta AI 研究团队最近的一篇 论文 中介绍了一种更为复杂的方法,名为 RA-DIT(检索增强的双向指令调整),提出了一种同时对大语言模型和检索器(论文中称为双编码器)进行调整的技术。这种技术依据问题、上下文和答案的三元组来进行调整。更多实施细节请参考这个 指南。这种技术不仅被用于通过微调 API 对 OpenAI 的大语言模型进行微调,也被用于 Llama2 的开源模型,相比于搭载 RAG 的 Llama2 65B,它在知识密集型任务上的表现提升了约 5%,在常识推理任务上也有所提升。

如果您对 RAG 环境下的大语言模型微调有更好的方法,请在评论区分享您的见解,尤其是那些适用于小型开源大语言模型的方法。

评估

目前存在多种框架用于评估 RAG (Retrieval-Augmented Generation) 系统的性能,它们普遍采用了几项独立的评估指标,如答案的相关性、答案的基于性、真实性和检索到的内容的相关性。

在之前章节提及的 Ragas,使用真实性和答案相关性来评价生成答案的质量,并使用传统的上下文精准度和召回率来评估 RAG 方案的检索性能。

在安德鲁 NG 最近推出的精彩短期课程构建和评估高级 RAG中,以及其中提到的 LlamaIndex 和评估框架Truelens,他们提出了一个RAG 三元组评估模式 — 分别是对问题的检索内容相关性、答案的基于性(即大语言模型的答案在多大程度上得到了提供的上下文的支持)和答案对问题的相关性。

其中最关键且可控的指标是检索内容的相关性 — 实际上是上述高级 RAG 管道的前 1-7 部分加上编码器和排名器的微调部分,这些都是为了提高这个指标。而第 8 部分和大语言模型的微调则专注于提高答案的相关性和基于性。

一个简单有效的检索器评估管道的例子可以在这里找到,它已被应用于编码器的微调部分。一个更高级的方法不仅考虑命中率,还包括了常用的搜索引擎评估指标平均倒数排名 (Mean Reciprocal Rank),以及生成答案的质量指标,如真实性和相关性,这在 OpenAI 的实用指南中有所展示。

LangChain 提供了一个颇为先进的评估框架 LangSmith。在这个框架中,你不仅可以实现自定义的评估器,还能监控 RAG 管道内的运行轨迹,进而增强系统的透明度。

如果你正在使用 LlamaIndex 进行构建,可以尝试 rag_evaluator llama pack。这是一个便捷的工具,能够帮助你利用公共数据集来对你的管道进行评估。

结论

我试图勾勒出 RAG 的核心算法方法,并展示其中的一些,希望这能激发你在 RAG 流程中尝试一些新思路,或者为今年涌现的众多技术带来一定的系统性 — 对我而言,2023 年是迄今为止在机器学习领域最令人兴奋的一年。

还有许多其他因素需要考虑,例如 基于网络搜索的 RAG(如 LlamaIndex 的 RAGs、webLangChain 等),更深入地探索 智能体架构(以及最近 OpenAI 在这个领域的投资),以及一些关于 大语言模型长期记忆 的想法。

RAG 系统的主要生产挑战除了回答的相关性和忠实度外,还有速度,尤其是如果你喜欢更灵活的基于智能体的方案,但这是另一篇文章的内容。ChatGPT 和大多数其他助手使用的这种流式特性不是随机的赛博朋克风格,而只是一种缩短感知答案生成时间的方法。 这就是为什么我看好小型大语言模型和最近的 Mixtral 和 Phi-2 发布,它们正在引导我们走向这个方向。

非常感谢你阅读这篇长文!

主要参考资料收集在我的知识库中,有一个 co-pilot 可以与这组文档对话: https://app.iki.ai/playlist/236

您可以在 LinkedIn 或 Twitter 上找到我

相关推荐

RN 鸿蒙安全三方库落地:react-native-digital-shield,密钥不出 TEE,AES-GCM/RSA-JWT 跨端身份打通

本文基于 RNOH 0.84 与 HarmonyOS API 26,自研数字盾库 react-native-digital-shield,把 ArkTS 专属的 HUKS、asset 能力经TurboModule 桥接给 JS:提供 AES-256-GCM 加解密与密钥托管、敏感资产存储、RSA-2048 签发校验 RS256 JWT,并以 JWK 公钥和 HS256 共享密钥实现跨端身份互认,密钥明文始终不出 TEE。文中还复盘 TurboModule 双侧注册、GCM 需拆分 16 字节

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 16

Flutter 修复鸿蒙 WebView Cookie 同名键丢失:离线回读与开发助手复核实录

修复了 Flutter 三方库 webview_cookie_manager 鸿蒙端因同名键误判导致的 Cookie 名称丢失问题,确保业务键如 path、domain 等在回读时正确保留。修复后,模拟器与真机测试均实现四组数据完整恢复,值中多等号也保持不变。补丁已提交至 CPF-Flutter 官方仓库,提升插件在鸿蒙平台的可靠性。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 27

Flutter 鸿蒙启动图撤销后编译失败:用 DevEco Code 复核三方库修复

本文针对 Flutter 鸿蒙启动图撤销时导致编译失败的问题,修复了 flutter_native_splash_ohos 库在移除启动图时误删仍被引用资源的缺陷。通过保留1×1透明图片并透传自定义模块路径,确保资源引用完整性,使工程可正常构建。修复已提交至官方仓库,支持幂等操作与多场景验证,提升开发体验。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 40

Flutter 接入鸿蒙 ArkWeb 预初始化:帮助页首开时机与主线程成本实测

为解决Flutter鸿蒙版webview_flutter无法在无视图状态下提前初始化引擎的问题,我在其原生实现中新增可选静态入口initializeWebEngine()。实测表明,在2秒预热窗口下,页面首屏渲染(FCP)耗时从中位数1791ms降至1019ms,显著提升首次打开体验;而在零延迟场景下,总路径耗时略有上升,需权衡预热时机与用户等待成本。该补丁已提交至CPF-Flutter官方仓库,支持社区协作优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 36

Flutter 优化鸿蒙 printing_ohos 多页 PDF 光栅化:原生资源释放与缓冲复用

针对鸿蒙端 printing_ohos 插件存在的页码映射错误与高内存占用问题,本文提出并实现三项优化:修正页码索引映射、引入原生资源分层释放机制、复用像素缓冲区。修复后,第三页显示正确,内存增长显著降低——在模拟器上PSS增量中位数下降65.8%,真机测试下降64.4%。完整补丁已提交至官方仓库,支持社区协作与问题反馈。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 33

React Native 鸿蒙权限审计实战:让每一项权限都有人认领

本文介绍一款纯 JS 编写的 React Native 鸿蒙权限审计工具,解决权限声明与使用对账难、最小权限集计算矛盾、审核必填字段缺失三大痛点。通过 module.json5 声明、使用台账与源码扫描三路输入对账,自动识别未使用权限、补全 reason/usedScene、生成可粘贴的最小清单,并支持 CI 卡控与风险分评估。工具内核独立,离线运行,兼顾准确性与安全性,助力应用高效通过审核。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 12

Cordova 鸿蒙 WebView 安全实战:能改的运行时拦,改不了的构建期审

本文通过 cordova-plugin-harmony-webguard 插件,解决 Cordova/HarmonyOS 应用中导航安全、引擎不可修改配置、JS 门面静默降级三大难题。基于运行时策略闸门(onPageStart + JS 预检)与构建期审计 CLI(扫描 MainPage.ets、config.xml 等),实现协议拦截、白名单校验、高危配置阻断,支持 CI 失败退出,将安全基线可视化、可测化,真正落地“能改的运行时拦,改不了的构建期审”。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 25

Flutter 鸿蒙供应链审计实战:让高危依赖卡在 CI 门口

本文介绍了一个基于纯 Dart CLI 与 Flutter Dashboard 的依赖审计工具,解决鸿蒙工程中三套锁文件(pubspec.lock、oh-package-lock.json5、package-lock.json)统一读取、离线快照与 OSV 在线情报选择、以及高危依赖通过退出码卡在 CI 的问题。工具以锁文件为事实依据,支持离线快照(4 条真实 GHSA)与在线查询,输出 SBOM(CycloneDX 1.5)、Markdown/JSON 报告,并通过退出码实现 CI 阻断,性能秒级

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 139

React Native 鸿蒙端侧 OCR 实战:TurboModule 与纯 TS 引擎的逐字契约

本文以鸿蒙真机上的 React Native 0.84 工程为例,详解三大难题:通过 TurboModule 接入 HarmonyOS Core Vision Kit 实现端侧离线 OCR;规避 Metro 预置头导致的 require 不存在问题;实现 npm 包与 HAR 双形态交付。项目采用 codegen 生成 ArkTS 接口,结合纯 TS 回退引擎,确保跨引擎逐字符一致,并通过 autolinking 支持无缝集成。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 90

Flutter 鸿蒙端侧 OCR 实战:让文字识别留在设备上

本文通过一个运行在鸿蒙真机上的 Flutter 插件工程,系统解决了三大核心问题:如何统一内存、文件、相册三类输入的识别链路;如何实现与 KMP 版本的 JSON 契约逐字节对齐;以及在系统不提供置信度时如何诚实处理。基于 HarmonyOS Core Vision Kit,插件实现端侧离线 OCR,全程无网络、无权限、无密钥,支持真实小票与截图识别,性能稳定(312~507ms),并通过 35 项单元测试与真机集成验证。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 148

跨端 RN 鸿蒙碰一碰分享库实战:基于 Share Kit 封装 react-native-ohos-knock-share,实现卡片、文件一碰传

本文介绍基于React Native的鸿蒙碰一碰分享库,解决RN开发者在鸿蒙生态中无法便捷使用Share Kit碰一碰功能的痛点。通过封装NFC/星闪触碰、3秒发送窗口、沙箱文件URI转换等核心逻辑,实现跨设备快速传递链接、文件、图片等内容。支持手机、平板、PC多端适配,提供回环演示与真机实测验证,显著降低接入门槛,提升跨端协作效率。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 188

React Native 鸿蒙实战:给应用接入小艺语音指令,跑通 Skill 声明到操作闭环

本文介绍如何在 React Native 鸿蒙(RNOH)工程中实现小艺技能闭环,通过开源库 react-native-ohos-ai-agent 封装技能声明、语音指令接收、端侧执行与结果回传全流程。针对三方应用权限限制,精准定位可操作边界,实现“被唤起→执行→反馈”链路贯通。结合五张真机截图验证:技能注册、指令匹配、意图上报、智能体调起及对话层弹出等关键节点均成功,完整打通语音交互闭环,助力应用无需入口即可被用户“叫”到。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 169

Flutter 鸿蒙实现精准碰一碰分享:tap_share_ohos 三方库打通真机传文件与传卡片

本文针对Flutter在鸿蒙系统上“碰一碰”分享功能缺失的痛点,基于DevEco Studio 26.0、HarmonyOS 7.0 API 26与Flutter 3.41.9实测环境,提出并实现开源库tap_share_ohos。该库封装NFC、Share Kit、权限管理及分布式能力,通过Dart门面层统一接口,利用MethodChannel与BasicMessageChannel对接原生ArkTS逻辑,实现文件与卡片的碰一碰分享。真机测试显示,可无缝调起系统分享面板,支持华为分享接力

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 169

Flutter 鸿蒙实战:用三方库接入小艺语音指令与智能体,跑通 Skill 声明到操作闭环

本文基于 flutter_ohos_ai_agent 三方库,实现 Flutter 应用一键调起鸿蒙小艺智能体。针对跨端框架对接鸿蒙 AI 能力的碎片化、环境差异等痛点,通过插件封装系统 Kit 能力,提供统一 Dart API。实测显示,模拟器与真机均支持文本/语音指令闭环,真机可完整调起小艺面板并回传事件。核心原理为:利用 ArkUI 承载页桥接 FunctionComponent,通过 MethodChannel 与 EventChannel 实现 Flutter 与原生能力双向通信,完成从语音识别到

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 151

RNOH 原生内存泄漏定位与治理:借助 LeakyGuard 实现图片解码降采样、LRU 字节缓存、资源生命周期管控,长列表图片页面峰值内存优化

本文面向 RNOH 0.84 长列表图片场景,剖析滑动数分钟后进程被系统杀掉的根因:原图解码尺寸失控、缓存按条数限流、PixelMap未显式 release。基于自研LeakyGuar,TurboModule 库,给出 desiredSize 真降采样、字节预算 LRU 缓存(逐出必 release)、资源登记与快照 diff、卸载即释放 Hook 的闭环方案,实测单图内存降 98.6%,24 图峰值由 1098.6MB 降至15.8MB。并总结双侧注册、ArkTS 语法差异、vp 转像

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 245

Flutter 鸿蒙空间音频实战:让音乐播放器的声音“长出方位感“

本文通过一个真实运行的 Flutter 工程,系统解析了在鸿蒙系统上实现空间音频的三大核心问题:基于双耳时间差(ITD)、声级差(ILD)和频谱整形的方位感算法;耳机切换与音频焦点管理;以及播放器与音效的集成。项目使用自研插件 spatial_audio,结合原生 HarmonyOS 接口,实现了跨设备的空间音频体验,支持普通耳机,无需硬件依赖,并提供实时参数反馈与系统状态监听。附带完整代码仓库与实测效果展示,为 Flutter on HarmonyOS 的音频开发提供了可复用的技术方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 289

Flutter 鸿蒙 HiTrace 请求归因:图片压缩等待时,主线程在做什么

为定位鸿蒙平台图片压缩性能瓶颈,我在 flutter_native_image 插件中新增可选的 HiTrace 阶段追踪功能,支持按请求开启解码、缩放、编码与资源释放阶段的耗时分析。通过对比模拟器与真机(华为畅享 100 Pro Max)数据,精准识别出耗时主要集中在解码与编码阶段,显著提升诊断效率。相关代码已提交至官方仓库,助力开发者快速定位性能问题。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 适配鸿蒙折叠屏铰链角度按需订阅:支架调节页用完即停与重入实测

本文介绍对 Flutter 三方库 dual_screen 的优化:针对支架调节页等短时观察场景,保留原有缓存入口的同时,新增按需订阅机制。通过按需启停监听,实现页面退出后停止原生回调,避免无效数据更新。实测显示,无消费者时原生回调从3条降至0条,重进页面则等待新事件,更符合临时工具页的使用逻辑。完整代码已开源,支持鸿蒙折叠屏角度动态感知,适用于需精准控制生命周期的折叠屏应用开发。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 适配鸿蒙手表屏幕形状:让计时卡片按圆屏和方屏布局

本文基于 wear_plus 1.2.4 版本,为鸿蒙系统补全屏幕形状查询功能,使 WatchShape 能正确返回 round 等形状信息。通过在原 Dart 接口不变的前提下,新增鸿蒙平台处理器,实现与 Android 一致的异步查询逻辑。对照实验验证:适配后页面可从“查询未接入”变为“圆屏已确认”,日志成功接收系统回执,布局行为保持一致。完整代码开源至 oh-flutter/wear_plus,支持 Flutter 鸿蒙穿戴设备开发。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 优化鸿蒙剪贴板大图复制卡顿:从主线程同步阻塞到异步文件描述符直解

针对鸿蒙系统下 flutter_image_clipboard 插件复制高清图片时阻塞主线程的问题,本文提出三项优化:异步文件描述符直解、异步剪贴板写入及原生资源逆序释放。实测显示,模拟器事件循环最大间隔从300.32ms降至54.36ms(降幅81.9%),真机上更从133.20ms降至11.20ms(降幅91.59%),显著提升页面响应性。完整代码已开源,支持直接使用与社区交流。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 三方库 petalmaps_ohos 的 OpenHarmony 适配实战(0 → 1 全记录)

本文详细记录了从零构建 Flutter 三方库 petalmaps_ohos 并适配 OpenHarmony/HarmonyOS 平台的全过程,涵盖项目初始化、原生实现(基于 ArkTS 和 MethodCallHandler)、插件注册、功能验证及升级至 HarmonyOS SDK 26 的关键步骤。重点解析了为何需实现 AbilityAware 获取 UIAbilityContext 以确保地图调起成功,并通过分层架构、枚举封装与兼容性设计,实现功能完整、易用且低维护成本的“调起系”地图插件,

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 59

Flutter 三方库 holding 的 OpenHarmony 适配实战:从 0 到 1 开发一个鸿蒙感知插件

本文详述了从零实现开源 Flutter 插件 holding 并适配 OpenHarmony/HarmonyOS 的全过程。基于鸿蒙专属 MultimodalAwarenessKit,实现握持手与操作手状态感知,覆盖事件流订阅、状态查询及 API 26 新增接口,确保功能一致、性能高效、接口稳定。通过 Dart 层封装、ArkTS 原生桥接、真机验证与单元测试,完成插件工程化落地,并发布至 pub.dev,为同类系统能力插件的鸿蒙化提供可复用的技术路径。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28

Flutter 三方库 gesturesshare 从 0 到 1:鸿蒙隔空传送分享插件的开发、升级与发布实战

本文详述了 Flutter for OpenHarmony 三方库 gesturesshare 从零开发到发布全过程:基于 HarmonyOS 隔空传送能力,实现跨设备图片与 App Linking 一键分享。涵盖双端代码设计、API 26 升级、真机验证及 Pub 发布流程,提供 Dart/ArkTS 对照与踩坑复盘。项目填补 Flutter 生态空白,推动鸿蒙多设备交互能力普及,助力开发者快速接入系统级功能。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28

Qt 鸿蒙沉浸光感实战:让玻璃的每个像素自己发光

本文通过一个 QML 模块与 Qt for OpenHarmony 构建实践,完整解答了三个核心问题:如何用 81 行 GLSL 实现无图片依赖的动态玻璃材质(含光核漂移、视差、SDF 圆角与颗粒);如何在 Qt Quick 中通过 C++ 控制器实现弹层锚定与翻转;以及如何将 Qt 应用交叉编译并以 HAR 形式部署至鸿蒙系统。项目基于 Qt 5.12.12,利用 ShaderEffect 实现纯渲染级视觉效果,兼顾性能与跨平台兼容性,提供可复用的开源方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 23

开源鸿蒙平台 KMP_CMP 三方库「端侧语音转文字」适配全流程

本项目实现KMP/CMP在OpenHarmony鸿蒙平台的语音转文字(TTS)完整链路适配,通过Kotlin/Native共享模型、C ABI/N-API桥接与ArkTS交互,解决权限、音频格式、生命周期及跨平台契约等难题。支持中文、英文、日文多语言识别,提供统一结果模型与JSON序列化,实测于华为Mate 80 Pro Max模拟器,验证了JVM测试、Native动态库、HAP签名部署及真机识别全流程。项目结构清晰,分六阶段推进,确保代码复用与平台无关性,为KMP在鸿蒙生态落地提供可复用的技术范式。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 30

开源鸿蒙平台 KMP_CMP 三方库「端侧OCR」新特性适配

本项目实现Kotlin Multiplatform(KMP)在OpenHarmony平台的端侧OCR适配,基于HarmonyOS 7.0 SDK与Compose Multiplatform 1.9.2,成功打通JVM、Kotlin/Native与ArkTS间的数据交互。通过C ABI/N-API桥接层,实现KMP模型与Core Vision Kit识别结果的跨语言共享,支持中、英、日、韩四语识别。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 13

开源鸿蒙平台 KMP/CMP 三方库「智感握姿」新特性

本项目实现鸿蒙KMP/CMP平台下智感握姿能力的跨端适配,基于Kotlin Multiplatform构建共享状态模型,通过C ABI与ArkTS桥接,支持左手、右手、双手等五种握持状态的稳定传递。项目打通ohosArm64目标、Native动态库构建与DevEco Studio集成链路,提供统一API与真机验证示例,确保状态模型、事件序列与生命周期在多端一致。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 19

Electron 鸿蒙沉浸光感实战:把玻璃交给操作系统去发光

本文通过一个 Electron 库与 HarmonyOS 三方库,系统解决三大核心问题:跨平台材质路径对齐(macOS vibrancy、Windows acrylic、鸿蒙 backgroundBlurStyle)、菜单子窗口精准定位(坐标转换与工作区适配)、以及无脚本菜单文档的安全事件回传(自定义 URL scheme 与焦点管理)。基于 CPF-Electron 鸿蒙化分支,实现桌面级玻璃效果——材质由操作系统合成器采样,网页仅渲染薄色层与光斑,确保视觉一致性与性能优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 21

Flutter 修复 flutter_secure_storage 鸿蒙入口路由与前缀隔离:让多账号登录凭证在手机上独立读写与清理

本文针对 flutter_secure_storage 在鸿蒙平台的适配问题,修复了主包缺失 TargetPlatform.ohos 路由导致的 unsupported_platform 错误,并统一鸿蒙子包多账号前缀的读写与清理范围,解决键值覆盖、前缀错用及批量操作匹配过宽等问题。修复后,业务层可继续使用统一接口实现跨账号安全存储,保障数据隔离性。完整代码已开源至 abaoge/flutter_secure_storage。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Cordova 鸿蒙沉浸光感实战:让弹层玻璃“喝到“背景的光

本文通过一个 HarmonyOS 模拟器上的 Cordova 项目,详解了在 WebView 中实现“玻璃拟态”效果的三大核心问题:利用 backdrop-filter 实现背景实时模糊与提亮,构建四层结构(采样、渐变底、光场、噪点)模拟真实玻璃质感;通过锚点定位与动态布局实现弹层贴边生长;并集成焦点管理、系统返回键及 inert 属性,确保交互礼仪完整。插件为纯 JS 无原生依赖,基于 ArkWeb 内核,兼顾性能与真实背景采样,适用于菜单、浮层等场景。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 28
上一篇: VSCode 核心开发吕鹏:对不久的未来的一些展望
下一篇: 构建高级RAG的指南和技巧
AtomGit 代码君
AtomGit 代码君 企业官方账号 企业官方账号
博客等级 码龄6年 2万+粉丝 856原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值