如何通过SK集成chatGPT实现DotNet项目工程化?
智能助手服务
以下案例将讲解如何实现天气插件
当前文档对应src/assistant/Chat.SemanticServer项目
首先我们介绍一下Chat.SemanticServer的技术架构
SemanticKernel 是什么?
Semantic Kernel是一个SDK,它将OpenAI、Azure OpenAI和Hugging Face等大型语言模型(LLMs)与传统的编程语言如C#、Python和Java集成在一起。Semantic Kernel通过允许您定义可以在几行代码中链接在一起的插件来实现这一目标。
如何集成使用SemanticKernel
以下是添加IKernel,OpenAIOptions.Model和OpenAIOptions.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");
使用插件,首先我们创建了一个ContextVariables,input则是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}}意图:
意图识别完成以后,当执行完成GetIntent,intent相应会根据options中提供的参数返回与之匹配的参数,
然后下面的代码将根据返回的意图进行实际上的操作,或加载相应的插件,比如当intent返回Weather,则首先从chatPlugin中使用Weather插件,并且传递当前用户输入内容,在这里将提取用户需要获取天气的城市。
完成返回以后将在使用MathFunction = _kernel.Skills.GetFunction("WeatherPlugin", "GetWeather")的方式获取WeatherPlugin插件的GetWeather方法,并且将得到的参数传递到_kernel.RunAsync执行的时候则会掉用GetWeather方法,这个时候会由插件返回的json在组合成定义的模板消息进行返回,就完成了调用。
ISKFunction MathFunction = null;SKContext? result = null;//获取意图后动态调用Funif (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
相关文章:
如何通过SK集成chatGPT实现DotNet项目工程化?
智能助手服务 以下案例将讲解如何实现天气插件 当前文档对应src/assistant/Chat.SemanticServer项目 首先我们介绍一下Chat.SemanticServer的技术架构 SemanticKernel 是什么? Semantic Kernel是一个SDK,它将OpenAI、Azure OpenAI和Hugging Face等大…...
DRM中render-node编号的分配
DRM系统 DRM是direct rendering manager的简称。DRM是linux kernel中与负责video cards功能的GPU打交道的子系统。DRM给出了一组API,可以供用户程序来发送命令和数据给GPU设备从而来控制比如display、render等功能。 render-node由来 在以前,DRM子系统…...
将输入对象转换为数组数组的维度大于等于1numpy.atleast_1d()
【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将输入对象转换为数组 数组的维度大于等于1 numpy.atleast_1d() 选择题 使用numpy.atleast_1d()函数,下列正确的是? import numpy as np a1 1 a2 ((1,2,3),(4,5,6)) print("…...
js 删除树状图无用数据,如果子级没有数据则删除
有一个需求,当你从后端拿到一个树状图的时候,有些子级没数据,这时就需要我们处理一下数据,当然了,如果第一层底下的第二层没数据,第二层底下的所有都没数据,那这一层都不需要。 我的写法&#x…...
Docker 容器化(初学者的分享)
目录 一、什么是docker 二、docker的缺陷 三、简单的操作 一、首先配置一台虚拟机 二、安装Docker-CE 一、安装utils 二、 将 Docker 的软件源添加到 CentOS 的 yum 仓库中。这样可以通过 yum 命令来安装、更新和管理 Docker 相关的软件包。 三、将 download.docker.co…...
LCS 01.下载插件
题目来源: leetcode题目,网址:写文章-CSDN创作中心 解题思路: 假设需要 n 分钟下载插件,前 n-1 分钟将带宽加倍,最后一分钟下载时总时间最少。 解题代码: class Solution { public:int l…...
架构-设计原则
1、面向对象的SOLID 1.1 概述 SOLID是5个设计原则开头字母的缩写,其本身就有“稳定的”的意思,寓意是“遵从SOLID原则可以建立稳定、灵活、健壮的系统”。5个原则分别如下: Single Responsibility Principle(SRP)&am…...
在 Python 3 中释放 LightGBM 的力量:您的机器学习大师之路
机器学习是 Python 占据主导地位的领域,它一直在给全球各行各业带来革命性的变化。要在这个不断变化的环境中脱颖而出,掌握正确的工具是关键。LightGBM 就是这样一个工具,它是一个强大且快速的梯度提升框架。在这份综合指南中,我们将通过实际示例和示例数据集从基础知识到高…...
Spring学习笔记(2)
Spring学习笔记(2) 一、Spring配置非定义Bean1.1 DruidDataSource1.2、Connection1.3、Date1.4、SqlSessionFactory 二、Bean实例化的基本流程2.1 BeanDefinition2.2 单例池和流程总结 三、Spring的bean工厂后处理器3.1 bean工厂后处理器入门3.2、注册Be…...
cmd使用ssh连接Linux脚本
前言 在开发过程中,由于MobaXterm,我不知道怎么分页(不是屏内分页),用crtlTab,用起来不习惯,主要是MobaXterm连接了多个服务器,切换起来很麻烦。我是比较习惯使用altTab,…...
Python万圣节蝙蝠
目录 系列文章 前言 蝙蝠 程序设计 程序分析 运行结果 尾声 系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want5…...
TCP流套接字编程
文章目录 前言TCP 和 UDP 的特点对比TcpEchoServer 服务端实现1. 创建 ServerSocket 类实现通信双方建立连接2. 取出建立的连接实现双方通信3. 服务端业务逻辑实现关闭资源服务端整体代码 TcpEchoClient 客户端实现1. 创建出 Socket 对象来与服务端实现通信2. 实现客户端的主要…...
Python迭代器创建与使用:从入门到精通
一、可迭代对象 1、 什么是可迭代对象? 表示可以逐一迭代或者遍历的对象,序列:列表、元组、集合、字符串。非序列:字典、文件。自定义对象:实现了__iter__()方法的对象;实现了使用整数索引的 getitem()方…...
mac虚拟机安装homebrew时的问题
安装了mac虚拟机,结果在需要通过“brew install svn”安装svn时,才注意到没有下载安装homebrew。 于是便想着先安装homebrew,网上查的教程大多是通过类似以下命令 “ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)” 但是都会出现…...
学信息系统项目管理师第4版系列32_信息技术发展
1. 大型信息系统 1.1. 大型信息系统是指以信息技术和通信技术为支撑,规模庞大,分布广阔,采用多级 网络结构,跨越多个安全域;处理海量的,复杂且形式多样的数据,提供多种类型应用 的大系统 1.1.…...
Vue3 + Nodejs 实战 ,文件上传项目--大文件分片上传+断点续传
目录 1.大文件上传的场景 2.前端实现 2.1 对文件进行分片 2.2 生成hash值(唯一标识) 2.3 发送上传文件请求 3.后端实现 3.1 接收分片数据临时存储 3.2 合并分片 4.完成段点续传 4.1修改后端 4.2 修改前端 5.测试 博客主页:専心_前端…...
宏(预编译)详解
目录 一、程序的编译环境 二、运行环境 三、预编译详解 3.1预定义符号 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 3.2.3#define替换规则 3.2.4 #和## 2)##的作用: 3.2.5宏和函数的对比 3.2.6宏的命名约定和#undef指令 一、命名约定: …...
hue实现对hiveserver2 的负载均衡
如果你使用的是CDH集群那就很是方便的 在Cloudera Manager中,进入HDFS Service 进入Instances标签页面,点击Add Role Instances按钮,如下图所示 点击Continue按钮,如下图所示 返回Instances页面,选择HttpFS角色…...
SkyWalking 告警规则配置说明
Skywalking告警功能是在6.x版本新增的,其核心由一组规则驱动,这些规则定义在config/alarm-settings.yml 文件中。告警规则定义分为两部分: 1、告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件 2、webhook(网络钩子):定义当告警触发时,哪些服务终端需要被…...
HTML 表单笔记/练习
表单 概述 表单用于收集用户信息,用户填写表单提交到服务器 一般传参方式: GETPOSTCookie 传参要素 传参方式 GETPOST 参数的名字目标页面内容的数据类型(只有在上传文件的时候) 提示信息 一个表单中通常还包含一些说明性的文…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
