反射助你无痛使用Semantic Kernel接入离线大模型

本文主要介绍如何使用 llama 的 server 部署离线大模型,并通过反射技术修改 Semantic Kernel 的 OpenAIClient 类,从而实现指定端点的功能。最后也推荐了一些学习 Semantic Kernel 的资料,希望能对你有所帮助。

封面图片: Dalle3 - 反射狐

请添加图片描述

1. 引言

随着 AI 技术的快速发展,越来越多的开发者和企业开始利用 Semantic Kernel 来接入离线大模型,以此获取更精准的自然语言处理能力。SK 框架提供了一种解决方案,可以在编程中更加有效地利用大语言模型来解决复杂的问题。然而,许多开发者在使用过程中都遇到了一个共同的问题:无法将 Semantic Kernel 的 OpenAIClient 配置为连接到自定义端点。

2. 问题背景

在默认情况下,Semantic Kernel的OpenAIClient类被配置为连接到Azure OpenAI的官方端点。这对于大部分用户来说是没有问题的,但对于某些特殊场景的用户来说,这成了一个难题。比如,某些企业或个人无法直接访问OpenAI的官方端点,他们需要使用一个中间服务器进行过滤(如安全审计,内部令牌使用成本分配等),然后将请求转发到OpenAI。对于这些用户来说,能够指定OpenAIClient的端点是非常重要的。

另外,许多开发者希望能够使用像 vLLM, llama.cpp 等技术的托管开源模型,这些模型的端点与 Azure OpenAI 的端点不同,因此也需要能够指定端点的功能。

3. llama 的 server 服务部署与问题复现

其实前面的问题由来已久,作为一个饕餮,虽然早早的 start 了 Semantic Kernel 库,但是我也是养了很久才开始食用的。在使用的过程中,我很自然的从文档的快速开始尝试跑第一个示例。示例中使用的是 Azure OpenAI 的服务,虽然在去年四月份我就有了 GPT-4 的访问权限,但如果用作测试和折腾的话还是离线的更有性价比呀,然后就造成了我第一步就遇到了些许麻烦。

3.1 llama 的 server 服务部署

在 llama.cpp 项目中,有一个示例的 server 服务,可以用来部署离线大模型。在项目的 README 中,有详细的部署步骤,这里就不再赘述了。如果你不想自行编译,或过程中遇到各种问题,可以直接在 releases 中找到编译好的二进制文件。下载或编译成功后,我们可以通过以下命令来启动 server 服务:

./server.exe -m models/qwen1_8b-gguf.bin -c 2048 -ngl 20 --port 8000 -a qwen

其中,-m 参数指定了模型的路径,-c 参数指定了模型的上下文长度,-ngl 参数表示你想让多少个层的计算在GPU上进行,在使用CLBlast或cuBLAS编译的二进制文件时,通常可以提高性能,--port 参数指定了服务的端口,-a 参数指定了模型的名称。启动成功后,我们就可以打开浏览器,访问 http://localhost:8000 来测试服务是否正常运行。

server 服务提供的API是类似于 OpenAI 的 API 的,可以在 READNE 中找到详细的说明。

请添加图片描述

3.2 问题复现

在启动 server 服务后,我们就可以在 Semantic Kernel 中使用了。在 Semantic Kernel 的文档中,有一个示例,可以用来测试是否可以正常连接到 Azure OpenAI 的服务。我们可以将这个示例稍作修改,来测试是否可以连接到我们自己的 server 服务。以下是修改后的示例代码:

var builder = Kernel.CreateBuilder();
var aiclietn =  new OpenAIClient(
            new Uri("http://127.0.0.1:8000/v1"),
            new Azure.AzureKeyCredential("empty")
        );
builder.AddOpenAIChatCompletion("qwen",aiclietn);
var kernel = builder.Build();
// ... 省略部分代码

在修改后的示例代码中,我们将 OpenAIClient 的端点指定为了我们自己的 server 服务的端点。但是事情并没有像我们想象的那样顺利,我们在运行示例代码时,会遇到以下错误:

Unhandled exception. Microsoft.SemanticKernel.HttpOperationException: Service request failed.
Status: 404 (Not Found)

Content:
File Not Found
...

通过服务端的控制台日志,我们可以看到,服务端收到了请求,但是却返回了 404 错误。这是因为 OpenAIClient 的默认端点是 Azure OpenAI 的端点,而我们的 server 服务并不是 Azure OpenAI 的端点,因此会返回 404 错误。

{"timestamp":1705215286,"level":"INFO","function":"log_server_request","line":2731,"message":"request","remote_addr":"127.0.0.1","remote_port":6274,"status":404,"method":"POST","path":"/v1/openai/deployments/qwen/chat/completions","params":{"api-version":"2023-12-01-preview"}}

4. 解决方案

4.1 问题分析

OpenAIClientAzure.AI.OpenAI 库提供的,通过其源码我们不难发现,其 OpenAI 的服务地址是固定的我们无法修改,如果指定了endpoint,那么也就是意味着我们默认使用了 Azure OpenAI 的服务,其私有的变量_isConfiguredForAzureOpenAI将为true。下面的代码,则是该库中请求路径拼接的相关函数:

internal RequestUriBuilder GetUri(string deploymentOrModelName, string operationPath)
{
    RawRequestUriBuilder rawRequestUriBuilder = new RawRequestUriBuilder();
    rawRequestUriBuilder.Reset(_endpoint);
    if (_isConfiguredForAzureOpenAI)
    {
        rawRequestUriBuilder.AppendRaw("/openai", escape: false);
        rawRequestUriBuilder.AppendPath("/deployments/", escape: false);
        rawRequestUriBuilder.AppendPath(deploymentOrModelName, escape: true);
        rawRequestUriBuilder.AppendPath("/" + operationPath, escape: false);
        rawRequestUriBuilder.AppendQuery("api-version", _apiVersion, escapeValue: true);
    }
    else
    {
        rawRequestUriBuilder.AppendPath("/" + operationPath, escape: false);
    }

    return rawRequestUriBuilder;
}

问题的根源找到了,那么解决方案也就呼之欲出了,我们只需要将_isConfiguredForAzureOpenAI设置为false,就可以指定端点了。但是这个事情并没有那么简单,因为_isConfiguredForAzureOpenAI是一个私有的变量,我们无法直接修改它的值。虽然项目是开源的,但是我们也不可能去修改源码,然后自己编译一个库,这样做的事情有些复杂了。

4.2 解决方案:反射

那么有没有什么办法可以修改私有变量的值呢?答案是肯定的,那就是反射。虽然通过源码我们可以发现这个OpenAIClient类使用了partial关键字,partial关键字允许你将一个类、结构或方法的定义分散在多个文件中。然而,要注意的是,partial关键字只能在同一个程序集(即同一个DLL或EXE)中的文件之间使用。

要修改私有变量的值,使用反射就没有这种限制了。反射是.NET框架提供的一种强大的技术,它允许我们在运行时获取类型的信息,并操作这些类型的成员(如类的字段和方法)。具体来说,我们可以使用反射来修改OpenAIClient类的私有字段_isConfiguredForAzureOpenAI的值。当这个字段的值被设置为false时,OpenAIClient将能够连接到任何端点,从而解决了我们的问题。关于反射的更多信息可以参考官方文档的高级主题:反射。

在.NET中,我们可以使用System.Reflection命名空间中的类来进行反射操作。以下是一个简单的例子,演示了如何使用反射修改_isConfiguredForAzureOpenAI的值:

using Azure.AI.OpenAI;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Reflection;

var builder = Kernel.CreateBuilder();
var aiclietn =  new OpenAIClient(
            new Uri("http://127.0.0.1:8000/v1"),
            new Azure.AzureKeyCredential("empty")
        );
// 获取_isConfiguredForAzureOpenAI字段的引用
var field = typeof(OpenAIClient).GetField("_isConfiguredForAzureOpenAI", BindingFlags.NonPublic | BindingFlags.Instance);
// 修改_isConfiguredForAzureOpenAI字段的值
if (field != null)
{
    field.SetValue(aiclietn, false);
}

builder.AddOpenAIChatCompletion("qwen",aiclietn);
// ... 省略部分代码

修改完成后,我们使用 dotnet run 命令运行示例代码,就可以看到我们的示例代码终于跑起来了。

请添加图片描述

4.3 更好的解决方案:修改源码

虽然反射可以解决我们的问题,但它并不是最佳的解决方案。反射操作可能会引入一些额外的复杂性和性能开销,而且它依赖于私有字段的名称,如果未来这个名称发生改变,我们的代码就可能会失效。

一个更好的解决方案是直接在OpenAIClient类的源码中添加一个公共方法,用于设置_isConfiguredForAzureOpenAI的值。这样,我们就可以直接调用这个方法来修改字段的值,而不需要使用反射。我已经在GitHub上提交了一个修改,添加了这样一个方法:

public void SetIsConfiguredForAzureOpenAI(bool value)
{
    _isConfiguredForAzureOpenAI = value;
}

5. 最后

通过反射和源码修改,我们成功解决了Semantic Kernel接入离线大模型的问题。这个解决方案将使Semantic Kernel能够与像vLLM, llama.cpp等技术的托管开源模型进行交互,同时也允许应用程序指定端点,满足了大家迫切的需求,希望这篇文章能对你有所帮助。

最后推荐一些学习Semantic Kernel的资料:

  • Semantic Kernel 官方文档:https://learn.microsoft.com/zh-cn/semantic-kernel/overview/?wt.mc_id=DT-MVP-5005195
  • Semantic Kernel 仓库:https://github.com/microsoft/semantic-kernel?wt.mc_id=DT-MVP-5005195
  • Semantic Kernel CookBook:https://github.com/kinfey/SemanticKernelCookBook?wt.mc_id=DT-MVP-5005195
  • LLM-Server:https://github.com/kinfey/SemanticKernel-Local-LLM-Server?wt.mc_id=DT-MVP-5005195

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java零基础教学文档servlet(1)

【Web开发和HTTP协议】 1. Web开发概述 1.1 web概述 万维网(英语:World Wide Web)亦作WWW、Web、全球广域网,是一个透过互联网访问的,由许多互相链接的超文本组成的信息系统。英国科学家蒂姆伯纳斯-李于1989年发明了…

更换为mainwindow.ui更新工程架构

文章目录 前言一、新建带mainwindow.ui的工程1.新建工程2. 添加工程模块添加opencv的库3.添加资源3.1工程上添加资源3.2引用资源 4.添加曲线文件4.1 复制关键文件到新工程4.2 新进显示曲线的ui带.h的为了方面名字取一样4.3添加曲线显示控件4.4 添加工具 5. 添加曲线.h文件内容6…

OpenCV-Python(39):Meanshift和Camshift算法

目标 学习了解Meanshift 和Camshift 算法在视频中找到并跟踪目标 Meanshift 原理 Meanshift算法是一种基于密度的聚类算法,用于将数据点划分为不同的类别。它的原理是通过数据点的密度分布来确定聚类中心,然后将数据点移动到离其最近的聚类中心&#…

UIAlertController简单使用-swift

UIAlertControlle时IOS的对话框控制器(警报控制器),简单使用方法如下: 步骤都一样,先是创建UIAlertController,然后创建UIAlertAction,再将UIAlertAction添加到UIAlertController中,…

PyTorch深度学习实战(30)——Deepfakes

PyTorch深度学习实战(30)——Deepfakes 0. 前言1. Deepfakes 原理2. 数据集分析3. 使用 PyTorch 实现 Deepfakes3.1 random_warp.py3.2 Deepfakes.py 小结系列链接 0. 前言 Deepfakes 是一种利用深度学习技术生成伪造视频和图像的技术。它通过将一个人的…

TongLINKQ(2):TongLINKQ服务端安装

1 安装前的准备 明确应用(JDK)和TongLINK/Q的版本、位数(要么都是32位,要么都是64位)TLQ安装程序使用InstallAnywhere打包而成,因此需要JDK1.5及以上版本。 2 安装步骤 3 选择安装安装包 目前TongLINKQ的…

相信我,努力真的有用!

2023年对很多人来说都是异常艰辛的一年,大环境下的每个人都面对着或多或少的挑战,在这一年的时间里,身边的朋友陆陆续续的跳槽、创业、再就业,结婚,生娃,回老家,每个人渐渐的在时代面前或妥协或…

快速入门Java NIO(New I/O)的网络通信框架--Netty

Netty 入门 了解netty前需要对nio有一定认识,该笔记基础来自bilinbili黑马,在此基础上自己学习的笔记,添加了一些自己的理解 了解java 非阻塞io编程 1. 概述 1.1 Netty 是什么? Netty is an asynchronous event-driven network application framework for rapid …

Linux系统的通配符* ,你可能还不知道的一些规则 。

最近老被同学问到关于通配符操作的问题,本来觉得是一个很简单的问题,结果它和其它命令一结合,就很难给出合理的理解 。进行了很多实验,总是难以有个满意的答案 。于是决定要好好研究一番 ,终于在多次的测试和验证过程中…

ElasticSearch分布式搜索引擎(两万字详解)

文章目录 ElasticSearch分布式搜索引擎1.了解ESELK技术栈elasticsearch和lucene为什么不是其他搜索技术?总结倒排索引正向索引倒排索引正向和倒排 es的一些概念文档和字段索引和映射mysql与elasticsearch 2.安装elasticsearch1.部署单点es1.1.创建网络1.2.拉取镜像1…

N5181A/安捷伦Agilent N5181A信号发生器

181/2461/8938产品概述: 规格(说明书):表示已校准的仪器在工作温度范围0-55C内存放至少2小时,除非另有说明,并经过45分钟预热期后的保证性能。的指标包括测量不确定度。除非另有说明,本文档中的…

2023年度总结 - 职业生涯第一个十年

2023年只剩下最后一周,又到了一年一度该做年末总结的时候了。 回想起去年,还有人专门建立了一个关于年度总结文章汇总的仓库。读了很多篇别人写的,给了我很多的触动和感想。这里的每篇文章都是关于某个人这一整年的生活和工作的轨迹啊。即使你…

12-桥接模式(Bridge)

意图 将抽象部分与它的实现部分分离,使他们可以独立地变化 个人理解 一句话概括就是只要是在抽象类中聚合了某个接口或者抽象类,就是使用了桥接模式。 抽象类A中聚合了抽象类B(或者接口B),A的子类的方法中在相同的场…

流程图用什么软件做?五款优质在线绘制工具看一看

流程图用什么软件做?现在,流程图已经成为了我们工作中不可或缺的工具。它能够清晰地展示各个步骤之间的关系,使我们更好地理解并优化流程。那么,在众多的流程图绘制工具中,哪一款最适合你呢?下面就给大家介…

【办公技巧】ppt修改全部字体怎么改?

制作完PPT之后,想要更换ppt中的字体,有没有什么快捷的方法呢?今天分享两个方法,一键修改ppt文件字体。 方法一: 找到功能栏中的编辑选项卡,点击替换 – 替换字体,在里面选择我们想要替换的字体…

单细胞转录组学对代谢功能障碍相关脂肪变性肝病的类器官模型进行分析

前言 最近接触比较多肝纤维化项目,包括空转、单细胞和普通的BULK转录组,本文是肝脏疾病类器官构建,所以结果是比较确定的,只是对比不同处理和培养哪种效果更好,适合了解纤维化进展和哪些分子和细胞参与,以…

数据可视化大屏自适应,保持比例不变形,满足不同分辨率的需求——利用transform的scale属性缩放,缩放整个页面。

文章目录 一、需求背景:二、需求分析:三、选择方案:四、实现代码:五、效果预览:六、封装组件: 一、需求背景: 数据可视化大屏是一种将数据、信息和可视化效果集中展示在一块或多块大屏幕上的技…

项目中使用iframe引入html 解决路由错乱问题以及父子组件传值调用方法

iframe与外部之间传值 父组件 <iframeid"iframe"src"luckysheet/index.html"frameborder"0"scrolling"no"style"width: 100%; height: 60vh; border: 0"/>const frame document.getElementById(iframe);frame.onloa…

8. 《自动驾驶与机器人中的SLAM技术》基于保存的自定义NDT地图文件进行自动驾驶车辆的激光定位

目录 1. 为 NDT 设计一个匹配度评估指标&#xff0c;利用该指标可以判断 NDT 匹配的好坏。 2. 利用第 1 题的指标&#xff0c;修改程序&#xff0c;实现 mapping 部分的回环检测。 3. 将建图结果导出为 NDT map&#xff0c;即将 NDT 体素内的均值和协方差都存储成文件。 4.…

学习记录11-SPI通信(软件)

目录 前言 一、引脚定义 二、代码 1.初始化 2.操作代码 三、验证 前言 对SPI进行结构封装&#xff0c;方便使用。方便讲解&#xff0c;用W&#xff12;&#xff15;Q&#xff16;&#xff14;芯片进行讲解 一、引脚定义 #define SPI_CS_PROT GPIOB //CS接线引脚通道…