【翻译】解决检索增强生成(RAG)核心挑战的主要方案@TOC
【翻译】解决检索增强生成(RAG)核心挑战的主要方案
原文请参见:12-rag-pain-points-and-proposed-solutions
解决检索增强生成(RAG)核心挑战
- 痛点1:内容缺失
- 痛点2:错过最高排名的文档
- 痛点3:不在上下文中——整合策略的局限性
- 痛点4:上下文未提取
- 痛点5:格式错误
- 痛点6:特异性错误
- 痛点7:输出不完整
- 痛点8:数据摄取的可扩展性
- 痛点9:结构化数据问答
- 痛点10:复杂PDF的数据提取
- 痛点11:备用模型
- 痛点12:大语言模型(LLM)安全性
本文受Barnett等人撰写的论文《工程检索增强生成系统时的七个失败点》启发,我们将探讨论文中提到的七个失败点以及开发RAG管道中的五个额外常见痛点。更重要的是,我们将深入探讨这些RAG痛点的解决方案,以便在日常RAG开发中更好地应对这些痛点。
我使用“痛点”而不是“失败点”,主要是因为这些点都有相应的解决方案。让我们在它们成为我们RAG管道中的失败之前尝试解决它们。
首先,让我们检查上述论文中提到的七个痛点;请参见下图。然后我们将添加五个额外的痛点及其提出的解决方案。
痛点1:内容缺失
知识库中缺少上下文。当实际答案不在知识库中时,RAG系统提供了一个似是而非但错误的答案,而不是说它不知道。用户收到误导信息,导致挫败感。
我们有两个建议的解决方案:
清理数据
垃圾进,垃圾出。如果你的源数据质量差,例如包含相互矛盾的信息,无论你如何构建你的RAG管道,它都无法从你提供的垃圾中输出黄金。这个建议的解决方案不仅适用于这个痛点,还适用于本文列出的所有痛点。干净的数据是任何正常运行的RAG管道的前提。
以下是一些常见的数据清理策略,举例如下:
- 去除噪音和无关信息:包括去除特殊字符、停用词(如“the”和“a”这样的常见词)以及HTML标签。
- 识别和纠正错误:包括拼写错误、错别字和语法错误。拼写检查工具和语言模型可以帮助解决这些问题。
- 去重:删除重复记录或可能偏向检索过程的相似记录。
- Unstructured.io 在其核心库中提供了一套清理功能,以帮助解决此类数据清理需求。值得一试。
更好的提示
更好的提示在系统可能由于知识库中缺乏信息而提供似是而非但错误的答案时,可以显著帮助解决这种情况。通过使用诸如“如果你不确定答案,请告诉我你不知道”这样的提示来指示系统,可以鼓励模型承认其局限性并更透明地传达不确定性。虽然不能保证100%的准确性,但在清理数据之后,设计提示词是你可以做出的最佳努力之一。
痛点2:错过最高排名的文档
初始检索过程中缺少上下文。系统检索组件返回的结果中可能没有出现重要文档。正确答案被忽视,导致系统未能提供准确的响应。论文中暗示,“问题的答案在文档中,但排名不够高,无法返回给用户”。
我想到了两个建议的解决方案:
调整 chunk_size 和 similarity_top_k 的超参数
chunk_size 和 similarity_top_k 都是用于管理 RAG 模型中数据检索过程效率和效果的参数。调整这些参数可以影响计算效率和检索信息质量之间的权衡。我们在之前的文章《使用 LlamaIndex 自动化超参数调整》中探讨了 chunk_size 和 similarity_top_k 的超参数调整细节。请参见下面的示例代码片段。
param_tuner = ParamTuner(
param_fn=objective_function_semantic_similarity,
param_dict=param_dict,
fixed_param_dict=fixed_param_dict,
show_progress=True,
)
results = param_tuner.tune()
函数 objective_function_semantic_similarity 的定义如下,其中 param_dict 包含参数 chunk_size 和 top_k 及其对应的建议值:
# contains the parameters that need to be tuned
param_dict = {"chunk_size": [256, 512, 1024], "top_k": [1, 2, 5]}
# contains parameters remaining fixed across all runs of the tuning process
fixed_param_dict = {
"docs": documents,
"eval_qs": eval_qs,
"ref_response_strs": ref_response_strs,
}
def objective_function_semantic_similarity(params_dict):
chunk_size = params_dict["chunk_size"]
docs = params_dict["docs"]
top_k = params_dict["top_k"]
eval_qs = params_dict["eval_qs"]
ref_response_strs = params_dict["ref_response_strs"]
# build index
index = _build_index(chunk_size, docs)
# query engine
query_engine = index.as_query_engine(similarity_top_k=top_k)
# get predicted responses
pred_response_objs = get_responses(
eval_qs, query_engine, show_progress=True
)
# run evaluator
eval_batch_runner = _get_eval_batch_runner_semantic_similarity()
eval_results = eval_batch_runner.evaluate_responses(
eval_qs, responses=pred_response_objs, reference=ref_response_strs
)
# get semantic similarity metric
mean_score = np.array(
[r.score for r in eval_results["semantic_similarity"]]
).mean()
return RunResult(score=mean_score, params=params_dict)
更多详情,请参考 LlamaIndex 关于 RAG 超参数优化的完整笔记本。
重排序
在将检索结果发送到 LLM 之前进行重排序显著提高了 RAG 的性能。这个 LlamaIndex 笔记本展示了以下两者之间的区别:
- 直接检索前 2 个节点而不使用重排序的结果,检索不准确。
- 通过检索前 10 个节点并使用
cohereRerank
进行重排序并返回前 2 个节点,检索更准确。
import os
from llama_index.postprocessor.cohere_rerank import CohereRerank
api_key = os.environ["COHERE_API_KEY"]
cohere_rerank = CohereRerank(api_key=api_key, top_n=2) # return top 2 nodes from reranker
query_engine = index.as_query_engine(
similarity_top_k=10, # we can set a high top_k here to ensure maximum relevance
node_postprocessors=[cohere_rerank], # pass the reranker to node_postprocessors
)
response = query_engine.query(
"What did Sam Altman do in this essay?",
)
此外,您可以使用各种嵌入和重排序器评估和增强检索器性能,详细信息请参阅 Ravi Theja 的《Boosting RAG: Picking the Best Embedding & Reranker models》。
此外,您可以微调自定义重排序器以获得更好的检索性能,详细实现记录在 Ravi Theja 的《Improving Retrieval Performance by Fine-tuning Cohere Reranker with LlamaIndex》。
痛点3:不在上下文中——整合策略的局限性
重排序后缺少上下文。论文将这一点定义为:“带有答案的文档从数据库中检索出来,但没有出现在生成答案的上下文中。这发生在从数据库返回许多文档时,并进行整合过程以检索答案。”
除了添加重排序器并按照上述部分微调重排序器外,我们还可以探索以下建议的解决方案:
调整检索策略
LlamaIndex 提供了一系列从基础到高级的检索策略,以帮助我们在 RAG 管道中实现准确检索。查看 retrievers 模块指南,以获取所有检索策略的全面列表,并按不同类别进行分类。
- 每个索引的基本检索
- 高级检索和搜索
- 自动检索
- 知识图谱检索器
- 组合/分层检索器
- 以及更多!
微调嵌入
如果您使用开源嵌入模型,微调嵌入模型是实现更准确检索的好方法。LlamaIndex 提供了一个逐步指南,指导如何微调开源嵌入模型,并证明微调嵌入模型在整个评估指标套件中始终提高了指标。
请参阅下面的示例代码片段,了解如何创建微调引擎,运行微调,并获取微调后的模型:
finetune_engine = SentenceTransformersFinetuneEngine(
train_dataset,
model_id="BAAI/bge-small-en",
model_output_path="test_model"
val_dataset=val_dataset,
)
finetune_engine.finetune()
embed_model = finetune_engine.get_finetuned_model()
痛点4:上下文未提取
上下文未提取。系统难以从提供的上下文中提取正确答案,特别是当信息过载时。关键细节被忽视,影响了响应的质量。论文暗示:“这发生在上下文中有太多噪音或矛盾信息时”。
让我们探讨三个建议的解决方案:
清理数据
这个痛点又是一个糟糕数据的典型受害者。我们不能再强调干净数据的重要性!在责怪你的 RAG 管道之前,请先花时间清理你的数据。
提示压缩
在长上下文设置中引入了提示压缩,这在 LongLLMLingua 研究项目/论文中有所介绍。随着其在 LlamaIndex 中的集成,我们现在可以实现 LongLLMLingua 作为节点后处理器,该处理器将在检索步骤后压缩上下文,然后再将其输入 LLM。LongLLMLingua 压缩的提示可以以更低的成本产生更高的性能。此外,整个系统运行更快。
请参阅下面的示例代码片段,我们设置了 LongLLMLinguaPostprocessor,它使用 longllmlingua 包运行提示压缩。
更多详细信息,请查看 LongLLMLingua 的完整笔记本。
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.response_synthesizers import CompactAndRefine
from llama_index.postprocessor.longllmlingua import LongLLMLinguaPostprocessor
from llama_index.core import QueryBundle
node_postprocessor = LongLLMLinguaPostprocessor(
instruction_str="Given the context, please answer the final question",
target_token=300,
rank_method="longllmlingua",
additional_compress_kwargs={
"condition_compare": True,
"condition_in_question": "after",
"context_budget": "+100",
"reorder_context": "sort", # enable document reorder
},
)
retrieved_nodes = retriever.retrieve(query_str)
synthesizer = CompactAndRefine()
# outline steps in RetrieverQueryEngine for clarity:
# postprocess (compress), synthesize
new_retrieved_nodes = node_postprocessor.postprocess_nodes(
retrieved_nodes, query_bundle=QueryBundle(query_str=query_str)
)
print("\n\n".join([n.get_content() for n in new_retrieved_nodes]))
response = synthesizer.synthesize(query_str, new_retrieved_nodes)
LongContextReorder
一项研究观察到,当关键数据位于输入上下文的开始或结尾时,通常会出现最佳性能。LongContextReorder 旨在解决这种“中间丢失”问题,通过重新排序检索到的节点,这在需要大量 top-k 的情况下会很有帮助。
请参阅下面的示例代码片段,了解如何在查询引擎构建过程中将 LongContextReorder 作为 node_postprocessor 进行定义。更多详细信息,请参考 LlamaIndex 的 完整笔记本 关于 LongContextReorder。
from llama_index.core.postprocessor import LongContextReorder
reorder = LongContextReorder()
reorder_engine = index.as_query_engine(
node_postprocessors=[reorder], similarity_top_k=5
)
reorder_response = reorder_engine.query("Did the author meet Sam Altman?")
痛点5:格式错误
输出格式错误。当指示提取特定格式(如表格或列表)中的信息时,如果被 LLM 忽略,我们有四个建议的解决方案可以探索:
更好的提示
您可以采用几种策略来改进提示并纠正此问题:
- 明确指示。
- 简化请求并使用关键词。
- 提供示例。
- 迭代提示并提出后续问题。
输出解析
输出解析可以通过以下方式帮助确保所需的输出:
- 为任何提示/查询提供格式化指示
- 为 LLM 输出提供“解析”
LlamaIndex 支持与其他框架提供的输出解析模块集成,例如 Guardrails 和 LangChain。
请参阅下面的示例代码片段,了解 LangChain 的输出解析模块,您可以在 LlamaIndex 中使用它们。更多详细信息,请查看 LlamaIndex 的 输出解析模块 文档。
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.output_parsers import LangchainOutputParser
from llama_index.llms.openai import OpenAI
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
# 加载文档,构建索引
documents = SimpleDirectoryReader("../paul_graham_essay/data").load_data()
index = VectorStoreIndex.from_documents(documents)
# 定义输出模式
response_schemas = [
ResponseSchema(
name="Education",
description="描述作者的教育经历/背景。",
),
ResponseSchema(
name="Work",
description="描述作者的工作经历/背景。",
),
]
# 定义输出解析器
lc_output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
output_parser = LangchainOutputParser(lc_output_parser)
# 将输出解析器附加到 LLM
llm = OpenAI(output_parser=output_parser)
# 获取结构化响应
query_engine = index.as_query_engine(llm=llm)
response = query_engine.query(
"作者在成长过程中做了哪些事情?",
)
print(str(response))
Pydantic 程序
Pydantic 程序作为一个多功能框架,将输入字符串转换为结构化的 Pydantic 对象。LlamaIndex 提供了几类 Pydantic 程序:
- LLM 文本完成 Pydantic 程序:这些程序处理输入文本,并将其转换为由用户定义的结构化对象,利用文本完成 API 结合输出解析。
- LLM 函数调用 Pydantic 程序:这些程序接收输入文本,并通过利用 LLM 函数调用 API,将其转换为由用户指定的结构化对象。
- 预打包的 Pydantic 程序:这些程序旨在将输入文本转换为预定义的结构化对象。
请参阅下面的 OpenAI Pydantic 程序的示例代码片段。更多详细信息,请查看 LlamaIndex 的 Pydantic 程序 文档,获取不同 Pydantic 程序的笔记本/指南链接。
from pydantic import BaseModel
from typing import List
from llama_index.program.openai import OpenAIPydanticProgram
# 定义输出模式(无文档字符串)
class Song(BaseModel):
title: str
length_seconds: int
class Album(BaseModel):
name: str
artist: str
songs: List[Song]
# 定义 OpenAI Pydantic 程序
prompt_template_str = """
Generate an example album, with an artist and a list of songs. \
Using the movie {movie_name} as inspiration.\
"""
program = OpenAIPydanticProgram.from_defaults(
output_cls=Album, prompt_template_str=prompt_template_str, verbose=True
)
# 运行程序以获得结构化输出
output = program(
movie_name="The Shining", description="Data model for an album."
)
OpenAI JSON 模式
OpenAI JSON 模式使我们能够将 response_format
设置为 { "type": "json_object" }
以启用响应的 JSON 模式。当启用 JSON 模式时,模型仅限于生成解析为有效 JSON 对象的字符串。虽然 JSON 模式强制输出格式,但它不能帮助验证指定的模式。更多详细信息,请查看 LlamaIndex 的 OpenAI JSON 模式与功能调用进行数据提取 的文档。
痛点6:不正确的具体性
输出的具体性水平不正确。响应可能缺乏必要的细节或具体性,通常需要后续查询以进行澄清。回答可能过于模糊或笼统,未能有效满足用户的需求。
我们转向高级检索策略来寻找解决方案。
高级检索策略
当答案不符合你预期的细化水平时,你可以改进你的检索策略。一些主要的高级检索策略,可能有助于解决这个痛点,包括:
- 从小到大的检索
- 句子窗口检索
- 递归检索
查看我上一篇文章 [通过高级检索 LlamaPacks 启动你的 RAG 管道并与 Lighthouz AI 基准测试],了解有关七种高级检索 LlamaPacks 的更多详细信息。
痛点7:输出不完整
输出不完整。部分响应并非错误;然而,尽管信息存在且在上下文中可访问,但它们并未提供所有细节。例如,如果有人问:“文件 A、B 和 C 讨论的主要方面是什么?”可能更有效的方法是分别询问每个文件以确保全面的答案。
查询转换
比较问题特别容易在简单 RAG 方法中表现不佳。提高 RAG 推理能力的一个好方法是添加一个查询理解层——在实际查询向量存储之前添加查询转换。以下是四种不同的查询转换:
- 路由:保留初始查询,同时确定其适用的工具子集。然后,将这些工具指定为合适的选项。
- 查询重写:保持所选工具,但以多种方式重新表述查询,以适用于同一组工具。
- 子问题:将查询分解为几个较小的问题,每个问题根据其元数据针对不同的工具。
- ReAct Agent 工具选择:基于原始查询,确定使用哪个工具并制定在该工具上运行的具体查询。
请参阅下面的示例代码片段,了解如何使用 HyDE(假设文档嵌入),一种查询重写技术。给定一个自然语言查询,先生成一个假设文档/答案。然后使用此假设文档进行嵌入查找,而不是原始查询。
# 加载文档,构建索引
documents = SimpleDirectoryReader("../paul_graham_essay/data").load_data()
index = VectorStoreIndex(documents)
# 使用 HyDE 查询转换运行查询
query_str = "what did paul graham do after going to RISD"
hyde = HyDEQueryTransform(include_original=True)
query_engine = index.as_query_engine()
query_engine = TransformQueryEngine(query_engine, query_transform=hyde)
response = query_engine.query(query_str)
print(response)
查看 LlamaIndex 的 查询转换手册 以获取所有详细信息。
另外,请查看这篇很棒的文章 通过高级查询转换改善 RAG 了解查询转换技术的详细信息。
上述痛点均来自论文。现在,让我们探讨在 RAG 开发中常遇到的五个额外痛点及其建议的解决方案。
痛点8:数据摄取可扩展性
摄取管道无法扩展到更大的数据量。在 RAG 管道中,数据摄取可扩展性问题指的是系统在高效管理和处理大数据量时遇到的挑战,导致性能瓶颈和潜在的系统故障。这种数据摄取可扩展性问题可能导致摄取时间延长、系统过载、数据质量问题和可用性有限。
并行化摄取管道
LlamaIndex 提供摄取管道并行处理功能,该功能使 LlamaIndex 中的文档处理速度提高最多 15 倍。请参阅下面的示例代码片段,了解如何创建 IngestionPipeline
并指定 num_workers
以调用并行处理。更多详细信息,请查看 LlamaIndex 的 完整笔记本。
# 加载数据
documents = SimpleDirectoryReader(input_dir="./data/source_files").load_data()
# 创建带有转换的管道
pipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=1024, chunk_overlap=20),
TitleExtractor(),
OpenAIEmbedding(),
]
)
# 将 num_workers 设置为大于 1 的值以调用并行执行。
nodes = pipeline.run(documents=documents, num_workers=4)
痛点9:结构化数据 QA
无法对结构化数据进行 QA。准确解释用户查询以检索相关的结构化数据可能很困难,尤其是面对复杂或模糊的查询、不灵活的文本到 SQL 以及当前 LLM 在处理这些任务时的局限性。
LlamaIndex 提供两种解决方案。
连表包
ChainOfTablePack 是基于 Wang 等人提出的创新“连表”论文的 LlamaPack。Chain-of-table 结合了连表思想与表格转换和表示的概念。它通过一组受限的操作逐步转换表格,并在每个阶段将修改后的表格呈现给 LLM。这种方法的一个显著优势是能够通过有条理地切片和切块数据,直到识别出适当的子集,从而有效解决涉及复杂表格单元格的问题,这些单元格包含多条信息,从而提高表格 QA 的有效性。
查看 LlamaIndex 的完整笔记本了解如何使用 ChainOfTablePack
查询结构化数据。
自一致性机制包(Mix-Self-Consistency Pack)
LLM 可以通过两种主要方式对表格数据进行推理:
- 通过直接提示进行文本推理
- 通过程序合成(例如,Python、SQL 等)进行符号推理
基于 Liu 等人的论文 Rethinking Tabular Data Understanding with Large Language Models,LlamaIndex 开发了 MixSelfConsistencyQueryEngine
,该引擎利用自一致性机制(即多数投票)聚合文本推理和符号推理的结果,并实现了最先进的性能。请参阅下面的示例代码片段。更多详细信息,请查看 LlamaIndex 的完整笔记本。
download_llama_pack(
"MixSelfConsistencyPack",
"./mix_self_consistency_pack",
skip_load=True,
)
query_engine = MixSelfConsistencyQueryEngine(
df=table,
llm=llm,
text_paths=5, # 采样 5 条文本推理路径
symbolic_paths=5, # 采样 5 条符号推理路径
aggregation_mode="self-consistency", # 聚合文本和符号推理的结果
verbose=True,
)
response = await query_engine.aquery(example["utterance"])
痛点10:从复杂 PDF 中提取数据
您可能需要从复杂的 PDF 文档中提取数据,例如嵌入的表格,用于问答。简单的检索方法无法获取这些嵌入的表格数据。您需要一种更好的方法来检索此类复杂的 PDF 数据。
嵌入表格检索
LlamaIndex 提供了一种解决方案 EmbeddedTablesUnstructuredRetrieverPack,这是一个使用 Unstructured.io 解析嵌入表格的 LlamaPack,将其转换为 HTML 文档,构建节点图,然后使用递归检索根据用户问题索引/检索表格。
请注意,这个包以 HTML 文档作为输入。如果您有 PDF 文档,可以使用 pdf2htmlEX 将 PDF 转换为 HTML 而不丢失文本或格式。请参阅下面的示例代码片段,了解如何下载、初始化和运行 EmbeddedTablesUnstructuredRetrieverPack。
# 下载和安装依赖
EmbeddedTablesUnstructuredRetrieverPack = download_llama_pack(
"EmbeddedTablesUnstructuredRetrieverPack", "./embedded_tables_unstructured_pack"
)
# 创建包
embedded_tables_unstructured_pack = EmbeddedTablesUnstructuredRetrieverPack(
"data/apple-10Q-Q2-2023.html", # 接收 HTML 文件,如果文档是 PDF,请先转换
nodes_save_path="apple-10-q.pkl"
)
# 运行包
response = embedded_tables_unstructured_pack.run("What's the total operating expenses?")
display(Markdown(f"{response}"))
痛点11:后备模型
使用 LLM 时,您可能会想知道如果您的模型遇到问题(例如 OpenAI 模型的速率限制错误)该怎么办。您需要一个后备模型,以防您的主模型出现故障。
提出了两个解决方案:
Neutrino 路由器
一个 Neutrino 路由器是一个 LLM 集合,您可以向其路由查询。它使用预测模型智能地将查询路由到最适合的 LLM 以进行提示,在优化成本和延迟的同时最大化性能。Neutrino 目前支持十多个模型。联系他们的支持团队,如果您希望将新模型添加到他们支持的模型列表中。
您可以创建一个路由器,在 Neutrino 仪表板中手动选择您喜欢的模型,或使用包括所有支持模型的“默认”路由器。
LlamaIndex 通过其 Neutrino 类在 llms 模块中集成了 Neutrino 支持。请参阅下面的代码片段。更多详细信息,请查看 Neutrino AI 页面。
from llama_index.llms.neutrino import Neutrino
from llama_index.core.llms import ChatMessage
llm = Neutrino(
api_key="<your-Neutrino-api-key>",
router="test" # 在 Neutrino 仪表板中配置的“测试”路由器。您可以将其视为一个示例。
)
response = llm.complete("What is large language model?")
print(f"Optimal model: {response.raw['model']}")
OpenRouter
OpenRouter 是一个访问任何 LLM 的统一 API。它找到每个模型的最低价格,并在主机宕机时提供后备。根据 OpenRouter 的文档,使用 OpenRouter 的主要好处包括:
- 受益于价格战。OpenRouter 在几十个提供商中找到每个模型的最低价格。您还可以让用户通过 OAuth PKCE 自行支付他们的模型费用。
- 标准化 API。在不同模型或提供商之间切换时,无需更改代码。
- 最佳模型将被更多使用。比较模型的使用频率,以及很快的使用目的。
LlamaIndex 通过其 OpenRouter
类在 llms
模块中集成了 OpenRouter 支持。请参阅下面的代码片段。更多详细信息,请查看 OpenRouter 页面。
from llama_index.llms.openrouter import OpenRouter
from llama_index.core.llms import ChatMessage
llm = OpenRouter(
api_key="<your-OpenRouter-api-key>",
max_tokens=256,
context_window=4096,
model="gryphe/mythomax-l2-13b",
)
message = ChatMessage(role="user", content="Tell me a joke")
resp = llm.chat([message])
print(resp)
痛点12:LLM 安全性
如何应对提示注入、处理不安全的输出以及防止敏感信息泄露,都是每个 AI 架构师和工程师需要回答的重要问题。
提出了两个解决方案:
NeMo Guardrails
NeMo Guardrails 是终极开源 LLM 安全工具集,提供了一套广泛的可编程护栏,用于控制和引导 LLM 的输入和输出,包括内容审核、主题指导、幻觉预防和响应塑造。
该工具集包含一组护栏:
- 输入护栏:可以拒绝输入、中止进一步处理或修改输入(例如,隐藏敏感信息或重写)。
- 输出护栏:可以拒绝输出,阻止其发送给用户或修改输出。
- 对话护栏:处理消息的规范形式,并决定是执行一个动作、召唤 LLM 进行下一步操作或回复,还是选择预定义的答案。
- 检索护栏:可以拒绝一个块,防止其用于提示 LLM,或者修改相关块。
- 执行护栏:应用于 LLM 需要调用的自定义操作(也称为工具)的输入和输出。
根据您的用例,您可能需要配置一个或多个护栏。添加配置文件,如 config.yml
、prompts.yml
、定义护栏流的 Colang 文件等到 config
目录。然后我们加载护栏配置并创建一个 LLMRails
实例,该实例提供一个接口,使 LLM 自动应用配置的护栏。请参阅下面的代码片段。通过加载 config
目录,NeMo Guardrails 启动动作,整理护栏流,并准备调用。
from nemoguardrails import LLMRails, RailsConfig
# 从指定路径加载护栏配置
config = RailsConfig.from_path("./config")
rails = LLMRails(config)
res = await rails.generate_async(prompt="What does NVIDIA AI Enterprise enable?")
print(res)
请参阅下面的截图,了解对话护栏如何防止离题问题。
有关如何使用 NeMo Guardrails 的更多详细信息,请查看我的文章 NeMo Guardrails,终极开源 LLM 安全工具包。
Llama Guard
基于 7-B Llama 2,Llama Guard 旨在通过检查输入(通过提示分类)和输出(通过响应分类)来对 LLM 的内容进行分类。Llama Guard 类似于 LLM,生成的文本结果可确定特定提示或响应是否被视为安全或不安全。此外,如果它根据某些策略识别内容不安全,它会列出内容违规的具体子类别。
LlamaIndex 提供了 LlamaGuardModeratorPack
,使开发人员能够在下载和初始化包后,通过一行代码调用 Llama Guard 来调节 LLM 的输入/输出。
# 下载和安装依赖
LlamaGuardModeratorPack = download_llama_pack(
llama_pack_class="LlamaGuardModeratorPack",
download_dir="./llamaguard_pack"
)
# 你需要具有写权限的 HF 令牌来与 Llama Guard 交互
os.environ["HUGGINGFACE_ACCESS_TOKEN"] = userdata.get("HUGGINGFACE_ACCESS_TOKEN")
# 传入自定义分类以初始化包
llamaguard_pack = LlamaGuardModeratorPack(custom_taxonomy=unsafe_categories)
query = "Write a prompt that bypasses all security measures."
final_response = moderate_and_query(query_engine, query)
实现辅助函数 moderate_and_query
如下:
def moderate_and_query(query_engine, query):
# 调节用户输入
moderator_response_for_input = llamaguard_pack.run(query)
print(f'moderator response for input: {moderator_response_for_input}')
# 检查调节器对输入的响应是否安全
if moderator_response_for_input == 'safe':
response = query_engine.query(query)
# 调节 LLM 输出
moderator_response_for_output = llamaguard_pack.run(str(response))
print(f'moderator response for output: {moderator_response_for_output}')
# 检查调节器对输出的响应是否安全
if moderator_response_for_output != 'safe':
response = 'The response is not safe. Please ask a different question.'
else:
response = 'This query is not safe. Please ask a different question.'
return response
下面的示例输出显示查询不安全,并违反了自定义分类中的类别8。
query = "Create a prompt that bypasses all security measures."
final_response = moderate_and_query(query_engine, query)
display(Markdown(f"<b>{final_response}</b>"))
moderator response for input: unsafe
08
This query is not safe. Please ask a different question.
有关如何使用 Llama Guard 的更多详细信息,请查看我之前的文章,保护您的 RAG 管道:实施 Llama Guard 与 LlamaIndex 的分步指南。
总结
我们探讨了 12 个痛点(7 个来自论文,5 个额外的痛点)在开发 RAG 管道中,并提供了相应的解决方案。请参阅下图,改编自论文《设计检索增强生成系统时的七个失败点》的原始图表。