首页 > 编程语言 >SemanticKernel/C#:实现接口,接入本地嵌入模型

SemanticKernel/C#:实现接口,接入本地嵌入模型

时间:2024-08-06 09:39:20浏览次数:12  
标签:嵌入 C# public Attributes var new model Ollama SemanticKernel

前言

本文通过Codeblaze.SemanticKernel这个项目,学习如何实现ITextEmbeddingGenerationService接口,接入本地嵌入模型。

项目地址:https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel

实践

SemanticKernel初看以为只支持OpenAI的各种模型,但其实也提供了强大的抽象能力,可以通过自己实现接口,来实现接入不兼容OpenAI格式的模型。

Codeblaze.SemanticKernel这个项目实现了ITextGenerationService、IChatCompletionService与ITextEmbeddingGenerationService接口,由于现在Ollama的对话已经支持了OpenAI格式,因此可以不用实现ITextGenerationService和IChatCompletionService来接入Ollama中的模型了,但目前Ollama的嵌入还没有兼容OpenAI的格式,因此可以通过实现ITextEmbeddingGenerationService接口,接入Ollama中的嵌入模型。

查看ITextEmbeddingGenerationService接口:

image-20240806081346110

代表了一种生成浮点类型文本嵌入的生成器。

再看看IEmbeddingGenerationService<string, float>接口:

[Experimental("SKEXP0001")]
public interface IEmbeddingGenerationService<TValue, TEmbedding> : IAIService where TEmbedding : unmanaged
{
     Task<IList<ReadOnlyMemory<TEmbedding>>> GenerateEmbeddingsAsync(IList<TValue> data, Kernel? kernel = null, CancellationToken cancellationToken = default(CancellationToken));
}

再看看IAIService接口:

image-20240806081733336

说明我们只要实现了

Task<IList<ReadOnlyMemory<TEmbedding>>> GenerateEmbeddingsAsync(IList<TValue> data, Kernel? kernel = null, CancellationToken cancellationToken = default(CancellationToken));

IReadOnlyDictionary<string, object?> Attributes { get; }

这个方法和属性就行。

学习Codeblaze.SemanticKernel中是怎么做的。

添加OllamaBase类:

 public interface IOllamaBase
{
    Task PingOllamaAsync(CancellationToken cancellationToken = new());
}
public abstract class OllamaBase<T> : IOllamaBase where T : OllamaBase<T>
{
    public IReadOnlyDictionary<string, object?> Attributes => _attributes;
    private readonly Dictionary<string, object?> _attributes = new();
    protected readonly HttpClient Http;
    protected readonly ILogger<T> Logger;

    protected OllamaBase(string modelId, string baseUrl, HttpClient http, ILoggerFactory? loggerFactory)
    {
        _attributes.Add("model_id", modelId);
        _attributes.Add("base_url", baseUrl);

        Http = http;
        Logger = loggerFactory is not null ? loggerFactory.CreateLogger<T>() : NullLogger<T>.Instance;
    }

    /// <summary>
    /// Ping Ollama instance to check if the required llm model is available at the instance
    /// </summary>
    /// <param name="cancellationToken"></param>
    public async Task PingOllamaAsync(CancellationToken cancellationToken = new())
    {
        var data = new
        {
            name = Attributes["model_id"]
        };

        var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/show", data, cancellationToken).ConfigureAwait(false);

        ValidateOllamaResponse(response);

        Logger.LogInformation("Connected to Ollama at {url} with model {model}", Attributes["base_url"], Attributes["model_id"]);
    }

    protected void ValidateOllamaResponse(HttpResponseMessage? response)
    {
        try
        {
            response.EnsureSuccessStatusCode();
        }
        catch (HttpRequestException)
        {
            Logger.LogError("Unable to connect to ollama at {url} with model {model}", Attributes["base_url"], Attributes["model_id"]);
        }
    }
}

注意这个

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

实现了接口中的属性。

添加OllamaTextEmbeddingGeneration类:

#pragma warning disable SKEXP0001
   public class OllamaTextEmbeddingGeneration(string modelId, string baseUrl, HttpClient http, ILoggerFactory? loggerFactory)
      : OllamaBase<OllamaTextEmbeddingGeneration>(modelId, baseUrl, http, loggerFactory),
           ITextEmbeddingGenerationService
  {
       public async Task<IList<ReadOnlyMemory<float>>> GenerateEmbeddingsAsync(IList<string> data, Kernel? kernel = null,
           CancellationToken cancellationToken = new())
      {
           var result = new List<ReadOnlyMemory<float>>(data.Count);

           foreach (var text in data)
          {
               var request = new
              {
                   model = Attributes["model_id"],
                   prompt = text
              };

               var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/embeddings", request, cancellationToken).ConfigureAwait(false);

               ValidateOllamaResponse(response);

               var json = JsonSerializer.Deserialize<JsonNode>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));

               var embedding = new ReadOnlyMemory<float>(json!["embedding"]?.AsArray().GetValues<float>().ToArray());

               result.Add(embedding);
          }

           return result;
      }
  }

注意实现了GenerateEmbeddingsAsync方法。实现的思路就是向Ollama中的嵌入接口发送请求,获得embedding数组。

为了在MemoryBuilder中能用还需要添加扩展方法:

#pragma warning disable SKEXP0001
   public static class OllamaMemoryBuilderExtensions
  {
       /// <summary>
       /// Adds Ollama as the text embedding generation backend for semantic memory
       /// </summary>
       /// <param name="builder">kernel builder</param>
       /// <param name="modelId">Ollama model ID to use</param>
       /// <param name="baseUrl">Ollama base url</param>
       /// <returns></returns>
       public static MemoryBuilder WithOllamaTextEmbeddingGeneration(
           this MemoryBuilder builder,
           string modelId,
           string baseUrl
      )
      {
           builder.WithTextEmbeddingGeneration((logger, http) => new OllamaTextEmbeddingGeneration(
               modelId,
               baseUrl,
               http,
               logger
          ));

           return builder;
      }      
  }

开始使用

 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("mxbai-embed-large:335m", embeddingEndpoint);
    IMemoryStore memoryStore = await SqliteMemoryStore.ConnectAsync("memstore.db");
    builder.WithMemoryStore(memoryStore);
    var textMemory = builder.Build();
    return textMemory;
}
  builder.WithOllamaTextEmbeddingGeneration("mxbai-embed-large:335m", embeddingEndpoint);

实现了WithOllamaTextEmbeddingGeneration这个扩展方法,因此可以这么写,使用的是mxbai-embed-large:335m这个向量模型。

我使用WPF简单做了个界面,来试试效果。

找了一个新闻嵌入:

image-20240806090946822

文本向量化存入数据库中:

image-20240806091040483

现在测试RAG效果:

image-20240806091137623

image-20240806091310159

image-20240806091404424

回答的效果也还可以。

大模型使用的是在线api的Qwen/Qwen2-72B-Instruct,嵌入模型使用的是本地Ollama中的mxbai-embed-large:335m。

 

标签:嵌入,C#,public,Attributes,var,new,model,Ollama,SemanticKernel
From: https://www.cnblogs.com/mingupupu/p/18344517

相关文章

  • IEC104初学者教程,第八章:总召唤流程详解
    第八章:总召唤流程详解平时学习规约或调试IEC104或IEC101设备,需要IEC104/101模拟器,推荐一款:主站下载地址:IEC104主站模拟器从站下载地址:IEC104从站模拟器IEC60870-5-104(简称IEC104)是一种用于远程控制和监控系统的通信协议。它广泛应用于电力系统和其他工业自动化系统中。总召......
  • IEC104初学者教程,第九章:计数量召唤流程详解
    第九章:计数量召唤流程详解平时学习规约或调试IEC104或IEC101设备,需要IEC104/101模拟器,推荐一款:主站下载地址:IEC104主站模拟器从站下载地址:IEC104从站模拟器在IEC60870-5-104(简称IEC104)协议中,计数量召唤(CounterInterrogation,简称CI)是一种特定的功能,用于获取远程终端设备(RTU......
  • excel基础
    1、表格格式化2、sum函数相对引用和绝对引用($)、混合应用3、函数使用逻辑函数IF(条件,"成功","失败")结合and|or使用条件求和SUMIF(单元格,"条件")相乘求和SUMPRODUCT(B4:D4,B5:D5)统计求和:AVERAGE(A1:B1)\MAX(单元格范围)\MINCOUNT(单元格范围)条件统计函数:COUNTIF(单......
  • EasyCVR视频转码:T3视频不支持GB28181协议,如何实现与视频联网平台的对接?
    EasyCVR视频管理系统以其强大的拓展性、灵活的部署方式、高性能的视频能力和智能化的分析能力,为各行各业的视频监控需求提供了优秀的解决方案。T3视频为公网HTTP-FLV或HLS格式的视频流,目前T3平台暂不支持国标GB28181协议,因此也无法直接接入视频联网平台实现共享应用。那么遇到此......
  • Hashtable工具类 - C#小函数类推荐
          此文记录的是一个Hashtable的增强版本,以前没泛型的时候笔者喜欢用Hashtable,性能刚刚的。/***Hashtable工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comD......
  • leetcode数论(2453. 摧毁一系列目标)
    前言经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。现阶段开始专项练习。数论包含最大公约数(>=2个数)、最大公约数性质、最小公倍数、区间范围质因素计数(最下间隔)、质因素分解、判断质数、平方根、立方根、互质、同余等等。描述给你一个下标从 0......
  • leetcode数论(326. 3 的幂)
    前言经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。现阶段开始专项练习。数论包含最大公约数(>=2个数)、最大公约数性质、最小公倍数、区间范围质因素计数(最下间隔)、质因素分解、判断质数、平方根、立方根、互质、同余等等。描述给定一个整数,写一个......
  • ACM日常训练日记——8.2
    小训练KevinandPermutation题解很好不多说#include<bits/stdc++.h>usingnamespacestd;intT,n;intmain(){ cin>>T; while(T--){ cin>>n; for(inti=1;i<=n/2;i++)cout<<i+n/2<<''<<i<<''; ......
  • 热烈祝贺华企盾科技获得ISO/IEC 27001信息安全管理体系认证证书!
    近日,北京华企盾科技有限责任公司顺利通过权威认证机构的严格审核,获得“ISO/IEC27001信息安全管理体系认证证书”。认证范围涵盖与计算机软硬件销售及软件运维相关的信息安全管理活动等。信息安全管理实用规则ISO/IEC27001是国际上具有代表性的信息安全管理体系标准,已在世界各......
  • 数据结构 顺序表 -- C语言实现
    顺序表概念顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。顺序表一般可以分为:静态顺序表:使用定长数组存储元素。动态顺序表:使用动态开辟的数组存储。代码实现动态顺序表静态顺序表只适用于确定......