智能助手服务

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

当前文档对应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. Python3.7源码编译

    1.下载Python3.7.0源码 git clone https://github.com/python/cpython.gitgit checkout v3.7.0 wget https://ww ...

  2. “easyExcel”导入的代码实现

    使用easyExcel在导入数据事有很好的使用性,方便操作. 添加依赖: <dependency> <groupId>com.alibaba</groupId> & ...

  3. 【小小demo】Springboot + Vue 增删改查

    vue-table-ui 该工程提供的是 一个简单的 Vue + Element-UI 的表格,增删改查操作. 工程代码在最下面. 环境 jdk1.8 idea maven springboot 2. ...

  4. 零基础实现Java直播(一):集成

    从泛娱乐的秀场直播,到直播带货潮流中的电商直播,直播已经成为2020年最热的应用技术之一,越来越多的产品希望加入直播功能.下面我们基于即构Express Video SDK,来分享如何从零实现线上直播 ...

  5. 求任意两个正整数的最大公约数(GCD)。

    问题描述 求任意两个正整数的最大公约数(GCD). 问题分析 如果有一个自然数a能被自然数b整除,则称a为b的倍数,b为a的约数.几个自然数公有的约数,叫做这几个自然数的公约数.公约数中最大的一个公约 ...

  6. 一文详解 Okio 输入输出流

    在 OkHttp 的源码中,我们经常能看到 Okio 的身影,这篇文章,我们把Okio拿出来进行一个详细的介绍学习. 输入输出的概念简述 Okio 简介 工程中引入 Okio API 简介及使用介绍 ...

  7. GoFrame v2.5 版本发布,企业级 Golang 开发框架

    大家好啊,GoFrame 框架今天发布了 v2.5.0 正式版本啦! 本次版本主要是对已有功能组件以及开发工具上的改进工作.其中,开发工具新增了 gf gen ctrl 命令,以规范化定义.开发 AP ...

  8. RDB初步了解

    RDB概念 快照文件是.rdb结尾 redis6.2以前和之后(包括6.2)在什么条件下会保存rdb文件有所不同 以前 15min&&1个key(改变)调用保存 5min&&a ...

  9. Linux内核笔记(三)内核编程语言和环境

    学习概要: Linux内核使用的编程语言.目标文件格式.编译环境.内联汇编.语句表达式.寄存器变量.内联函数 c和汇编函数之间的相互调用机制Makefile文件的使用方法. as86汇编语言语法 汇编 ...

  10. grafana 容器无法启动,打印权限问题

    报错日志 open /var/lib/grafana/alerting/1/notifications: permission denied 问题原因 sudo chown -R docker: /v ...