🤖

100 行で作る Firebase Genkit の Slack ボットアプリ

2024/08/19に公開

生成 AI を学び始めたエンジニアにとって、次の課題は「実際にどうやって実用的なアプリを作るか?」だと思います。今回のテーマの Slack ボットアプリはその 1 つの実用例です。多くのエンジニアが Slack を普段の仕事で使っており、コミュニケーションツールとして慣れ親しんでいることを考えると、Slack ボットという形態でアプリを提供することは生成 AI の用途として自然なものです。しかも、100 行以内で!

Firebase Genkit の説明や Firebase Functions へのデプロイなどは以前のブログで詳細に説明しているので、今回は省略します。

https://medium.com/@yukinagae/your-first-guide-to-getting-started-with-firebase-genkit-6948d88e8a92
https://medium.com/@yukinagae/deploying-your-firebase-genkit-application-with-firebase-functions-99c7d0044964

今回のブログ記事では、主に Firebase Genkit で Slack ボットアプリを作る際の手順や注意点について書きます。

最終的には以下のように、slack ボットにメンションすると回答してくれるアプリが作れます。

slack

コードや詳細手順は以下のレポジトリを見てください。

https://github.com/yukinagae/genkit-firebase-functions-slack-bolt-sample

事前にローカルに git clone しておいてください。このコードをそのまま使えばハマることなく Slack ボットアプリが作れるはずです。

$ git clone https://github.com/yukinagae/genkit-firebase-functions-slack-bolt-sample.git

コードの説明

見るべきコードは functions/src/index.ts だけです。全体として 100 行程度ですが、import や設定などのコードを除外すると、主な処理は以下のみです。

// Flow definition to answer a question
const answerFlow = ai.defineFlow(
  {
    name: "answerFlow",
    inputSchema: z.string(),
    outputSchema: z.string(),
  },
  async (question: string) => {
    const llmResponse = await ai.generate({
      prompt: `You are a helpful AI assistant. You are asked: ${question}`,
      model: gpt4o, // Specify the model to use for generation
      tools: [],
      config: {
        temperature: 1, // Set the creativity/variation of the response
      },
    });
    return llmResponse.text;
  }
);

// Create a slack receiver
function createReceiver() {
  const receiver = new ExpressReceiver({
    signingSecret: process.env.SLACK_SIGNING_SECRET || "",
    endpoints: "/events",
    processBeforeResponse: true,
  });

  const app = new App({
    receiver: receiver,
    token: process.env.SLACK_BOT_TOKEN,
    processBeforeResponse: true,
  });

  app.event("app_mention", async ({ event, context, client, say }) => {
    const { bot_id: botId, text: rawInput, channel } = event;
    const { retryNum } = context;
    const ts = event.thread_ts || event.ts;

    if (retryNum) return; // skip if retry
    if (botId) return; // skip if bot mentions itself

    // thinking...
    const botMessage = await say({
      thread_ts: ts,
      text: "typing...",
    });
    if (!botMessage.ts) return; // skip if failed to send message

    const input = rawInput.replace(/<@.*?>/, "").trim(); // delete mention
    const answer = await answerFlow(input); // run the flow to get the answer

    await client.chat.update({
      channel,
      ts: botMessage.ts as string,
      text: answer,
    });
  });
  return receiver;
}

export const slack = onRequest(
  { secrets: ["OPENAI_API_KEY", "SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"] },
  async (req, res) => {
    return createReceiver().app(req, res);
  }
);

では、実際に動かしてみましょう!

事前セットアップ

以下がインストールされていることを前提としています。

  • Node.js version 22 or later
  • npm
  • Genkit
  • Firebase CLI
  • ngrok

また、事前に Firebase Project を作っておいてください。Firebase Functions を使うので、Blaze プラン(有料)に変更しておくのも忘れずに。

Slack ボットアプリを作る

以下の手順で Slack の管理画面からアプリを作ります。

Slack - Your Apps にアクセスし、 manifest ベースでアプリを作ります。もちろん、スクラッチからでも作れますが、manifest から作るのが楽です。

以下の JSON の [your_app_name] のみを置き換えて、それをコピペするだけで作れます。"request_url": "http://dummy/events", 部分は後で置き換えますが、初期値としてダミーを設定しているので、このままで大丈夫です。

{
  "display_information": {
    "name": "[your_app_name]"
  },
  "features": {
    "bot_user": {
      "display_name": "[your_app_name]",
      "always_online": true
    }
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "app_mentions:read",
        "channels:history",
        "chat:write",
        "files:read"
      ]
    }
  },
  "settings": {
    "event_subscriptions": {
      "request_url": "http://dummy/events",
      "bot_events": ["app_mention"]
    },
    "org_deploy_enabled": false,
    "socket_mode_enabled": false,
    "token_rotation_enabled": false
  }
}

あなたのワークスペースに Slack アプリをインストールしたあとに

  • Bot User OAuth Token(左サイドバーの Features -> OAuth & Permissions にあります)※後の手順の SLACK_BOT_TOKEN として使います。
  • Signing Secret(左サイドバーの Settings -> Basic Information にあります)※後の手順の SLACK_SIGNING_SECRET として使います。

作成した Slack アプリを対象のワークスペース内の任意のチャンネルに招待しておきます。

/invite @[your_app_name]

# アプリ名が `gpt-test` の場合
/invite @gpt-test

ローカル Emulator から Slack ボットアプリに接続

Firebase Functions を使う一つの理由は、Emulator がローカル開発に便利だからです。わざわざデプロイしなくても、Emulator から Slack アプリと接続することで効率的に開発することができます。

まずは functions/.secret.local の秘密情報を適宜置き換えてください。

$ cp -p ./.secret.local.example ./secret.local
$ vim ./secret.local
OPENAI_API_KEY=your_api_key
SLACK_BOT_TOKEN=your_bot_token
SLACK_SIGNING_SECRET=your_signing_secret

あとは Emulator を起動するだけです。

$ npm run emulator
✔  functions[us-central1-slack]: http function initialized (http://127.0.0.1:5001/[your_project_name]/us-central1/slack).

このままだとローカルで起動しているだけで、Slack アプリとは接続されていません。ngrok でローカルの 5001 を公開 URL にポートフォワードします。

$ ngrok http 5001
Forwarding https://[your_ngrok_id].ngrok-free.app -> http://localhost:5001

[your_ngrok_id] は実際には ngrok が自動的に割り振ったランダムな英数字になっているはずです。

この公開 URL https://[your_ngrok_id].ngrok-free.app を Slack の管理画面から設定して、ローカルと Slack アプリを接続します。

Slack 管理画面の Event Subscriptions にアクセスし、Request URL に以下のように入力します。少し待って、 Request URL Verified と表示されれば接続が成功です。Save changes で設定を保存しましょう。

  • https://[your_ngrok_id].ngrok.io/[your_project_name]/us-central1/slack/events

早速 Slack チャンネル上でテストしてみましょう。

@[your_app_name] hello

以下のようにメッセージ返信があれば成功です!

Hello! How can I assist you today?

Firebas Functions にデプロイ

開発時には Emulator と ngrok を使い試行錯誤しますが、実際に運用する際には Firebase Functions にデプロイしてみましょう。

以下 3 つの秘密情報については、それぞれ firebase functions:secrets:set コマンドを使って Firebase Functions に設定します。

  • OPENAI_API_KEY
  • SLACK_BOT_TOKEN
  • SLACK_SIGNING_SECRET

実際にはこんな感じです。

$ firebase functions:secrets:set OPENAI_API_KEY
? Enter a value for OPENAI_API_KEY [input is hidden]
$ firebase functions:secrets:set SLACK_BOT_TOKEN
? Enter a value for SLACK_BOT_TOKEN [input is hidden]
$ firebase functions:secrets:set SLACK_SIGNING_SECRET

設定した秘密情報は以下のように functions:secrets:access コマンドで確認できます。タイポしたり、コピペミスなどもあり得るので、念の為確認しておくとよいでしょう。

$ firebase functions:secrets:access OPENAI_API_KEY
your_api_key
$ firebase functions:secrets:access SLACK_BOT_TOKEN
your_bot_token
$ firebase functions:secrets:access SLACK_SIGNING_SECRET
your_signing_secret

では、デプロイしてみましょう!

$ npm run deploy

デプロイした Firebase Functions の URL と、先程の ngrok の公開 URL を置き換えます。

Slack 管理画面から Event SubscriptionsRequest URL を変更することは一緒ですが、その値を以下のように設定します。

  • https://slack-[your_function_id]-uc.a.run.app/events

[your_function_id] は Firebase 管理画面の Build -> Functions から確認できます。

再度 Slack チャンネル上でボットにメッセージを送ってみましょう!

@[your_app_name] hello

slack

CureApp テックブログ

Discussion