智能助手服务

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

当前文档对应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

如何通过SK集成chatGPT实现DotNet项目工程化?的更多相关文章

  1. eclipse集成jetty开发web项目(不采用maven方式)

    以前开发过程部署项目都是采用tomcat,偶然发现jetty,所以试了下,挺方便的,直切主题. 1.下载jetty,楼主使用的jetty8,地址http://download.eclipse.org/ ...

  2. SNF快速开发平台MVC-富文本控件集成了百度开源项目editor

    一.效果如下: 二.在框架当中调用代码如下: 1.在js里配置如下: <script type="text/javascript"> var viewModel =fu ...

  3. 将React Native 集成进现有OC项目中(过程记录) 、jsCodeLocation 生成方式总结

    将RN集成到现有OC项目应该是最常见的,特别是已经有OC项目的,不太可能会去专门搞个纯RN的项目.又因为RN不同版本,引用的依赖可能不尽相同,所以特别说明下,本文参考的文档是React Native ...

  4. 使用Jenkins与Docker持续集成与发布NetCore项目(实操篇)

    使用Jenkins与Docker持续集成与发布NetCore项目(教程一) 原文地址:https://www.cnblogs.com/Jackyye/p/12588182.html 基本环境 该教程的 ...

  5. dotnet 项目生成自签名证书

    解决dotnet 项目浏览器不安全提示 dotnet dev-certs - 生成自签名证书,以便在开发中使用 HTTPS. dotnet dev-certs https --clean dotnet ...

  6. 基于开源的 ChatGPT Web UI 项目,快速构建属于自己的 ChatGPT 站点

    作为一个技术博主,了不起比较喜欢各种折腾,之前给大家介绍过 ChatGPT 接入微信,钉钉和知识星球(如果没看过的可以翻翻前面的文章),最近再看开源项目的时候,发现了一个 ChatGPT Web UI ...

  7. 【共享单车】—— React后台管理系统开发手记:项目工程化开发

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...

  8. Visual Studio容器项目工程化心得

    引言 关注博主的网友会看到我使用ASP.NET Core 容器化部署企业级项目的过程, 回想到开发过程中,鄙人有一些工程化心得, 分享给同学们. 项目工程化 因为本项目涉及单元测试Project.容器 ...

  9. Django集成celery实战小项目

    上一篇已经介绍了celery的基本知识,本篇以一个小项目为例,详细说明django框架如何集成celery进行开发. 本系列文章的开发环境: window 7 + python2.7 + pychar ...

  10. React-Native集成到已有项目中的总结

    安装Python 从官网下载并安装python 2.7.x(3.x版本不行) 安装node.js 从官网下载node.js的官方V6.X.X版本或更高版本.安装完成后检测是否安装成功:node -v ...

随机推荐

  1. Python运维开发之路《python基础介绍》

    一. python介绍相关 1. Python简介 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. - Python 的设计具有很强的可读性,相比其他语言经常使用英文关 ...

  2. 彻底解决各种浏览器访问不了GitHub问题(注意代理)

    如果有穿墙插件如Google助手  VPN  SS 之类别 有可能被全局代理 首先关闭这些软件 或者浏览器插件 假设,您的本地代理端口为:1080 ,打开git base窗口进行按下列的方式设置.(在 ...

  3. 即构微信小程序直播组件是什么?有哪些功能?哪些小程序类目可以使用?

    即构直播助手是微信官方认证的微信小程序插件,为开发者提供便捷.强大的微信小程序音视频直播服务. 即构直播助手除了包含微信小程序下的音视频推拉流能力,还支持iOS.Android.Windows.Web ...

  4. AIGC:新AI时代,推动数字人进化的引擎

    摘要:CV.NLP.大模型...AI技术的加持下,让数字人内外在更加生动真实.在未来的发展中,数字人的应用场景越来越广泛,并将发挥出重要的作用,让美好照进生活. 本文分享自华为云社区<AIGC: ...

  5. 2023CCPC大学生程序设计竞赛-nhr

    新生菜菜第一次参加这种大型比赛,还是有点紧张的,CCPC我们队就A了三题,铜牌.第一道,以为是签到,然后就交给clk了,我和crf看下一道过的题比较多的,然后感觉是一个滑动窗口,另一道题是纯数学公式. ...

  6. Java中的线程池使用及原理

    开篇-为什么要使用线程池? ​ Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来 3 个好处. ​ 第一:降低 ...

  7. AVR汇编(四):数据传送指令

    AVR汇编(四):数据传送指令 AVR指令主要分为五类:算术和逻辑指令.分支指令.位操作指令.数据传送指令.MCU控制指令,今天我们先来认识其中最常用的数据传送指令. 汇编程序的编写.编译和调试 学习 ...

  8. Pixi.js的使用整理

    最近在做的一个前端项目中,有一些图像的处理操作(3D图,2D图都有),其中3D图也是通过获取后端服务的图像2D数据进行绘制展示,通过鼠标各种操作调用后端服务来进行重新获取新图数据.这里前端设计到图像的 ...

  9. WPF --- 非Button自定义控件实现点击功能

    引言 今天在做一个设置文件夹路径的功能,就是一个文本框,加个按钮,点击按钮,弹出 FolderBrowserDialog 再选择文件夹路径,简单做法,可以直接 StackPanel 横向放置一个 Te ...

  10. MindSponge分子动力学模拟——软件架构(2023.08)

    技术背景 在前面一篇文章中,我们介绍了MindSponge的两种不同的安装与使用方法,让大家能够上手使用.这篇文章主要讲解MindSponge的软件架构,并且协同mindscience仓库讲解一下二者 ...