原文链接:https://icloudnative.io/posts/build-chatgpt-web-using-laf/
OpenAI 已经公布了 ChatGPT 正式版 API,背后的新模型是 gpt-3.5-turbo,这是 OpenAI 目前最先进的模型,响应速度更快,价格更便宜。
作为开发人员,我们还是希望通过 API 将 ChatGPT 和相关模型集成到自己的产品和应用中,尴尬的是,目前无法访问 ChatGPT API,原因大家都懂得。于是网上出现了各种各样的 API 反代服务,我们可以直接通过反代服务来变相访问 ChatGPT API。
即使我们解决了 API 的访问问题,还要准备一个开发环境,比如对于 Node.js 客户端来说,需要准备一个 Node.js 环境。
有没有一种简单快捷的方法来调用 ChatGPT API 呢?
那当然是用 Laf 了。
Laf 是一个完全开源的一站式云开发平台,提供了开箱即用的云函数,云数据库,对象存储等能力,让你可以像写博客一样写代码。
如果你希望快速了解 Laf 的用法,可以参考这篇文章:三分钟学会 Laf。
言归正传,下面我们开始计时,三分钟时间用 Laf 实现一个自己的 ChatGPT!
前提条件:你需要准备一个 ChatGPT 账号并且生成一个 API Key (这一步可以问 Google )
首先需要登录 laf.dev,然后新建一个应用。

点击开发按钮进入开发页面。

在 NPM 依赖面板中点击右上角的 +:

然后输入 chatgpt 并回车进行搜索,选择第一个搜索结果,保存并重启:

重启之后,自定义依赖项中便出现了 chatgpt。

然后就可以像我一样新建一个云函数名字叫 send,并写入以下内容:
import cloud from '@lafjs/cloud'
export async function main(ctx: FunctionContext) {
const { ChatGPTAPI } = await import('chatgpt')
const api = new ChatGPTAPI({ apiKey: cloud.env.CHAT_GPT_API_KEY })
let res = await api.sendMessage('“鸡你太美”指的是中国大陆哪位男艺人?给你个提示,他喜欢唱、跳、篮球、Rap')
console.log(res.text)
return res.text
}

API Key 是通过环境变量 CHAT_GPT_API_KEY 传入的,所以我们还需要创建一个环境变量。点击左下角的设置图标:

依次选择「环境变量」--> 「新增环境变量」,输入环境变量的名称和值,然后点击「确定」,再点击「更新」,便会重启应用。

现在点击右上角的「运行」,即可调试运行。

Perfect!现在我们来试试添加追踪上下文的功能。其实也很简单,只需要在对话时传入上一次对话的 ID 即可,代码如下:
import cloud from '@lafjs/cloud'
export async function main(ctx: FunctionContext) {
const { ChatGPTAPI } = await import('chatgpt')
const api = new ChatGPTAPI({ apiKey: cloud.env.CHAT_GPT_API_KEY })
let res = await api.sendMessage('“鸡你太美”指的是中国大陆哪位男艺人?给你个提示,他喜欢唱、跳、篮球、Rap')
console.log(res.text)
// 传入 parentMessageId 追踪上下文
res = await api.sendMessage('不对,他姓蔡,请重新回答', {
parentMessageId: res.id
})
console.log(res.text)
return res.text
}
运行一下看看:

好厉害,竟然两次就答对了我的问题!
好了,现在才开始真的计时,因为刚刚是教学环节,不计入耗时?
接下来我们就可以开始动手打造自己的 ChatGPT 了,首先把上一节的函数替换为下面的内容:
import cloud from '@lafjs/cloud'
export async function main(ctx: FunctionContext) {
const { ChatGPTAPI } = await import('chatgpt')
const data = ctx.body
// 这里需要把 api 对象放入 cloud.shared 不然无法追踪上下文
let api = cloud.shared.get('api')
if (!api) {
api = new ChatGPTAPI({ apiKey: cloud.env.CHAT_GPT_API_KEY })
cloud.shared.set('api', api)
}
let res
// 这里前端如果传过来 parentMessageId 则代表需要追踪上下文
if (!data.parentMessageId) {
res = await api.sendMessage(data.message)
} else {
res = await api.sendMessage(data.message, { parentMessageId: data.parentMessageId })
}
return res
}
现在应该很好理解这个函数了吧?
我们要实现的是 Web 版 ChatGPT,所以还需要一个前端页面。首先需要安装 Laf 的 SDK:
$ npm install laf-client-sdk
接下来,需要创建一个 cloud 对象:
import { Cloud } from "laf-client-sdk";
// 创建 cloud 对象 这里需要将 <appid> 替换成自己的 App ID
const cloud = new Cloud({
baseUrl: "https://<appid>.laf.dev",
getAccessToken: () => "", // 这里不需要授权,先填空
});
这里我们看一下前端的核心代码,非常的简单,就是把提问的内容和上下文 id 传入云函数就可以了。
async function send() {
// 我们提问的内容
const message = question.value;
let res;
// 与云函数逻辑一样,有上下文 id 就传入
if (!parentMessageId.value) {
res = await cloud.invoke("send", { message });
} else {
res = await cloud.invoke("send", { message, parentMessageId: parentMessageId.value });
}
// 回复我们的内容在 res.text
// 这个是上下文 id
parentMessageId.value = res.id;
}
到这一步 我们已经可以发信息给 ChatGPT 并且拿到回复的消息了。
我们只要稍微加亿点点细节,就可以变成这样:

加完这点细节之后,基本开发工作就完成了,接下来就是把项目上线分享给你的朋友,顺便装个杯。
说到上线我们现在应该要去买一台服务器安装 Nginx,配置 Nginx,解析域名,绑定域名...
NO NO NO 我不允许你浪费年轻而美好的生命,life is short, you need laf ?
打开你的 Laf,点击存储界面 --> 点击上方加号 --> 创建一个权限为 readonly 的存储桶(名字随意)。

创建完之后,在你的前端项目中运行打包命令。我这里用的是 npm run build。
打包完毕之后找到打包好的 dist 文件夹,像我一样把 dist 文件里面的所有东西都上传到我们刚刚创建的存储桶里面,记住是原封不动的上传哦,文件就是文件,文件夹就是文件夹。

上传完毕之后,发现右上角有一个 “开启网站托管”,点一下它!

点完之后出来一个链接,我们点击一下访问看看是啥东西。

哦!我的老天鹅呀 这不就是我刚刚开发的项目吗??

恭喜,到这里你的项目已经上线了,快分享给你的好朋友吧!
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub