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 maneira programática.
Definição de avaliador
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 (testes): 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 é retornado a um LLM, que precisa pontuar o resultado 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 seu 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 o comando
Neste exemplo, o comando solicitará que o LLM avalie a qualidade da saída. Primeiro, forneça contexto ao LLM, descreva o que você quer que ele faça e, por fim, dê alguns exemplos para basear a resposta.
O Genkit vem com o dotprompt
, que oferece uma maneira fácil de definir e gerenciar comandos com recursos como validação de esquema de entrada/saída. Veja como usar dotprompt
para definir um comando de avaliação.
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:
`
);
Definir a função de pontuação
Agora, defina a função que vai usar um exemplo que inclui output
conforme exigido pelo comando e atribuir uma pontuação ao 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 a presença de todos os campos obrigatórios da avaliação.
/**
* 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 },
};
}
Definir a ação do avaliador
A etapa final é escrever uma função que defina a própria ação do avaliador.
/**
* 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,
};
}
);
}
Avaliadores heurísticos
Um avaliador heurístico pode ser qualquer função usada para avaliar a entrada, o contexto ou a saída do seu 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 dos jurados nem a configuração dele.
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 },
};
}
Definir a ação do avaliador
/**
* 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);
}
);
});
}
Configuração
Opções de plug-in
Defina o PluginOptions
que o plug-in do avaliador personalizado usará. Esse objeto não tem requisitos rígidos e depende dos tipos de avaliadores definidos.
No mínimo, será necessário 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 o novo plug-in usar um LLM como juiz e o plug-in oferecer suporte à troca de qual LLM usar, 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 com o 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.
Neste caso, temos dois avaliadores, DELICIOUSNESS
e US_PHONE_REGEX_MATCH
. Os avaliadores são registrados com o plug-in e o 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;
Configurar o Genkit
Adicione o plug-in recém-definido à configuração do Genkit.
Para fazer a avaliação com o Gemini, desative as configurações de segurança para que o avaliador possa aceitar, detectar e pontuar conteúdo potencialmente nocivo.
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
],
}),
],
...
});
testes
Os mesmos problemas que se aplicam à avaliação da qualidade da saída de um recurso de IA generativa se aplicam à avaliação da capacidade de julgamento de um avaliador baseado em LLM.
Para ter uma noção do desempenho do avaliador personalizado no nível esperado, crie um conjunto de casos de teste que tenham uma resposta certa e errada.
Por exemplo, pode parecer com um arquivo 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."
}
]
Esses exemplos podem ser gerados por humanos ou você pode pedir para um LLM ajudar a criar um conjunto de casos de teste que podem ser selecionados. Há muitos conjuntos de dados de comparativo de mercado 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
Veja os resultados na interface do Genkit.
genkit start
Acesse localhost:4000/evaluate
.