Genkit Evaluator 플러그인 작성

Firebase Genkit를 확장하여 테스트 사례 결과물의 커스텀 평가를 지원할 수 있습니다. LLM을 심사위원으로 사용하거나 순수하게 프로그래매틱 방식으로 사용할 수도 있습니다.

평가자 정의

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

LLM 기반 평가자

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

Genkit의 LLM 기반 평가자는 3가지 구성요소로 이루어져 있습니다.

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

프롬프트 정의

이 예에서 프롬프트는 LLM에 출력이 얼마나 맛있는지 판단하도록 요청합니다. 먼저 LLM에 컨텍스트를 제공하고 원하는 작업을 설명하고 마지막으로 대답의 기반이 되는 몇 가지 예시를 제공합니다.

Genkit에는 dotprompt가 포함되어 있어 입력/출력 스키마 검증과 같은 기능을 통해 프롬프트를 쉽게 정의하고 관리할 수 있습니다. 다음은 dotprompt를 사용하여 평가 프롬프트를 정의하는 방법입니다.

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

// Define the expected output values
const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const;

// Define the response schema expected from the LLM
const DeliciousnessDetectionResponseSchema = z.object({
  reason: z.string(),
  verdict: z.enum(DELICIOUSNESS_VALUES),
});
type DeliciousnessDetectionResponse = z.infer<
  typeof DeliciousnessDetectionResponseSchema
>;

const DELICIOUSNESS_PROMPT = defineDotprompt(
  {
    input: {
      schema: z.object({
        output: z.string(),
      }),
    },
    output: {
      schema: DeliciousnessDetectionResponseSchema,
    },
  },
  `You are a food critic with a wide range in taste. Given the output, decide if it sounds delicious and provide your reasoning. Use only "yes" (if delicous), "no" (if not delicious), "maybe" (if you can't decide) as the verdict.

Here are a few examples:

Output:
Chicken parm sandwich
Response:
{ "reason": "This is a classic sandwich enjoyed by many - totally delicious", "verdict":"yes"}

Output:
Boston logan international airport tarmac
Response:
{ "reason": "This is not edible and definitely not delicious.", "verdict":"no"}

Output:
A juicy piece of gossip
Response:
{ "reason": "Gossip is sometimes metaphorically referred to as tasty.", "verdict":"maybe"}

Here is a new submission to assess:

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

점수 함수 정의

이제 프롬프트에서 필요에 따라 output를 포함하는 예를 사용하는 함수를 정의하고 결과에 점수를 매깁니다. Genkit 테스트 사례에는 필수 필드로 input가 포함되며 outputcontext의 선택사항 필드가 포함됩니다. 평가에 필요한 모든 필드가 있는지 검증하는 것은 평가자의 책임입니다.

/**
 * Score an individual test case for delciousness.
 */
export async function deliciousnessScore<
  CustomModelOptions extends z.ZodTypeAny,
>(
  judgeLlm: ModelArgument<CustomModelOptions>,
  dataPoint: BaseDataPoint,
  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 },
  };
}

평가자 작업 정의

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

/**
 * 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: BaseDataPoint) => {
      const score = await deliciousnessScore(judge, datapoint, judgeConfig);
      return {
        testCaseId: datapoint.testCaseId,
        evaluation: score,
      };
    }
  );
}

휴리스틱 평가자

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

Genkit의 휴리스틱 평가자는 두 가지 구성요소로 이루어져 있습니다.

  • 점수 함수
  • 평가자 작업

점수 함수 정의

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

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: BaseDataPoint
): 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 },
  };
}

평가자 작업 정의

/**
 * 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: BaseDataPoint) => {
        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>(
  params: PluginOptions<ModelCustomOptions>
): PluginProvider {
  // Define the new plugin
  const plugin = genkitPlugin(
    'myAwesomeEval',
    async (params: PluginOptions<ModelCustomOptions>) => {
      const { judge, judgeConfig, metrics } = params;
      const evaluators: EvaluatorAction[] = metrics.map((metric) => {
        // We'll create these functions in the next step
        switch (metric) {
          case DELICIOUSNESS:
            // This evaluator requires an LLM as judge
            return createDeliciousnessEvaluator(judge, judgeConfig);
          case US_PHONE_REGEX_MATCH:
            // This evaluator does not require an LLM
            return createUSPhoneRegexEvaluator();
        }
      });
      return { evaluators };
    }
  );

  // Create the plugin with the passed params
  return plugin(params);
}
export default myAwesomeEval;

Genkit 구성

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

Gemini로 평가할 때는 평가자가 잠재적으로 유해한 콘텐츠를 허용, 감지하고 점수를 매길 수 있도록 안전 설정을 사용 중지하세요.

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

export default configureGenkit({
  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로 이동합니다.