Genkit 평가자 작성

Firebase Genkit은 LLM을 심사자로 사용하거나 순수하게 프로그래매틱 방식으로 테스트 사례 출력의 맞춤 평가를 지원하도록 확장할 수 있습니다.

평가자 정의

평가자는 LLM에 제공되고 LLM에서 생성된 콘텐츠를 평가하는 함수입니다. 자동 평가 (테스트)에는 휴리스틱 평가와 LLM 기반 평가라는 두 가지 주요 접근 방식이 있습니다. 휴리스틱 접근 방식에서는 기존 소프트웨어 개발의 함수와 같이 결정론적 함수를 정의합니다. LLM 기반 평가에서는 콘텐츠가 LLM에 다시 제공되고 LLM에 프롬프트에 설정된 기준에 따라 출력의 점수를 매기도록 요청됩니다.

LLM 기반 평가자

LLM 기반 평가자는 LLM을 활용하여 생성형 AI 기능의 입력, 컨텍스트 또는 출력을 평가합니다.

Genkit의 LLM 기반 평가자는 다음 세 가지 구성요소로 구성됩니다.

  • 프롬프트
  • 점수 함수
  • 평가자 작업

프롬프트 정의

이 예에서는 프롬프트가 LLM에 출력물의 맛을 판단해 달라고 요청합니다. 먼저 LLM에 컨텍스트를 제공한 다음, LLM이 수행할 작업을 설명하고, 마지막으로 대답의 기반이 될 몇 가지 예시를 제공합니다.

Genkit의 definePrompt 유틸리티는 입력 및 출력 유효성 검사를 사용하여 프롬프트를 간편하게 정의할 수 있는 방법을 제공합니다. definePrompt를 사용하여 평가 메시지를 설정하는 방법은 다음과 같습니다.

const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const;

const DeliciousnessDetectionResponseSchema = z.object({
  reason: z.string(),
  verdict: z.enum(DELICIOUSNESS_VALUES),
});
type DeliciousnessDetectionResponse = z.infer<typeof DeliciousnessDetectionResponseSchema>;

const DELICIOUSNESS_PROMPT = ai.definePrompt(
  {
    name: 'deliciousnessPrompt',
    inputSchema: z.object({
      output: z.string(),
    }),
    outputSchema: DeliciousnessDetectionResponseSchema,
  },
  `You are a food critic. Assess whether the provided output sounds delicious, giving only "yes" (delicious), "no" (not delicious), or "maybe" (undecided) as the verdict.

  Examples:
  Output: Chicken parm sandwich
  Response: { "reason": "A classic and beloved dish.", "verdict": "yes" }

  Output: Boston Logan Airport tarmac
  Response: { "reason": "Not edible.", "verdict": "no" }

  Output: A juicy piece of gossip
  Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" }

  New Output:
  {{output}}
  Response:
  `
);

점수 함수 정의

이제 프롬프트에 필요한 output가 포함된 예시를 가져와 결과를 점수화하는 함수를 정의합니다. Genkit 테스트 사례에는 input가 필수 필드로 포함되어 있으며 outputcontext의 선택 필드가 있습니다. 평가에 필요한 모든 필드가 있는지 확인하는 것은 평가자의 책임입니다.

import { BaseEvalDataPoint, Score } from 'genkit/evaluator';

/**
 * Score an individual test case for delciousness.
 */
export async function deliciousnessScore<
  CustomModelOptions extends z.ZodTypeAny,
>(
  judgeLlm: ModelArgument<CustomModelOptions>,
  dataPoint: BaseEvalDataPoint,
  judgeConfig?: CustomModelOptions
): Promise<Score> {
  const d = dataPoint;
  // Validate the input has required fields
  if (!d.output) {
    throw new Error('Output is required for Deliciousness detection');
  }

  //Hydrate the prompt
  const finalPrompt = DELICIOUSNESS_PROMPT.renderText({
    output: d.output as string,
  });

  // Call the LLM to generate an evaluation result
  const response = await generate({
    model: judgeLlm,
    prompt: finalPrompt,
    config: judgeConfig,
  });

  // Parse the output
  const parsedResponse = response.output;
  if (!parsedResponse) {
    throw new Error(`Unable to parse evaluator response: ${response.text}`);
  }

  // Return a scored response
  return {
    score: parsedResponse.verdict,
    details: { reasoning: parsedResponse.reason },
  };
}

평가자 작업 정의

마지막 단계는 평가자 작업 자체를 정의하는 함수를 작성하는 것입니다.

import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';

/**
 * Create the Deliciousness evaluator action.
 */
export function createDeliciousnessEvaluator<
  ModelCustomOptions extends z.ZodTypeAny,
>(
  judge: ModelReference<ModelCustomOptions>,
  judgeConfig: z.infer<ModelCustomOptions>
): EvaluatorAction {
  return defineEvaluator(
    {
      name: `myAwesomeEval/deliciousness`,
      displayName: 'Deliciousness',
      definition: 'Determines if output is considered delicous.',
    },
    async (datapoint: BaseEvalDataPoint) => {
      const score = await deliciousnessScore(judge, datapoint, judgeConfig);
      return {
        testCaseId: datapoint.testCaseId,
        evaluation: score,
      };
    }
  );
}

휴리스틱 평가자

휴리스틱 평가자는 생성형 AI 기능의 입력, 컨텍스트 또는 출력을 평가하는 데 사용되는 모든 함수일 수 있습니다.

Genkit의 휴리스틱 평가자는 다음 두 가지 구성요소로 구성됩니다.

  • 점수 함수
  • 평가자 작업

점수 함수 정의

LLM 기반 평가자와 마찬가지로 점수 함수를 정의합니다. 이 경우 점수 함수는 심사 LLM 또는 구성에 관해 알 필요가 없습니다.

import { BaseEvalDataPoint, Score } from 'genkit/evaluator';

const US_PHONE_REGEX =
  /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4}$/i;

/**
 * Scores whether an individual datapoint matches a US Phone Regex.
 */
export async function usPhoneRegexScore(
  dataPoint: BaseEvalDataPoint
): Promise<Score> {
  const d = dataPoint;
  if (!d.output || typeof d.output !== 'string') {
    throw new Error('String output is required for regex matching');
  }
  const matches = US_PHONE_REGEX.test(d.output as string);
  const reasoning = matches
    ? `Output matched regex ${regex.source}`
    : `Output did not match regex ${regex.source}`;
  return {
    score: matches,
    details: { reasoning },
  };
}

평가자 작업 정의

import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';

/**
 * Configures a regex evaluator to match a US phone number.
 */
export function createUSPhoneRegexEvaluator(
  metrics: RegexMetric[]
): EvaluatorAction[] {
  return metrics.map((metric) => {
    const regexMetric = metric as RegexMetric;
    return defineEvaluator(
      {
        name: `myAwesomeEval/${metric.name.toLocaleLowerCase()}`,
        displayName: 'Regex Match',
        definition:
          'Runs the output against a regex and responds with 1 if a match is found and 0 otherwise.',
        isBilled: false,
      },
      async (datapoint: BaseEvalDataPoint) => {
        const score = await regexMatchScore(datapoint, regexMetric.regex);
        return fillScores(datapoint, score);
      }
    );
  });
}

구성

플러그인 옵션

맞춤 평가자 플러그인이 사용할 PluginOptions를 정의합니다. 이 객체에는 엄격한 요구사항이 없으며 정의된 평가자의 유형에 따라 다릅니다.

최소한 등록할 측정항목의 정의를 가져와야 합니다.

export enum MyAwesomeMetric {
  WORD_COUNT = 'WORD_COUNT',
  US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH',
}

export interface PluginOptions {
  metrics?: Array<MyAwesomeMetric>;
}

이 새 플러그인이 LLM을 판사로 사용하고 플러그인이 사용할 LLM 교체를 지원하는 경우 PluginOptions 객체에 추가 매개변수를 정의합니다.

export enum MyAwesomeMetric {
  DELICIOUSNESS = 'DELICIOUSNESS',
  US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH',
}

export interface PluginOptions<ModelCustomOptions extends z.ZodTypeAny> {
  judge: ModelReference<ModelCustomOptions>;
  judgeConfig?: z.infer<ModelCustomOptions>;
  metrics?: Array<MyAwesomeMetric>;
}

플러그인 정의

플러그인은 프로젝트의 genkit.config.ts 파일을 통해 프레임워크에 등록됩니다. 새 플러그인을 구성하려면 GenkitPlugin를 정의하고 위에 정의된 PluginOptions로 구성하는 함수를 정의합니다.

이 경우 두 개의 평가자 DELICIOUSNESSUS_PHONE_REGEX_MATCH가 있습니다. 여기에서 이러한 평가자가 플러그인 및 Firebase Genkit에 등록됩니다.

export function myAwesomeEval<ModelCustomOptions extends z.ZodTypeAny>(
  options: PluginOptions<ModelCustomOptions>
): PluginProvider {
  // Define the new plugin
  const plugin = (options?: MyPluginOptions<ModelCustomOptions>) => {
    return genkitPlugin(
    'myAwesomeEval',
    async (ai: Genkit) => {
      const { judge, judgeConfig, metrics } = options;
      const evaluators: EvaluatorAction[] = metrics.map((metric) => {
        switch (metric) {
          case DELICIOUSNESS:
            // This evaluator requires an LLM as judge
            return createDeliciousnessEvaluator(ai, judge, judgeConfig);
          case US_PHONE_REGEX_MATCH:
            // This evaluator does not require an LLM
            return createUSPhoneRegexEvaluator();
        }
      });
      return { evaluators };
    })
  }
  // Create the plugin with the passed options
  return plugin(options);
}
export default myAwesomeEval;

Genkit 구성

새로 정의된 플러그인을 Genkit 구성에 추가합니다.

Gemini를 사용한 평가의 경우 평가자가 잠재적으로 유해한 콘텐츠를 수락, 감지, 점수 매기기를 할 수 있도록 안전 설정을 사용 중지합니다.

import { gemini15Flash } from '@genkit-ai/googleai';

const ai = genkit({
  plugins: [
    ...
    myAwesomeEval({
      judge: gemini15Flash,
      judgeConfig: {
        safetySettings: [
          {
            category: 'HARM_CATEGORY_HATE_SPEECH',
            threshold: 'BLOCK_NONE',
          },
          {
            category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
            threshold: 'BLOCK_NONE',
          },
          {
            category: 'HARM_CATEGORY_HARASSMENT',
            threshold: 'BLOCK_NONE',
          },
          {
            category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
            threshold: 'BLOCK_NONE',
          },
        ],
      },
      metrics: [
        MyAwesomeMetric.DELICIOUSNESS,
        MyAwesomeMetric.US_PHONE_REGEX_MATCH
      ],
    }),
  ],
  ...
});

테스트

생성형 AI 기능의 출력 품질을 평가하는 데 적용되는 문제는 LLM 기반 평가자의 판단 능력을 평가하는 데도 적용됩니다.

맞춤 평가자가 예상 수준으로 작동하는지 확인하려면 명확한 정답과 오답이 있는 테스트 사례 집합을 만듭니다.

맛있는 음식의 예를 들면 JSON 파일 deliciousness_dataset.json과 같습니다.

[
  {
    "testCaseId": "delicous_mango",
    "input": "What is a super delicious fruit",
    "output": "A perfectly ripe mango – sweet, juicy, and with a hint of tropical sunshine."
  },
  {
    "testCaseId": "disgusting_soggy_cereal",
    "input": "What is something that is tasty when fresh but less tasty after some time?",
    "output": "Stale, flavorless cereal that's been sitting in the box too long."
  }
]

이러한 예시는 사람이 생성할 수도 있고, 선별할 수 있는 테스트 사례 집합을 만드는 데 LLM의 도움을 요청할 수도 있습니다. 사용할 수 있는 벤치마크 데이터 세트도 많이 있습니다.

그런 다음 Genkit CLI를 사용하여 이러한 테스트 사례에 대해 평가자를 실행합니다.

genkit eval:run deliciousness_dataset.json

Genkit UI에서 결과를 확인합니다.

genkit start

localhost:4000/evaluate로 이동합니다.