作者: meathill

  • 【视频】Nuxt3+Vercel+Serverless 全栈开发(3):实现投票效果,使用 Redis

    【视频】Nuxt3+Vercel+Serverless 全栈开发(3):实现投票效果,使用 Redis

    课程继续。仍然结合近期的开发经验,分享最近我比较喜欢的全栈+高效+免费+云原生技术方案。

    本次课程内容:

    • CSS 里使用 ~ 兄弟选择器实现投票效果
    • 使用 Upstash Redis
    • 使用 Nuxt3 风格的加载方法读取数据

    打分组件,我们需要用到 ~ 选择器选择某个节点后面的所有弟弟节点,配合蒙版属性 mask-* 可以实现我们要的打分效果。

    接下来我们开始接触远程数据的操作。Vercel 免费版 Redis 每天只有 1k 的请求数,不太够用;如果升级付费版,也没法针对某个部分单独升级。所以很明显,Upstash 这样专门的服务商更合适。

    Nuxt3 为实现 SSR,对远程请求进行了封装,提供 useFetchuseLazyFetchuseAsyncDatauseLazyAsyncData 这 4 个新方法。其中前两者可以视作后两者的封装。在本节课里,我们先实现了读取数据的 API,然后用 useFetch 加载数据,实现服务器端渲染。

    这期视频也剪得很细,还做了字幕,希望大家能学到东西。

    如果你有任何问题、建议,欢迎留言讨论。请 b 站有号的同学帮忙分享完播一键三连,谢谢大家。

    另外,我也在 YouTube 上上传了一份,大家有空的话,麻烦帮忙关注下我的油管频道,感谢感谢。肉山全栈小课堂 – YouTube

  • 聊聊 Serverless 与 Edge Function

    聊聊 Serverless 与 Edge Function

    熟悉我的同学可能记得,前几年我经常推荐 Serverless,而且我的博客里现在还挂着 LeanCloud 的推荐链接。我觉得 Serverless 对前端、全栈开发者来说简直是个福音,很多原本很难的开发工作,有了 Serverless 之后,都能很容易的由前端处理,大大扩展了前端的能力覆盖范围。

    时至今日,我们不仅有 Serverless,还有 Edge Function,后者几乎没有冷启动的问题,更能提供 SSR 功能,好上加好。很多数据存储服务也针对性地提供好用的接口。今天的开发环境对前端来说,简直无与伦比。所以我想聊聊这个话题,希望每个前端都能在 Serverless/Edge Function 得帮助下成长为全栈开发。

    Serverless 是什么?

    我就不去抄准确定义了,按照我的体验来分享:

    1. 不需要维护独立的服务器
    2. 分为两个部分:
      1. 基于行级权限控制的数据库
      2. 可以自动扩容、降容的云函数执行器
    3. 云数据库,配合行级权限,配合合理的数据逻辑,可以在前端完成大部分数据处理的工作
    4. 云函数可以自动扩容、降容,可以休眠、启动,可以完成云数据库无法放在前端的操作
    5. 云函数以函数为一个执行单位,实现更精准的资源控制

    总之,前端有了 Serverless,就可以在没有后端、没有运维的配合下,独立完成完整应用的开发,并且具备相当强的扩展能力,费用也很低。

    其它应用场景

    除此之外,Serverless 还可以用在各种有突发计算高峰的场景里,比如音视频处理。我们可以利用 Serverless 在没人使用时自动休眠(不计费),有人访问时自动启动的特性,有需求的时候才启动,完成计算后自动关机,既节省成本,又提供很高的理论并发。

    Serverless 的问题

    早期的 Serverless 调度系统有些问题。出于学习成本的考虑,早期 Serverless 多数是基于容器技术,虽然可以完整支持大部分编程语言及其 API,但是冷启动一般都需要不少时间,少则十几秒,多则几十秒。

    这么久的启动时间,对于计算密集型领域来说,问题并不严重。因为在这些场景里,几十秒的启动时间占比不高,通常来说用户需要等待的时间远比它久,自然有队列和轮询放在前面。但是对于一般的网站应用,服务普通客户,如果启动一次要几十秒,那就很难接受了。

    比如最近大热的 ChatGPT,有些同行尝试用 Serverless 做转发,就受困于冷启动,经常超时报错。

    一代更比一代强:Edge Function

    我不太确定 Edge Function 是什么时候诞生的,最初我以为 Edge Function 无非就是跑在运算力富裕的边缘节点上,本质上跟 Serverless 没什么区别,直到前些日子开始大规模使用 Vercel,深入了解之后,才明白 Edge Function 的优势。

    简单来说,无论是 Vercel、Supabase、还是 Cloudflare Worker,都抛弃了以前基于 Docker 的做法,或者精简运行时,或者使用 deno 这样更快更小的核心,将冷启动的速度降到毫秒级——基本就是网络传输的延迟级别。

    于是 Edge Function 的可用性就大大提升,可以用在各种速度敏感型的场合,比如现代框架的 SSR、代理服务器、HTML 内容改写,等等。

    目前,我的 Edge Function 主要用在以下几个场景:

    1. Nuxt.js/Next.js 的 SSR
    2. 代理 OpenAI 的 API 访问
    3. 操作 Upstash Redis 存储和修改数据
    4. 响应 Stripe 的付款请求

    Vercel Edge Function 的限制

    我用 Vercel 比较多,这里介绍一下 Vercel Edge Function 的限制。

    首先,Vercel Edge Function 是 node.js 的子集。除了拥有完全一致的语法和基本对象之外,Edge Function 并不能使用全部的系统 API,比如 fs、http/https 等。所以类似 axios 这样知名的 ajax 封装就不能使用;也不能使用 OpenAI 的官方 npm 包。

    Edge Function 只支持 ESM,所以依赖里面的包即使没有用到系统 API,也必须使用 ESM 才能使用。所以我们必须经常性的处理一下公共包。

    虽然支持 TypeScript,但部署的时候,会使用 ESM 编译,所以 import 必须使用相对路径,否则会报错。报错的内容是使用了不支持的格式,非常有误导性。

    其它就是一些系统性要求,比如代码限制、内存限制、请求体限制等,我目前都还没有撞到。大家小心点就是。比较可能遇到的问题是,Edge Function 30s 内必须开始返回内容,这在 OpenAI 开发中很容易遇到问题,开启 stream: true 就好。

    更好的生态环境

    Vercel、CloudFlare 均提供非常完整的网络环境,包括网站、CDN 缓存层等必备服务。

    如果要使用数据库,选项非常丰富,除了 Vercel 自带的各种不解渴的“小样”,我们还可以使用 Upstash Redis,MongoDB,PlantScale MySQL,Supabase PosgreSQL,以及 TiDB Cloud Serverless。

    基本上,应有尽有,几乎所有 Web 开发所需要的存储方案都能找到有免费额度的试用装。开发环境真是盛世空前。

    我们应该怎么做

    现在经济大环境不好,很多同学面临失业、降薪,V2EX 上经常有同学讨论是开网约车好还是送外卖好……这个我也没办法。但是从另一个角度来说,我们的开发环境却是史无前例的好,部署、运维成本非常低,几乎所有必须的服务都有免费试用,每家都提供清晰的文档、易用的 SDK。

    我建议,大家好好利用闲暇时间,利用现在各种生成式模型大爆发,但是市场还没稳定下来的时间节点,多多尝试,即使做不出惊世之作,也能为自己的将来累积足够多的资本,坚持到云开日出那一天。

    广告时间:我的全栈开发课程

    发现忘记打广告了,赶紧补上。

    我目前正在连载一套云原生全栈开发课程,使用 Nuxt3 + Vercel + Serverless 数据库开发。目前发布了三集,本周计划制作第四集(如果有时间就做第五集,嗯嗯),请大家观看,有号的话还请帮我一键三连,多谢。

    B站地址:Nuxt3+Vercel+Serverless 数据库全栈开发

    油管地址:Nuxt3+Vercel+Serverless数据库全栈开发 – YouTube

    总结

    曾经面试金山的时候,有位面试官问我:你这么大年纪,你觉得你比年轻人有什么优势?我答道:因为我年纪足够大,见识过足够多的技术发展,所以我能更快地学会一门技术,也能更准确的判断一门技术是否值得学习。我一直很看好 Serverless,如今有了 Edge Function,serverless 更好用;有了各种服务,serverless 更强大。Serverless 是未来全栈不可或缺的技术。

    希望大家都学会使用。有任何问题、建议、意见,欢迎留言讨论。

  • 【视频】Nuxt3+Vercel+Serverless 全栈开发(2):配置 TailwindCSS,使用 grid 布局

    【视频】Nuxt3+Vercel+Serverless 全栈开发(2):配置 TailwindCSS,使用 grid 布局

    课程继续。仍然结合近期的开发经验,分享最近我比较喜欢的全栈+高效+免费+云原生技术方案。

    本次课程内容:

    • 配置 TailwindCSS
    • 使用 grid 布局构建页面

    Nuxt3 为支持 SSR,架构要复杂很多,所以大部分组件都要专门适配,比如 pinia,就有 @nuxt/pinia;vue-i18n,也有 @nuxt/i18n。同时,大部分组件的配置也都要归拢到一起,所以使用的时候一般要特意找一下,配一下。

    由于界面布局的因素,使用 flex 需要多一个容器,于是我干脆使用了 display:grid,这样代码会更简单。可能大家平时用 grid 不多,所以不妨从这个小项目入手。

    这次视频剪得比较细,字幕也做了,希望大家能学到东西。

    如果你有任何问题、建议,欢迎留言讨论。请 b 站有号的同学帮忙分享完播一键三连,谢谢大家。

    另外,我也在 YouTube 上上传了一份,大家有空的话,麻烦帮忙关注下我的油管频道,感谢感谢。肉山全栈小课堂 – YouTube

  • 如何做在创业小厂里做技术领导

    如何做在创业小厂里做技术领导

    今天有位老板朋友问我:他在创业,融资、推广、运营、内容都没问题,唯独缺少技术研发带头人,应该怎么去找这类人才,或者将来面试招聘的时候应该注重哪些方面?我简单帮他总结了一下,趁着还能记得,写下来分享给大家。

    职责

    一般来说,在创业小厂里做技术领导,要负责的事情如下:

    1. 技术攻关。不要让技术问题阻碍公司发展。
    2. 技术团队管理。保证技术团队能稳定可靠高效的输出技术产品。
    3. 基础设施建设。公司所需要的各种网站、网络服务,等。

    这里面,技术攻关比较难,可能不容易出结果,但反而比较好操作,因为只需要读文档、查文献、向人请教、写代码就能解决。基础设施大部分是体力活,也不会有太大问题。所以我们今天就来重点讨论第二点:技术团队管理

    核心:提升短板

    我的经验,创业小厂的技术领导,大家的首要工作,就是要提升团队的短板。

    通常来说,创业公司的钱包都不会太鼓,一方面很难招到行业里最好的那部分开发者,另一方面岗位设置上往往也是能省则省,需要大家互相填补空白。

    所以作为技术领导,我们必须要通过工具、流程、规范等手段,提升团队短板,确保即使团队能力有限、即使团队岗位有缺失,也能保证产品质量。

    工具

    工具是死的,所以用工具提升短板是最可靠的方案,因为它可以无差别、全天候地守护我们的产品代码。可惜目前工具很难独立完成工作,往往需要流程、规范来配合。希望随着 AI 的发展,工具能越来越好用,越来越能独立生效。

    下面是我推荐的必备工具:

    Git

    版本管理工具,妥善使用可以很好的帮助团队并行开发,提升代码质量。推荐大家看下我之前写的系列文章:

    自动化测试

    测试对产品质量的提升非常明显。人工测试存在一些问题,主要是回归测试又累又无聊,自动化测试可以很好的弥补。不过,自动化测试需要开发团队投入时间维护,往往缺乏群众基础,需要管理者权衡。

    建议在项目初期就建立测试框架,然后随着开发、debug 不断增加测试用例。我最反对的是极端化:不需要追求测试覆盖率;更不要因为测试需要花费时间就彻底拒绝测试自动化测试。在时间、精力允许的前提下,尽量多准备测试用例;bug 修复要带上测试用例;测试用例可以不覆盖所有特殊情况,但完全没有测试用例也很要不得。

    函数、API 的测试工具比较多,大家按需选用;UI 自动化推荐 Cypress。

    代码静态分析

    静态分析可以提升代码质量、减少安全隐患。相比于自动化测试,这方面推进的难度比较低。代表工具是 ESLint,建议大家使用配合 Sonarqube 等安全软件使用。

    故障收集

    收集线上故障有助于我们提前发现问题、解决问题。目前最流行的工具是 Sentry,大家可以考虑使用公共服务,或者自行搭建。我的经验供参考:

    流程

    流程执行的好,对提升短板的效果也是立竿见影。经验告诉我,一般创业小团队较多采用敏捷或类敏捷的开发过程,所以我的流程推荐也基于敏捷开发方法。

    立会

    我会要求团队每天开立会。我不要求每天都有具体的产出,但是有没有都得报告一下。立会的内容很简单,每人几分钟:

    • 总结自己前一天的工作结果
    • 公开自己今天的计划
    • 如果遇到无法解决的问题,寻求帮助
    • 如果需要他人协作,预约时间

    每日立会会给团队带来不小的心理压力,对于远程或者混合远程的公司来说,这个压力是必须的。每日立会对远程公司非常重要。

    需求评审会

    公司每天都会产生大量需求,但不是所有需求都能做,也不是所有需求都要做。需求交付给研发之前,需要经过评审:

    1. 是否有必要?
    2. 是否已完成设计?
    3. 是否包含数据预期与验证标准?

    这里大家要记得,按照紧急重要四象限法,优先级:重要>紧急。

    技术评审会

    开发人员开始做需求的时候,也不能太随意,我会要求针对每个需求做技术评审。当然,技术评审会也可以简化,这个过程只为保证开发人员进行了足够的思考。

    1. 开发人员要说明自己解决问题的思路
    2. 开发人员要介绍自己选择的技术方案,选择此方案的原因、潜在风险、其它备选方案、优劣对比
    3. 大需求要组织会议,小需求可以直接发在研发群里备案

    Code Review

    Code Review 的重要性不需赘述,大家直接看我另一系列的分享吧:

    规范

    规范的目的是让大家更好的使用工具,更严格的推进流程。今天不讨论具体规范,简单列一下常见的规范范围吧。

    • Code Style
    • 需求规范
    • 代码架构规范
    • 文档规范
    • 版本管理规范

    一般来说,规范都不会差太多,关键在于执行,以及执行中尺度的把握。建议大家随便抄一些,然后根据公司、团队特性修改后执行。并保持开放的心态,边做边修正,最后通常都能有不错的结果。

    总结

    创业要看命,我们要做的,则是尽人事。希望这些经验对大家有帮助。

    不过限于个人能力和眼界,上面这些也未必都对。各位老板也可以在面试的时候跟候选人聊聊这些话题,看看对方能否给出逻辑清晰、有启发性的答案,我觉得,只要是勤奋思考,积极寻证,哪怕有出入也不是问题。

    大家有问题、意见和建议,欢迎留言讨论。

  • 【视频】Nuxt3+Vercel+Serverless 全栈开发(1):技术选型介绍,项目基础搭建

    【视频】Nuxt3+Vercel+Serverless 全栈开发(1):技术选型介绍,项目基础搭建

    大家好,时隔小半年,我又来做系列视频了。这次计划结合近期的一些开发经验,分享最近我比较喜欢的全栈+高效+免费+云原生技术方案。

    这套技术方案大约是这样:

    • Nuxt3
      • 提供 Vue 开发环境
      • 提供服务器渲染 SSR 能力
      • 提供 API 环境
    • Vercel
      • 提供部署环境和各项网站能力
      • 提供 Edge Function 和各种存储
      • 提供全球化服务能力
      • 提供 SSL
      • 免费额度足够用很久
    • Upstash
      • 提供 Redis
      • 免费额度足够用很久
      • 付费也不贵
    • TiDB Cloud Serverless
      • 云数据库,MySQL 兼容 API
      • HTTP endpoint
      • 免费额度足够用很久
    • 其它
      • Nuxt- i18n
      • TailwindCSS
      • DaisyUI
      • GitHub
      • pnpm

    使用这套技术方案,可以应对绝大多数网站需求,部署在全球化网络里,使用丰富的免费资源。云原生会带来天生的高可用性、鲁棒性、伸缩性。

    这是第一次分享,详细介绍了这套技术选型,nuxt3 项目的基础搭建,和它自带的路由、服务器 API 系统。

    这次会把每次的分享压缩到 25~30 分钟,降低观看门槛。

    如果你有任何问题、建议,欢迎留言讨论。请 b 站有号的同学帮忙分享完播一键三连,谢谢大家。

    另外,我也在 YouTube 上上传了一份,大家有空的话,麻烦帮忙关注下我的油管频道,感谢感谢。肉山全栈小课堂 – YouTube

  • 我的 AI 学习一周/月总结:四月三场 Hackathon

    我的 AI 学习一周/月总结:四月三场 Hackathon

    这篇文章在我草稿箱里躺了一个月,因为种种原因,内容不够充足,就一直没发。周末想了想,还是补一些内容把它发出来。经过连续几个月的高强度信息获取、吸收,我觉得现在已经进展到必须躬行实践的阶段。未来类似这种信息汇编、浅尝辄止的博客会减少,还是回到案例分析、实践分享的老路上吧。

    最近几周,我一直在努力 release 产品,先死磕计费系统、然后跟 Chrome Extension 在各个网站里搏斗,基本上眼睛一睁一闭,一天就过去了;与此同时,之前报名参加 Hackathon 开始进入正赛阶段,最近三周一周一场,时间非常紧张,周更文章根本来不及……

    不过笔记该写还得写,近期也学到一些东西,记一记。

    我不看好 AutoGPT

    近期最火的产品应该是 AutoGPT。顾名思义,AutoGPT 就是在 ChatGPT 的基础上,让 AI 自己决策、自己尝试、自己评估。这样,我们只需要告诉 AI 我们的需求,然后等待,就能拿到自己需要的东西。

    理想状态,尤其是电影里,这样当然是可行的,然后天网就来了……但是我认为,现阶段这种尝试就是纯烧钱,效果跟猴子打字机差不多。我甚至怀疑这个产品是 OpenAI 自己放出来做压力测试的。

    ChatGPT 的确从人类生产的文本知识里获得到了很多经验,以至于可以回答比如“我想做一个XXX,我应该怎么做”这样的问题。我们普通人多半也是这么开始工作的:先找出一些方案,然后尝试这些方案;遇到问题就再想方案,再解决;重复若干次,直至达成主要目的。似乎用 ChatGPt 也能做到这一点。

    但是我们的决策大量依赖经验、和人类与生俱来的直觉(生物进化 37 亿年得到)。很多时候,经验丰富的老手和新人之间的区别,就是判断哪些方向可以尝试、哪些方向应该优先尝试。所以我们可以把 ChatGPT 当成一名新晋毕业生,刚刚学了一堆知识,但是实操经验几乎为零,然后你告诉他:按照你的想法随便搞,预算不限。结果可能好么?

    我看好在 ChatGPT 等 LLM 加持下,胶水层、DSL、专家系统能够达到新高度,但我不看好 AutoGPT 这样的无头苍蝇。

    OpenAI 发布 Embedding 指南

    openai-cookbook/Question_answering_using_embeddings.ipynb at main · openai/openai-cookbook · GitHub

    昨天 OpenAI 发布了 Embedding 指南,其中一个要点:为什么(Embedding)搜索要好于微调(fine-tuning)?

    文中有个比喻:模型权重就像长期记忆,微调模型就像是为了 闭卷 考试而学习,到了考试的时候,记忆还是会混乱,还是会做错题。搜索就像是开卷考试,通过携带信息(现场翻书),答案会更准确,通常得分也更高。

    显然,使用 Embedding 搜索时,发送的内容会更多,能用到的上下文就会更受限,而且价格会更高。所以最终怎么选择,需要我们自己权衡。

    这篇文章还提到了 Embedding 不适用的场合,以及如何 debug+调优,非常值得一看,推荐给大家。

    四月份参加三场 AIGC Hackathon

    四月连续参加三场 Hackathon,连续三周都在各种赶工,累得够呛,也是本系列文章搁置的原因。我在 复盘文章 里比较详细的记录了参赛过程和作品设计开发,感兴趣的同学可以去看看。简单来说,我们努力开发了一款 web app,叫做 拜拜,可以让用户在任何场合都能求神拜佛,并得到一些心灵的慰藉。

    目前我们已经让神佛可以用语音的方式跟信众交流,结合回声效果,实测效果不错,欢迎大家试用、反馈。

    下一步我们会添加用户体系,并先在大陆地区以外上线。大陆地区以内当然也不会放弃,不过要确保我们合法合规,肯定会慢很多。所以,一步一步来吧,顺利的话,我们希望今年能够提交到各种市场、平台上线;不顺利的话,把主要流程做出来跑通,找个公司卖掉也好。

    一些碎碎念

    我这两天再看 Supabase 的文档,发现它们在 Supabase AI 搜索的下面增加了这样一个补充说明:

    Supabase AI is experimental and may produce incorrect answers.

    Always verify the output before executing.

    看起来,利用 ChatGPT 提升文档可用性并非一帆风顺,乱编答案的问题可能会长期困扰我们。期待大家进一步的最佳实践。

    目前更大上下文规模的模型也不断放出,比如前两天公布的 Claude,支持 100K 上下文,大家可以试一试。

    AI 倦怠期

    ChatGPT 去年引爆行业,今年过完春节破圈,接下来整个 234 月,大家都沉迷于各种 AI 新技术无法自拔。终于,过完五一之后,舆论环境终于开始降温,我相信各位读者老爷跟我的感觉应该差不多吧。

    其实也很正常,密集的投入开发之后,目前 AI 技术能干什么、不能干什么、什么能干好、花多少钱能干好之类的边界,虽然不能说非常清晰吧,但是大概在哪里,大家应该都知道了。割韭菜的那些我们不聊,只说付出辛勤劳动就能有所收获的部分,其实也不太多,或者成本不低。

    所以最近新东西不太多,更多的则是降本增效,比如用小模型换取接近的效果,比如用开源产品替代闭源产品等等。我相信,这是技术发展的必然,也是新一波创新前的宁静,后面会更好。


    总结

    地球生物的身体是自然界进化了 37 亿年得到的,我们可以非常高效地使用资源,比如一个馒头,就够我们活蹦乱跳几个小时。但是当需求上升到一定阶段后,效率就要让位给绝对值,比如硬拉世界纪录,也只有区区 500+kg,但是大型机械随便就是几十吨。

    AI 其实复刻了这条道路。它在思想深度广度上,都不如人类;但是凭借现在的算力冗余,大力出奇迹,从另一个角度超越了人类。将来怎么走,不好说,希望我们能像使用机械一样,使用 AI。

    就目前的状况来看,AI 基础设施现在已经非常好了,应用层存在大量机会。我维持之前的判断不变,建议大家有机会多多接触,早日加入。

  • 【视频】使用 pnpm workspace 管理 monorepo

    【视频】使用 pnpm workspace 管理 monorepo

    有些时候,一个产品由多个不同的项目组成。它们虽然可以独立发布,但彼此之间存在很强的版本依赖关系。这个时候,使用 monorepo 可以大大简化我们的项目管理成本。所以,我们可以看到,很多开源项目都开始使用 monorepo 进行管理,比如 next.js,vite,vue3 等等。

    本视频介绍如何使用 pnpm workspace 功能管理 monorepo,为什么这么做,以及怎么做。希望面临类似需求的同学能学到需要的知识。

    肯定大家点赞分享,一键三连。

    视频大纲:

    1. 开场白
    2. 介绍 pnpm
    3. 介绍 monorepo
    4. monorepo 的优劣
    5. 何时该用 monorepo
    6. 为何推荐用 pnpm 管理 monorepo
    7. 项目实操
    8. 答疑

    有任何问题、意见、建议,欢迎留言弹幕私信与我交流。另外,我现在还在组织模拟面试,想参加的同学,也请跟我联系。目前下周还是空缺,欢迎投稿。

  • 我的四月 AIGC Hackathon 参赛记

    我的四月 AIGC Hackathon 参赛记

    草长莺飞,Hackathon 纷至沓来

    春节过后,ChatGPT 彻底出圈,带动整个 AIGC 领域备受瞩目。于是乎各项赛事活动纷纷上马,都想抢先收割一波流量,也抢先开始对未来的探索。我也积极报名参加,一不小心报了三个 Hackathon 之多:

    1. 思否举办 AIGC Hackathon
    2. 即刻举办 HackEngine
    3. 腾讯举办 Light 公益创新挑战赛

    其中,思否 AIGC Hackathon 我以主创的身份参赛,其它两项赛事则是以交朋友为目来报名。按照我最初的想法,主要开发一个作品,其它两组尽量以顾问身份贡献力量——至少,我这里有各种后端服务、已经开放 GPT-4 的 OpenAI API、SD 服务器随时可用。结果呢,还是逃脱不了干活人的命运,三个组的产品我都得做,连续三周高强度的开会、开发,把所有上班以外的时间都投入进去,才堪堪做完。还好部分代码可以共用,不然真的忙不过来。

    思否作品:拜拜

    我们在思否的作品“拜拜”获得了大家的广泛欢迎,拿下最佳人气奖。今天主要分享下这个产品的构思与开发。

    创意来源

    我有一位多年好友,叫京超,是位产品经理,我经常会跟他讨论产品想法,我偏向技术,他偏向产品,互相攻防,有点类似头脑体操。后来我们商量要一起做点小产品,万一玩票玩成了呢。不过基本也都停留在口头阶段。

    今年过年,他发现亲戚中存在大量拜佛需求,每天必拜,赶上忙的时候,从相册里翻一张照片也能拜。于是他就想,这个需求我们应该可以满足,用互联网思维来看,这就是个打卡应用。我也看好这款产品,因为从技术角度来说,这类应用几乎不需要后端和数据库,只要前端页面+本地存储就能做,开发、运营成本都很低。

    ChatGPT 爆火之后,我很快想到:如果把 ChatGPT 加上,让用户每日拜佛之后还可以跟神佛交流,得到一些心灵的慰藉,岂不更好?于是马上联系京超,把应用开发提上日程。

    尝试开源共建,失败

    熟悉我的朋友可能知道,我还在做一些前端全栈培训方面的尝试,也有几个交流群。我发现对很多新人朋友来说,缺少项目经验通常是他们的大问题,写简历、面试都捉襟见肘。于是我想,把这个项目打造成开源项目,给群里的同学一些做实战项目的机会,我一方面负责产品规划、代码审查,另一方面尽量跟京超把这个项目的边界扩宽,让更多的人能参与进来。

    结果当然失败了😂。项目启动的时候,大家热情很高,有报名参加的、有围观学习的,20人的群分分钟建立起来。分配任务也比较顺利,大家分别领了一些小任务去做。但到代码审查阶段,问题就出现了。

    我只接受新人同学加入,他们经验不多,没受过系统的编程训练,提交的代码质量自然不好,甚至有同学把整个 node_modules 一起传到 PR 里。我就提了很多修改意见。第一波修改大家基本还愿意做,但修改过的 PR 仍然不过关,犯过的错误一犯再犯,A 同学的错误 B 同学也会出现,让他们互相观看学习也基本做不到。

    项目进度更是一言难尽,每日例会(只需要报告进度和同步计划),从全勤到一半人再到没人来,仅仅用了一周。

    最终,我选择放弃,希望他们能通过别的途径收获项目经验吧。

    参加 Hackathon

    虽然我们的创意过完年就定下来了,但是实际上,到思否 Hackathon 举办的时候,我们的正式代码都还没有任何动静,是真正的 Hackathon 作品。

    看到思否 AIGC Hackathon 的报名启事之后,我觉得我们的想法与之契合度甚高,所以立刻就拉着京超去报名。前面几位同学隐身退群之后,我正打算自己动手写代码,另一位好友竹子突然找我聊天,于是我问她有没有兴趣,结果一拍即合,她也加入我们的团队一起开发。

    我们的分工大约是:

    1. 京超负责产品和设计;
    2. 竹子负责主要流程,即拜佛相关功能;
    3. 我负责杂项、API、基础设施、以及特殊功能(比如语音识别和语音转换)

    我们都是工作多年的专业职人,虽然远程协作,没有很强的约束,但基本上进度很顺利,路演前顺利完成了拜佛流程,还能识别用户的口头祈愿,并用 ChatGPT 给予反馈。路演表现很好,引发大家的热烈响应,最后顺利拿到最佳人气奖。

    线上参赛

    其实我本来没想过要报这么多活动。腾讯 light 每年都有,我也每年都进来划个水,今年的团队比较厉害,“意外”进入复赛。思否启动的最早,我们带着作品来,自然很快就决定报名,也算主次分明。即刻 HackEngine 启动时,我其实犹豫了很久,就是怕时间上错不开,最终决定还是要加入学习一下。

    思否和即刻不约而同的选择把线上和线下分成两个赛道,这种做法很有道理,毕竟线上团队基本上有一个月的时间慢慢打造产品,而线下团队则要现场确定方案、只有 1.5~2 天的时间能真正动手开发。

    比较遗憾的是,即刻连线下赛的 demo 路演都不允许围观,我觉得稍微有点过。其实单纯从产品角度,大家能做的、想做的其实都差不多,不让围观也没太大作用。

    经过几年锻炼,大家对线上活动也都非常熟悉,线上赛的氛围还是蛮好的。秀产品,互加好友,找机会合作,除了不能见面细聊,都挺好的。我们也见到很多令人印象深刻的优秀作品,没拿到前三名也心服口服。希望下次再加油。

    未来

    活动结束,我们的开发还没结束。截止到目前,我们已经初步完成神佛语音合成功能,贴一段视频给大家试听一下:

    下一步我们会逐步完善功能,并且争取多平台发布,成为我们第一款上线应用。

    副产品

    为方便京超寻找最合适的音效,我开发了这个网站,可以尝试在线语音识别与语音合成,只需要腾讯云的 id 和 key 即可使用:

    https://buddha-stt.roudan.io/

    上面的带回音的视频即来源于此。如果需要的话,也欢迎大家使用我们的 API 来进行语音识别与语音合成。

    其它两项赛事的结果

    即刻

    我们组两次尝试均以碰壁告终。第一次我们选择“老年人打卡送鸡蛋”这个方向(也是我把方向理顺的),即老年人每天不定时打卡可以攒积分换鸡蛋粮油,我们通过 ChatGPT + 语音系统与老年人交流,并且将结果反馈给家中的年轻人。这个创意没能押中题 Copilot for X,于是开赛日换主题。第二次选择做装修效果图生成,因为组中小同学缺乏经验,无法做出最终作品,也宣告失败。不过 Hackathon 嘛,本来做不出东西就是常态,而且我们都觉得方向不是问题,现在还在摸索着前进,说不定未来哪天大家会见到我们的成果。

    腾讯 Light

    我们选择的是老年人保护方向,希望用一款输入法保护老年人免遭诈骗分子的侵害。通过初赛,没能通过复赛。

    总结

    ChatGPT 从去年年底震撼业界,到今年火爆出圈,再到现在各种应用层出不穷,几乎每天都有新消息,离不开大家的积极参与。分所谓众人拾柴火焰高,今年与 AI GC 相关的活动非常多,据我所知,思否今年还有六场 Hackathon;即刻 HackEngine 二期即将启动;TiDB 的活动也在筹备之中。如果大家对这方面感兴趣,随时入坑都不算晚。

    还是那句话:期待在不远的 AI 未来里,有你也有我。

  • 💪 WordPress 使用 TiDB Cloud 替换 MySQL 💪

    💪 WordPress 使用 TiDB Cloud 替换 MySQL 💪

    白嫖使我快乐。一直白嫖,一直快乐,😊。感谢 TiDB,感谢 TiDB Cloud,你们让我的博客内容更丰富多彩。

    前言

    我这个博客从 2011 年开始写,如今已经 12 年。最早,从 ZOL 离职后,我需要换个新平台写博客;另一方面,我也想学习 Linux、PHP、MySQL,这些原本不熟悉的技术,于是选择了 WordPress。这么多年来,服务器从共享主机搬迁到 VPS,又升级到云主机;PHP 从 5.5、5.6 升级到 7,又升级到 8;Apache 被换成 Nginx;唯独数据库没有变化,基本一套老框架沿用至今。

    我前阵子发现:因为编码问题,无法在标题里或正文里插入表情符号。于是升级数据库也被提上日程。刚好我一直关注的 TiDB 开始提供云数据库服务,采用 Serverless 模式,Free Tier 有 5GB 可以用,足够我写博客。于是我就准备趁此机会切换到 TiDB Cloud 上。

    注册 TiDB Cloud

    打开 tidbcloud.com 注册即可。

    TiDB Cloud 很大方,不需要绑卡,不需要繁琐的操作,直接第三方登录就可以使用。Serverless(Free Tier)只支持 AWS 机房,可选的位置也不多,因为我 ECS 买在美西,所以就把数据库也买在美西,这样速度应该会快一些。

    每个账号可以创建若五个 Serverless 节点,有免费额度,超过限制用量则开始收费。只要稍稍点两下,就建好了,体验很流畅,这里不再赘述。接下来开始使用。

    连接 TiDB Cloud

    数据库准备就绪之后,我们可以进入数据库详情页。点击右上角的“Connect”按钮,即可打开连接信息窗口。

    TiDB 贴心的准备了各种客户端、各种平台的连接方式,对于我这种数据库准小白来说非常有帮助。第一次使用,需要点击右下角的“Reset password”按钮生成数据库密码,生成后,这个按钮会变成复制密码。记得要妥善保存密码,因为我们不能再次查看或者获取。

    这里发现一个设计缺陷:点击 Reset password 按钮没有确认过程,所以我的数据库密码直接就被改掉了……这种破坏性操作还是应该多一个确认比较好,回头反馈给 TiDB 的工作人员。

    导入数据

    TiDB Cloud 只支持导入 CSV 文件,比较难用。可能因为我数据库知识储备不足,我甚至想象不出 CSV 该怎么支持表结构😅。不过没关系,我有 JetBrains 全家桶,命令行操作也还凑合,所以直接从本地跑就行。

    首先在服务器上执行 mysqldump -u USER -p --database BLOG_DB > backup.sql 把整个博客数据库包含数据结构都导出到 backup.sql 文件里。

    打开 sql 文件,把数据库的编码全部改成 utf8mb4 或者 utf8mb4_unicode_ci,这样就可以支持表情符号咯 🎉🎉。

    打开 DataGrip,按照上一节介绍建立数据库连接,右键,选择“SQL Scripts”,然后执行刚才的 SQL,等一会儿,数据即可完成导入。当然,实际过程肯定没有这么顺利,不过云数据库嘛,有问题就直接整个节点干掉再重建就好。不留脏数据。

    WordPress 连接

    TiDB Cloud 要求必须使用 TLS 安全连接,所以我们需要修改 wp-config.php

    /** WordPress数据库的名称 */
    define('DB_NAME', 'blog');
    
    /** MySQL数据库用户名 */
    define('DB_USER', '用户名');
    
    /** MySQL数据库密码 */
    define('DB_PASSWORD', '密码');
    
    /** MySQL主机 */
    define('DB_HOST', '主机域名:端口');
    
    /** 使用 SSL 连接 */
    define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);
    

    因为我的服务器是 Ubuntu 22.04 系统,证书放在默认位置,可以自动加载。如果你使用其它系统,可能需要修改一下证书路径。

    解决 SQL_CALC_FOUND_ROWS 导致翻页丢失的问题

    完成上述操作,打开博客,500 😱。查看 error.log,原来是不支持 SQL_CALC_FOUND_ROWS 导致报错。我并不知道这个东西是干嘛用的,丢到 Google 里搜索,找到这个 issue,原来 WordPress 的 SQL 要使用这个函数,但是 TiDB 并不支持。

    解决方案是在数据库里执行 SET GLOBAL tidb_enable_noop_functions=1。之后 WordPress 不会再报错,但是相应的,翻页功能也没有了,因为 WordPress 无法统计博文数量。

    继续搜索。看起来 SQL_CALC_FOUND_ROWS 并不是什么好东西,不知道为什么 WordPress 至今都不愿意把它移除。还好,WP 留有开关,我们可以关闭这个函数的使用。我自己创建了一个 WP 小插件,用来给博客加广告、调整页面,所以我就在里面添加函数,关掉 SQL_CALC_FOUND_ROWS

    if ( ! function_exists( 'wpartisan_set_no_found_rows' ) ) :
      function wpartisan_set_no_found_rows( \WP_Query $wp_query ) {
        $wp_query->set( 'no_found_rows', true );
      }
    endif;
    add_filter( 'pre_get_posts', 'wpartisan_set_no_found_rows', 10, 1 );
    
    if ( ! function_exists( 'wpartisan_set_found_posts' ) ) :
      function wpartisan_set_found_posts( $clauses, \WP_Query $wp_query ) {
        // Don't proceed if it's a singular page.
        if ( $wp_query->is_singular()  ) {
          return $clauses;
        }
    
        global $wpdb;
    
        // Check if they're set.
        $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
        $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
        $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
    
        // Construct and run the query. Set the result as the 'found_posts'
        // param on the main query we want to run.
        $wp_query->found_posts = $wpdb->get_var( "SELECT $distinct COUNT(*) FROM {$wpdb->posts} $join WHERE 1=1 $where" );
    
        // Work out how many posts per page there should be.
        $posts_per_page = ( ! empty( $wp_query->query_vars['posts_per_page'] ) ? absint( $wp_query->query_vars['posts_per_page'] ) : absint( get_option( 'posts_per_page' ) ) );
    
        // Set the max_num_pages.
        $wp_query->max_num_pages = ceil( $wp_query->found_posts / $posts_per_page );
    
        // Return the $clauses so the main query can run.
        return $clauses;
      }
    endif;
    add_filter( 'posts_clauses', 'wpartisan_set_found_posts', 10, 2 );

    调整完毕,翻页功能恢复正常。

    使用体验小结

    Serverless 讲究一个即用即起,平时是休眠状态,有人用才会启动。所以最好避免冷启动,否则第一个用的人要等很长时间,体验很差。作为博客,更简单的方式是配置 CDN,给首页外的页面添加长期缓存,就能改善大部分用户的体验。

    实际上我目前使用了一个多月,并没有冷启动的感觉。我的博客日均几百的访问量,感觉相当可以。另外一个实例,作为 NoCoDB 的数据库,因为访问量很小,所以冷启动感觉很明显。

    其它方面,速度和效率都不比本地数据库差,以后我再搞产品,应该都不会自己数据库了,嘿嘿嘿。


    后记

    我已经两次报名参加 TiDB Hackathon,第一次止步外围筛选第二次虽然进入决赛圈,但是因为对云数据库不了解,挑战开发 NoCoDB+TiDB Cloud 失败,铩羽而归。

    但是借用群里同学的说法:人生没有白走的路,每一步,都算数。虽然 Hackathon 没能获得好成绩,但是我获得了更多关于 TiDB Cloud 的知识,于是这次可以顺利完成迁移。

    目前博客网站使用体验良好,速度不比之前使用本地数据库慢。前两天,我发现 TiDB Cloud 开放了 Data Service,提供 HTTP Endpoint,这表示着他们向 DaaS(Database as a Service)更近一步,也意味着我们在 Edge Function 里使用 TiDB Cloud 成为可能。那么接下来,我打算好好利用一下这个薅羊毛机会,把几个 Side Project 的数据端放在 TiDB Cloud 上,跟 Supabase 比一比,看哪个更好用,更适合新项目、小公司从零到一。

    到时候也会写成更多文章,或许做成视频,跟大家分享。

    如果各位读者老爷对数据库、云开发、薅羊毛感兴趣,欢迎留言讨论,可提出问题,亦可指点一二,均非常欢迎。

  • 使用 Vercel、Supabase、Stripe 打造 OpenAI 计费系统:1. 系统篇

    使用 Vercel、Supabase、Stripe 打造 OpenAI 计费系统:1. 系统篇

    过去两周,我基本都在跟这套付费/计费系统死磕,相对来说投入到 AI 学习里的时间不多,以至于 我的 AI 学习周记 系列本周停更。如今终于基本搞定这套系统,基本概念、开发调试都不存在难以逾越的问题,所以打算写几篇博客总结一下,给后来者分享我收获的知识、踩到的坑。也让自己需要的时候可以翻查笔记。

    OpenAI 计费系统现状

    目前我们讨论的 OpenAI 计费,基本是针对 ChatGPT 对应的 gpt-3.5-turbo 和 gpt-4 这两个模型,在 API 调用方面的费用。官方规定,gpt-3.5-turbo 是 $0.002/1000 tokens,gpt-4 根据容量不同,最多要贵 30 倍,所以提供服务时,我们基本还是以 3.5 为主。如果用户愿意多付钱,我们也可以提供 gpt-4(已经拥有权限)。

    不使用 stream: true 的时候,OpenAI 会返回使用的 token 数量,这个时候,数据准确可靠。但是为用户体验考虑,也为云服务考虑,使用 stream: true 模式明显更好。但是在这种模式下,OpenAI 不返回使用的 token 数量。(我猜测这跟 Transformer 模型的工作原理有关。)所以我们就需要手动统计 token 的消耗。

    这个时候得到的结果可能并不准确,但是没办法,我们只能这么做。

    我们开始也不知道 OpenAI 怎么统计 token 数,好在官方提供了计算页面:https://platform.openai.com/tokenizer 和推荐方案(看起来是传说中的 GPT 地牢作者的作品),可以帮我们完成计算。至于详情,将来会具体介绍。

    基于 Edge Function 的计费系统设计

    首先,我们要选择服务器架构。

    传统方案是配置一台云服务器,然后前面架一个 CDN。但由于我们是一个面向全球的服务,这样的架构不甚理想。而且基于 stream: true 的方案需要长时间维持网络连接,单机容量也不够。全球部署的话,成本太高,初创团队不现实。

    所以很自然的,我们准备使用 Edge Function。Edge Function 的优势在于:

    1. 它借助云服务厂商的边缘节点提供服务,节点分布广泛,可以就近服务用户,大大改进响应时间。
    2. Edge Function 在用户请求的时候才工作,平时休眠。这种按需付费成本更低。
    3. 相比于传统的 Serverless Function,Edge Function 一般都会修改运行时,用减少负载的方式提升启动速度,以几乎 0 延迟的方式启动,用户体验很好。

    以上几点我均已在之前的试做项目中在 Vercel 平台上体验过,所以这次没怎么纠结,直接选择 Vercel Edge Function 开发。

    也欢迎大家阅读我前一篇分享文:使用 Vercel Edge Function 访问 OpenAI API 的注意事项

    数据库选择:Supabase

    确定使用 Edge Function 之后,下一步要选择数据库。传统的、基于数据库协议的方式不可行,必须支持 http 请求,必须支持连接池,可选方案不太多。刚好前阵子我为了抽键盘,了解到有一家叫 Supabase 的新 serverless 服务商,可以很好满足我们的需求。

    首先,他们能满足 Vercel Edge Function 的需要,也是官方推荐的厂家之一。

    其次,他们的服务基于 PosgreSQL,具备丰富的插件生态,可以实现各种功能,包括未来给 ChatGPT 提供内容拓展的 pg_vector,可以不用担心将来需求延伸。

    再次,作为一家 serverless 服务商,他们家的免费额度看起来还不错,应该可以满足我们早期验证产品的目标(薅羊毛就是爽)。而且,自带 RLS(行级安全策略)也会给未来的开发带来很多方便,比如我们可以放心的把用户身份有关的读取操作放在客户端,不用单独开发接口,节省很多人力。

    最后,我们决定选用 Supabase,作为数据库服务供应商。

    其实 Supabase 也提供 Edge Function,效果理论上并不比 Vercel 差,但它是基于 Deno 的封装,生态和环境都跟 Node.js 不太一样。考虑到学习成本,以及分散投入有利于多嫖资源,所以我暂时不打算用,还是先集中在他们家的数据库上。

    收费选择:Stripe

    作为超迷你初创团队,主攻海外英文市场,我们的策略自然是一切从简,那么付费方案很自然就选择了 Stripe。Stripe 的功能全,文档强大,很适合我们这种小团队使用:

    1. 接受多种收费方式,各种信用卡不一而足,尽可能满足用户
    2. 提供订阅式购买
    3. 支持优惠码等促销手段
    4. 提供 SaaS 服务,方便管理用户
    5. 可以用 Payment link 创建付费页面,省去几乎所有购买流程的开发成本
    6. 提供 webhook,对接我们的账户体系
    7. 提供大量 API,如果有需要,将来我们可以逐步迁移到自己的平台

    最终选择

    基本上,我们最终形成了这样一套方案:

    1. Vercel Edge Function 提供 OpenAI API 的封装,我们借由它完成用户请求、额度拦截、计费等功能
    2. Supabase 提供存储,我们借由它存储用户的账户状态、付费记录、消费记录等功能
    3. Stripe 提供购买功能,我们用它的 Payment link 让用户完成购买付费

    这些产品都支持 TS/JS 为主的语言,提供基于 npm 的 SDK,开发环境很大众,开发体验不错。未来属于 JS。

    这套架构在初期没什么成本,可以覆盖几乎全球的用户,提供不俗的性能。将来用户量增长,需要扩容的时候,也不需要我们再手动开发扩容功能,只要根据用户量、使用量付费给云服务商就好。如果将来我们要提供 embedding,嵌入用户自己的数据,也可以很容易的在现有框架下实现扩展。

    乐观估计,未来半年到一年内,我们都可以在这个体系下开发。


    小结

    我感觉全世界就我一个人在做计费系统,其他人:

    1. 要么是不知道是弄了很多免费账号还是自己负担费用先抢用户,反正是敞开给用户免费用
    2. 要么是收一大笔钱割韭菜,反正收的钱多,普通用户随便用也用不完

    总之都不在意区分用户,也不在意回本。于是几乎看不到有人讨论如何做自己的付费系统。

    我们认为成熟的商品,还是要在成本、收益、风险上形成稳定、友好的比例,计费系统早晚都得做,不如先做好。

    如果你对 OpenAI 计费系统,对我们这套基于公共云平台的 Edge Function 方案感兴趣或者有问题,欢迎留言讨论。