Menulis Plugin Evaluator Genkit

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

Definisi evaluator

Evaluator adalah fungsi yang menilai konten yang diberikan kepada 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 fungsi pengembangan perangkat lunak tradisional. Dalam penilaian berbasis LLM, konten dimasukkan kembali ke LLM, lalu LLM diminta untuk menilai output sesuai kriteria yang ditetapkan dalam prompt.

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:

  • Prompt
  • Fungsi penskoran
  • Tindakan evaluator

Menentukan perintah

Dalam contoh ini, prompt akan meminta LLM untuk menilai seberapa lezat output yang diberikan. Pertama, berikan konteks ke LLM, lalu jelaskan apa yang Anda inginkan, dan terakhir, berikan beberapa contoh sebagai dasar responsnya.

Genkit dilengkapi dengan dotprompt, yang menyediakan cara mudah untuk menentukan dan mengelola perintah dengan fitur seperti validasi skema input/output. Berikut cara menggunakan dotprompt untuk menentukan perintah 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 beri skor hasilnya. Kasus pengujian Genkit mencakup input sebagai kolom wajib diisi, dengan kolom opsional untuk output dan context. Adalah tanggung jawab evaluator untuk memvalidasi bahwa semua {i>field<i} yang diperlukan untuk evaluasi ada.

/**
 * 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 di Genkit terdiri dari 2 komponen:

  • Fungsi penskoran
  • Tindakan evaluator

Menentukan fungsi penskoran

Sama seperti evaluator berbasis LLM, tentukan fungsi skor. Dalam hal ini, fungsi penskoran tidak perlu mengetahui tentang 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 ketat dan bergantung pada jenis evaluator yang ditetapkan.

Anda setidaknya perlu menentukan metrik mana yang akan dicatat.

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 mana 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 ke framework melalui file genkit.config.ts dalam sebuah 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 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;

Konfigurasikan Genkit

Tambahkan plugin yang baru ditetapkan 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 untuk mengevaluasi kualitas output fitur AI generatif juga berlaku untuk mengevaluasi kapasitas penjurian dari evaluator berbasis LLM.

Untuk mendapatkan gambaran tentang apakah evaluator kustom berperforma pada tingkat yang diharapkan, buat kumpulan kasus pengujian yang memiliki jawaban benar dan salah yang jelas.

Untuk mengetahui kelezatannya, contohnya 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 yang 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.