Puedes extender Firebase Genkit para admitir la evaluación personalizada, ya sea con un LLM como juez o con una evaluación programática (heurística).
Definición de evaluador
Los evaluadores son funciones que evalúan la respuesta de un LLM. Existen dos enfoques principales para la evaluación automatizada: la evaluación heurística y la basada en LLM. En el enfoque heurístico, defines una función determinista. Por el contrario, en una evaluación basada en LLM, el contenido se vuelve a enviar a un LLM, y se le pide que califique el resultado según los criterios establecidos en una instrucción.
El método ai.defineEvaluator
, que usas para definir una acción de evaluador en Genkit, admite cualquiera de los enfoques. En este
documento, se exploran algunos ejemplos de cómo usar este
método para evaluaciones heurísticas y basadas en LLM.
Evaluadores basados en LLM
Un evaluador basado en LLM aprovecha un LLM para evaluar input
, context
y output
de tu función de IA generativa.
Los evaluadores basados en LLM en Genkit se componen de 3 componentes:
- Una instrucción
- Una función de puntuación
- Una acción de evaluador
Define la instrucción
En este ejemplo, el evaluador aprovecha un LLM para determinar si un alimento (el output
) es delicioso o no. Primero, proporciona contexto al LLM, luego describe lo que quieres que haga y, por último, dale algunos ejemplos en los que basar su respuesta.
La utilidad definePrompt
de Genkit proporciona una forma fácil de definir instrucciones con validación de entrada y salida. El siguiente código es un ejemplo de cómo configurar una instrucción de evaluación con 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:
`
});
}
Define la función de puntuación
Define una función que tome un ejemplo que incluya output
como requiere la instrucción y califique el resultado. Los casos de prueba de Genkit incluyen input
como campo obligatorio, con output
y context
como campos opcionales.
Es responsabilidad del evaluador validar que todos los campos necesarios para la evaluación estén presentes.
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 },
};
}
Define la acción del evaluador
El último paso es escribir una función que defina el 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,
};
}
);
}
El método defineEvaluator
es similar a otros constructores de Genkit, como defineFlow
y defineRetriever
. Este método requiere que se proporcione un EvaluatorFn
como devolución de llamada. El método EvaluatorFn
acepta un objeto BaseEvalDataPoint
, que corresponde a una sola entrada en un conjunto de datos en evaluación, junto con un parámetro de opciones personalizadas opcional si se especifica. La función procesa el dato y muestra un objeto EvalResponse
.
Los esquemas de Zod para BaseEvalDataPoint
y EvalResponse
son los siguientes.
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(),
});
El objeto defineEvaluator
permite que el usuario proporcione un nombre, un nombre visible legible por el usuario y una definición para el evaluador. El nombre visible y la definición se muestran junto con los resultados de la evaluación en la IU de Dev.
También tiene un campo isBilled
opcional que marca si este evaluador puede generar facturación (p.ej., usa un LLM o una API facturados). Si se factura a un evaluador, la IU le solicita al usuario una confirmación en la CLI antes de permitirle ejecutar una evaluación. Este paso ayuda a proteger contra gastos imprevistos.
Evaluadores heurísticos
Un evaluador heurístico puede ser cualquier función que se use para evaluar input
, context
o output
de tu función de IA generativa.
Los evaluadores heurísticos en Genkit se componen de 2 componentes:
- Una función de puntuación
- Una acción de evaluador
Define la función de puntuación
Al igual que con el evaluador basado en LLM, define la función de puntuación. En este caso, la función de puntuación no necesita un LLM de juez.
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 },
};
}
Define la acción del evaluador
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,
};
}
);
}
Une todo
Definición del complemento
Los complementos se registran en el framework instalándolos en el momento de inicializar Genkit. Para definir un complemento nuevo, usa el método auxiliar genkitPlugin
para crear instancias de todas las acciones de Genkit dentro del contexto del complemento.
En esta muestra de código, se muestran dos evaluadores: el evaluador de delicias basado en LLM y el evaluador de números de teléfono de EE.UU. basado en regex. Crear instancias de estos evaluadores dentro del contexto del complemento los registra con el complemento.
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;
Configura Genkit
Agrega el complemento myCustomEvals
a tu configuración de Genkit.
Para la evaluación con Gemini, inhabilita la configuración de seguridad para que el evaluador pueda aceptar, detectar y calificar el contenido potencialmente dañino.
import { gemini15Pro } from '@genkit-ai/googleai';
const ai = genkit({
plugins: [
vertexAI(),
...
myCustomEvals({
judge: gemini15Pro,
}),
],
...
});
Usa tus evaluadores personalizados
Una vez que crees instancias de tus evaluadores personalizados dentro del contexto de la app de Genkit (ya sea a través de un complemento o directamente), estarán listos para usarse. En el siguiente ejemplo, se muestra cómo probar el evaluador de delicias con algunas entradas y salidas de muestra.
- 1. Crea un archivo JSON llamado “deliciousness_dataset.json” con el siguiente contenido:
[
{
"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. Usa la CLI de Genkit para ejecutar el evaluador en estos casos de prueba.
# Start your genkit runtime genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
- 3. Navega a "localhost:4000/evaluate" para ver los resultados en la IU de Genkit.
Es importante tener en cuenta que la confianza en los evaluadores personalizados aumenta a medida que los comparas con conjuntos de datos o enfoques estándar. Itera en los resultados de esas comparativas para mejorar el rendimiento de tus evaluadores hasta que alcancen el nivel de calidad objetivo.