跳至主要內容

开发一个 ChatGPT 真的只是当 "接口侠" 吗?GPT Terminal 细节分享!

ltyzzz约 2260 字大约 8 分钟

专栏目录

耗时一下午,我实现了 GPT Terminal,真正拥有了专属于我的 GPT 终端!open in new window

如何用 GPT 在 5 分钟内 ”调教“ 出一个专属于你的 ”小黑子“?open in new window

如何丝滑实现 GPT 打字机流式回复?Server-Sent Events!open in new window

我是如何让我的 GPT Terminal “长记性” 的?还是老配方!open in new window

一个合格的类 GPT 应用需要具备什么?一文带你打通 GPT 产品功能!open in new window

开发一个 ChatGPT 真的只是当 "接口侠" 吗?GPT Terminal 细节分享!open in new window

如何借助于 OpenAI 以命令的方式在 GPT 终端上画一只 “坤”?open in new window

不满足当 ChatGPT “接口侠”?轻松可视化 Fine-tuning 训练你的模型!open in new window

耗时一下午,我终于上线了我的 GPT 终端!(内含详细部署方案记录)open in new window

项目地址:github.com/ltyzzzxxx/g…open in new window

欢迎大家Star、提出PR,一起快乐地用 GPT Terminal 玩耍吧~

前言

相信大家在看了上一篇open in new window的分享后,一定对于如何实现一个类 GPT 应用,做到了心中有数。当你自己去动手实现的时候,只需要依照之前列出的功能点,去寻找相应的解决方案即可。

在这篇文章,我想给大家分享一下我在面对这些问题时,是如何解决的。我会就上一篇文章提到的 用户体验支撑功能,逐一进行讲解。

对于 基础功能 部分的实现,大家可以参考专栏之前发布的文章,基本都会对应的讲解与实现方案

大家是不是看的有点困了?没关系,在这篇文章中,我会贴一些代码片段,给大家醒醒瞌睡!如果大家不满足于此的话,可以进入 GPT Terminalopen in new window 的项目中,深入了解一下实现细节!

用户体验支撑功能

在开始正式讲解功能方案之前,先给大家展示一下 GPT TerminalDemo,相信大家看了 Demo 之后再看文章讲解,一定会有更加形象深刻的印象与理解!

iShot2023-06-20-22.40.50_2.gif
iShot2023-06-20-22.40.50_2.gif

GPT 响应状态下,禁止用户输入

上一篇open in new window文章中,我们提到为了避免 GPT 上下文混乱,因此在 GPT 响应状态下,应禁止用户输入。在 GPT Terminal 中,我也同样实现了这一点。

我使用的是 Vue3 框架。在设计上,用户与 GPT 的聊天记录展示,我将其放置于 ChatBox 组件中。而用户在终端上的输入框是放置于外层的父组件 GptTerminal 组件中。

至于为什么要这样设计,是因为组件分工不同。GptTerminal 是负责整体终端,包括命令输入、命令执行发起、展示结果;而对应到命令输出的结果,是动态的,根据输入命令的不同,输出展示不同的组件。

为了实现响应状态下禁止用户输入这一功能,我采用了 Vueemit 事件机制,通过此特性完成父子组件之间的通信。如果大家对这一特性不熟悉,可以先去简单了解一下官方的示例open in new window

父组件 GptTerminal.vue 部分代码如下所示:

对应于项目中 src/components/gpt-terminal/GptTerminal.vue 文件

// 处理 GPT 请求开始时的操作
const handleStart = () => {
  console.log("开始")
  isRunning.value = true
}

// 处理 GPT 请求结束后的操作
const handleFinish = () => {
  console.log("结束")
  isRunning.value = false
}

子组件 ChatBox.vue 部分代码如下所示:

对应于项目中 src/core/commands/gpt/subCommands/chat/ChatBox.vue 文件

// 定义 emit 事件
const emit = defineEmits(['start', 'finish']);

// 发出请求前,子组件向父组件发送 `start` 事件
emit('start')
// 执行发送请求方法
getGptOutput()
// 请求响应结束后,子组件向父组件发送 `finish` 事件
emit('finish')

在这部分代码中,isRunning 主要是为了控制输入框是否可写,将其绑定到 ant-design-vuea-input 组件的 disabled 属性。

  • 在调用 GPT 接口之前,子组件向父组件发送 start 事件,父组件禁止用户输入
  • 在结束 GPT 借口调用后,子组件向父组件发送 finish 事件,解除输入限制

Loading 状态提示

在请求发送到 GPT 服务之后,GPT 服务响应之前,会有一定时间的停顿,以及国内用户访问 OpenAI 可能存在较高的网络延时,因此在这里是有必要设置 Loading 状态的。

为了在终端界面实现这一点,我想到了比较朴素的做法 - 定时器。当用户发出命令的瞬间,即组件进入初始化状态,定时器便开始工作。

对应于项目中 src/core/commands/gpt/subCommands/chat/ChatBox.vue 文件

// 如下代码位于 onMounted 初始化方法中
let count = 0;
let loadingInterval = setInterval(() => {
count++;
if (count > 3) {
  count = 0;
}
switch (count) {
  case 0:
    output.value = "正在加载内容中";
    break;
  case 1:
    output.value = "正在加载内容中.";
    break;
  case 2:
    output.value = "正在加载内容中..";
    break;
  case 3:
    output.value = "正在加载内容中...";
    break;
}
}, 500)

相信聪明的大家一看就懂,这不就是三个 . 符号无限循环,直至 GPT 开始响应回复。对的,就是这么简单!因为在终端界面,不需要有什么花里胡哨的组件,只需要简单地表明 Loading 即可。

但是,还有一个细节需要注意,当 GPT 开始响应后,一定要清空定时器与输出内容呀!

// 清空定时器与输出
clearInterval(loadingInterval)
output.value = ""

网络超时提示

当用户使用我们的 GPT Terminal 时,可能会出现 API Key 配置错误或者根本无法访问 OpenAI 的网络问题。因此,我们不仅需要为用户显示 Loading 提示,还需要处理这些网络异常情况。

对于网络超时问题,我采用了 延时器 做法。当超过一段时间之后,若 GPT 仍然无法响应并回复,则需要为用户做出相应的超时提醒。

想必很聪明的大家应该已经想到了,我们这个三个功能有着紧密的关联。当延时器到达指定时间时,需要做一些 收尾工作

  • 关闭定时器,即关闭 Loading 提示
  • 关闭延时器
  • 判断 GPT 是否已经成功响应
    • 若未响应,则向父组件发送结束事件,并提示用户网络超时

对应于项目中 src/core/commands/gpt/subCommands/chat/ChatBox.vue 文件

// 延时器
let timeoutTimer = setTimeout(() => {
    clearInterval(loadingInterval)
    clearTimeout(timeoutTimer)
    if (!flag.value) {
      emit('finish')
      output.value = "请求超时,请检查您的网络环境是否配置正确 或 后端是否启动~"
    }
}, 35000)

可以看到,这里 flag 的作用是判断 GPT 响应是否成功,若成功的话,在 getGptOutput 方法中,会将 flag 的值设置为 true,并发送 finish 事件。若很遗憾没有成功的话,这部分操作就需要等到延时器触发时执行啦。

总结

看完之后,其实大家会发现,在开发过程中,我们很容易注意到这些用户体验细节。但是它们又是一些比较零碎的点,很容易让大家失去做的兴趣。从我角度来看,为了让产品更加完整、让用户体验更好,这些还是必须得实现呀!毕竟有一句话是,细节决定成败

为了防止大家对 用户体验支撑功能 的设计不太清楚,我给大家画了 GPT 请求与响应的流程图,相信大家看了图之后,不仅能够清楚地知道如上 3 个功能的设计,而且能够对 GPT Terminal 三方(前端、后端、第三方 Open AI)交互有着更加深刻的理解。

whiteboard_exported_image (3).png
whiteboard_exported_image (3).png

后记

今天给大家展示了我在 GPT Terminal 中,对于用户体验相关的细节功能是如何实现的。

其实本来还要为大家展示一下我在 GPT Terminal拓展功能 的实现方案,但是用户体验相关的细节功能又写了好多字,再写下去字数就太多啦~

为了防止大家看得疲惫,所以我决定将这部分放到下一篇中(内心OS:下次再也不拖了!)

最后再小小地提一下,GPT Terminal 目前已经基本实现了主体功能啦,还有一些 Bug 需要修改,如果大家想要了解 GPT Terminalopen in new window 项目的更多细节与解锁更多玩法的话,请到其主页查看哦。

如果各位小伙伴关于文章或项目有什么不懂的地方,请直接提出 Issue,我会在 24 小时内回复!

看在我这么认真的份上,大家点个 Star、点个赞不过分吧(磕头!)下期再见!