Firebase & AI のオーケストレーションを実現!Genkit アーキテクチャ 8 選
こんにちは、cobo です。
今回は、Google I/O '24 で発表された Firebase の新機能の 1 つである Genkit についての記事です。
Genkit は生成 AI ワークフローのオーケストレーション、デプロイ、モニタリングを行うためのコードファーストのフレームワーク と言われています。
この記事では、オーケストレーション という部分にフォーカスし、Genkit が Firebase の指揮者 としてどのような構成(アーキテクチャ)を採ることができるのか紹介します。
1. Web API 構成
Web API 構成
クライアントからのリクエスト(例えば、質問文など)を受け付けて、それに対して、Genkit 内で用意されたプロンプトを基にレスポンスを返す、一番シンプルな Genkit の構成です。
サーバーサイドである Firebase で、Google AI Studio から払い出された Gemini の API キーを保持する形となります。
ですので、実際には、Genkit から Secret Manager という秘匿情報を管理するサービスを参照するようになっています。
補足イメージ
import * as genkitFunctions from "@genkit-ai/firebase/functions";
import * as z from "zod";
export const genkitFunction = genkitFunctions.onFlow(
{
name: "genkitFunction",
httpsOptions: {
cors: true,
secrets: [googleAIapiKey],
},
authPolicy: genkitFunctions.noAuth(),
outputSchema: z.string(),
},
async () => {}
);
2. Firebase 認証基盤を組み込んだ構成
Firebase 認証基盤を組み込んだ構成
Web API 構成 を、よりセキュアにした構成です。
Genkit コール時に、Firebase Authentication を使用して、アプリ側のユーザーを判断して、使用元を制限することができます。
例えば、匿名認証済みユーザーのみ許可、特定のメールアドレスのみ許可 などです。
export const genkitFunction = onFlow(
{
name: `genkitFunction`,
httpsOptions: {
cors: true,
secrets: [googleAIapiKey],
},
outputSchema: chatbotOutputSchema,
authPolicy: firebaseAuth((user) => {
if (user.firebase?.sign_in_provider !== `anonymous`) {
throw new Error(`Only anonymously authenticated users can access this function`)
}
}),
},
3. Firestore を生成 AI のコンテキストとした構成
Firestore を生成 AI のコンテキストとした構成
Firestore のデータを、生成 AI のコンテキストとして活用する構成です。
クライアントからのリクエストに加えて、Firestore に保存されているデータを参照して、より豊富な情報を基に回答を生成します。
例えば、チャットアプリでは、ユーザーの過去の対話履歴や設定情報を Firestore に保存しておき、それらを考慮した上で回答を生成することが可能です。
【処理の流れ】
-
- Firestore からコンテキストを取得
- クライアントからリクエストを受けた際、まず Firestore に保存されている関連データを参照します。
例えば、チャットアプリの場合、ユーザーの過去の対話履歴や設定情報が Firestore に保存されており、それを基にリクエストを補強します。
-
- プロンプトの生成
- Firestore のデータとクライアントのリクエスト内容を組み合わせて、Gemini API に送信するプロンプトを生成します。
-
- Gemini の回答生成
- 生成されたプロンプトを基に、Gemini が回答を生成します。
これにより、ユーザーが前回の会話で何について話していたかを踏まえた回答を提供することが可能となります。
import { prompt } from "@genkit-ai/dotprompt";
import { firebaseAuth } from "@genkit-ai/firebase/auth";
import * as genkitFunctions from "@genkit-ai/firebase/functions";
import * as z from "zod";
import { db, googleAIapiKey } from "../config/firebase";
import { chatbotInputSchema } from "../schemas/chatbotInputSchema";
import { chatbotOutputSchema } from "../schemas/chatbotOutputSchema";
import { createChatbotInput } from "../utils/genkitUtils";
export const anonymousFirestoreChatbot = genkitFunctions.onFlow(
{
name: `anonymousFirestoreChatbot`,
httpsOptions: {
cors: true,
secrets: [googleAIapiKey],
},
inputSchema: z.object({
userId: z.string(),
chatId: z.string(),
currentQuery: z.string(),
}),
outputSchema: chatbotOutputSchema,
authPolicy: firebaseAuth((user) => {
if (user.firebase?.sign_in_provider !== `anonymous`) {
throw new Error(
`Only anonymously authenticated users can access this function`
);
}
}),
},
async (input) => {
try {
const userDoc = await db.collection(`users`).doc(input.userId).get();
const userData = userDoc.data();
const chatHistoryDoc = await db
.collection(`users/${input.userId}/chatHistory`)
.doc(input.chatId)
.get();
const chatData = chatHistoryDoc.data();
const productDoc = await db
.collection(`productCatalog`)
.doc(chatData?.productId)
.get();
const productData = productDoc.data();
const chatbotPrompt = await prompt<z.infer<typeof chatbotInputSchema>>(
`chatbot`
);
const result = await chatbotPrompt.generate({ input: createChatbotInput(
input.userId,
input.currentQuery,
userData,
chatData,
productData
)});
return result.output();
} catch (error) {
console.error(`Error in anonymousFirestoreChatbot:`, error);
throw error;
}
}
);
4. Cloud Storage for Firebase を生成 AI のコンテキストとした構成
Cloud Storage for Firebase を生成 AI のコンテキストとした構成
Cloud Storage for Firebase に保存されているファイル(画像、音声、動画など)を生成 AI のコンテキストとして活用する構成です。
クライアントからのリクエストに加えて、Storage 内のファイルを AI に与えることで、マルチモーダルな処理が可能となります。
import { prompt } from "@genkit-ai/dotprompt";
import * as genkitFunctions from "@genkit-ai/firebase/functions";
import * as z from "zod";
import { googleAIapiKey } from "../config/firebase";
import { analyzeStorageInputSchema } from "../schemas/analyzeStorageInputSchema";
import { analyzeStorageOutputSchema } from "../schemas/analyzeStorageOutputSchema";
export const analyzeStorageImage = genkitFunctions.onFlow(
{
name: `analyzeStorageImage`,
httpsOptions: {
cors: true,
secrets: [googleAIapiKey],
},
inputSchema: analyzeStorageInputSchema,
outputSchema: analyzeStorageOutputSchema,
authPolicy: genkitFunctions.noAuth(),
},
async (input) => {
const analyzeFilePrompt = await prompt<
z.infer<typeof analyzeStorageInputSchema>
>(`analyze`);
const result = await analyzeFilePrompt.generate({ input });
return result.output();
}
);
5. Firebase 各種サービスをトリガーとした構成
Firebase 各種サービスをトリガーとした構成
Firebase の各種サービス (Firestore, Firebase Authentication, Storage など) のイベントをトリガーとして Genkit を起動する構成です。
これによって、特定のデータ変更や条件が満たされた際に、自動的に AI 処理を実行することができます。
6. Web コンテンツを解析する構成
Web コンテンツを解析する構成
外部の Web コンテンツを Genkit の入力として使用する構成です。
詳細は、こちらの記事で述べられています。
7. Observability を強化した構成
Observability を強化した構成
Genkit の動作をより詳細に監視、分析するために OpenTelemetry を組み込んだ構成です。
OpenTelemetry を Google Cloud の Observability ツールである、Cloud Logging, Cloud Monitoring, Cloud Trace などに統合することで、Genkit への入力から出力にかかるフローを視覚化することができます。
例えば、ユーザーからのリクエストが Genkit に到達し、Firestore から値を取得し、Gemini API を呼び出すといった一連のフローを OpenTelemetry でモニタリングすることができます。
トレースデータから、リクエストが正常に処理されたか、どの部分でパフォーマンスが低下したか、エラーが発生したかを視覚的に確認できます。
この構成を採ることで、Genkit サービスの信頼性を高めていくことができます。
configureGenkit({
plugins: [firebase(), vertexAI(), googleCloud()],
enableTracingAndMetrics: true,
telemetry: {
instrumentation: `googleCloud`,
logger: `googleCloud`,
},
});
8. Vertex AI を連携した構成
Vertex AI を連携した構成
Genkit と Google Cloud の Vertex AI を連携させることで、より高度な AI 機能を実現します。
例えば、Genkit で生成した文章を Vertex AI の感情分析モデルでさらに分析することや、Imagen のモデルを活用して、画像生成の機能を実装することができます。
import { prompt } from "@genkit-ai/dotprompt";
import * as genkitFunctions from "@genkit-ai/firebase/functions";
import * as z from "zod";
import { generateImageInputSchema } from "../schemas/generateImageInputSchema";
export const generateImage = genkitFunctions.onFlow(
{
name: `generateImage`,
httpsOptions: {
cors: true,
},
inputSchema: generateImageInputSchema,
authPolicy: genkitFunctions.noAuth(),
},
async (input) => {
const generateImagePrompt = await prompt<
z.infer<typeof generateImageInputSchema>
>(`generateImage`);
const result = await generateImagePrompt.generate({ input });
return result.media();
}
);
Discussion