🤖

OpenAIのChat Completion APIを使って、ChatGPTライクに会話ができるSlackBotを作る

2023/03/02に公開

以前の記事では、GPT3 の Completion API を使って Chat GPT ライクな Slack Bot を作っていましたが、 2023/03/02(日本時間)ついに GPT-3.5-turbo を使う Chat Completion API が公開されました 🎉

早速そちらを使って Slack Bot を作ってみました。

https://openai.com/blog/introducing-chatgpt-and-whisper-apis

🤖 作ったもの

Chat GPT のように前後の会話を考慮して回答してくれる Slack Bot です。

🛠️ 実装

Slack の API でスレッドの内容を取得し、それを Chat Completion API に渡すことで Chat GPT のように前後の会話を考慮した回答を返してくれます。

以下 Slack のBolt フレームワーク をつかった場合の実装例です。

export const useReplyEvent = (app: App) => {
  app.event("message", async ({ event, client, logger }) => {
    const { thread_ts: threadTs, bot_id: botId, text } = event as any;
    // botの返信またはスレッドのメッセージでなければ何もしない
    if (botId || !threadTs) {
      return;
    }

    // スレッドのメッセージを取得
    const threadMessagesResponse = await client.conversations.replies({
      channel: event.channel,
      ts: threadTs,
    });
    const messages = threadMessagesResponse.messages?.sort(
      (a, b) => Number(a.ts) - Number(b.ts)
    );

    // GPT Botへの返信でなければ何もしない
    if (!(messages![0].text?.includes(GPT_BOT_NAME) && messages![0].bot_id)) {
      return;
    }

    try {
      // token数の制限を回避するため、最大20件のメッセージで区切る
      const prevMessages =
        messages!.slice(1).slice(-20).map(m => {
          const role = m.bot_id ? ChatCompletionRequestMessageRoleEnum.Assistant : ChatCompletionRequestMessageRoleEnum.User
          return {role: role, content: m.text as string}
        })
      });

      const configuration = new Configuration({
        apiKey: config.openai.key,
      });
      const openAIClient = new OpenAIApi(configuration);

      // Chat Completion API を呼び出す
      const response = await openAIClient.createChatCompletion({
        model: "gpt-3.5-turbo",
        // 以下で会話部分を組み立てる
        messages: [
          {
            role: ChatCompletionRequestMessageRoleEnum.System,
            content: CHAT_GPT_SYSTEM_PROMPT,
          },
          ...prevMessages,
          {
            role: ChatCompletionRequestMessageRoleEnum.User,
            content: text as string,
          },
        ],
        top_p: 0.5,
        frequency_penalty: 0.5,
      });
      const message = response.data.choices[0].message?.content || "";

      // 回答メッセージを投稿する
      if (message) {
        await postAsGptBot({
          client,
          channel: event.channel,
          threadTs,
          text: message,
        });
      } else {
        throw new Error("message is empty");
      }
    } catch (e) {
      logger.error(e);
      await postAsGptBot({
        client,
        channel: event.channel,
        threadTs,
        text: "大変申し訳ございません。エラーです。別スレッドでやり直してください。",
      });
    }
  });
};

Chat Completion API に渡すメッセージにはrolecontentの 2 つのプロパティがあります。
それを Slack Bot 側と、ユーザー側で適切に分けて渡すことが重要です。

[
  { "role": "system", "content": "You are a helpful assistant." },
  { "role": "user", "content": "Who won the world series in 2020?" },
  { "role": "assistant", "content": "The Los Angeles Dodgers won the World" },
  { "role": "user", "content": "Where was it played?" }
]

https://platform.openai.com/docs/guides/chat/introduction

また API へ最初に渡す system ロールの prompt の部分も大切です。
今回は Slack の formatting を考慮して返答してくれるように以下のような Prompt を渡しています。

export const CHAT_GPT_SYSTEM_PROMPT = `
You are an excellent AI assistant Slack Bot.
Please output your response message according to following format.

- bold: "*bold*"
- italic: "_italic_"
- strikethrough: "~strikethrough~"
- code: " \`code\` "
- link: "<https://slack.com|link text>"
- block: "\`\`\` code block \`\`\`"
- bulleted list: "* item1"

Be sure to include a space before and after the single quote in the sentence.
ex) word\`code\`word -> word \`code\` word

Let's begin.
`;

上記のコードを実際に実装した PR はこちらです。

以前記事で紹介した tell-me-bot という便利な質問回答 Bot にチャット機能を追加しています。

https://zenn.dev/ryo_kawamata/articles/tell-me-bot-slack-app

おわりに

Slack で Chat GPT が使えるのは最高に便利!
Open AI の API は今後も進化していくと思うので、楽しみです。

Discussion