Menulis Evaluator Genkit

Firebase Genkit dapat diperluas untuk mendukung evaluasi kustom output kasus pengujian, baik dengan menggunakan LLM sebagai hakim, atau sepenuhnya secara terprogram.

Definisi evaluator

Evaluator adalah fungsi yang menilai konten yang diberikan ke dan dihasilkan oleh LLM. Ada dua pendekatan utama untuk evaluasi otomatis (pengujian): penilaian heuristik dan penilaian berbasis LLM. Dalam pendekatan heuristik, Anda menentukan fungsi deterministik seperti pengembangan software tradisional. Dalam penilaian berbasis LLM, konten akan dimasukkan kembali ke LLM dan LLM diminta untuk menilai output sesuai dengan kriteria yang ditetapkan dalam perintah.

Evaluator berbasis LLM

Evaluator berbasis LLM memanfaatkan LLM untuk mengevaluasi input, konteks, atau output fitur AI generatif Anda.

Evaluator berbasis LLM di Genkit terdiri dari 3 komponen:

  • Perintah
  • Fungsi penskoran
  • Tindakan evaluator

Menentukan perintah

Untuk contoh ini, perintah akan meminta LLM untuk menilai seberapa lezat output-nya. Pertama, berikan konteks ke LLM, lalu jelaskan apa yang Anda inginkan, dan terakhir, berikan beberapa contoh untuk mendasarkan responsnya.

Utilitas definePrompt Genkit menyediakan cara mudah untuk menentukan perintah dengan validasi input dan output. Berikut cara menyiapkan perintah evaluasi dengan 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:
  `
);

Menentukan fungsi penskoran

Sekarang, tentukan fungsi yang akan mengambil contoh yang menyertakan output seperti yang diperlukan oleh perintah dan beri skor pada hasilnya. Kasus pengujian Genkit menyertakan input sebagai kolom wajib diisi, dengan kolom opsional untuk output dan context. Evaluator bertanggung jawab untuk memvalidasi bahwa semua kolom yang diperlukan untuk evaluasi sudah ada.

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 },
  };
}

Menentukan tindakan evaluator

Langkah terakhir adalah menulis fungsi yang menentukan tindakan evaluator itu sendiri.

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

Evaluator Heuristik

Evaluator heuristik dapat berupa fungsi apa pun yang digunakan untuk mengevaluasi input, konteks, atau output fitur AI generatif Anda.

Evaluator heuristik di Genkit terdiri dari 2 komponen:

  • Fungsi penskoran
  • Tindakan evaluator

Menentukan fungsi penskoran

Sama seperti evaluator berbasis LLM, tentukan fungsi penskoran. Dalam hal ini, fungsi penskoran tidak perlu mengetahui LLM hakim atau konfigurasinya.

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 },
  };
}

Menentukan tindakan evaluator

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);
      }
    );
  });
}

Konfigurasi

Opsi Plugin

Tentukan PluginOptions yang akan digunakan plugin evaluator kustom. Objek ini tidak memiliki persyaratan ketat dan bergantung pada jenis evaluator yang ditentukan.

Setidaknya, metrik ini harus mengambil definisi metrik yang akan didaftarkan.

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

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

Jika plugin baru ini menggunakan LLM sebagai hakim dan plugin mendukung penggantian LLM yang akan digunakan, tentukan parameter tambahan dalam objek 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>;
}

Definisi plugin

Plugin didaftarkan dengan framework melalui file genkit.config.ts dalam project. Agar dapat mengonfigurasi plugin baru, tentukan fungsi yang menentukan GenkitPlugin dan mengonfigurasinya dengan PluginOptions yang ditentukan di atas.

Dalam hal ini, kita memiliki dua evaluator DELICIOUSNESS dan US_PHONE_REGEX_MATCH. Di sinilah evaluator tersebut terdaftar dengan plugin dan dengan 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;

Mengonfigurasi Genkit

Tambahkan plugin yang baru ditentukan ke konfigurasi Genkit Anda.

Untuk evaluasi dengan Gemini, nonaktifkan setelan keamanan agar evaluator dapat menerima, mendeteksi, dan memberi skor konten yang berpotensi berbahaya.

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
      ],
    }),
  ],
  ...
});

Pengujian

Masalah yang sama yang berlaku untuk mengevaluasi kualitas output fitur AI generatif juga berlaku untuk mengevaluasi kapasitas penilaian evaluator berbasis LLM.

Untuk mengetahui apakah evaluator kustom berperforma sesuai dengan tingkat yang diharapkan, buat serangkaian kasus pengujian yang memiliki jawaban benar dan salah yang jelas.

Sebagai contoh untuk kelezatan, yang mungkin terlihat seperti file 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."
  }
]

Contoh ini dapat dibuat oleh manusia atau Anda dapat meminta LLM untuk membantu membuat serangkaian kasus pengujian yang dapat diseleksi. Ada banyak set data benchmark yang tersedia dan juga dapat digunakan.

Kemudian, gunakan Genkit CLI untuk menjalankan evaluator terhadap kasus pengujian ini.

genkit eval:run deliciousness_dataset.json

Lihat hasil Anda di UI Genkit.

genkit start

Buka localhost:4000/evaluate.