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

يمكن توسيع نطاق أدوات Firebase Genkit للسماح بإجراء تقييم مخصّص لنتائج حالات الاختبار، إمّا باستخدام النموذج اللغوي الكبير كحكم أو آليًا بشكل كامل.

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

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

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

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

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

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

تحديد الطلب

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

تتوفّر أداة 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 من مكوّنَين:

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

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

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

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

الاختبار

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

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

كمثال على طعم اللذيذ، قد يبدو ذلك مثل ملف 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 CLI لتشغيل المقيِّم في حالات الاختبار هذه.

genkit eval:run deliciousness_dataset.json

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

genkit start

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