哪个公司的 CEO 不想拥有一个自己的数字克隆?

⚠️ FBI Warning:本文纯属作者自娱自乐,数字人的观点不代表 CEO 本人的观点,请大家不要上当受骗!!

哪个公司的 CEO 不想拥有一个自己的数字克隆?

想象🤔一下,如果 CEO 数字克隆上线了,那他是不是就可以一天约见 100 个投资人了?把他接入企业官方公众号后台作为客服,24 小时不吃饭不睡觉不喝水给用户答疑解惑,想想就很刺激!感觉 CEO 在给我打工

环界云的 CEO 做到了!先来看看效果:

怎么样,你也想拥有一个自己的数字克隆么?问题不大,跟着我操作。

首先你需要准备自己的语料,我们 CEO 的语料就是来自各种同性交友大会的演讲内容,如果你的语料不够多,那就得自己想办法了。

当然,本文提供的方法不仅仅适用于数字克隆,你可以基于任意专有知识库来打造一个私有领域的专家或者客服,然后再对接到公众号,它不香吗?

准备工作

已认证的微信公众号

首先你需要有一个微信公众号,而且是已经认证的公众号,因为公众号强制要求服务器每次必须在 15s 以内回复消息,公众号平台在发送请求到服务器后,如果 5s 内没收到回复,会再次发送请求等候 5 秒,如果还是没有收到请求,最后还会发送一次请求,所以服务器必须在 15s 以内完成消息的处理。如果超过 15s 还没有返回怎么办?那就超时了,用户将永远都收不到这条消息。

如果你想突破 15s 限制怎么办?

  • 如果是已认证的公众号,可以直接使用客服消息进行回复,它的原理是通过 POST 一个 JSON 数据包来发送消息给普通用户。客服消息就厉害了,只要在 48 小时以内都可以回复。具体可查看微信官方文档。
  • 如果是未认证的公众号,并不能完全解决 15s 限制的问题,但是可以优化。这里提供一个思路,你可以使用流式响应来缓解这个限制,先与 OpenAI 建立连接,再一个字符一个字符获取生成的文本,最后将所获取的文本列表拼接成回复文本。能缓解请求超时的关键在于:建立连接的时间一般情况下不会超过 15s,所以只要在给定的时间内,成功建立连接,基本就能返回内容(15s 之后接收到多少文本就返回多少文本)。虽然有可能会出现回复内容被截断的情况,但总比你回复不了强吧?

本文给出的方法是基于微信客服消息进行回复,所以需要一个已认证的公众号。如果是未认证的公众号,就需要你自己研究流式响应了,本文不做赘述。

FastGPT

其次你需要注册一个 FastGPT 账号。它是一个 ChatGPT 平台项目,目前已经集成了 ChatGPT、GPT4 和 Claude,可以使用任意文本来训练自己的知识库

🌐 注册链接:https://fastgpt.run/?inviterId=64215e9914d068bf840141d0

知识库

注册完 FastGPT 后,你可以直接填写自己的 API Key 进行使用,也可以在 FastGPT 平台充值使用。

接下来点击侧栏的数据库图标进入知识库界面,然后点击 “+” 号新建一个知识库。

点击「导入」,可以看到有 3 种方法来导入知识库。

如果你有多个文本文件,可以直接选择「文本/文件拆分」进行导入,模式建议选「QA 拆分」,也可以直接分段。

导入之后,就会开始训练,训练完成后的效果:

Laf

最后你还需要一个平台来开发你的应用,那当然是 Laf 啦。据环界云 CEO 数字克隆所说👀,Laf 是一个 Serverless 框架,可以用来快速开发具有 AI 能力的分布式应用,助你像写博客一样写代码,随时随地快速发布上线应用。真⭕五分钟上线 CEO 数字克隆!

🌐 Laf 注册链接:https://laf.run

编写云函数

一切工作准备就绪后,开始动笔写亿点点代码。

先新建应用,直接新建免费的进行测试:

点击「+」新建云函数:

然后将下面的云函数代码直接复制粘贴到 Web IDE 中:

import cloud from '@lafjs/cloud';
import * as crypto from 'crypto';

// 公众号配置
const appid = 'wxb1833715d8f0809d'
const appsecret = 'fd76ce714a8083112100c2160b2f2c5d'
const wxToken = 'test';
// fastgpt配置
const apikey = "63f9a14228d2a688d8dc9e1b-xsyvfby3cui09tfcvxen3"
const modelId = "642adec15f01d67d4613efdb"

// 创建数据库连接并获取Message集合
const db = cloud.database();
const _ = db.command
const Message = db.collection('messages')

// 处理接收到的微信公众号消息
export async function main(event) {
  // const res = await cloud.fetch.post(` https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${await getAccess_token()}`, {
  //   button: [
  //     {
  //       "type": "click",
  //       "name": "清空记录",
  //       "key": "CLEAR"
  //     },
  //   ]
  // })
  const { signature, timestamp, nonce, echostr } = event.query;

  // 验证消息是否合法,若不合法则返回错误信息
  if (!verifySignature(signature, timestamp, nonce, wxToken)) {
    return 'Invalid signature';
  }
  // 如果是首次验证,则返回 echostr 给微信服务器
  if (echostr) {
    return echostr;
  }

  // -------------- 正文开始

  const payload = event.body.xml;
  const sessionId = payload.fromusername[0]

  console.log(payload)
  // 点击了清空记录
  if (payload.msgtype[0] === 'event' && payload.eventkey[0] === 'CLEAR') {
    console.log(1111)
    await Message.where({ sessionId: sessionId }).remove({ multi: true })
    await replyBykefu('记录已清空', sessionId)
    return 'clear record'
  }

  // 仅做文本消息例子
  if (payload.msgtype[0] !== 'text') return 'no text'
  const newMessage = {
    msgid: payload.msgid[0],
    question: payload.content[0].trim(),
    username: payload.fromusername[0],
    sessionId,
    createdAt: Date.now()
  }

  await replyText(newMessage, payload.fromusername[0])

  return 'success'
}

// 处理文本回复消息
async function replyText(message, touser) {
  const { question, sessionId, msgid } = message;

  // 重复的内容,不回复
  const { data: msg } = await Message.where({ msgid: message.msgid }).getOne()
  if (msg) return

  console.log("收到用户消息", touser, message)

  // 立即添加一条待回复记录 
  await Message.add(message);

  // 回复提示
  await replyBykefu("🤖机器人正在思考🤔中...", sessionId)
  await changesState(sessionId)

  const reply = await getFastGptReply(question, sessionId);

  const { answer } = reply;

  await Message.where({ msgid: message.msgid }).update({
    answer,
  });

  // return answer;
  await replyBykefu(answer, touser)
}

// 获取微信公众号ACCESS_TOKEN
async function getAccess_token() {
  const shared_access_token = await cloud.shared.get("mp_access_token")

  if (shared_access_token && shared_access_token.access_token && shared_access_token.exp > Date.now()) {
    return shared_access_token.access_token
  }
  // ACCESS_TOKEN不存在或者已过期
  // 获取微信公众号ACCESS_TOKEN
  const mp_access_token = await cloud.fetch.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`)
  mp_access_token.data.access_token && cloud.shared.set("mp_access_token", {
    access_token: mp_access_token.data.access_token,
    exp: Date.now() + 7100 * 1000
  })
  return mp_access_token.data.access_token
}

// 公众号客服回复文本消息
export async function replyBykefu(message, touser) {
  // 判断是否为中文字符
  function isChinese(char) {
    return /[\u4e00-\u9fa5]/.test(char)  // 判断是否是中文字符
  }
  // 拆分文本长度
  function splitText(text) {
    let result = []
    let len = text.length
    let index = 0
    while (index < len) {
      let part = ''
      let charCount = 0
      while (charCount < 800 && index < len) {
        let char = text[index]
        charCount++
        part += char
        if (isChinese(char)) charCount++  // 中文字符计数+1
        index++
      }
      result.push(part)
    }
    return result
  }
  // 定义休眠函数
  function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) };

  const access_token = await getAccess_token()
  let text = splitText(message)
  let len = splitText(message).length

  try {
    for (let i = 0; i < len; i++) {
      let part = text[i]  // 获取第 i 段
      await sleep(1000)
      // 回复消息
      const res = await cloud.fetch.post(`https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${access_token}`, {
        "touser": touser,
        "msgtype": "text",
        "text":
        {
          "content": part
        }
      })
    }
  } catch (err) {
    console.log(err)
  }
}

// 修改公众号回复状态
export async function changesState(touser) {
  const access_token = await getAccess_token()
  // 修改正在输入的状态
  const res = await cloud.fetch.post(`https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=${access_token}`, {
    "touser": touser,
    "command": "Typing"
  })
}

// 校验微信服务器发送的消息是否合法
export function verifySignature(signature, timestamp, nonce, token) {
  const arr = [token, timestamp, nonce].sort();
  const str = arr.join('');
  const sha1 = crypto.createHash('sha1');
  sha1.update(str);
  return sha1.digest('hex') === signature;
}

// 返回组装 xml
export function toXML(payload, content) {
  const timestamp = Date.now();
  const { tousername: fromUserName, fromusername: toUserName } = payload;
  return `
      <xml>
        <ToUserName><![CDATA[${toUserName}]]></ToUserName>
        <FromUserName><![CDATA[${fromUserName}]]></FromUserName>
        <CreateTime>${timestamp}</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[${content}]]></Content>
      </xml>
      `
}

// 调用 fastgpt 回答
async function getFastGptReply(question, sessionId) {
  const res = await db.collection('messages')
    .where({ sessionId })
    .get()

  // 获取最多10组上下文
  const list = res.data.slice(-10)
  const prompts = list.map((item) => [{
    obj: "Human",
    value: item.question || ''
  }, {
    obj: "AI",
    value: item.answer || ''
  }]).concat({
    obj: "Human",
    value: question
  }).flat()

  const config = {
    method: 'post', // 设置请求方法为POST
    url: 'https://fastgpt.run/api/openapi/chat/chat', // 设置请求地址
    headers: { // 设置请求头信息
      apikey,
      'Content-Type': 'application/json'
    },
    data: { // 设置请求体数据
      modelId,
      isStream: false,
      prompts
    }
  }
  try {
    const ret = await cloud.fetch(config)
    console.log("fastgpt响应", ret.data)
    return { answer: ret.data.data || ret.data || '' }
  } catch (e) {
    console.log("出错了", e.response)
    return {
      error: "问题太难了 出错了. (uДu〃).",
    }
  }
}

整个云函数的调用流程如下:

❶ 当收到微信公众号消息时,首先调用 main 函数。在 main 函数中,首先验证消息是否合法,如果不合法则返回错误信息。如果是首次验证,则返回 echostr 给微信服务器。

❷ 接着根据消息类型进行处理。对于文本消息,调用 replyText 函数进行处理。

❸ 在 replyText 函数中,首先检查是否为重复的内容,如果是则不回复。然后将用户发送的问题存入数据库,并回复提示信息给用户,表示机器人正在思考中。

❹ 接下来调用 getFastGptReply 函数获取 FastGPT 的回答。在 getFastGptReply 函数中,首先从数据库中获取最多 10 组上下文信息,然后将问题和上下文信息一起发送给 FastGPT。接收到 FastGPT 的回答后返回给 replyText 函数。

❺ 回到 replyText 函数,将 FastGPT 返回的回答更新到数据库中,并通过客服接口将回答发送给用户。在发送回答之前,会调用 changesState 函数修改公众号回复状态为正在输入中。

❻ 调用 replyBykefu 函数通过微信公众号客服接口发送文本消息给用户。在 replyBykefu 函数中,首先根据文本长度拆分成多段,并逐段发送给用户。

先不要改动代码中的任何内容,后面会告诉你如何修改。

点击「发布」:

最后复制已发布的函数地址:

配置微信公众号

这一步我们需要在微信公众号平台上配置开发者信息,并将服务器地址设置为部署好的云函数服务地址。步骤如下:

首先登录微信公众平台,点开左侧的「设置与开发」,点击「基本设置」,然后点击「服务器配置」,服务器配置那里点击修改配置:

将之前的云函数服务地址复制到「服务器 URL」中,下边的 Token 与云函数代码中的 token 保持一致,下边的 EncodingAESKey 点击右侧随机生成就行,然后点击提交:

返回 token 校验成功即可。

获取公众号的 AppID 和 AppSecret:

这一步的操作请务必不要忘记!!!你需要把 laf.run 的 IP 地址全部添加到 IP 白名单中:

laf.run 域名的 IP 地址可通过以下命令获取:

$ dig +short laf.run
112.124.8.17
120.26.163.28
112.124.9.83
47.97.22.68
112.124.9.194
114.55.179.67
114.55.177.246
120.27.246.172
120.26.161.248
47.97.5.237

把获取到的 AppID 和 AppSecret 填写到 Laf 云函数中,然后点击「发布」:

最后在公众号平台点击「启用」即可。

配置 FastGPT

接下来开始配置 FastGPT,首先新建一个 API Key:

然后新建一个应用:

然后选择需要关联的知识库:

可以根据自己的需求设置一下温度、搜索模式和系统提示词,最终点击「保存修改」。

获取应用的 modelId:

将你获取的 API Key 和 modelId 填写到 Laf 云函数中,修改完成后点击发布:

到公众号里测试一下:

完美👀

当然,接入数字 CEO 只是图个乐呵,演示完了就撤了。目前 Laf 公众号真正接入的是 Laf 专有模型,可以回答与 Laf 相关的任何问题,感兴趣的小伙伴可以去体验一下,公众号的名字是:Laf 开发者

QA

如果发送消息后无响应,可以先去 Laf 控制台的日志中检查是否收到用户消息,有下面的提示代表是正常的(可能需要点下搜索才能刷新出来)。

如果收到了消息,但是没有回复,八成是公众号没有发送客服消息权限。对应是下图的权限:

sealos 以kubernetes为内核的云操作系统发行版,让云原生简单普及

laf 写代码像写博客一样简单,什么docker kubernetes统统不关心,我只关心写业务!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/28317.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ISP下载原理分析

STM32的启动方式&#xff0c;系统存储器启动就是通过ISP下载 ISP简介 ISP下载是指可以通过串行外设&#xff0c;直接将程序下载Flash中&#xff0c;然后自动运行程序的启动方式。 ISP的时候需要用到bootloder(自举程序)&#xff0c;bootloader存储在STM32内部的自举ROM存储器…

wordpress去除分类URL的categpory

前言 在日常使用Wordpress搭建网站时&#xff0c;发现文章或者分类页的URL地址中默认带有Category&#xff0c;URL层级过长会影响我们网站SEO的优化&#xff0c;也不利于用户体验。这里讲一下如何去除URL中categpory的方法。 操作 第一步先登录到WordPress后台&#xff0c;然…

【论说文】段落与结构

结构和段落 开头和结尾 怎么写开头呢&#xff1f;基本套路就是用三句话。即表达清楚三层意思&#xff0c;第一句话是用简短的话来概括材料&#xff0c;但是不要照抄。写论说文&#xff0c;不是就事论事&#xff0c;而是就事论理。第二句话是过渡句。第三句&#xff0c;写出来中…

IDEA整合GO并创建module工程

IDEA整合Go 安装包环境配置idea配置并创建test mode 安装包 1.去官网下载对应还的安装包 官网下载地址 我选择下载的window 版本&#xff1a; 直接按照对应的目录&#xff0c;然后点击下一步 环境配置 1.配置go环境变量 在高级环境变量PAHT中添加安装包的**/bin 目录&…

Java网络开发(Tomcat同步数据增删改查)—— 用Jsp语法实现同步请求的 增删改查

目录 引出显示所有数据到前端&#xff08;1&#xff09;前端代码&#xff1a;list.jsp&#xff08;2&#xff09;后端代码&#xff1a;CompanyListServlet.java 新增数据---转发类型信息---新增信息业务&#xff08;1&#xff09;在list.jsp页面点击添加&#xff08;2&#xff…

SQL调优:让Java内存分担计算

作者: 剽悍一小兔 CSDN前端优质创作者&#xff0c;打破编程小说次元壁第一人《JavaScript百炼成仙》作者&#xff0c;专注Java硬核干货分享&#xff0c;分享创造快乐&#xff0c;技术成就梦想&#xff01; 我们在工作中&#xff0c;经常会因为一条慢sql调半天。这一节&#xff…

Jenkins集成钉钉通知插件的具体步骤怎么做你知道吗?

最近公司要求工作务必使用钉钉&#xff0c;其他聊天软件不再用于工作沟通了。虽然很抓狂&#xff0c;但是上面的决定不可违逆&#xff0c;只好转战钉钉。虽然强制使用钉钉挺令人反感的&#xff0c;但阿里在这款软件上确实下了些功夫&#xff0c;比如jenkins集成钉钉通知插件后&…

MySQL 数据库基础

这里写目录标题 一、Mysql的基本概念数据库管理系统&#xff08;DBMS&#xff09;数据库系统 二、数据库的发展史三、 主流的数据库介绍数据库分为关系型数据库与非关系型数据库关系型数据库非关系型数据库介绍 四、 操作Mysql常用的数据类型&#xff1a;常看数据库结构查看当前…

Linux内核中内存管理相关配置项的详细解析16

接前一篇文章&#xff1a;Linux内核中内存管理相关配置项的详细解析15 三十五、Data Access Monitoring 此项展开后如下图所示&#xff1a; “DAMON: Data Access Monitoring Framework”项默认不选中。如果将其选中&#xff0c;则页面变为&#xff1a; 1. DAMON: Data Access…

Kafka学习---1、Kafka 概述、Kafka快速入门

1、Kafka概述 1.1 定义 1、Kafka传统定义&#xff1a;Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue)&#xff0c;主要是应用于大数据实时处理领域。 2、发布/订阅&#xff1a;消息的发布者不会将信息直接发送给特定的订阅者&#xff0c;而是将发布的信息分…

系统稳定性与高可用保障

一、前言 高并发、高可用、高性能被称为互联网三高架构&#xff0c;这三者都是工程师和架构师在系统架构设计中必须考虑的因素之一。今天我们就来聊一聊三 H 中的高可用&#xff0c;也是我们常说的系统稳定性。 > 本篇文章只聊思路&#xff0c;没有太多的深入细节。阅读全…

大数据分析案例-基于逻辑回归算法构建心脏病发作预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

几个直接在TigerISP上查看全志芯片接Sensor分辨率的方法

TigerISP是全志提供的量产工具&#xff0c;在连接TigerISP时需要填写Sensor名称、Sensor分辨率、Sensor帧率及选择ISP通道、Vich、Wdr模式等… 准备工作&#xff1a;打开debugfs 操作&#xff1a;adb shell进入系统后输入以下两个命令&#xff1a; mount -t debugfs mone /s…

【办公类-30-01】(Python)大班毕业证书批量打印(幼儿信息、性别、毕业日期、学校、公章、签名、证书日期)

背景需求 大班毕业在即&#xff0c;需要打印大班幼儿毕业证书。&#xff08;已有打印好的彩色证书&#xff09; 常规操作模式&#xff1a; 1&#xff0c;统一盖章&#xff0c;反复签字 200份证书&#xff0c;每张证书上需要盖园所章、园长签字200次 2. 每个班主任自己领取班…

服务日志性能调优,由log引出的巨坑

只有被线上服务问题毒打过的人才明白日志有多重要&#xff01; 谁赞成&#xff0c;谁反对&#xff1f;如果你深有同感&#xff0c;那恭喜你是个社会人了&#xff1a;&#xff09; 日志对程序的重要性不言而喻&#xff0c;轻巧、简单、无需费脑&#xff0c;程序代码中随处可见…

Spring事物失效的八大场景

1.方法内的自调用&#xff1a;spring事物是基于aop的&#xff0c;只要使用代理对象调用某个方法时&#xff0c;spring事物才能生效&#xff0c;而在一个方法内使用this.xxx()时。this并不是代理对象&#xff0c;所以会失效&#xff08;实际上是transaction注解失效&#xff09;…

64位和32位相比优势是什么(一)

前置知识&#xff1a;程序是如何执行的&#xff1f; 一道常规的面试题&#xff1a;相比 32 位&#xff0c;64 位的优势是什么&#xff1f; 面试官考察这种类型的问题&#xff0c;主要是想看求职者是否有扎实的计算机基础&#xff0c;同时想知道求职者在工作中是否充满好奇&am…

渲染案例 | 《妈妈的牵牛花》荣获厦门国际动漫节金奖

2023年5月25日&#xff0c;第十五届厦门国际动漫节“金海豚奖”动画组获奖名单公布。其中&#xff0c;蓝海创意云《青团计划》优秀代表作品——《妈妈的牵牛花》荣获最佳学生动画金奖。 蓝海创意云作为行业内知名的影视动画渲染服务商&#xff0c;深度参与《妈妈的牵牛花》的后…

【CSS】文字扫光 | 渐变光

码来 可调整角度与颜色值来改变效果 <p class"gf-gx-color">我是帅哥</p> <style>.gf-gx-color {background: -webkit-linear-gradient(135deg,red,red 25%,red 50%,#fff 55%,red 60%,red 80%,red 95%,red);-webkit-text-fill-color: transparen…

systemctl 命令设置开机自启动失败

1.案例现象 我在 3 月 31日的时候发表了一篇《shell 脚本之一键部署安装 Nginx 》&#xff0c;介绍了如何通过 shell 脚本一键安装 Nginx 我脚本中执行了 Nginx 开机自启动的命令&#xff0c;当我使用 systemctl status nginx 命令复核的时候&#xff0c;我发现 Nginx 服务设…