V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Recommended Services
Amazon Web Services
LeanCloud
New Relic
ClearDB
Jianzs
V2EX  ›  云计算

分享一种更简单的薅 AWS 羊毛姿势

  •  
  •   Jianzs · 2023-10-31 10:44:42 +08:00 · 4318 次点击
    这是一个创建于 384 天前的主题,其中的信息可能已经有所发展或是发生改变。

    网上绝大多数薅 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.jsondependencies 添加一行,

    "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 KeySecret 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 的。对项目感兴趣可以了解了解,蛮有意思。

    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.",
      };
    });
    
    第 1 条附言  ·  2023-10-31 12:54:36 +08:00
    感兴趣的大佬,欢迎了解下 Plutolang 这个项目,给个 star 🌟,一起共建就更好啦

    https://github.com/pluto-lang/pluto

    项目还处于初期阶段,有任何想法、需求都可以探讨呀,大家讨论讨论,或许你的想法就能在后面逐步实现
    第 2 条附言  ·  2023-10-31 20:13:29 +08:00
    收藏的 V 友在使用过程中遇到问题可以随时联系我
    19 条回复    2023-11-01 13:57:16 +08:00
    coinxu
        1
    coinxu  
       2023-10-31 11:42:46 +08:00   ❤️ 1
    虽然是软文,看你这么诚恳,支持一下,不错
    zsj1029
        2
    zsj1029  
       2023-10-31 11:58:41 +08:00 via iPhone   ❤️ 1
    挺好,降低上手云开发的难度,关注业务,忽略运维细节,支持
    apiman
        3
    apiman  
       2023-10-31 12:12:57 +08:00   ❤️ 1
    薅羊毛的有几个是用来做开发的
    qweruiop
        4
    qweruiop  
       2023-10-31 12:13:07 +08:00   ❤️ 1
    太软了,实际上 aws lambda 太贵了,不太适合用。现在都用 vercel 或者 cloudflare worker 了。
    gps949
        5
    gps949  
       2023-10-31 12:18:01 +08:00
    @apiman
    龟壳的 ARM 机器油 24G 内存,用来 vscode 远程开发真的还挺不错的。相比起来我远程回家 AIO 上的虚机或者其他地方买的 VPS (大多数是 8G ),跑两下内存就满了
    Jianzs
        6
    Jianzs  
    OP
       2023-10-31 12:43:50 +08:00
    @coinxu 感谢感谢,虽然是软文,但是也是真的想给大家带来一点价值,真诚是必杀技,哈哈哈哈
    Jianzs
        7
    Jianzs  
    OP
       2023-10-31 12:45:14 +08:00
    @zsj1029 对!这就是想给大家提供的一点价值,但是总说不好,感觉没有让大家理解的很清楚。大佬感兴趣可以以一起来玩玩呀
    Jianzs
        8
    Jianzs  
    OP
       2023-10-31 12:50:31 +08:00
    @qweruiop AWS 永久免费 每月 100 万请求,一般的项目够用啦,vercel 的免费访问限制好像也差不多是这些吧。

    另外,vercel 主要是前端嘛,写业务逻辑需要在 api 目录 一个子目录一个子目录 的去写每个函数,个人感觉体验差点意思,你觉得呢?
    zsj1029
        9
    zsj1029  
       2023-10-31 12:55:33 +08:00 via iPhone
    @Jianzs laf.run 我最近在用,这种云开发更贴近用户体验,大佬可以试用下,看看有没有可取之处
    Jianzs
        10
    Jianzs  
    OP
       2023-10-31 13:06:52 +08:00
    @zsj1029 #9 巧了,我知道这个项目,你说这种方式更贴近用户体验,我还挺好奇的,你为啥觉得 Laf 更贴近用户体验?从我个人看法来说,我感觉有几点不同,刚好和你探讨一下

    1 ) Laf 这种方式还是独立编写每个函数(比如路由处理函数),项目整体性比较差,如果我要写一个多函数的应用程序,就比较麻烦,思维不连贯,容易被打断

    2 ) Laf 需要用户自行创建数据库等组件,不过好在创建的体验做了优化,不需要什么成本就能够创建,并在编程时使用。而 Pluto 这种完全是在代码里定义一个变量,组件资源就创建好了,整个开发流程都在代码界面。

    3 ) Laf 目前好像存在一定的 运营商锁定 问题,必须使用 K8s 或者它的平台。Pluto 是支持多平台的,目前阶段是在 K8s 和 AWS 上做了验证,一点代码都不需要修改,就能直接迁移平台。这篇文章的例子没有体现,感兴趣欢迎看看 README 中的 例子,这是例子的视频,2 分钟,https://www.bilibili.com/video/BV1HB4y1d7cq/

    所以,相较于 Laf ,Pluto 更想做到的是,用户只在代码界面就能完成所有操作,并且还都是以往常的编程方式完成的,类似简简单单定义一个变量。顺带解决运行商锁定问题。

    这是我个人角度的想法,你能简单说说为啥你觉得 Laf 更贴近用户体验么?应该是他的开箱即用?
    Jianzs
        11
    Jianzs  
    OP
       2023-10-31 13:07:50 +08:00
    @zsj1029 #9 另外,zsj 是你名字的缩写吗?如果是的话,那咱俩一样,哈哈哈哈
    zsj1029
        12
    zsj1029  
       2023-10-31 13:23:44 +08:00
    @Jianzs 是的,开箱即用,web ide ,甚至不需要本地开发环境,配套的对象存储,文档也很完善
    保存即发布,对于小项目很合适的,没错是的 zsj 缩写
    腾讯云开发也有个类似的,集成组件开发模板,不过两年没更新了
    总之你的这个 pluto 还是需要点门槛研究的,考虑整合 cloudflare worker ,这个我也挺看好的,多个运营商可以选择
    zsj1029
        13
    zsj1029  
       2023-10-31 13:28:11 +08:00
    @Jianzs 你这个 pluto 真的挺好的,回头有空研究下,最近对比了不少集成后端开发环境,比如 appwrite ,很感兴趣
    Jianzs
        14
    Jianzs  
    OP
       2023-10-31 13:35:36 +08:00
    @zsj1029 好呀好呀,感谢反馈建议。我也尝试对比了与 BaaS 、PaaS 之类产品的不同,大佬可以看看,后续继续交流呀,感兴趣一起玩玩,加个绿色软件? c2hvdWppYW5fNDI2

    https://github.com/pluto-lang/pluto/blob/main/docs/zh-CN/whats-different.md
    zsj1029
        15
    zsj1029  
       2023-10-31 13:38:58 +08:00
    @Jianzs 我已经加入了你的 slack ,那边沟通吧
    cmhonker
        16
    cmhonker  
       2023-10-31 19:03:39 +08:00
    AWS ,吃码三折,EC2/Lightsail

    my.cloudcpp.com
    Jianzs
        17
    Jianzs  
    OP
       2023-10-31 22:43:53 +08:00
    @cmhonker 哈哈哈哈,这也来蹭?问一下,国内用 AWS 的多么?
    JIeJaitt
        18
    JIeJaitt  
       2023-11-01 13:25:18 +08:00 via Android
    aws 有免费的 mysql 数据库?
    Jianzs
        19
    Jianzs  
    OP
       2023-11-01 13:57:16 +08:00
    @JIeJaitt 免费 12 个月,没有永久免费,AWS RDS ,支持 MySQL 引擎

    https://aws.amazon.com/cn/free/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1003 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 21:35 · PVG 05:35 · LAX 13:35 · JFK 16:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.