如何通过SK集成chatGPT实现DotNet项目工程化?

news2024/11/20 10:42:12

智能助手服务

以下案例将讲解如何实现天气插件

当前文档对应src/assistant/Chat.SemanticServer项目

首先我们介绍一下Chat.SemanticServer的技术架构

SemanticKernel 是什么?

Semantic Kernel是一个SDK,它将OpenAI、Azure OpenAI和Hugging Face等大型语言模型(LLMs)与传统的编程语言如C#、Python和Java集成在一起。Semantic Kernel通过允许您定义可以在几行代码中链接在一起的插件来实现这一目标。

如何集成使用SemanticKernel

以下是添加IKernel,OpenAIOptions.ModelOpenAIOptions.Key在一开始使用了builder.Configuration.GetSection("OpenAI").Get<OpenAIOptions>();绑定。对应配置文件,OpenAIChatCompletion则是用于直接请求OpenAI。

"OpenAI": {
    "Key": "",
    "Endpoint": "",
    "Model": "gpt-3.5-turbo"
  }
builder.Services.AddTransient<IKernel>((services) =>
{
    var httpClientFactory = services.GetRequiredService<IHttpClientFactory>();
    return Kernel.Builder
        .WithOpenAIChatCompletionService(
            OpenAIOptions.Model,
            OpenAIOptions.Key,
            httpClient: httpClientFactory.CreateClient("ChatGPT"))
        .Build();
}).AddSingleton<OpenAIChatCompletion>((services) =>
{
    var httpClientFactory = services.GetRequiredService<IHttpClientFactory>();
    return new OpenAIChatCompletion(OpenAIOptions.Model, OpenAIOptions.Key,
        httpClient: httpClientFactory.CreateClient("ChatGPT"));
});

在项目中存在plugins文件夹,这是提供的插件目录,在BasePlugin目录下存在一个识别意图的插件。

config.json对应当前插件的一些参数配置,

{
  "schema": 1,
  "type": "completion",
  "description": "获取用户的意图。",
  "completion": {
    "max_tokens": 500,
    "temperature": 0.0,
    "top_p": 0.0,
    "presence_penalty": 0.0,
    "frequency_penalty": 0.0
  },
  "input": {
    "parameters": [
      {
        "name": "input",
        "description": "用户的请求。",
        "defaultValue": ""
      },
      {
        "name": "history",
        "description": "对话的历史。",
        "defaultValue": ""
      },
      {
        "name": "options",
        "description": "可供选择的选项。",
        "defaultValue": ""
      }
    ]
  }
}

skprompt.txt则是当前插件使用的prompt

加载插件

在这里我们注入了IKernel

    private readonly IKernel _kernel;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly RedisClient _redisClient;
    private readonly ILogger<IntelligentAssistantHandle> _logger;
    private readonly OpenAIChatCompletion _chatCompletion;

    public IntelligentAssistantHandle(IKernel kernel, RedisClient redisClient,
        ILogger<IntelligentAssistantHandle> logger, IHttpClientFactory httpClientFactory,
        OpenAIChatCompletion chatCompletion)
    {
        _kernel = kernel;
        _redisClient = redisClient;
        _logger = logger;
        _httpClientFactory = httpClientFactory;
        _chatCompletion = chatCompletion;

        _redisClient.Subscribe(nameof(IntelligentAssistantEto),
            ((s, o) => { HandleAsync(JsonSerializer.Deserialize<IntelligentAssistantEto>(o as string)); }));
    }

然后准备加载插件。


//对话摘要  SK.Skills.Core 核心技能
_kernel.ImportSkill(new ConversationSummarySkill(_kernel), "ConversationSummarySkill");

// 插件根目录
var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins");

// 这个是添加BasePlugin目录下面的所有插件,会自动扫描
var intentPlugin = _kernel
                .ImportSemanticSkillFromDirectory(pluginsDirectory, "BasePlugin");

// 这个是添加Travel目录下面的所有插件,会自动扫描
var travelPlugin = _kernel
                .ImportSemanticSkillFromDirectory(pluginsDirectory, "Travel");

// 这个是添加ChatPlugin目录下面的所有插件,会自动扫描
var chatPlugin = _kernel
                .ImportSemanticSkillFromDirectory(pluginsDirectory, "ChatPlugin");

// 这个是添加WeatherPlugin类插件,并且给定插件命名WeatherPlugin
var getWeather = _kernel.ImportSkill(new WeatherPlugin(_httpClientFactory), "WeatherPlugin");

使用插件,首先我们创建了一个ContextVariablesinput则是GetIntent插件中的的{{$input}}options则对应{{$options}}getIntentVariables则将替换对应的prompt中响应的参数。

var getIntentVariables = new ContextVariables
            {
                ["input"] = value,
                ["options"] = "Weather,Attractions,Delicacy,Traffic" //给GPT的意图,通过Prompt限定选用这些里面的
            };
string intent = (await _kernel.RunAsync(getIntentVariables, intentPlugin["GetIntent"])).Result.Trim();

plugins/BasePlugin/GetIntent/skprompt.txt内容

{{ConversationSummarySkill.SummarizeConversation $history}}
用户: {{$input}}

---------------------------------------------
提供用户的意图。其意图应为以下内容之一: {{$options}}

意图: 

意图识别完成以后,当执行完成GetIntentintent相应会根据options中提供的参数返回与之匹配的参数,

然后下面的代码将根据返回的意图进行实际上的操作,或加载相应的插件,比如当intent返回Weather,则首先从chatPlugin中使用Weather插件,并且传递当前用户输入内容,在这里将提取用户需要获取天气的城市。

完成返回以后将在使用MathFunction = _kernel.Skills.GetFunction("WeatherPlugin", "GetWeather")的方式获取WeatherPlugin插件的GetWeather方法,并且将得到的参数传递到_kernel.RunAsync执行的时候则会掉用GetWeather方法,这个时候会由插件返回的json在组合成定义的模板消息进行返回,就完成了调用。

            ISKFunction MathFunction = null;
            SKContext? result = null;

            //获取意图后动态调用Fun
            if (intent is "Attractions" or "Delicacy" or "Traffic")
            {
                MathFunction = _kernel.Skills.GetFunction("Travel", intent);
                result = await _kernel.RunAsync(value, MathFunction);
            }
            else if (intent is "Weather")
            {
                var newValue = (await _kernel.RunAsync(new ContextVariables
                {
                    ["input"] = value
                }, chatPlugin["Weather"])).Result;
                MathFunction = _kernel.Skills.GetFunction("WeatherPlugin", "GetWeather");
                result = await _kernel.RunAsync(newValue, MathFunction);

                if (!result.Result.IsNullOrWhiteSpace())
                {
                    if (result.Result.IsNullOrEmpty())
                    {
                        await SendMessage("获取天气失败了!", item.RevertId, item.Id);
                        return;
                    }

                    var weather = JsonSerializer.Deserialize<GetWeatherModule>(result.Result);
                    var live = weather?.lives.FirstOrDefault();
                    await SendMessage(WeatherTemplate
                        .Replace("{province}", live!.city)
                        .Replace("{weather}", live?.weather)
                        .Replace("{temperature_float}", live?.temperature_float)
                        .Replace("{winddirection}", live?.winddirection)
                        .Replace("{humidity}", live.humidity), item.RevertId, item.Id);
                    return;
                }
            }
            else
            {
                var chatHistory = _chatCompletion.CreateNewChat();
                chatHistory.AddUserMessage(value);
                var reply = await _chatCompletion.GenerateMessageAsync(chatHistory);
                
                return;
            }

Weather的prompt

我会给你一句话,你需要找到需要获取天气的城市,如果存在时间也提供给我:
{{$input}}

仅返回结果,除此之外不要有多余内容,按照如下格式:
{
    "city":"",
    "time":""
}

WeatherPlugin获取天气插件


/// <summary>
/// 获取天气插件
/// </summary>
public class WeatherPlugin
{
    private static List<AdCode>? _codes;

    static WeatherPlugin()
    {
        var path = Path.Combine(AppContext.BaseDirectory, "adcode.json");
        if (File.Exists(path))
        {
            var str = File.ReadAllText(path);
            _codes = JsonSerializer.Deserialize<List<AdCode>>(str);
        }

        _codes ??= new List<AdCode>();
    }

    private readonly IHttpClientFactory _httpClientFactory;

    public WeatherPlugin(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [SKFunction, Description("获取天气")]
    [SKParameter("input", "入参")]
    public async Task<string> GetWeather(SKContext context)
    {
        var weatherInput = JsonSerializer.Deserialize<WeatherInput>(context.Result);
        var value = _codes.FirstOrDefault(x => x.name.StartsWith(weatherInput.city));
        if (value == null)
        {
            return "请先描述指定城市!";
        }

        var http = _httpClientFactory.CreateClient(nameof(WeatherPlugin));
        var result = await http.GetAsync(
            "https://restapi.amap.com/v3/weather/weatherInfo?key={高德天气api的key}&extensions=base&output=JSON&city=" +
            value.adcode);

        if (result.IsSuccessStatusCode)
        {
            return await result.Content.ReadAsStringAsync();
        }
        
        return string.Empty;
    }
}

public class WeatherInput
{
    public string city { get; set; }
    public string time { get; set; }
}

public class AdCode
{
    public string name { get; set; }

    public string adcode { get; set; }

    public string citycode { get; set; }
}

效果图

以上代码可从仓库获取

项目开源地址

体验地址:https://chat.tokengo.top/ (可以使用Gitee快捷登录)
Github : https://github.com/239573049/chat
Gitee: https://gitee.com/hejiale010426/chat

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

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

相关文章

2023年中国婚礼仪式服务行业发展趋势分析:市场集中度有望持续提升[图]

婚礼仪式服务指为备婚人群提供从婚礼筹备到结婚典礼所需的一系列相关产品及服务&#xff0c;婚礼仪式服务提供商包括一站式婚礼仪式服务提供商和垂直婚礼仪式服务提供商。 婚礼仪式服务提供商分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 婚礼仪式服…

将AirPods恢复到出厂设置方便快捷,用时不会超过一分钟

苹果最初的AirPods、AirPods 2、AirPod 3和AirPods Pro都有重置功能&#xff0c;可以将其恢复到出厂设置。如果你把无线耳机交给别人&#xff0c;或者你对它们有任何问题&#xff0c;这会派上用场。 以下是如何重置AirPods和‌AirPods Pro‌: 如何重置AirPods和AirPods Pro …

美芯片禁令再次扩大,波及英伟达、AMD以及intel等科技公司 | 百能云芯

拜登政府17日宣布&#xff0c;计划停止英伟达&#xff08;Nvidia&#xff09;、超微半导体以及英特尔等科技公司设计的先进AI芯片输出中国大陆&#xff0c;英伟达&#xff08;Nvidia&#xff09;昨日股价重挫4.68%至每股439.38美元&#xff1b;天风国际证券分析师郭明錤表示&am…

基于SVM+Webdriver的智能NBA常规赛与季后赛结果预测系统——机器学习算法应用(含python、ipynb工程源码)+所有数据集(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 特征提取1&#xff09;常规赛特征提取2&#xff09;季后赛特征提取&#xff08;1&#xff09;常规赛球队得分&#xff08;2&#xff09;球员、教练数据及数据整合 相关其它博客工程源代码下载其它资…

WebSocket:实现实时互动、数据推送的利器,你了解多少

WebSocket技术是一种基于TCP协议的全双工通信协议&#xff0c;它允许浏览器和服务器之间进行实时、双向的通信。相比传统的HTTP请求-响应模式&#xff0c;WebSocket提供了持久连接&#xff0c;可以实时地推送数据&#xff0c;减少了通信的延迟。 WebSocket的工作原理是通过建立…

视觉Slam面试题(不定时更新)

文章目录 0 引言1 单目、双目、深度相机和RGBD相机的区别2 特征点法与直接法的优缺点3 等距变换、相似变换、仿射变换、射影变换的区别4 单应矩阵、本质矩阵和基础矩阵的区别5 Slam中为什么用李群李代数6 解释Slam中的绑架问题7 ORB、SIFT和SURF特征点检测算法的区别8 什么是对…

QGIS如何给元素添加属性

选中图层&#xff0c;右键&#xff0c;属性

汽车上的A/C按键是做什么用的?

汽车上的A/C按键是做什么用的? 汽车上的a/c 键是空调制冷开关。A/C是空调的缩写&#xff0c;它的全称是air condition&#xff0c;理解为空气调节。它通过空调压缩机的不同运转方式来达到制冷或制暖的目的。 打开a/c 按键之后&#xff0c;因为空调压缩机是靠发动机工作的&…

【小白专用】安装Apache2.4+ 安装PHP8.2+ php与sql server 2008 r2连接测试教程

PHP安装 1、PHP下载 PHP For Windows: Binaries and sources Releases 注意&#xff1a; 1.要下载Thread Safe&#xff0c;否则没有php7apache2_4.dll这个文件 2.如果是64位系统要下载x64的&#xff0c;x86的不行 3.下载Zip 2、PHP解压安装 将Zip进行解压&#xff0c;里…

【六、docker中hyperf项目怎么进行跨域设置】

1、第一步就是新建跨域文件,即跨域中间件 跨域中间件的代码如下 <?phpdeclare(strict_types=1);namespace App\Middleware; namespace App\Middleware; namespace App\Middleware;use Hyperf\Context\Context; use Psr\Http\Message\ResponseInterface;

Linux文件管理与用户管理

一、查看文件内容 1、回顾之前的命令 cat命令、tac命令、head命令、tail命令、扩展&#xff1a;tail -f动态查看一个文件的内容 2、more分屏显示文件内容&#xff08;了解&#xff09; 基本语法&#xff1a; # more 文件名称 特别注意&#xff1a;more命令在加载文件时并不…

爆肝整理,性能测试-非GUI模式执行Jemter压测,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、上传脚本 把在…

如何利用vscode进行断点调试后端node开发的项目,如express或koa?

介绍 在我们平常开发中前端可以通过浏览器断点调试代码执行的过程&#xff0c;但是node项目后端通常用命令启动&#xff0c;但命令启动项目也只能打印日志信息&#xff0c;不能断点。所以这一章节教大家如何利用vscode进行断点调试。 步骤 配置launch.json文件 代码如下&…

matlab奇技淫巧——绘制三维地图

在数据处理工作中&#xff0c;常常会用到地图的绘制&#xff0c;最常用的自然是绘制平面的区域/全球地图&#xff0c;通过 worldmap(world) % 创建世界地图坐标区域 load coastlines % 导入海岸线数据 plotm(coastlat,coastlon)即可绘制&#xff0c;效果…

【eNSP】VLAN间通信

VLAN间通信 文章目录 一、使用路由器物理接口实现VLAN间通信实验拓扑图1、配置交换机Access接口和路由器IP地址LSW1AR1 2、验证 二、使用路由器子接口实验拓扑图1、路由器、交换机基本配置LSW1AR1 2、配置路由器子接口AR1 3、验证 三、使用VLANIF技术实现VLAN间通信原理图实验拓…

vue3弹窗中循环生成表单的校验和重置问题

应用场景&#xff1a; 1、弹框里的表单是根据后台返回的时段生成的&#xff0c;后台返回几个时段&#xff0c;就渲染几组表单。 -1- 重置&#xff1a;遍历每个表单&#xff0c;获取当前表单的引用&#xff0c;在resetFields() -2- 校验&#xff1a;创建一个数组来存储每个表单的…

MySQL学习(五)——索引

文章目录 1. 索引介绍2. 索引结构2.1 索引结构介绍2.2 二叉树2.3 B-Tree2.4 BTree2.5 Hash 3. 索引分类3.1 索引分类3.2 聚集索引和二级索引 4 索引语法4.1 语法介绍4.2 数据准备4.3 索引演示 5 SQL 性能优化5.1 执行频率查询5.2 慢查询日志5.3 profile详情5.4 explain 6 索引使…

谈谈 Redis 如何来实现分布式锁

谈谈 Redis 如何来实现分布式锁 基于 setnx 可以实现&#xff0c;但是不是可重入的。 基于 Hash 数据类型 Lua脚本 可以实现可重入的分布式锁。 获取锁的 Lua 脚本&#xff1a; 释放锁的 Lua 脚本&#xff1a; 但是还是存在分布式问题&#xff0c;比如说&#xff0c;一个客…

金融信息化研究所与YashanDB等单位启动金融多主数据库应用行动计划

10月13日&#xff0c;2023金融业 数据库技术大会在京成功召开。会上&#xff0c;金融信息化研究所与崖山数据库YashanDB、阿里巴巴、奥星贝斯、达梦、南大通用、华为、天翼云、万里数据库、优炫数据库共同启动金融多主数据库应用行动计划&#xff0c;并成立金融多主数据库应用…

基于SpringBoot的大学生体质测试管理系统

基于SpringBoot的大学生体质测试管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 管理员界面 教师界面 学生界面 摘要 大学生体质测试管理系统是一…