编写 Genkit Evaluator 插件

您可以扩展 Firebase Genkit,以便通过使用 LLM 作为评委或仅以程序化方式对测试用例输出进行自定义评估。

评估器定义

评估器是指用于评估提供给 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 { 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
      ],
    }),
  ],
  ...
});

测试

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