Sie können Firebase Genkit um eine benutzerdefinierte Bewertung erweitern, indem Sie entweder ein LLM als Juror verwenden oder eine programmatische (heuristische) Bewertung vornehmen.
Definition des Prüfers
Bewerter sind Funktionen, die die Antwort eines LLM bewerten. Es gibt zwei Hauptansätze für die automatisierte Bewertung: heuristische Bewertung und LLM-basierte Bewertung. Beim heuristischen Ansatz definieren Sie eine deterministische Funktion. Bei einer LLM-basierten Bewertung werden die Inhalte dagegen an ein LLM zurückgegeben und das LLM wird aufgefordert, die Ausgabe anhand der in einem Prompt festgelegten Kriterien zu bewerten.
Die ai.defineEvaluator
-Methode, mit der Sie eine Evaluator-Aktion in Genkit definieren, unterstützt beide Ansätze. In diesem Dokument werden einige Beispiele dafür vorgestellt, wie diese Methode für heuristische und LLM-basierte Bewertungen verwendet werden kann.
LLM-basierte Prüfer
Bei einem LLM-basierten Evaluator wird ein LLM verwendet, um die input
, context
und output
Ihrer generativen KI-Funktion zu bewerten.
LLM-basierte Bewerter in Genkit bestehen aus drei Komponenten:
- Einen Prompt
- Eine Bewertungsfunktion
- Eine Aktion des Bewerters
Prompt definieren
In diesem Beispiel verwendet der Prüfer ein LLM, um festzustellen, ob ein Lebensmittel (die output
) lecker ist oder nicht. Geben Sie dem LLM zuerst einen Kontext, beschreiben Sie dann, was es tun soll, und geben Sie ihm abschließend einige Beispiele, auf die es seine Antwort stützen kann.
Das definePrompt
-Dienstprogramm von Genkit bietet eine einfache Möglichkeit, Prompts mit Eingabe- und Ausgabevalidierung zu definieren. Im folgenden Codebeispiel wird gezeigt, wie Sie einen Bewertungsvorschlag mit definePrompt
einrichten.
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,
}
},
`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:
`
);
}
Bewertungsfunktion definieren
Definieren Sie eine Funktion, die ein Beispiel mit output
als vom Prompt geforderten Wert annimmt und das Ergebnis bewertet. Genkit-Testfälle enthalten input
als Pflichtfeld und output
und context
als optionale Felder. Es liegt in der Verantwortung des Prüfers, zu prüfen, ob alle für die Bewertung erforderlichen Felder vorhanden sind.
import { ModelArgument, z } from 'genkit';
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 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 },
};
}
Aktion für die Bewertung definieren
Im letzten Schritt schreiben Sie eine Funktion, die die EvaluatorAction
definiert.
import { Genkit, z } from 'genkit';
import { BaseEvalDataPoint, 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(judge, datapoint, judgeConfig);
return {
testCaseId: datapoint.testCaseId,
evaluation: score,
};
}
);
}
Die Methode defineEvaluator
ähnelt anderen Genkit-Konstruktoren wie defineFlow
und defineRetriever
. Für diese Methode muss eine EvaluatorFn
als Callback angegeben werden. Die Methode EvaluatorFn
akzeptiert ein BaseEvalDataPoint
-Objekt, das einem einzelnen Eintrag in einem zu bewertenden Datensatz entspricht, sowie einen optionalen Parameter für benutzerdefinierte Optionen, falls angegeben. Die Funktion verarbeitet den Datenpunkt und gibt ein EvalResponse
-Objekt zurück.
Die Zod-Schemas für BaseEvalDataPoint
und EvalResponse
sehen so aus:
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(),
});
Mit dem defineEvaluator
-Objekt kann der Nutzer einen Namen, einen für Nutzer lesbaren Anzeigenamen und eine Definition für den Bewerter angeben. Der Anzeigename und die Definition werden zusammen mit den Bewertungsergebnissen in der Entwickler-Benutzeroberfläche angezeigt.
Es gibt auch ein optionales Feld isBilled
, das angibt, ob diese Bewertung zu einer Abrechnung führen kann (z.B. wenn ein in Rechnung gestelltes LLM oder eine in Rechnung gestellte API verwendet wird). Wenn eine Rechnung für einen Prüfer erstellt wird, wird der Nutzer in der Benutzeroberfläche aufgefordert, in der Befehlszeile eine Bestätigung abzugeben, bevor er eine Bewertung durchführen kann. So können Sie unerwünschte Ausgaben vermeiden.
Heuristische Bewerter
Als heuristischer Bewerter kann jede Funktion verwendet werden, die die input
, context
oder output
Ihrer generativen KI-Funktion bewertet.
Heuristische Prüfer in Genkit bestehen aus zwei Komponenten:
- Eine Bewertungsfunktion
- Eine Aktion des Bewerters
Bewertungsfunktion definieren
Definieren Sie wie beim LLM-basierten Bewerter die Bewertungsfunktion. In diesem Fall benötigt die Bewertungsfunktion kein LLM für die Jury.
import { EvalResponses } from 'genkit';
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 },
};
}
Aktion für die Bewertung definieren
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,
};
}
);
}
Ergebnis
Plug-in-Definition
Plugins werden beim Initialisieren von Genkit installiert und damit beim Framework registriert. Wenn du ein neues Plug-in definieren möchtest, verwende die Hilfsmethode genkitPlugin
, um alle Genkit-Aktionen im Plug-in-Kontext zu instanziieren.
Dieses Codebeispiel zeigt zwei Prüfer: den LLM-basierten Prüfer für den Geschmack und den regulären Ausdruck-basierten Prüfer für US-amerikanische Telefonnummern. Wenn Sie diese Bewerter im Plug-in-Kontext instanziieren, werden sie beim Plug-in registriert.
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;
Genkit konfigurieren
Fügen Sie das myCustomEvals
-Plug-in Ihrer Genkit-Konfiguration hinzu.
Deaktivieren Sie für die Bewertung mit Gemini die Sicherheitseinstellungen, damit die Prüfer potenziell schädliche Inhalte akzeptieren, erkennen und bewerten können.
import { gemini15Pro } from '@genkit-ai/googleai';
const ai = genkit({
plugins: [
vertexAI(),
...
myCustomEvals({
judge: gemini15Pro,
}),
],
...
});
Benutzerdefinierte Bewerter verwenden
Sobald Sie Ihre benutzerdefinierten Bewerter im Genkit-App-Kontext instanziiert haben (entweder über ein Plug-in oder direkt), können sie verwendet werden. Im folgenden Beispiel wird gezeigt, wie Sie den Bewertungsalgorithmus für die Delikatesse mit einigen Beispielen für Eingaben und Ausgaben testen.
- 1. Erstellen Sie eine JSON-Datei namens „deliciousness_dataset.json“ mit folgendem Inhalt:
[
{
"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. Führen Sie mit der Genkit-Befehlszeile die Bewertung für diese Testfälle aus.
# Start your genkit runtime genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
- 3. Rufen Sie localhost:4000/evaluate auf, um die Ergebnisse in der Genkit-Benutzeroberfläche aufzurufen.
Die Zuverlässigkeit benutzerdefinierter Bewertungsmethoden steigt, wenn Sie sie mit Standarddatensätzen oder -ansätzen vergleichen. Nutzen Sie die Ergebnisse solcher Benchmarks, um die Leistung Ihrer Prüfer zu verbessern, bis die gewünschte Qualität erreicht ist.