大模型高级 RAG 检索策略之流程与模块化

news2024/11/27 1:45:40

我们介绍了很多关于高级 RAG(Retrieval Augmented Generation)的检索策略,每一种策略就像是机器中的零部件,我们可以通过对这些零部件进行不同的组合,来实现不同的 RAG 功能,从而满足不同的需求。

今天我们就来介绍高级 RAG 检索中一些常见的 RAG 模块,以及如何通过流程的方式来组合这些模块,实现高级 RAG 检索功能。

RAG 模块化

模块化 RAG 提出了一种高度可扩展的范例,将 RAG 系统分为模块类型、模块和操作符的三层结构。每个模块类型代表 RAG 系统中的一个核心流程,包含多个功能模块。每个功能模块又包含多个特定的操作符。整个 RAG 系统变成了多个模块和相应操作符的排列组合,形成了我们所说的 RAG 流程。在流程中,每种模块类型可以选择不同的功能模块,并且在每个功能模块中可以选择一个或多个操作符。

图片

RAG 流程

RAG 流程是指在 RAG 系统中,从输入查询到输出生成文本的整个工作流程。这个流程通常涉及多个模块和操作符的协同工作,包括但不限于检索器、生成器以及可能的预处理和后处理模块。RAG 流程的设计旨在使得 LLM(大语言模型)能够在生成文本时利用外部知识库或文档集,从而提高回答的准确性和相关性。

RAG 推理阶段的流程一般分为以下几种模式:

  • Sequential: 线性流程,包括高级和简单的 RAG 范式

  • Conditional: 基于查询的关键词或语义选择不同的 RAG 路径

  • Branching: 包括多个并行分支,分为预检索和后检索的分支结构

  • Loop: 包括迭代、递归和自适应检索等多种循环结构

下图是 Loop 模式的 RAG 流程图:

图片

后面我们主要以 Sequential 模式为例,介绍如何通过模块化和流水线的方式来实现高级 RAG 检索功能。

代码示例

LlamaIndex[1]的查询流水线(Query Pipeline)功能提供了一种模块化的方式来组合 RAG 检索策略。我们可以通过定义不同的模块,然后将这些模块按照一定的顺序组合起来,形成一个完整的查询流水线。下面我们通过一个从简单到复杂的示例来演示如何使用 LlamaIndex 的查询流水线功能实现高级 RAG 检索。

普通 RAG

首先我们定义一个普通 RAG 的流水线,这个流水线包含了 3 个模块,分别是:输入、检索和输出。其中输入模块用于接收用户输入的查询,检索模块用于从知识库中检索相关文档,输出模块用于根据检索结果生成回答。

图片

在定义查询流水线之前,我们先将我们的测试文档索引入库,这里的测试文档还是用维基百科上的复仇者联盟[2]电影剧情,示例代码如下:

import os
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import (
    Settings,
    SimpleDirectoryReader,
    StorageContext,
    VectorStoreIndex,
    load_index_from_storage,
)
from llama_index.core.node_parser import SentenceSplitter

documents = SimpleDirectoryReader("./data").load_data()
node_parser = SentenceSplitter()
llm = OpenAI(model="gpt-3.5-turbo")
embed_model = OpenAIEmbedding(model="text-embedding-3-small")
Settings.llm = llm
Settings.embed_model = embed_model
Settings.node_parser = node_parser

if not os.path.exists("storage"):
    index = VectorStoreIndex.from_documents(documents)
    index.set_index_id("avengers")
    index.storage_context.persist("./storage")
else:
    store_context = StorageContext.from_defaults(persist_dir="./storage")
    index = load_index_from_storage(
        storage_context=store_context, index_id="avengers"
    )
  • 首先我们通过SimpleDirectoryReader读取./data目录下的文档

  • 然后我们定义了一个SentenceSplitter用于将文档进行分割

  • 接着我们使用OpenAI的 LLM 和 Embedding 模型来生成文本和向量,并将他们添加到Settings

  • 最后我们将文档索引入库,并将索引保存到./storage目录下,以便后续使用

接下来我们定义一个普通的 RAG 流水线,示例代码如下:

from llama_index.core.query_pipeline import QueryPipeline, InputComponent
from llama_index.core.response_synthesizers.simple_summarize import SimpleSummarize

retriever =  index.as_retriever()
p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
        "retriever": retriever,
        "output": SimpleSummarize(),
    }
)

p.add_link("input", "retriever")
p.add_link("input", "output", dest_key="query_str")
p.add_link("retriever", "output", dest_key="nodes")
  • 我们创建了一个普通检索器retriever,用于从知识库中检索相关文档

  • 然后创建了一个QueryPipeline对象,这是查询流水线的主体,设置 verbose 参数为 True 用于输出详细信息

  • 通过QueryPipelineadd_modules方法添加了 3 个模块:input、retriever和output

  • input模块的实现类是InputComponent,这是查询流水线常用的输入组件,retriever模块是我们定义的检索器,output模块的实现类是SimpleSummarize,这是可以将问题和检索结果进行简单总结的输出组件

  • 接着我们添加模块间的连接关系,add_link方法用于连接模块之间的关系,第一个参数是源模块,第二个参数是目标模块

  • dest_key参数用于指定目标模块的输入参数,因为输出模块有 2 个参数,分别是问题和检索结果,所以我们需要指定dest_key参数,当目标模块只有一个参数时则不需要指定

  • add_link方法中,与dest_key 参数对应的是src_key 参数,当源模块有多个参数时,我们需要指定src_key参数,反之则不需要。

查询流水线添加模块和连接关系的方式除了add_modulesadd_link方法外,还可以通过add_chain方法添加,示例代码如下:

p = QueryPipeline(verbose=True)
p.add_chain([InputComponent(), retriever])

这种方式可以一次性添加模块与连接关系,但这种方式只能添加单参数的模块,如果模块有多个参数则需要使用add_modulesadd_link方法。

接下来我们再来运行查询流水线,示例代码如下:

question = "Which two members of the Avengers created Ultron?"
output = p.run(input=question)
print(str(output))

# 结果显示
> Running module input with input:
input: Which two members of the Avengers created Ultron?

> Running module retriever with input:
input: Which two members of the Avengers created Ultron?

> Running module output with input:
query_str: Which two members of the Avengers created Ultron?
nodes: [NodeWithScore(node=TextNode(id_='53d32f3a-a2d5-47b1-aa8f-a9679e83e0b0', embedding=None, metadata={'file_path': '/data/Avengers:Age-of-Ul...

Bruce Banner and Tony Stark.
  • 使用查询流水线的run方法运行查询流水线,传入问题作为输入参数

  • 在显示结果中可以看到查询流水线的调试信息,查询流水线首先运行了input模块,然后运行了retriever模块,最后运行了output模块,调试信息还打印了每个模块的输入参数,最后输出了问题的答案

增加 reranker 模块

接下来我们在普通 RAG 的基础上增加一个 reranker 模块,用于对检索结果进行重新排序。

图片

+from llama_index.postprocessor.cohere_rerank import CohereRerank

+reranker = CohereRerank()
p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
        "retriever": retriever,
+        "reranker": reranker,
        "output": SimpleSummarize(),
    }
)

p.add_link("input", "retriever")
+p.add_link("input", "reranker", dest_key="query_str")
+p.add_link("retriever", "reranker", dest_key="nodes")
p.add_link("input", "output", dest_key="query_str")
-p.add_link("retriever", "output", dest_key="nodes")
+p.add_link("reranker", "output", dest_key="nodes")
  • 这里我们使用了Cohere[3]公司的 rerank 功能,在 LlamaIndex 中提供了CohereRerank类用于实现 Cohere 的 rerank 功能

  • 要使用CohererRerank类,需要先在 Cohere 官网上注册账号并获取 API KEY,并在环境变量中设置COHERE_API_KEY的值:export COHERE_API_KEY=your-cohere-api-key

  • 然后我们在查询流水线中添加一个reranker模块,并将其添加到retriever模块和output模块之间,用于对检索结果进行重新排序

  • 我们去除原来从retriever模块到output模块的连接关系,增加了retriever模块到reranker模块和reranker模块到output模块的连接关系

  • reranker模块同样需要 2 个参数,分别是问题和检索结果,这样reranker模块才可以根据问题对检索结果进行重新排序,所以我们需要指定dest_key参数

查询流水线的运行方法除了run方法外,还有run_with_intermeation方法,这个方法可以获取流水线的中间结果,我们将retrieverrerank模块的中间结果打印出来进行对比,示例代码如下:

output, intermediates = p.run_with_intermediates(input=question)
retriever_output = intermediates["retriever"].outputs["output"]
print(f"retriever output:")
for node in retriever_output:
    print(f"node id: {node.node_id}, node score: {node.score}")
reranker_output = intermediates["reranker"].outputs["nodes"]
print(f"\nreranker output:")
for node in reranker_output:
      print(f"node id: {node.node_id}, node score: {node.score}")

# 显示结果
retriever output:
node id: 53d32f3a-a2d5-47b1-aa8f-a9679e83e0b0, node score: 0.6608391314791646
node id: dea3844b-789f-46de-a415-df1ef14dda18, node score: 0.5313643379538727

reranker output:
node id: 53d32f3a-a2d5-47b1-aa8f-a9679e83e0b0, node score: 0.9588471
node id: dea3844b-789f-46de-a415-df1ef14dda18, node score: 0.5837967
  • 执行run_with_intermediates方法后返回结果是一个元组,包含了输出结果和中间结果

  • 要获取某个模块的中间结果,可以通过intermediates变量加上模块 key 进行获取,比如intermediates["retriever"]是获取检索模块的中间结果

  • 每个中间结果都有 2 个参数,分别是inputsoutputsinputs表示模块的输入参数,outputs表示模块的输出参数

  • inputsoutputs参数类型是字典,比如reranker模块的outputs参数中包含了nodes属性,我们可以这样来获取nodes属性的值:intermediates["reranker"].outputs["nodes"]

增加 query rewrite 模块

之前我们在查询流水线中加入了 reranker 模块,相当是对检索结果的后处理操作,现在我们再加入一个 query rewrite 模块,用于对查询问题进行预处理操作。

图片

+query_rewriter = HydeComponent()
p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
+        "query_rewriter": query_rewriter,
        "retriever": retriever,
        "reranker": reranker,
        "output": SimpleSummarize(),
    }
)

-p.add_link("input", "retriever")
+p.add_link("input", "query_rewriter")
+p.add_link("query_rewriter", "retriever")
p.add_link("input", "reranker", dest_key="query_str")
p.add_link("retriever", "reranker", dest_key="nodes")
p.add_link("input", "output", dest_key="query_str")
p.add_link("reranker", "output", dest_key="nodes")

  • 这里我们定义了一个HydeComponent类用于实现查询重写的功能,使用的是 HyDE(假设性文档向量)查询重写策略,它会根据查询问题生成一个假设性回答,然后使用这个假设性回答去检索文档,从而提高检索的准确性

  • HydeComponent是一个自定义的查询流水线组件,后面我们再详细介绍它的实现

  • 我们在原有的查询流水线上增加了一个query_rewriter模块,放在input模块和retriever模块之间,用于对查询问题进行预处理

  • 我们去除原来从input模块到retriever模块的连接关系,增加了input模块到query_rewriter模块和query_rewriter模块到retriever模块的连接关系

  • query_rewriter模块只有一个参数,所以不需要指定dest_key参数

LlamaIndex 的查询流水线提供了自定义组件的功能,我们可以通过继承CustomQueryComponent类来实现自定义组件,下面我们来实现HydeComponent类,示例代码如下:

from llama_index.core.query_pipeline import CustomQueryComponent
from typing import Dict, Any
from llama_index.core.indices.query.query_transform import HyDEQueryTransform

class HydeComponent(CustomQueryComponent):
    """HyDE query rewrite component."""

    def _validate_component_inputs(self, input: Dict[str, Any]) -> Dict[str, Any]:
        """Validate component inputs during run_component."""
        assert "input" in input, "input is required"
        return input

    @property
    def _input_keys(self) -> set:
        """Input keys dict."""
        return {"input"}

    @property
    def _output_keys(self) -> set:
        return {"output"}

    def _run_component(self, **kwargs) -> Dict[str, Any]:
        """Run the component."""
        hyde = HyDEQueryTransform(include_original=True)
        query_bundle = hyde(kwargs["input"])
        return {"output": query_bundle.embedding_strs[0]}

  • HydeComponent类中的_validate_component_inputs方法用于验证组件的输入参数,必须实现这个方法,否则会抛出异常

  • _input_keys_output_keys属性分别用于定义组件的输入和输出 key 值

  • _run_component方法用于实现组件的具体功能,这里我们使用HyDEQueryTransform类实现了 HyDE 查询重写功能,将查询问题转换为假设性回答,并返回这个假设性回答

替换 output 模块

在之前的查询流水线中,我们使用的是简单的总结输出组件,现在我们将其替换为树形总结组件,用来提高最终的输出结果。

树形总结组件以自底向上的方式递归地合并文本块并对其进行总结(即从叶子到根构建一棵树)。 具体地说,在每个递归步骤中:

  1. 我们重新打包文本块,使得每个块填充大语言模型的上下文窗口

  2. 如果只有一个块,我们给出最终响应

  3. 否则,我们总结每个块,并递归地总结这些摘要

图片

from llama_index.core.response_synthesizers.tree_summarize import TreeSummarize

p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
        "query_rewriter": query_rewriter,
        "retriever": retriever,
        "reranker": reranker,
-        "output": SimpleSummarize(),
+        "output": TreeSummarize(),
    }
)
  • 替换output模块的组件比较简单,只需要将原来的SimpleSummarize替换为TreeSummarize即可

  • TreeSummarize组件的结构和SimpleSummarize组件类似,因此这里我们不需要修改其他模块的连接关系

查询流水线实际上是一个 DAG(有向无环图),每个模块是图中的一个节点,模块之间的连接关系是图中的边,我们可以通过代码来展示这个图形结构,示例代码如下:

from pyvis.network import Network

net = Network(notebook=True, cdn_resources="in_line", directed=True)
net.from_nx(p.clean_dag)
net.write_html("output/pipeline_dag.html")
  • 我们使用pyvis库来绘制查询流水线的图形结构

  • Network类用于创建一个网络对象,notebook=True表示在 Jupyter Notebook 中显示,cdn_resources="in_line"表示使用内联资源,directed=True表示有向图

  • from_nx方法用于将查询流水线的 DAG 结构转换为网络对象

  • write_html方法用于将网络对象保存为 HTML 文件,这样我们就可以在浏览器中查看查询流水线的图形结构

保存后的查询流水线图形结构如下:

图片

使用句子窗口检索

在之前的查询流水线中,retriever模块使用的是普通的检索策略,现在我们将其替换为句子窗口检索策略,用于提高检索的准确性。

句子窗口检索的原理:首先在文档切分时,将文档以句子为单位进行切分,同时进行 Embedding 并保存数据库。然后在检索时,通过问题检索到相关的句子,但并不只是将检索到的句子作为检索结果,而是将该句子前面和后面的句子一起作为检索结果,包含的句子数量可以通过参数来进行设置,最后将检索结果再一起提交给 LLM 来生成答案。

图片

+from llama_index.core.node_parser import SentenceWindowNodeParser

-node_parser = SentenceSplitter()
+node_parser = SentenceWindowNodeParser.from_defaults(
+    window_size=3,
+    window_metadata_key="window",
+    original_text_metadata_key="original_text",
+)

+meta_replacer = MetadataReplacementPostProcessor(target_metadata_key="window")
p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
        "query_rewriter": query_rewriter,
        "retriever": retriever,
+        "meta_replacer": meta_replacer,
        "reranker": reranker,
        "output": TreeSummarize(),
    }
)
p.add_link("input", "query_rewriter")
p.add_link("query_rewriter", "retriever")
+p.add_link("retriever", "meta_replacer")
p.add_link("input", "reranker", dest_key="query_str")
-p.add_link("retriever", "reranker", dest_key="nodes")
+p.add_link("meta_replacer", "reranker", dest_key="nodes")
p.add_link("input", "output", dest_key="query_str")
p.add_link("reranker", "output", dest_key="nodes")

  • 句子窗口检索首先需要调整文档的入库策略,以前是用SentenceSplitter来切分文档,现在我们使用SentenceWindowNodeParser来切分文档,窗口大小为 3,原始文本的 key 为original_text,窗口文本的 key 为window

  • 句子窗口检索的原理是在检索出结果后,将检索到的节点文本替换成窗口文本,所以这里需要增加一个meta_replacer模块,用来替换检索结果中的节点文本

  • meta_replacer模块的实现类是MetadataReplacementPostProcessor,输入参数是检索结果nodes,输出结果是替换了节点文本的检索结果nodes

  • 我们将meta_replacer模块放在retriever模块和reranker模块之间,先对检索结果进行元数据替换处理,然后再进行 rerank 操作,因此这里修改了这 3 个模块的连接关系

我们可以打印出retriever模块和meta_replacer模块的中间结果,来对比检索结果的变化,示例代码如下:

output, intermediates = p.run_with_intermediates(input=question)
retriever_output = intermediates["retriever"].outputs["output"]
print(f"retriever output:")
for node in retriever_output:
    print(f"node: {node.text}\n")
meta_replacer_output = intermediates["meta_replacer"].outputs["nodes"]
print(f"meta_replacer output:")
for node in meta_replacer_output:
    print(f"node: {node.text}\n")

# 显示结果
retriever output:
node: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Rogers, Natasha Romanoff, and Clint Barton—raid a Hydra facility commanded by Baron Wolfgang von Strucker, who has experimented on humans using the scepter previously wielded by Loki.

node: They meet two of Strucker's test subjects—twins Pietro (who has superhuman speed) and Wanda Maximoff (who has telepathic and telekinetic abilities)—and apprehend Strucker, while Stark retrieves Loki's scepter.

meta_replacer output:
node: and attacks the Avengers at their headquarters.  Escaping with the scepter, Ultron uses the resources in Strucker's Sokovia base to upgrade his rudimentary body and build an army of robot drones.  Having killed Strucker, he recruits the Maximoffs, who hold Stark responsible for their parents' deaths by his company's weapons, and goes to the base of arms dealer Ulysses Klaue in Johannesburg to get vibranium.  The Avengers attack Ultron and the Maximoffs, but Wanda subdues them with haunting visions, causing Banner to turn into the Hulk and rampage until Stark stops him with his anti-Hulk armor. [a]
A worldwide backlash over the resulting destruction, and the fears Wanda's hallucinations incited, send the team into hiding at Barton's farmhouse.  Thor departs to consult with Dr.  Erik Selvig on the apocalyptic future he saw in his hallucination, while Nick Fury arrives and encourages the team to form a plan to stop Ultron.

node: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Rogers, Natasha Romanoff, and Clint Barton—raid a Hydra facility commanded by Baron Wolfgang von Strucker, who has experimented on humans using the scepter previously wielded by Loki.  They meet two of Strucker's test subjects—twins Pietro (who has superhuman speed) and Wanda Maximoff (who has telepathic and telekinetic abilities)—and apprehend Strucker, while Stark retrieves Loki's scepter.
Stark and Banner discover an artificial intelligence within the scepter's gem, and secretly decide to use it to complete Stark's "Ultron" global defense program.  The unexpectedly sentient Ultron, believing he must eradicate humanity to save Earth, eliminates Stark's A.I.

从结果中我们可以看出,原来的retreiver模块输出的只是简单的一句话,而meta_replacer模块输出的是多个句子,包含了检索节点的前后节点的文本,这样可以让 LLM 生成更准确的答案。

增加评估模块

最后我们再为查询流水线增加一个评估模块,用于评估查询流水线,这里我们使用Ragas[6]来实现评估模块。

Ragas 是一个评估 RAG 应用的框架,拥有很多且详细的评估指标。

图片

+evaluator = RagasComponent()
p = QueryPipeline(verbose=True)
p.add_modules(
    {
        "input": InputComponent(),
        "query_rewriter": query_rewriter,
        "retriever": retriever,
        "meta_replacer": meta_replacer,
        "reranker": reranker,
        "output": TreeSummarize(),
+        "evaluator": evaluator,
    }
)
-p.add_link("input", "query_rewriter")
+p.add_link("input", "query_rewriter", src_key="input")
p.add_link("query_rewriter", "retriever")
p.add_link("retriever", "meta_replacer")
-p.add_link("input", "reranker", dest_key="query_str")
+p.add_link("input", "reranker", src_key="input", dest_key="query_str")
p.add_link("meta_replacer", "reranker", dest_key="nodes")
-p.add_link("input", "output", dest_key="query_str")
+p.add_link("input", "output", src_key="input", dest_key="query_str")
p.add_link("reranker", "output", dest_key="nodes")
+p.add_link("input", "evaluator", src_key="input", dest_key="question")
+p.add_link("input", "evaluator", src_key="ground_truth", dest_key="ground_truth")
+p.add_link("reranker", "evaluator", dest_key="nodes")
+p.add_link("output", "evaluator", dest_key="answer")
  • RagasComponent也是一个自定义的查询流水线组件,后面我们再详细介绍它的实现

  • 在查询流水线中增加了一个evaluator模块,用于评估查询流水线

  • 我们将evaluator模块放到output模块之后,用于评估输出结果

  • evaluator模块有 4 个输入参数,分别是问题、真实答案、检索结果和生成答案,其中问题和真实答案通过input模块传入,检索结果通过reranker模块传入,生成答案通过output模块传入

  • 因为input模块现在有 2 个参数,分别是问题input和真实答案ground_truth,所以我们在添加input模块的相关连接关系时,需要指定src_key参数

我们再来看下RagasComponent的实现,示例代码如下:

from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from ragas import evaluate
from datasets import Dataset
from llama_index.core.query_pipeline import CustomQueryComponent
from typing import Dict, Any

metrics = [faithfulness, answer_relevancy, context_precision, context_recall]

class RagasComponent(CustomQueryComponent):
    """Ragas evalution component."""

    def _validate_component_inputs(self, input: Dict[str, Any]) -> Dict[str, Any]:
        """Validate component inputs during run_component."""
        return input

    @property
    def _input_keys(self) -> set:
        """Input keys dict."""
        return {"question", "nodes", "answer", "ground_truth", }

    @property
    def _output_keys(self) -> set:
        return {"answer", "source_nodes", "evaluation"}

    def _run_component(self, **kwargs) -> Dict[str, Any]:
        """Run the component."""
        question, ground_truth, nodes, answer = kwargs.values()
        data = {
            "question": [question],
            "contexts": [[n.get_content() for n in nodes]],
            "answer": [str(answer)],
            "ground_truth": [ground_truth],
        }
        dataset = Dataset.from_dict(data)
        evalution = evaluate(dataset, metrics)
        return {"answer": str(answer), "source_nodes": nodes, "evaluation": evalution}
  • 和之前的自定义组件一样,RagasComponent类需要实现_validate_component_inputs_input_keys_output_keys_run_component方法

  • 组件的输入参数是问题、真实答案、检索结果和生成答案,输出参数是生成答案、检索结果和评估结果

  • _run_component方法中,我们将输入参数重新封装成一个可供 Ragas 评估的Dataset对象

  • 评估指标我们使用的分别是:faithfulness(评估QuestionContext的一致性),answer_relevancy(评估AnswerQuestion的一致性),context_precision(评估Ground TruthContext中是否排名靠前),context_recall(评估Ground TruthContext的一致性)

  • 我们再调用evaluate方法对Dataset对象进行评估,得到评估结果

  • 最后将生成答案、检索结果和评估结果一起返回

最后我们来运行下查询流水线,示例代码如下:

question = "Which two members of the Avengers created Ultron?"
ground_truth = "Tony Stark (Iron Man) and Bruce Banner (The Hulk)."
output = p.run(input=question, ground_truth=ground_truth)
print(f"answer: {output['answer']}")
print(f"evaluation: {output['evaluation']}")

# 显示结果
answer: Tony Stark and Bruce Banner
evaluation: {'faithfulness': 1.0000, 'answer_relevancy': 0.8793, 'context_precision': 1.0000, 'context_recall': 1.0000}
  • 运行查询流水线时,我们需要传入问题和真实答案作为输入参数

  • 在输出结果中,我们可以看到生成的答案,以及评估结果 4 个评估指标的值

总结

通过上面的示例,我们可以看到如何通过模块化和流程的方式来实现高级 RAG 检索功能,我们可以根据具体的需求,自定义不同的模块,然后将这些模块按照一定的顺序组合起来,形成一个完整的查询流水线。在 RAG 应用中,我们还可以定义多个查询流水线,用于不同的场景,比如问答、对话、推荐等,这样可以更好地满足不同的需求。

关注我,一起学习各种人工智能和 AIGC 新技术,欢迎交流,如果你有什么想问想说的,欢迎在评论区留言。

引用参考

  • Modular RAG and RAG Flow: Part Ⅰ[8]

  • Modular RAG and RAG Flow: Part II[9]

  • An Introduction to LlamaIndex Query Pipelines[10]

参考:

[1] LlamaIndex: https://www.llamaindex.ai/

[2]复仇者联盟: https://en.wikipedia.org/wiki/Avenger

[3]Cohere: https://cohere.com/

[6] Ragas: https://docs.ragas.io/

[8]Modular RAG and RAG Flow: Part Ⅰ: https://medium.com/@yufan1602/modular-rag-and-rag-flow-part-%E2%85%B0-e69b32dc13a3

[9]Modular RAG and RAG Flow: Part II: https://medium.com/@yufan1602/modular-rag-and-rag-flow-part-ii-77b62bf8a5d3

[10]An Introduction to LlamaIndex Query Pipelines: https://docs.llamaindex.ai/en/stable/examples/pipeline/query_pipeline/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1816488.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

连接查询-外连接(FULL JOIN)、内连接(JOIN)、自身连接

一、表与表之间存在着某种联系,如果一个查询必须在多个表之间完成,则需要用到连接查询 二、连接查询的SQL查询语句 格式: SELECT A1,A2,...,Am FROM R1,R2,..,Rn WH…

大厂Java面试题:详细描述MyBatis缓存的实现原理

大家好,我是王有志。今天给大家带来的是一道来自光大科技的 MyBatis 面试题:详细描述MyBatis缓存的实现原理。 在通过源码分析 MyBatis 一二级缓存的实现原理前,我先给出我的回答。 首先是 MyBatis 一级缓存的实现原理: MyBaits…

Clion控制台打印中文乱码

第一步 第二步 第三步 ctrlaltshift/ 把run.processes,with.pty

文字悬停效果

文字悬停效果 效果展示 CSS 知识点 CSS 变量使用回顾-webkit-text-stroke 属性的运用与回顾 页面整体结构实现 <ul><li style"--clr: #e6444f"><a href"#" class"text">First</a></li><li style"--cl…

NiceGUI:让Python变身为Web应用开发大师的神器

简介 NiceGUI是一个易于使用的基于Python的UI框架&#xff0c;可以在您的Web浏览器中使用。您可以创建按钮、对话框、Markdown、3D场景、图表等等。 NiceGUI开源支持较好&#xff0c;代码更新频率较高&#xff0c;目前已经更新至: V1.4.26。 适用场景 NiceGUI非常适用于各种…

端点物联开发教程之(一)什么是端点物联

目录 一、手机端演示 二、开发套件 三、嵌入式端 四、平台端 五、手机端 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 物…

BL104钡铼多协议采集网关助力企业智能化转型

BL104钡铼多协议采集网关&#xff08;PLC物联网关BL104&#xff09;是为满足工业环境需求而设计的专业工业级协议转换网关。它在企业智能化转型过程中扮演着关键角色&#xff0c;为企业提供了高效、稳定的通信解决方案&#xff0c;助力企业实现智能化转型。 首先&#xff0c;P…

可转债全部历史因子数据,提供api支持

今天在写可转债系统&#xff0c;顺便下载了一下服务器的可转债数据&#xff0c;给大家研究使用 from trader_tool.stock_data import stock_datafrom trader_tool.lude_data_api import lude_data_apiimport osclass convertible_bond_back_test_system: 可转债回测系统…

弱监督语义/实例/全景分割综述2022

摘要 我们从一个统一的角度总结了现有的高效标签图像分割方法&#xff0c;讨论了一个重要的问题:如何弥合弱监督和密集预测之间的差距——目前的方法大多是基于启发式先验&#xff0c;如跨像素相似性、跨标签约束、跨视图一致性和跨图像关系。最后&#xff0c;对标签高效深度图…

黑苹果睡眠总是自动唤醒(RTC)

黑苹果睡眠总是自动唤醒【RTC】 1. 问题2. 解决方案2.1. 查看重启日志2.2. 配置Disable RTC wake scheduling补丁 3. 后续4. 参考 1. 问题 黑苹果EFI 更换后&#xff0c;总是在手动 睡眠后&#xff0c;间歇性重启&#xff0c;然后再次睡眠&#xff0c;然后再重启。原因归结为&…

HX519 防倒流数据线芯片IC

一般概述 苹果iPhone防倒流数据线芯片&#xff0c;可完美支持iPhone、iPad、iPod等8针闪电接口的数据传输同步功能及充电功能。 特点 ❥集成度高&#xff0c;极少的外围元器件。 ❥电路简单&#xff0c;价格优势明显。 ❥稳定性高&#xff0c;兼容性强。 ❥与市面上普通…

meilisearch,老版本的文档

Elasticsearch 做为老牌搜索引擎&#xff0c;功能基本满足&#xff0c;但复杂&#xff0c;重量级&#xff0c;适合大数据量。 MeiliSearch 设计目标针对数据在 500GB 左右的搜索需求&#xff0c;极快&#xff0c;单文件&#xff0c;超轻量。 所以&#xff0c;对于中小型项目来说…

golang函数

【1】函数&#xff1a; 对特定的功能进行提取&#xff0c;形成一个代码片段&#xff0c;这个代码片段就是我们所说的函数 【2】函数的作用&#xff1a;提高代码的复用性 【3】函数和函数是并列的关系&#xff0c;所以我们定义的函数不能写到main函数中 【4】基本语法 func 函…

北交字节联合提出ClassDiffusion: 使用显式类别引导的一致性个性化生成。

在个性化生成领域, 微调可能会引起过拟合导致模型无法生成与提示词一致的结果。针对这个问题&#xff0c;北交&字节联合提出ClassDiffusion&#xff0c;来提升个性化生成的一致性。 通过两个重要观察及理论分析提出了新的观点:一致性的损失是个性化概念语义偏移导致的, 还…

three.js 第四节 - 创建顶点(索引的使用)

顶点不共用&#xff08;不使用索引&#xff09; // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControlsconst scence new THREE.Scene()const camera new THREE.PerspectiveCamera(45, …

cesium 多边形加边框宽度 Polygon outlineWidth

cesium中用polygon添加多边形时&#xff0c;设置outlineWidth无效&#xff0c;常见做法是在添加polygon的同时加一个polyline&#xff0c;但是当多边形相邻两条边的角度比较小的情况下&#xff0c;这两个点的连接处有明显的交叉。 解决方案&#xff1a; 第一步&#xff1a;通过…

永磁同步电机滞环电流控制(PI双闭环)matlab仿真模型

微♥“电击小子程高兴的MATLAB小屋”获取模型 1.滞环电流控制的原理 将给定的电流信号与反馈的电流信号进行比较&#xff0c;然后控制它俩之间的差值稳定在一个滞环范围内&#xff0c;若超出范围&#xff0c;则进行相应的调节操作。 操作如下叙述&#xff1a;假设以三相中的A相…

网络安全领域国内外有哪些法律法规?

1. 中国 1.中华人民共和国网络安全法&#xff08;简称网安法&#xff09; 生效时间&#xff1a;2017年6月1日主要内容&#xff1a;规范网络运营行为&#xff0c;维护网络安全&#xff0c;保护国家安全和公共利益&#xff0c;以及保护公民、法人和其他组织的合法权益。 2.中华…

使用AlphaCodium进行代码生成,从提示工程到流程工程

AlphaCodium 的代码生成方法 论文地址&#xff1a;https://arxiv.org/pdf/2401.08500.pdf 源码地址&#xff1a;https://github.com/codium-ai/alphacodium 研究要点包括 **挑战&#xff1a;**现有的自然语言优化方法无法扩展 LLM 的代码生成能力**解决方案&#xff1a;**使…

通过搭建 24 点小游戏应用实战,带你了解 AppBuilder 的技术原理

本文将通过一个 24 点小游戏的案例&#xff0c;详细介绍百度智能云千帆 AppBuilder 的基本技术原理和使用方法&#xff0c;帮助读者快速掌握 AI 原生应用的开发流程。 1 三步构建 AI 原生应用方法论 AI 原生应用与传统应用的最大区别是交互形态彻底的拟人化&#xff0c;通过…