Pisanie wtyczki Genkit Evaluator

Pakiet Firebase Genkit można rozszerzyć tak, aby obsługiwał niestandardową ocenę danych wyjściowych przypadku testowego za pomocą LLM do oceny lub całkowicie automatycznie.

Definicja oceniającego

Oceniający to funkcje, które oceniają treści przekazywane do modelu 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 definiujesz funkcję deterministyczną, taką jak w przypadku tradycyjnego tworzenia oprogramowania. W przypadku oceny opartej na LLM treść jest dostarczana z powrotem do LLM, a LLM jest proszony o ocenę danych wyjściowych zgodnie z kryteriami określonymi w prompcie.

Oceniający korzystający z LLM

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

Oceniający korzystający z LLM w Genkit składają się z 3 elementów:

  • Prompt
  • Funkcja punktacji
  • Działanie osoby oceniającej

Zdefiniuj prompt

W tym przykładzie prompt będzie polegać na tym, że LLM ma ocenić, jak pyszne są dane wyjściowe. Najpierw podaj LLM kontekst, potem opisz, co ma się stać, a na koniec podaj kilka przykładów, na których można oprzeć odpowiedź.

Genkit zawiera usługę dotprompt, która ułatwia definiowanie promptów i zarządzanie nimi za pomocą funkcji takich jak weryfikacja schematu danych wejściowych i wyjściowych. Oto jak możesz użyć funkcji dotprompt do zdefiniowania prośby o ocenę.

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 użyje przykładu zawierającego output w sposób wymagany przez prompt, i oceń wynik. Przypadki testowe Genkit zawierają pole wymagane input oraz opcjonalne pola output i context. Obowiązkiem osoby oceniającej jest sprawdzenie, czy dostępne są wszystkie pola wymagane do oceny.

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

Oceny heurystyczne

Ocenianiem heurystycznym może być dowolna funkcja używana do oceny danych wejściowych, kontekstu lub wyników funkcji generatywnej AI.

Oceniający heurystycznie w Genkit składają się z 2 elementów:

  • Funkcja punktacji
  • Działanie osoby oceniającej

Zdefiniuj funkcję punktacji

Zdefiniuj funkcję punktacji tak samo jak w przypadku modelu LLM. W tym przypadku funkcja punktacji nie musi mieć informacji o już LLM ani o 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 wtyczek

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

W tym przypadku chodzi o definicję wskaźników do zarejestrowania.

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 do oceny, a obsługa umożliwia zamianę LLM, 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 platformie za pomocą pliku genkit.config.ts w projekcie. Aby móc skonfigurować nową wtyczkę, zdefiniuj funkcję, która definiuje GenkitPlugin, i skonfiguruje ją za pomocą zdefiniowanej powyżej PluginOptions.

W tym przypadku mamy 2 weryfikatorów: DELICIOUSNESS i US_PHONE_REGEX_MATCH. To właśnie tutaj weryfikatorzy są zarejestrowani we wtyczce i w 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 w Gemini wyłącz ustawienia bezpieczeństwa, aby weryfikator mógł zaakceptować, wykryć i ocenić potencjalnie szkodliwe treści.

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

Testowanie

Te same problemy, które dotyczą oceny jakości wyników funkcji generatywnej AI, również mają zastosowanie do oceny zdolności sędziowskiego tego narzędzia.

Aby przekonać się, czy narzędzie do oceniania niestandardowego działa na oczekiwanym poziomie, utwórz zestaw przypadków testowych z jasną prawidłową i nieprawidłową odpowiedzią.

Przykładem może być plik 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."
  }
]

Przykłady te mogą zostać wygenerowane przez człowieka lub możesz poprosić LLM o pomoc w utworzeniu zestawu przypadków testowych, które można wyselekcjonować. Istnieje też wiele zbiorów danych porównawczych, których można użyć.

Następnie użyj interfejsu wiersza poleceń Genkit, aby przeprowadzić ocenę w tych przypadkach testowych.

genkit eval:run deliciousness_dataset.json

Wyświetl wyniki w interfejsie użytkownika Genkit.

genkit start

Wejdź na localhost:4000/evaluate.