Wdrażanie procesów za pomocą Cloud Functions dla Firebase

Genkit zawiera wtyczkę, która ułatwia wdrażanie przepływów do Cloud Functions dla Firebase. Po wdrożeniu przepływy są dostępne jako punkty końcowe HTTPS i można je wywoływać za pomocą bibliotek klienta Cloud Functions.

Zanim zaczniesz

  • Zainstaluj Firebase CLI.
  • Musisz znać pojęcie przepływów w Genkit i wiedzieć, jak je pisać. Instrukcje na tej stronie zakładają, że masz już zdefiniowane przepływy, które chcesz wdrożyć.
  • Przyda się, choć nie jest to wymagane, jeśli korzystasz już z funkcji Cloud Functions for Firebase.

1. Konfigurowanie projektu Firebase

Jeśli nie masz jeszcze projektu Firebase z skonfigurowanymi funkcjami TypeScript Cloud Functions, wykonaj te czynności:

  1. Utwórz nowy projekt Firebase w konsoli Firebase lub wybierz istniejący.

  2. Przekształć projekt na abonament Blaze, który jest wymagany do wdrażania funkcji Cloud Functions.

  3. Zaloguj się za pomocą wiersza poleceń Firebase:

    firebase login
    firebase login --reauth # alternative, if necessary
    firebase login --no-localhost # if running in a remote shell
  4. Utwórz nowy katalog projektu:

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  5. Aby zainicjować projekt Firebase w katalogu:

    cd $PROJECT_ROOT
    firebase init genkit

    Pozostała część tej strony zakłada, że funkcje są pisane w TypeScript, ale jeśli używasz JavaScriptu, możesz też wdrażać przepływy Genkit.

2. Aktualizowanie definicji przepływów

Po skonfigurowaniu projektu Firebase za pomocą Cloud Functions możesz skopiować lub napisać definicje przepływu w katalogu functions/src projektu i wyeksportować je w katalogu index.ts.

Aby móc wdrażać przepływy, musisz wprowadzić w ich definicji drobne zmiany. Główna logika pozostanie taka sama, ale dodasz dodatkowe informacje, aby ułatwić wdrażanie i zwiększyć bezpieczeństwo.

Załóżmy, że masz taki proces:

const generatePoemFlow = ai.defineFlow(
  {
    name: "generatePoem",
    inputSchema: z.string(),
    outputSchema: z.string(),
  },
  async (subject: string) => {
    const { text } = await ai.generate(`Compose a poem about ${subject}.`);
    return text;
  }
);

W sekcjach poniżej opisujemy zmiany, które musisz wprowadzić, zanim wdrożysz tę funkcję.

Definiowanie przepływów za pomocą onFlow

Zamiast definiowania przepływu za pomocą funkcji Genkit.defineFlow() użyj funkcji onFlow() z pluginu Firebase. Za pomocą tej funkcji możesz umieścić logikę przepływu w obejmującym ją obiekcie Cloud Functions, podobnie jak w przypadku funkcji onCall.

import { onFlow } from "@genkit-ai/firebase/functions";

export const generatePoem = onFlow(
  ai,
  {
    // ...
  },
  async (subject: string) => {
    // ...
  }
);

Pamiętaj, że onFlow nie jest metodą funkcji Genkit, ale funkcją, która przyjmuje instancję Genkit jako pierwszy parametr. W pozostałych przypadkach składnia jest podobna do defineFlow.

Definiowanie zasady autoryzacji

Wszystkie wdrożone przepływy danych, niezależnie od tego, czy zostały wdrożone w Firebase, czy nie, powinny mieć zasady autoryzacji. Bez nich potencjalnie kosztowne przepływy danych oparte na generatywnej AI mogłyby być wywoływane przez dowolną osobę. Aby zdefiniować zasady autoryzacji, użyj parametru authPolicy w definicji przepływu:

import { firebaseAuth } from "@genkit-ai/firebase/auth";

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    // ...
    authPolicy: firebaseAuth((user, input) => {
      if (!user.email_verified) {
        throw new Error("Verified email required to run flow");
      }
    }),
  },
  async (subject: string) => {
    // ...
  }
);

Te zasady używają pomocnika firebaseAuth(), aby zezwolić na dostęp tylko zarejestrowanym użytkownikom Twojej aplikacji z weryfikowanymi adresami e-mail. Po stronie klienta musisz ustawić nagłówek Authorization: Bearer na token identyfikatora Firebase, który spełnia Twoje zasady. Pakiety SDK klienta Cloud Functions udostępniają wywoływalne metody funkcji, które automatyzują ten proces. Przykład znajdziesz w sekcji Wypróbuj wdrożony proces.

Udostępnianie danych logowania interfejsu API wdrożonym przepływom

Po wdrożeniu procesy muszą mieć możliwość uwierzytelniania się w usługach zdalnych, z których korzystają. Większość przepływów wymaga co najmniej danych logowania do interfejsu API modelu, z którego korzystają.

W tym przykładzie wykonaj jedną z tych czynności w zależności od wybranego dostawcy modelu:

Gemini (Google AI)

  1. Upewnij się, że AI od Google jest dostępna w Twoim regionie.

  2. Wygeneruj klucz interfejsu API dla Gemini API za pomocą Google AI Studio.

  3. Zapisz klucz interfejsu API w usłudze Cloud Secret Manager:

    firebase functions:secrets:set GOOGLE_GENAI_API_KEY

    Ten krok jest ważny, aby zapobiec przypadkowemu ujawnieniu klucza interfejsu API, który umożliwia dostęp do usługi z możliwym naliczaniem opłat.

    Więcej informacji o zarządzaniu obiektami tajnymi znajdziesz w artykule Przechowywanie i dostęp do poufnych informacji konfiguracyjnych.

  4. Zmień plik src/index.ts i dodaj po dotychczasowych instrukcjach importu te elementy:

    import {defineSecret} from "firebase-functions/params";
    const googleAIapiKey = defineSecret("GOOGLE_GENAI_API_KEY");
    

    Następnie w definicji przepływu zadeklaruj, że funkcja w Cloud Functions musi mieć dostęp do tej wartości obiektu tajnego:

    export const generatePoem = onFlow(
      {
        name: "generatePoem",
        // ...
        httpsOptions: {
          secrets: [googleAIapiKey],  // Add this line.
        },
      },
      async (subject) => {
        // ...
      }
    );
    

Gdy teraz wdrożysz tę funkcję, klucz interfejsu API zostanie zapisany w usłudze Cloud Secret Manager i będzie dostępny z poziomu środowiska Cloud Functions.

Gemini (Vertex AI)

  1. W konsoli Cloud włącz interfejs API Vertex AI w projekcie Firebase.

  2. Na stronie Uprawnienia sprawdź, czy domyślnemu kontu usługi obliczeniowej przypisano rolę Użytkownik Vertex AI.

W tym samouczku musisz skonfigurować tylko jeden sekret – dla dostawcy modelu. Ogólnie jednak musisz wykonać podobne czynności w przypadku każdej usługi używanej przez przepływ danych.

Ustawianie zasady CORS

Jeśli będziesz korzystać z przepływu z aplikacji internetowej (co zrobisz w sekcji Wypróbuj wdrożony przepływ), w parametrze httpsOptions ustaw zasadę CORS:

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    // ...
    httpsOptions: {
      cors: '*',
    },
  },
  async (subject: string) => {
    // ...
  }
);

W przypadku aplikacji produkcyjnych prawdopodobnie zechcesz zastosować bardziej restrykcyjne zasady, ale na potrzeby tego samouczka te zasady wystarczą.

Pełny przykład

Po wprowadzeniu wszystkich opisanych wyżej zmian proces wdrażania będzie wyglądał mniej więcej tak:

const googleAIapiKey = defineSecret("GOOGLE_GENAI_API_KEY");

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    inputSchema: z.string(),
    outputSchema: z.string(),
    authPolicy: firebaseAuth((user, input) => {
      if (!user.email_verified) {
        throw new Error("Verified email required to run flow");
      }
    }),
    httpsOptions: {
      secrets: [googleAIapiKey],
      cors: '*',
    },
  },
  async (subject: string) => {
    const { text } = await ai.generate(`Compose a poem about ${subject}.`);
    return text;
  }
);

3. Wdrażanie procesów w Firebase

Po zdefiniowaniu przepływów za pomocą onFlow możesz je wdrażać tak samo jak inne funkcje w Cloud Functions:

cd $PROJECT_ROOT
firebase deploy --only functions

Twój przepływ został wdrożony jako funkcja w Cloud Functions. Nie będziesz jednak mieć dostępu do wdrożonego punktu końcowego za pomocą curl ani podobnej metody ze względu na zasady autoryzacji przepływu danych. Aby dowiedzieć się, jak bezpiecznie uzyskać dostęp do przepływu, przejdź do następnej sekcji.

Opcjonalnie: wypróbuj wdrożony proces

Aby wypróbować punkt końcowy przepływu, możesz wdrożyć tę minimalną przykładową aplikację internetową:

  1. W sekcji Ustawienia projektu w konsoli Firebase dodaj nową aplikację internetową, wybierając opcję konfiguracji hostingu.

  2. W sekcji Uwierzytelnianie konsoli Firebase włącz usługę Google, której użyjesz w tym przykładzie.

  3. W katalogu projektu skonfiguruj Hosting Firebase, w którym wdrożesz przykładową aplikację:

    cd $PROJECT_ROOT
    firebase init hosting

    Zaakceptuj domyślne wartości dla wszystkich promptów.

  4. Zastąp public/index.html tymi wartościami:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Genkit demo</title>
      </head>
      <body>
        <div id="signin" hidden>
          <button id="signinBtn">Sign in with Google</button>
        </div>
        <div id="callGenkit" hidden>
          Subject: <input type="text" id="subject" />
          <button id="generatePoem">Compose a poem on this subject</button>
          <p id="generatedPoem"></p>
        </div>
        <script type="module">
          import { initializeApp } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-app.js";
          import {
            getAuth,
            onAuthStateChanged,
            GoogleAuthProvider,
            signInWithPopup,
          } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-auth.js";
          import {
            getFunctions,
            httpsCallable,
          } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-functions.js";
    
          const firebaseConfig = await fetch("/__/firebase/init.json");
          initializeApp(await firebaseConfig.json());
    
          async function generatePoem() {
            const poemFlow = httpsCallable(getFunctions(), "generatePoem");
            const subject = document.querySelector("#subject").value;
            const response = await poemFlow(subject);
            document.querySelector("#generatedPoem").innerText = response.data;
          }
    
          function signIn() {
            signInWithPopup(getAuth(), new GoogleAuthProvider());
          }
    
          document.querySelector("#signinBtn").addEventListener("click", signIn);
          document
            .querySelector("#generatePoem")
            .addEventListener("click", generatePoem);
    
          const signinEl = document.querySelector("#signin");
          const genkitEl = document.querySelector("#callGenkit");
    
          onAuthStateChanged(getAuth(), (user) => {
            if (!user) {
              signinEl.hidden = false;
              genkitEl.hidden = true;
            } else {
              signinEl.hidden = true;
              genkitEl.hidden = false;
            }
          });
        </script>
      </body>
    </html>
    
  5. Wdróż aplikację internetową i funkcję w Cloud Functions:

    cd $PROJECT_ROOT
    firebase deploy

Otwórz aplikację internetową, klikając adres URL wydrukowany przez polecenie deploy. Aplikacja wymaga zalogowania się na konto Google, po czym można inicjować żądania dotyczące urządzeń końcowych.

Opcjonalnie: uruchamianie procesów w interfejsie programisty

Możesz uruchamiać przepływy zdefiniowane za pomocą onFlow w interfejsie programisty w taki sam sposób, jak przepływy zdefiniowane za pomocą defineFlow, więc nie musisz przełączać się między tymi dwoma opcjami podczas wdrażania i tworzenia.

cd $PROJECT_ROOT/functions
npx genkit start -- npx tsx --watch src/index.ts

lub

cd $PROJECT_ROOT/functions
npm run genkit:start

Teraz możesz przejść do adresu URL wydrukowanego przez polecenie genkit start.

Opcjonalnie: tworzenie aplikacji za pomocą Pakietu emulatorów lokalnych Firebase

Firebase oferuje pakiet emulatorów do lokalnego tworzenia aplikacji, których możesz używać z Genkit.

Aby korzystać z interfejsu Genkit Dev w pakiecie emulatorów Firebase, uruchom emulatory Firebase w ten sposób:

npx genkit start -- firebase emulators:start --inspect-functions

Spowoduje to uruchomienie kodu w emulatorze i ramówki Genkit w trybie programowania, co spowoduje uruchomienie i udostępnienie interfejsu Genkit reflection API (ale nie interfejsu Dev UI).

Aby zobaczyć ścieżki z Firestore w interfejsie dla deweloperów, przejdź na kartę Sprawdzanie i przełącz przełącznik „Dev/Prod”. Gdy przełączysz się na „prod”, będą wczytywane ścieżki z Firestore.