Firebase Genkit può essere esteso per supportare la valutazione personalizzata dell'output del caso di test, utilizzando un LLM come giudice o in modo puramente programmatico.
Definizione del valutatore
I valutatori sono funzioni che valutano i contenuti forniti e generati da un LLM. Esistono due approcci principali alla valutazione automatica (test): valutazione basata su regole ed euristica e valutazione basata su LLM. Nell'approccio euristico, definisci una funzione deterministica come quelle dello sviluppo software tradizionale. In una valutazione basata su LLM, i contenuti vengono reintrodotti in un LLM a cui viene chiesto di assegnare un punteggio all'output in base ai criteri impostati in un prompt.
Valutatori basati su LLM
Un valutatore basato su LLM sfrutta un LLM per valutare l'input, il contesto o l'output della funzionalità di IA generativa.
I valutatori basati su LLM in Genkit sono costituiti da tre componenti:
- Un prompt
- Una funzione di punteggio
- Un'azione di valutazione
Definisci il prompt
Per questo esempio, il prompt chiederà all'LLM di giudicare quanto sia delizioso l'output. Innanzitutto, fornisci il contesto all'LLM, poi descrivi cosa vuoi che faccia e, infine, fornisci alcuni esempi su cui basare la sua risposta.
L'utilità definePrompt
di Genkit offre un modo semplice per definire i prompt con convalida di input e output. Ecco come configurare un prompt di valutazione 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:
`
);
Definisci la funzione di punteggio
Ora definisci la funzione che prenderà un esempio che include output
come richiesto dal prompt e assegnerà un punteggio al risultato. I casi di test di Genkit includono input
come campo obbligatorio, con campi facoltativi per output
e context
. È responsabilità del valutatore verificare che siano presenti tutti i campi richiesti per la valutazione.
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 },
};
}
Definisci l'azione dell'valutatore
Il passaggio finale consiste nello scrivere una funzione che definisce l'azione dello valutatore stesso.
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,
};
}
);
}
Valutatori euristici
Un valutatore di euristiche può essere qualsiasi funzione utilizzata per valutare l'input, il contesto o l'output della funzionalità di AI generativa.
I valutatori euristici in Genkit sono costituiti da due componenti:
- Una funzione di punteggio
- Un'azione di valutazione
Definisci la funzione di punteggio
Come per l'valutatore basato su LLM, definisci la funzione di punteggio. In questo caso, la funzione di punteggio non deve conoscere l'LLM del giudice o la relativa configurazione.
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 },
};
}
Definisci l'azione dell'valutatore
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);
}
);
});
}
Configurazione
Opzioni del plug-in
Definisci il PluginOptions
che verrà utilizzato dal plug-in di valutazione personalizzata. Questo oggetto non ha requisiti rigorosi e dipende dai tipi di valutatori definiti.
Come minimo, dovrà includere la definizione delle metriche da registrare.
export enum MyAwesomeMetric {
WORD_COUNT = 'WORD_COUNT',
US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH',
}
export interface PluginOptions {
metrics?: Array<MyAwesomeMetric>;
}
Se questo nuovo plug-in utilizza un LLM come giudice e supporta la sostituzione dell'LLM da utilizzare, definisci parametri aggiuntivi nell'oggetto 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>;
}
Definizione del plug-in
I plug-in vengono registrati nel framework tramite il file genkit.config.ts
in un progetto. Per poter configurare un nuovo plug-in, definisci una funzione che definisca un GenkitPlugin
e lo configuri con il PluginOptions
definito sopra.
In questo caso abbiamo due valutatori, DELICIOUSNESS
e US_PHONE_REGEX_MATCH
. Qui vengono registrati gli valutatori con il plug-in e 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
Aggiungi il plug-in appena definito alla configurazione di Genkit.
Per la valutazione con Gemini, disattiva le impostazioni di sicurezza in modo che il valutatore possa accettare, rilevare e valutare i contenuti potenzialmente dannosi.
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
],
}),
],
...
});
Test
Gli stessi problemi che si applicano alla valutazione della qualità dell'output di una funzionalità di IA generativa si applicano alla valutazione della capacità di giudizio di un valutatore basato su LLM.
Per capire se il valutatore personalizzato funziona al livello previsto, crea un insieme di casi di test con una risposta chiara corretta e sbagliata.
Un esempio di bontà potrebbe avere il seguente aspetto in un file 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."
}
]
Questi esempi possono essere generati da persone fisiche oppure puoi chiedere a un modello LLM di aiutarti a creare un insieme di casi di test che possono essere selezionati. Esistono anche molti set di dati di benchmark che possono essere utilizzati.
Quindi, utilizza l'interfaccia a riga di comando Genkit per eseguire lo strumento di valutazione su questi casi di test.
genkit eval:run deliciousness_dataset.json
Visualizza i risultati nell'interfaccia utente di Genkit.
genkit start
Vai a localhost:4000/evaluate
.