·痛点 1:缺失内容
·痛点 2:错过排名靠前的文档
·痛点 3:不在上下文中 — 整合策略限制
·痛点 4:未提取
·痛点 5:格式错误
·痛点 6:特异性不正确
·痛点 7:不完整
·痛点 8:数据提取可扩展性
·痛点 9:结构化数据 QA
·痛点 10:从复杂 PDF 中提取数据
·痛点 11:后备模型
·痛点 12:LLM 安全性
受到 Barnett 等人撰写的论文《设计检索增强生成系统的七个失败点》的启发,我们将在本文中探讨论文中提到的七个失败点以及开发 RAG 管道时的其他五个常见痛点。更重要的是,我们将深入研究这些 RAG 痛点的解决方案,以便我们能够更好地在日常 RAG 开发中解决这些痛点。
我使用“痛点”而不是“故障点”主要是因为这些点都有相应的解决方案。让我们尝试在它们成为 RAG 管道中的故障之前修复它们。
首先,让我们来看看上述论文中提到的七个痛点;见下图。然后我们将添加另外五个痛点及其建议的解决方案。
痛点一:内容缺失
知识库中缺少上下文。当实际答案不在知识库中时,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,
)
结果=param_tuner.tune()
该函数objective_function_semantic_similarity
定义如下,param_dict
包含参数、chunk_size
和top_k
以及它们对应的建议值:
# 包含需要调整的参数
param_dict = { "chunk_size" : [ 256 , 512 , 1024 ], "top_k" : [ 1 , 2 , 5 ]}
# 包含在调整过程的所有运行中保持不变的参数
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" ]
# 构建索引
index = _build_index(chunk_size, docs)
# 查询引擎
query_engine = index.as_query_engine(similarity_top_k=top_k)
# 获取预测响应
pred_response_objs = get_responses(
eval_qs, query_engine, show_progress= True
)
# 运行评估器
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
)
# 获取语义相似度度量
mean_score = np.array(
[r.score for r in eval_results[ "semantic_similarity" ]]
).mean()
return运行结果(分数=平均分数,参数=params_dict)
有关更多详细信息,请参阅 LlamaIndex关于 RAG 超参数优化的完整笔记本。
重新排序
在将检索结果发送到 LLM 之前对其进行重新排序显著提高了 RAG 性能。此 LlamaIndex笔记本演示了以下两者之间的区别:
- 不经过重新排序,直接检索前 2 个节点,导致检索不准确。
- 通过检索前 10 个节点并
CohereRerank
重新排序并返回前 2 个节点来实现准确检索。
导入 os
从 llama_index.postprocessor.cohere_rerank 导入 CohereRerank
api_key = os.environ[ "COHERE_API_KEY" ]
cohere_rerank = CohereRerank(api_key=api_key, top_n=2) # 从重新排名器返回前 2 个节点
query_engine = index.as_query_engine(
similarity_top_k=10, # 我们可以在这里设置一个高的 top_k 以确保最大相关检索
node_postprocessors=[cohere_rerank], # 将重新排名器传递给 node_postprocessors
)
response = query_engine.query(
"Sam Altman 在这篇文章中做了什么?" ,
)
此外,您还可以使用各种嵌入和重新排序器来评估和增强检索器性能,如Ravi Theja撰写的《Boosting RAG:挑选最佳嵌入和重新排序器模型》中所述。
此外,您可以微调自定义重新排序器以获得更好的检索性能,详细实现记录在Ravi Theja撰写的《使用 LlamaIndex 微调 Cohere Reranker 来提高检索性能》中。
痛点三:不切合实际——整合策略的局限性
重新排序后上下文缺失。论文定义了这一点:“从数据库中检索到带有答案的文档,但未将其放入生成答案的上下文中。当从数据库返回许多文档时会发生这种情况,并且会进行合并过程以检索答案”。
除了如上一节所述添加重新排序器并微调重新排序器之外,我们还可以探索以下建议的解决方案:
调整检索策略
LlamaIndex 提供了一系列检索策略,从基础到高级,以帮助我们在 RAG 管道中实现准确检索。查看检索器模块指南,获取所有检索策略的完整列表,这些策略分为不同的类别。
- 从每个索引进行基本检索
- 高级检索和搜索
- 自动检索
- 知识图谱检索器
- 组合/分层检索器
- 还有更多!
微调嵌入
如果您使用开源嵌入模型,微调嵌入模型是实现更准确检索的好方法。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()
痛点四:无法提取
上下文未提取。系统很难从提供的上下文中提取正确的答案,尤其是在信息过载的情况下。关键细节被遗漏,影响了回答的质量。论文暗示:“当上下文中有太多噪音或矛盾的信息时,就会发生这种情况”。
让我们探讨一下提出的三种解决方案:
清理数据
这个痛点是坏数据的另一个典型受害者。我们再怎么强调干净数据的重要性也不为过!在责怪您的 RAG 管道之前,请先花时间清理您的数据。
即时压缩
LongLLMLingua 研究项目/论文介绍了长上下文设置中的提示压缩。通过将其集成到 LlamaIndex,我们现在可以将 LongLLMLingua 实现为节点后处理器,它将在检索步骤之后压缩上下文,然后再将其输入到 LLM 中。LongLLMLingua 压缩提示可以以更低的成本获得更高的性能。此外,整个系统运行速度更快。
请参阅下面的示例代码片段,我们在其中设置了LongLLMLinguaPostprocessor
,它使用该longllmlingua
包来运行提示压缩。
欲了解更多详细信息,请查看LongLLMLingua 上的完整笔记本。
从llama_index.core.query_engine导入RetrieverQueryEngine
从llama_index.core.response_synthesizers导入CompactAndRefine
从llama_index.postprocessor.longllmlingua导入LongLLMLinguaPostprocessor
从llama_index.core导入QueryBundle
node_postprocessor = LongLLMLinguaPostprocessor(
indication_str= "根据上下文,请回答最后一个问题" ,
target_token= 300 ,
rank_method= "longllmlingua" ,
additional_compress_kwargs={
"condition_compare" : True ,
"condition_in_question" : "after" ,
"context_budget" : "+100" ,
"reorder_context" : "sort" , # 启用文档重新排序
},
)
removed_nodes = withdrawer.retrieve(query_str)
synthesizer = CompactAndRefine()
# 为清晰起见,概述 RetrieverQueryEngine 中的步骤:
# 后处理(压缩)、合成
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
旨在通过重新排序检索到的节点来解决这个“丢失在中间”的问题,这在需要大量 top-k 的情况下会有所帮助。
请参阅下面的示例代码片段,了解如何在查询引擎构建过程中将其定义LongContextReorder
为您的。有关更多详细信息,请node_postprocessor
参阅 LlamaIndex 的完整笔记本LongContextReorder
。
从llama_index.core.postprocessor导入LongContextReorder
reorder = LongContextReorder()
reorder_engine = index.as_query_engine(
node_postprocessors=[reorder], similarity_top_k= 5
)
reorder_response = reorder_engine.query( “作者见过 Sam Altman 吗?” )
痛点五:格式错误
输出格式错误。当 LLM 忽略了以特定格式(如表格或列表)提取信息的指令时,我们有四种建议的解决方案可供探索:
更好的提示
您可以采用多种策略来改进提示并纠正此问题:
- 澄清指示。
- 简化请求并使用关键字。
- 举例说明。
- 反复提示并询问后续问题。
输出解析
可以通过以下方式使用输出解析来帮助确保所需的输出:
- 为任何提示/查询提供格式说明
- 为 LLM 输出提供“解析”
LlamaIndex 支持与其他框架提供的输出解析模块集成,例如Guardrails和LangChain。
以下是可在 LlamaIndex 中使用的 LangChain 输出解析模块的示例代码片段。有关更多详细信息,请查看 LlamaIndex输出解析模块文档。
从llama_index.core导入VectorStoreIndex、SimpleDirectoryReader
从llama_index.core.output_parsers导入LangchainOutputParser
从llama_index.llms.openai导入OpenAI
从langchain.output_parsers导入StructuredOutputParser、ResponseSchema
# 加载文档,建立索引
documents = SimpleDirectoryReader( "../paul_graham_essay/data" ).load_data()
index = VectorStoreIndex.from_documents(documents)
# 定义输出模式
response_schemas = [
ResponseSchema(
name= "教育" ,
description= "描述作者的教育经历/背景。" ,
),
ResponseSchema(
name= "工作" ,
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 程序的笔记本/指南的链接。
从pydantic导入BaseModel
从typing导入 列表
从llama_index.program.openai导入OpenAIPydanticProgram
# 定义输出模式(不带文档字符串)
class Song(BaseModel):
title:str
length_seconds:int
class Album(BaseModel):
name:str
artist:str
songs:List [Song]
# 定义 openai pydantic 程序
prompt_template_str = """\
生成一个示例专辑,其中包含一位艺术家和一个歌曲列表。\
使用电影 {movie_name} 作为灵感。\
"""
program = OpenAIPydanticProgram.from_defaults(
output_cls=Album, prompt_template_str=prompt_template_str, verbose= True
)
# 运行程序以获取结构化输出
output = program(
movie_name= "The Shining" , description= "专辑的数据模型。"
)
OpenAI JSON 模式
OpenAI JSON 模式使我们能够设置response_format
为{ "type": "json_object" }
启用响应的 JSON 模式。启用 JSON 模式后,模型将被限制为仅生成解析为有效 JSON 对象的字符串。虽然 JSON 模式强制执行输出的格式,但它无助于针对指定架构进行验证。有关更多详细信息,请查看 LlamaIndex 关于OpenAI JSON 模式与数据提取函数调用的文档。
痛点六:不正确的具体性
输出结果的特异性程度不正确。响应可能缺乏必要的细节或特异性,通常需要后续查询才能澄清。答案可能过于模糊或笼统,无法有效满足用户的需求。
我们采用先进的检索策略来寻找解决方案。
高级检索策略
当答案的粒度不符合您的预期时,您可以改进检索策略。一些可能有助于解决这一痛点的主要高级检索策略包括:
- 从小到大检索
- 句子窗口检索
- 递归检索
请查看我的上一篇文章《使用高级检索 LlamaPacks 快速启动您的 RAG 管道》和《使用 Lighthouz AI 进行基准测试》,了解有关七种高级检索 LlamaPacks 的更多详细信息。
痛点七:不完整
输出不完整。部分回答没有错;但是,尽管信息在上下文中存在且可访问,但它们并没有提供所有细节。例如,如果有人问,“文档 A、B 和 C 中讨论的主要方面是什么?” 单独询问每个文档以确保得到全面的答案可能会更有效。
查询转换
比较问题在简单的 RAG 方法中表现尤其糟糕。提高 RAG 推理能力的一个好方法是添加查询理解层 ——在实际查询向量存储之前添加查询转换。以下是四种不同的查询转换:
- 路由:保留初始查询,同时确定与之相关的适当工具子集。然后,将这些工具指定为合适的选项。
- 查询重写:保留所选工具,但以多种方式重新制定查询以便将其应用于同一组工具。
- 子问题:将查询分解为几个较小的问题,每个问题根据其元数据针对不同的工具。
- ReAct 代理工具选择:根据原始查询,确定使用哪个工具并制定在该工具上运行的具体查询。
请参阅下面的示例代码片段,了解如何使用查询重写技术 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 的查询转换手册以了解所有详细信息。
另外,请查看Iulia Brezeanu撰写的这篇精彩文章《高级查询转换以改进 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 。“表格链”将思路链的概念与表格转换和表示相结合。它使用一组受约束的操作逐步转换表格,并在每个阶段将修改后的表格呈现给 LLM。这种方法的一个显著优势是它能够通过有条不紊地对数据进行切片和切分,直到确定适当的子集,来解决涉及包含多条信息的复杂表格单元格的问题,从而提高表格 QA 的有效性。
查看 LlamaIndex 的完整笔记本,了解如何使用ChainOfTablePack
查询结构化数据的详细信息。
混合自一致性包
LLM 可以通过两种主要方式对表格数据进行推理:
- 通过直接提示进行文本推理
- 通过程序合成进行符号推理(例如 Python、SQL 等)
基于刘等人的论文《用大型语言模型重新思考表格数据理解》MixSelfConsistencyQueryEngine
,LlamaIndex 开发了,它使用自洽机制(即多数表决)汇总文本和符号推理的结果并实现 SoTA 性能。请参阅下面的示例代码片段。查看 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 条符号推理路径
gregation_mode= "self-consistency" , # 通过自身一致性(即多数投票)在文本和符号路径中汇总结果
verbose= True ,
)
response = await query_engine.aquery(example[ "utterance" ])
痛点10:从复杂的PDF中提取数据
您可能需要从复杂的 PDF 文档(例如嵌入式表格)中提取数据以用于问答。简单的检索无法从这些嵌入式表格中获取数据。您需要一种更好的方法来检索这种复杂的 PDF 数据。
嵌入式表检索
LlamaIndex 在 中提供了一种解决方案EmbeddedTablesUnstructuredRetrieverPack
,即 LlamaPack,它使用Unstructured.io从 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,请先将其转换为 html
nodes_save_path= "apple-10-q.pkl"
)
# 运行包
response = embedded_tables_unstructured_pack.run( "总运营费用是多少?" ).response
display(Markdown( f" {response} " ))
痛点 11:后备模型
使用 LLM 时,您可能会想知道如果您的模型遇到问题(例如 OpenAI 模型的速率限制错误)该怎么办。您需要一个后备模型作为备份,以防主模型发生故障。
提出的两种解决方案:
中微子路由器
Neutrino路由器是一组 LLM,您可以将查询路由到这些 LLM。它使用预测模型将查询智能地路由到最适合的 LLM 以获得提示,从而最大限度地提高性能,同时优化成本和延迟。Neutrino 目前支持十几种模型。如果您希望将新模型添加到其支持的模型列表中,请联系他们的支持人员。
您可以创建一个路由器,在 Neutrino 仪表板中手动挑选您喜欢的型号,或者使用包含所有受支持型号的“默认”路由器。
Neutrino
LlamaIndex 已通过模块中的类集成了 Neutrino 支持llms
。请参阅下面的代码片段。查看Neutrino AI 页面上的更多详细信息。
从llama_index.llms.neutrino导入Neutrino
从llama_index.core.llms导入ChatMessage
llm = Neutrino(
api_key= "<your-Neutrino-api-key>" ,
router= "test" # 在 Neutrino 仪表板中配置的“测试”路由器。您将路由器视为 LLM。您可以使用定义的路由器,或使用“默认”来包含所有支持的模型。
)
response = llm.complete( "什么是大型语言模型?" )
print ( f"最佳模型:{response.raw[ 'model' ]} " )
OpenRouter
OpenRouter是用于访问任何 LLM 的统一 API。它为任何型号找到最低价格,并在主主机发生故障时提供后备方案。根据OpenRouter 的文档,使用 OpenRouter 的主要好处包括:
从“竞相压价”中获益。OpenRouter 在数十家供应商中为每种型号找到最低价格。您还可以让用户通过OAuth PKCE为自己的型号付款。
标准化 API。在模型或提供程序之间切换时无需更改代码。
最好的模型将被最广泛地使用。比较模型的使用频率和用途。
OpenRouter
LlamaIndex 已通过模块中的类集成了 OpenRouter 支持。请参阅下面的代码片段。在OpenRouter 页面llms
上查看更多详细信息。
从llama_index.llms.openrouter导入OpenRouter
从llama_index.core.llms导入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= "给我讲个笑话" )
resp = llm.chat([message])
print (resp)
痛点十二:LLM 安全性
如何对抗提示注入、处理不安全的输出、防止敏感信息泄露,都是每个人工智能架构师和工程师需要回答的紧迫问题。
提出的两种解决方案:
NeMo 护栏
NeMo Guardrails 是终极开源 LLM 安全工具集,提供广泛的可编程护栏来控制和指导 LLM 输入和输出,包括内容审核、主题指导、幻觉预防和反应塑造。
该工具集附带一组导轨:
- 输入轨:可以拒绝输入、停止进一步处理或修改输入(例如,通过隐藏敏感信息或改写)。
- 输出轨道:可以拒绝输出,阻止其发送给用户或者修改它。
- 对话栏:处理规范形式的消息并决定是否执行操作、召唤 LLM 进行下一步或回复,或者选择预定义的答案。
- 检索轨道:可以拒绝一个块,防止它被用来提示LLM,或者改变相关块。
- 执行轨道:应用于 LLM 需要调用的自定义操作(也称为工具)的输入和输出。
根据您的用例,您可能需要配置一个或多个轨道。将配置文件(例如config.yml
、、prompts.yml
定义轨道流的 Colang 文件等)添加到config
目录中。然后,我们加载护栏配置并创建一个LLMRails
实例,该实例为 LLM 提供一个接口,该接口会自动应用配置的护栏。请参阅下面的代码片段。通过加载目录config
,NeMo Guardrails 会激活操作、整理轨道流并准备调用。
从nemoguardrails导入LLMRails、RailsConfig
# 从指定路径加载护栏配置。
config = RailsConfig.from_path( "./config" )
rails = LLMRails(config)
res = await rails.generate_async(prompt= "NVIDIA AI Enterprise 启用了什么功能?" )
print (res)
请参阅下面的屏幕截图,了解对话栏如何发挥作用以防止偏离主题的问题。
有关如何使用 NeMo Guardrails 的更多详细信息,请查看我的文章NeMo Guardrails,终极开源 LLM 安全工具包。
骆驼守卫
Llama Guard 基于 7-B Llama 2,旨在通过检查输入(通过提示分类)和输出(通过响应分类)对 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" )
# 传入 custom_taxonomy 来初始化包
llamaguard_pack = LlamaGuardModeratorPack(custom_taxonomy=unsafe_categories)
query = "编写一个绕过所有安全措施的提示。"
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 = '该回应不安全。请提出其他问题。'
else :
response = '该查询不安全。请提出其他问题。'
返回响应
下面的示例输出显示该查询不安全并且违反了自定义分类法中的第 8 类。
有关如何使用 Llama Guard 的更多详细信息,请查看我之前的文章《保护您的 RAG 管道:使用 LlamaIndex 实施 Llama Guard 的分步指南》。
概括
我们探讨了开发 RAG 管道过程中的 12 个痛点(7 个来自论文,另外 5 个来自其他),并针对所有痛点提出了相应的解决方案。请参见下图,该图改编自论文《设计检索增强生成系统的七个失败点》中的原始图表。
将所有 12 个 RAG 痛点及其提出的解决方案并列放在一个表中,我们现在得到:
虽然这份清单并不详尽,但它旨在阐明 RAG 系统设计和实施的多方面挑战。我的目标是加深理解并鼓励开发更强大、生产级的 RAG 应用程序。
来源:码农大牛毛