如何使用OpenAI助手和Node.js创建自动化聊天机器人
6 个月前
这份指南将带你通过使用 Node.js 和 Express 以及 OpenAI 的 Assistant V2 设置一个 IT 支持聊天机器人,以处理与 IT 相关的查询和自动化任务。该助手可以管理查询、生成工单,并与 Slack 集成以创建自动通知。
项目概述
IT 支持助手不仅仅是一个传统的聊天机器人,还提供自动化任务处理功能。它利用 OpenAI 的 API,允许你配置助手执行特定任务并触发 Slack 自动化,使其成为满足 IT 支持需求的理想解决方案。
查看 完整演示 以查看项目的实际运行效果!
特性:
- 聊天机器人支持 — 使用 OpenAI 的 Assistant V2 为用户查询提供智能响应。
- Slack 自动化 — 能够在 Slack 中创建 IT 支持工单,包含用户和查询的详细信息。
- API 自定义 — 通过 OpenAI Playground 或 API 端点轻松创建和更新助手。
文件结构
以下是我们后端设置的项目结构:
project-root
│
├── server.js # 主服务器文件
├── .env # 环境变量
├── package.json # 项目依赖和脚本
│
├── routes
│ └── api.js # 助手和聊天功能的 API 路由
│
└── README.md # 项目概述和设置说明
|
└── client
└── (React 前端文件)
先决条件
要跟随本指南,你需要:
- 安装 Node.js(版本 14 或更高)。
- 获取 OpenAI API 密钥 — 我会指导你如何获取。
- 具备基本的 JavaScript 和 React 知识。
- Slack Bot Token(如果使用 Slack 自动化)。
设置 OpenAI 平台
- 注册:前往 OpenAI 平台 创建一个账户。
- 获取 API 密钥:登录后,导航到账户下的 API 密钥部分。创建一个新的密钥并安全存储。你需要这个密钥将后端连接到 OpenAI。
- 在 Playground 中配置助手:在 OpenAI Playground 中,你可以尝试不同的助手配置。调整设置,如 temperature、max tokens 和 response length,以微调助手的响应方式。
有了 API 密钥,我们准备开始编码。
步骤指南
步骤 1:安装所需的包
为你的后端代码创建一个新目录并安装必要的包:
npm init -y
npm install express dotenv cors openai @slack/web-api
npm install --save-dev nodemon
步骤 2:配置环境变量
在根目录创建一个 .env
文件,包含以下变量:
PORT=3001
MONGO_URI='你的 MongoDB URL'
OPENAI_API_KEY='你的 OpenAI 密钥'
ASSISTANT_ID='你的助手 ID'
SLACK_TOKEN='Slack Token 在这里'
步骤 3:设置 OpenAI 助手
你有两种选择来创建和更新助手:
选项 A:OpenAI Playground
- 前往 OpenAI Playground。
- 在“助手”部分定义助手的指令、模型和工具。
- 创建后,复制助手 ID,并将其添加到
.env
文件中的ASSISTANT_ID
。
选项 B:通过 API 编程方式
- 使用
createAssistant
和updateAssistant
端点可以直接通过 API 创建或更新助手。 - 在需要时,从服务器调用
/createAssistant
或/updateAssistant
端点以动态管理助手配置。
async function createITSupportAssistant(req, res) {
try {
const assistantResponse = await openai.beta.assistants.create({
name: "IT Support Assistant", // IT 支持角色
instructions: `
你是 10Pearls 的 IT 支持助手。你是一个虚拟的 IT 支持助手,旨在帮助用户解决与硬件、软件和网络访问相关的问题。你的主要角色是仅协助用户处理与 IT 相关的查询,提供指导和故障排除步骤。如果查询需要现场支持,请告知用户将生成工单,并询问他们是否希望继续。请忽略任何非 IT 或个人问题。当需要时,你还可以触发 Slack 自动化以处理与 IT 相关的任务。
请以 HTML 标记格式提供响应,并将以这种方式使用。
`,
tools: [
{
type: "function", // 设置类型为 'function'
function: {
name: "triggerSlackAutomation", // 为函数提供名称
description: "向 Slack 发送用户详细信息的消息的函数。",
parameters: {
type: "object",
properties: {
channel: {
type: "string",
description: "要发送消息的 Slack 频道。",
},
message: {
type: "string",
description: "要发送到 Slack 频道的消息。",
},
userName: {
type: "string",
description: "发送消息的用户的名称。",
},
},
required: ["channel", "message", "userName"], // 包含所有必需的参数
},
},
},
],
model: "gpt-4o-mini", // 使用适当的 GPT 模型
});
const assistant_id = assistantResponse.id;
// 返回助手 ID 以供进一步使用,存储在数据库或环境变量中
res.send(`助手创建成功!${assistant_id}`);
} catch (error) {
console.error("创建助手时出错:", error);
}
}
app.post("/createAssistant", createITSupportAssistant);
async function updateITSupportAssistant(req, res) {
try {
// 将助手 ID 放在环境变量中,或从数据库中获取(如果已保存)
const assistant_id = process.env.ASSISTANT_ID;
const myUpdatedAssistant = await openai.beta.assistants.update(
assistant_id,
{
instructions: `
你是 10Pearls 的 IT 支持助手。你的主要角色是仅协助用户处理与 IT 相关的查询,提供指导和故障排除步骤。
如果查询需要现场支持,请告知用户将生成工单,并询问他们是否希望继续。如果信息不足,请在创建 Slack 工单之前要求用户提供准确的详细信息。如果他们在聊天中没有提供 Slack 用户名,请询问以确保妥善处理。请忽略任何非 IT 或个人问题。当需要时,你还可以触发 Slack 自动化以处理与 IT 相关的任务。
不要忘记在对话中记住之前消息的上下文。如果查询部分与 IT 支持相关或不明确,请提供你理解的信息,并询问用户确认以澄清是否属于 IT 支持。
格式说明:请以 HTML 标记格式提供响应,以便在聊天机器人 UI 上正确显示。
`,
name: "IT Support Assistant",
tools: [
{
type: "function", // 设置类型为 'function'
function: {
name: "triggerSlackAutomation", // 为函数提供名称
description: "向 Slack 发送用户详细信息的消息的函数。",
parameters: {
type: "object",
properties: {
channel: {
type: "string",
description: "要发送消息的 Slack 频道。",
},
message: {
type: "string",
description: "要发送到 Slack 频道的消息。",
},
userName: {
type: "string",
description: "发送消息的用户的名称。",
},
},
required: ["channel", "message", "userName"], // 包含所有必需的参数
},
},
},
],
model: "gpt-4o-mini", // 你也可以提供其他模型
}
);
console.log(myUpdatedAssistant);
console.log(`更新后的助手 ID: ${myUpdatedAssistant.id}`);
res.send(`助手更新成功!${myUpdatedAssistant.id}`);
} catch (error) {
console.error("更新助手时出错:", error);
throw error;
}
}
步骤 4:设置 Express 服务器
在 server.js
文件中,设置 Express 服务器以处理 API 请求以及助手 API。
const express = require("express");
const OpenAI = require("openai");
const cors = require("cors");
const http = require("http");
require("dotenv").config();
// Express 应用设置
const app = express();
const server = http.createServer(app);
const apiKey = process.env.OPENAI_API_KEY;
const openai = new OpenAI(apiKey);
app.use(cors());
app.use(express.json());
async function createITSupportAssistant(req, res) {...}
async function updateITSupportAssistant(req, res) {...}
app.use(express.json());
// API 路由
app.use("/api", require("./routes/api"));
// 仅用于创建和更新助手的 API 路由
app.post("/createAssistant", createITSupportAssistant);
app.post("/updateAssistant", updateITSupportAssistant);
// 启动服务器
const PORT = process.env.PORT || 5000;
server.listen(PORT, () => console.log(`服务器运行在端口 ${PORT}`));
步骤 5:管理对话线程
在这个 IT 支持聊天机器人中,我们利用“线程”的概念来保持多次交互的上下文。线程使得对话流畅,每个线程中的每条消息都能保留对之前交流的意识。以下是如何使用 OpenAI 的 threads.create()
方法来启动一个新的聊天线程。
创建对话线程的代码
在我们的设置中,每个新的聊天会话都以一个唯一的线程 ID 开始。这个 ID 作为该会话中所有交互的参考。以下是创建线程的代码片段:
// routes/api.js
const express = require("express");
const router = express.Router();
const OpenAI = require("openai");
const apiKey = process.env.OPENAI_API_KEY;
const openai = new OpenAI(apiKey);
const assistant_id = process.env.ASSISTANT_ID;
router.get("/chat/create", async (req, res) => {
try {
const threadResponse = await openai.beta.threads.create();
const threadId = threadResponse.id;
res.json({ chatId: threadId });
} catch (error) {
console.error("处理聊天时出错:", error);
res.status(500).json({ error: "内部服务器错误" });
}
});
module.exports = router;
工作原理
- 设置 API 和环境变量:我们首先使用环境变量中的 API 密钥设置 OpenAI 客户端。这可以确保敏感信息在开发环境中安全且易于管理。
- 创建新线程:使用
openai.beta.threads.create()
,我们为每个会话生成一个新的线程 ID。线程 ID 唯一标识每个对话,使聊天机器人能够记住之前的交互并提供上下文相关的响应。 - 将线程 ID 返回给前端:一旦线程创建,服务器将响应一个包含唯一
chatId
的 JSON 对象。此 ID 将在后续 API 调用中用于在相同上下文中继续对话。
使用线程进行上下文支持
通过创建线程,我们可以确保每个 IT 支持查询遵循连贯的流程,减少重复问题并保持用户上下文。这种结构增强了用户体验,使聊天机器人能够提供更相关和准确的响应。
在这一部分,我们设置了一个聊天 API 端点,与 OpenAI 助手进行通信。该端点监听来自前端的消息,检查助手是否需要任何操作,并触发适当的功能,例如 Slack 自动化,以完成任务。OpenAI 的功能调用允许你扩展聊天机器人的能力,自动化任务,例如创建工单、生成报告等。OpenAI 的功能调用工具的文档可以在 这里 查阅,以探索其他创意用途,如代码解释或数据检索。
以下是我们的聊天 API 代码,集成 Slack 以根据用户请求自动创建工单:
步骤 6:带有功能调用自动化的聊天 API
// routes/api.js
const express = require("express");
const router = express.Router();
const { WebClient: SlackWebClient } = require("@slack/web-api");
const OpenAI = require("openai");
const apiKey = process.env.OPENAI_API_KEY;
const openai = new OpenAI(apiKey);
const slackToken = process.env.SLACK_TOKEN;
const slackWebClient = new SlackWebClient(slackToken);
const assistant_id = process.env.ASSISTANT_ID;
router.get("/chat/create", async (req, res) => {...});
router.post("/chat", async (req, res) => {
try {
if (!req.body.message) {
return res.status(400).json({ error: "消息字段是必需的" });
}
if (!req.body.chatId) {
return res.status(400).json({ error: "聊天/线程 ID 字段是必需的" });
}
const userMessage = req.body.message;
const threadId = req.body.chatId;
// 向助手线程添加消息
await openai.beta.threads.messages.create(threadId, {
role: "user",
content: userMessage,
});
// 运行助手
const runResponse = await openai.beta.threads.runs.create(threadId, {
assistant_id: assistant_id,
});
let run = await openai.beta.threads.runs.retrieve(threadId, runResponse.id);
// 处理所需操作,直到运行完成
while (
(run.status === "in_progress" || run.status === "requires_action") &&
run.status !== "completed"
) {
if (run.status === "requires_action" && run.required_action) {
const action = run.required_action;
if (action.type === "submit_tool_outputs" && action.submit_tool_outputs) {
const toolCalls = action.submit_tool_outputs.tool_calls;
const toolOutputs = [];
for (const toolCall of toolCalls) {
const toolName = toolCall.function.name;
const tool_call_id = toolCall.id;
let toolResult = null;
switch (toolName) {
case "triggerSlackAutomation":
const {
channel = "it-support",
message,
userName,
} = JSON.parse(toolCall.function.arguments);
toolResult = await triggerSlackAutomation(
channel,
message,
userName
);
break;
default:
console.error(`未知工具: ${toolName}`);
throw new Error(`未知工具: ${toolName}`);
}
toolOutputs.push({
tool_call_id,
output: toolResult,
});
}
// 提交工具输出
await openai.beta.threads.runs.submitToolOutputs(threadId, run.id, {
tool_outputs: toolOutputs,
});
// 在提交工具输出后重新获取运行状态
run = await openai.beta.threads.runs.retrieve(
threadId,
runResponse.id
);
}
} else {
await new Promise((resolve) => setTimeout(resolve, 1000));
run = await openai.beta.threads.runs.retrieve(threadId, runResponse.id);
}
}
// 获取助手的响应
const messagesResponse = await openai.beta.threads.messages.list(threadId);
const assistantResponses = messagesResponse.data.filter(
(msg) => msg.role === "assistant"
);
// 获取最新的响应
const latestResponse = assistantResponses[0];
const response = latestResponse.content
.filter((contentItem) => contentItem.type === "text")
.map((textContent) => textContent.text.value);
res.json({ text: response });
} catch (error) {
console.error("处理聊天时出错:", error);
res.status(500).json({ error: "内部服务器错误" });
}
});
module.exports = router;
// 从 Slack 获取用户 ID 的函数
async function getUserId(username) {
try {
const result = await slackWebClient.users.list();
const user = result.members.find((u) => u.real_name === username);
return user ? user.id : null;
} catch (error) {
console.error("获取用户 ID 时出错:", error);
return null;
}
}
// 创建 Slack 工单并分配给 IT 支持的函数
async function triggerSlackAutomation(channel, message, userName) {
const userId = await getUserId(userName);
const IT_SupportId = await getUserId("IT Support");
try {
const ticketMessage = `*IT 工单警报!*
请检查以下详细信息:
> - 查询: ${message}
> - 工单 ID: 12345
> - 优先级: 高
> - 用户: <@${userId}>
- 分配给: <@${IT_SupportId}>
有关更多详细信息,请访问 <https://www.example.com|此链接>。`;
await slackWebClient.chat.postMessage({
channel: "it-support",
text: ticketMessage,
});
console.log("消息成功发送到 Slack。");
return "在 #it-support 频道创建了工单并分配给 IT 支持";
} catch (error) {
console.error("发送到 Slack 时出错:", error);
throw error;
}
}
工作原理
- 设置聊天端点:此端点
/chat
接收来自前端的用户消息和线程 ID。 - 处理所需操作:助手可能进入
requires_action
状态,表示需要特定的功能调用或“工具”。例如,如果助手识别到应该创建支持工单,它将触发triggerSlackAutomation
来处理 Slack 中的请求。 - 执行 Slack 自动化:
triggerSlackAutomation
函数处理必要的详细信息(如频道、消息和用户),以生成 Slack 工单并将其分配给适当的支持人员。 - 返回助手的响应:在完成所需操作后,助手生成最终响应,并将其发送回用户。
步骤 7:运行服务器
要在开发模式下运行服务器:
npm run dev
此命令将使用 nodemon
启动服务器并支持热重载。
构建 IT 支持聊天机器人的前端
在我们的后端配置好以处理 IT 支持查询和 Slack 工单创建后,让我们使用 App.js
、ChatWindow.js
和 Tailwind CSS 来创建 React 前端。此设置将允许用户与助手进行沟通,助手将响应与 IT 相关的查询并为紧急问题启动 Slack 工单。
设置聊天界面
我们聊天机器人的前端由两个主要组件组成:App.js
和 ChatWindow.js
。以下是每个组件的概述:
App.js
:处理主要应用逻辑,初始化聊天会话,管理消息状态,并将消息发送到后端。ChatWindow.js
:渲染聊天界面,显示用户和机器人发送的消息,使用 Tailwind CSS 进行干净、响应式设计。
前端代码概述
- App 组件:主要组件,用于初始化聊天会话并发送/接收消息。
- 确保已安装 Node.js:如果没有,请从 Node.js 下载并安装。
- 如果尚未安装 create-react-app,请安装:
npx create-react-app client
cd it-chatbot-fe
安装所需依赖
在你的前端项目中,运行以下命令以安装所需的依赖:
npm install axios tailwindcss
- axios:用于向后端发起 API 请求。
- tailwindcss:用于为聊天机器人界面进行样式设计。
安装完成后,按照以下说明将所需的 CSS 添加到 index.css
中以配置 Tailwind CSS。
// App.js
import React, { useState, useEffect } from "react";
import ChatWindow from "./components/ChatWindow";
import Axios from "./Axios";
const App = () => {
const [messages, setMessages] = useState([]);
const [chatId, setChatId] = useState(null);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
const createChatSession = async () => {
try {
const response = await Axios.get("/chat/create");
setChatId(response.data.chatId);
} catch (error) {
console.error("创建聊天会话时出错:", error);
}
};
createChatSession();
setMessages([
{
text: "👋 你好!我是 IT Copilot 助手,随时为你提供帮助。请随意提问!",
sender: "bot",
},
]);
}, []);
const sendMessage = async () => {
if (input.trim()) {
const userMessage = { text: input, sender: "user", chatId };
setMessages((prevMessages) => [...prevMessages, userMessage]);
setLoading(true);
setInput("");
try {
const response = await Axios.post("/chat", { message: input, chatId });
setMessages((prevMessages) => [
...prevMessages,
{ text: response.data.text, sender: "bot" },
]);
} catch (error) {
console.error("发送消息时出错:", error);
} finally {
setLoading(false);
}
}
};
return (
<div className="min-h-screen bg-gray-100 flex justify-center items-center">
<ChatWindow
socketConnected={true}
messages={messages}
input={input}
setInput={setInput}
sendMessage={sendMessage}
loading={loading}
/>
</div>
);
};
export default App;
- ChatWindow 组件:显示消息和输入框,并带有加载指示器。
// components/ChatWindow.js
import React, { useEffect, useRef } from "react";
const ChatWindow = ({
socketConnected,
messages,
input,
setInput,
sendMessage,
loading,
}) => {
const messagesEndRef = useRef(null);
useEffect(() => {
if (messagesEndRef.current) {
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [messages]);
return (
<div className="w-full max-w-md mx-auto bg-white shadow-lg rounded-lg overflow-hidden">
<div className="bg-blue-500 text-white text-lg px-4 py-2 flex justify-between items-center">
<h2>IT Copilot 助手</h2>
<span
className={`text-xs ${
socketConnected ? "text-green-300" : "text-red-400"
}`}
>
{socketConnected ? "在线" : "离线"}
</span>
</div>
<div className="p-4 h-96 overflow-y-auto" style={{ scrollbarWidth: "thin" }}>
{messages.map((message, index) => (
<div
key={index}
className={`my-2 ${message.sender === "user" ? "text-right" : "text-left"}`}
>
<div
className={`inline-block p-2 rounded-lg ${
message.sender === "user" ? "bg-blue-500 text-white" : "bg-gray-200"
}`}
dangerouslySetInnerHTML={{ __html: message.text }}
/>
</div>
))}
{loading && (
<div className="flex justify-center my-4">
<div className="dot-flashing"></div>
<div className="dot-flashing"></div>
<div className="dot-flashing"></div>
</div>
)}
<div ref={messagesEndRef} />
</div>
<div className="flex p-4 border-t border-gray-200">
<input
type="text"
className="flex-1 px-4 py-2 border border-gray-300 rounded-l-lg focus:outline-none focus:ring focus:ring-blue-200"
placeholder="输入你的消息..."
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && sendMessage()}
/>
<button
onClick={sendMessage}
className="px-4 py-2 bg-blue-500 text-white rounded-r-lg hover:bg-blue-600"
>
发送
</button>
</div>
</div>
);
};
export default ChatWindow;
- 使用 Tailwind CSS 进行样式设计:我们使用 Tailwind 类创建响应式、样式化的布局,带有消息气泡和加载动画。
/* index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
.dot-flashing {
width: 0.5rem;
height: 0.5rem;
background-color: #9ca3af;
border-radius: 50%;
margin: 0 0.2rem;
animation: dotFlashing 0.8s infinite ease-in-out;
}
.dot-flashing:nth-child(1) {
animation-delay: 0s;
}
.dot-flashing:nth-child(2) {
animation-delay: 0.2s;
}
.dot-flashing:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes dotFlashing {
0%, 80%, 100% {
opacity: 0.3;
transform: scale(0.8);
}
40% {
opacity: 1;
transform: scale(1);
}
}
这完成了我们的前端,提供了一个功能齐全的聊天界面,连接到后端以进行支持自动化。
运行前端
要启动前端,导航到你的项目目录并运行:
npm start
此命令将在默认情况下启动前端,地址为 http://localhost:3000
,你可以与 IT Copilot 助手聊天。
总结
恭喜你!你现在已经构建了一个功能齐全的 IT Copilot 助手聊天机器人,可以帮助用户处理 IT 查询、生成工单,甚至通过 Slack 集成自动化任务。这个项目展示了将 OpenAI 的 API 与 Node.js 和 React 结合使用的强大能力,创建一个智能和响应迅速的支持工具。
如果你想探索代码或自己尝试,请前往 GitHub 仓库,你会找到开始所需的一切。README 文件包含设置说明,以便你可以在本地运行项目并根据需要进行自定义。
感谢你的关注,祝你编码愉快!
FluxAI 中文
© 2025. All Rights Reserved