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

news2025/1/11 14:18:59

前言

本文通过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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1994567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(24)(24.2) Minim OSD快速安装指南(一)

文章目录 前言 1 概述 2 基本接线图 3 关键冷却条件的可选设置 4 固件可用于MinimOSD 5 MWOSD 前言 MinimOSD “屏幕显示”是一个小型电路板&#xff0c;它从你的自动驾驶仪中提取遥测数据&#xff0c;并将其覆盖在你的第一人称视图监视器上(First Person View)。Minim …

发布包到npm

目录 注册npm账号 创建包 登录npm 上架包 更新包 删除包 注册npm账号 首先注册npm账号&#xff1a;npm | Sign Up (npmjs.com) 创建包 可以在桌面上新建一个文件夹&#xff1a;文件夹名随便起&#xff0c;但是别跟npm已经上架的包名重复了 可以通过下面的指令查看&…

【小记】这也算是经验分享了吧~

最近在进行跳槽的一些准备&#xff0c;从简历制作、投递简历、准备面试、视频面试、线下面试、接受录取、辞职准备&#xff0c;每一个过程都超级紧张刺激 大学的时候就有一些制作PPT的经验&#xff0c;靠这个收入了一点点&#xff0c;进而对于office这一系列的操作还是比较熟悉…

韶音Open Fir Air好用吗?南卡、韶音、漫步者三款开放式耳机无广避坑测评!

近期&#xff0c;我注意到后台有许多小伙伴向我咨询如何挑选合适的开放式耳机。市场上开放式耳机品牌琳琅满目&#xff0c;它们在音质表现、佩戴舒适度以及综合性能上均展现出各自的独特魅力与差异。对于追求耳朵极致舒适体验的朋友而言&#xff0c;选择一款合适的开放式耳机显…

从零开始搭建监控系统 (三) 指标采集

从零开始搭建监控系统 (三) 指标采集 背景 Node Exporter就可以用来采集机器的各项指标&#xff0c;从而监控机器的状态。 如果机器上运行了一些小脚本&#xff0c;想要对其进行监控但又不想用上一些在代码里做信息采集的SDK那么重&#xff0c;比如只是单纯想要监控该脚本是…

【C语言】红黑树

红黑树 1.二叉查找树 首先要了解的是二叉查找树&#xff0c;也称为二叉排序树&#xff0c;优点是在节点均匀分布的情况下&#xff0c;查找效率更高&#xff0c;缺点是&#xff0c;如果节点分布在一侧&#xff0c;查找时间就会约等于数组从头到尾的去查找。 二叉查找树的子树…

24/8/8算法笔记 决策回归树

from sklearn.tree import DecisionTreeRegressor from sklearn import tree import numpy as np import matplotlib.pyplot as plt 创建数据 X_train np.linspace(0,2*np.pi,40).reshape(-1,1)#训练数据就是符合要求的二维数据 #二维&#xff1a;[[样本一].[样本二]&#x…

服务器数据恢复—Raid5阵列热备盘上线过程中断导致阵列崩溃的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 两组分别由4块SAS硬盘组建的raid5磁盘阵列&#xff0c;ext3文件系统lvm结构。 磁盘阵列中一块硬盘离线&#xff0c;热备盘自动上线替换离线硬盘并开始同步数据。在热备盘同步数据的过程中该组raid中另外一块硬盘出现故障掉线&#xff…

Docker最佳实践(六):安装Nacos

大家好&#xff0c;欢迎各位工友。 本篇呢我们就来演示一下如何在Docker中部署nacos容器&#xff0c;市面上的教程多多少少都有点小坑&#xff0c;博主磕磕绊绊测试了好几次&#xff0c;才算解决此问题。 1. 拉取Nacos镜像 首先&#xff0c;拉取对应版本的Nacos镜像文件。可以…

【ACM出版,EI稳定检索】第四届信号处理与通信技术国际学术会议(SPCT 2024)

第四届信号处理与通信技术国际学术会议&#xff08;SPCT 2024&#xff09; 2024 4th International Conference on Signal Processing and Communication Technology 重要信息 大会官网&#xff1a;www.icspct.com 大会时间&#xff1a;2024年12月27-29日 大会地点&#xff1a…

Nodejs实现图片加水印 【使用jimp】

Nodejs实现图片加水印 【使用jimp】 先看效果 我们将使用jimp实现图片加上水印&#xff0c;可以结合路由进行用户上传后处理该图片生成带水印的图片返回个用户 const path require("path"); const jimp require("jimp");/*** 给一张图片加水印* para…

【C语言篇】自定义类型:联合体和枚举详细介绍

文章目录 联合体联合体类型的声明联合体的特点联合体和结构体对比联合体大小的计算判断大小端 枚举枚举类型的声明枚举类型的优点枚举的使用 联合体 联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 但是编…

第10章 无持久存储的文件系统 (1)

目录 前言 10.1 proc文件系统 10.1.1 /proc 内容 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;查看后续文章。 前言 即存在于内存中的文件系统。如&#xff1a; proc&#xff1a; sysfs&#xff1a; 即/sys目录。 内容不一定是ASCII文本&#xff0c;可能是二进…

Delaunay三角化重要性质,最小角最大化

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 最大化最小角 推论 有点集P是一般点集&#xff08;没有多点共线&#xff0c;没有4点共圆&#xff09;&#xff0c;那么该点集的delauney三角后的最小角不小于其他非de…

HTML5+CSS3笔记(Xmind格式):第五天

Xmind鸟瞰图&#xff1a; 简单文字总结&#xff1a; HTML5CSS3知识总结&#xff1a; canvas坐标&#xff1a; 画图的基本步骤&#xff1a; 1.创建画布 2.获取画布 3.开始路径规划 4.规定画笔颜色 5.规定画笔粗细 6.开始作…

OLAP与OLTP:数据处理系统的两种核心架构

目录 一、什么是OLAP&#xff1f; 二、什么是OLTP&#xff1f; 三、OLAP与OLTP的主要区别 四、结论 在数据管理和分析的领域中&#xff0c;OLAP&#xff08;在线分析处理&#xff09;和OLTP&#xff08;在线事务处理&#xff09;代表了两种重要的数据处理模式。它们在功能、目标…

MoE-LLaVA: Mixture of Experts for Large Vision-Language Models

发表时间&#xff1a;6 Jul 2024 论文链接&#xff1a;https://arxiv.org/pdf/2401.15947 作者单位&#xff1a;Peking University Motivation&#xff1a;最近的进展表明&#xff0c;扩展大型视觉语言模型 (LVLM) 有效地提高了下游任务的性能。然而&#xff0c;现有的缩放方…

uniapp版本更新除了plus.runtime.getProperty的解决办法

以下是展示图 带尺寸的图片: 首先把以下代码放到想要更新弹出的页面 //template部分<uni-popup ref"popup" background-color"#fff"><versionUp handleCloseVersion"closeVersion"></versionUp></uni-popup>//script…

支付宝开放平台竟出现一张神秘人脸!

前言 ​ 我因一个单子来到支付宝开放平台来。在将其加入书签的时候&#xff0c;我发现出现了个神秘的人脸 一张笑容明媚的脸&#xff0c;就是出现的时候不太对 正常的收藏网址 应该是显示对应log 就不继续找相关例子了 ​ 添加书签的页面&#xff0c;本该出现log的地方缺出现了…

SQL注入实例(sqli-labs/less-23)

0、初始网页 1、闭合方式判断 闭合符号为单引号&#xff0c;通过测试发现过滤了注释&#xff0c;所以直接闭合 2、确定查询表的列数 确定查询表的列数为3列 ?id1 order by 3 3、确定回显位置 回显位置为第二列和第三列 ?id-1 union select 1,2,3 4、查看当前登录和数据…