Genkit Evaluator 플러그인 작성

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

평가자 정의

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

LLM 기반 평가자

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

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

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

프롬프트 정의

이 예에서 프롬프트는 LLM에 출력이 얼마나 만족스러운지 판단하도록 요청합니다. 먼저 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 테스트 사례에는 필수 필드인 inputoutputcontext에 대한 선택사항 필드가 포함됩니다. 평가에 필요한 모든 필드가 있는지 확인하는 것은 평가자의 책임입니다.

/**
 * 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 { geminiPro } from '@genkit-ai/googleai';

export default configureGenkit({
  plugins: [
    ...
    myAwesomeEval({
      judge: geminiPro,
      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로 이동합니다.