本人是一家小公司的技术总监,工作包括写市场分析、工作汇报、产品推广文案及代码开发等。在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. Excel 字符串拆分

    用 Excel 处理数据时,有时需要对字符串进行拆分.对于比较简单的拆分,使用 Excel 函数可以顺利完成,但碰到一些特殊需求,或者拆分的规则比较复杂时,则很难用 Excel 实现了.这里列出一些拆 ...

  2. 重新点亮shell————awk数组[十四]

    前言 简单介绍一下awk的数组. 正文 数组的定义: 数组的遍历: 删除数组: 例子: 例子2: 结 下一节awk函数.

  3. 对于小程序canvas在某些情况下touchmove 不能触发导致的签名不连续替代方案(企微)

    1.问题 微信开放社区链接 尝试过新版canvas,在企业微信中签名依然是依然断触,有问题的手机是iphoe15,系统版本以及企微版本微信版本均与签名正常的手机一致,但是那个手机就是无法正常签字,在微 ...

  4. React纯组件的使用

    1. 有无必要使用纯组件 如果应用不是很大型,页面渲染效率使用纯组件与非纯组件差别不大,尽量使用组件 应用一定注意,setState时子组件依赖渲染的属性一定要传递给子组件,不然父组件setState ...

  5. P10160 [DTCPC 2024] Ultra 题解

    [题目描述] 给你一个 \(01\) 序列,你可以进行如下操作若干次(或零次): 将序列中形如 \(101\cdots01\) 的一个子串(即 \(1(01)^k\),\(k\ge 1\))替换成等长 ...

  6. 日志服务Dashboard加速

    简介: 阿里云日志服务致力于为用户提供统一的可观测性平台,同时支持日志.时序以及Trace数据的查询存储.用户可以基于收集到的各类数据构建统一的监控以及业务大盘,从而及时发现系统异常,感知业务趋势.但 ...

  7. 基于英特尔® 优化分析包(OAP)的 Spark 性能优化方案

    ​简介: Spark SQL 作为 Spark 用来处理结构化数据的一个基本模块,已经成为多数企业构建大数据应用的重要选择.但是,在大规模连接(Join).聚合(Aggregate)等工作负载下,Sp ...

  8. goland配置在远程linux里运行代码开发,并debug调适

    环境: windows 10 phpstudy8.1.1.3 Vmware安装centos7.6 场景 window10里goland开发,在远程linux里运行,并debug断点调适 步骤: win ...

  9. 手机自适应的单位rem,与自适应网页的区别

    一个网站有的会分为pc站和移动站,有的网站只有pc站,而现在更多的是自适应的站点.现在针对自适应的网页设计有很多模板,如bootstrap,它会让你轻松定制一个只适应网站,而现在大多数的网站并不是靠程 ...

  10. python教程8-页面爬虫

    python爬虫常用requests和beautifulSoup这2个第三方模块.需要先进行手动安装. requests负责下载页面数据,beautifulSoup负责解析页面标签. 关于beauti ...