Pisanie wtyczki Genkit Evaluator

Firebase Genkit można rozszerzyć o obsługę niestandardowej oceny danych wyjściowych z danego przypadku testowego, wykorzystując LLM w roli jurora lub wyłącznie w sposób zautomatyzowany.

Definicja osoby oceniającej

Funkcje oceniające to funkcje, które oceniają treści przekazywane LLM i przez niego generowane. Istnieją 2 główne podejścia do automatycznej oceny (testowania): ocena heurystyczna i ocena oparta na LLM. W metodzie heurystycznej definiuje się funkcję deterministyczną, taką jak funkcja w przypadku tradycyjnego tworzenia oprogramowania. W ocenie opartej na LLM treść jest przekazywana z powrotem do LLM, a treść jest proszona o ocenę wyników zgodnie z kryteriami określonymi w prompcie.

Oceniający oparte na LLM

Weryfikator oparty na LLM wykorzystuje LLM do oceny danych wejściowych, kontekstu lub danych wyjściowych funkcji generatywnej AI.

Testy oparte na LLM w Genkit składają się z 3 komponentów:

  • Prompt
  • Funkcja punktacji
  • Działanie weryfikatora

Zdefiniuj prompt

W tym przykładzie prompt to prośba do LLM o ocenę jakości danych wyjściowych. Najpierw podaj kontekst LLM, opisz, czego od niego oczekujesz, a na koniec podaj kilka przykładów, na których możesz oprzeć jego reagowanie.

Wraz z Genkit dostajesz usługę dotprompt, która ułatwia definiowanie promptów i zarządzanie nimi za pomocą funkcji takich jak walidacja schematu danych wejściowych i wyjściowych. Oto, jak za pomocą parametru dotprompt zdefiniować prompt oceny.

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

Zdefiniuj funkcję punktacji

Teraz zdefiniuj funkcję, która wykorzysta przykład, który zawiera element output wymagany w prompcie, i oceń wynik. Przypadki testowe Genkit obejmują pole input jako wymagane oraz pola opcjonalne dotyczące pól output i context. Weryfikatorowi odpowiada za sprawdzenie, czy wszystkie pola wymagane do oceny zostały wypełnione.

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

Zdefiniuj działanie oceniającego

Ostatnim krokiem jest napisanie funkcji, która określa samo działanie oceniającego.

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

Oceniający heurystyczne

Weryfikator heurystyczny może być dowolną funkcją używaną do oceny danych wejściowych, kontekstu lub danych wyjściowych funkcji generatywnej AI.

Testery heurystyczne w Genkit składają się z 2 komponentów:

  • Funkcja punktacji
  • Działanie weryfikatora

Zdefiniuj funkcję punktacji

Podobnie jak w przypadku oceniającego opartego na LLM, zdefiniuj funkcję punktacji. W tym przypadku funkcja punktacji nie musi znać jurora LLM ani jego konfiguracji.

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

Zdefiniuj działanie oceniającego

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

Konfiguracja

Opcje wtyczki

Określ zasób PluginOptions, którego będzie używać niestandardowa wtyczka oceny. Ten obiekt nie ma ścisłych wymagań i zależy od zdefiniowanych typów weryfikatorów.

Wymagane jest minimum zdefiniowanie wskaźników, które można zarejestrować.

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

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

Jeśli nowa wtyczka używa LLM jako jurora, a wtyczka obsługuje zamianę tego, który z nich ma być używany, zdefiniuj dodatkowe parametry w obiekcie 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>;
}

Definicja wtyczki

Wtyczki są rejestrowane w ramach platformy za pomocą pliku genkit.config.ts w projekcie. Aby móc skonfigurować nową wtyczkę, zdefiniuj funkcję, która definiuje GenkitPlugin i konfiguruje ją za pomocą zdefiniowanej powyżej zasady PluginOptions.

W tym przypadku mamy 2 weryfikatorów: DELICIOUSNESS i US_PHONE_REGEX_MATCH. Tutaj weryfikatorzy są rejestrowani za pomocą wtyczki i 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;

Skonfiguruj Genkit

Dodaj nowo zdefiniowaną wtyczkę do konfiguracji Genkit.

Na potrzeby oceny przy użyciu Gemini wyłącz ustawienia bezpieczeństwa, aby weryfikator mógł zaakceptować, wykryć i ocenić potencjalnie szkodliwe treści oraz ocenić je.

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

Testowanie

Te same problemy, które dotyczą oceny jakości wyników działania funkcji generatywnej AI, mają zastosowanie do oceny zdolności oceniania weryfikatora opartego na LLM.

Aby mieć wyobrażenie, czy weryfikator niestandardowy ma skuteczność na oczekiwanym poziomie, utwórz zestaw przypadków testowych, w których odpowiedzi będą jasno poprawne.

Oto przykład smaku pliku 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."
  }
]

Te przykłady mogą być wygenerowane przez człowieka. Możesz też poprosić LLM o pomoc w utworzeniu zestawu przypadków testowych, które można wybrać. Dostępnych jest też wiele zbiorów danych porównawczych.

Następnie za pomocą interfejsu wiersza poleceń Genkit uruchom weryfikatora pod kątem tych przypadków testowych.

genkit eval:run deliciousness_dataset.json

Wyświetl wyniki w interfejsie Genkit.

genkit start

Wejdź na localhost:4000/evaluate.