薅 AWS 羊毛的船新方式,以 ChatBot 为例
还在担心一年免费服务器到期后该怎么办?(Solo社区 投稿)
网上绝大多数薅 AWS 羊毛的教程都是在教大家如何申请创建一年免费的 VPS,太 OUT 了!就问一个问题,一年到期了那咋办?
其实,除了一年免费的 VPS 外,AWS 足足有 40 多个永久免费的服务,其中就包括的 AWS 最为出名的 Lambda,以及日常开发常用的 DynamoDB(NoSQL 数据库)、SNS(发布订阅)。而这么多的服务挨个读文档、装 SDK 太麻烦了,开发个 App 得写一堆的函数,体验实在是,emmmmm...
这篇文章就教大家如何用最简单的方式把这些能力都用上,过程中不需要安装任何软件,一切的一切只需要创建一个 AWS 账号就可以了,创建 AWS 账号需要一张外币卡,VISA、Master 或者运通都 OK。
这篇文章会以一个“在命令行终端运行的聊天机器”为例,给大家展示如何优雅薅羊毛,这个聊天机器人支持多会话、会话自动保存与恢复,5 分钟拥有自己的 ChatBot 不是梦。效果如下️:
️注意:这只是一个示例,方法通用的,你可以使用这个方法去创建更多不同种类的服务,目前这个方法支持使用 ApiGateway、消息队列、定时器等多种服务。
环境准备
不需要 VS Code,不需要 vim,只需要打开这个网址 「Plutolang - CodeSandbox」,然后点右上角的 Fork 就能创建一个你自己的工程环境了。
如果你不想输入代码,也可以直接 Fork 这个环境,这个环境已经准备好代码,只需要配置 AWS 凭证 和 OpenAI Key 就好。
修改代码
首先需要添加一个 OpenAI 的依赖,打开 package.json
在 dependencies
添加一行,
"openai": "^4.13.0"
添加完后记得 Command/Ctrl-S
保存,然后在点击下方控制台中 终端 的小图标,执行 Install 任务,这会自动下载 NPM 依赖。
接下来就是编写业务代码了。打开 src/index.ts
文件,接下来,我们会先定义用于保存会话的 KV 数据库,然后编写新建会话,和聊天的 HTTP 路由。
1、 导入 ChatBot 依赖的库。
import OpenAI from "openai";
import { Router, KVStore, HttpRequest, HttpResponse } from "@plutolang/pluto";
2、 定义 KV 数据库 和 路由 资源变量。
其中 KV 数据库用来保存会话,他会以机器人的名字为 Key,将消息历史记录为 Value。 Router 就是 Web 开发中的服务器,与 express
库类似。
const kvstore = new KVStore("kvstore");
const router = new Router("router");
3、 路由:创建新的聊天会话。
给 router 添加一个 /new
路径的处理函数,接受 POST 请求。请求的 query 中会一个 bot 参数,表示机器人的名称,同时请求体(Request Body)里会有这个机器人的角色定位,比如“一个高级前端工程师”之类的。随后会在 KV 数据库中创建一个键值对,来保存这个新的会话。
router.post("/new", async (req: HttpRequest): Promise<HttpResponse> => {
const bot = req.query["bot"];
if (!bot) {
return {
statusCode: 400,
body: "Missing bot parameter. Please provide a name and its initialization system message for your bot in order to define the assistant's behavior effectively.",
};
}
const sysMsg = req.body;
const messages = [{ role: "system", content: sysMsg }];
await kvstore.set(bot, JSON.stringify(messages));
return {
statusCode: 200,
body: "Now you can enjoy your chatbot.",
};
});
4、 路由:进行会话聊天。
因为这个聊天机器人是基于 OpenAI 的 API 实现的,因此需要首先申请一个 Open API Key。
这里需要你先注册 OpenAI 账户,然后打开这个网页,点击 Create new secret key
,设置一个你喜欢的名称。随后,会生成一个密钥,一定要保存这个密钥!你点了 Done
之后,就再看不到这个密钥了。
然后把 OPENAI_API_KEY
替换成你申请到的 OpenAI 的 API Key,如果你是 OpenAI 的付费用户,你也可以把 MODEL
替换成 gpt-4
。
router.post("/chat", async (req: HttpRequest): Promise<HttpResponse> => {
// Replace the placeholder with your OpenAI API Key and DO NOT publish it publicly.
const OPENAI_API_KEY = "sk-Acj6oPEXKUctapxWxxxxxxxxxxxxxxxx";
// Replace your desired model. You can find all available models here: https://platform.openai.com/docs/models
const MODEL = "gpt-3.5-turbo";
const bot = req.query["bot"] ?? "default";
const newMsg = req.body;
console.debug("Received a user message, Bot:", bot, ", Message:", newMsg);
const record = await kvstore.get(bot).catch(() => undefined);
const messages = record ? JSON.parse(record) : [];
messages.push({ role: "user", content: newMsg });
const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
const chatCompletion = await openai.chat.completions.create({
messages: messages,
model: MODEL,
});
// Check if the response is valid.
const choices = chatCompletion.choices;
if (choices.length == 0 || choices[0].message.content == null) {
console.error("OpenAI Response: ", chatCompletion);
return {
statusCode: 500,
body: "Something went wrong. OpenAI did not respond with a valid message. Please try again later.",
};
}
const respMsg = choices[0].message;
// To maintain the continuity of the conversation, store the response message in the database.
messages.push(respMsg);
await kvstore.set(bot, JSON.stringify(messages));
return {
statusCode: 200,
body: respMsg.content!,
};
});
快速部署
配置 AWS 凭证
进入下方的控制台的 Configure AWS Certificate
标签页,会提示你输入 AWS 凭证信息,这个凭证信息就是 AWS 的 Access Key
和 Secret Access Key
,如果你不知道怎么创建凭证,可以根据这篇文档操作。
拿到凭证信息后,按照提示信息输入就好,output format
不需要填写,最后按下回车后,标签页会显示一个小对号️。
一键发布
点击下方控制台中 终端 的小图标,执行 Deploy 任务,等待一两分钟,直到输出一条 URL
这时,支持多会话、会话自动保存与恢复的聊天机器人已经部署完成了,接下来就是和聊天机器人对话了。
与机器人对话
这里,我提供了一个命令行工具 chat
来与刚部署的 Chat Bot 服务端交互进行聊天,你在项目根目录创建一个 chat
文件或者在你本地的任何位置,将下面 ️ 的内容复制进去就能使用。
#!/bin/bash
# set -o xtrace
read -p "Input the URL that Pluto has outputted: " URL
if [ -z $URL ]; then
echo "Please set the BOT_URL env var first";
exit 1;
fi
echo "Choose mode:"
echo ' 1) create a new bot;'
echo ' 2) select a existed bot;'
read -p "> " mode
if [[ -z $mode || ( $mode -ne 1 && $mode -ne 2 ) ]]; then
echo "Invalid mode";
exit 1;
fi
read -p "Give a name for your bot: " bot_name
if [ -z $bot_name ]; then
echo "Invalid name";
exit 1;
fi
if [[ $mode -eq 1 ]]; then
echo -e "\nHello, I'm $bot_name. "
echo "What role would you like me to fulfill? Please provide a detailed description of the skills you expect me to possess."
echo "For example, a TypeScript expert who familiar with the principle of compilation."
read -p "> " system_message
echo -e "Got it. Creating..."
if [[ -n $system_message ]]; then
curl -s -X POST $URL/new?bot="$bot_name" -d "$system_message" -H 'Content-type: text/plain' > /dev/null
fi
fi
echo -e "\nNow you can enjoy your chatbot."
user_message=""
while :
do
echo "Press 'q' to quit."
read -p "> " user_message
if [[ $user_message == "q" ]]; then
echo "Bye. "
break;
fi
curl -X POST $URL/chat?bot="$bot_name" -d "$user_message" -H 'Content-type: text/plain'
echo -e "\n"
done
在 Web 网站上的使用方法是:点击下方控制台中 终端 的小图标,选择 New Terminal
,然后执行下面这条命令就会进入对话界面,首先会提示你输入刚才部署得到的 URL,随后继续交互就能完成对话。
bash ./chat
现在就能看到开篇截图的效果:
后记
这种方式就是利用 Plutolang 的能力来降低 AWS 复杂服务的上手难度,想要开发其他的应用完全是 OK 的。如果对 Plutolang 感兴趣,欢迎一起共建,另外另外,点个 Star 呗。
Refs
完整代码:
import OpenAI from "openai";
import { Router, KVStore, HttpRequest, HttpResponse } from "@plutolang/pluto";
const kvstore = new KVStore("kvstore");
const router = new Router("router");
router.post("/chat", async (req: HttpRequest): Promise<HttpResponse> => {
// Replace the placeholder with your OpenAI API Key and DO NOT publish it publicly.
const OPENAI_API_KEY = "sk-Acj6oPEXKUctapxWxxxxxxxxxxxxxxxx";
// Replace your desired model. You can find all available models here: https://platform.openai.com/docs/models
const MODEL = "gpt-3.5-turbo";
const bot = req.query["bot"] ?? "default";
const newMsg = req.body;
console.debug("Received a user message, Bot:", bot, ", Message:", newMsg);
const record = await kvstore.get(bot).catch(() => undefined);
const messages = record ? JSON.parse(record) : [];
messages.push({ role: "user", content: newMsg });
const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
const chatCompletion = await openai.chat.completions.create({
messages: messages,
model: MODEL,
});
// Check if the response is valid.
const choices = chatCompletion.choices;
if (choices.length == 0 || choices[0].message.content == null) {
console.error("OpenAI Response: ", chatCompletion);
return {
statusCode: 500,
body: "Something went wrong. OpenAI did not respond with a valid message. Please try again later.",
};
}
const respMsg = choices[0].message;
// To maintain the continuity of the conversation, store the response message in the database.
messages.push(respMsg);
await kvstore.set(bot, JSON.stringify(messages));
return {
statusCode: 200,
body: respMsg.content!,
};
});
router.post("/new", async (req: HttpRequest): Promise<HttpResponse> => {
const bot = req.query["bot"];
if (!bot) {
return {
statusCode: 400,
body: "Missing bot parameter. Please provide a name and its initialization system message for your bot in order to define the assistant's behavior effectively.",
};
}
const sysMsg = req.body;
const messages = [{ role: "system", content: sysMsg }];
await kvstore.set(bot, JSON.stringify(messages));
return {
statusCode: 200,
body: "Now you can enjoy your chatbot.",
};
});
订阅
这个专栏会同步更新在 Solo 社区、公众号、知乎、社群。
微信搜索"Solo 独立开发者社区"或者扫描二维码,即可手机订阅。
社区网址:Solo 独立开发者社区-链接每一位独立开发者, 从 Solo 开始
薅 AWS 羊毛的船新方式,以 ChatBot 为例的更多相关文章
- 通过xib加载UITableViewCell的新方式
我们以前通常会这样做 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPa ...
- Visual Studio 2012 应用软件开发新方式
微软正式发布Visual Studio 2012 应用软件开发新方式 2012-09-13 09:54 51CTO.com 我要评论(0) 字号:T | T “现在,开发者将有更好的机会开发与云服务连 ...
- 异步编程新方式async/await
一.前言 实际上对async/await并不是很陌生,早在阮大大的ES6教程里面就接触到了,但是一直处于理解并不熟练使用的状态,于是决定重新学习并且总结一下,写了这篇博文.如果文中有错误的地方还请各位 ...
- JDK8-日期时间新方式
日期时间新方式 在日常开发中,对于日期操作是非常常见的,但是对于有经验的开发人员来说Java8之前的日期操作是有较大问题 的.比方说SimpleDateFormat.但是在Java8之后提出了Da ...
- SNF快速开发平台MVC-各种级联绑定方式,演示样例程序(包含表单和表格控件)
做了这么多项目,经常会使用到级联.联动的情况. 如:省.市.县.区.一级分类.二级分类.三级分类.仓库.货位. 方式:有表单需要做级联的,还是表格行上需要做级联操作的. 实现:实现方法也有很多种方式. ...
- CSS 加载新方式
Chrome 浏览器有意改变<link rel="stylesheet">的加载方式,当其出现在<body>中时,这一变化将更加明显.笔者决定在本文中进行详 ...
- debuggap,移动端调试新方式
最近发现了一个移动端调试的新技能,这里简单描述一下基本情况. 移动端调试常遇到的问题 手机访问只能看到页面的展现,除此之外看不到任何其他信息 无法像调试PC页面那么方便的查看js.dom.networ ...
- Docker 集群环境实现的新方式
近几年来,Docker 作为一个开源的应用容器引擎,深受广大开发者的欢迎.随着 Docker 生态圈的不断建设,应用领域越来越广.云计算,大数据,移动技术的快速发展,加之企业业务需求的不断变化,紧随技 ...
- (转载)ECCV 2018:IBN-Net:打开域适应的新方式
(本文转自极视角) 本文由香港中文大学发表于ECCV2018,论文探索了IN和BN的优劣,据此提出的IBN-Net在语义分割的域适应任务上取得了十分显著的性能提升. 论文地址:https://arxi ...
- 介绍一个船新的 PHP SDK + Runtime: PeachPie
前言 这几天想基于 .NET Core 搞一个自己的博客网站,于是在网上搜刮各种博客引擎,找到了这些候选:Blogifier.Miniblog 以及 edi 写的 Moonglate. Blogifi ...
随机推荐
- C数据结构:KMP算法详解(呕心沥血)
KMP算法 作者心声 了解暴力求解(必需会) KMP算法详解 记住我这段话(你会爱上它的)← : ①前后缀及其用处 ②求出前后缀的next数组 求出next数组的代码 开始实现KMP算法 结尾 附上源 ...
- C 语言编程 — 运算符
目录 文章目录 目录 前文列表 运算符 算数运算符 自增.自减运算符 比较运算符 逻辑运算符 位运算符 赋值运算符 逗号运算符 sizeof 运算符 杂项运算符 运算符的优先级 前文列表 <程序 ...
- ansible使用详解
ansible执行,用户主机配置 免密同一个同一个用户执行命令 1 能免密登录的[root@mcw1 ~]$ ansible 10.0.0.132 -m shell -a "hostname ...
- docker安装MySQL8.0.35主从复制(实战保姆级)
很久没有记录了,今天有时间就记录一下最近安装遇到的问题 liunx安装docker这个是前提,就不多过述 1 准备两台服务器 10.104.13.139 10.104.13.140 2 确保liunx ...
- 手写Word2vec算法实现
1. 语料下载:https://dumps.wikimedia.org/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2 [中文维基百科语料] 2. ...
- WPF使用事件聚合器,实现任意页面跨页通信
前言:最近几天有好几个小伙伴玩WPF,遇到不同页面,不知道要怎么传递消息.于是,我今天就来演示一个事件聚合器的玩法,采用prism框架来实现.作为福利,内容附带了主页面打开对话框时候直接通过参数传递消 ...
- 鸿蒙HarmonyOS实战-Web组件(请求响应和页面调试)
前言 请求响应是指客户端发送请求给服务器,服务器接收到请求后返回的响应.响应包含了服务器处理请求的结果,并将结果返回给客户端. 页面调试是指在开发过程中,通过调试工具分析页面的运行状况,查找问题和修复 ...
- 8.21考试总结(NOIP模拟45)[打表·蛇·购物·ants]
有型的东西终究会消逝,不过--终于,这份回忆还是永远不朽的- 前言 这次考试暴露出来了不少问题. 比如答题策略策略不当导致 T2 的 65pts 暴力根本没有打. 知识遗忘太快不牢固,T4 是之前的一 ...
- C# asp.net mvc 创建虚拟目录
使用背景: 虚拟目录(virtual directory),计算机术语,每个 Internet服务可以从多个目录中发布.通过以通用命名约定 (UNC) 名.用户名及用于访问权限的密码指定目录,可将每个 ...
- 微信刷脸SDK获取sub_openid
当调用SDK中 获取用户信息(getWxpayfaceUserInfo) /人脸支付凭证(getWxpayfaceCode) 方法获取 sub_openid 时,除了SDK自身要传入sub_appid ...