基於檢索增強生成 (RAG) 的多工具編排旨在建立智慧工作流,該工作流使用大型語言模型 (LLM) 和工具(包括網路搜尋引擎或向量資料庫)來響應查詢。透過這種方式,LLM 將自動動態地選擇用於每個查詢的工具。例如,網路搜尋工具將開啟當前更新資訊的域,而向量資料庫(如 Pinecone)將開啟上下文特定資訊。
在實踐中,RAG 通常需要定義函式呼叫工具(例如網路搜尋或資料庫查詢),並透過 API(例如 Responses API 或 OpenAI)來編排這些工具。這種用法會為每個使用者查詢啟動一系列檢索和生成步驟。因此,模型能力的各個方面與當前資訊交織在一起。
什麼是RAG?
RAG 是指語言模型使用檢索到的相關外部資訊並將其整合到輸出中的過程。因此,RAG 模型並非完全依賴內部訓練資料的“閉卷”模型,而是執行明確的檢索步驟。它會瀏覽一組文件(例如向量資料庫或搜尋索引),並使用這些檢索到的文件來增強 LLM 的提示。
目的是提取 LLM 所需的知識,以便對查詢提供準確的響應。這樣,我們可以將這個過程視為即時的“增強”生成。LLM 能夠利用生成功能和透過檢索增強的資訊,為查詢提供與上下文相關的準確答案。透過這樣做,LLM 能夠使用在訓練時不具備的準確、最新、領域特定或專有的知識來回答問題。
RAG 的主要優勢:
- 最新且領域特定的知識:RAG 允許模型訪問新的非靜態訓練資料(例如,當前新聞、內部文件)來回答查詢。
- 降低幻讀率:由於模型是基於實際檢索到的事實進行回答,因此 RAG 可以最大限度地減少幻讀。
- 可驗證性:答案可以引用或顯示檢索到內容的來源,從而提高答案的透明度和可信度。
RAG 允許 LLM 將生成能力與知識檢索功能相結合。在 RAG 方法中,模型在給出答案之前會從外部語料庫中檢索相關的資訊片段,然後利用這些上下文生成更準確、更合理的答案。
為什麼在RAG中使用工具?
網頁搜尋和向量索引查詢等工具對 RAG 至關重要,因為它們提供了 LLM 自身無法提供的檢索功能。新增這些工具後,RAG 可以消除僅依賴 LLM 服務所帶來的問題。例如,LLM 具有知識截止值,可以自信地生成不正確或過時的資訊。搜尋工具允許系統自動獲取最新的按需事實。同樣,像 Pinecone 這樣的向量資料庫儲存了特定領域和專有資料,例如醫生記錄、公司政策等,而這些資料模型原本無法獲取。
每種工具都有其優勢,而使用多種工具的組合就是多工具協同工作。例如,通用網頁搜尋工具可以回答高階問題。像 PineconeSearchDocuments 這樣的工具可以在包含專有資訊集知識的內部向量儲存庫中找到正確的相關條目。它們共同確保無論模型的答案是什麼,都可以在原始檔或任何質量最佳的地方找到它。一般性問題可以透過功能齊全的內建工具(例如網頁搜尋)來處理。“非常具體”的問題或醫學問題,需要利用系統內部的知識,則可以透過從向量資料庫中檢索上下文來解決。總而言之,在 RAG 流程中使用多工具可以提高效度、待校正資料以及準確性和同期上下文。
使用RAG建立多工具編排
現在,我們將透過一個實際示例,使用醫學問答資料集建立一個多工具 RAG 系統。具體過程是,我們將一個問答資料集嵌入到 Pinecone 中並建立一個系統。該模型包含一個網頁搜尋工具和一個基於 Pinecone 的搜尋工具。以下是此過程的一些步驟和程式碼示例。
載入依賴項和資料集
首先,我們將安裝、匯入必要的庫,最後下載資料集。這需要您對資料處理、嵌入和 Pinecone SDK 有基本的瞭解。例如:
import os, time, random, string import pandas as pd from tqdm.auto import tqdm from sentence_transformers import SentenceTransformer from pinecone import Pinecone, ServerlessSpec import openai from openai import OpenAI import kagglehub
接下來,我們將下載並載入一個包含醫學問答關係的資料集。在程式碼中,我們使用 Kagglehub 實用程式訪問了一個以醫學為重點的 QA 資料集:
path = kagglehub.dataset_download("thedevastator/comprehensive-medical-q-a-dataset") DATASET_PATH = path # local path to downloaded files df = pd.read_csv(f"{DATASET_PATH}/train.csv")
對於此示例版本,我們可以取一個子集,即前 2500 行。接下來,我們將在列前新增“Question:”和“Answer:”作為字首,並將它們合併為一個文字字串。這將是我們要嵌入的上下文。我們將使用文字進行嵌入。例如:
df = df[:2500] df['Question'] = 'Question: ' + df['Question'] df['Answer'] = ' Answer: ' + df['Answer'] df['merged_text'] = df['Question'] + df['Answer']
合併後的文字行如下:“問題:[醫學問題] 答案:[答案]”
問題:誰有患淋巴細胞性脈絡叢腦膜炎 (LCM) 的風險?
答案:接觸受感染齧齒動物的新鮮尿液、糞便、唾液或築巢材料後,可能會發生淋巴細胞性脈絡叢腦膜炎 (LCMV) 感染。當這些物質直接進入破損的皮膚、鼻子、眼睛或口腔,或者可能透過受感染齧齒動物的咬傷而發生傳播。目前尚無人際傳播的報道,除了從受感染的母親到胎兒的垂直傳播,以及罕見的透過器官移植傳播的情況。
基於資料集建立Pinecone索引
現在資料集已載入,我們將為每個合併的問答字串生成向量嵌入。我們將使用句子轉換器模型“BAAI/bge-small-en”對文字進行編碼:
MODEL = SentenceTransformer("BAAI/bge-small-en") embeddings = MODEL.encode(df['merged_text'].tolist(), show_progress_bar=True) df['embedding'] = list(embeddings)
我們將從單個樣本 len(embeddings[0])
中獲取嵌入維數。在我們的例子中,它是 384。然後,我們將建立一個新的 Pinecone 索引並賦予維數。這是使用 Pinecone Python 客戶端完成的:
def upsert_to_pinecone(df, embed_dim, model, api_key, region="us-east-1", batch_size=32): # Initialize Pinecone and create the index if it doesn't exist pinecone = Pinecone(api_key=api_key) spec = ServerlessSpec(cloud="aws", region=region) index_name = 'pinecone-index-' + ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) if index_name not in pinecone.list_indexes().names(): pinecone.create_index( index_name=index_name, dimension=embed_dim, metric='dotproduct', spec=spec ) # Connect to index index = pinecone.Index(index_name) time.sleep(2) print("Index stats:", index.describe_index_stats()) # Upsert in batches for i in tqdm(range(0, len(df), batch_size), desc="Upserting to Pinecone"): i_end = min(i + batch_size, len(df)) # Prepare input and metadata lines_batch = df['merged_text'].iloc[i:i_end].tolist() ids_batch = [str(n) for n in range(i, i_end)] embeds = model.encode(lines_batch, show_progress_bar=False, convert_to_numpy=True) meta = [ { "Question": record.get("Question", ""), "Answer": record.get("Response", "") } for record in df.iloc[i:i_end].to_dict("records") ] # Upsert to index vectors = list(zip(ids_batch, embeds, meta)) index.upsert(vectors=vectors) print(f"Upsert complete. Index name: {index_name}") return index_name
這就是將我們的資料匯入 Pinecone 的過程;用 RAG 的術語來說,這相當於將外部權威知識載入到向量儲存中。索引建立完成後,我們會將所有嵌入連同後設資料(原始問答文字)一起批次更新插入,以供檢索:
index_name = upsert_to_pinecone( df=df, embed_dim=384, model=MODEL, api_key="your-pinecone-api-key" )
這裡,每個向量都與其文字和後設資料一起儲存。Pinecone 索引現在已填充我們特定領域的資料集。
查詢Pinecone索引
為了使用索引,我們定義了一個函式,可以透過傳入新問題來呼叫該函式。該函式嵌入查詢文字,並呼叫 index.query
返回最相似的前 k 個文件:
def query_pinecone_index(index, model, query_text): query_embedding = model.encode(query_text, convert_to_numpy=True).tolist() res = index.query(vector=query_embedding, top_k=5, include_metadata=True) print("--- Query Results ---") for match in res['matches']: question = match['metadata'].get("Question", 'N/A') answer = match['metadata'].get("Answer", "N/A") print(f"{match['score']:.2f}: {question} - {answer}") return res
例如,如果我們呼叫 query_pinecone_index(index, MODEL, "What is the most common treatment for diabetes?")
,我們將看到從資料集中列印出最匹配的問答對。這是流程的檢索部分:使用者查詢被嵌入,查詢索引,並返回最接近的文件(以及它們的後設資料)。檢索到這些上下文後,我們就可以使用它們來幫助構建最終答案。
協調多工具呼叫
接下來,我們定義模型可以使用的工具。在此流水線中,我們定義了兩個工具。一個是網頁搜尋預覽,它是一個通用的網頁搜尋工具,用於從開放網際網路中搜尋事實。另一個是 PineconeSearchDocuments
,用於對我們的 Pinecone 索引執行語義搜尋。每個工具都定義為一個 JSON 物件,其中包含名稱、描述和預期引數。以下是示例:
步驟 1:定義網頁搜尋工具
該工具使代理能夠透過輸入自然語言請求來執行網頁搜尋。有可選的位置後設資料,可以增強使用者相關性的具體性(例如,特定於該地區的新聞、服務)。
web_search_tool = { "type": "function", "name": "web_search_preview", "function": { "name": "web_search_preview", "description": "Perform a web search for general queries.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "The search query string" }, "user_location": { "type": "object", "properties": { "country": {"type": "string", "default": "IN"}, "region": {"type": "string", "default": "Delhi"}, "city": {"type": "string", "default": "New Delhi"} }}}, "required": ["query"] }} }
因此,當代理需要當前資訊或訓練資料中未包含的資訊時,可以使用它。
步驟 2:定義Pinecone搜尋工具
此工具使代理能夠在向量資料庫(例如 Pinecone)上進行語義搜尋,從而使 RAG 系統能夠依賴向量之間的點積和角度等語義。
該工具接受查詢,並基於向量嵌入返回最相似的文件。
pinecone_tool = { "type": "function", "name": "PineconeSearchDocuments", "function": { "name": "PineconeSearchDocuments", "description": "Search for relevant documents based on the user’s question in the vector database.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "The question to search in the vector database." }, "top_k": { "type": "integer", "description": "Number of top results to return.", "default": 3 } }, "required": ["query"], "additionalProperties": False } } }
當代理需要從包含嵌入上下文的文件中檢索特定上下文時,就會使用這種方法。
步驟 3:合併工具
現在我們將兩個工具合併成一個列表,並將其傳遞給代理。
tools = [web_search_tool, pinecone_tool]
因此,每個工具都包含一個定義,用於定義我們的模型在呼叫時應該傳入哪些引數。例如,Pinecone 搜尋工具需要自然語言查詢字串,該工具會在內部返回索引中排名前 K 的匹配文件。
除了該工具之外,我們還將包含一組需要處理的使用者查詢。對於每個查詢,模型將確定是呼叫工具還是直接響應。例如:
queries = [ {"query": "Who won the cricket world cup in 1983?"}, {"query": "What is the most common cause of death in India?"}, {"query": "A 7-year-old boy with sickle cell disease has knee and hip pain... What is the next step in management according to our internal knowledge base?"} ]
流程中的多工具編排
最後,我們執行對話流程,讓模型代表它們控制工具。我們為模型提供一個系統提示,指導它按特定順序使用這些工具。在此示例中,我們的提示告訴模型:“當出現問題時,首先呼叫網路搜尋工具,然後呼叫 PineconeSearchDocuments
”:
system_prompt = ( "Every time it's prompted with a question, first call the web search tool for results, " "then call `PineconeSearchDocuments` to find relevant examples in the internal knowledge base." )
我們收集訊息並使用針對使用者的每個查詢啟用的工具呼叫響應 API:
for item in queries: input_messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": item["query"]} ] response = openai.responses.create( model="gpt-4o-mini", input=input_messages, tools=tools, parallel_tool_calls=True )
輸出:
API 返回一條輔助訊息,其中可能包含也可能不包含工具呼叫。我們會檢查 response.output
以檢視模型是否呼叫了任何工具。如果呼叫了,我們會執行這些呼叫並將結果包含在對話中。例如,如果模型呼叫了 PineconeSearchDocuments
,我們的程式碼會在內部執行 query_pinecone_index(index, MODEL, query)
,獲取文件答案,並返回包含此資訊的工具響應訊息。最後,我們將重新整理後的對話傳送回模型以獲取最終響應。
上述流程展示了多工具編排的工作原理;模型會動態選擇與查詢相關的工具。正如示例所示,對於像“什麼是哮喘?”這樣的一般性問題,它可以使用網路搜尋工具,但對於與“哮喘”更具體的聯絡的問題,可能需要使用 Pinecone 上下文,並在此基礎上進行構建。
我們在程式碼迴圈中進行多次工具呼叫,所有呼叫完成後,我們呼叫 API,讓模型根據收到的提示構建“最終”答案。總的來說,我們希望收到一個答案,該答案將來自網路知識的外部事實與來自內部知識文件的上下文結合起來,並根據我們的指示進行確認。
您可以在此處參考完整程式碼。
小結
使用 RAG 的多工具編排功能,可以建立一個功能強大且選項豐富的問答系統。將模型生成與檢索工具結合使用,使我們能夠充分利用模型的自然語言理解能力和外部資料集的事實準確性。在我們的用例中,我們對一個醫學問答的 Pinecone 向量索引進行了地面實況測試,並能夠將網路搜尋或該索引作為選項呼叫。透過這種方式,我們的模型能夠更加基於實際資料,並能夠回答原本無法回答的問題。
在實踐中,這種 RAG 流程能夠提供更高的答案准確性和相關性,因為該模型可以引用最新的來源,涵蓋細分領域知識,並最大限度地減少幻覺。未來的迭代可能會包含更高階的檢索模式或生態系統中的其他工具,例如使用知識圖譜或 API,但核心部分無需進行任何更改。
常見問題
問題 1:RAG 與傳統 LLM 相比最大的優勢是什麼?
答:RAG 允許 LLM 訪問外部資料來源(例如向量資料庫或 Web),從而生成更準確、更及時、更針對特定領域的響應,而這是傳統的“閉卷”模型無法實現的。
問題 2:RAG 流程中最常用的工具有哪些?
答:通常,常用工具包括:– 用於語義檢索的向量資料庫(例如 Pinecone、FAISS 或 Weaviate)。– 使用 API 進行即時 Web 資訊搜尋。– 提供知識圖譜、SQL 資料庫或文件儲存查詢功能的自定義 API 或函式。
問題 3:RAG 可以用於聊天機器人等即時應用程式嗎?
答:可以。RAG 非常適合需要動態、基於事實的答案的應用程式,例如客服機器人、醫療或財務助理。因為這些答案基於可檢索的文件或事實。
評論留言