跳至主要內容

微信公众号-chatgpt智能客服搭建

Zephery约 2987 字大约 10 分钟

微信公众号-chatgpt智能客服搭建

想体验的可以去微信上搜索【旅行的树】公众号。

一、ChatGPT注册

1.1 短信手机号申请

openai提供服务的区域,美国最好,这个解决办法是搞个翻墙,或者买一台美国的服务器更好。

国外邮箱,hotmail或者google最好,qq邮箱可能会对这些平台进行邮件过滤。

国外手机号,没有的话也可以去https://sms-activate.orgopen in new window,费用大概需要1美元,这个网站记得也用国外邮箱注册,需要先充值,使用支付宝支付。

image-20230220172107296

之后再搜索框填openai进行下单购买即可。

img

1.2 云服务器申请

openai在国内不提供服务的,而且也通过ip识别是不是在国内,解决办法用vpn也行,或者,自己去买一台国外的服务器也行。我这里使用的是腾讯云轻量服务器,最低配置54元/月,选择windows的主要原因毕竟需要注册openai,需要看页面,同时也可以搭建nginx,当然,用ubuntu如果能自己搞界面也行。

image-20230221193455384

1.3 ChatGPT注册

购买完之后,就可以直接打开openai的官网了,然后去https://platform.openai.com/signup官网里注册,注册过程具体就不讲了,讲下核心问题——短信验证码open in new window

image-20230222104357230

然后回sms查看验证码。

image-20230222104225722

注册成功之后就可以在chatgpt里聊天啦,能够识别各种语言,发起多轮会话的时候,可能回出现访问超过限制什么的。

image-20230220173335691

通过chatgpt聊天不是我们最终想要的,我们需要的是在微信公众号也提供智能客服的聊天回复,所以我们需要在通过openai的api来进行调用。

image-20230222104316514

二、搭建nginx服务器

跟页面一样,OpenAI的调用也是不能再国内访问的,这里,我们使用同一台服务器来搭建nginx,还是保留使用windows吧,主要还是得注意下面这段话,如果API key被泄露了,OpenAI可能会自动重新更新你的API key,这个规则似乎是API key如果被多个ip使用,就会触发这个规则,调试阶段还是尽量使用windows的服务器吧,万一被更新了,还能去页面上重新找到。

Do not share your API key with others, or expose it in the browser or other client-side code. In order to protect the security of your account, OpenAI may also automatically rotate any API key that we've found has leaked publicly.

windows的安装过程参考网上的来,我们只需要添加下面这个配置即可,原理主要是将调用OpenAI的接口全部往官网转发。

    location /v1/completions {
      proxy_pass https://api.openai.com/v1/completions;
    }

然后使用下面的方法进行调试即可:

POST http://YOUR IP/v1/completions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
  "model": "text-davinci-003",
  "prompt": "Say this is a test",
  "max_tokens": 7,
  "temperature": 0
}

三、公众号开发

网上有很多关于微信通过chatgpt回复的文章,有些使用自己微信号直接做为载体,因为要扫码网页登陆,而且是网页端在国外,很容易被封;有些是使用公众号,相对来说,公众号被封也不至于导致个人微信号出问题。

3.1 微信云托管

微信公众平台提供了微信云托管,无需鉴权,比其他方式都方便不少,可以免费试用3个月,继续薅羊毛,当然,如果自己开发能力足够,也可以自己从0开始开发。

image-20230220192603751

提供了各种语言的模版,方便快速开发,OpenAI官方提供的sdk是node和python,这里我们选择express(node)。

image-20230220201004069

3.2 一个简单ChatGPT简单回复

微信官方的源码在这,https://github.com/WeixinCloud/wxcloudrun-express,我们直接fork一份自己来开发。open in new window

一个简单的消息回复功能(无db),直接在我们的index.js里添加如下代码。

const configuration = new Configuration({
    apiKey: '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',
    basePath: 'http://43.153.15.174/v1'
});
const openai = new OpenAIApi(configuration);

async function simpleResponse(prompt) {
    const completion = await openai.createCompletion({
        model: 'text-davinci-003',
        prompt,
        max_tokens: 1024,
        temperature: 0.1,
    });
    const response = (completion?.data?.choices?.[0].text || 'AI 挂了').trim();
    return strip(response, ['\n', 'A: ']);
}

app.post("/message/simple", async (req, res) => {
    console.log('消息推送', req.body)
    // 从 header 中取appid,如果 from-appid 不存在,则不是资源复用场景,可以直接传空字符串,使用环境所属账号发起云调用
    const appid = req.headers['x-wx-from-appid'] || ''
    const {ToUserName, FromUserName, MsgType, Content, CreateTime} = req.body
    console.log('推送接收的账号', ToUserName, '创建时间', CreateTime)
    if (MsgType === 'text') {
        message = await simpleResponse(Content)
        res.send({
            ToUserName: FromUserName,
            FromUserName: ToUserName,
            CreateTime: CreateTime,
            MsgType: 'text',
            Content: message,
        })
    } else {
        res.send('success')
    }
})

本地可以直接使用http请求测试,成功调用之后直接提交发布即可,在微信云托管上需要先授权代码库,即可使用云托管的流水线,一键发布。注意,api_base和api_key可以填写在服务设置-基础信息那里,一个是OPENAI_API_BASE,一个是OPENAI_API_KEY,服务会自动从环境变量里取。

3.3 服务部署

提交代码只github或者gitee都可以,值得注意的是,OpenAI判断key泄露的规则,不知道是不是判断调用的ip地址不一样,还是github的提交记录里含有这块,有点玄学,同样的key本地调用一次,然后在云托管也调用的话,OpenAI就很容易把key给重新更新。

image-20230220203313790

部署完之后,云托管也提供了云端调试功能,相当于在服务里发送了http请求。这一步很重要,如果没有调用成功,则无法进行云托管消息推送。

image-20230220203711436

这里填上你自己的url,我们这里配置的是/meesage/simple,如果没有成功,需要进行下面步骤进行排查:

(1)服务有没有正常启动,看日志

(2)端口有没有设置错误,这个很多次没有注意到

image-20230220203445297

保存成功之后,就可以在微信公众号里测试了。

image-20230221134250689

体验还可以

四、没有回复(超时回复问题)

很多OpenAI的回答都要几十秒,有的甚至更久,比如对chatgpt问“写一篇1000字关于深圳的文章”,就需要几十秒,而微信的主动回复接口,是需要我们3s内返回给用户。

订阅号的消息推送分几种:

  1. 被动消息回复:指用户给公众号发一条消息,系统接收到后,可以回复一条消息。
  2. 主动回复/客服消息:可以脱离被动消息的5秒超时权限,在48小时内可以主动回复。但需要公众号完成微信认证。

根据微信官方文档,没有认证的公众号是没有调用主动回复接口权限的,https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.htmlopen in new window

image-20230221100251825

对于有微信认证的订阅号或者服务号,可以调用微信官方的/cgi-bin/message/custom/send接口来实现主动回复,但是对于个人的公众号,没有权限调用,只能尝试别的办法。想来想去,只能在3s内返回让用户重新复制发送的信息,同时后台里保存记录异步调用,用户重新发送的时候再从数据库里提取回复。

1.先往数据库存一条 回复记录,把用户的提问存下来,以便后续查询。设置回复的内容为空,设置状态为 回复中(thinking)。

  // 因为AI响应比较慢,容易超时,先插入一条记录,维持状态,待后续更新记录。
  await Message.create({
    fromUser: FromUserName,
    response: '',
    request: Content,
    aiType: AI_TYPE_TEXT, // 为其他AI回复拓展,比如AI作画
  });

2.抽象一个 chatGPT 请求方法 getAIMessage,函数内部得到 GPT 响应后,会更新之前那条记录(通过用户id & 用户提问 查询),把状态更新为 已回答(answered),并把回复内容更新上。

 // 成功后,更新记录
  await Message.update(
    {
      response: response,
      status: MESSAGE_STATUS_ANSWERED,
    },
    {
      where: {
        fromUser: FromUserName,
        request: Content,
      },
    },
  );

3.前置增加一些判断,当用户在请求时,如果 AI 还没完成响应,直接回复用户 AI 还在响应,让用户过一会儿再重试。如果 AI 此时已响应完成,则直接把 内容返回给用户。

  // 找一下,是否已有记录
  const message = await Message.findOne({
    where: {
      fromUser: FromUserName,
      request: Content,
    },
  });

  // 已回答,直接返回消息
  if (message?.status === MESSAGE_STATUS_ANSWERED) {
    return `[GPT]: ${message?.response}`;
  }

  // 在回答中
  if (message?.status === MESSAGE_STATUS_THINKING) {
    return AI_THINKING_MESSAGE;
  }

4.最后就是一个 Promise.race

  const message = await Promise.race([
    // 3秒微信服务器就会超时,超过2.9秒要提示用户重试
    sleep(2900).then(() => AI_THINKING_MESSAGE),
    getAIMessage({ Content, FromUserName }),
  ]);

这样子大概就能实现超时之前返回了。

五、会话保存

掉接口是一次性的,一次接口调用完之后怎么做到下一次通话的时候,还能继续保持会话,是不是应该类似客户端与服务端那种有个session这种,但是实际上在openai里是没有session这种东西的,令人震惊的是,chatgpt里是通过前几次会话拼接起来一起发送给chatgpt里的,需要通过回车符来拼接。

async function buildCtxPrompt({ FromUserName }) {
  // 获取最近10条对话
  const messages = await Message.findAll({
    where: {
      fromUser: FromUserName,
      aiType: AI_TYPE_TEXT,
    },
    limit: 10,
    order: [['updatedAt', 'ASC']],
  });
  // 只有一条的时候,就不用封装上下文了
  return messages.length === 1
    ? messages[0].request
    : messages
        .map(({ response, request }) => `Q: ${request}\n A: ${response}`)
        .join('\n');
}

之后就可以实现会话之间的保存通信了。

image-20230218203309437

六、其他问题

6.1 限频

chatgpt毕竟也是新上线的,火热是肯定的,聊天窗口只能开几个,api调用的话,也是有限频的,但是规则具体没有找到,只是在调用次数过多的时候会报429的错误,出现之后就需要等待一个小时左右。

对于这个的解决办法只能是多开几个账号,一旦429就只能换个账号重试了。

6.2 秘钥key被更新

没有找到详细的规则,凭个人经验的话,可能github提交的代码会被扫描,可能ip调用的来源不一样,最好还是开发一个秘钥,生产一个秘钥吧。

6.3 为啥和官网的回复不一样

我们这里用的模型算法是text-davinci-003,具体可以参考:https://platform.openai.com/docs/models/overview,也算是一个比较老的样本了吧open in new window

image-20230221192417900

从官方文档来看,官方服务版的 ChatGPT 的模型并非基础版的text-davinci-003,而是经过了「微调:fine-tunes」。文档地址在这:platform.openai.com/docs/guides…open in new window

6.4 玄学挂掉

有时候消息没有回复,真的不是我们的问题,chatgpt毕竟太火了,官网的这个能力都经常挂掉,也可以订阅官网修复的通知,一旦修复则会发邮件告知你。

image-20230221175150025

参考:https://juejin.cn/post/7200769439335546935open in new window

最后

记得去微信关注【旅行的树】公众号体验
代码地址:https://github.com/Zephery/wechat-gptopen in new window