用ChatGPT API和Node.js创建一个CLI聊天机器人

672 阅读8分钟

ChatGPT已经风靡全球,本周,OpenAI发布了ChatGPT的API玩ChatGPT,但真正使用这些新功能的最好方法是尝试用它来构建一些东西。随着API的出现,现在就是这个时候了。

这是受Greg Baugues用16行Python实现聊天机器人命令行界面(CLI)的启发。我想我应该先尝试建立同样的聊天工具,但使用JavaScript。

(结果发现Ricky Robinett也有这个想法,并在这里发表了他的机器人代码。我很高兴地看到这两种实现方式是如此的相似!)

代码

事实证明,Node.js需要比Python更多的代码来处理命令行输入,所以Greg的版本是16行,而我的是31行。在建立了这个小机器人之后,我对用这个API建立的潜力不无兴奋。

下面是完整的代码。我将进一步解释它在做什么。

import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output, env } from "node:process";
import { Configuration, OpenAIApi } from "openai";

const configuration = new Configuration({ apiKey: env.OPENAI_API_KEY });
const openai = new OpenAIApi(configuration);
const readline = createInterface({ input, output });

const chatbotType = await readline.question(
  "What type of chatbot would you like to create? "
);
const messages = [{ role: "system", content: chatbotType }];
let userInput = await readline.question("Say hello to your new assistant.\n\n");

while (userInput !== ".exit") {
  messages.push({ role: "user", content: userInput });
  try {
    const response = await openai.createChatCompletion({
      messages,
      model: "gpt-3.5-turbo",
    });

    const botMessage = response.data.choices[0].message;
    if (botMessage) {
      messages.push(botMessage);
      userInput = await readline.question("\n" + botMessage.content + "\n\n");
    } else {
      userInput = await readline.question("\nNo response, try asking again\n");
    }
  } catch (error) {
    console.log(error.message);
    userInput = await readline.question("\nSomething went wrong, try asking again\n");
  }
}

readline.close();

当你运行这段代码时,它看起来像这样:

让我们来看看它是如何工作的,以及你如何建立你自己的。

建立一个聊天机器人

你需要一个OpenAI平台账户来与ChatGPT API互动。一旦你注册了,从你的账户仪表板上创建一个API密钥。

只要你安装了Node.js,你唯一需要的东西就是openai Node.js模块。

让我们开始一个Node.js项目并创建这个CLI应用程序。首先为该项目创建一个目录,进入该目录,并用npm初始化它:

mkdir chatgpt-cli
cd chatgpt-cli
npm init --yes

openai 模块作为一个依赖项安装:

npm install openai

打开package.json ,在配置中添加键"type": "module" ,这样我们就可以将其构建为ES模块,这将使我们能够使用顶层等待。

创建一个名为index.js 的文件并在你的编辑器中打开它。

与OpenAI的API进行交互

代码有两个部分:处理命令行的输入和输出以及处理OpenAI API。让我们先来看看API是如何工作的。

首先我们从openai 模块中导入两个对象,ConfigurationOpenAIApiConfiguration 类将被用来创建一个持有API密钥的配置,然后你可以使用该配置来创建一个OpenAIApi 客户端。

import { env } from "node:process";
import { Configuration, OpenAIApi } from "openai";

const configuration = new Configuration({ apiKey: env.OPENAI_API_KEY });
const openai = new OpenAIApi(configuration);

在这种情况下,我们将在环境中存储API密钥,并使用env.OPENAI_API_KEY

为了与API互动,我们现在使用OpenAI客户端为我们创建聊天完成。OpenAI的文本生成模型实际上并不与你交谈,而是用来接受输入,并在输入之后想出听起来合理的文本,即完成度。在ChatGPT中,该模型被配置为接收一个信息列表,然后为对话提供一个完成语。这个系统中的信息可以来自三个不同的实体之一,"系统"、"用户 "和 "助手"。"助手 "是ChatGPT本身,"用户 "是互动的人,而系统允许程序(或用户,正如我们在这个例子中看到的)提供指令,定义助手的行为方式。改变系统对助手行为方式的提示是最有趣的玩法之一,可以让你创建不同类型的助手。

有了我们的openai 对象的上述配置,我们可以创建消息来发送给助手,并要求像这样的回应:

const messages = [
  { role: "system", content: "You are a helpful assistant" },
  { role: "user", content: "Can you suggest somewhere to eat in the centre of London?" }
];
const response = await openai.createChatCompletion({
  messages,
  model: "gpt-3.5-turbo",
});
console.log(response.data.choices[0].message);
// => "Of course! London is known for its diverse and delicious food scene..."

随着对话的进行,我们可以将用户的问题和助手的回应添加到消息数组中,我们将其与每个请求一起发送。这给了机器人对话的历史,它可以在此基础上建立进一步的答案。

为了创建CLI,我们只需要将其与终端的用户输入连接起来。

与终端互动

Node.js提供了Readline模块,使我们可以很容易地接收输入并将输出写入流中。为了与终端一起工作,这些流将是stdinstdout

我们可以从node:process 模块中导入stdinstdout ,将它们重命名为inputoutput ,以使它们更容易与Readline一起使用。我们还可以从createInterface 中导入函数。node:readline

import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";

然后我们把inputoutput 流传给createInterface ,这样我们就有了一个对象,我们可以用它来向输出端写和从输入端读,所有这些都通过question 函数来实现:

const readline = createInterface({ input, output });

const chatbotType = await readline.question(
  "What type of chatbot would you like to create? "
);

上面的代码连接了输入和输出流。然后,readline 对象被用来将问题发布到输出端并返回一个承诺。当用户通过向终端写问题并按下返回键进行回复时,该承诺就会以用户写的文本进行解析。

完成CLI

有了这两个部分,我们就可以编写所有的代码了。创建一个名为index.js 的新文件,并输入以下代码。

我们从上面描述的导入开始:

import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output, env } from "node:process";
import { Configuration, OpenAIApi } from "openai";

然后我们初始化API客户端和Readline模块:

const configuration = new Configuration({ apiKey: env.OPENAI_API_KEY });
const openai = new OpenAIApi(configuration);
const readline = createInterface({ input, output });

接下来,我们向用户提出第一个问题:"你想创建什么类型的聊天机器人?"我们将使用这个答案在一个新的信息数组中创建一个 "服务 "信息,随着对话的进行,我们将继续添加这个信息。

const chatbotType = await readline.question(
  "What type of chatbot would you like to create? "
);
const messages = [{ role: "system", content: chatbotType }];

然后我们提示用户开始与聊天机器人互动,并启动一个循环,说当用户的输入不等于字符串".exit "时继续将该输入发送到API。如果用户输入了".exit",程序就会结束,就像在Node.js的REPL中一样。

let userInput = await readline.question("Say hello to your new assistant.\n\n");

while (userInput !== ".exit") {
  // loop
}

readline.close();

在循环中,我们将userInput ,作为 "用户 "消息添加到消息数组中。然后,在一个try/catch块中,将其发送到OpenAI的API。我们将模型设置为 "gpt-3.5-turbo",这是ChatGPT的基础名称。

当我们从API得到一个响应时,我们从response.data.choices 数组中得到消息。如果有消息,我们将其作为 "助理 "消息存储在消息数组中,并将其输出给用户,等待他们再次使用readline输入。如果API的响应中没有消息,我们就提醒用户并等待用户的进一步输入。最后,如果向API发出请求时出现错误,我们会捕捉错误,记录信息,并告诉用户再试一次。

while (userInput !== ".exit") {
  messages.push({ role: "user", content: userInput });
  try {
    const response = await openai.createChatCompletion({
      messages,
      model: "gpt-3.5-turbo",
    });

    const botMessage = response.data.choices[0].message;
    if (botMessage) {
      messages.push(botMessage);
      userInput = await readline.question("\n" + botMessage.content + "\n\n");
    } else {
      userInput = await readline.question("\nNo response, try asking again\n");
    }
  } catch (error) {
    console.log(error.message);
    userInput = await readline.question(
      "\nSomething went wrong, try asking again\n"
    );
  }
}

把这些都放在一起,你就有了你的助手。完整的代码在这篇文章的顶部或GitHub上。

现在你可以通过在命令行上将你的OpenAI API密钥作为环境传递给它来运行这个助手:

OPENAI_API_KEY=YOUR_API_KEY node index.js

这将开始你与助手的互动,首先是它询问你想要什么样的助手。一旦你声明了这一点,你就可以开始和它聊天了。

实验帮助我们理解

就个人而言,我实际上不确定ChatGPT有多大用处。它显然令人印象深刻;它返回的文本读起来就像人写的一样,这一点令人难以置信。然而,它返回的内容不一定是正确的,无论它如何自信地展示这些内容。

对ChatGPT进行实验是我们尝试了解它有什么用处的唯一方法,因此建立这样一个简单的聊天机器人给了我们实验的依据。了解到系统命令可以赋予机器人不同的个性,并使其以不同的方式做出反应是非常有趣的。

例如,你可能听说过,你可以要求ChatGPT帮助你编程,但你也可以指定一个JSON结构,也可以有效地把它作为一个API。但当你进行实验时,你很可能会发现它不应该是一个信息API,而更可能是你可以用来理解你的自然文本并把它变成一个JSON对象。对我来说,这是令人兴奋的,因为这意味着ChatGPT可以帮助创建更自然的语音助手,比现有的作物更好地从语音中翻译意义,因为现有的作物期望以更准确的方式发出命令。我仍然要对这个想法进行实验,有了这个工具,我就有了这个机会。

这仅仅是个开始

如果对这项技术进行实验是我们了解我们可以用它建造什么以及我们应该或不应该用它建造什么的重要事情,那么使它更容易实验就是下一个目标。我的下一个目标是扩展这个工具,使它能够保存、互动和编辑多个助手,这样你就可以继续使用它们,并随着时间的推移改进它们。

同时,你可以在GitHub上查看这第一个助手的完整代码,并关注该repo以了解改进情况。