编写 Genkit Evaluator 插件

您可以使用 LLM 作为评委,也可以仅以编程方式对 Firebase Genkit 进行扩展,以支持对测试用例输出进行自定义评估。

评估器定义

评估员是指评估提供给 LLM 并由 LLM 生成的内容的功能。自动化评估(测试)主要有两种方法:启发式评估和基于 LLM 的评估。在启发式方法中,您可以定义确定性函数(类似于传统软件开发的函数)。在基于 LLM 的评估中,系统会将内容反馈给 LLM,并要求 LLM 根据提示中设置的标准给输出打分。

基于 LLM 的评估程序

基于 LLM 的评估程序利用 LLM 来评估生成式 AI 功能的输入、上下文或输出。

Genkit 中基于 LLM 的评估程序由 3 个部分组成:

  • 提示
  • 评分函数
  • 评估器操作

定义提示

在本示例中,提示会要求 LLM 判断输出内容的美味程度。首先,为 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 为必填字段,还包含 outputcontext 的可选字段。评估员负责验证评估所需的所有字段是否都存在。

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

启发式评估器

启发式评估程序可以是任何用于评估生成式 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 对其进行配置。

在本例中,我们有两个评估器 DELICIOUSNESSUS_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
      ],
    }),
  ],
  ...
});

测试

在评估生成式 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

在 Genkit 界面中查看结果。

genkit start

导航到 localhost:4000/evaluate