🤖

Firebase と PaLM で作るお手軽チャットボット開発入門

2023/12/26に公開

こんにちは。
フロントエンドエンジニアの雪村です。

今回は Firebase を使って簡単に開発できる PaLM API チャットボットアプリについてご紹介します。

はじめに

本記事では Firebase の機能の紹介と、実際に Firebase と生成 AI を使ったウェブアプリのアーキテクチャを紹介していきます。

以下のような方を対象として本記事を作成しました。

  • これから Firebase を触ってみようと思っている方
  • 生成 AI が気になるけど、どこから始めればいいか悩んでいる方
  • 特にこれらの状況にあるフロントエンドエンジニアの方!!

ぜひ、これを機に Firebase に興味を持っていただければと思います。

チャットボットの想定機能

今回作るチャットボットは、以下の最低限の機能を実装します。

  • ユーザーの投稿に対して AI が返信するチャットボット機能
  • 特定のユーザーだけが利用できるようにする機能
  • データ容量を抑えるための定期的なデータ削除

こんな感じです。

もう少し噛み砕いて言うと、
ログインして、
login.png

チャットボットと会話します。
chatbot

会話データが増える(= データ容量が増える)と、その分課金が発生する場合があるので会話データは定期的に削除します。

アーキテクチャ

想定機能を満たすために今回考えた構成です。

architecture.png

各プロダクトについて簡単にご紹介します。

  • Firebase Hosting
    ウェブアプリや静的コンテンツのホスティングを提供します。
    今回はウェブアプリのホスティングに使用します。

  • Firebase Authentication
    認証機能を提供します。
    今回は Google アカウントによる認証機能を使用します。

  • Cloud Functions for Firebase
    バックエンドのコードを自動的に実行するサーバーレスフレームワークです。
    Firebase の機能や HTTP リクエストなどによってトリガーされた様々なイベントをトリガーに動作します。
    今回は以下のケースで使用します。

    • 認証時に Blocking Functions を使用して、サインアップ・サインインに対して制限をつけます。
    • チャットデータの保存をトリガーに、Firebase Extensions によって作成されたチャットボット関数を実行します。
  • Firestore
    NoSQL データベースです。スケーリングを気にすることなくブラウザからも簡単にデータを保存できます。
    今回はチャットデータを保存します。
    ユーザーの入力データの保存をトリガーにPaLM API 拡張機能が Vertex AI を通して回答を作成、再度 Firestore に格納するので、そのデータを取得します。

アーキテクチャをご覧の通り、今回の構成ではバックエンドアプリケーションの開発はほとんど行わず、サーバーレスの環境で構築することができます。
アーキテクチャを実現するために、それぞれのプロダクトについて詳細を見ていきましょう。

1. チャットボット機能を用意する(Firebase Extensions)

extensions.png

まず、今回の目玉であるチャットボット機能は Firebase Extensions を使用して簡単に用意します。

Firebase Extensions はまさに時間を節約できるプリパッケージソリューションです。
すでにパッケージ化された機能が複数提供されており、ユーザーはその機能を自プロジェクトにデプロイ可能です。
デプロイ時に、必要な構成が自動で生成されます。

Firebase Extensions でどんな機能が提供されているのかは Firebase Extensions Hub をご覧ください。

Chatbot with PaLM API

Firebase Extensions の中で、今回は PaLM API を使用したチャットボット機能 Chatbot with PaLM API を利用します。
Chatbot with PaLM API はカスタマイズ可能なチャットボットを拡張機能として提供しています。

チャットボットのシステムを実現するフローは、次の3つのステップを踏みます。

chatbot-logic.png

  1. ドキュメントの追加をトリガーにイベントを発火
  2. PaLM API を実行
  3. 回答をドキュメントに追加

指定の key にユーザーの入力値が追加されると、それトリガーとして PaLM API による回答が行われます。

正直、私はこの構成を思いつきませんでした。
この時点で開発のコストが節約できていると言っても良いかもしれません。

2. チャットボットの会話を保存する(Firestore)

firestore.png

会話データは Firestore に保存します。
Firestore のデータの扱いについて少しご紹介します。

データの追加と取得

ウェブアプリから Firestore のデータを追加・取得する方法について簡単にご紹介します。

データの追加

Firestore にデータを保存する場合は addDoc 関数を使用できます。
addDoc 関数で Firestore のどこに保存するかを指定、保存するデータを json 形式で指定することでFirestore にデータを登録することができます。

以下は addDoc を使用してデータを追加する処理の一部を抜粋したものになります。

addDoc.ts
// prompt にチャットボットに応答してほしいデータを入れる想定
// message: ユーザーの入力データ
// createTime: データの登録時間
// expireTime: データの削除時間(後述のデータの定期削除で使用)
// status: Chatbot with PaLM API が回答状況に応じてステータスを更新
await addDoc(collection(db, `users/${uid}/messages`), {
  prompt: message,
  createTime: now,
  expireTime: expireTime,
  status: {
    state: 'SENDING',
  },
});

データの取得

チャットデータの取得についてはいくつか方法がありますが、今回はリアルタイムに情報更新をリッスンできる方法を紹介します。

リアルタイムにデータを取得したい場合、onSnapshot 関数を使用します。Firestore のどこを監視するかを指定するだけで、更新があった場合に onSnapshot 内の処理を実行することができます。ポーリングは不要です。

以下は onSnapshot を使用してデータに格納する処理の一部を抜粋したものになります。

snapshot.ts
onSnapshot(collection(db, `users/${uid}/messages`), (snapshot) => {
  // コレクションに更新があった(チャットボットから返事がきた)ことをトリガーに、チャット表示データを更新
  // id: ドキュメントの id
  // text: ユーザーの入力データ
  // createdAt: データの登録時間
  // response: AI の回答
  // responseStatus: 回答状況のステータス
  const messages = snapshot.docs.map((doc) => {
    return {
      id: doc.id,
      text: doc.data().prompt,
      createdAt: doc.data().createTime,
      response: doc.data().response,
      responseStatus:
        doc.data().status === undefined ? '' : doc.data().status['state'],
    } as Message;
  });

  // 取得したメッセージをセットする
  setMessages(
    messages.sort((a: Message, b: Message) => {
      return a.createdAt < b.createdAt ? -1 : 1;
    })
  );
});

データの保持期間の設定

Firestore には保存データやドキュメントの読み取りなどに対して無料の割り当てが用意されています
もちろん、この無料の割り当てを超えてしまうと課金が発生してしまうので、なるべく費用を抑えるために保存データを定期的に削除しましょう。

Firestore では有効期間(TTL)ポリシーを設定することができます。
有効期間ポリシーを設定することでデータを自動的に削除することができます。バッチ処理を書く必要はありません。

データの削除については指定した key に保存されている日時データを参照します。それだけです。

自動削除の注意点として、指定した日時から 24 ~ 72 時間以内に削除されます。
指定した時間ぴったりに削除される訳ではないため、厳密にデータ保持期間が決まっているようなケースには適しません。

有効期間ポリシーは Google Cloud コンソールから設定可能です。
コレクショングループとタイムスタンプフィールドを設定するだけで、タイムスタンプフィールドに基づく削除スケジュールが設定されます。

3. 認証機能を用意する(Firebase Authentication)

authentication.png

Firebase Authentication はフルマネージドな認証基盤です。
Google アカウントでのログインも簡単に実装することができます。

さらに、Identity Platform にアップグレードすることで従来の Firebase Authentication よりも機能が充実します。

サインイン

Google アカウントを使用したサインインは signInWithPopup を使用します。
この関数が実行されると Google アカウントに対して認証確認のウインドウが開きます。

signIn.ts
// Google アカウントを使用するための認証ポップアップを表示する
signInWithPopup(auth, new GoogleAuthProvider());

サインアウト

サインアウト処理もシンプルです。
1行でサインアウトを実装することができます。

signOut.ts
// auth は初期化した Firebase Authentication の参照
auth.signOut();

Blocking Functions

今回は認証を少しカスタマイズする方法をご紹介します。

Blocking Functions は Firebase Authentication を Identify Platform にアップグレードすることで使用できる機能の一つです。
サインアップ前、サインイン前に発火するイベントを設定することができます。

今回は「特定のドメインのみ」を許可する処理をご紹介します。

サインアップ前

beforeCreated.ts
import {
  HttpsError,
  beforeUserCreated,
} from "firebase-functions/v2/identity";

export const beforeCreated = beforeUserCreated((event) => {
  // event.data にサインアップ時の Google アカウントのデータが入る
  const user = event.data;
  if (!user.email.includes("@cloud-ace.jp")) {
    throw new HttpsError("invalid-argument", "Unauthorized email");
  }
});

サインイン前

beforeSignedIn.ts
import {
  HttpsError,
  beforeUserSignedIn,
} from "firebase-functions/v2/identity";

export const beforeSignedIn = beforeUserSignedIn((event) => {
  // event.data にサインイン時の Google アカウントのデータが入る
  const user = event.data;
  if (!user.email.includes("@cloud-ace.jp")) {
    throw new HttpsError("invalid-argument", "Unauthorized email");
  }
});

今回は明示的にサインアップ作成前とサインイン時の関数を分けていますが、中身は同じものになっています。
ユーザーデータの email 情報に @cloud-ace.jp が含まれていない場合はエラーとして認証をブロックする、という内容です。

完成した関数は Cloud Functions For Firebase にデプロイします。
Firebase CLI を使用してデプロイします。

firebase deploy --only functions

デプロイ後、Firebase コンソールにてデプロイした関数を設定します。

blocking-functions

注意点として、ブロッキング関数を使用するためには Identity Platform にアップグレードする必要がありますIdentity Platform にアップグレードすることで、Firebase Authentication の料金体系が変更になるため、あらかじめどのような料金体系になるのか確認の上、ご使用ください。

4.コンテンツを配信する(Firebase Hosting)

hosting.png

デプロイについて確認しましょう。
デプロイには Firebase Hosting を使用します。

Firebase Hosting を使用すると 静的コンテンツ、動的コンテンツ、マイクロサービスをホスティングすることができます。デフォルトでSSL証明書が適用されるので HTTPS 通信が可能です。

コマンド 1つでグローバル CDN にデプロイすることができ、簡単に利用することができます。

動的コンテンツの取り扱い

動的コンテンツとしては Next.js アプリケーションや Angular、Nuxt、SvelteKit、Astro にも対応しています。

最近のフレームワークではサーバーサイドロジックも含まれることが多くなってきましたが、 Firebase Hosting ではサーバーサイドロジックを Cloud Fucntions for Firebase にデプロイすることで実現しています。

サーバーサイドロジックについてもフルマネージドで管理することができるため、アプリケーションの開発に集中することができます。

コンテンツの配信

デプロイコマンドはシンプルです。Firebase CLI を使用してデプロイします。

firebase deploy --only hosting

これでチャットボットアプリを公開することができました。

まとめ

簡単にですが Firebase の各プロダクトをチャットボット開発の流れに沿ってご紹介しました。
紹介した機能は全てサーバーレス環境で動作し、開発者がバックエンドの開発を意識することはほとんどありません。
設定も複雑なコマンドを覚える必要はなくコンソール上で完結するものも多くありますので、ぜひお試しいただければと思います。

Discussion