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


클라이언트 코드를 변경하지 않고도 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 CLI 및 Firebase 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.json의 firebase-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.request는 Gemini 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.response는 Gemini 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를 다시 실행합니다.

함수 실행 중지

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

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

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

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

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

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

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

      firebase functions:delete FUNCTION_NAME
      



이벤트 데이터 참조

beforeGenerateContent 및 afterGenerateContent는 모두 요청에 대한 컨텍스트 및 메타데이터가 포함된 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.data는 BeforeGenerateContentData 객체로 채워집니다.

  • 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.data는 AfterGenerateContentData 객체로 채워집니다. 이 객체는 BeforeGenerateContentData(api, model, template, request 제공)를 확장하고 모델의 응답을 추가합니다.



제한사항 및 동작

이러한 함수를 구현할 때는 다음 동작과 제한사항에 유의하세요.

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

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

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

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

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

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

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

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