智能助手服务

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

当前文档对应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. 【从0开始编写webserver·基础篇#03】TinyWeb源码阅读,还是得看看靠谱的项目

    [前言] 之前通过看书.看视频和博客拼凑了一个webserver,然后有一段时间没有继续整这个项目 现在在去看之前的代码,真的是相当之简陋,而且代码设计得很混乱,我认为没有必要继续在屎堆上修改了,于是 ...

  2. Blazor前后端框架Known-V1.2.3

    V1.2.3 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Gith ...

  3. 【Java 新的选择】,Solon v2.3.8 发布

    Solon 是什么开源项目? 一个,Java 新的生态型应用开发框架.它从零开始构建,有自己的标准规范与开放生态(历时五年,已有全球第二级别的生态规模).与其他框架相比,它解决了两个重要的痛点:启动慢 ...

  4. 基于SM4和LSB算法实现图片数字水印加密软件(密码赛)

    一.前言 密码赛和星火杯时做的小项目,密码赛的时候是个半成品,没有过初赛,星火杯之前完善了 设计思路 最开始是想做一个图片水印用作对图片来源的不可否认性做保护,又考虑保护数据完整性,因此选中了易损水印 ...

  5. 华为P9黑屏的解决方案-更换屏幕

    解决办法(系统软件) 1.回退系统版本,b198或者b139固件. 2.升级版本,到最新版本.新版本使用时并没有发现这个问题. 解决方法(系统设置) 点开设置-电池-选择进入超级省电模式,然后退出超级 ...

  6. Redis的设计与实现(3)-字典

    Redis 的数据库使用字典实现, 对数据库的增, 删, 查, 改也是构建在对字典的操作之上的. 字典是哈希键的底层实现之一: 当一个哈希键包含的键值对比较多, 又或者键值对中的元素都是比较长的字符串 ...

  7. Unity的IFilterBuildAssemblies:深入解析与实用案例

    Unity IFilterBuildAssemblies Unity IFilterBuildAssemblies是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目时自定义哪些程序集需要 ...

  8. 简述分布式链路追踪工具——Jaeger

    1.简介 1.1 Jaeger是什么 Jaeger  是受到 ​ ​Dapper​​​ 和 ​ ​OpenZipkin​​​ 启发的由 ​ ​Uber Technologies​​ 作为开源发布的分布 ...

  9. Maven资源导出问题所需配置

    <!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> < ...

  10. DirtyCow 脏牛提权漏洞(CVE-2016-5195)

    描述: 该漏洞是 Linux 内核经典漏洞,内核内存子系统在处理写时拷贝(Copy-on-Write)时存在条件竞争漏洞, 导致可以破坏私有只读内存映射.黑客可以在获取低权限的的本地用户后,利用此漏洞 ...