การเขียนปลั๊กอิน Genkit Evaluator

คุณสามารถขยายการใช้งาน Firebase Genkit เพื่อรองรับการประเมินที่กำหนดเองสำหรับผลลัพธ์ของกรอบการทดสอบ ไม่ว่าจะโดยใช้ LLM เป็นกรรมการ หรือโดยการใช้โปรแกรมเท่านั้น

คำจำกัดความของผู้ประเมิน

ผู้ประเมินเป็นฟังก์ชันที่ประเมินเนื้อหาที่ LLM มอบให้และสร้างขึ้น การประเมินอัตโนมัติ (ทดสอบ) มี 2 วิธีหลัก ได้แก่ การประเมินแบบศึกษาผู้เรียนและการประเมินโดยใช้ LLM ในแนวทางการเรียนรู้ คุณจะกำหนดฟังก์ชันเชิงกำหนดได้เหมือนกับการพัฒนาซอฟต์แวร์แบบดั้งเดิม ในการประเมินโดยใช้ LLM เนื้อหาจะส่งกลับไปยัง LLM และขอให้ LLM ให้คะแนนผลลัพธ์ตามเกณฑ์ที่กำหนดไว้ในพรอมต์

ผู้ประเมินที่ใช้ LLM

ผู้ประเมินที่ใช้ LLM ใช้ประโยชน์จาก LLM เพื่อประเมินอินพุต บริบท หรือผลลัพธ์ของฟีเจอร์ Generative AI

ผู้ประเมินที่ใช้ LLM ใน Genkit ประกอบด้วย 3 องค์ประกอบดังนี้

  • พรอมต์
  • ฟังก์ชันการให้คะแนน
  • การดำเนินการของผู้ประเมิน

กำหนดข้อความแจ้ง

สำหรับตัวอย่างนี้ พรอมต์จะขอให้ LLM ตัดสินความสวยงามของผลลัพธ์ที่ได้ ก่อนอื่นให้ให้บริบทกับ LLM จากนั้นอธิบายสิ่งที่คุณต้องการให้ LLM ทำ และสุดท้ายยกตัวอย่าง 2-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,
      };
    }
  );
}

ผู้ประเมินการประเมินพฤติกรรม

เครื่องมือประเมินการเรียนรู้อาจเป็นฟังก์ชันใดก็ได้ที่ใช้ในการประเมินอินพุต บริบท หรือผลลัพธ์ของฟีเจอร์ Generative AI

ผู้ประเมินการประเมินการเรียนรู้ใน Genkit ประกอบด้วย 2 องค์ประกอบดังนี้

  • ฟังก์ชันการให้คะแนน
  • การดำเนินการของผู้ประเมิน

กำหนดฟังก์ชันการให้คะแนน

กำหนดฟังก์ชันการให้คะแนน เช่นเดียวกับผู้ประเมินที่ใช้ LLM ในกรณีนี้ ฟังก์ชันการให้คะแนนไม่จำเป็นต้องทราบเกี่ยวกับผู้พิพากษา 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 ตามที่ระบุข้างต้น

ในกรณีนี้ เรามีผู้ประเมิน 2 ราย คือ 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
      ],
    }),
  ],
  ...
});

การทดสอบ

ปัญหาเดียวกันกับการประเมินคุณภาพผลลัพธ์ของฟีเจอร์ Generative AI จะใช้กับการประเมินความสามารถในการตัดสินของผู้ประเมินที่ใช้ LLM ด้วย

หากต้องการทราบว่าผู้ประเมินที่กำหนดเองทำงานได้ในระดับที่คาดไว้หรือไม่ ให้สร้างชุดกรอบการทดสอบที่มีคำตอบที่ถูกต้องและผิดอย่างชัดเจน

เพื่อเป็นตัวอย่างความน่าสนใจ รูปแบบดังกล่าวอาจมีลักษณะเหมือนไฟล์ 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

ดูผลลัพธ์ใน UI ของ Genkit

genkit start

นำทางไปยัง localhost:4000/evaluate