SemanticKernel/C#:使用Ollama中的對話模型與嵌入模型用於本地離線場景

mingupupup發表於2024-08-02

前言

上一篇文章介紹了使用SemanticKernel/C#的RAG簡易實踐,在上篇文章中我使用的是相容OpenAI格式的線上API,但實際上會有很多本地離線的場景。今天跟大家介紹一下在SemanticKernel/C#中如何使用Ollama中的對話模型與嵌入模型用於本地離線場景。

開始實踐

本文使用的對話模型是gemma2:2b,嵌入模型是all-minilm:latest,可以先在Ollama中下載好。

image-20240802155643905

2024年2月8號,Ollama中的相容了OpenAI Chat Completions API,具體見https://ollama.com/blog/openai-compatibility。

因此在SemanticKernel/C#中使用Ollama中的對話模型就比較簡單了。

var kernel = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion(modelId: "gemma2:2b", apiKey: null, endpoint: new Uri("http://localhost:11434")).Build();

這樣構建kernel即可。

簡單嘗試一下效果:

public async Task<string> Praise()
{
    var skPrompt = """                           
                  你是一個夸人的專家,回覆一句話夸人。                         
                  你的回覆應該是一句話,不要太長,也不要太短。                                                  
                  """;
    var result = await _kernel.InvokePromptAsync(skPrompt);
    var str = result.ToString();
    return str;
}

image-20240802161927375

就這樣設定就成功在SemanticKernel中使用Ollama的對話模型了。

現在來看看嵌入模型,由於Ollama並沒有相容OpenAI的格式,所以直接用是不行的。

Ollama的格式是這樣的:

image-20240802162315493

OpenAI的請求格式是這樣的:

curl https://api.openai.com/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "input": "Your text string goes here",
    "model": "text-embedding-3-small"
  }'

OpenAI的返回格式是這樣的:

{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "index": 0,
      "embedding": [
        -0.006929283495992422,
        -0.005336422007530928,
        ... (omitted for spacing)
        -4.547132266452536e-05,
        -0.024047505110502243
      ],
    }
  ],
  "model": "text-embedding-3-small",
  "usage": {
    "prompt_tokens": 5,
    "total_tokens": 5
  }
}

因此透過請求轉發的方式是不行的。

之前也有人在ollama的issue提了這個問題:

image-20240802164433012

似乎也有準備實現嵌入介面的相容:

image-20240802164711059

目前試了一下還沒有相容。

在SemanticKernel中需要自己實現一些介面來使用Ollama的嵌入模型,但是經過搜尋,我發現已經有大佬做了這個事,github地址:https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel。

使用方法見:https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel/tree/main/dotnet/Codeblaze.SemanticKernel.Connectors.Ollama

大佬實現了ChatCompletion、EmbeddingGeneration與TextGenerationService,如果你只使用到EmbeddingGeneration可以看大佬的程式碼,在專案裡自己新增一些類,來減少專案中的包。

這裡為了方便,直接安裝大佬的包:

image-20240802165405190

構建ISemanticTextMemory:

 public async Task<ISemanticTextMemory> GetTextMemory3()
 {
     var builder = new MemoryBuilder();
     var embeddingEndpoint = "http://localhost:11434";
     var cancellationTokenSource = new System.Threading.CancellationTokenSource();
     var cancellationToken = cancellationTokenSource.Token;
     builder.WithHttpClient(new HttpClient());
     builder.WithOllamaTextEmbeddingGeneration("all-minilm:latest", embeddingEndpoint);           
     IMemoryStore memoryStore = await SqliteMemoryStore.ConnectAsync("memstore.db");
     builder.WithMemoryStore(memoryStore);
     var textMemory = builder.Build();
     return textMemory;
 }

現在開始試試效果,基於昨天的分享做改進,今天上傳一個txt文件。

一個私有文件如下所示,隱私資訊已替換:

各位同學:
你好,為了幫助大家平安、順利地度過美好的大學時光,學校專門引進“網際網路+”高校安全教育服務平臺,可透過手機端隨時隨地學習安全知識的網路微課程。大學生活多姿多彩,牢固掌握安全知識,全面提升安全技能和素質。請同學們務必在規定的學習時間完成該課程的學習與考試。
請按如下方式自主完成學習和考試:
1、手機端學習平臺入口:請關注微信公眾號“XX大學”或掃描下方二維碼,進入後點選公眾號選單欄【學術導航】→【XX微課】,輸入賬號(學號)、密碼(學號),點【登入】後即可繫結資訊,進入學習平臺。
2、網頁端學習平臺入口:開啟瀏覽器,登入www.xxx.cn,成功進入平臺後,即可進行安全知識的學習。
3、平臺開放時間:2024年4月1日—2024年4月30日,必須完成所有的課程學習後才能進行考試,試題共計50道,滿分為100分,80分合格,有3次考試機會,最終成績取最優分值。
4、答疑qq群號:123123123。
學習平臺登入流程
1.	手機端學習平臺入口:
請掃描下方二維碼,關注微信公眾號“XX大學”;
公眾號選單欄【學術導航】→【XX微課】,選擇學校名稱,輸入賬號(學號)、密碼(學號),點【登入】後即可繫結資訊,進入學習平臺;
遇到問題請點【線上課服】或【常見問題】,進行諮詢(諮詢時間:週一至週日8:30-17:00)。
2.	網頁端學習平臺入口:
開啟瀏覽器,登入www.xxx.cn,成功進入平臺後,即可進行安全知識的學習。
3.	安全微課學習、考試
1)	微課學習
	點選首頁【學習任務中】的【2024年春季安全教育】,進入課程學習;
	展開微課列表,點選微課便可開始學習;
	大部分微課是點選繼續學習,個別微課是向上或向左滑動學習;
	微課學習完成後會有“恭喜,您已完成本微課的學習”的提示,需點選【確定】,再點選【返回課程列表】,方可記錄微課完成狀態;
2)	結課考試
完成該專案的所有微課學習後,點選【考試安排】→【參加考試】即可參加結課考試。

上傳文件:

image-20240802170116241

切割為三段:

image-20240802170255136

存入資料:

image-20240802170408646

回一個問題,比如“答疑qq群號是多少?”:

image-20240802170515662

雖然耗時有點久,大概幾十秒,但是回答對了:

image-20240802171241525

image-20240802171302527

再嘗試回答一個問題:

image-20240802171753214

回答效果不是很好,而且由於配置不行,本地跑也很慢,如果有條件可以換一個模型,如果沒有條件並且不是一定要離線執行的話,可以接一個免費的api,在結合本地的嵌入模型。

換成線上api的Qwen/Qwen2-7B-Instruct,效果還不錯:

image-20240802172218766

image-20240802172254618

總結

本次實踐的主要收穫是如何在SemanticKernel中使用Ollama中的對話模型與嵌入模型用於本地離線場景。在實踐RAG的過程中,發現影響效果的最主要在兩個地方。

第一個地方是切片大小的確定:

 var lines = TextChunker.SplitPlainTextLines(input, 20);
 var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, 100);

第二個地方是要獲取幾條相關資料與相關度的設定:

var memoryResults = textMemory.SearchAsync(index, input, limit: 3, minRelevanceScore: 0.3);

相關度太高一條資料也找不到,太低又容易找到不相關的資料,需要透過實踐,調整成一個能滿足需求的設定。

參考

1、https://medium.com/@johnkane24/local-memory-c-semantic-kernel-ollama-and-sqlite-to-manage-chat-memories-locally-9b779fc56432

2、https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel

相關文章