Skip to content

Commit 85d1224

Browse files
committed
提供简化版的案例,编写 Readme 教程
1 parent 6590930 commit 85d1224

File tree

5 files changed

+175
-8
lines changed

5 files changed

+175
-8
lines changed

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,84 @@ Senparc.Weixin SDK 的 AI 扩展,赋能微信应用基于 OpenAI API、ChatGPT
7878
> 您可以在已经创建好的 MessageHandler 中继续集成 Senparc.AI(以及 Senparc.Weixin.AI),机器人默认使用文字方式接收和应答,但当内容被判断为需要输出图片时,使用 OpenAI 的 DallE 接口生成图片(注意:如果 `appsettings.json`文件中未配置 OpenAIKeys 信息,将始终使用文字方式返回)。<br>
7979
> 下面以一个全新的文件为例,创建一个对话聊天机器人。
8080
81+
新创建的 CustomFullTimeMessageHandler.cs:
82+
```
83+
public class CustomFullTimeMessageHandler : MessageHandler<DefaultMpMessageContext>
84+
{
85+
const string DEFAULT_MESSAGE = @"欢迎使用 Senparc.Weixin SDK!
86+
87+
===开源地址===
88+
Senparc.AI模块:https://github.com/Senparc/Senparc.AI
89+
微信SDK:https://github.com/JeffreySu/WeiXinMPSDK";
90+
91+
92+
public CustomFullTimeMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false, DeveloperInfo developerInfo = null, IServiceProvider serviceProvider = null) : base(inputStream, postModel, maxRecordCount, onlyAllowEncryptMessage, developerInfo, serviceProvider)
93+
{
94+
}
95+
96+
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
97+
{
98+
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
99+
responseMessage.Content = DEFAULT_MESSAGE;
100+
return responseMessage;
101+
}
102+
}
103+
```
104+
105+
从上述的默认消息 `DEFAULT_MESSAGE` 说明文字中,可以看到当前我们希望创建的机器人的对话方式:
106+
107+
由于我们需要在公众号上接收用户发来的文字信息,因此配置接收方法:
108+
```
109+
public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
110+
{
111+
//TODO..
112+
113+
return base.CreateResponseMessage<ResponseMessageNoResponse>();
114+
}
115+
```
116+
117+
在上述 `//TODO...` 位置加入一个队列对象,这么做的好处是可以拜托微信公众号必须在规定时间内回复消息的限制,独立的线程完成工作后,将使用客服消息进行回复(注意:上述返回类型 `ResponseMessageNoResponse` 可以避免出现不返回任何消息时,出现”公众号服务发生故障“的错误信息),代码如下:
118+
119+
```
120+
//使用消息队列处理
121+
var smq = new SenparcMessageQueue();
122+
var smqKey = $"Chat-{OpenId}-{SystemTime.NowTicks}";
123+
smq.Add(smqKey, async () =>
124+
{
125+
try
126+
{
127+
var handler = new SemanticAiHandler();
128+
var factory = new ReponseMessageFactory(ServiceProvider);
129+
var responseMessage = await factory.GetResponseMessageAsync(_appId, this, requestMessage, handler);
130+
await factory.SendCustomMessageAsync(responseMessage, ApiEnlightener, _appId, OpenId);
131+
}
132+
catch (Exception ex)
133+
{
134+
SenparcTrace.SendCustomLog("Chatting Exception", $"ex:{ex.Message},stack:{ex.StackTrace}");
135+
}
136+
137+
});
138+
```
139+
140+
只需运行上述代码,即可实现和 ChatGPT 一致的的对话功能。
141+
142+
上述代码解析如下:
143+
144+
`var smq = new SenparcMessageQueue();` 用于定义一个 `SenparcMessageQueue` 队列操作对象。
145+
146+
`var smqKey = $"Chat-{OpenId}-{SystemTime.NowTicks}";` 定义类了这个队列的唯一编号(全局不能重复,否则会被覆盖)。
147+
148+
`smq.Add(smqKey, async () =>{...});` 用于创建一个队列请求。
149+
150+
`var handler = new SemanticAiHandler();` 用于创建一个 `SemanticAiHandler` 对象,负责处理 AI 业务。
151+
152+
`var factory = new ReponseMessageFactory(ServiceProvider);` 用于创建一个微信响应消息工厂。
153+
154+
`var responseMessage = await factory.GetResponseMessageAsync(...)` 使用微信响应消息工厂,自动根据用户发送的消息,得到响应信息,可能会是文本信息,也可能是图片信息,取决于用户发送消息的内容(注意:当 OpenAI 接口未配置时,始终为文字消息)。
155+
156+
`await factory.SendCustomMessageAsync(...)` 用于根据上一步获取到的 `responseMessage` 对象,自动发送客服消息。这里没有直接在上一步就自动发送的原因,是希望在过程中暴露 `responseMessage`,给开发者更多的自由。
157+
158+
159+
160+
### 进阶:自动管理对话状态
81161

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Senparc.AI.Kernel;
2+
using Senparc.AI.Kernel.Handlers;
3+
using Senparc.CO2NET.Cache;
4+
using Senparc.CO2NET.MessageQueue;
5+
using Senparc.CO2NET.Trace;
6+
using Senparc.NeuChar.App.AppStore;
7+
using Senparc.NeuChar.Entities;
8+
using Senparc.NeuChar.Entities.Request;
9+
using Senparc.Weixin.MP.Entities;
10+
using Senparc.Weixin.MP.Entities.Request;
11+
using Senparc.Weixin.MP.MessageContexts;
12+
using Senparc.Weixin.MP.MessageHandlers;
13+
14+
namespace Senparc.Weixin.AI.MPSample
15+
{
16+
public class CustomFullTimeMessageHandler : MessageHandler<DefaultMpMessageContext>
17+
{
18+
const string DEFAULT_MESSAGE = @"欢迎使用 Senparc.Weixin SDK!
19+
20+
===开源地址===
21+
Senparc.AI模块:https://github.com/Senparc/Senparc.AI
22+
微信SDK:https://github.com/JeffreySu/WeiXinMPSDK";
23+
24+
private string _appId => Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId;
25+
26+
/// <summary>
27+
/// 为中间件提供生成当前类的委托
28+
/// </summary>
29+
public static Func<Stream, PostModel, int, IServiceProvider, CustomMessageHandler> GenerateMessageHandler = (stream, postModel, maxRecordCount, serviceProvider)
30+
=> new CustomMessageHandler(stream, postModel, maxRecordCount, false /* 是否只允许处理加密消息,以提高安全性 */, serviceProvider: serviceProvider);
31+
32+
public CustomFullTimeMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false, DeveloperInfo developerInfo = null, IServiceProvider serviceProvider = null) : base(inputStream, postModel, maxRecordCount, onlyAllowEncryptMessage, developerInfo, serviceProvider)
33+
{
34+
}
35+
36+
public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessageText requestMessage)
37+
{
38+
//使用消息队列处理
39+
var smq = new SenparcMessageQueue();
40+
var smqKey = $"Chat-{OpenId}-{SystemTime.NowTicks}";
41+
smq.Add(smqKey, async () =>
42+
{
43+
try
44+
{
45+
var handler = new SemanticAiHandler();
46+
var factory = new ReponseMessageFactory(ServiceProvider);
47+
var responseMessage = await factory.GetResponseMessageAsync(_appId, this, requestMessage, handler);
48+
await factory.SendCustomMessageAsync(responseMessage, ApiEnlightener, _appId, OpenId);
49+
}
50+
catch (Exception ex)
51+
{
52+
SenparcTrace.SendCustomLog("Chatting Exception", $"ex:{ex.Message},stack:{ex.StackTrace}");
53+
}
54+
55+
});
56+
57+
return base.CreateResponseMessage<ResponseMessageNoResponse>();
58+
}
59+
60+
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
61+
{
62+
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
63+
responseMessage.Content = DEFAULT_MESSAGE;
64+
return responseMessage;
65+
}
66+
}
67+
}

Samples/Senparc.Weixin.AI.MPSample/CustomMessageHandler.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,18 @@ public override async Task<IResponseMessageBase> OnTextRequestAsync(RequestMessa
8383
var smqKey = $"ChatGPT-{OpenId}-{SystemTime.NowTicks}";
8484
smq.Add(smqKey, async () =>
8585
{
86-
try {
86+
try
87+
{
8788
var handler = new SemanticAiHandler();
8889
var factory = new ReponseMessageFactory(ServiceProvider);
8990
var responseMessage = await factory.GetResponseMessageAsync(_appId, this, requestMessage, handler);
90-
91-
if (responseMessage is ResponseMessageText textResponse)
92-
{
93-
//使用客服消息
94-
await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(_appId, OpenId, textResponse.Content);
95-
}
91+
await factory.SendCustomMessageAsync(responseMessage, ApiEnlightener, _appId, OpenId);
9692
}
9793
catch (Exception ex)
9894
{
9995
SenparcTrace.SendCustomLog("Chatting Exception", $"ex:{ex.Message},stack:{ex.StackTrace}");
10096
}
101-
97+
10298
});
10399

104100
return base.CreateResponseMessage<ResponseMessageNoResponse>();

src/Senparc.Weixin.AI.Tests/BaseSupport/BaseTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Threading.Tasks;
1111
using Senparc.AI;
1212
using Moq;
13+
using Senparc.AI.Kernel;
1314

1415
namespace Senparc.Weixin.AI.Tests.BaseSupport
1516
{

src/Senparc.Weixin.AI/ReponseMessageFactory.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
using Senparc.CO2NET.Helpers;
99
using Senparc.CO2NET.MessageQueue;
1010
using Senparc.NeuChar;
11+
using Senparc.NeuChar.ApiHandlers;
1112
using Senparc.NeuChar.Entities;
1213
using Senparc.NeuChar.MessageHandlers;
1314
using Senparc.Weixin.AI.Entities;
1415
using Senparc.Weixin.AI.WeixinSkills;
1516
using Senparc.Weixin.MP.AdvancedAPIs;
17+
using Senparc.Weixin.MP.AdvancedAPIs.User;
1618
using Senparc.Weixin.MP.Entities;
1719
using System;
1820
using System.IO;
@@ -271,5 +273,26 @@ public async Task<IResponseMessageBase> GetResponseMessageAsync(string appId, IM
271273
}
272274
return responseMessage;
273275
}
276+
277+
/// <summary>
278+
/// 根据 ResposeMessage 类型自动发送客服消息
279+
/// </summary>
280+
/// <param name="responseMessage"></param>
281+
/// <returns></returns>
282+
public async Task SendCustomMessageAsync(IResponseMessageBase responseMessage, ApiEnlightener apiEnlightener, string appId, string openId)
283+
{
284+
//使用客服消息
285+
if (responseMessage is ResponseMessageText textResponse)
286+
{
287+
await apiEnlightener.SendText(appId, openId, textResponse.Content);
288+
289+
//等效于:
290+
//await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(_appId, OpenId, textResponse.Content);
291+
}
292+
else if (responseMessage is ResponseMessageImage imageResponse)
293+
{
294+
await apiEnlightener.SendImage(appId, openId, imageResponse.Image.MediaId);
295+
}
296+
}
274297
}
275298
}

0 commit comments

Comments
 (0)