O Firebase Genkit pode ser estendido para oferecer suporte à avaliação personalizada da saída do caso de teste usando um LLM como juiz ou de forma puramente programática.
Definição do avaliador
Os avaliadores são funções que avaliam o conteúdo fornecido e gerado por um LLM. Há duas abordagens principais para a avaliação automatizada (teste): avaliação heurística e avaliação baseada em LLM. Na abordagem heurística, você define uma função determinística, como as do desenvolvimento de software tradicional. Em uma avaliação baseada em LLM, o conteúdo é enviado de volta a um LLM, que é solicitado a pontuar a saída de acordo com os critérios definidos em um comando.
Avaliadores baseados em LLM
Um avaliador baseado em LLM usa um LLM para avaliar a entrada, o contexto ou a saída do recurso de IA generativa.
Os avaliadores baseados em LLM no Genkit são compostos por três componentes:
- Um comando
- Uma função de pontuação
- Uma ação do avaliador
Definir a solicitação
Neste exemplo, o comando vai pedir ao LLM para julgar o quão deliciosa é a saída. Primeiro, forneça contexto ao LLM, depois descreva o que você quer que ele faça e, por fim, dê alguns exemplos para embasar a resposta.
O utilitário definePrompt
do Genkit oferece uma maneira fácil de definir comandos com validação de entrada e saída. Saiba como configurar uma solicitação de avaliação com 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:
`
);
Definir a função de pontuação
Agora, defina a função que vai receber um exemplo que inclui output
, conforme exigido pelo comando, e avalie o resultado. Os casos de teste do Genkit incluem input
como um campo obrigatório, com campos opcionais para output
e context
. É responsabilidade do avaliador validar se todos os campos necessários para a avaliação estão presentes.
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 },
};
}
Definir a ação do avaliador
A etapa final é escrever uma função que defina a própria ação do avaliador.
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,
};
}
);
}
Avaliadores heurísticos
Um avaliador heurístico pode ser qualquer função usada para avaliar a entrada, o contexto ou a saída do recurso de IA generativa.
Os avaliadores heurísticos no Genkit são compostos por dois componentes:
- Uma função de pontuação
- Uma ação do avaliador
Definir a função de pontuação
Assim como o avaliador baseado em LLM, defina a função de pontuação. Nesse caso, a função de pontuação não precisa saber sobre o LLM do juiz ou a configuração dele.
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 },
};
}
Definir a ação do avaliador
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);
}
);
});
}
Configuração
Opções do plug-in
Defina o PluginOptions
que o plug-in do avaliador personalizado vai usar. Esse objeto não tem requisitos rígidos e depende dos tipos de avaliadores definidos.
No mínimo, ele precisa ter a definição de quais métricas registrar.
export enum MyAwesomeMetric {
WORD_COUNT = 'WORD_COUNT',
US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH',
}
export interface PluginOptions {
metrics?: Array<MyAwesomeMetric>;
}
Se esse novo plug-in usar um LLM como juiz e tiver suporte para trocar o LLM a ser usado, defina outros parâmetros no 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>;
}
Definição do plug-in
Os plug-ins são registrados no framework pelo arquivo genkit.config.ts
em um projeto. Para configurar um novo plug-in, defina uma função que defina um GenkitPlugin
e o configure com o PluginOptions
definido acima.
Nesse caso, temos dois avaliadores, DELICIOUSNESS
e US_PHONE_REGEX_MATCH
. É aqui que esses avaliadores são registrados no plug-in e no 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;
Configurar o Genkit
Adicione o plug-in recém-definido à configuração do Genkit.
Para a avaliação com o Gemini, desative as configurações de segurança para que o avaliador possa aceitar, detectar e classificar o conteúdo potencialmente nocivo.
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
],
}),
],
...
});
Teste
Os mesmos problemas que se aplicam à avaliação da qualidade da saída de um recurso de IA generativa também se aplicam à capacidade de julgamento de um avaliador baseado em LLM.
Para ter uma ideia se o avaliador personalizado tem o desempenho esperado, crie um conjunto de casos de teste com uma resposta certa e errada clara.
Como exemplo de delícia, um arquivo JSON deliciousness_dataset.json
pode ter esta aparência:
[
{
"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."
}
]
Esses exemplos podem ser gerados por humanos ou você pode pedir a um LLM para ajudar a criar um conjunto de casos de teste que podem ser selecionados. Há muitos conjuntos de dados de comparação disponíveis que também podem ser usados.
Em seguida, use a CLI do Genkit para executar o avaliador nesses casos de teste.
genkit eval:run deliciousness_dataset.json
Confira seus resultados na interface do Genkit.
genkit start
Acesse localhost:4000/evaluate
.