nodejs搭建chatgpt服务
5分钟快速搭建基于nodejs的chatgpt服务
写在前面
首先大家都知道,在国内是无法成功调用openapi的接口,甚至openai的官网都很难打开。所以如果想使用chatgpt的几乎是不可能,不过咱们可以抱着了解的心态去体验一下。
*阅读本文档您可以了解以下内容*
- eggjs框架
- egg-mysql的使用
- openai的实例创建聊天和图片
准备工作
- apiKey: 这是调用chatgpt的密钥。目前有两种获取方式:
- 通过openai的官网去申请,不过这需要一个国外的手机号。
- 去某宝直接购买,大概1块钱。
- 国外服务器: chatgpt必须用国外的服务器访问。可以:去各大厂购买云服务
就绪
- 准备环境,本文默认使用的centos7.6
- 开发环境,nodejs >=16,目前centos只能安装node16及以下版本,所以大家安装时需要注意,不要直接安装最新的nodejs。为了方便切换,可以安装nvm来管理node版本,
nvm install nodejs版本,nvm use nodejs版本 - 安装eggjs,和所需插件:
npm init egg --type=simplenpm installnpm install egg-mysqlnpm install openainpm install egg-validate
说明: mysql是为了保存聊天上下文,当然你也可以保存在前端本地,validate是校验参数插件
数据库请自行创建,数据表的创建后面会写在代码中
修改配置文件
- 在config/config.default.js中添加
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['*']
}
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,POST,DELETE,PATCH'
}
config.mysql = {
client: {
host: 'localhost',
port: '3306',
user: 'root',
password: 'Chat9527', // 数据库密码
database: 'chatDB', // 数据库名称
},
app: true,
agent: false,
}
config.validate = {
convert: true,
widelyUndefined: true,
async formatter(ctx, error) {
ctx.status = 400;
ctx.body = {
code: 'INVALID_PARAM',
errors: error.errors,
};
},
}
config.constant = {
API_KEY: 'sk-xxxx', // 你的apiKey
};
- 在config/plugin.js中添加
cors: {
enable: true,
package: 'egg-cors'
},
validate: {
enable: true,
package: 'egg-validate'
},
mysql: {
enable: true,
package: 'egg-mysql',
}
api接口
分析目前只有两个接口,一个是openai的聊天,另一个是生成图片
具体代码如下:
module.exports = app => {
const { router, controller } = app;
app.beforeStart(async () => {
const ctx = app.createAnonymousContext();
await ctx.app.mysql.query(`
CREATE TABLE IF NOT EXISTS user_contexts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
memory INT(11) DEFAULT NULL,
affinity INT(11) DEFAULT NULL,
context TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
});
router.post('/creatChat', controller.chat.createChat);
router.post('/creatImg', controller.chat.creatImg);
};
user_id 表示访问服务的用户;context 为聊天的记录;memory和affinity为保留字段可以不要。
openai使用
- nodejs中使用
new openai({ opentions })来创建实例; - 使用
实例.chat.completions.create({ messages, model: "gpt-3.5-turbo", });来创建一个聊天
具体代码如下:app/controller/chat.js
const { Controller } = require('egg');
const OpenAI = require('openai');
class ChatController extends Controller {
// 创建图片
async creatImg() {
const { ctx } = this;
const { prompt, n=1, size="1024x1024" } = ctx.request.body;
const openai = new OpenAI({
apiKey: this.app.config.constant.API_KEY,
});
const response = await openai.images.generate({ prompt, n, size });
ctx.body = {
data: response.data
};
}
// 创建聊天
async createChat() {
const { ctx } = this;
const { request, service } = ctx;
const { prompt, userId } = request.body;
try {
ctx.validate(this.indexRule, request.body);
const openai = new OpenAI({
apiKey: this.app.config.constant.API_KEY,
});
const userContext = await service.chat.getUserContext(userId);
const messages = this.buildMessageList(userContext, prompt);
const response = await openai.chat.completions.create({
messages,
model: "gpt-3.5-turbo",
});
const assistantResponse = response.choices[0].message.content.trim();
const updatedUserContext = this.updateUserContext(userContext, prompt, assistantResponse);
service.chat.updateUserContext(userId, updatedUserContext);
const shouldEndConversation = this.isConversationEnding(assistantResponse);
if (shouldEndConversation) {
service.chat.deleteUserContext(userId);
ctx.body = { code: 0, message: "拜拜!" };
} else {
ctx.body = { code: 0, message: assistantResponse };
}
} catch (err) {
ctx.status = 442;
ctx.body = {
code: -1,
message: '系统错误',
error: err.errors,
}
}
}
// 构建消息列表
buildMessageList = (userContext, prompt) => {
return [
...userContext,
{ role: "system", content: "You are a helpful assistant that answers questions." },
{ role: "user", content: prompt }
];
}
// 更新用户上下文
updateUserContext = (userContext, prompt, assistantResponse) => {
return [
...userContext,
{ role: "user", content: prompt },
{ role: "assistant", content: assistantResponse }
];
}
// 判断是否结束聊天
isConversationEnding(response) {
const keywords = ["再见", "退出", "滚", "拜拜"];
return keywords.some(keyword => response.includes(keyword));
}
// 定义参数校验规则
get indexRule() {
return {
userId: {
type: 'string',
required: true,
format: /^[A-Za-z0-9]+$/,
message: 'userId不能为空,且只能包含字母和数字',
},
prompt: {
type: 'string',
required: true,
max: 300,
message: 'content不能为空,且长度不能超过300',
},
};
}
}
module.exports = ChatController;
- app/service/chat.js里是数据的存储逻辑,如下:
const { Service } = require('egg');
class ChatService extends Service {
// 获取用户上下文
async getUserContext(userId) {
const result = await this.app.mysql.select('user_contexts', {
where: { user_id: userId },
orders: [['created_at', 'desc']],
limit: 1,
});
return result[0] ? JSON.parse(result[0].context) : [];
}
// 更新用户上下文
async updateUserContext(userId, context) {
const existingData = await this.app.mysql.get('user_contexts', { user_id: userId });
const record = {
user_id: userId,
context: JSON.stringify(context),
created_at: new Date(),
};
if (existingData) {
await this.app.mysql.update('user_contexts', record, {
where: { user_id: userId },
});
} else {
await this.app.mysql.insert('user_contexts', record);
}
}
// 删除用户上下文
async deleteUserContext(userId) {
await this.app.mysql.delete('user_contexts', {
user_id: userId
});
}
}
module.exports = ChatService;
一切完成就绪
- 使用
npm run dev来运行服务器 - 用Postman 访问
ip:7001/creatChat,参数userId: '1',prompt: '你好' - 使用
npm start来后台运行服务 - 使用
npm stop来停止服务
花费了不止5分钟?
- 直接克隆项目
git clone https://github.com/812781385/chatgptService.git npm inpm run dev
nodejs搭建chatgpt服务的更多相关文章
- nodejs搭建web服务教程
nodejs搭建web服务教程 先安装nodejs 然后 命令node js文件 即可开启服务了 输出如下 Magic happens on port 9011 你查看端口可以看到tcp 0 0 :: ...
- nodejs搭建简单的websocket服务端
创建websocket服务端使用了nodejs-websocket ,首先要安装nodejs-websocket,在项目的目录下: npm install nodejs-websocket 1.搭建w ...
- mock---前端搭建模拟服务
在做前端开发接口的时候,往往接口都是从后端来的,这让前端很苦恼,但是有了 MockServer ,前端也可以搭建API服务了. server-mock是什么? 是一款nodejs的应用,用于搭建web ...
- 用nodejs搭建类似于C++的服务器后台.类似网易pomelo
实际的情况,用nodejs跑业务,非常的快,只要用好其无阻塞和回调这两点,处理速度真的是杠杠的. 从年初开始,我用nodejs搭建了类似C++的服务器后台,也想和做同样的事情的朋友分享,本服务平台因为 ...
- 使用 Node.js 搭建微服务网关
目录 Node.js 是什么 安装 node.js Node.js 入门 Node.js 应用场景 npm 镜像 使用 Node.js 搭建微服务网关 什么是微服务架构 使用 Node.js 实现反向 ...
- nodejs搭建web服务器初级
nodejs搭建简单的web服务器 1.1简介 Node.js是基于Chrome JavaScript运行时建立的一个平台,实际上它是对Google Chrome V8引擎进行了封装,它主要用于创建快 ...
- 用nodejs搭建代理服务器
题图 From 极客时间 From Clm 前端开发者在工作中常常遇到跨域的问题,一般我们遇到跨域问题主要使用以下办法来解决: 1.jsonp 2.cors 3.配置代理服务器. jsonp不是很灵活 ...
- 利用 nodeJS 搭建一个简单的Web服务器(转)
下面的代码演示如何利用 nodeJS 搭建一个简单的Web服务器: 1. 文件 WebServer.js: //-------------------------------------------- ...
- 使用node+puppeteer+express搭建截图服务
使用node+puppeteer+express搭建截图服务 转载请注明出处https://www.cnblogs.com/funnyzpc/p/14222807.html 写在之前 一开始我们的需求 ...
- 如何搭建DNS服务
继NTP时间服务器后,继续搭建DNS服务,鉴于昨晚撰写时间超过预期,这次改变策略,先把自己需要用到的部分写出来(主要是基于RAC的搭建,只涉及正向和反向DNS解析),后面再添加必要的说明和阐述. 试验 ...
随机推荐
- 配置腾讯云轻量级linux服务器用到的资源和步骤
pasv_address=82.157.112.34 #请修改为您的 Linux 云服务器公网 IPsftp://82.157.112.34:21 ①下载系统可视化https://cloud.tenc ...
- 11.硬核的volatile考点分析
大家好,我是王有志.关注王有志,一起聊技术,聊游戏,聊在外漂泊的生活. 今天我们学习并发编程中另一个重要的关键字volatile,虽然面试中它的占比低于synchronized,但依旧是不可忽略的内容 ...
- Refresh 重构(Refactor)
最近在闲暇之余重(第)温(一..次)此书, 首先能感受到的, 无论你是新程序员还是老程序员, 这本书都已经不具备太多的可读性了. 由于本书成书年代久远, 那个时候软件行业还不够发达, 面向对象还没有被 ...
- #分块,懒标记#LOJ 3631「2021 集训队互测」学姐买瓜
题目传送门 分析 有一个很简单的做法就是处理出每个位置能够一次到达的最左边的右端点(后继). 然后直接从 \(l\) 开始能跳就跳,这样单次询问时间复杂度是 \(O(n)\) 的. 观察到时间复杂度因 ...
- #构造,黑白染色#AT4378 [AGC027D] Modulo Matrix
题目 构造一个 \(n*n(n\leq 500)\) 的矩阵,满足元素均为正整数,不超过 \(10^15\) 且互不相同, 并且相邻两数若较大的为 \(x\),较小的为 \(y\),那么任意相邻两数 ...
- .NET Emit 入门教程:第六部分:IL 指令:5:详解 ILGenerator 指令方法:创建实例指令
前言: 上上篇介绍了 IL 指令的分类以及参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令. 上一篇介绍参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变 ...
- Linux系统奇安信浏览器报错跨域:the resource is in more-private address space 'local'
报错: Access to XMLHttpRequest at "123" from origin "456" has been blocked by CORS ...
- openGauss Cluster Manager RTO Test
一.环境介绍 软件环境 类别 版本 下载链接 备注 OS openEuler 20.03 (LTS) https://repo.openeuler.org/openEuler-20.03-LTS/IS ...
- HarmonyOS线性容器特性及使用场景
线性容器实现能按顺序访问的数据结构,其底层主要通过数组实现,包括ArrayList.Vector.List.LinkedList.Deque.Queue.Stack七种. 线性容器,充分考虑了数据 ...
- 动态规划(六)——树形dp
树形dp,又称树状dp,即在树上进行的dp,在设计动态规划算法时,一般就以节点从深到浅(子树从小到大)的顺序作为dp的"阶段",dp的状态表示中,第一维通常是节点编号(代表以该节点 ...