iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
生成式 AI

AI 全餐,好吃嗎?用 Semantic Kernel 打造你的客製化滿漢全席!系列 第 12

Day 12: 精準撈料:Vector Stores 的資料注入與搜尋 (Part 2 - 應用)

  • 分享至 

  • xImage
  •  

昨天我們成功打造了超強的米其林級湯底——向量儲存庫,但光有湯底還不夠,你總不能讓客人喝清水吧?今天,我們就要學習如何把「新鮮食材」(外部資料)放進湯裡,並在客人點餐時,精準地從湯裡撈出最對味的料。這就是 RAG 應用的第二步:資料注入 (Upsert)向量搜尋 (SearchAsync)

讓資料入湯:Upsert 的魔法

Upsert 這個詞是 UpdateInsert 的組合,意思是如果資料存在就更新,不存在就新增。在我們的「湯底」情境中,Upsert 就是將文字資料轉換成向量,然後儲存到向量儲存庫中。

Semantic Kernel 讓這個過程變得非常簡單,你只需要建立一個 MemoryStore 物件,並告訴它要使用的向量儲存庫和嵌入模型 (Embedding Model)。

using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Connectors.OpenAI; 

// 假設你已經設定好 Kernel 和 OpenAI 服務
// 這是我們昨天定義的 Recipe 類別
var collection = vectorStore.GetCollection<string, Recipe>("Recipe");
// 建立 collection
await collection.EnsureCollectionExistsAsync();

// 準備我們的「食譜」資料
var recipes = new List<Recipe>
{
    new() { Name = "麻婆豆腐", Cuisine = "川菜", Type = "主菜", Description = "麻、辣、燙、嫩、酥、香,這六個字完美地詮釋了麻婆豆腐的精髓。一道極具代表性的川菜,吃起來溫暖又過癮。" },
    new() { Name = "宮保雞丁", Cuisine = "川菜", Type = "主菜", Description = "以雞丁、乾辣椒、花生米、花椒粒等材料烹炒而成,鹹甜微辣,醬汁濃郁,是道經典的家常菜。" },
    new() { Name = "三杯雞", Cuisine = "台菜", Type = "主菜", Description = "以一杯麻油、一杯醬油、一杯米酒烹調的台式經典名菜,香氣四溢,配飯一流。" },
};

// 進行資料注入,Semantic Kernel 會自動將 Description 轉成向量並存入
await recipeStore.UpsertAsync(recipes);

Console.WriteLine("食譜資料已成功加入湯底!");

這段程式碼中,recipeStore.UpsertAsync(recipes) 就是那句神奇的咒語!它會自動呼叫嵌入模型,將每道食譜的 Description 轉化為向量,然後連同其他屬性一起存到我們的 Qdrant 向量儲存庫中。

精準撈料:SearchAsync 的藝術

當客人問:「有沒有一道暖心又過癮的菜?」時,我們需要精準地從湯底中撈出最相關的食譜。這時,SearchAsync 就派上用場了。它會將你的問題轉換成向量,然後在資料庫中尋找語意上最接近的資料。

// 客人的問題
string question = "有什麼吃起來很溫暖又有點辣的菜?";

// 進行向量搜尋,尋找最相關的 3 個結果
var searchResults = recipeStore.SearchAsync(
    searchValue: question,
    top: 3 // 只撈取最相關的前 3 個結果
);

Console.WriteLine($"針對您的問題「{question}」,我從食譜資料庫中找到了以下結果:");

// 顯示搜尋結果
await foreach (var result in searchResults)
{
    Console.WriteLine($"\n--- 找到的食譜:{result.Record.Name} (相關度:{result.Score}) ---");
    Console.WriteLine($"描述:{result.Record.Description}");
}

這個範例中,SearchAsync 會自動處理所有複雜的細節,你只需要提供一個問題和想要撈取的數量。瞧,我們就這麼簡單地實現了語意搜尋!

過濾功能:讓撈料更精準

光靠語意搜尋有時還不夠,如果客人說:「我想找一道台式的、吃起來溫暖的菜」,我們就需要結合過濾 (Filter) 來進一步縮小範圍。

Semantic Kernel 允許你在 SearchAsync 中加入過濾條件。在 Qdrant 中,這就是一個 Filter 物件。

// 客人更精確的問題:我要找台菜,而且吃起來很溫暖
var morePreciseQuestion = "我想找一道溫暖又好吃的台式菜色";

// 向量搜尋後,再進行 LINQ 過濾
var filteredResults = collection.SearchAsync(
    searchValue: morePreciseQuestion,
    top: 10 // 這次先撈取多一點,再進行過濾
).Where(r => r.Record.Cuisine == "台菜");

Console.WriteLine($"\n針對您的問題「{morePreciseQuestion}」,我從台菜食譜中找到了以下結果:");

await foreach (var result in filteredResults)
{
    Console.WriteLine($"\n--- 找到的食譜:{result.Record.Name} (相關度:{result.Score}) ---");
    Console.WriteLine($"描述:{result.Record.Description}");
}

透過 filter 參數,我們可以精準地告訴向量儲存庫,我們只對 Cuisine 屬性為 "台菜" 的食譜感興趣。這就像在撈麵時,先用篩子把其他類型的麵條都過濾掉,只留下我們想要的。

今天我們學會了如何讓 AI 擁有外部知識,並能精準地從中檢索。明天,我們會把這個「湯底」的概念再提升一個層次,將 RAG 流程抽象化,讓 AI 能夠自主地進行外部搜尋!


完整程式碼範例


上一篇
Day 11: 米其林星級湯底:用 Vector Stores 實現 RAG (Part 1 - 基礎與塑模)
系列文
AI 全餐,好吃嗎?用 Semantic Kernel 打造你的客製化滿漢全席!12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言