如何通过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 参数的名字目标页面内容的数据类型(只有在上传文件的时候) 提示信息 一个表单中通常还包含一些说明性的文…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...