當我們談到 RAG 應用的時候,除了向量化之外,另一個重點就是向量資料庫,與傳統資料庫不同,向量資料庫是專門為了高維度資料以及相似度搜尋而存在的,目前市場有許多向量資料庫,而 Qdrant 則是其中較知名的,它的各項評比都相當不錯。
Qdrant 是專門用來儲存和搜尋「向量」的資料庫。簡單來說,當我們用 AI 模型來處理文字、圖片或其他資料時,模型會把這些資料轉換成數字向量(就是一串數字),這些向量能幫助我們比較資料之間的相似度。而 Qdrant 的工作就是把這些向量管理好,讓我們可以快速找到最相似的結果。傳統資料庫像 SQL、NoSQL 這些,對數字、文字的查詢非常強大,但他們缺少對向量的處理以及相似度搜尋演算法(目前有部份傳統資料庫也開始提供向量支援),這時候傳統資料庫就沒那麼好用了。這就是為什麼會需要一個專門處理向量的資料庫。
這裡容我置入一下工商,本月我與另二位作者合作的新書<LangChain 奇幻旅程:OpenAI x Gemini x 多模態應用開發指南>其中第12章對 Qdrant 有詳細的教學。購書連結(點這裡)
這裡我使用容器方式來架設 Qdrant 向量資料庫,首先你得先安裝好 docker 環境(這部份請自行參考docker官方教學,當然在正式環境上可能會使用 K8S).
接著執行以下指令,拉取 Qdrant 官方 docker image 並安裝。完成後 Qdrant 會在本機監聽 Port 6333,開啟瀏覽器查看 localhost:6333,如果有顯示 Qdrant Version資訊,就代表 Qdrant 已經成功安裝並啟動。
docker pull qdrant/qdrant
docker run -p 6333:6333 qdrant/qdrant
另外Qdrant 向量資料庫也提供WebUI介面,localhost:6333/dashboard,可以方便進行管理。(本例未配置Qdrant DB 的密碼)
以上一篇文章的範例,修改為使用 Qdrant 向量資料庫。
dotnet add package Microsoft.SemanticKernel.Connectors.Qdrant
Kernel kernel = Kernel.CreateBuilder()
.AddAzureOpenAITextEmbeddingGeneration(
endpoint: Config.aoai_endpoint,
deploymentName: Config.aoai_embedding_deployment,
apiKey: Config.aoai_apiKey)
.Build();
var embeddingGenerator = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
var memoryStore = new QdrantMemoryStore("http://localhost:6333", 3072);
SemanticTextMemory textMemory = new(memoryStore, embeddingGenerator);
string collectionName = "Law";
string[] kmEntries =
[
"""
1.為規定勞動條件最低標準,保障勞工權益,加強勞雇關係,促進社會與經濟發展,特制定本法;本法未規定者,適用其他法律之規定。
2.雇主與勞工所訂勞動條件,不得低於本法所定之最低標準。
""",
"""
勞動契約,分為定期契約及不定期契約。臨時性、短期性、季節性及特定性工作得為定期契約;有繼續性工作應為不定期契約。派遣事業單位與派遣勞工訂定之勞動契約,應為不定期契約。
定期契約屆滿後,有下列情形之一,視為不定期契約:
一、勞工繼續工作而雇主不即表示反對意思者。
二、雖經另訂新約,惟其前後勞動契約之工作期間超過九十日,前後契約間斷期間未超過三十日者。
前項規定於特定性或季節性之定期工作不適用之。
""",
"""
未符合下列規定者,雇主不得與勞工為離職後競業禁止之約定:
一、雇主有應受保護之正當營業利益。
二、勞工擔任之職位或職務,能接觸或使用雇主之營業秘密。
三、競業禁止之期間、區域、職業活動之範圍及就業對象,未逾合理範疇。
四、雇主對勞工因不從事競業行為所受損失有合理補償。
前項第四款所定合理補償,不包括勞工於工作期間所受領之給付。
違反第一項各款規定之一者,其約定無效。
離職後競業禁止之期間,最長不得逾二年。逾二年者,縮短為二年。
"""
];
foreach (var km in kmEntries)
{
await textMemory.SaveInformationAsync(
collection: collectionName,
text: km,
id: Guid.NewGuid().ToString());
}
var result = await textMemory.SearchAsync(collection: collectionName, query: "我離職後,不能再從事類似性質的工作嗎", limit: 2, minRelevanceScore: 0.5).ToListAsync();
foreach (var item in result)
{
Console.WriteLine($"Id: {item.Metadata.Id}, Text: {item.Metadata.Text}, Relevance: {item.Relevance} \n\n");
}
與上一篇使用快閃記憶體相比,使用 Qdrant 向量資料庫可以持久化向量資料,不需要每次重啟應用時,重新轉入資料,另外當資料量大的時候,專用的向量資料庫可以明顯察覺到搜尋速度會快非常多。本篇的程式碼與上一篇的程式碼幾乎87%,而這也是因為有了 Semantic Kernel 的幫忙,它的高度抽象化架構讓我們在實作應用上,對於抽換服務是相當方便的,重要的是不需要大幅的改動程式碼。當然還是老話一句,用與不同有不同的角度思考,沒有絕對的答案,同時現階段 Gen AI 的應用處於一種爆發期,AI Model及平台功能快速推陳出新,相對的這些框架的變動性也會跟著變很快,這也是開發者需要認知的一點。
特別提醒,向量資料一旦換了 embedding 模型,就必須重新做向量處理,因為不同模型在語議理解以及維度上並不相同,也因此一旦更換 embedding 模型,則原本已儲存的向量資料就得重新再轉過囉。