Firebase용 Cloud Functions를 사용하여 흐름 배포

Genkit에는 Firebase용 Cloud Functions에 흐름을 배포하는 데 도움이 되는 플러그인이 포함되어 있습니다. 흐름은 배포된 후 HTTPS 엔드포인트로 사용할 수 있으며 Cloud Functions 클라이언트 라이브러리를 사용하여 호출 가능한 함수로 액세스할 수 있습니다.

시작하기 전에

  • Firebase CLI 설치
  • Genkit의 흐름 개념과 이를 작성하는 방법을 숙지해야 합니다. 이 페이지의 안내는 배포하려는 흐름이 이미 정의되어 있다고 가정합니다.
  • 이전에 Firebase용 Cloud Functions를 사용한 적이 있으면 도움이 되지만 필수는 아닙니다.

1. Firebase 프로젝트 설정

TypeScript Cloud Functions가 설정된 Firebase 프로젝트가 아직 없는 경우 다음 단계를 따르세요.

  1. Firebase Console을 사용하여 새 Firebase 프로젝트를 만들거나 기존 프로젝트를 선택합니다.

  2. Cloud Functions를 배포하는 데 필요한 Blaze 요금제로 프로젝트를 업그레이드합니다.

  3. Firebase CLI로 로그인합니다.

    firebase login
    firebase login --reauth # alternative, if necessary
    firebase login --no-localhost # if running in a remote shell
  4. 새 프로젝트 디렉터리를 만듭니다.

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  5. 디렉터리에서 Firebase 프로젝트를 초기화합니다.

    cd $PROJECT_ROOT
    firebase init genkit

    이 페이지의 나머지 부분에서는 TypeScript로 함수를 작성하도록 선택했다고 가정하지만 JavaScript를 사용하는 경우에도 Genkit 흐름을 배포할 수 있습니다.

2. 흐름 정의 업데이트

Cloud Functions로 Firebase 프로젝트를 설정한 후에는 프로젝트의 functions/src 디렉터리에 흐름 정의 파일을 복사하거나 작성하고 index.ts에서 내보낼 수 있습니다.

흐름을 배포하려면 흐름을 정의하는 방식을 약간 변경해야 합니다. 핵심 로직은 동일하게 유지되지만, 배포 후 원활하게 배포하고 더 안전하게 만들기 위해 추가 정보를 추가합니다.

다음과 같은 흐름이 있다고 가정해 보겠습니다.

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;
  }
);

다음 섹션에서는 배포하기 전에 변경해야 하는 사항을 설명합니다.

onFlow를 사용하여 흐름 정의

Genkit.defineFlow()로 흐름을 정의하는 대신 Firebase 플러그인의 onFlow() 함수를 사용하세요. 이 함수를 사용하면 onCall와 마찬가지로 Cloud Functions 요청 핸들러에 흐름 로직이 래핑됩니다.

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

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

onFlowGenkit의 메서드가 아니라 Genkit 인스턴스를 첫 번째 매개변수로 사용하는 함수입니다. 그 밖의 구문은 defineFlow와 유사합니다.

승인 정책 정의

Firebase에 배포되었는지 여부와 관계없이 배포된 모든 흐름에는 승인 정책이 있어야 합니다. 승인 정책이 없으면 비용이 많이 들 수 있는 생성형 AI 흐름을 누구나 호출할 수 있습니다. 승인 정책을 정의하려면 흐름 정의에서 authPolicy 매개변수를 사용합니다.

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) => {
    // ...
  }
);

이 정책은 firebaseAuth() 도우미를 사용하여 확인된 이메일 주소가 있는 앱의 등록된 사용자만 액세스하도록 허용합니다. 클라이언트 측에서는 Authorization: Bearer 헤더를 정책을 충족하는 Firebase ID 토큰으로 설정해야 합니다. Cloud Functions 클라이언트 SDK는 이를 자동화하는 호출 가능 함수 메서드를 제공합니다. 예시는 배포된 흐름 사용해 보기 섹션을 참고하세요.

배포된 흐름에 API 사용자 인증 정보 제공

배포된 후에는 흐름이 사용하는 원격 서비스와 인증할 방법이 필요합니다. 대부분의 흐름에는 최소한 사용하는 모델 API 서비스에 액세스하기 위한 사용자 인증 정보가 필요합니다.

이 예에서는 선택한 모델 공급업체에 따라 다음 중 하나를 실행합니다.

Gemini(Google AI)

  1. 거주 중인 지역에서 Google AI를 사용할 수 있는지 확인합니다.

  2. Google AI Studio를 사용하는 Gemini API의 API 키를 생성합니다.

  3. Cloud Secret Manager에 API 키를 저장합니다.

    firebase functions:secrets:set GOOGLE_GENAI_API_KEY

    이 단계는 잠재적으로 요금이 청구될 수 있는 서비스에 대한 액세스 권한을 부여하는 API 키가 실수로 유출되는 것을 방지하는 데 중요합니다.

    보안 비밀 관리에 관한 자세한 내용은 민감한 구성 정보 저장 및 액세스를 참고하세요.

  4. src/index.ts를 수정하고 기존 가져오기 뒤에 다음을 추가합니다.

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

    그런 다음 흐름 정의에서 Cloud 함수에 이 보안 비밀 값에 대한 액세스 권한이 필요하다고 선언합니다.

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

이제 이 함수를 배포하면 API 키가 Cloud Secret Manager에 저장되고 Cloud Functions 환경에서 사용할 수 있습니다.

Gemini(Vertex AI)

  1. Cloud 콘솔에서 Firebase 프로젝트에 대해 Vertex AI API를 사용 설정합니다.

  2. IAM 페이지에서 기본 컴퓨팅 서비스 계정Vertex AI 사용자 역할이 부여되었는지 확인합니다.

이 튜토리얼에서 설정해야 하는 유일한 보안 비밀은 모델 제공업체이지만, 일반적으로 흐름에서 사용하는 각 서비스에 대해 유사한 작업을 수행해야 합니다.

CORS 정책 설정

웹 앱에서 흐름에 액세스하는 경우 (배포된 흐름 사용해 보기 섹션에서 수행) httpsOptions 매개변수에서 CORS 정책을 설정합니다.

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

프로덕션 앱에는 더 제한적인 정책을 사용하는 것이 좋지만 이 튜토리얼에서는 이 정책으로 충분합니다.

전체 예시

위에서 설명한 변경사항을 모두 적용하면 배포 가능한 흐름이 다음 예와 같이 표시됩니다.

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. Firebase에 흐름 배포

onFlow를 사용하여 흐름을 정의한 후에는 다른 Cloud Functions를 배포하는 것처럼 배포할 수 있습니다.

cd $PROJECT_ROOT
firebase deploy --only functions

이제 흐름을 Cloud 함수로 배포했습니다. 하지만 흐름의 승인 정책으로 인해 curl 등으로 배포된 엔드포인트에 액세스할 수 없습니다. 다음 섹션으로 이동하여 흐름에 안전하게 액세스하는 방법을 알아보세요.

선택사항: 배포된 흐름 사용해 보기

흐름 엔드포인트를 사용해 보려면 다음과 같은 최소한의 웹 앱 예시를 배포하면 됩니다.

  1. Firebase Console의 프로젝트 설정 섹션에서 새 웹 앱을 추가하고 호스팅도 설정하는 옵션을 선택합니다.

  2. Firebase Console의 인증 섹션에서 이 예에서 사용할 Google 제공업체를 사용 설정합니다.

  3. 프로젝트 디렉터리에서 샘플 앱을 배포할 Firebase 호스팅을 설정합니다.

    cd $PROJECT_ROOT
    firebase init hosting

    모든 프롬프트의 기본값을 수락합니다.

  4. public/index.html를 다음으로 바꿉니다.

    <!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. 웹 앱과 Cloud 함수를 배포합니다.

    cd $PROJECT_ROOT
    firebase deploy

deploy 명령어로 출력된 URL을 방문하여 웹 앱을 엽니다. 앱을 사용하려면 Google 계정으로 로그인해야 하며, 로그인한 후 엔드포인트 요청을 시작할 수 있습니다.

선택사항: 개발자 UI에서 흐름 실행

defineFlow를 사용하여 정의된 흐름을 실행하는 것과 정확히 동일한 방식으로 개발자 UI에서 onFlow를 사용하여 정의된 흐름을 실행할 수 있으므로 배포와 개발 간에 두 가지 간에 전환할 필요가 없습니다.

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

또는

cd $PROJECT_ROOT/functions
npm run genkit:start

이제 genkit start 명령어로 출력된 URL로 이동하여 액세스할 수 있습니다.

선택사항: Firebase 로컬 에뮬레이터 도구 모음을 사용한 개발

Firebase는 Genkit과 함께 사용할 수 있는 로컬 개발용 에뮬레이터 모음을 제공합니다.

Firebase 에뮬레이터 도구 모음에서 Genkit Dev UI를 사용하려면 다음과 같이 Firebase 에뮬레이터를 시작합니다.

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

이렇게 하면 에뮬레이터에서 코드가 실행되고 개발 모드에서 Genkit 프레임워크가 실행되며, 이 프레임워크는 Genkit 리플렉션 API를 실행하고 노출하지만 개발자 UI는 실행하지 않습니다.

Dev UI에서 Firestore의 트레이스를 보려면 검사 탭으로 이동하여 'Dev/Prod' 스위치를 전환합니다. 'prod'로 전환하면 Firestore에서 트레이스를 로드합니다.