كتابة تقييم Genkit

يمكنك توسيع نطاق Firebase Genkit لتتوافق مع التقييم المخصّص، وذلك باستخدام نموذج تعلم لغوي كبير (LLM) كمحكّم أو من خلال التقييم الآلي (التخميني).

تعريف المقيّم

التقييمات هي دوالّ تقيِّم ردّ نموذج اللغة الضخمة. هناك طريقتان رئيسيتان للتقييم الآلي: التقييم الاستقرائي والتقييم استنادًا إلى نموذج اللغة الضخمة. في المنهج الاستكشافي، يمكنك تحديد دالة حتمية. في المقابل، في التقييم المستنِد إلى النماذج اللغوية الكبيرة، تتم إعادة تقديم المحتوى إلى نموذج لغوي كبير، ويُطلب من النموذج اللغوي الكبير تقييم النتيجة وفقًا للمعايير المحدّدة في طلب.

تتيح لك الطريقة ai.defineEvaluator، التي تستخدمها لتحديد ai.defineEvaluatorإجراء تقييم في Genkit، استخدام أي من الطريقتَين. يستكشف هذا المستند بعض الأمثلة على كيفية استخدام هذه الطريقة للتقييمات المستندة إلى الأساليب الاستقرائية ونموذج اللغة الضخمة.

المقيّمون المستندون إلى نموذج اللغة الضخمة

يستفيد التقييم المستنِد إلى نموذج لغوي كبير من نموذج لغوي كبير لتقييم input وcontext وoutput لميزة الذكاء الاصطناعي التوليدي.

تتكوّن أدوات التقييم المستندة إلى النماذج اللغوية الكبيرة (LLM) في Genkit من 3 مكوّنات:

  • طلب
  • دالة تسجيل النقاط
  • إجراء تقييم

تحديد الطلب

في هذا المثال، يستفيد المُقيّم من نموذج تعلُّم لغوي كبير (LLM) لتحديد ما إذا كان أحد أطباق الطعام (output) لذيذًا أم لا. أولاً، قدِّم سياقًا للنموذج اللغوي الكبير، ثم صف ما تريده منه أن يفعله، وأخيراً، قدِّم له بعض الأمثلة التي يستند إليها في ردّه.

توفّر الأداة definePrompt في Genkit طريقة سهلة لتحديد طلبات المستخدمين مع التحقّق من الإدخال والإخراج. التعليمة البرمجية التالية هي مثال على إعداد طلب تقييم باستخدام definePrompt.

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

تحديد وظيفة التسجيل

حدِّد دالة تأخذ مثالاً يتضمّن output كما هو مطلوب في الطلب، وتُحسِّن النتيجة. تتضمّن اختبارات Genkit input كحقل مطلوب، مع output وcontext كحقول اختيارية. تقع على عاتق المقيّم مهمة التحقّق من توفّر جميع الحقول المطلوبة لتقييم الأداء.

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

تحديد إجراء المُقيِّم

الخطوة الأخيرة هي كتابة دالة تحدِّد EvaluatorAction.

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 أدوات إنشاء Genkit الأخرى، مثل defineFlow وdefineRetriever. تتطلّب هذه الطريقة تقديم EvaluatorFn كوسيطة ردّ اتصال. تقبل طريقة EvaluatorFn كائنًا من نوع BaseEvalDataPoint، والذي يتوافق مع إدخال واحد في مجموعة بيانات قيد التقييم، بالإضافة إلى مَعلمة خيارات مخصّصة اختيارية إذا تم تحديدها. تعالج الدالة نقطة البيانات وتُرجع عنصرًا من النوع EvalResponse.

في ما يلي مخطّطات Zod لكلّ من BaseEvalDataPoint وEvalResponse:

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 للمستخدم تقديم اسم واسم معروض يسهل على المستخدم قراءته وتعريف للمقيّم. يتم عرض الاسم المعروض و التعريف مع نتائج التقييم في واجهة مستخدم المطوّر. يتضمّن أيضًا حقل isBilled اختياريًا يحدِّد ما إذا كان هذا المُقيِّم يمكن أن يؤدّي إلى الفوترة (على سبيل المثال، إذا كان يستخدم نموذجًا لغويًا كبيرًا أو واجهة برمجة تطبيقات يتم تحصيل رسوم استخدامها). إذا تم تحصيل رسوم من أحد المقيمين، تطلب واجهة المستخدم من المستخدم تأكيدًا في سطر الأوامر قبل السماح له بإجراء تقييم. تساعد هذه الخطوة في تجنُّب النفقات غير المقصودة.

أدوات التقييم المستندة إلى الإعدادات الإرشادية

يمكن أن يكون المُقيّم الاستقرائي أيّ دالة تُستخدَم لتقييم input أو context أو أو output لميزة الذكاء الاصطناعي التوليدي.

تتألف أدوات التقييم الاستكشافي في Genkit من مكوّنين:

  • دالة تسجيل النقاط
  • إجراء تقييم

تحديد وظيفة التسجيل

كما هو الحال مع المُقيِّم المستنِد إلى نموذج اللغة الضخمة، حدِّد دالة التقييم. في هذه الحالة، لا تحتاج دالة التسجيل إلى نموذج لغة طبيعية لمحكّم.

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

تحديد إجراء المُقيِّم

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

تجميع العناصر معًا

تعريف المكوّن الإضافي

يتم تسجيل المكوّنات الإضافية في إطار العمل من خلال تثبيتها في وقت بدء Genkit. لتحديد مكوّن إضافي جديد، استخدِم genkitPlugin طريقة لإنشاء مثيل لجميع إجراءات Genkit ضمن سياق المكوّن الإضافي.

يعرض نموذج الرمز هذا اثنين من المقيّمين: مقيّم الطعم المستنِد إلى نموذج اللغة الكبيرة، ومقيّم رقم الهاتف في الولايات المتحدة المستنِد إلى التعبير العادي. يؤدي إنشاء مثيل لهذه التقييمات ضمن سياق المكوّن الإضافي إلى تسجيلها في المكوّن الإضافي.

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

أضِف المكوّن الإضافي myCustomEvals إلى إعدادات Genkit.

لإجراء التقييم باستخدام Gemini، أوقِف إعدادات السلامة كي يتمكّن المُقيّم من قبول المحتوى الذي يُحتمل أن يكون ضارًا ورصده وتقييمه.

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

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

استخدام المقيّمين المخصّصين

بعد إنشاء مثيل للمقيّمين المخصّصين في سياق تطبيق Genkit (إما من خلال مكوّن إضافي أو مباشرةً)، يصبحون جاهزين للاستخدام. يوضّح المثال التالي كيفية تجربة أداة تقييم اللذة باستخدام بعض نماذج المدخلات والمخرجات.

  • 1. أنشئ ملفًا بتنسيق 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."
  }
]
  • 2- استخدِم Genkit CLI لتشغيل أداة التقييم على حالات الاختبار هذه.
# Start your genkit runtime
genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
  • 3- انتقِل إلى localhost:4000/evaluate لعرض النتائج في واجهة مستخدم Genkit.

من المهمّ ملاحظة أنّ الثقة في المقيّمين المخصّصين تزداد مع مقارنة أدائهم بمجموعات البيانات أو الأساليب العادية. كرِّر تقييم نتائج هذه المقاييس لتحسين أداء المقيّمين إلى أن يتم بلوغ المستوى المستهدف من الجودة.