本人是一家小公司的技术总监,工作包括写市场分析、工作汇报、产品推广文案及代码开发等。在ChatGPT推出之后本人一直在工作中使用,在头脑风暴、大纲生成、语句优化、代码生成方面很有效果。但ChatGPT在一些常识性生成方面并不理想,比如某个省有哪些旅游景点、三角数学公式推算等,大家使用中一定要注意并仔细甄别。ChatGPT的一些使用场景可以参考用 ChatGPT 来了解 ChatGPT

由于我之前一直是在电脑浏览器中使用,且经常碰见超时需要重新进入的情况,遂产生想法把ChatGPT和DALL-E(OpenAI开发的图片生成模型)集成到本人的公众号上,使用更加方便和稳定。二话不说,先上效果图和代码。

代码

开发中主要使用了两个库:

OpenAI-DotNet:一个简单的C# .NET客户端库,用于通过RESTful API使用chat-gpt、GPT-4、GPT-3.5-Turbo和Dall-E。这是一个独立开发的库,不是官方库,需要OpenAI API帐户。

Senparc.Weixin —— 微信 .NET SDK:使用 Senparc.Weixin,您可以方便快速地开发微信全平台的应用(包括微信公众号、小程序、小游戏、企业号、开放平台、微信支付、JS-SDK、微信硬件/蓝牙,等等。

  1. 新建一个空的.net core WebAPI项目并引入Nuget包如下:
Install-Package OpenAI-DotNet

Install-Package Senparc.Weixin.AspNet
Install-Package Senparc.Weixin.MP.Middleware
  1. 在Program.cs中引入微信配置代码
public static void Main(string[] args)
{
... builder.Services.AddMemoryCache();//使用本地缓存必须添加 //Senparc.Weixin 微信注册
builder.Services.AddSenparcWeixinServices(builder.Configuration); ... //启用微信配置
var registerService = app.UseSenparcWeixin(app.Environment, null, null,
register => { /* CO2NET 全局配置 */ },
(register, weixinSetting) =>
{
register.RegisterMpAccount(weixinSetting);
}); //启用微信消息处理中间件
app.UseMessageHandlerForMp("/WeixinAsync", CustomMessageHandler.GenerateMessageHandler, options =>
{
options.AccountSettingFunc = context => Senparc.Weixin.Config.SenparcWeixinSetting;
}); //Nginx部署使用
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
}); ... }
  1. 新建一个类CustomMessageHandler用于处理微信消息
/// <summary>
/// 自定义消息处理器
/// </summary>
public class CustomMessageHandler : MessageHandler<DefaultMpMessageContext>
{
private readonly ILogger _logger;
private readonly string _openAPI_Key;
private string appId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId; /// <summary>
/// 为中间件提供生成当前类的委托
/// </summary>
public static Func<Stream, PostModel, int, IServiceProvider, CustomMessageHandler> GenerateMessageHandler = (stream, postModel, maxRecordCount, serviceProvider)
=> new CustomMessageHandler(stream, postModel, maxRecordCount, serviceProvider: serviceProvider); public CustomMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount, IServiceProvider serviceProvider)
: base(inputStream, postModel, maxRecordCount, false, null, serviceProvider)
{
GlobalMessageContext.ExpireMinutes = 3;
GlobalMessageContext.MaxRecordCount = 3;
OmitRepeatedMessage = true; //启用消息去重功能
_logger = serviceProvider.GetService<ILogger<CustomMessageHandler>>();
_openAPI_Key = serviceProvider.GetService<IConfiguration>()["OpenAIKey"];
} /// <summary>
/// 回复以文字形式发送的信息
/// </summary>
public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
{
#region 由于微信回复时长5秒限制,这里采用异步推送客服信息,后面直接返回空消息。 _ = Task.Factory.StartNew(async () =>
{
OpenAIClient api = new OpenAIClient(_openAPI_Key); if (requestMessage.Content.Contains("图片", StringComparison.OrdinalIgnoreCase)
|| requestMessage.Content.Contains("图像", StringComparison.OrdinalIgnoreCase)
|| requestMessage.Content.Contains("照片", StringComparison.OrdinalIgnoreCase)
|| requestMessage.Content.Contains("Image", StringComparison.OrdinalIgnoreCase)
|| requestMessage.Content.Contains("Photo", StringComparison.OrdinalIgnoreCase)) //DALL-E 模型
{
var imageRequest = requestMessage.Content.Replace("图片", "").Replace("图像", "").Replace("照片", "").Replace("Image", "").Replace("Photo", "").Trim();
var results = await api.ImagesEndPoint.GenerateImageAsync(imageRequest, 1, ImageSize.Small); //调用DALL-E模型接口生成图片
if (results.Count > 0)
{
var imageRemoteUrl = results[0]; #region 临时保存图片到本地
var imageLocalUrl = ServerUtility.ContentRootMapPath($"~/App_Data/TempImages/{Guid.NewGuid()}.png");
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(30);
byte[] bytes = await client.GetByteArrayAsync(imageRemoteUrl);
if (bytes.Length>0)
{
FileStream fs = new FileStream(imageLocalUrl, FileMode.Create);
BinaryWriter w = new BinaryWriter(fs); try
{
w.Write(bytes);
}
finally
{
fs.Close();
w.Close();
}
}
#endregion var uploadResult = MediaApi.UploadTemporaryMedia(appId, UploadMediaFileType.image, imageLocalUrl);
await CustomApi.SendImageAsync(appId, OpenId, uploadResult.media_id);
}
}
else //GPT3_5_Turbo 模型
{
var currentMessageContext = await base.GetCurrentMessageContext(); //使用StorageData保存对话上下文
if (currentMessageContext.StorageData == null || !(currentMessageContext.StorageData is List<ChatPrompt>))
{
currentMessageContext.StorageData = new List<ChatPrompt>();
}
var chatPrompts = (List<ChatPrompt>)currentMessageContext.StorageData; chatPrompts.Add(new ChatPrompt("user", requestMessage.Content)); var chatRequest = new ChatRequest(chatPrompts, model: Model.GPT3_5_Turbo);
var result = await api.ChatEndpoint.GetCompletionAsync(chatRequest); //调用GPT3_5_Turbo模型接口生成对话
var firstChoice = result.FirstChoice.ToString().Trim();
await CustomApi.SendTextAsync(appId, OpenId, firstChoice); chatPrompts.Add(new ChatPrompt("assistant", firstChoice));
currentMessageContext.StorageData = chatPrompts;
GlobalMessageContext.UpdateMessageContext(currentMessageContext);//储存到缓存
} }).ContinueWith(async task =>
{
if (task.Exception != null)
{
await CustomApi.SendTextAsync(appId, OpenId, $"生成失败,请稍后再试。");
_logger.LogError($"生成失败,错误信息:{task.Exception.InnerException}。");
}
}); return new SuccessResponseMessage(); #endregion
} /// <summary>
/// 默认消息
/// </summary>
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = $"欢迎来到车骑数说,该公众号已集成【ChatGPT 对话生成】和【DALL-E 图片生成】功能:\r\n1、如若使用ChatGPT 对话生成功能请直接输入想问的问题。\r\n1、如若使用DALL-E 图片生成请在描述文字后以“图片”结尾,比如“一匹马在月球上图片”。";
return responseMessage;
}
}
  1. 在配置文件appsettings.json里面添加对应配置

  2. 运行代码,在url后加上/WeixinAsync后缀,如果出现以下页面即代表开发成功。

OpenAI接口账号

注册完OpenAI账号后(具体注册过程请自行百度),打开https://platform.openai.com/,在用户管理里面的View API Keys里面创建一个New Secret Key,并保存下来用于API调用。每个用户有18美元的免费额度用于调用。具体使用额度可以在Usage页面查看。

后端部署

由于OpenAI及接口不允许中国地区访问,所以在部署代码的时候有两种方案:

  • 国内服务器+安装国外代理
  • 国外服务器

同时对接微信公众号一定是需要域名的,目前域名和国外服务器绑定不需要备案,而和国内服务器绑定需要备案,如果考量备案时间关系建议使用国外服务器,而且部分国外服务器并不贵,这里推荐RackNerd,RackNerd是一家美国VPS服务商,其公司于2015年注册,可选机房有圣何塞、芝加哥、达拉斯、亚特兰大、纽约、阿什本等10个机房。一直以价格低廉、守候到位、服务器稳定而火爆市场。付款方式支持支付宝。

促销款便宜套餐入口:套餐1 套餐2 套餐3 2023年最新优惠码:15OFFDEDI 官方网站:racknerd

本人购买的是套餐2,一年大概170元。

我是部署在Ubuntu 20.4的操作系统上,主要有三步:

  1. 安装运行时 aspnetcore-runtime

添加 Microsoft 包存储库

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

安装运行时

sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-6.0
  1. Nginx 安装配置

由于请求通过反向代理转发,因此使用 Microsoft.AspNetCore.HttpOverrides 包中的转接头中间件。 此中间件使用 X-Forwarded-Proto 标头来更新

Request.Scheme,使重定向 URI 和其他安全策略能够正常工作。

using Microsoft.AspNetCore.HttpOverrides;

...

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
}); app.UseAuthentication();

安装 Nginx

sudo apt-get install nginx

首次安装 Nginx,通过运行以下命令显式启动后确认浏览器显示 Nginx 的默认登陆页。

sudo service nginx start

配置 Nginx

修改 /etc/nginx/sites-available/default。 在文本编辑器中打开它,并将内容替换为以下代码片段,Nginx 将匹配的请求转发到 http://127.0.0.1:5000 中的 Kestrel上运行的 ASP.NET Core 应用。

server {
listen 80;
#server_name example.com *.example.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

完成配置 Nginx 后,运行 sudo nginx -t 来验证配置文件的语法。 如果配置文件测试成功,可以通过运行 sudo nginx -s reload 强制 Nginx 选取更改。

  1. 设置服务自启动

systemd 可用于创建服务文件以启动和监视基础 Web 应用。 systemd 是一个初始系统,可以提供启动、停止和管理进程的许多强大的功能。

创建服务定义文件:

sudo nano /etc/systemd/system/wx-ai.service

并填充以下内容

[Unit]
Description=wx-ai service [Service]
WorkingDirectory=/var/www/wx-ai
ExecStart=/usr/bin/dotnet /var/www/wx-ai/OpenAI.Examples.WX.dll
Restart=always
# Restart service after 90 seconds if the dotnet service crashes:
RestartSec=90
KillSignal=SIGINT
SyslogIdentifier=wx-ai
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install]
WantedBy=multi-user.target

systemd常用命令如下:

sudo systemctl enable wx-ai.service  #开机启动
sudo systemctl start wx-ai.service #启动服务
sudo systemctl stop wx-ai.service #停止服务
sudo systemctl status wx-ai.service #查看服务状态
sudo systemctl restart wx-ai.service #重新启动服务

查看日志命令如下:

sudo journalctl -fu wx-ai.service --since "2016-10-18" --until "2016-10-18 04:00"

至此部署完成,最后需要申请域名绑定服务器IP。

微信集成

这里以微信测试公众号为例子,打开https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index,找到appID和appsecret放在ASP.NET Core 应用的配置文件里,把ASP.NET Core应用部署的地址+/WeixinAsync填入接口配置信息,并填写自定义Token,保证Token在微信配置和ASP.NET Core 应用的配置文件里一直。接下来就可以正式测试了。



正式公众号配置类似,但这里有个比较尴尬的地方,ChatGPT和DALL-E和接口数据返回时间一般比较长,大于微信公众号普通回复接口5秒的时长限制,所以最好使用客服接口进行结果回复。而目前客服接口只适用于通过微信认知的企业公众号而非个人公众号。所以本人的个人公众号【车骑数说】无法接入,目前自己也只能在个人测试号上使用。不过我也正在开发小程序版本,后续开发完成后会及时更新。

使用C#开发微信公众号对接ChatGPT和DALL-E的更多相关文章

  1. Java开发微信公众号(四)---微信服务器post消息体的接收及消息的处理

    在前几节文章中我们讲述了微信公众号环境的搭建.如何接入微信公众平台.以及微信服务器请求消息,响应消息,事件消息以及工具处理类的封装:接下来我们重点说一下-微信服务器post消息体的接收及消息的处理,这 ...

  2. 小机器人自动回复(python,可扩展开发微信公众号的小机器人)

    api来之图灵机器人.我们都知道微信公众号可以有自动回复,我们先用python脚本编写一个简单的自动回复的脚本,利用图灵机器人的api. http://www.tuling123.com/help/h ...

  3. vue+node.js+webpack开发微信公众号功能填坑——v -for循环

    页面整体框架实现,实现小功能,循环出数据,整体代码是上一篇 vue+node.js+webpack开发微信公众号功能填坑--组件按需引入 修改部门代码 app.vue <yd-flexbox&g ...

  4. vue+node.js+webpack开发微信公众号功能填坑——组件按需引入

    初次开发微信公众号,整体框架是经理搭建,小喽喽只是实现部分功能,整体页面效果 整个页面使用两个组件:布局 FlexBox,搜索框 Search,demo文档 http://vue.ydui.org/d ...

  5. PHP开发微信公众号

    PHP开发微信公众号:配置和部署服务器及Token认证 https://zhuanlan.zhihu.com/p/28259840

  6. 使用vue开发微信公众号下SPA站点的填坑之旅

    原文发表于本人博客,点击进入使用vue开发微信公众号下SPA站点的填坑之旅 本文为我创业过程中,开发项目的填坑之旅.作为一个技术宅男,我的项目是做一个微信公众号,前后端全部自己搞定,不浪费国家一分钱^ ...

  7. PHP开发微信公众号(一)二维码的获取

    要开发微信公众号,首先进行需要注册一个,然后认证.这就不用多说了. 当然如果没有,也可以去申请一个测试号来使用,地址:https://mp.weixin.qq.com/debug/cgi-bin/sa ...

  8. Java开发微信公众号(五)---微信开发中如何获取access_token以及缓存access_token

    获取access_token是微信api最重要的一个部分,因为调用其他api很多都需要用到access_token.比如自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等在请求的时候 ...

  9. Java开发微信公众号(三)---微信服务器请求消息,响应消息,事件消息以及工具处理类的封装

    在前面几篇文章我们讲了微信公众号环境的配置 和微信公众号服务的接入,接下来我们来说一下微信服务器请求消息,响应消息以及事件消息的相关内容,首先我们来分析一下消息类型和返回xml格式及实体类的封装. ( ...

  10. Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发

    接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...

随机推荐

  1. Linux系统中查找文件的方法

    -name 必须用到的选项.表明要求系统按照文件名查找. 一般格式:find /(dirname) -name filename 具体文件名查找法: 如果知道了某个文件的文件名,而不知道这个文件放到哪 ...

  2. 老夫当年手写的js动画库

    前言 当年我学习js的时候,那时候学生时代不知道有jquery,所以手写了一些东西,留下的不多作为回忆. 正文 ``` javascript window.onload = function () { ...

  3. 基于locust全链路压测系统

    2021年中旬就计划着搭建一套压测系统,大约9月份已经搭建完成,使用至今还是比较稳定了,分享一下搭建思路及过程: 为什么选择Locust呢,因为Locust可以仅需要执行命令就可以完成压测任务,并且集 ...

  4. 把 Maven 提交到项目?Maven Wrapper的使用与好处

    本文收录于 Github.com/niumoo/JavaNotes,Java 系列文档,数据结构与算法! 本文收录于网站:https://www.wdbyte.com/,我的公众号:程序猿阿朗 前言 ...

  5. ORA-02303: cannot drop or replace a type with type or table dependents,即无法使用类型或表的相关性来删除或取代一个类型

    ORA-02303: cannot drop or replace a type with type or table dependents,即无法使用类型或表的相关性来删除或取代一个类型 在修改一个 ...

  6. 一文详解 Nacos 高可用特性

    简介: 我今天介绍的 Nacos 高可用,是 Nacos 为了提升系统稳定性而采取的一系列手段.Nacos 的高可用不仅仅存在于服务端,同时也存在于客户端,以及一些与可用性相关的功能特性中,这些点组装 ...

  7. 基于DataWorks搭建新零售数据中台

    文章作者:许日(欢伯),在2016年盒马早期的时候,转到盒马事业部作为在线数据平台的研发负责人,现任阿里云计算平台DataWorks建模引擎团队负责人. 文章简介:本篇文章向大家分享新零售企业如何基于 ...

  8. Spring官方RSocket Broker 0.3.0发布: 快速构建你的RSocket架构

    ​简介:Spring官方的RSocket Broker其实开发已经非常久了,我以为会伴随着Spring Cloud 2021.0发布的,但是没有发生.不过Spring RSocket Broker还是 ...

  9. 如何专业化监控一个Kubernetes集群?

    简介: 本文会介绍 Kubernetes 可观测性系统的构建,以及基于阿里云云产品实现 Kubernetes 可观测系统构建的最佳实践. 作者:佳旭 阿里云容器服务技术专家 引言 Kubernetes ...

  10. 龙蜥社区成立系统运维SIG,重磅开源sysAK系统运维工具集

    简介:系统运维SIG致力于打造一个集主机管理.配置部署.监控报警.异常诊断.安全审计等一系列功能的自动化运维平台. ​ OpenAnolis 龙蜥社区(以下简称"龙蜥社区")正式成 ...