通常,你的 AI 代理必須從外部來源擷取資料,才能產生有根據的回應。 若沒有這些額外脈絡,你的 AI 代理可能會產生幻覺或提供錯誤資訊。 為了解決這個問題,你可以使用外掛從外部來源擷取資料。
在考慮用於檢索增強生成(RAG)的外掛時,你應該問自己兩個問題:
- 你(或你的 AI 代理)會如何「搜尋」所需的資料? 你需要語 意搜尋 還是 經典搜尋?
- 你是事先知道 AI 代理需要的資料(預先取得的資料),還是 AI 代理需要 動態擷取資料?
- 你將如何保護資料安全, 防止敏感資訊過度分享?
語意搜尋與經典搜尋
在開發檢索擴增生成(RAG)外掛時,可以使用兩種搜尋方式:語意搜尋與經典搜尋。
語意搜尋
語意搜尋利用向量資料庫,根據查詢的意義與上下文來理解並檢索資訊,而非僅僅是關鍵字匹配。 此方法讓搜尋引擎能掌握語言的細微差異,如同義詞、相關概念及查詢背後的整體意圖。
語意搜尋在使用者查詢複雜、開放式或需要更深入理解內容的環境中表現優異。 例如,搜尋「最適合攝影的智慧型手機」會顯示考量到智慧型手機攝影功能相關情境脈絡的結果,而不只是比對「最適合」、「智慧型手機」和「攝影」這幾個詞。
提供帶有語意搜尋功能的大型語言模型時,通常只需定義一個包含單一搜尋查詢的函式。 LLM 接著會利用這個函式來擷取必要的資訊。 以下是一個使用 Azure AI 搜尋服務 來尋找與特定查詢相似文件的語意搜尋功能的範例。
using System.ComponentModel;
using System.Text.Json.Serialization;
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Embeddings;
public class InternalDocumentsPlugin
{
private readonly ITextEmbeddingGenerationService _textEmbeddingGenerationService;
private readonly SearchIndexClient _indexClient;
public AzureAISearchPlugin(ITextEmbeddingGenerationService textEmbeddingGenerationService, SearchIndexClient indexClient)
{
_textEmbeddingGenerationService = textEmbeddingGenerationService;
_indexClient = indexClient;
}
[KernelFunction("Search")]
[Description("Search for a document similar to the given query.")]
public async Task<string> SearchAsync(string query)
{
// Convert string query to vector
ReadOnlyMemory<float> embedding = await _textEmbeddingGenerationService.GenerateEmbeddingAsync(query);
// Get client for search operations
SearchClient searchClient = _indexClient.GetSearchClient("default-collection");
// Configure request parameters
VectorizedQuery vectorQuery = new(embedding);
vectorQuery.Fields.Add("vector");
SearchOptions searchOptions = new() { VectorSearch = new() { Queries = { vectorQuery } } };
// Perform search request
Response<SearchResults<IndexSchema>> response = await searchClient.SearchAsync<IndexSchema>(searchOptions);
// Collect search results
await foreach (SearchResult<IndexSchema> result in response.Value.GetResultsAsync())
{
return result.Document.Chunk; // Return text from first result
}
return string.Empty;
}
private sealed class IndexSchema
{
[JsonPropertyName("chunk")]
public string Chunk { get; set; }
[JsonPropertyName("vector")]
public ReadOnlyMemory<float> Vector { get; set; }
}
}
經典搜尋
經典搜尋,也稱為屬性導向或條件導向搜尋,依賴於篩選並精確匹配資料集中的詞彙或值。 它在資料庫查詢、庫存搜尋以及任何需要依特定屬性篩選的情況下特別有效。
例如,若使用者想查找特定顧客 ID 下的所有訂單,或在特定價格區間與類別內檢索產品,經典搜尋能提供精確且可靠的結果。 然而,經典搜尋受限於無法理解語境或語言變異。
Tip
在大多數情況下,你現有的服務已經支援經典搜尋。 在實施語意搜尋前,請考慮現有服務是否能為 AI 代理提供必要的上下文。
舉例來說,有一個外掛能利用經典搜尋從CRM系統中擷取客戶資訊。 在這裡,AI 只需用客戶 ID 呼叫 GetCustomerInfoAsync 功能,取得必要的資訊。
using System.ComponentModel;
using Microsoft.SemanticKernel;
public class CRMPlugin
{
private readonly CRMService _crmService;
public CRMPlugin(CRMService crmService)
{
_crmService = crmService;
}
[KernelFunction("GetCustomerInfo")]
[Description("Retrieve customer information based on the given customer ID.")]
public async Task<Customer> GetCustomerInfoAsync(string customerId)
{
return await _crmService.GetCustomerInfoAsync(customerId);
}
}
由於語意查詢的非確定性特性,要用語意搜尋達成相同的搜尋功能,可能不可能或不切實際。
何時分別使用
選擇語意搜尋與經典搜尋取決於查詢的性質。 它非常適合內容密集的環境,如知識庫和客戶支援,使用者可能會用自然語言提問或尋找產品。 另一方面,當精確度和精確匹配很重要時,應該採用經典搜尋。
在某些情況下,您可能需要結合兩種方法,以提供完整的搜尋功能。 例如,協助電子商務商店顧客的聊天機器人可能會使用語意搜尋來理解用戶的查詢,而傳統搜尋則根據價格、品牌或供應狀況等特定屬性來篩選產品。
以下是一個結合語意搜尋與經典搜尋的外掛範例,用以從電子商務資料庫中取得產品資訊。
using System.ComponentModel;
using Microsoft.SemanticKernel;
public class ECommercePlugin
{
[KernelFunction("search_products")]
[Description("Search for products based on the given query.")]
public async Task<IEnumerable<Product>> SearchProductsAsync(string query, ProductCategories category = null, decimal? minPrice = null, decimal? maxPrice = null)
{
// Perform semantic and classic search with the given parameters
}
}
動態與預先擷取資料的資料擷取方式
在開發檢索增強生成(RAG)外掛時,也必須考慮資料檢索過程是靜態還是動態。 這讓你能在必要時才擷取資料,優化 AI 代理的效能。
動態資料檢索
在大多數情況下,使用者的查詢會決定 AI 代理需要取得的資料。 例如,使用者可能會詢問兩種不同產品的差異。 AI 代理接著需要從資料庫或 API 動態擷取產品資訊,並透過 函式呼叫產生回應。 事先取得所有可能的產品資訊並交給 AI 代理是不切實際的。
以下是一個使用者與 AI 代理之間來回對話的範例,此時需要動態資料擷取。
| Role | Message |
|---|---|
| 🔵 使用者 | 你能告訴我最好的床墊嗎? |
| 🔴 助理(函式呼叫) | Products.Search("mattresses") |
| 🟢 工具 | [{"id": 25323, "name": "Cloud Nine"},{"id": 63633, "name": "Best Sleep"}] |
| 🔴 助理 | 當然! 我們同時擁有Cloud Nine和Best Sleep |
| 🔵 使用者 | 兩者之間有何差異? |
| 🔴 助理(函式呼叫) |
Products.GetDetails(25323)
Products.GetDetails(63633)
|
| 🟢 工具 | { "id": 25323, "name": "Cloud Nine", "price": 1000, "material": "Memory foam" } |
| 🟢 工具 | { "id": 63633, "name": "Best Sleep", "price": 1200, "material": "Latex" } |
| 🔴 助理 | Cloud Nine 是記憶泡棉製成,售價 1000 美元。 最佳睡眠由乳膠製成,售價1200美元。 |
預先擷取資料的取回
靜態資料擷取涉及從外部來源擷取資料,並 始終 提供給 AI 代理。 當每個請求都需要這些資料,或資料相對穩定且不常變動時,這樣會很有用。
舉例來說,一位總是回答當地天氣問題的代理人。 假設你有 WeatherPlugin,你可以從天氣 API 預先取得天氣資料,並在聊天紀錄中提供。 這讓代理能在不浪費時間向 API 請求資料的情況下產生關於天氣的回應。
using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);
builder.Plugins.AddFromType<WeatherPlugin>();
Kernel kernel = builder.Build();
// Get the weather
var weather = await kernel.Plugins.GetFunction("WeatherPlugin", "get_weather").InvokeAsync(kernel);
// Initialize the chat history with the weather
ChatHistory chatHistory = new ChatHistory("The weather is:\n" + JsonSerializer.Serialize(weather));
// Simulate a user message
chatHistory.AddUserMessage("What is the weather like today?");
// Get the answer from the AI agent
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var result = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
維護資料安全
從外部來源檢索資料時,確保資料安全且敏感資訊不會外洩非常重要。 為避免敏感資訊過度分享,您可以採取以下策略:
| Strategy | Description |
|---|---|
| 使用使用者的認證令牌 | 避免建立 AI 代理用來為使用者檢索資訊的服務主體。 這樣做會讓驗證使用者是否能存取所取得資訊變得困難。 |
| 避免重複建立搜尋服務 | 在使用向量資料庫建立新的搜尋服務之前,請先確認是否已存在具備所需資料的搜尋服務。 透過重複使用現有服務,你可以避免重複敏感內容,利用現有的存取控制,並使用只回傳使用者可存取資料的現有過濾機制。 |
| 將參考資料儲存在向量資料庫中,而非內容 | 與其將敏感內容複製到向量資料庫,不如儲存指向實際資料的參照。 使用者若要存取這些資訊,必須先使用其認證憑證取得真實資料。 |
下一步
既然你已經知道如何用外部來源的數據來鞏固你的 AI 代理,現在你就可以學習如何利用 AI 代理自動化業務流程。 欲了解更多,請參閱 使用任務自動化函式。