Genkit Değerlendiricisi Yazma

Firebase Genkit'i, özel değerlendirmeyi desteklemek için hakim olarak bir LLM kullanarak veya programatik (heuristic) değerlendirmeyle genişletebilirsiniz.

Değerlendirici tanımı

Değerlendiriciler, LLM'nin yanıtını değerlendiren işlevlerdir. Otomatik değerlendirmeye iki ana yaklaşım vardır: sezgisel değerlendirme ve LLM tabanlı değerlendirme. Heuristic yaklaşımda, deterministik bir işlev tanımlarsınız. Buna karşılık, LLM tabanlı bir değerlendirmede içerik bir LLM'ye geri beslenir ve LLM'den çıkışı bir istemde belirtilen ölçütlere göre puanlaması istenir.

Genkit'te bir değerlendirici işlemi tanımlamak için kullandığınız ai.defineEvaluator yöntemi, her iki yaklaşımı da destekler. Bu dokümanda, bu yöntemin sezgisel ve LLM tabanlı değerlendirmeler için nasıl kullanılacağına dair birkaç örnek incelenmektedir.

LLM tabanlı değerlendirme araçları

LLM tabanlı bir değerlendirici, üretken yapay zeka özelliğinizin input, context ve output özelliklerini değerlendirmek için LLM'den yararlanır.

Genkit'teki LLM tabanlı değerlendiriciler 3 bileşenden oluşur:

  • İstem
  • Puanlama işlevi
  • Değerlendirici işlemi

İstemi tanımlama

Bu örnekte, değerlendirici bir yemeğin (output) lezzetli olup olmadığını belirlemek için LLM'den yararlanır. Öncelikle LLM'ye bağlam bilgisi verin, ardından ne yapmasını istediğinizi açıklayın ve son olarak yanıtını temel alacağı birkaç örnek verin.

Genkit'in definePrompt yardımcı programı, giriş ve çıkış doğrulamasıyla istemleri tanımlamanın kolay bir yolunu sunar. Aşağıdaki kod, definePrompt ile değerlendirme istemi oluşturmaya dair bir örnektir.

import { z } from "genkit";

const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const;

const DeliciousnessDetectionResponseSchema = z.object({
  reason: z.string(),
  verdict: z.enum(DELICIOUSNESS_VALUES),
});

function getDeliciousnessPrompt(ai: Genkit) {
  return  ai.definePrompt({
      name: 'deliciousnessPrompt',
      input: {
        schema: z.object({
          responseToTest: z.string(),
        }),
      },
      output: {
        schema: 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: {{ responseToTest }}
    Response:
    `
  );
}

Puanlama işlevini tanımlama

İstem tarafından gerektiği gibi output içeren bir örnek alan ve sonucu puanlayan bir işlev tanımlayın. Genkit test örnekleri, zorunlu alan olarak input ve isteğe bağlı alanlar olarak output ve context içerir. Değerlendirme için gerekli tüm alanların mevcut olduğunu doğrulamak değerlendirmecinin sorumluluğundadır.

import { ModelArgument, z } from 'genkit';
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 and generate an evaluation result
  const deliciousnessPrompt = getDeliciousnessPrompt(ai);
  const response = await deliciousnessPrompt(
    {
      responseToTest: d.output as string,
    },
    {
      model: judgeLlm,
      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 },
  };
}

Değerlendirici işlemini tanımlama

Son adım, EvaluatorAction değerini tanımlayan bir işlev yazmaktır.

import { Genkit, z } from 'genkit';
import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';

/**
 * Create the Deliciousness evaluator action.
 */
export function createDeliciousnessEvaluator<
  ModelCustomOptions extends z.ZodTypeAny,
>(
  ai: Genkit,
  judge: ModelArgument<ModelCustomOptions>,
  judgeConfig?: z.infer<ModelCustomOptions>
): EvaluatorAction {
  return ai.defineEvaluator(
    {
      name: `myCustomEvals/deliciousnessEvaluator`,
      displayName: 'Deliciousness',
      definition: 'Determines if output is considered delicous.',
      isBilled: true,
    },
    async (datapoint: BaseEvalDataPoint) => {
      const score = await deliciousnessScore(judge, datapoint, judgeConfig);
      return {
        testCaseId: datapoint.testCaseId,
        evaluation: score,
      };
    }
  );
}

defineEvaluator yöntemi, defineFlow ve defineRetriever gibi diğer Genkit kurucularına benzer. Bu yöntem için geri çağırma olarak bir EvaluatorFn sağlanmalıdır. EvaluatorFn yöntemi, değerlendirilmekte olan bir veri kümesinde tek bir girişe karşılık gelen bir BaseEvalDataPoint nesnesini ve isteğe bağlı olarak belirtilmişse özel seçenekler parametresini kabul eder. İşlev, veri noktasını işler ve bir EvalResponse nesnesi döndürür.

BaseEvalDataPoint ve EvalResponse için Zod şemaları aşağıdaki gibidir.

BaseEvalDataPoint
export const BaseEvalDataPoint = z.object({
  testCaseId: z.string(),
  input: z.unknown(),
  output: z.unknown().optional(),
  context: z.array(z.unknown()).optional(),
  reference: z.unknown().optional(),
  testCaseId: z.string().optional(),
  traceIds: z.array(z.string()).optional(),
});

export const EvalResponse = z.object({
  sampleIndex: z.number().optional(),
  testCaseId: z.string(),
  traceId: z.string().optional(),
  spanId: z.string().optional(),
  evaluation: z.union([ScoreSchema, z.array(ScoreSchema)]),
});
ScoreSchema
const ScoreSchema = z.object({
  id: z.string().describe('Optional ID to differentiate multiple scores').optional(),
  score: z.union([z.number(), z.string(), z.boolean()]).optional(),
  error: z.string().optional(),
  details: z
    .object({
      reasoning: z.string().optional(),
    })
    .passthrough()
    .optional(),
});

defineEvaluator nesnesi, kullanıcının değerlendirici için bir ad, kullanıcı tarafından okunabilen bir görünen ad ve bir tanım sağlamasına olanak tanır. Görünen ad ve tanım, değerlendirme sonuçlarıyla birlikte Geliştirici kullanıcı arayüzünde gösterilir. Ayrıca, bu değerlendiricinin faturalandırmayla sonuçlanıp sonuçlanmayacağını (ör. faturalandırılan bir LLM veya API kullanıp kullanmadığını) belirten isteğe bağlı bir isBilled alanı da vardır. Bir değerlendiriciye fatura gönderilirse kullanıcı arayüzü, değerlendirme yapmalarına izin vermeden önce kullanıcıdan CLI'de onay ister. Bu adım, istenmeyen harcamalardan korunmanıza yardımcı olur.

Buluşsal Yöntemlerle Değerlendirme Araçları

Heuristic evaluator, üretken yapay zeka özelliğinizin input, context veya output değerini değerlendirmek için kullanılan herhangi bir işlev olabilir.

Genkit'teki sezgisel değerlendiriciler 2 bileşenden oluşur:

  • Puanlama işlevi
  • Değerlendirici işlemi

Puanlama işlevini tanımlama

LLM tabanlı değerlendiricide olduğu gibi puanlama işlevini tanımlayın. Bu durumda, puanlama işlevinin bir hakim LLM'ye ihtiyacı yoktur.

import { EvalResponses } from 'genkit';
import { BaseEvalDataPoint, Score } from 'genkit/evaluator';

const US_PHONE_REGEX =
  /[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4}/i;

/**
 * Scores whether a datapoint output contains a US Phone number.
 */
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 US_PHONE_REGEX`
    : `Output did not match US_PHONE_REGEX`;
  return {
    score: matches,
    details: { reasoning },
  };
}

Değerlendirici işlemini tanımlama

import { Genkit } from 'genkit';
import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';

/**
 * Configures a regex evaluator to match a US phone number.
 */
export function createUSPhoneRegexEvaluator(ai: Genkit): EvaluatorAction {
  return ai.defineEvaluator(
    {
      name: `myCustomEvals/usPhoneRegexEvaluator`,
      displayName: "Regex Match for US PHONE NUMBER",
      definition: "Uses Regex to check if output matches a US phone number",
      isBilled: false,
    },
    async (datapoint: BaseEvalDataPoint) => {
      const score = await usPhoneRegexScore(datapoint);
      return {
        testCaseId: datapoint.testCaseId,
        evaluation: score,
      };
    }
  );
}

Tümünü bir araya getirme

Eklenti tanımı

Genkit başlatılırken eklentiler çerçeveye kaydedilir. Yeni bir eklenti tanımlamak için genkitPlugin yardımcı yöntemini kullanarak tüm Genkit işlemlerini eklenti bağlamında örnekleyin.

Bu kod örneğinde iki değerlendirici gösterilmektedir: LLM tabanlı lezzetlilik değerlendirici ve normal ifade tabanlı ABD telefon numarası değerlendirici. Bu değerlendirmecilerin eklenti bağlamında oluşturulması, bunları eklentiye kaydeder.

import { GenkitPlugin, genkitPlugin } from 'genkit/plugin';

export function myCustomEvals<
  ModelCustomOptions extends z.ZodTypeAny
>(options: {
  judge: ModelArgument<ModelCustomOptions>;
  judgeConfig?: ModelCustomOptions;
}): GenkitPlugin {
  // Define the new plugin
  return genkitPlugin("myCustomEvals", async (ai: Genkit) => {
    const { judge, judgeConfig } = options;

    // The plugin instatiates our custom evaluators within the context
    // of the `ai` object, making them available
    // throughout our Genkit application.
    createDeliciousnessEvaluator(ai, judge, judgeConfig);
    createUSPhoneRegexEvaluator(ai);
  });
}
export default myCustomEvals;

Genkit'i yapılandırma

myCustomEvals eklentisini Genkit yapılandırmanıza ekleyin.

Gemini ile değerlendirme için güvenlik ayarlarını devre dışı bırakın. Böylece değerlendirici, zararlı olabilecek içerikleri kabul edebilir, algılayabilir ve puanlayabilir.

import { gemini15Pro } from '@genkit-ai/googleai';

const ai = genkit({
  plugins: [
    vertexAI(),
    ...
    myCustomEvals({
      judge: gemini15Pro,
    }),
  ],
  ...
});

Özel değerlendiricilerinizi kullanma

Özel değerlendiricilerinizi Genkit uygulama bağlamında (bir eklenti aracılığıyla veya doğrudan) oluşturduktan sonra kullanılabilir hale gelirler. Aşağıdaki örnekte, lezzet değerlendirme aracının birkaç örnek giriş ve çıkışla nasıl deneneceği gösterilmektedir.

  • 1. Aşağıdaki içeriğe sahip bir "deliciousness_dataset.json" JSON dosyası oluşturun:
[
  {
    "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."
  }
]
  • 2. Değerlendiriciyi bu test durumlarına karşı çalıştırmak için Genkit CLI'yi kullanın.
# Start your genkit runtime
genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
  • 3. Sonuçlarınızı Genkit kullanıcı arayüzünde görüntülemek için "localhost:4000/evaluate" adresine gidin.

Özel değerlendiricileri standart veri kümeleriyle veya yaklaşımlarla karşılaştırdıkça özel değerlendiricilere olan güvenin arttığını unutmayın. Değerlendiricilerinizin performansını hedeflenen kalite düzeyine ulaşana kadar iyileştirmek için bu tür karşılaştırmaların sonuçlarını tekrarlayın.