基于最新 ChatGPT API 实现命令行版 ChatGPT

xueyuanjun

共 5467字,需浏览 11分钟

 · 2023-03-04

引子


OpenAI 这两天发布了 ChatGPT API,基于 gpt-3.5-turbo 模型,这是一个 GPT-3.5 的优化版本,用于支持开发者把 ChatGPT 集成到自己的产品中,同时把 API 调用价格降到 $0.002 每千 token,意味着处理 100万字符的文本只需要 2 美元,也就是差不多十几块钱人民币,效果更好、价格更低,这让 ChatGPT API 更具性价比,因此这两天基于 ChatGPT API 的各种套壳应用如雨后春笋般大量冒出。


我也来凑个热闹,试一试水,正好在我今天新建的 ChatGPT 互助讨论群里有人问有没有命令行版 ChatGPT,那就拿它来开刀吧:



老规矩,还是面向 ChatGPT 编程来实现这个命令行版 ChatGPT 应用。

在《面向 ChatGPT 编程实现全栈开发的 18 种方法》这篇教程中,我在最后说到在面向 ChatGPT 编程的时候,需要牢记两个原则:第一,知道你在做什么,第二,不要相信 ChatGPT 的代码。

落地到实践的时候,就是在与 ChatGPT 合作形成的结对编程联盟中,作为开发者我们需要承担代码设计、架构、编排与审核(CodeReview)的职责,对方案和结果负责,而具体的代码片段编写这种纯体力活则交由 ChatGPT 完成。

接下来,我们将遵循这个思路实现命令行版 ChatGPT 应用。

代码设计

我在微信群里所说,这个需求确实很简单 —— 通过编程在控制台应用代码中调用 ChatGPT API 接口,实现一个命令行版的 ChatGPT,几十行代码就能搞定。不过这里仍然需要做一些简单的设计:

  1. 调用 API 需要传递 API KEY,我们不希望这个 KEY 硬编码在代码中,而是从系统环境变量读取,从而让代码更安全可维护;

  2. 用封装好的 Go OpenAI 库与 API 接口进行交互,避免通过 HTTP 协议与原生 API 交互编写大量重复代码,让代码更简洁优雅;

  3. 做一个给客户使用的产品,美观会带来更好的用户体验,所以我希望即便是命令行应用,也尽可能让交互和输出更美观一些。


于第 2 点,可以使用 go-gpt3 库,这是一个通过 Go 封装的 OpenAI API 调用库。

于第3点,可以使用 glamour 库,这是一个 Go 语言实现的、能够在兼容 ANSI 终端基于样式渲染 Markdown 文本的第三方库,它是让命令行更美观的开源项目 Charm 的一部分。

因为项目很简单,又是在客户端本地使用,所以不需要做什么复杂的架构,下面直接进入编码部分。

代码编写

面向 ChatGPT 编程的核心就是把需求尽可能准确全面地转化为 Prompt 传递给 ChatGPT,这里有产品需求(来自业务和产品),也有代码设计和架构上的需求(来自开发者),然后让它生成代码,这是作为一个合格的 Prompt 工程师自我修养的必要组成部分。


在《ChatGPT 提示的艺术 —— 编写清晰有效提示指南(二)》这篇教程中,我已经给大家介绍了编写清晰有效 Prompt 的原则、做法和技巧,感兴趣的可以去看下,这里我先将我的需求转化为 Prompt 让 ChatGPT 替我编写对应的 Go 代码实现:


代码优化


看起来不错,基本流程没问题,但是代码审核会发现,它现在把输入的 Prompt 写死了,并不能动态接收用输入,而且运行一次后就退出了,这不是 ChatGPT 的问题,而是我们的需求并没有明确这一点,作为一个完整的需求和程序,需要说明是什么,怎么样,什么时候开始,什么时候退出。


不过细心的同学可能还留意到 go-gpt3 包引入的时候没有设置别名,会导致运行时出错,同时调用的 OpenAI API 接口也不对,最新的 ChatGPT API 接口方法应该是 CreateChatCompletion,可能是太新的缘故,ChatGPT 还没有学习到这里,不过这都属于 ChatGPT 要优化的点了。以及使用了另一个同名的包,这个也需要明确告知它。


细节上还是需要优化,现在我们基于这些要点先来完善我们的 Prompt:


这一次,我们更加细化了程序的行为,用户输入 start 启动,输入 quit 退出,在此期间则不断读取用户输入,返回 ChatGPT 处理结果并渲染到控制台输出,另外,我还在读取系统环境变量时嘱咐通过 os.Getenv 获取,因为有时候发现 ChatGPT 输出不稳定,会尝试从 .env 文件读取 OPENAI_API_KEY,当然这不是 ChatGPT 的问题,是我们没有给出明确清晰的 Prompt。

现在再来看 ChatGPT 输出的代码,就更完善了:

package main
import ( "context" "fmt" "os" "strings"
"github.com/charmbracelet/glamour" "github.com/common-nighthawk/go-figure" gpt3 "github.com/sashabaranov/go-gpt3")
func main() { // 获取 OpenAI API Key apiKey := os.Getenv("OPENAI_API_KEY") if apiKey == "" { fmt.Println("请设置 OPENAI_API_KEY 环境变量") return }
// 初始化 Glamour 渲染器 renderStyle := glamour.WithEnvironmentConfig() mdRenderer, err := glamour.NewTermRenderer( renderStyle, ) if err != nil { fmt.Println("初始化 Markdown 渲染器失败") return }
// 输出欢迎语(命令行应用启动界面) myFigure := figure.NewFigure("ChatGPT", "", true) myFigure.Print() fmt.Println("输入 start 启动应用,输入 quit 退出应用")
// 创建 ChatGPT 客户端 client := gpt3.NewClient(apiKey) if err != nil { fmt.Printf("创建客户端失败: %s\n", err.Error()) return }
messages := []gpt3.ChatCompletionMessage{ { Role: "system", Content: "你是ChatGPT, OpenAI训练的大型语言模型, 请尽可能简洁地回答我的问题", }, }
// 读取用户输入并交互 var userInput string for { fmt.Scanln(&userInput)
if strings.ToLower(userInput) == "start" { fmt.Println("ChatGPT 启动成功,请输入您的问题:") } else if strings.ToLower(userInput) == "quit" { fmt.Println("ChatGPT 已退出") return } else if userInput != "" { messages = append( messages, gpt3.ChatCompletionMessage{ Role: "user", Content: userInput, }, ) // 调用 ChatGPT API 接口生成回答 resp, err := client.CreateChatCompletion( context.Background(), gpt3.ChatCompletionRequest{ Model: gpt3.GPT3Dot5Turbo, Messages: messages, MaxTokens: 1024, Temperature: 0, N: 1, }, ) if err != nil { fmt.Printf("ChatGPT 接口调用失败: %s\n", err.Error()) continue }
// 格式化输出结果 output := resp.Choices[0].Message.Content mdOutput, err := mdRenderer.Render(output) if err != nil { fmt.Printf("Markdown 渲染失败: %s\n", err.Error()) continue } fmt.Println(mdOutput) messages = append( messages, gpt3.ChatCompletionMessage{ Role: "assistant", Content: output, }, ) } }}

gpt3 别名和 CreateChatCompletion 方法调用相关的代码还是需要手动调整,不过这也是我前面说的面向 ChatGPT 编程的原则之一,最后一定要审核 ChatGPT 的代码,它目前对于最新的知识还是有一定的迟滞性。

代码细节我就不展开解释了,有不明白的地方可以参考我在《面向 ChatGPT 编程实现全栈开发的 18 种方法》这篇教程中代码解释部分提供的方法自行去基于 ChatGPT 查看。

效果展示


最后我们在终端体验一下这个命令行版 ChatGPT,我这里使用的是 Windows WSL 终端,Windows 终端本身体验其实不太好,尤其是中文输入的时候,删除字符特别费劲,且很容易造成消息变形,如果是 Mac 或者 Ubuntu 终端可能效果会更好一些。


首先我们启动这个应用,如果没有设置 OPENAI_API_KEY 这个系统环境变量(运行 export OPENAI_API_KEY={你的 OpenAI SECRET KEY} 命令设置即可),会提示你设置:



如果已经设置,会进入下面这样的启动欢迎界面:


输入 start 即可启动应用,然后我们在命令行输入问题,回车,就会将问题提交给 ChatGPT,ChatGPT 处理的结果会返回并输出到控制台,这里的格式经过了 Glamour 库的美化:


因为是 for 循环,所以你可以持续提问、得到答案,直到输入 quit 退出应用。

终端需要能够访问 OpenAI API 才能调用成功,这意味着命令行也要支持科学上网。

好了,这就是我们基于最新 ChatGPT API 实现的命令行版 ChatGPT 应用,因为 ChatGPT API 是前两天才发布的,所以看起来 ChatGPT 并没有学习到这个最新的 API 如何调用,存在迟滞性,进而导致编写的代码并不能直接满足需求,需要人为介入去修改,希望未来 ChatGPT 能够在这一块上有所进化。

欢迎点赞、关注、分享,更多关于 ChatGPT 的学习实践探讨,请关注这个订阅号或者点击阅读原文了解极客书房最新动态。

浏览 74
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报