写点什么

浅尝 semantic kernel

作者:为自己带盐
  • 2025-01-16
    河北
  • 本文字数:3477 字

    阅读完需:约 11 分钟

浅尝semantic kernel

背景

前些天使用直接对接大模型接口的方式,在我们自己的系统里对接了腾讯混元和 DeepSeek,并且封装了一个内部的函数,可以很方便的增加其他 OpenAPI 风格的模型到系统里来,而不用做很大的调整(长这样👇,也写过一篇文章)。



但事实上,已经有很多大模型的开发框架已经帮我们完成了集成多种模型的功能,大多数场景下,我们没必要自己再去造一个轮子。


更重要的原因是,单纯的自己去对接各个厂家的 api,也就意味着,我们想利用大模型来完成的一些真正落地到项目本身来提高效率或者的功能,都需要我们自己写代码完成,我的意思是尽管都是 openapi 兼容的风格,但实际开发起来,工作量还是有的。比如我们想要使用 function call 的功能来实现调用本地的方法,这里面就涉及到一些复杂的 tool 参数传递,尤其是当你的本地方法相对复杂的时候,在通过硬编码的方式去执行相关操作,会非常痛苦,很考验耐力!


因此,我们需要一个框架来帮我们完成这些工作。

sk 框架

关于 Semantic Kernel(以下简称 sk)的介绍,以及基本的使用方法,强烈推荐直接看官网内容:https://learn.microsoft.com/en-us/semantic-kernel/overview/


我这里简单介绍一下,sk 就是微软推出的轻量级,开源,的开发套件,主要目的是针对构建 AI Agent 的场景,只需要简单的配置,我们就可以集成多加大模型作为底座支撑,方便的把本地方法构建为插件,集成到 sk 当中,来实现类似“小爱同学,把灯打开”之类的操作,使 AI 真正和我们本地的系统相结合,完成一些以前只能靠人工完成的重复性工作,比如审核材料,查找信息等。


sk 不单单支持微软的 C#语言,还提供 Python 和 Java 语言的支持,非常适合企业开发,来增强现有的应用服务能力。


类似的开发框架还有 LangChain(python),CrewAI,AutoGen 等(这些咱都没用过哈,也是查了一下才知道,这有一篇介绍性的文章,或者大家自己 gpt 一下哈)


集成步骤

⚠️__说明,因为我也是前两天才开始接触 sk 并尝试着将其集成到现有的项目中,小有所成便兴奋的想发文记录,所以给出的流程,代码等仅供参考,后续挖掘更多 sk 的能力后,会再次发文。

添加引用

使用习惯的方式,把 sk 的 nuget 包安装到项目里


  <ItemGroup>    <!--其他包-->    <PackageReference Include="Microsoft.SemanticKernel" Version="1.33.0" />  </ItemGroup>
复制代码


基本上,在需要使用 sk 能力的地方,需要引入下面这几个命名空间


using Microsoft.SemanticKernel;using Microsoft.SemanticKernel.ChatCompletion;using Microsoft.SemanticKernel.Connectors.OpenAI;
复制代码

注入服务

官网的 quick start 引导页里给出的例子是使用的控制台程序,而通过依赖注入的方式将 sk 注入到项目中的例子也给了,只有阅读官方文档就可以找到。


此外,官方的仓库里还给出了一些代码案例,比如👉:https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/GettingStarted/Step4_Dependency_Injection.cs


还有这个👉:https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/Demos/HomeAutomation


强烈推荐先看一下官方的介绍。


我这里就是使用依赖注入的方式来注入 sk 服务


private static IServiceCollection AddSemanticKernel3(this IServiceCollection services){    string deployment = "{模型名字,如gpt-3.5-turbo}";    var apikey = "{申请的apikey}";    services.AddKernel();    Uri uri = new Uri( "{端点地址,自己部署网关的话就改成自己的网关地址,如https://... .openai.azure.com/或者本地的网关}");#pragma warning disable SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。    services.AddOpenAIChatCompletion(deployment, uri, apikey);#pragma warning restore SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
return services;}
复制代码

准备一个本地服务

这里我们可以直接使用默认的日志服务,这也是官网教程里给出的例子,或者自己手搓一个,也可以直接集成系统之前写好的服务。


我这里是直接集成的原来的接口服务


public interface IUserAnswerRecordRepo : IExaminationRepository<UserAnswerRecord>{    //各种接口服务    //...}
复制代码

增加插件

插件这个环节非常重要,算是 sk 框架最大的特色之一了,它提供了很多集成插件的方式,我这里只介绍使用本地函数封装成插件的方式


public class RecordSearchPlugin(IServiceProvider serviceProvider){    private IServiceProvider _serviceProvider = serviceProvider;        [KernelFunction("get_userrecord_by_id")]    [Description("Get the record by id")]    [return: Description("The record of the user; will return null if the user dose not exist")]    public async Task<DbServices.Dtos.Exam.UserAnswerRecord.UserAnswerSubmitRecordDto?> GetUserRecordByIdAsync(long id)    {        var _recordRepo = _serviceProvider.GetRequiredService<IRecordBase>();        if (!await _recordRepo.getAnyAsync(u => u.Id == id))        {            return null;        }        return (await _recordRepo.getOneAsync(u => u.Id == id))            .Adapt<DbServices.Dtos.Exam.UserAnswerRecord.UserAnswerSubmitRecordDto>();    }}
复制代码


这个插件的功能跟简单就是提供一个 id,然后找到相应的记录。注意,插件的封装要保证单一原则,如果我们需要复杂的交互,可以封装多个插件,但最大不要超过 20 个,官方对此给出的解释如下。



OpenAI 也给出过类似的规定:https://platform.openai.com/docs/guides/function-calling#keep-the-number-of-functions-low-for-higher-accuracy


还有就是,我这里面给插件的提示词用的都是英文,因为我还是按照文档的介绍来改造的,所以就先用英文进行的测试,实际上使用中文应该也是 ok 的。

调用

事实上,在调用之前,我们还可以编排一下插件的执行逻辑,这里我还没有试到那一步,所以先跳过了,官方的介绍在这里👉:https://learn.microsoft.com/en-us/semantic-kernel/concepts/planning?pivots=programming-language-csharp


public class HomeController : Controller{    private readonly ILogger<HomeController> _logger;    private readonly Kernel _kernel;    private readonly IServiceProvider _serviceProvider;    public HomeController(ILogger<HomeController> logger,Kernel kernel, IServiceProvider serviceProvider)    {        _logger = logger;        _kernel = kernel.Clone();        _serviceProvider = serviceProvider;    }
public async Task<IActionResult> Test() { _kernel.Plugins.AddFromType<RecordSearchPlugin>("RecordSearch", _serviceProvider); // 获取聊天完成服务 var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>(); // 启用自动函数调用 OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, }; ChatHistory chatHistory = []; string? input = null; chatHistory.AddUserMessage("Please get the record which id is 617235768115590"); var chatResult = await chatCompletionService.GetChatMessageContentAsync( chatHistory, openAIPromptExecutionSettings, _kernel); Console.Write($"\nAssistant : {chatResult}\n"); return Content("查看控制台输出内容"); }}
复制代码


执行之后,看到控制台输出的内容



完成✌️

总结

浅尝基本就是这样啦,后续的工作就是一些优化方面的了,可以发挥无限的想象力去给我们的线上系统插上翅膀了,我感觉这就是本地化的“小爱同学”,比如以前审查资料,需要人一个个的去找,去查用户提交的信息,有了 AI 加持之后,我们可以像使唤家里的“小爱同学”一样,让它帮我们去完成这类重复性的工作。


比如给他一个命令“帮我把今天考试所有考 90 分的同学导出来”,或者更复杂一点,“帮我把今天考试所有得 90 分的同学导出来,并给他们发送邮件”等等。


当然了,基础的功能还是要我们自己来开发,AI 作为代理角色,完成的是以前系统操作人完成的工作,而不是开发者,或者不完全是开发者。


好了,先聊这么多,下次继续!


发布于: 刚刚阅读数: 4
用户头像

学着写代码 2019-04-11 加入

是一枚,热爱技术,天赋不高,又有点轴,的猿。。

评论

发布
暂无评论
浅尝semantic kernel_人工智能_为自己带盐_InfoQ写作社区