终于!我们把 CEO 炒了,让 ChatGPT 出任 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 控制台的日志中检查是否收到用户消息,有下面的提示代表是正常的(可能需要点下搜索才能刷新出来)。
如果收到了消息,但是没有回复,八成是公众号没有发送客服消息权限。对应是下图的权限:
终于!我们把 CEO 炒了,让 ChatGPT 出任 CEO的更多相关文章
- 北电之死:谁谋杀了华为的对手?——银湖资本(Silver Lake)董事总经理爱德华·詹德出任CEO,既不了解华为,也不重视中国,直截了当地否决了收购华为
作者:戴老板:微信公众号:饭统戴老板(ID: worldofboss) 2003年5月,北京SARS疫情紧张,摩托罗拉集团总裁迈克·扎菲罗夫斯基(Mike Zafirovski)却准备不走寻常路,决定 ...
- 【文学文娱】《屌丝逆袭》-出任CEO、迎娶白富美、走上人生巅峰
本文地址:http://www.cnblogs.com/aiweixiao/p/7759790.html 原文地址:(微信公众号) 原创 2017-10-30 微信号wozhuzaisi 程序员的文娱 ...
- Python之路【第二篇】:Python基础
Python基础 对于Python,一切事物都是对象,对象基于类创建 所以,以下这些值都时对象:"zhurui".22.['北京','上海','深圳'],并且是根据不同的类生成的对 ...
- 3星|《给你讲个笑话:我是创业公司CEO》:创业成功就是上帝掷骰子
给你讲个笑话:我是创业公司CEO 作者有过数次创业经历,最后一次在济南创业,后来公司搬到北京,看书中的交代公司目前好像还不算太成功.书中交代作者公司的业务是文化产品的策划,没细说做什么,也没说做成过哪 ...
- 搜狐畅游CEO王滔辞职
凤凰科技讯 11月3日消息,搜狐公布公告确认搜狐畅游CEO离职.公告称王滔因个人原因辞去畅游首席运行官职务.将继续担任畅游公司董事和首席产品官. 据搜狐公告,董事会任命搜狐总裁余楚媛与畅游总裁陈德文为 ...
- CEO 系列之一:如何当好创业公司 CEO?(不要用战术的勤奋掩盖战略的懒惰,在创业过程中,最核心问题,就是能把创业情怀变成具体问题。这个问题越具体越好)
1. 创业公司要先定一个目标,要善于把目标简化, 分解成一个, 一个更具体,更简单的问题2. 针对简单的问题进行聚焦, 做深做强3. 在做的过程中, 把断地推出自己的产品到市场上去试错, 要用事实来证 ...
- 从CIO、CEO、CFO、COO...到CVO 这22个你了解几个? (史上最完整版)
1.CEO:是Chief Executive Officer的缩写,即首席执行官. 由于市场风云变幻,决策的速度和执行的力度比以往任何时候都更加重要.传统的“董事会决策.经理层执行”的公司体制已经难以 ...
- 创业草堂之六:CEO的财务自修课
创业团队中一个最普遍的缺陷,是团队--尤其是团队的核心人物CEO,缺乏基本的财务知识和技能.一个不懂财务知识的CEO,即使业务能力再强,在投资人的眼里,他/她依然是一个笨拙的CEO.粗糙的CEO.鲁莽 ...
- 董事长、总裁与CEO的区别与实质
自从信息产业兴起以来,尤其是网络股泡沫产生以来,“CEO”在中国骤然成为一个流行词汇.总经理和总裁们纷纷改称CEO,这个缩写词比它的中译版“首席执行官”更简洁,在中国人心目中更有神圣感,于是便出现了今 ...
- 董事局主席董事长总裁首席执行官CEO总裁董事监事区别
董事长是公司的最大股东:董事长是董事会的主席,一般是企业的所有者,掌握企业的股权并且决定企业的发展策略. 董事局主席通常是在大财团中才会出现,董事局主席管数个董事长,一个大财团涉及很多方面的业务,因此 ...
随机推荐
- WordPress简码实现的一些常用的效果
首先要确保框架里已经安装好element pro插件,下面是使用简码,来实现效果,在element中找到简码,并且在WordPress后台主题编辑器中,找到function.php文件, 显示产品的分 ...
- NestJs系列之使用Vite搭建项目
介绍 在使用nest创建项目时,默认使用webpack进行打包,有时候启动项目需要1-2分钟.所以希望采用vite进行快速启动项目进行开发. 本文主要使用NestJs.Vite和swc进行配置.文章实 ...
- DRF限流
https://www.django-rest-framework.org/api-guide/throttling/ 限制.控制客户端可以向 API 发出的请求的速率. 设置限流策略 默认限制策略可 ...
- tomcat中文乱码怎么解决
需要修改Tomcat根目录下面的"logging.properties"文件,把所有的encoding=UTF-8的改成encodng=GBK,保存之后,重启Tomcat服务器,就 ...
- vivo 数据库备份恢复系统演化
作者:vivo 互联网数据库团队 - Han Chaobing 介绍 vivo 数据库备份恢复功能的演化,以及对备份文件的功能扩展. 一.概述 vivo互联网领域拥有的数据库组件分别为 MySQL.M ...
- Python脚本猜解网站登录密码(带token验证)
目录: 关键代码解释 设置请求头 get_token函数获取token值 完整代码: 运行结果: 上一篇文章:一个简单的Python暴力破解网站登录密码脚本 测试靶机为Pikachu漏洞练习平台暴力破 ...
- DVWA Cross Site Scripting (XSS) 跨站脚本攻击
文章目录 DVWA_XSS(Stored) 存储性XSS 1.Low 2.Medium 3.High 4.Impossible XSS平台 DVWA_XSS(Stored) 存储性XSS 一句话概括: ...
- .NET周刊 【12月第3期 2023-12-24】
国内文章 CAP 8.0 版本发布通告 - CAP 7岁生日快乐! https://www.cnblogs.com/savorboard/p/cap-8-0.html 今天宣布CAP 8.0版本正式发 ...
- 从VMWare安装到Nginx配置
1.安装VMWare Workstation 16 player,Mac也可使用Parallels Desktop,自己有虚拟主机的跳过: 2.在虚拟机中,安装Centos7操作系统,使用Minima ...
- Dio网络请求
包 dio: ^4.0.0 http: ^0.13.3 dio_cookie_manager: ^2.0.0 cookie_jar: ^3.0.1 dio_http2_adapter: ^2.0.0 ...