本篇來談談最多人關心的如何在ChatGPT模型中引用企業私有知識庫,目前最常見的做法不外乎是微調(fine-tuning)或embeddings的使用。本篇就來看看如何使用semantic kernel搭配embeddings模型與ChatGPT。
範例採用C#程式語言,並以主控台應用程式做為示範,使用的是.net 7.0。
此外GPT模型使用的是Azure OpenAI GPT-4,事實也可以依需求改用OpenAI服務,而模型也可以改用GPT-3.5。
至目前為止本系列文中的範例皆採用Microsoft.SemanticKernel套件,版號為0.24.230918.1-preview,然而下週Semantic Kernel預計會正式發行1.0版,因此後面文章若遇到1.0版發行,則會直接使用1.0版,而前面文章的範例,也會另行找時間更新,github上的程式碼也會同時更新。
由於ChatGPT模型訓練集僅至2021/09為止,對於新的資料是未知的,而企業也常提心資料外洩的風險,因為希望找尋一種方式可以運用ChatGPT的能力又能確保有自已的私有資料知識庫,這個需求便可以使用embeddings來實現。
embeddings簡單來說是一種將文本資料轉成固定大小的數值向量方法,這些向量可以反映文本的語義和關係資訊,因此可以將企業私有資料分成小塊文本資料,然後轉化為向量儲存於向量資料庫中。然後當使用者提問時,也將使用者的提問prompt轉成向量,再將之與向量資料庫的向量進行比較,最終找出與提問最相關的向量資料,再交由ChatGPT模型生成文本回答使用者。而由於企業私有資料是儲存於獨立的向量資料庫,因此可以確保資料的安全性。
接著實際來做一遍示範:
var kernel = new KernelBuilder()
.WithAzureTextEmbeddingGenerationService(embedding_Model, aoai_Endpoint, api_Key)
.WithMemoryStorage(new VolatileMemoryStore())
.WithAzureChatCompletionService(
deploy_Model, // Azure OpenAI Deployment Name
aoai_Endpoint, // Azure OpenAI Endpoint
api_Key // Azure OpenAI Key
).Build();
VolatileMemoryStore,semantic kernel內建提供用記憶向量資料的記憶體儲存功能,使用的是本機記憶體因此非永久性儲存。而從原始碼中可以看到在WithMemoryStorage是注入IMemoryStore的實作,換句話任何向量資料庫只要是有實作IMemoryStore,皆可做為semantic kernel支援的向量資料庫。
public KernelBuilder WithMemoryStorage(IMemoryStore storage) { Verify.NotNull(storage); this._memoryStorageFactory = () => storage; return this; }
此部份若為永久性儲存的向量資料庫,則不需每次都匯入資料。
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第1條", text: "本細則依檔案法(以下簡稱本法)第二十九條規定訂定之。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第2條第1項", text: "本法第二條第二款所稱管理程序,指依文書處理或機關業務相關法令規定,完成核定、發文或辦結之程序。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第2條第2項", text: "本法第二條第二款所稱文字或非文字資料及其附件,指各機關處理公務或因公務而產生之各類紀錄資料及其附件,包括各機關所持有或保管之文書、圖片、紀錄、照片、錄影(音)、微縮片、電腦處理資料等,可供聽、讀、閱覽或藉助科技得以閱覽或理解之文書或物品。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第3條", text: "各機關管理檔案,應依本法第四條規定,並參照檔案中央主管機關訂定之機關檔案管理單位及人員配置基準,設置或指定專責單位或人員。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第4條第1項", text: "各機關依本法第五條規定,經該管機關核准,將檔案運往國外者,應先以微縮、電子或其他方式儲存,並經管理該檔案機關首長核定。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第4條第2項", text: "前項檔案如屬永久保存之機關檔案,並應經檔案中央主管機關同意。");
await kernel.Memory.SaveInformationAsync(embedding_CollectionName, id: "第5條", text: "各機關依本法第六條第二項規定,將檔案中之器物交有關機構保管時,應訂定書面契約或作成紀錄存查。");
這種embedding方式,也相同適用於需要精準內容的應用,以避免ChatGPT的幻覺現象。
這是從知識庫所搜尋到的結果
###
{{$ans_result}}
###
請根據上述資料,簡短扼要的回答使用者的問題,不需要再廢話解釋資料來源
問題:{{$query_input}}
答案:
// Import the Plugin from the plugins directory.
var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins");
var plugin = kernel.ImportSemanticSkillFromDirectory(pluginsDirectory, "QASkill");
var searchResult = await kernel.Memory.SearchAsync(embedding_CollectionName, ask, minRelevanceScore: 0.8).FirstOrDefaultAsync();
var ans = searchResult != null ? searchResult.Metadata.Text : "很抱歉,知識庫沒有相關資料可以提供";
var variables = new ContextVariables
{
["ans_result"] = ans,
["query_input"] = ask
};
//叫用GPT模型等得生成結果
var result = (await kernel.RunAsync(variables, plugin["AssistantResults"])).Result;
範例原始碼 : https://github.com/iangithub/sklearn/tree/main/KmSearch
本篇展示了semantic kernel對Embedding的支援並以一個簡化的實作做為示範,對於私有知識庫應用來說相較於採用微調(fine-tuning),Embedding會是更容易並且效果會是更好的方式,不需要訓練專屬模型,且保有資料獨立的隱私性,並且有了semantic kernel,實作起來也是相當的容易。