Menulis Plugin Evaluator Genkit

Firebase Genkit dapat diperluas untuk mendukung evaluasi kustom atas output kasus pengujian, baik dengan menggunakan LLM sebagai juri, maupun secara terprogram.

Definisi {i>evaluator<i}

Evaluator adalah fungsi yang menilai konten yang diberikan kepada dan dihasilkan oleh LLM. Ada dua pendekatan utama terhadap evaluasi otomatis (pengujian): penilaian heuristik dan penilaian berbasis LLM. Dalam pendekatan heuristik, Anda mendefinisikan fungsi deterministik seperti pengembangan perangkat lunak tradisional. Pada penilaian berbasis LLM, konten dimasukkan kembali ke LLM dan LLM akan 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, perintahnya akan meminta LLM untuk menilai seberapa lezat outputnya. Pertama, berikan konteks kepada LLM, lalu jelaskan apa yang Anda ingin LLM lakukan, dan terakhir, berikan beberapa contoh sebagai dasar responsnya.

Genkit dilengkapi dengan dotprompt, yang memberikan cara mudah untuk menentukan dan mengelola prompt dengan fitur seperti validasi skema input/output. Berikut cara menggunakan dotprompt untuk menentukan dialog evaluasi.

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:
`
);

Menentukan fungsi penskoran

Sekarang, tentukan fungsi yang akan mengambil contoh yang menyertakan output seperti yang diperlukan oleh perintah dan skor hasilnya. Kasus pengujian Genkit mencakup input sebagai kolom wajib diisi, dengan kolom opsional untuk output dan context. Evaluator bertanggung jawab untuk memvalidasi bahwa semua {i>field<i} yang diperlukan untuk evaluasi sudah tersedia.

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

Menentukan tindakan evaluator

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

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

Evaluator Heuristik

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

Evaluator heuristik dalam Genkit terdiri dari 2 komponen:

  • Fungsi penskoran
  • Tindakan evaluator

Menentukan fungsi penskoran

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

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

Menentukan tindakan 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: BaseDataPoint) => {
        const score = await regexMatchScore(datapoint, regexMetric.regex);
        return fillScores(datapoint, score);
      }
    );
  });
}

Konfigurasi

Opsi Plugin

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

Setidaknya Anda perlu menentukan 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 juri dan plugin mendukung penukaran LLM yang akan digunakan, tentukan parameter tambahan di 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 didaftarkan dengan plugin dan 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;

Mengonfigurasi Genkit

Tambahkan plugin yang baru ditentukan ke konfigurasi Genkit Anda.

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

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

Pengujian

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

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

Sebagai contoh kelezatan, file tersebut 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 {i>dataset<i} tolok ukur yang tersedia dan 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.