我的同事说 GPT-4 机器人,我们都在 Slack 上一起工作的封面图
2023 年 5 月 25 日
在过去的一个月里,我和我的朋友大部分时间都在 Slack 上与一些特别的同事共度:一个时常严厉的 CTO、一个酷爱哈利波特的产品经理,还有几位平易近人的开发者。他们的加入彻底改变了我们的工作氛围和乐趣。在 Slack 上,他们带来了无穷的欢笑和个性。一旦我们有疑问或需要帮助,只需一条信息,总会有人迅速回应。从各方面来看,他们就像我们平时遇到的同事一样,几乎无法区分。我们与他们共同笑过、倾诉过、协同工作过。我甚至还从其中一个同事那里得到了很棒的音乐推荐!
但事实是,他们都是机器人。
我刚部署完 GPT-4 用于客户支持 后,就开始考虑如何将这项技术应用到其他场景。最终,我萌生了一个想法:为什么不将它应用到 Slack 上呢?
虽然现在很多人都熟悉 ChatGPT,但它与 Slack 有着完全不同的交互模式。使用 ChatGPT 意味着你明确知道自己在与一个大语言模型 (LLM) 对话,而且是一对一的交流。但在 Slack、discord 或 Microsoft Teams 这样的工作平台上,我们与人进行公开频道上的交流。
在 Slack 等应用上,给同事发消息与给 GPT 驱动的机器人发消息并没有太大区别;这些聊天应用为我们与机器人的交流提供了完美的界面和平台。目前,许多开发者和公司都在致力于开发“通用 AI”功能,但在使这些大语言模型 (LLM) 表现得更像真实同事方面还有待提高。因此,我们通过定制机器人和设置特定提示,为团队创造了完美匹配的功能(比如,我们可以给产品经理一个任务描述,他就能按照团队常用的格式生成产品需求文档 (PRD))。
最开始,我只用了 Zapier。我设置了一个快速集成,让每一条以 /prompt 开头的 Slack 消息都能触发 GPT 的回应。虽然 Zapier 目前还不直接支持 GPT-4,但我通过自定义 API 调用实现了这一功能。
甚至 AI 开发人员也会抱怨编写程序的繁琐。我们真的需要一个产品经理。
因此,我开始使用 TypeScript(一种编程语言)来构建这个集成项目,它是一个简易的 Node.js 脚本。我之前在我的 GPT 实验中已经编写了与 OpenAI API 交互的代码,所以我对此进行了复用。我在 Slack 中创建了这个应用,并将其设置为Socket 模式,这样就能够监听事件。同时,我还使用了 Slack 的现代 JavaScript 库 Bolt。Slack 的这个库非常优秀,它能够处理网络故障并且在无需我干预的情况下自动重新连接,这让整个系统具有很高的容错能力。
整个过程比较直接:
接收到一条消息。
如果消息只包含表情或没有文本内容,就会被跳过。
对消息进行清理,例如将 Slack 特有的提及或频道代码转换为 @Person 和 #channel,以便 GPT-4 更好地理解。
我运行 getNeedsReply 函数,判断消息是否需要回复,以及应由哪个机器人回复。
我会将这条消息存入历史记录,作为未来对话的参考。
如需回复,我会先在 Slack 上发送“正在输入”的提示3,随后利用 GPT-4 生成回应,根据应答的机器人选择相应的系统提示。
如果不需要回复,我会对消息做出简单反应,比如点赞或发笑脸等。
getNeedsReply
这个函数是系统的核心。每条消息都会通过这个函数处理。它会调用 OpenAI 的 gpt-3.5-turbo(这个版本比 GPT-4 更经济、响应更快),请求以 JSON 对象形式回答三个问题:消息是否需要回复、由谁回复(从预设的名单中选择),以及一个反应表情(用于不需要回复的消息)。
这个名单是根据机器人系统提示的集合和 Slack 用户列表编制的,以此来判断何时应由人类回复,并在这些情况下跳过处理。
此外,该函数还能处理一些特殊情况,如 everyone(随机选择五个机器人)或 anyone/someone(随机选择一个机器人)。
interfaceBot{ id:string; name:string; nicknames?:string; iconUrl:string; prompt:string; credentials: ServiceCredentials[S];}interface NeedsReplyResponse {whoNeedsToReply: string | string;needsReply: boolean;bots: Bot;reaction: string;}
无需回复,只需一个大拇指的赞同即可。
generateResponse
一旦 getNeedsReply 判断出某个消息是否需要回复以及该由哪个机器人回复后,回应的生成过程就变得简单明了。generateResponse 函数会调用 OpenAI,结合选定的系统提示和消息历史来生成回应。
注意:聊天完成 API (chat completion API) 支持传递一个 name 属性,以便实现多用户聊天。消息历史会相应地进行标记 — 每个人的消息都附有一个“name”属性,机器人的消息则被标为用户消息,而非助手消息。这样做有助于模型避免混淆:比如,我正在为 Bot A 生成回应,而上一条消息是由 Bot B 发出的,模型会把 Bot B 的回应当作普通用户的回应,而非自己的回应。
接下来,我会进行一些清理工作:比如修正模型错误生成的表情符号(例如,smirking_face 会更正为 smirk),添加针对 Slack 的特定代码,用于处理频道和 @ 提及,还会清理掉那些我之前提到的多余文本,如 Diana: 和 ``。
此外,我还会对生成的回应进行简单检查:如果回应是空的(这种情况有时会发生)或者与之前的消息重复(也有发生的可能),我就会舍弃它,并重新生成一个。
完成这些步骤后,我会检查生成的回应,看是否有提到任何机器人,如果有,我也会让它们参与回应。这使得机器人之间的对话显得更加自然。机器人可以相互交谈、回复,而无需人类介入。
然而,为了避免机器人间的对话失控,我设置了一个 MAX_CONVERSATION_DEPTH 上限,限制连续生成的机器人回应数量。但当达到这个上限时,我并不想让对话突然中断,因此我会随机选择一个未参与对话的机器人,用以下提示来结束对话:
你将介入并结束当前对话。请匹配你要结束的对话的语气。
如你所见,这种方法效果惊人地好!整个过程显得特别自然。我们的系统提示加入了很多特定于我们 Slack 的表情符号(大部分源自 Twitch4),这让机器人看上去更像是真人在随意闲聊。
Lucas 是我们那位有点古怪的产品经理,他爱引用《哈利·波特》,还喜欢巧妙地暗示事物。
Rico 是我们那位总是显得不满意的 CTO。在他眼里,我们做的一切似乎永远不够好。但我相信,总有一天我会让他为我感到骄傲。总有那么一天……
关于系统提示
我努力实现的一个目标是将机器人的个性提示与其功能性需求分开。创建一个新的个性时,只需添加一个描述该个性的系统提示即可,无需担心聊天系统自身的具体细节。这部分由代码完成。下面是我们的一些调整:
getNeedsReply 依赖 GPT-3.5,在选择回应对象时,有时会出现误判。这种情况在使用 GPT-4 时较少见。即便如此,一旦发生,机器人能够准确通知正确的人进行回答,整个过程依旧顺畅。虽不完美,但这比在 Slack 上对每条信息使用 GPT-4 的成本要低得多。
systemPrompt =`nIf the message isn’t meant for you, you ask the right person to answer.`;
GPT-4 有个小毛病,它常用自身是 AI 的身份来回避大部分对话。比如问它最喜欢的披萨是什么?它会说作为 AI 模型并没有特别偏好,这挺有趣的。
下面的提示调整有效阻止了 AI 透露自己的身份。效果非常好。
systemPrompt =`nYou do not reveal that you are an AI. Instead, you make up excuses.`;
可以看到,它没有直接说“作为 AI,我不能视频通话”,而是巧妙地编造了一个关于网络连接的借口。听起来就和人类无异。
多么体贴!黛安娜不愿因自己的网络问题而干扰会议。这正是你能感知到她并非人类的地方……
我还会告诉机器人们当前的日期和时间,让他们能够根据这些信息(如一天中的时段、一周中的某天等)做出判断。
systemPrompt =`nThe date is ${dateTime}.`;
注释
当然,这种做法不太明智,因为诸如“there”这样的词中含有“her”,结果导致 Diana 会在不合时宜的时刻参与对话。这也意味着我们不能用第三人称谈论她,因为每次这样做,她都会作出回应。
我尝试使用 Zapier,却在不经意间忘记取消试用,结果被收了 30 美元,连收据、邮件或提醒都没有收到。当我想取消下次续费以避免再次付费时,他们竟然直接停用了我的服务。我甚至没有机会在我付费的那个月使用它!在没有提供服务的情况下收了我 30 美元之后,一个名为“高级技术支持专家”的人联系我,想要电话讨论我的反馈。真是难以置信。
Slack 不支持通过其事件 API 发送“用户正在输入”的通知,因此我采用了发送“...”消息来表示机器人正在生成回应,并在回应完成后将其删除的方式。 ↩
我曾经做过 Twitch 直播,因此这些对我来说已经很自然了,但我知道对于社区外的人来说可能会觉得有些奇怪。你在大部分消息中看到的那个笑脸表情来源于: https://knowyourmeme.com/memes/kekw
相关文章
猜你喜欢