通过在Slack中构建GPT聊天机器人,我们如何提高了我们组织内的人工智能使用率

如何让 Slack 更智能 — 将 GPT 融入你的日常工作流程。

ChatGPT中文站

你还记得ChatGPT发布的时候吗?我们的工程团队立刻被吸引了:我们开始看到这个新时代以人工智能为驱动的开发的巨大潜力。

在将GPT纳入我们的开发工作流程后,我们的目标变成了为整个团队开发一款AI助手。我们自问:如何确保非技术人员能够以无缝和便利的方式享受到人工智能的惊人好处呢?

我们在日常工作流程中已经使用了数十种工具。因此,购买ChatGPT高级订阅并给每个人访问GPT-4的权限不是一个选择,原因有两个:

  1. 成本:你需要为每个团队成员支付订阅费,即使他们很少使用,这非常不划算。
  2. 开销:由于人们不想开始使用另一个工具,并且它无法与他们的日常工作流程融合,因此使用率将很低。

因此,我们探索了一种替代方案——在Slack上创建我们自己的GPT聊天机器人。通过利用OpenAI API以及每个人已经广泛使用Slack的事实,这被证明是我们最具可扩展性、用户友好和成本效益的解决方案。

在本文中,你将学习如何通过4个步骤构建自己的GPT驱动的聊天机器人。

它是如何工作的?

我们的解决方案完全集成到Slack中。您可以与机器人开始对话,它将会在一个线程中回复。回复是实时推送的,因此您不必等待整个回应完成。线程的上下文被保存,而且Slack已经提供了所有标准的聊天功能,如历史记录、搜索和语法格式化。

为了更好地说明它的工作原理,请查看与我们的 Slack 机器人进行互动的快速演示。

ChatGPT中文站
Visual Demo of Slack OpenAI Bot

接下来,让我们深入了解如何为Slack创建一个GPT Bot。

1. 先决条件和技术栈

开始之前,请确保您拥有以下内容:

  1. 在您的 Slack 工作空间上部署应用程序的能力
  2. 访问Open AI的API

您可以使用许多不同的技术栈构建基于GPT的Slack Bot,这里仅提供一个我们使用的示例:

  1. Node.js后端从Slack应用接收用户的提示,并向Open AI的API发出请求,然后返回到Slack。
  2. 一个用于持久化聊天上下文的数据库(我们使用Redis),以便您可以构建聊天功能。

2. 创建一个 Slack 应用

现在让我们继续在Slack中创建我们自定义的应用程序:

  1. 打开Slack应用面板(https://api.slack.com/apps/)。
  2. 点击“创建新应用程序”
  3. 从清单创建
  4. 您可以使用下面的模板🔽
  5. 您需要在第28行适应字段“request_url”,并将其设置为您的API端点的URL,该端点将处理Slack请求。
  6. 查看设置,然后点击“创建”
  7. 点击“将应用安装到工作区”
  8. 在完成这些操作之后,你应该可以在你的 Slack 工作区中搜索你刚刚创建的应用程序的名称(根据我们的模板为 "GPT-Bot")。

在打开“GPT-Bot”之后,注意到它显示“无法发送消息到此应用”。为了修复这个问题,再次打开你在Slack Apps Panel中创建的应用,然后点击侧边栏中的“Features > App Home”。

您需要激活“允许用户从消息选项卡发送斜杠命令和消息”的切换按钮。

一旦您刷新了Slack工作区选项卡,您现在应该能够发送消息。现在,在“GPT-Bot”内发送消息应该会触发您在此步骤的3a)中定义的`request_url`的请求。

3. 连接您的后端

您将需要一个API端点,它将:

  1. 从用户接收输入
  2. 验证该请求是否有效且来自 Slack。
  3. 将提示发送到OpenAI API聊天完成的流模式中
  4. 使用Slack SDK的chat.update方法将来自OpenAI的更新结果每秒发送回Slack。
  5. 这样AI的答案将会持续出现,就像在ChatGPT中一样,就像真正在打字一样。

让我们看一些示例代码,看看我们如何在Express.js(Typescript)中实现这个。

➡️你可以在这里找到完整的代码⬅️

让我们逐步拆解实现步骤,并使用一些稍微简化的代码!

首先,您将设置与Slack和OpenAI API进行交互的服务:

class SlackService {
private _messageId: string;
private _threadId: string;
private _userId: string;

constructor(
private readonly _client: WebClient,
private readonly _openai: OpenAI,
) {
this._messageId = '';
this._threadId = '';
this._userId = '';
}

public async receiveBotMessage(event: SlackEvent) {
const res = await this._client.chat.postMessage({
channel: event.channel,
thread_ts: event.event_ts,
text: '',
});

this._threadId = event.thread_ts || event.event_ts;
this._userId = event.user;

this._messageId = res.ts;

await this._getChatCompletion(event);
}

private async _getChatCompletion(event: SlackEvent): Promise<OpenAI.Chat.ChatCompletionMessageParam[]> {
let slackMessage = '';

const stream = await this._openai.chat.completions.create({
model: 'gpt-4',
messages: [
{role: 'system', content: 'You are a helpful assistant.'},
{role: 'user', content: event.text},
],
stream: true,
});

for await (const chunk of stream) {
slackMessage += chunk.choices[0].delta.content ?? '';

await this._client.chat.update({
channel: event.channel,
text: slackMessage,
ts: this._messageId,
});
}

return [
{role: 'user', content: event.text},
{role: 'assistant', content: slackMessage},
];
}
}

为了设置Rest API端点,您需要一个函数来验证来自Slack的请求。

/** https://github.com/slackapi/bolt-js/blob/main/src/receivers/verify-request.ts */
export function verifySlackRequestMiddleware(req: express.Request, _, next: express.NextFunction) {
const requestTimestampSec = req.headers['x-slack-request-timestamp'] as unknown as number;
const signature = req.headers['x-slack-signature'] as string;

if (!requestTimestampSec) {
throw new Error(`x-slack-request-timestamp header is missing`);
} else if (isNaN(requestTimestampSec)) {
throw new Error(`x-slack-request-timestamp header is not a number`);
}

const [signatureVersion, signatureHash] = signature?.split('=') ?? ['', ''];
if (signatureVersion !== 'v0') {
throw new Error('unknown signature version');
}

const hmac = createHmac('sha256', process.env.SLACK_SIGNING_SECRET);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hmac.update(`${signatureVersion}:${requestTimestampSec}:${(req as any).rawBody}`);
const ourSignatureHash = hmac.digest('hex');
if (!signatureHash || !tsscmp(signatureHash, ourSignatureHash)) {
throw new Error(`Slack: signature mismatch`);
}

return next();
}

最后,您将所有内容连在一起,以接收包括上述中间件的API请求。

const app = express();

app.post('/slack', verifySlackRequestMiddleware, async (req: SlackRequest, res: express.Response) => {
// Verify to Slack that you own this URL
if (req.body.challenge) {
return res.status(200).json({success: true, challenge: req.body.challenge});
}

const event = req.body.event;
if (event.user === process.env.SLACK_BOT_USER_ID || event.subtype === 'message_changed') {
// This is a message from our bot, which we ignore to avoid loops
return res.status(200).json({success: true});
}

/**
* No `await` here as the API needs to respond
* fast, otherwise Slack will think the request
* failed and will retry it.
* */
new SlackService(
new WebClient(process.env.SLACK_BOT_TOKEN),
new OpenAI({apiKey: process.env.OPENAI_API_KEY}),
).receiveBotMessage(event);

return res.status(200).json({success: true});
});

const port = 3000;
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

4. 添加持久性

关于Slack的一大优点是,您的AI聊天历史已经在开箱即用中包含。为了确保AI机器人能够在线程级别上记住您提示的上下文,您需要在后端添加一些持久层,在其中存储一定时间的消息。最简单的解决方案是在内存中存储上下文一段时间。

在Lingvano,我们使用Redis数据库来存储聊天记录,保存48小时。这样,用户可以回来继续在特定的对话中写作,并且AI会保存其所有上下文,非常方便!

你的想法很重要

这是我们如何成功提升整个组织对人工智能的使用率的方式!在这篇文章中,您学到了:

  1. 创建一个由GPT驱动的聊天机器人所需要的东西
  2. 如何设置自定义Slack应用
  3. 如何将您的Slack应用程序的消息连接到后端。
  4. 如何为您的AI机器人添加持久性以保留上下文

你是否渴望自己构建一个由GPT驱动的聊天机器人?让我们知道你的想法吧!如果社区有足够的兴趣,我们正在考虑将这个逻辑捆绑到一个NPM包中。

Curious about our app? You can check out our production version on Web, iOS, and Android. 对我们的应用感到好奇吗?你可以在Web、iOS和Android上查看我们的正式版本。

随意浏览我们的公共仓库和其他项目,我们在其中分享了我们的Web、iOS和Android React Native Expo应用的技术设置和基础设施的一些细节。

你关于我们的设置有什么问题或者对改进有什么建议吗?请在下方留言!记得关注我们,以获取更多关于我们项目和开发者日常工作的信息。

我们很高兴在Twitter、Instagram或LinkedIn上进行连接。而且,我们一直在招聘。加入我们吧!🚀

2023-10-20 17:18:03 AI中文站翻译自原文