Viết một trình bổ trợ Genkit Eliluator

Firebase Genkit có thể được mở rộng để hỗ trợ hoạt động đánh giá tuỳ chỉnh đối với kết quả của trường hợp kiểm thử, bằng cách sử dụng một mô hình ngôn ngữ lớn (LLM) làm thẩm phán hoặc chỉ sử dụng theo phương thức lập trình.

Định nghĩa của yếu tố đánh giá

Trình đánh giá là các hàm đánh giá nội dung do một mô hình ngôn ngữ lớn (LLM) cung cấp và tạo ra. Có hai phương pháp chính để đánh giá tự động (kiểm thử): đánh giá theo phương thức phỏng đoán và đánh giá dựa trên mô hình ngôn ngữ lớn (LLM). Trong phương pháp suy nghiệm, bạn định nghĩa một hàm quyết định giống như hàm trong quá trình phát triển phần mềm truyền thống. Trong quá trình đánh giá dựa trên LLM, nội dung được trả về một LLM và LLM được yêu cầu chấm điểm kết quả theo các tiêu chí được đặt ra trong một câu lệnh.

Người đánh giá dựa trên LLM

Người đánh giá dựa trên LLM sử dụng một LLM để đánh giá thông tin đầu vào, bối cảnh hoặc kết quả đầu ra của tính năng AI tạo sinh.

Nhân viên đánh giá dựa trên LLM trong Genkit bao gồm 3 thành phần:

  • Một câu lệnh
  • Hàm tính ��iểm
  • Hành động của bên đánh giá

Định nghĩa câu lệnh

Trong ví dụ này, câu lệnh sẽ yêu cầu LLM đánh giá mức độ ngon miệng của nội dung đầu ra. Trước tiên, hãy cung cấp ngữ cảnh cho LLM, sau đó mô tả những gì bạn muốn LLM này làm và cuối cùng là đưa ra một vài ví dụ để làm cơ sở cho câu trả lời của LLM.

Genkit đi kèm với dotprompt, giúp bạn dễ dàng xác định và quản lý lời nhắc bằng các tính năng như xác thực giản đồ đầu vào/đầu ra. Sau đây là cách bạn có thể sử dụng dotprompt để xác định một lời nhắc đánh giá.

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

Định nghĩa hàm tính điểm

Bây giờ, hãy xác định hàm sẽ lấy ví dụ bao gồm output theo yêu cầu của lời nhắc và tính điểm kết quả. Các trường hợp thử nghiệm Genkit bao gồm input theo trường bắt buộc, có các trường không bắt buộc cho outputcontext. Người đánh giá có trách nhiệm xác thực rằng tất cả các trường bắt buộc để đánh giá đều có sẵn.

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

Định nghĩa hành động của bên đánh giá

Bước cuối cùng là viết một hàm xác định chính hành động của bên đánh giá.

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

Chỉ số đánh giá hiệu quả

Công cụ đánh giá phỏng đoán có thể là bất kỳ hàm nào được dùng để đánh giá thông tin đầu vào, bối cảnh hoặc đầu ra của tính năng AI tạo sinh.

Bộ đánh giá phân tích chẩn đoán trong Genkit gồm 2 thành phần:

  • Hàm tính điểm
  • Hành động của bên đánh giá

Định nghĩa hàm tính điểm

Tương tự như người đánh giá dựa trên LLM, hãy xác định chức năng tính điểm. Trong trường hợp này, chức năng tính điểm không cần biết về mô hình ngôn ngữ lớn (LLM) của thẩm phán hay cấu hình của nó.

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

Định nghĩa hành động của bên đánh giá

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

Cấu hình

Tùy chọn trình bổ trợ

Xác định PluginOptions mà trình bổ trợ trình đánh giá tuỳ chỉnh sẽ sử dụng. Đối tượng này không có yêu cầu nghiêm ngặt và phụ thuộc vào loại người đánh giá được xác định.

Ít nhất thì bạn cần phải lấy định nghĩa về những chỉ số cần đăng ký.

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

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

Nếu trình bổ trợ mới này sử dụng một LLM (mô hình ngôn ngữ lớn) làm người thẩm phán và trình bổ trợ hỗ trợ hoán đổi LLM nào cần sử dụng, hãy xác định các tham số bổ sung trong đối tượng 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>;
}

Định nghĩa trình bổ trợ

Các trình bổ trợ được đăng ký với khung thông qua tệp genkit.config.ts trong một dự án. Để có thể định cấu hình một trình bổ trợ mới, hãy xác định một hàm định nghĩa GenkitPlugin và định cấu hình hàm đó bằng PluginOptions được xác định ở trên.

Trong trường hợp này, chúng ta có hai người đánh giá DELICIOUSNESSUS_PHONE_REGEX_MATCH. Đây là nơi những người đánh giá đó được đăng ký với trình bổ trợ và với 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;

Định cấu hình Genkit

Thêm trình bổ trợ mới xác định vào cấu hình Genkit của bạn.

Để đánh giá bằng Gemini, hãy tắt các chế độ cài đặt an toàn để người đánh giá có thể chấp nhận, phát hiện và cho điểm nội dung có khả năng gây hại.

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

Thử nghiệm

Việc đánh giá chất lượng kết quả đầu ra của một tính năng AI tạo sinh cũng áp dụng cho việc đánh giá khả năng đánh giá của người đánh giá dựa trên mô hình ngôn ngữ lớn (LLM).

Để biết liệu trình đánh giá tuỳ chỉnh có hoạt động ở mức dự kiến hay không, hãy tạo một nhóm trường hợp kiểm thử có câu trả lời đúng và sai rõ ràng.

Ví dụ về độ ngon. Tệp này có thể trông giống như tệp 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."
  }
]

Những ví dụ này có thể do con người tạo hoặc bạn có thể yêu cầu một LLM trợ giúp tạo một tập hợp các trường hợp kiểm thử có thể được tuyển chọn. Bạn cũng có thể sử dụng nhiều tập dữ liệu điểm chuẩn có sẵn.

Sau đó, hãy sử dụng Genkit CLI để chạy trình đánh giá dựa trên các trường hợp kiểm thử này.

genkit eval:run deliciousness_dataset.json

Xem kết quả của bạn trong giao diện người dùng Genkit.

genkit start

Chuyển đến localhost:4000/evaluate.