Viết Trình đánh giá Genkit

Bạn có thể mở rộng Firebase Genkit để hỗ trợ tính năng đánh giá tuỳ chỉnh, sử dụng LLM làm trình đánh giá hoặc đánh giá có lập trình (tìm kiếm quy tắc).

Định nghĩa về trình đánh giá

Trình đánh giá là các hàm đánh giá phản hồi của LLM. Có hai phương pháp chính để đánh giá tự động: đánh giá phỏng đoán và đánh giá dựa trên LLM. Trong phương pháp phỏng đoán, bạn xác định một hàm xác định. Ngược lại, trong quy trình đánh giá dựa trên LLM, nội dung được đưa trở lại LLM và LLM được yêu cầu tính điểm cho kết quả đầu ra theo các tiêu chí được đặt trong câu lệnh.

Phương thức ai.defineEvaluator mà bạn sử dụng để xác định hành động của trình đánh giá trong Genkit hỗ trợ cả hai phương pháp. Tài liệu này khám phá một số ví dụ về cách sử dụng phương thức này cho các hoạt động đánh giá dựa trên phương pháp phỏng đoán và LLM.

Trình đánh giá dựa trên LLM

Trình đánh giá dựa trên LLM tận dụng LLM để đánh giá input, contextoutput của tính năng AI tạo sinh.

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

  • Lời nhắc
  • Hàm tính điểm
  • Hành động của trình đánh giá

Xác định lời nhắc

Trong ví dụ này, trình đánh giá tận dụng LLM để xác định xem một món ăn (output) có ngon hay không. 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 làm và cuối cùng, hãy đưa ra một vài ví dụ để LLM dựa vào đó để phản hồi.

Tiện ích definePrompt của Genkit cung cấp một cách dễ dàng để xác định lời nhắc bằng tính năng xác thực đầu vào và đầu ra. Đoạn mã sau đây là ví dụ về cách thiết lập lời nhắc đánh giá bằng 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,
      }
      prompt: `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:
      `
  });
}

Xác định hàm tính điểm

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

import { ModelArgument } from 'genkit';
import { BaseEvalDataPoint, Score } from 'genkit/evaluator';

/**
 * Score an individual test case for delciousness.
 */
export async function deliciousnessScore<
  CustomModelOptions extends z.ZodTypeAny,
>(
  ai: Genkit,
  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 },
  };
}

Xác định hành động của trình đánh giá

Bước cuối cùng là viết một hàm xác định EvaluatorAction.

import { 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(ai, judge, datapoint, judgeConfig);
      return {
        testCaseId: datapoint.testCaseId,
        evaluation: score,
      };
    }
  );
}

Phương thức defineEvaluator tương tự như các hàm khởi tạo Genkit khác như defineFlowdefineRetriever. Phương thức này yêu cầu phải cung cấp EvaluatorFn làm lệnh gọi lại. Phương thức EvaluatorFn chấp nhận đối tượng BaseEvalDataPoint, tương ứng với một mục trong tập dữ liệu đang được đánh giá, cùng với tham số tuỳ chọn tuỳ chọn tuỳ chỉnh nếu được chỉ định. Hàm này xử lý điểm dữ liệu và trả về một đối tượng EvalResponse.

Giản đồ Zod cho BaseEvalDataPointEvalResponse như sau.

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

Đối tượng defineEvaluator cho phép người dùng cung cấp tên, tên hiển thị dễ đọc và định nghĩa cho trình đánh giá. Tên hiển thị và định nghĩa sẽ xuất hiện cùng với kết quả đánh giá trong Giao diện người dùng dành cho nhà phát triển. Tệp này cũng có một trường isBilled không bắt buộc để đánh dấu xem trình đánh giá này có thể dẫn đến việc thanh toán hay không (ví dụ: sử dụng LLM hoặc API đã thanh toán). Nếu người đánh giá được tính phí, giao diện người dùng sẽ nhắc người dùng xác nhận trong CLI trước khi cho phép họ chạy quy trình đánh giá. Bước này giúp ngăn chặn các khoản chi tiêu ngoài ý muốn.

Trình đánh giá quy trình phỏng đoán

Trình đánh giá phỏng đoán có thể là bất kỳ hàm nào dùng để đánh giá input, context hoặc output của tính năng AI tạo sinh.

Trình đánh giá phỏng đoán trong Genkit bao gồm 2 thành phần:

  • Hàm tính điểm
  • Hành động của trình đánh giá

Xác định hàm tính điểm

Cũng như với trình đánh giá dựa trên LLM, hãy xác định hàm tính điểm. Trong trường hợp này, hàm tính điểm không cần LLM của người đánh giá.

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

Xác định hành động của trình đánh giá

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

Tổng hợp kiến thức đã học

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

Trình bổ trợ được đăng ký với khung bằng cách cài đặt các trình bổ trợ đó tại thời điểm khởi chạy Genkit. Để xác định một trình bổ trợ mới, hãy sử dụng phương thức trình trợ giúp genkitPlugin để tạo bản sao cho tất cả thao tác Genkit trong ngữ cảnh trình bổ trợ.

Mẫu mã này cho thấy hai trình đánh giá: trình đánh giá độ ngon dựa trên LLM và trình đánh giá số điện thoại ở Hoa Kỳ dựa trên biểu thức chính quy. Việc tạo bản sao cho các trình đánh giá này trong ngữ cảnh trình bổ trợ sẽ đăng ký các trình đánh giá đó với trình bổ trợ.

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;

Định cấu hình Genkit

Thêm trình bổ trợ myCustomEvals vào cấu hình Genkit.

Để đá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à tính điểm nội dung có khả năng gây hại.

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

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

Sử dụng trình đánh giá tuỳ chỉnh

Sau khi bạn tạo bản sao của trình đánh giá tuỳ chỉnh trong ngữ cảnh ứng dụng Genkit (thông qua trình bổ trợ hoặc trực tiếp), bạn có thể sử dụng các trình đánh giá đó. Ví dụ sau đây minh hoạ cách thử trình đánh giá độ ngon với một số dữ liệu đầu vào và đầu ra mẫu.

  • 1. Tạo tệp json `deliciousness_dataset.json` có nội dung sau:
[
  {
    "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. Sử dụng Genkit CLI để chạy trình đánh giá theo các trường hợp kiểm thử này.
# Start your genkit runtime
genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
  • 3. Chuyển đến `localhost:4000/evaluate` để xem kết quả trong giao diện người dùng Genkit.

Điều quan trọng cần lưu ý là độ tin cậy của các trình đánh giá tuỳ chỉnh sẽ tăng lên khi bạn đo điểm chuẩn cho các trình đánh giá đó bằng các tập dữ liệu hoặc phương pháp tiêu chuẩn. Lặp lại các kết quả của các điểm chuẩn đó để cải thiện hiệu suất của người đánh giá cho đến khi đạt được mức chất lượng mục tiêu.