您的位置:首页 >聚焦 >

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

2023-03-04 05:42:30    来源:程序员客栈
引子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,几十行代码就能搞定。不过这里仍然需要做一些简单的设计:

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

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


(资料图片仅供参考)

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

对于第 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 mainimport ("context""fmt""os""strings""github.com/charmbracelet/glamour""github.com/common-nighthawk/go-figure"gpt3 "github.com/sashabaranov/go-gpt3")func main() {// 获取 OpenAI API KeyapiKey := 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 stringfor {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.ContentmdOutput, 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 的学习实践探讨,请关注这个订阅号或者点击阅读原文了解极客书房最新动态。

关键词: 系统环境 这是一个 可以使用

相关阅读