反射助你无痛使用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

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

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

相关文章

软信天成:数据安全管理解决方案分享

近年来,随着数据环境日趋复杂多变和潜在的数据隐私泄露风险潜伏,如何确保企业数据安全已成为众多企业亟待面对与妥善处理的重要问题。 为了应对这一严峻的现实挑战,软信天成凭借专业的知识体系和丰富的实战经验积累,总结出了一套…

Java多线程并发篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、ReadWriteLock 读写锁二、共享锁和独占锁三、重量级锁(Mutex Lock)四、轻量级锁前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给…

Spring MVC中的一些常用注解

目录 RequestMapping 实现路由映射 限制请求方式 PathVariable 从url中获取变量的值 更改绑定参数的名字 RequestParam 可以传递集合 更改绑定参数的名字 可修改是否为必传参数 RequestBody 获取请求正文的内容 可修改是否为必传参数 RequestPart 可以支持上传…

new mars3d.layer.GeoJsonLayer({实现图标点billboard贴模型聚合效果

说明: 1.【mars3d】的依赖库cesium本身是不支持贴地/贴模型操作的 2.sdk内部异步计算了数据的贴地/高度值之后,更新到图层上实现贴地/贴模型效果的 3.相关的示例链接: 1.功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 4.相关的计算…

【Python数据可视化】matplotlib之绘制三维图形:三维散点图、三维柱状图、三维曲面图

文章传送门 Python 数据可视化matplotlib之绘制常用图形:折线图、柱状图(条形图)、饼图和直方图matplotlib之设置坐标:添加坐标轴名字、设置坐标范围、设置主次刻度、坐标轴文字旋转并标出坐标值matplotlib之增加图形内容&#x…

精确掌控并发:滑动时间窗口算法在分布式环境下并发流量控制的设计与实现

这是《百图解码支付系统设计与实现》专栏系列文章中的第(15)篇,也是流量控制系列的第(2)篇。点击上方关注,深入了解支付系统的方方面面。 上一篇介绍了固定时间窗口算法在支付渠道限流的应用以及使用redis…

消息的发送与接收

消息的发送与接收 消息的发送与接收不仅仅是在于聊天功能的实现。其实还有很多种情况也算"消息的发送与接收"。而且我们还可以通过多种方法去实现。我们可以基于实际情况来选择。 WebSocket实现 node做后端。找了好多,前端页面总是用到了jQuery&#x…

lvgl简介

LVGL(Light and Versatile Graphics Library)是一个开源的图形用户界面库,旨在提供轻量级、可移植、灵活和易于使用的图形用户界面解决方案。 它适用于嵌入式系统,可以在不同的操作系统、微控制器和图形加速器上运行。LVGL的核心…

1.go安装及相关配置

目录 概述下载基本命令Go build环境设置 结束 概述 下载 官网速递 选择 1.20.x 一个是因为是次新版本,另一个,mac 系统是 10.13.6 ,1.20.x 是最后一个支持此版本的。 环境变量 GOROOT go的安装目录(将go安装到哪里 which go、cat /etc/p…

高并发IO底层原理

1 概述 IO底层原理是隐藏在Java编程知识之下的基础知识,是开发人员必须掌握的基本原理。本文从操作系统的底层原理入手,通过图文的方式为大家深入剖析高并发IO的底层原理,并介绍如何通过设置来让操作系统支持高并发。 2 IO读写的基本原理 为…

文件模块常用api

文件模块常用api 文件夹常用操作 文件夹操作 fs.mkdir fs.rmdir 需要是空目录 题目:递归删除目录* 串行/并行删除文件*

【电商API】DIY网络爬虫收集电商数据

DIY网络爬虫收集电商数据 网络爬虫是最常见和使用最广泛的数据收集方法。DIY网络爬虫确实需要一些编程知识,但整个过程比一开始看起来要简单得多。 当然,爬虫的有效性取决于许多因素,例如目标的难度、网站方的反爬虫措施等。如果将网络抓取用…

机器学习根据金标准标记数据-九五小庞

根据金标准标记数据是一种在机器学习和数据科学中常见的操作,主要用于评估分类模型的性能。其基本步骤如下: 收集数据:首先需要收集相关领域的原始数据,这些数据通常来自不同的来源和渠道。数据清洗和预处理:在这一步…

soft212期末

文章目录 安卓填空题选择题 C# 安卓 Dalvik中得到Dx工具会把部分class文件转换成dex文件。 如果希望在XML布局文件中调用颜色资源,可以使用color调用 Android程序入口的Activity是在AndroidManifest.xml文件中注册的 Android中查看应用程序日志的工具是LogCat Dal…

JavaScript面向对象之实践项目

1、cat项目 (1)需要修改的原代码 (2)修改要求 使用括号表示法将name属性的值存储在变量catName中。使用点表示法运行greeting()方法。将color属性值更新为白。重写greeting() 方法,使它的问候语为"孟买猫碳头对…

良心推荐!几款收藏的神级IDEA插件分享

本文已收录至Github,推荐阅读 👉 Java随想录 微信公众号:Java随想录 文章目录 CodeGlanceGsonFormatPOJO to JsonRainbow BracketsTranslationLombokMaven HelperAlibaba Java Code GuidelinesGenerateAllSetterMybatisXChinese (Simplified…

Tomcat10.X部署老版本axis2 webservice项目不生效

目录 一、使用场景 二、问题描述 三、原因排查 四、解决方案 一、使用场景 原来项目是OpenJDK8tomcat9构建,现在需要升级到OpenJDK17tomcat10的组合。原来的webservice项目打包成aar格式,通过axis2部署在tomcat上。 二、问题描述 在配置好jdk和to…

【软件测试学习笔记1】测试基础

1.软件测试的定义 软件的定义:控制计算机硬件工作的工具 软件的基本组成:页面客户端,代码服务器,数据服务器 软件产生的过程:需求产生(产品经理),需求文档,设计效果图…

NEAU_Python程序设计结课作业

1.身份证号合法性判别 【问题描述】我国身份证号码由数字与字母混合组成。早期身份证由15位数字构成。后来考虑到千年虫问题((15位的身份证号码只能为150c年1月1日到9年12月31日出生的人确号),所以又增加了18位身份证号码编号规则。最后—位(第18位)校验…

用二维码介绍产品详情,扫码查看图文并茂的宣传册

传统的产品宣传方式,往往以产品手册、宣传单等纸质物料为主,更新成本高昂,一旦修改内容,就必须重新印刷,而且不易携带和保存,影响宣传效果和客户体验。 为了避免上述问题,可以在草料上搭建产品…