كتابة المكوّن الإضافي Genkit Evaluator

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

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

أدوات التقييم هي وظائف تقيّم المحتوى المقدَّم للنموذج اللغوي الكبير وتنشئه. هناك منهجان رئيسيان للتقييم الآلي (الاختبار): التقييم الإرشادي والتقييم المستند إلى النموذج اللغوي الكبير. في النهج الإرشادية، يمكنك تحديد دالة حتمية مثل وظائف تطوير البرامج التقليدية. في التقييم المستند إلى "النموذج اللغوي الكبير"، تتم إعادة إرسال المحتوى إلى نموذج لغوي كبير (LLM)، وسيُطلب من النموذج اللغوي الكبير تسجيل النتائج وفقًا لمعايير محدّدة في الطلب.

مقيِّمون يستندون إلى النموذج اللغوي الكبير (LLM)

تستعين أداة التقييم المستندة إلى "النموذج اللغوي الكبير" (LLM) بالنموذج اللغوي الكبير لتقييم مدخلات أو سياق أو مخرجات ميزة الذكاء الاصطناعي التوليدي.

يتألّف المقيّمون استنادًا إلى النموذج اللغوي الكبير في GenK من 3 مكوّنات:

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

تحديد الطلب

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

تأتي Genkit مع dotprompt، التي توفر طريقة سهلة لتحديد الطلبات وإدارتها باستخدام ميزات مثل التحقق من مخطط الإدخال/الإخراج. في ما يلي طريقة استخدام dotprompt لتحديد طلب تقييم.

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

تحديد دالة التسجيل

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

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

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

الخطوة الأخيرة هي كتابة دالة تحدد إجراء المقيّم نفسه.

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

مقيّمون من خلال الإرشادات

يمكن أن يكون المقيِّم أي وظيفة تُستخدَم لتقييم المدخلات أو السياق أو المخرجات الخاصة بميزة الذكاء الاصطناعي التوليدي.

يتكون المقيّمون في Genkit من مكونَين:

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

تحديد دالة التسجيل

عليك تحديد دالة التسجيل، تمامًا مثل المقيّم المستند إلى النموذج اللغوي الكبير. في هذه الحالة، لا تحتاج دالة تسجيل النتائج إلى معرفة معلومات عن القاضي "النموذج اللغوي الكبير" (LLM) أو إعداداته.

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

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

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

الإعداد

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

حدِّد PluginOptions الذي سيستخدمه المكوّن الإضافي للمقيِّم المخصّص. ليس لهذا الكائن متطلبات صارمة ويعتمد على أنواع المقيّمين المحددة.

وعلى الأقل، سيحتاج إلى تحديد المقاييس المطلوب تسجيلها.

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

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

إذا كان هذا المكوّن الإضافي الجديد يستخدم نموذج لغوي كبير (LLM) كحكم وكان المكوّن الإضافي يتيح استبدال النموذج اللغوي الكبير (LLM) المطلوب، حدِّد معلَمات إضافية في الكائن 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>;
}

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

يتم تسجيل المكوّنات الإضافية في إطار العمل من خلال ملف genkit.config.ts في مشروع. لتتمكّن من إعداد مكوّن إضافي جديد، حدِّد دالة تعرِّف GenkitPlugin وتضبطها باستخدام PluginOptions المحدّد أعلاه.

في هذه الحالة، لدينا مقيّمان: DELICIOUSNESS وUS_PHONE_REGEX_MATCH. وهذا هو المكان الذي يتم فيه تسجيل هؤلاء المقيّمين في المكوّن الإضافي ومع 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;

إعداد Genkit

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

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

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

الاختبار

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

للتعرف على ما إذا كان المقيِّم المخصّص يعمل على المستوى المتوقع، أنشئ مجموعة من حالات الاختبار التي تحتوي على إجابة صحيحة وخاطئة.

وقد يبدو الملف بتنسيق json كمثال عن أطباق لذيذة، كملف 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."
  }
]

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

وبعد ذلك، استخدِم واجهة سطر الأوامر التوليدي لإجراء المقيِّم في حالات الاختبار هذه.

genkit eval:run deliciousness_dataset.json

يمكنك عرض النتائج في واجهة مستخدم Genkit.

genkit start

الانتقال إلى localhost:4000/evaluate