Dotprompt로 프롬프트 관리

Firebase Genkit는 생성형 AI 프롬프트를 작성하고 정리하는 데 도움이 되는 Dotprompt 플러그인과 텍스트 형식을 제공합니다.

Dotprompt는 프롬프트는 코드라는 전제를 바탕으로 설계되었습니다. dotprompt 파일이라고 하는 특수 형식의 파일로 프롬프트를 작성하고 유지관리하고 코드에 사용하는 것과 동일한 버전 제어 시스템을 사용하여 변경사항을 추적하고 생성형 AI 모델을 호출하는 코드와 함께 배포합니다.

Dotprompt를 사용하려면 먼저 프로젝트 루트에 prompts 디렉터리를 만든 다음 이 디렉터리에 .prompt 파일을 만듭니다. 다음은 greeting.prompt를 호출할 수 있는 간단한 예입니다.

---
model: vertexai/gemini-1.0-pro
config:
  temperature: 0.9
input:
  schema:
    location: string
    style?: string
    name?: string
  default:
    location: a restaurant
---

You are the world's most welcoming AI assistant and are currently working at {{location}}.

Greet a guest{{#if name}} named {{name}}{{/if}}{{#if style}} in the style of {{style}}{{/if}}.

이 프롬프트를 사용하려면 dotprompt 플러그인을 설치하고 @genkit-ai/dotprompt 라이브러리에서 prompt 함수를 가져옵니다.

import { dotprompt, prompt } from '@genkit-ai/dotprompt';

configureGenkit({ plugins: [dotprompt()] });

그런 다음 prompt('file_name')를 사용하여 프롬프트를 로드합니다.

const greetingPrompt = await prompt('greeting');

const result = await greetingPrompt.generate({
  input: {
    location: 'the beach',
    style: 'a fancy pirate',
  },
});

console.log(result.text());

Dotprompt의 구문은 Handlebars 템플릿 언어를 기반으로 합니다. if, unless, each 도우미를 사용하여 프롬프트에 조건부 부분을 추가하거나 구조화된 콘텐츠를 반복할 수 있습니다. 이 파일 형식은 YAML 인터페이스를 활용하여 템플릿과 함께 프롬프트의 메타데이터를 제공합니다.

Picoschema로 입력/출력 스키마 정의

Dotprompt에는 Picoschema라고 하는 YAML에 최적화된 간단한 스키마 정의 형식이 포함되어 있어 LLM 사용에 대한 스키마의 가장 중요한 속성을 쉽게 정의할 수 있습니다. 다음은 기사의 스키마 예입니다.

schema:
  title: string # string, number, and boolean types are defined like this
  subtitle?: string # optional fields are marked with a `?`
  draft?: boolean, true when in draft state
  status?(enum, approval status): [PENDING, APPROVED]
  date: string, the date of publication e.g. '2024-04-09' # descriptions follow a comma
  tags(array, relevant tags for article): string # arrays are denoted via parentheses
  authors(array):
    name: string
    email?: string
  metadata?(object): # objects are also denoted via parentheses
    updatedAt?: string, ISO timestamp of last update
    approvedBy?: integer, id of approver

위의 스키마는 다음 TypeScript 인터페이스와 동일합니다.

interface Article {
  title: string;
  subtitle?: string;
  /** true when in draft state */
  draft?: boolean;
  /** approval status */
  status?: 'PENDING' | 'APPROVED';
  /** the date of publication e.g. '2024-04-09' */
  date: string;
  /** relevant tags for article */
  tags: string[];
  authors: {
    name: string;
    email?: string;
  }[];
  metadata?: {
    /** ISO timestamp of last update */
    updatedAt?: string;
    /** id of approver */
    approvedBy?: number;
  };
}

Picoschema는 스칼라 유형 string, integer, number, boolean를 지원합니다. 객체, 배열 및 enum의 경우 필드 이름 뒤에 괄호로 표시됩니다.

Picoschema로 정의된 객체는 ?에 의해 선택사항으로 표시되지 않는 한 모든 필수 속성을 가지며 추가 속성을 허용하지 않습니다.

Picoschema는 전체 JSON 스키마의 기능 대부분을 지원하지 않습니다. 더 강력한 스키마가 필요한 경우 대신 JSON 스키마를 제공할 수 있습니다.

output:
  schema:
    type: object
    properties:
      field1:
        type: number
        minimum: 20

프롬프트 메타데이터 재정의

.prompt 파일을 사용하면 파일 자체에 모델 구성과 같은 메타데이터를 삽입할 수 있지만 호출별로 이러한 값을 재정의할 수도 있습니다.

const result = await greetingPrompt.generate({
  model: 'google-genai/gemini-pro',
  config: {
    temperature: 1.0,
  },
  input: {
    location: 'the beach',
    style: 'a fancy pirate',
  },
});

구조화된 출력

JSON으로 강제 변환하도록 프롬프트의 형식 및 출력 스키마를 설정할 수 있습니다.

---
model: vertexai/gemini-1.0-pro
input:
  schema:
    theme: string
output:
  format: json
  schema:
    name: string
    price: integer
    ingredients(array): string
---

Generate a menu item that could be found at a {{theme}} themed restaurant.

구조화된 출력으로 프롬프트를 생성할 때는 output() 도우미를 사용하여 프롬프트를 검색하고 검증합니다.

const createMenuPrompt = await prompt('create_menu');

const menu = await createMenuPrompt.generate({
  input: {
    theme: 'banana',
  },
});

console.log(menu.output());

다중 메시지 프롬프트

기본적으로 Dotprompt는 "user" 역할이 있는 단일 메시지를 구성합니다. 일부 프롬프트는 시스템 프롬프트와 같은 여러 메시지의 조합으로 가장 잘 표현됩니다.

{{role}} 도우미는 다중 메시지 프롬프트를 구성하는 간단한 방법을 제공합니다.

---
model: vertexai/gemini-1.0-pro
input:
  schema:
    userQuestion: string
---

{{role "system"}}
You are a helpful AI assistant that really loves to talk about food. Try to work
food items into all of your conversations.
{{role "user"}}
{{userQuestion}}

다중 모달 프롬프트

텍스트와 함께 이미지와 같은 멀티모달 입력을 지원하는 모델의 경우 {{media}} 도우미를 사용할 수 있습니다.

---
model: vertexai/gemini-1.0-pro-vision
input:
  schema:
    photoUrl: string
---

Describe this image in a detailed paragraph:

{{media url=photoUrl}}

URL은 https:// 또는 '인라인' 이미지 사용을 위해 base64로 인코딩된 data: URI일 수 있습니다. 코드는 다음과 같습니다.

const describeImagePrompt = await prompt('describe_image');

const result = await describeImagePrompt.generate({
  input: {
    photoUrl: 'https://example.com/image.png',
  },
});

console.log(result.text());

프롬프트 변형

프롬프트 파일은 텍스트에 불과하므로 버전 제어 시스템에 커밋할 수 있으며 또 그래야만 합니다. 따라서 시간 경과에 따른 변경사항을 쉽게 비교할 수 있습니다. 프롬프트의 수정된 버전은 프로덕션 환경에서 기존 버전과 병행하여만 완전히 테스트할 수 있는 경우가 많습니다. Dotprompt는 변형 기능을 통해 이를 지원합니다.

변형을 만들려면 [name].[variant].prompt 파일을 만듭니다. 예를 들어 프롬프트에서 Gemini 1.0 Pro를 사용 중이지만 Gemini 1.5 Pro의 성능이 더 우수한지 확인하려면 다음 두 파일을 만들면 됩니다.

  • my_prompt.prompt: '기준' 프롬프트
  • my_prompt.gemini15.prompt: 'gemini'라는 변이

프롬프트 변형을 사용하려면 로드 시 variant 옵션을 지정합니다.

const myPrompt = await prompt('my_prompt', { variant: 'gemini15' });

프롬프트 로더는 해당 이름의 변형을 로드하려고 시도하고 존재하지 않을 경우 기준으로 돌아갑니다. 즉, 애플리케이션에 적합한 모든 기준에 따라 조건부 로드를 사용할 수 있습니다.

const myPrompt = await prompt('my_prompt', {
  variant: isBetaTester(user) ? 'gemini15' : null,
});

변이의 이름은 생성 트레이스의 메타데이터에 포함되므로 Genkit 트레이스 검사기에서 변이 간의 실제 성능을 비교하고 대조할 수 있습니다.

프롬프트를 로드하고 정의하는 다른 방법

Dotprompt는 프롬프트 디렉토리의 구성에 최적화되어 있습니다. 그러나 프롬프트를 로드하고 정의하는 다른 몇 가지 방법이 있습니다.

  • loadPromptFile: 프롬프트 디렉터리의 파일에서 프롬프트를 로드합니다.
  • loadPromptUrl: URL에서 프롬프트를 로드합니다.
  • defineDotprompt: 코드에 프롬프트를 정의합니다.

예:

import {
  loadPromptFile,
  loadPromptUrl,
  defineDotprompt,
} from '@genkit-ai/dotprompt';
import path from 'path';
import { z } from 'zod';

// Load a prompt from a file
const myPrompt = await loadPromptFile(
  path.resolve(__dirname, './path/to/my_prompt.prompt')
);

// Load a prompt from a URL
const myPrompt = await loadPromptUrl('https://example.com/my_prompt.prompt');

// Define a prompt in code
const myPrompt = defineDotprompt(
  {
    model: 'vertexai/gemini-1.0-pro',
    input: {
      schema: z.object({
        name: z.string(),
      }),
    },
  },
  `Hello {{name}}, how are you today?`
);