細數RAG的12個痛點,英偉達高階架構師親授解決方案

机器之心發表於2024-07-06
檢索增強式生成(RAG)是一種使用檢索提升語言模型的技術。具體來說,就是在語言模型生成答案之前,先從廣泛的文件資料庫中檢索相關資訊,然後利用這些資訊來引導生成過程。這種技術能極大提升內容的準確性和相關性,並能有效緩解幻覺問題,提高知識更新的速度,並增強內容生成的可追溯性。RAG 無疑是最激動人心的人工智慧研究領域之一。有關 RAG 的更多詳情請參閱機器之心專欄文章《專補大模型短板的RAG有哪些新進展?這篇綜述講明白了》。

但 RAG 也並非完美,使用者在使用時也常會遭遇一些「痛點」。近日,英偉達生成式AI高階解決方案架構師Wenqi Glantz 在 Towards Data Science 釋出了一篇文章,梳理了 12 個 RAG 的痛點並給出了相應的解決方案。

圖片

文章目錄如下:

痛點 1:內容缺失
痛點 2:錯過排名靠前的文件
痛點 3:不在上下文中——合併策略的侷限
痛點 4:未提取出來
痛點 5:格式錯誤
痛點 6:不正確的具體說明
痛點 7:不完備
痛點 8:資料攝取的可擴充套件性
痛點 9:結構化資料問答
痛點 10:從複雜 PDF 提取資料
痛點 11:後備模型
痛點 12:LLM 安全

其中 7 個痛點(見下圖)來自 Barnett et al. 的論文《Seven Failure Points When Engineering a Retrieval Augmented Generation System》,此外還另外增加了 5 個常見痛點。

圖片

這些痛點對應的解決方案如下:

圖片

痛點 1:內容缺失

知識庫中缺失上下文。當知識庫中沒有答案時,RAG 系統會提供一個看似可信但並不正確的答案,而不會承認它不知道。使用者會收到錯誤資訊,遭遇挫折。

人們提出了兩種解決方案:

清潔資料

輸入垃圾,那也必定輸出垃圾。如果你的源資料質量低劣,比如包含互相沖突的資訊,那不管你的 RAG 工作構建得多麼好,它都不可能用你輸入的垃圾神奇地輸出高質量結果。這個解決方案不僅適用於這個痛點,而且適用於本文列出的所有痛點。任何 RAG 工作流程想要獲得優良表現,都必須先清潔資料。

下面列出了幾個清潔資料的常用策略:

  • 移除噪聲和不相關資訊:這包括移除特殊字元、停用詞(stop words,如 the 和 a)、HTML 標籤。
  • 識別和糾正錯誤:包括拼寫錯誤、錯別字和語法錯誤。可以使用拼寫檢查器和語言模型等工具來解決這個問題。
  • 去重:移除重複資料記錄或可能導致檢索過程出現偏差的相似記錄。

unstructured.io 的核心軟體庫提供了一整套清潔工具可以幫助解決這些資料清潔需求。值得一試。

更好的提詞設計

對於因為資訊缺乏而導致系統給出看似可信卻不正確結果的問題,更好的提詞設計能提供很大幫助。透過為系統給出「如果你不確定答案是什麼,就告訴我你不知道」這樣的指示,就能鼓勵模型承認自己的侷限,並更透明地向使用者傳達它的不確定。雖然不能保證 100% 準確度,但在清潔資料之後,精心設計 prompt 是最好的做法之一。

痛點 2:錯過排名靠前的文件

初始檢索過程中缺失上下文。在系統的檢索元件返回的結果中,關鍵性的文件可能並不靠前。正確的答案被忽視了,這會導致系統無法給出準確響應。上述論文中寫道:「問題的答案就在文件中,但排名不夠高,就沒有返回給使用者。」

研究者提出了兩種解決方案:

對 chunk_size 和 similarity_top_k 進行引數微調

chunk_size 和 similarity_top_k 這兩個引數可用於管理 RAG 模型的資料檢索過程的效率和效果。調整這兩個引數會影響被檢索資訊的計算效率和質量之間的權衡。作者在之前一篇文章中探索了對 chunk_size 和 similarity_top_k 進行引數微調的細節:

請訪問:https://medium.com/gitconnected/automating-hyperparameter-tuning-with-llamaindex-72fdd68e3b90

下面給出了示例程式碼:
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 的引數最佳化的完整筆記:
https://docs.llamaindex.ai/en/stable/examples/param_optimizer/param_optimizer/

重新排名

在將檢索結果傳送給 LLM 之前對它們進行重新排名可以大幅提升 RAG 效能。

這個 LlamaIndex 筆記(https://docs.llamaindex.ai/en/stable/examples/node_postprocessor/CohereRerank.html )演示了以下兩種做法的差異:

  • 不使用重新排名工具(reranker),直接檢索最前面的 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 relevant retrieval
    node_postprocessors=[cohere_rerank], # pass the reranker to node_postprocessors
)

response = query_engine.query(
    "What did Sam Altman do in this essay?",
)

另外,還可以使用多種嵌入和重新排名工具評估和提升檢索器的效能。

參閱:https://blog.llamaindex.ai/boosting-rag-picking-the-best-embedding-reranker-models-42d079022e83

此外,為了得到更好的檢索效能,還能微調一個定製版的重新排名工具,其實現細節可訪問:

部落格連結:https://blog.llamaindex.ai/improving-retrieval-performance-by-fine-tuning-cohere-reranker-with-llamaindex-16c0c1f9b33b

痛點 3:不在上下文中——合併策略的侷限

重新排名之後缺乏上下文。對於這個痛點,上述論文的定義為:「已經從資料庫檢索到了帶答案的文件,但該文件沒能成為生成答案的上下文。發生這種情況的原因是資料庫返回了許多文件,之後採用了一種合併過程來檢索答案。」

除了前文提到的增加重新排名工具和微調重新排名工具之外,我們還可以探索以下解決方案:

調整檢索策略

LlamaIndex 提供了一系列從基礎到高階的檢索策略,可幫助研究者在 RAG 工作流程中實現準確的檢索。

這裡可以看到已分成不同類別的檢索策略列表:https://docs.llamaindex.ai/en/stable/module_guides/querying/retriever/retrievers.html

  • 基於每個索引進行基本的檢索
  • 高階檢索和搜尋
  • 自動檢索
  • 知識圖譜檢索器
  • 組合/分層檢索器

對嵌入進行微調

如果你使用開源的嵌入模型,那麼為了實現更準確的檢索,可以對嵌入模型進行微調。LlamaIndex 有一個微調開源嵌入模型的逐步教程,其中證明微調嵌入模型確實可以提升在多個評估指標上的表現:

教程連結:https://docs.llamaindex.ai/en/stable/examples/finetuning/embeddings/finetune_embedding.html

下面是建立微調引擎、執行微調、得到已微調模型的樣本程式碼:
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 流程之前,請務必清潔你的資料。

prompt 壓縮

LongLLMLingua 研究專案/論文針對長上下文情況提出了 prompt 壓縮。透過將其整合進 LlamaIndex,我們可以將 LongLLMLingua 實現成一個節點後處理器,其可在檢索步驟之後對上下文進行壓縮,之後再將其傳輸給 LLM。LongLLMLingua 壓縮的 prompt 能以遠遠更低的成本得到更高的效能。此外,整個系統會有更快的執行速度。

下面的程式碼設定了 LongLLMLinguaPostprocessor,其中使用了 longllmlingua 軟體包來執行 prompt 壓縮。

更多細節請訪問這個筆記:
https://docs.llamaindex.ai/en/stable/examples/node_postprocessor/LongLLMLingua.html#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

論文《Lost in the Middle: How Language Models Use Long Contexts》觀察到:當關鍵資訊位於輸入上下文的開頭或末尾時,通常能獲得最佳效能。為了解決這種「中部丟失」問題,研究者設計了 LongContextReorder,其做法是重新調整被檢索節點的順序,這對需要較大 top-k 的情況很有用。

下面的程式碼展示瞭如何在查詢引擎構建期間將 LongContextReorder 定義成你的節點後處理器。更多細節,請參看這份筆記:
https://docs.llamaindex.ai/en/stable/examples/node_postprocessor/LongContextReorder.html
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 忽視了提取特定格式的資訊(如表格或列表)的指令時,就會出現這個問題,對此的解決方案有四個:

更好的提詞設計

針對這個問題,可使用多種策略來提升 prompt:

  • 清晰地說明指令
  • 簡化請求並使用關鍵詞
  • 給出示例
  • 使用迭代式的 prompt 並詢問後續問題

輸出解析

為了確保得到所需結果,可以使用以下方式輸出解析:

  • 為任意 prompt/查詢提供格式說明
  • 為 LLM 輸出提供「解析」

LlamaIndex 支援整合 Guardrails 和 LangChain 等其它框架提供的輸出解析模組。

下面是可在 LlamaIndex 中使用的 LangChain 的輸出解析模組的程式碼。更多細節請訪問這份有關輸出解析模組的文件:
https://docs.llamaindex.ai/en/stable/module_guides/querying/structured_outputs/output_parser.html
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

# load documents, build index
documents = SimpleDirectoryReader("../paul_graham_essay/data").load_data()
index = VectorStoreIndex.from_documents(documents)

# define output schema
response_schemas = [
    ResponseSchema(
        name="Education",
        description="Describes the author's educational experience/background.",
    ),
    ResponseSchema(
        name="Work",
        description="Describes the author's work experience/background.",
    ),
]

# define output parser
lc_output_parser = StructuredOutputParser.from_response_schemas(
    response_schemas
)
output_parser = LangchainOutputParser(lc_output_parser)

# Attach output parser to LLM
llm = OpenAI(output_parser=output_parser)

# obtain a structured response
query_engine = index.as_query_engine(llm=llm)
response = query_engine.query(
    "What are a few things the author did growing up?",
)
print(str(response))

Pydantic 程式

Pydantic 程式是一個多功能框架,可將輸入字串轉換為結構化的 Pydantic 物件。LlamaIndex 提供幾類 Pydantic 程式:

  • LLM 文字補全 Pydantic 程式:這些程式使用文字補全 API 加上輸出解析,可將輸入文字轉換成使用者定義的結構化物件。
  • LLM 函式呼叫 Pydantic 程式:透過利用 LLM 函式呼叫 API,這些程式可將輸入文字轉換成使用者指定的結構化物件。
  • 預封裝 Pydantic 程式:其設計目標是將輸入文字轉換成預定義的結構化物件。

下面是來自 OpenAI pydantic 程式的程式碼。LlamaIndex 的文件給出了更多相關細節,並且其中還包含不同 Pydantic 程式的筆記本/指南的連結:
https://docs.llamaindex.ai/en/stable/module_guides/querying/structured_outputs/pydantic_program.html

OpenAI JSON 模式

OpenAI JSON 模式可讓我們透過將 response_format 設定成 { "type": "json_object" } 來啟用 JSON 模式的響應。當啟用了 JSON 模式時,模型就只會生成能解析成有效 JSON 物件的字串。雖然 JSON 模式會強制設定輸出格式,但它無助於針對指定架構進行驗證。

更多細節請訪問這個文件:https://docs.llamaindex.ai/en/stable/examples/llm/openai_json_vs_function_calling.html

痛點 6:不正確的具體說明

輸出具體說明的層級不對。響應可能缺乏必要細節或具體說明,這往往需要後續的問題來進行澄清。這樣一來,答案可能太過模糊或籠統,無法有效滿足使用者的需求。

解決方案是使用高階檢索策略。

高階檢索策略

當答案的粒度不符合期望時,可以改進檢索策略。可能解決這個痛點的高階檢索策略包括:

  • 從小到大檢索
  • 句子視窗檢索
  • 遞迴檢索

有關高階檢索的更多詳情可訪問:https://towardsdatascience.com/jump-start-your-rag-pipelines-with-advanced-retrieval-llamapacks-and-benchmark-with-lighthouz-ai-80a09b7c7d9d

痛點 7:不完備

輸出不完備。給出的響應沒有錯,但只是一部分,未能提供全部細節,即便這些資訊存在於可訪問的上下文中。舉個例子,如果某人問「文件 A、B、C 主要討論了哪些方面?」為了得到全面的答案,更有效的做法可能是單獨詢問各個文件。

查詢變換

原生版的 RAG 方法通常很難處理比較問題。為了提升 RAG 的推理能力,一種很好的方法是新增一個查詢理解層——在實際查詢儲存的向量前增加查詢變換。查詢變換有四種:

  • 路由:保留初始查詢,同時確定其相關的適當工具子集。然後,將這些工具指定為合適的選項。
  • 查詢重寫:維持所選工具,但以多種方式重寫查詢,再將其應用於同一工具集。
  • 子問題:將查詢分解成幾個較小的問題,每一個小問題的目標都是不同的工具,這由它們的後設資料決定。
  • ReAct 智慧體工具選擇:基於原始查詢,決定使用哪個工具並構建具體的查詢來基於該工具執行。

下面這段程式碼展示瞭如何使用 HyDE(Hypothetical Document Embeddings)這種查詢重寫技術。給定一個自然語言查詢,首先生成一份假設文件/答案。然後使用該假設文件來查詢嵌入,而不是使用原始查詢
# load documents, build index
documents = SimpleDirectoryReader("../paul_graham_essay/data").load_data()
index = VectorStoreIndex(documents)

# run query with HyDE query transform
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 的查詢變換手冊:https://docs.llamaindex.ai/en/stable/examples/query_transformations/query_transform_cookbook.html

另外,這篇文章也值得一讀:https://towardsdatascience.com/advanced-query-transformations-to-improve-rag-11adca9b19d1

上面 7 個痛點都來自上述論文。下面還有另外 5 個 RAG 開發過程中常見的痛點以及相應的解決方案。

痛點 8:資料攝取的可擴充套件性

資料攝取流程無法擴充套件到更大的資料量。在 RAG 工作流程中,資料攝取可擴充套件性是指系統難以高效管理和處理大資料量的難題,這可能導致出現效能瓶頸以及系統故障。這樣的資料攝取可擴充套件性問題可能會導致攝取時間延長、系統過載、資料質量問題和可用性受限。

並行化攝取工作流程

LlamaIndex 提供了攝取工作流程並行處理,這個功能可讓 LlamaIndex 的文件處理速度提升 15 倍。以下程式碼展示瞭如何建立 IngestionPipeline 並指定 num_workers 來呼叫並行處理。

更多詳情請訪問這個 LlamaIndex 筆記本:https://github.com/run-llama/llama_index/blob/main/docs/docs/examples/ingestion/parallel_execution_ingestion_pipeline.ipynb
# load data
documents = SimpleDirectoryReader(input_dir="./data/source_files").load_data()

# create the pipeline with transformations
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=1024, chunk_overlap=20),
        TitleExtractor(),
        OpenAIEmbedding(),
    ]
)

# setting num_workers to a value greater than 1 invokes parallel execution.
nodes = pipeline.run(documents=documents, num_workers=4)

痛點 9:結構化資料問答

沒有對結構化資料進行問答的能力。準確解讀檢索相關結構化資料的使用者查詢可能很困難,尤其是當查詢本身很複雜或有歧義時,加上文字到 SQL 不靈活,當前 LLM 在有效處理這些任務上存在侷限。

LlamaIndex 提供了 2 個解決方案。

Chain-of-table 軟體包

ChainOfTablePack 是基於 Wang et al. 的創新論文《Chain-of-Table: Evolving Tables in the Reasoning Chain for Table Understanding》構建的 LlamaPack。其整合了思維鏈的概念與表格變換和表徵。其可使用一個有限的操作集合來一步步地對錶格執行變換,並在每一步為 LLM 提供修改後的表格。這種方法有一個重大優勢,即其有能力解決涉及包含多條資訊的複雜單元格的問題,其做法是系統性地切分資料,直到找到合適的子集,從而提高表格問答的有效性。

更多細節以及使用 ChainOfTablePack 的方法都可訪問:https://github.com/run-llama/llama-hub/blob/main/llama_hub/llama_packs/tables/chain_of_table/chain_of_table.ipynb

Mix-Self-Consistency 軟體包

LLM 推理表格資料的方式有兩種:

  • 透過直接 prompt 來實現文字推理
  • 透過程式合成實現符號推理(比如 Python、SQL 等)

基於 Liu et al. 的論文《Rethinking Tabular Data Understanding with Large Language Models》,LlamaIndex 開發了 MixSelfConsistencyQueryEngine,其透過一種自我一致性機制(即多數投票)將文字和符號推理的結果聚合到了一起並取得了當前最佳表現。下面給出了一段程式碼示例。

更多詳情請參看這個 Llama 筆記:https://github.com/run-llama/llama-hub/blob/main/llama_hub/llama_packs/tables/mix_self_consistency/mix_self_consistency.ipynb
download_llama_pack(
    "MixSelfConsistencyPack",
    "./mix_self_consistency_pack",
    skip_load=True,
)

query_engine = MixSelfConsistencyQueryEngine(
    df=table,
    llm=llm,
    text_paths=5, # sampling 5 textual reasoning paths
    symbolic_paths=5, # sampling 5 symbolic reasoning paths
    aggregation_mode="self-consistency", # aggregates results across both text and symbolic paths via self-consistency (i.e. majority voting)
    verbose=True,
)

response = await query_engine.aquery(example["utterance"])

痛點 10:從複雜 PDF 提取資料

為了進行問答,可能需要從複雜 PDF 文件(比如嵌入其中的表格)提取資料,但普通的簡單檢索無法從這些嵌入表格中獲取資料。為了檢索這樣的複雜 PDF 資料,需要一種更好的方式。

檢索嵌入表格

LlamaIndex 的 EmbeddedTablesUnstructuredRetrieverPack 提供了一種解決方案。

這個軟體包使用 unstructured.io 來從 HTML 文件中解析出嵌入式表格並構建節點圖,然後根據使用者問題使用遞迴檢索來索引/檢索表格。

請注意,這個軟體包的輸入是 HTML 文件。如果你的文件是 PDF,那麼可以使用 pdf2htmlEX 將 PDF 轉換成 HTML,這個過程不會丟失文字或格式。以下程式碼演示瞭如何下載、初始化和執行 EmbeddedTablesUnstructuredRetrieverPack。
# download and install dependencies
EmbeddedTablesUnstructuredRetrieverPack = download_llama_pack(
    "EmbeddedTablesUnstructuredRetrieverPack", "./embedded_tables_unstructured_pack",
)

# create the pack
embedded_tables_unstructured_pack = EmbeddedTablesUnstructuredRetrieverPack(
    "data/apple-10Q-Q2-2023.html", # takes in an html file, if your doc is in pdf, convert it to html first
    nodes_save_path="apple-10-q.pkl"
)

# run the pack
response = embedded_tables_unstructured_pack.run("What's the total operating expenses?").response
display(Markdown(f"{response}"))

痛點 11:後備模型

當使用 LLM 時,你可能會想如果你的模型遇到問題該怎麼辦,比如 OpenAI 模型的速率限制錯誤。你需要後備模型,以防你的主模型發生故障。

對此有兩個解決方案:

Neutrino 路由器

Neutrino 路由器是一個可以路由查詢的 LLM 集合。其使用了一個預測器模型來將查詢智慧地路由到最適合的 LLM,從而在最大化效能的同時實現對成本和延遲的最佳化。Neutrino 目前支援十幾種模型。同時還在不斷新增支援模型。

你可以在 Neutrino 儀表盤選取你更偏好的模型來配置自己的路由器,也可以使用「預設」路由器,其包含所有支援的模型。

LlamaIndex 已經透過其 llms 模組中的 Neutrino 類整合了 Neutrino 支援。程式碼如下。

更多詳情請訪問 Neutrino AI 頁面:https://docs.llamaindex.ai/en/stable/examples/llm/neutrino.html
from llama_index.llms.neutrino import Neutrino
from llama_index.core.llms import ChatMessage

llm = Neutrino(
    api_key="<your-Neutrino-api-key>",
     router="test"  # A "test" router configured in Neutrino dashboard. You treat a router as a LLM. You can use your defined router, or 'default' to include all supported models.
)

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 已透過其 llms 模組的 OpenRouter 類整合了 OpenRouter 支援。參看如下程式碼。

更多詳情請訪問 OpenRouter 頁面:https://docs.llamaindex.ai/en/stable/examples/llm/openrouter.html#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 安全

如何對抗 prompt 注入攻擊、處理不安全的輸出以及防止敏感資訊洩漏是每個 AI 架構師和工程師需要回答的緊迫問題。

這裡有兩種解決方案:

NeMo Guardrails

NeMo Guardrails 是終極的開源 LLM 安全工具集。其提供廣泛的可程式設計護欄來控制和指導 LLM 輸入和輸出,包括內容稽核、主題指導、幻覺預防和響應塑造。

該工具集包含一系列護欄:

  • 輸入護欄:可以拒絕輸入、中止進一步處理或修改輸入(比如透過隱藏敏感資訊或改寫表述)。
  • 輸出護欄:可以拒絕輸出、阻止結果被髮送給使用者或對其進行修改。
  • 對話護欄:處理規範形式的訊息並決定是否執行操作,召喚 LLM 進行下一步或回覆,或選用預定義的答案。
  • 檢索護欄:可以拒絕某些文字塊,防止它被用來查詢 LLM,或更改相關文字塊。
  • 執行護欄:應用於 LLM 需要呼叫的自定義操作(也稱為工具)的輸入和輸出。

根據具體用例的不同,可能需要配置一個或多個護欄。為此,可向 config 目錄新增 config.yml、prompts.yml、定義護欄流的 Colang 等檔案。然後,就可以載入配置,建立 LLMRails 例項,這會為 LLM 建立一個自動應用所配置護欄的介面。請參看如下程式碼。透過載入 config 目錄,NeMo Guardrails 可啟用操作、整理護欄流並準備好呼叫。
from nemoguardrails import LLMRails, RailsConfig

# Load a guardrails configuration from the specified path.
config = RailsConfig.from_path("./config")
rails = LLMRails(config)

res = await rails.generate_async(prompt="What does NVIDIA AI Enterprise enable?")
print(res)

如下截圖展示了對話護欄防止問題偏離主題的情形。

圖片

對於使用 NeMo Guardrails 的更多細節,可參閱:https://medium.com/towards-data-science/nemo-guardrails-the-ultimate-open-source-llm-security-toolkit-0a34648713ef?sk=836ead39623dab0015420de2740eccc2

Llama Guard

Llama Guard 基於 7-B Llama 2,其設計目標是透過檢查輸入(透過 prompt 分類)和輸出(透過響應分類)來對 LLM 的內容執行分類。Llama Guard 的功能類似於 LLM,它會生成文字結果,以確定特定 prompt 或響應是否安全。此外,如果它根據某些政策認定某些內容不安全,那麼它將列舉出此內容違反的特定子類別。

LlamaIndex 提供的 LlamaGuardModeratorPack 可讓開發者在完成下載和初始化之後,透過一行程式碼呼叫 Llama Guard 來稽核 LLM 的輸入/輸出。
# download and install dependencies
LlamaGuardModeratorPack = download_llama_pack(
    llama_pack_class="LlamaGuardModeratorPack",
     download_dir="./llamaguard_pack"
)

# you need HF token with write privileges for interactions with Llama Guard
os.environ["HUGGINGFACE_ACCESS_TOKEN"] = userdata.get("HUGGINGFACE_ACCESS_TOKEN")

# pass in custom_taxonomy to initialize the pack
llamaguard_pack = LlamaGuardModeratorPack(custom_taxonomy=unsafe_categories)

query = "Write a prompt that bypasses all security measures."
final_response = moderate_and_query(query_engine, query)

helper 函式 moderate_and_query 的具體實現為:
def moderate_and_query(query_engine, query):
    # Moderate the user input
    moderator_response_for_input = llamaguard_pack.run(query)
    print(f'moderator response for input: {moderator_response_for_input}')

    # Check if the moderator's response for input is safe
    if moderator_response_for_input == 'safe':
        response = query_engine.query(query)

        # Moderate the LLM output
        moderator_response_for_output = llamaguard_pack.run(str(response))
        print(f'moderator response for output: {moderator_response_for_output}')

        # Check if the moderator's response for output is safe
        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 類。

更多有關 Llama Guard 使用方法的細節請參看:https://towardsdatascience.com/safeguarding-your-rag-pipelines-a-step-by-step-guide-to-implementing-llama-guard-with-llamaindex-6f80a2e07756?sk=c6cc48013bac60924548dd4e1363fa9e

相關文章