解鎖GraphRag.Net的無限可能:手把手教你整合國產模型和本地模型

许泽宇發表於2024-08-05

在上次的文章中,我們已經詳細介紹了GraphRag的基本功能和使用方式。如果你還不熟悉,建議先閱讀前面的文章

透過前兩篇文章,相信你已經瞭解到GraphRag.Net目前只支援OpenAI規範的介面,但許多小夥伴在社群中提議,希望能增加對本地模型(例如:ollama等)的支援。所以這次,我們將探討如何在GraphRag.Net中使用自定義模型和本地模型。

為什麼選擇GraphRag.Net?

GraphRag.Net採用了Semantic Kernel作為基礎,讓我們能夠非常簡潔地抽象出會話與向量介面。因此,使用者可以非常方便地實現自己定製的解決方案。接下來,我們會透過一個具體的例子,展示如何將本地模型和國產模型整合到GraphRag.Net中。

預設配置方法

首先,我們來看看如何進行預設配置:

// OpenAI配置
builder.Configuration.GetSection("OpenAI").Get<OpenAIOption>();
// 文件切片配置
builder.Configuration.GetSection("TextChunker").Get<TextChunkerOption>();
// 配置資料庫連線
builder.Configuration.GetSection("GraphDBConnection").Get<GraphDBConnectionOption>();

// 注意,需要先注入配置檔案,然後再注入GraphRag.Net
builder.Services.AddGraphRagNet();

這裡,我們將在預設配置中注入OpenAI的配置、文字切片的配置和資料庫連線的配置。然後,依次注入這些配置檔案和GraphRag.Net的服務。

自定義配置方法

如果需要自定義模型或本地模型,可能需要實現一些額外的服務介面,下面是自定義配置的示例:

var kernelBuild = Kernel.CreateBuilder();
kernelBuild.Services.AddKeyedSingleton<ITextGenerationService>("mock-text", new MockTextCompletion());
kernelBuild.Services.AddKeyedSingleton<IChatCompletionService>("mock-chat", new MockChatCompletion());
kernelBuild.Services.AddSingleton<ITextEmbeddingGenerationService>(new MockTextEmbeddingGeneratorService());
kernelBuild.Services.AddKeyedSingleton("mock-embedding", new MockTextEmbeddingGeneratorService());

builder.Services.AddGraphRagNet(kernelBuild.Build());

在這個自定義配置示例中,我們引入了三個自定義服務介面:ITextGenerationServiceIChatCompletionServiceITextEmbeddingGenerationService

實現自定義服務介面

接下來,我們需要為每個服務介面提供具體的實現。以下是三個介面的具體實現:

實現IChatCompletionService

  public class MockChatCompletion : IChatCompletionService
  {
      private readonly Dictionary<string, object?> _attributes = new();
      private string _chatId;


      private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
      {
          NumberHandling = JsonNumberHandling.AllowReadingFromString,
          Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
      };

      public IReadOnlyDictionary<string, object?> Attributes => _attributes;

      public MockChatCompletion()
      {

      }

      public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
      {
          StringBuilder sb = new();
          string result = $"這是一條Mock資料,便於聊天測試,你的訊息是:{chatHistory.LastOrDefault().ToString()}";
          return [new(AuthorRole.Assistant, result.ToString())];
      }

      public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
      {
          StringBuilder sb = new();
          string result = $"這是一條Mock資料,便於聊天測試,你的訊息是:{chatHistory.LastOrDefault().ToString()}";
          foreach (var c in result)
          {
              yield return new StreamingChatMessageContent(AuthorRole.Assistant, c.ToString());
          }
      }
  }

  

實現ITextGenerationService

 public class MockTextCompletion : ITextGenerationService, IAIService
 {
     private readonly Dictionary<string, object?> _attributes = new();
     private string _chatId;

     private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
     {
         NumberHandling = JsonNumberHandling.AllowReadingFromString,
         Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
     };

     public IReadOnlyDictionary<string, object?> Attributes => _attributes;

     public MockTextCompletion()
     {

     }

     public async Task<IReadOnlyList<TextContent>> GetTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
     {
         StringBuilder sb = new();
         string result = $"這是一條Mock資料,便於聊天測試,你的訊息是:{prompt}";
         return [new(result.ToString())];
     }

     public async IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(string prompt, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
     {
         StringBuilder sb = new();
         string result = $"這是一條Mock資料,便於聊天測試,你的訊息是:{prompt}";
         foreach (var c in result)
         {
             var streamingTextContent = new StreamingTextContent(c.ToString(), modelId: "mock");

             yield return streamingTextContent;
         }
     }
 }

實現ITextEmbeddingGenerationService

  public sealed class MockTextEmbeddingGeneratorService : ITextEmbeddingGenerationService
  {
      private Dictionary<string, object?> AttributesInternal { get; } = [];
      public IReadOnlyDictionary<string, object?> Attributes => this.AttributesInternal;
      public MockTextEmbeddingGeneratorService()
      {

      }
      public async Task<IList<ReadOnlyMemory<float>>> GenerateEmbeddingsAsync(
        IList<string> data,
        Kernel? kernel = null,
        CancellationToken cancellationToken = default)
      {
          IList<ReadOnlyMemory<float>> results = new List<ReadOnlyMemory<float>>();

          float[] array1 = { 1.0f, 2.0f, 3.0f };
          float[] array2 = { 4.0f, 5.0f, 6.0f };
          float[] array3 = { 7.0f, 8.0f, 9.0f };

          // 將陣列包裝為ReadOnlyMemory<float>並新增到列表中
          results.Add(new ReadOnlyMemory<float>(array1));
          results.Add(new ReadOnlyMemory<float>(array2));
          results.Add(new ReadOnlyMemory<float>(array3));

          return results;
      }

      public void Dispose()
      {

      }
  }

  

看到這裡,你可能已經發現,整合自定義模型和本地模型非常簡單。只需按照上述步驟,實現相應的介面並注入配置,你就可以在GraphRag.Net中使用這些自定義的功能。

結語

透過本文的介紹,我們瞭解瞭如何在GraphRag.Net中整合國產模型和本地模型。希望大家能夠根據這些示例,開發出更多適合自己需求的功能。更多精彩內容,歡迎關注我的公眾號,併傳送進群加入我們的GraphRag.Net交流群,與社群小夥伴們一起交流學習!

感謝閱讀,我們下期再見!

相關文章