Firebase Genkit se puede extender para admitir la evaluación personalizada de los resultados de los casos de prueba, ya sea con un LLM como juez o de forma puramente programática.
Definición de evaluador
Los evaluadores son funciones que evalúan el contenido que se le proporciona a un LLM y que este genera. Existen dos enfoques principales para la evaluación automatizada (pruebas): la evaluación heurística y la evaluación basada en LLM. En el enfoque heurístico, defines una función determinista como las del desarrollo de software tradicional. En una evaluación basada en LLM, el contenido se vuelve a enviar a un LLM y se le solicita que califique el resultado según los criterios establecidos en una instrucción.
Evaluadores basados en LLM
Un evaluador basado en LLM aprovecha un LLM para evaluar la entrada, el contexto o el resultado 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, la instrucción le pedirá al LLM que juzgue qué tan delicioso es el resultado. 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 sencilla de definir instrucciones con validación de entrada y salida. A continuación, te indicamos cómo configurar una instrucción de evaluación con definePrompt
.
const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const;
const DeliciousnessDetectionResponseSchema = z.object({
reason: z.string(),
verdict: z.enum(DELICIOUSNESS_VALUES),
});
type DeliciousnessDetectionResponse = z.infer<typeof DeliciousnessDetectionResponseSchema>;
const DELICIOUSNESS_PROMPT = ai.definePrompt(
{
name: 'deliciousnessPrompt',
inputSchema: z.object({
output: z.string(),
}),
outputSchema: DeliciousnessDetectionResponseSchema,
},
`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:
{{output}}
Response:
`
);
Define la función de puntuación
Ahora, define la función que tomará un ejemplo que incluya output
como lo requiere la instrucción y califica el resultado. Los casos de prueba de Genkit incluyen input
como un campo obligatorio, con campos opcionales para output
y context
. Es responsabilidad del evaluador validar que estén presentes todos los campos necesarios para la evaluación.
import { BaseEvalDataPoint, Score } from 'genkit/evaluator';
/**
* Score an individual test case for delciousness.
*/
export async function deliciousnessScore<
CustomModelOptions extends z.ZodTypeAny,
>(
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
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 },
};
}
Define la acción del evaluador
El último paso es escribir una función que defina la acción del evaluador.
import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';
/**
* 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: BaseEvalDataPoint) => {
const score = await deliciousnessScore(judge, datapoint, judgeConfig);
return {
testCaseId: datapoint.testCaseId,
evaluation: score,
};
}
);
}
Evaluadores heurísticos
Un evaluador heurístico puede ser cualquier función que se use para evaluar la entrada, el contexto o el resultado 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 el evaluador basado en LLM, define la función de puntuación. En este caso, la función de puntuación no necesita conocer el LLM del juez ni su configuración.
import { BaseEvalDataPoint, Score } from 'genkit/evaluator';
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: 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 regex ${regex.source}`
: `Output did not match regex ${regex.source}`;
return {
score: matches,
details: { reasoning },
};
}
Define la acción del evaluador
import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator';
/**
* 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: BaseEvalDataPoint) => {
const score = await regexMatchScore(datapoint, regexMetric.regex);
return fillScores(datapoint, score);
}
);
});
}
Configuración
Opciones del complemento
Define el PluginOptions
que usará el complemento del evaluador personalizado. Este objeto no tiene requisitos estrictos y depende de los tipos de evaluadores que se definan.
Como mínimo, deberá tomar la definición de qué métricas registrar.
export enum MyAwesomeMetric {
WORD_COUNT = 'WORD_COUNT',
US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH',
}
export interface PluginOptions {
metrics?: Array<MyAwesomeMetric>;
}
Si este nuevo complemento usa un LLM como juez y admite el intercambio de LLM, define parámetros adicionales en el objeto 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>;
}
Definición del complemento
Los complementos se registran en el framework a través del archivo genkit.config.ts
de un proyecto. Para poder configurar un nuevo complemento, define una función que defina un GenkitPlugin
y configúralo con el PluginOptions
definido anteriormente.
En este caso, tenemos dos evaluadores: DELICIOUSNESS
y US_PHONE_REGEX_MATCH
. Aquí es donde esos evaluadores se registran con el complemento y con Firebase Genkit.
export function myAwesomeEval<ModelCustomOptions extends z.ZodTypeAny>(
options: PluginOptions<ModelCustomOptions>
): PluginProvider {
// Define the new plugin
const plugin = (options?: MyPluginOptions<ModelCustomOptions>) => {
return genkitPlugin(
'myAwesomeEval',
async (ai: Genkit) => {
const { judge, judgeConfig, metrics } = options;
const evaluators: EvaluatorAction[] = metrics.map((metric) => {
switch (metric) {
case DELICIOUSNESS:
// This evaluator requires an LLM as judge
return createDeliciousnessEvaluator(ai, judge, judgeConfig);
case US_PHONE_REGEX_MATCH:
// This evaluator does not require an LLM
return createUSPhoneRegexEvaluator();
}
});
return { evaluators };
})
}
// Create the plugin with the passed options
return plugin(options);
}
export default myAwesomeEval;
Configura Genkit
Agrega el complemento recién definido 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 { gemini15Flash } from '@genkit-ai/googleai';
const ai = genkit({
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
],
}),
],
...
});
Prueba
Los mismos problemas que se aplican a la evaluación de la calidad del resultado de un atributo de IA generativa se aplican a la evaluación de la capacidad de evaluación de un evaluador basado en LLM.
Para tener una idea de si el evaluador personalizado funciona al nivel esperado, crea un conjunto de casos de prueba que tengan una respuesta correcta y otra incorrecta.
Como ejemplo de delicia, podría verse como un archivo 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."
}
]
Estos ejemplos pueden ser generados por humanos o puedes pedirle a un LLM que te ayude a crear un conjunto de casos de prueba que se puedan seleccionar. También hay muchos conjuntos de datos de comparativas disponibles que se pueden usar.
Luego, usa la CLI de Genkit para ejecutar el evaluador en estos casos de prueba.
genkit eval:run deliciousness_dataset.json
Consulta tus resultados en la IU de Genkit.
genkit start
Navega hacia localhost:4000/evaluate
.