요청 전후에 맞춤 서버 측 스크립트 실행


클라이언트 코드를 변경하지 않고도 앱이 Firebase AI Logic를 통해 Gemini API에 전송하는 모든 요청 전후에 자체 맞춤 서버 측 스크립트를 실행할 수 있습니다. 이러한 스크립트는 Cloud Functions for Firebase에 배포된 콜백 스타일 함수로 구현합니다.

이 기능을 사용하면 프롬프트를 검토하거나, 토큰 사용량을 제한하거나, 분석을 위해 생성된 콘텐츠를 기록하거나, 대답 콘텐츠를 수정하는 등의 작업을 할 수 있습니다.

두 가지 이벤트 유형을 사용할 수 있습니다.

  • beforeGenerateContent: 요청이 Gemini API에 도달하기 전에 실행됩니다. 이 함수는 요청을 검사하거나 수정할 수 있으며 오류를 발생시켜 요청을 완전히 차단할 수도 있습니다.

  • afterGenerateContent: Gemini API에서 응답이 다시 전송된 후 클라이언트 앱으로 반환되기 전에 실행됩니다. 이 함수는 응답을 검사하거나 수정하고, 응답을 완전히 차단하거나, 로깅 또는 감사와 같이 응답을 관찰할 수 있습니다.

스크립트가 Cloud Functions for Firebase에 함수로 배포되면 Firebase AI Logic 트리거로 등록됩니다. 즉, 프로젝트에서 Firebase AI Logic를 통해 Gemini API에 대한 모든 generateContent 요청에 대해 실행됩니다 (서버 프롬프트 템플릿으로 만든 요청 포함).

이러한 함수는 Firebase AI Logic을 통하지 않고 Gemini API에 이루어진 요청에 의해 트리거되지 않습니다.

기본 요건

1단계: Cloud Functions for Firebase용 프로젝트 설정

Firebase 프로젝트에서 Cloud Functions for Firebase를 사용한 적이 없는 경우 다음 설정을 완료하세요.

  1. Firebase 프로젝트에서 사용한 만큼만 지불하는 Blaze 요금제를 사용하고 있는지 확인합니다 (Cloud Functions for Firebase 사용에 필요).

  2. 명령줄 인터페이스 (CLI) gcloud CLIFirebase CLI를 설치합니다.

  3. 함수를 빌드하는 데 필요한 Cloud Build 서비스 계정 역할 (roles/cloudbuild.builds.builder)을 기본 컴퓨팅 서비스 계정에 부여합니다. 다음 gcloud CLI 명령어를 실행합니다.

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. Firebase 프로젝트에서 Cloud Functions for Firebase를 초기화합니다.

    1. 다음 Firebase CLI 명령어를 실행하세요.

      firebase init functions
      
    2. 메시지가 표시되면 TypeScript를 선택합니다.

    3. functions/package.jsonfirebase-functions가 버전 6.3.0 이상인지 확인합니다. 버전을 확인하는 방법은 다음과 같습니다.

      npm --prefix functions list firebase-functions
      

2단계: 함수 작성

사전 요청 함수 작성 (beforeGenerateContent) 사후 요청 함수 작성 (afterGenerateContent)

사전 요청 함수 작성 (beforeGenerateContent)

beforeGenerateContent 이벤트 유형을 사용하면 Firebase AI Logic 프록시가 generateContent 요청을 수신할 때 함수가 트리거됩니다. 이 함수는 Gemini API에 요청이 전송되기 에 요청에 대해 실행됩니다. 이 함수는 요청을 수정하거나 요청을 완전히 차단할 수 있습니다.

함수를 작성하기 전에 다음 정보를 검토하세요.

다음은 다음 작업을 실행하는 사전 요청 함수의 예입니다.

  • 요청이 특정 Gemini API 제공자를 위한 경우에만 함수가 실행되어야 함을 지정합니다.

  • 차단된 주제에 대한 프롬프트를 검사하고 오류를 발생시켜 요청을 거부합니다.

  • 텍스트 생성 모델의 최대 출력 토큰을 제한합니다.

import { logger } from "firebase-functions";
import {
  beforeGenerateContent,
  HttpsError,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentRequest,
} from "firebase-functions/v2/ai";

const BLOCKED_TOPICS = ["weapon", "explosive", "self-harm"];
const MAX_OUTPUT_TOKENS = 4000;

export const guardPrompts = beforeGenerateContent((event) => {
  // 1. Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const request = event.data.request as VertexV1Beta1GenerateContentRequest;

  // 2. Read the prompt: contents[] -> parts[] -> text
  const prompt = (request.contents ?? [])
    .flatMap((c) => c.parts ?? [])
    .map((p) => ("text" in p ? p.text : "") ?? "")
    .join(" ")
    .toLowerCase();

  // 3. Throwing rejects the request. The request is never sent to the Gemini API.
  const blocked = BLOCKED_TOPICS.find((t) => prompt.includes(t));
  if (blocked) {
    logger.warn("Blocked a prompt", { topic: blocked });
    throw new HttpsError("invalid-argument", `We don't return content about ${blocked}.`);
  }

  logger.info("Allowing generation", {
    model: event.data.model,
    authType: event.authType,
    authId: event.authId,
    appId: event.appId,
  });

  // 4. The next step truncates the response, but that will break images.
  if (event.data.model.includes("image")) return;

  // 5. Return the WHOLE request, edited. Returning nothing leaves it untouched.
  return {
    ...request,
    generationConfig: {
      ...request.generationConfig,
      maxOutputTokens: Math.min(
        request.generationConfig?.maxOutputTokens ?? MAX_OUTPUT_TOKENS,
        MAX_OUTPUT_TOKENS,
      ),
    },
  };
});

사전 요청 함수의 주요 고려사항

  • Gemini API 제공자 지정: event.data.requestGemini Developer API 또는 Agent Platform Gemini API (formerly Vertex AI)용일 수 있습니다. 이러한 다양한 API의 요청 객체는 모양이 서로 다릅니다. 요청 객체를 안전하게 사용하려면 event.data.api를 확인해야 합니다(예: 각각 geminiV1Beta 또는 vertexV1Beta1와 비교).

  • 예외가 요청을 차단함: HttpsError을 발생시키면 요청이 거부됩니다.

  • 전체 요청 반환: 함수가 요청을 수정하는 경우 수정된 전체 요청 객체를 반환해야 합니다. 아무것도 반환하지 않으면 (또는 undefined) 요청이 변경되지 않습니다.

  • 지연 시간 테스트: 함수가 수행하는 작업에 따라 지연 시간이 추가되어 사용자 환경에 영향을 미칠 수 있습니다.

후속 요청 함수 작성 (afterGenerateContent)

afterGenerateContent 이벤트 유형을 사용하면 Firebase AI Logic 프록시가 generateContent 요청으로부터 응답을 수신할 때 함수가 트리거됩니다. 이 함수는 대답이 클라이언트 앱에 반환되기 에 대답에 대해 실행됩니다. 이 함수는 사용량을 로깅하거나, 대답을 수정하거나, 대답을 완전히 차단할 수 있습니다.

함수를 작성하기 전에 다음 정보를 검토하세요.

다음은 토큰 사용량과 종료 이유를 로깅하는 요청 후 함수의 예입니다.

import { logger } from "firebase-functions";
import {
  afterGenerateContent,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentResponse,
} from "firebase-functions/v2/ai";

export const recordGenerationUsage = afterGenerateContent((event) => {
  // Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const response = event.data.response as VertexV1Beta1GenerateContentResponse;

  logger.info("Generation finished", {
    model: event.data.model,
    promptTokens: response.usageMetadata?.promptTokenCount,
    totalTokens: response.usageMetadata?.totalTokenCount,
    finishReason: response.candidates?.[0]?.finishReason,
  });

  // To leave the response untouched, return nothing.
  // To modify the response, return a modified response object here.
});

요청 후 함수의 주요 고려사항

  • Gemini API 제공자 지정: event.data.responseGemini Developer API 또는 Agent Platform Gemini API (formerly Vertex AI)용일 수 있습니다. 요청 객체를 안전하게 캐스팅하고 사용하려면 event.data.api를 확인해야 합니다 (예: 각각 geminiV1Beta 또는 vertexV1Beta1와 비교).

  • 지연 시간 테스트: 함수가 수행하는 작업에 따라 지연 시간이 추가되어 사용자 환경에 영향을 미칠 수 있습니다.

3단계: 함수 배포

함수를 Firebase에 배포하면 Firebase AI Logic 서비스 에이전트가 이러한 함수를 호출할 수 있는 권한이 부여되고 각 함수가 Firebase AI Logic 트리거로 등록됩니다.

  1. Firebase CLI를 사용한 함수 배포:

    firebase deploy --only functions
    
  2. 배포 후 함수가 Firebase에 배포되었는지 확인합니다.

    firebase functions:list
    
  3. 함수를 반복해야 하는 경우 다음 단계를 따르세요.

    프로젝트 디렉터리에서 함수를 업데이트한 후 firebase deploy --only functions를 다시 실행합니다.

함수 실행 중지

이러한 함수 중 하나가 실행되지 않도록 하려면 Google 서버에서 삭제하고 Firebase AI Logic 트리거로 등록 취소해야 합니다 . 다음 옵션 중 하나를 사용하여 Firebase CLI를 사용하면 됩니다.

  • 옵션 1: 함수를 암시적으로 삭제

    1. 프로젝트 디렉터리 코드베이스에서 함수를 삭제합니다.

    2. 다음 Firebase CLI 명령어를 실행하세요.

      firebase deploy --only functions
      
  • 옵션 2: 함수를 명시적으로 삭제

    1. 프로젝트 디렉터리 코드베이스에서 함수를 삭제합니다.

    2. 다음 Firebase CLI 명령어를 실행하세요.

      firebase functions:delete FUNCTION_NAME
      



이벤트 데이터 참조

beforeGenerateContentafterGenerateContent 모두 요청에 관한 컨텍스트와 메타데이터가 포함된 AIBlockingEvent 객체를 수신합니다.

최상위 요청 메타데이터 (AIBlockingEvent)

최상위 AIBlockingEvent 객체는 호출자 및 트리거 환경에 관한 정보를 제공합니다.

  • event.authType: 호출자의 인증 상태입니다("app_user", "unauthenticated", "unknown").
  • event.authId: 로그인한 경우 호출자의 Firebase 인증 UID입니다.
  • event.authClaims: 호출자의 맞춤 인증 클레임입니다(있는 경우).
  • event.appId: 요청을 한 Firebase 앱 ID입니다.
  • event.androidPackageName / event.iosBundleId: 호출 앱의 패키지 이름 또는 번들 ID입니다 (각각 Android 또는 Apple 플랫폼에 적용됨).
  • event.data: 이벤트 페이로드로, 사전 요청 함수와 사후 요청 함수 간에 차이가 있습니다.

요청 전 이벤트 데이터 (beforeGenerateContent)

beforeGenerateContent 함수에서 event.dataBeforeGenerateContentData 객체로 채워집니다.

  • event.data.api: Gemini API 프로바이더: geminiV1Beta(Gemini Developer API) 또는 vertexV1Beta1(Agent Platform Gemini API (formerly Vertex AI))
  • event.data.model: 전체 모델 리소스 경로입니다 (예: projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash).
  • event.data.template: 사용된 서버 프롬프트 템플릿에 관한 메타데이터(PromptTemplateInfo)(해당하는 경우).
  • event.data.request: 전송되는 요청 페이로드입니다. 객체 유형과 속성은 Gemini API 제공업체에 따라 다릅니다.

요청 후 이벤트 데이터 (afterGenerateContent)

afterGenerateContent 함수에서 event.dataAfterGenerateContentData 객체로 채워집니다. 이 객체는 BeforeGenerateContentData를 확장하고 (api, model, template, request 제공) 모델의 응답을 추가합니다.



제한사항 및 동작

이러한 기능을 구현할 때는 다음 동작과 제한사항을 고려하세요.

  • generateContent 요청만: 이러한 함수는 Firebase AI Logic를 통해 Gemini API에 대한 generateContent 요청으로만 트리거할 수 있습니다.

    다음의 경우 이러한 함수가 트리거되지 않으며 해당 요청에 대해 함수가 자동으로 우회됩니다.

    • generateContentStream에 대한 요청은 이러한 함수를 트리거하지 않습니다.

    • Gemini Live API에 대한 요청은 이러한 함수를 트리거하지 않습니다.

  • 클라이언트 측 코드 변경 없음: 이러한 함수를 실행할 때 generateContent 요청을 사용해야 하는 것 외에는 클라이언트 측 코드베이스를 변경할 필요가 없습니다.

    이러한 함수는 서버에 배포되며 Firebase AI Logic 프록시가 서버 측에서 요청과 응답을 가로챌 수 있도록 Firebase AI Logic 트리거로 등록됩니다.

  • 프로젝트 수준 범위: Firebase 프로젝트당 최대 하나의 beforeGenerateContent 함수와 하나의 afterGenerateContent 함수를 배포할 수 있습니다.

  • 기본 위치: 이러한 함수는 기본적으로 us-central1에 배포됩니다 (함수 위치에 대해 알아보기). 하지만 함수를 배포하는 위치와 관계없이 함수는 global 리전에 Firebase AI Logic 트리거로 등록됩니다.