Możesz rozszerzyć Firebase Genkit, aby obsługiwał niestandardową ocenę, korzystając z modelu LLM jako sędziego lub z oceny programowej (heurystycznej).
Definicja oceniającego
Oceniacze to funkcje, które oceniają odpowiedź LLM. Istnieją 2 główne podejścia do automatycznej oceny: ocena heurystyczna i ocena na podstawie modeli LLM. W przypadku podejścia heurystycznego definiujesz funkcję deterministyczną. Natomiast w przypadku oceny opartej na modelu LLM treści są przekazywane do tego modelu, który ocenia dane wyjściowe zgodnie z kryteriami określonymi w promptzie.
Metoda ai.defineEvaluator
, której używasz do definiowania działania evaluatora w Genkit, obsługuje oba podejścia. W tym dokumencie znajdziesz kilka przykładów stosowania tej metody do oceny za pomocą heurystyki i modeli LLM.
Oceniający na podstawie LLM
Evaluator oparty na LLM wykorzystuje model LLM do oceny input
, context
i output
funkcji generatywnej AI.
Oceny oparte na LLM w Genkit składają się z 3 komponentów:
- prompt
- Funkcja punktacji
- Działanie instancji oceny
Definiowanie promptu
W tym przykładzie weryfikator korzysta z LLM, aby określić, czy jedzenie (output
) jest smaczne. Najpierw podaj kontekst LLM, a potem opisz, czego oczekujesz od modelu. Na koniec podaj kilka przykładów, na których może się on opierać.
Narzędzie definePrompt
w Genkit ułatwia definiowanie promptów z weryfikacją danych wejściowych i wyjściowych. Poniższy kod to przykład konfigurowania promptu oceny za pomocą funkcji definePrompt
.
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:
`
);
}
Definiowanie funkcji punktacji
Zdefiniuj funkcję, która przyjmuje przykład z wartością output
wymaganą przez prompt, i wyznacza wynik. Testy Genkit zawierają pole input
jako pole wymagane, a pola output
i context
jako opcjonalne. Oceniający musi sprawdzić, czy wszystkie pola wymagane do oceny są obecne.
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 },
};
}
Definiowanie działania oceniającego
Ostatnim krokiem jest napisanie funkcji, która definiuje EvaluatorAction
.
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,
};
}
);
}
Metoda defineEvaluator
jest podobna do innych konstruktorów Genkit, takich jak defineFlow
i defineRetriever
. Ta metoda wymaga przekazania parametru EvaluatorFn
jako wywołania zwrotnego. Metoda EvaluatorFn
przyjmuje obiekt BaseEvalDataPoint
, który odpowiada pojedynczemu wpisowi w danym zbiorze danych, a także opcjonalny parametr custom-options (jeśli został podany). Funkcja przetwarza punkt danych i zwraca obiekt EvalResponse
.
Schematy Zod dla BaseEvalDataPoint
i EvalResponse
są następujące:
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(),
});
Obiekt defineEvaluator
umożliwia użytkownikowi podanie nazwy, czytelnej dla użytkownika nazwy wyświetlanej i definicji dla oceniającego. Wyświetlana nazwa i definicja są wyświetlane wraz z wynikami oceny w interfejsie programisty.
Zawiera on też opcjonalne pole isBilled
, które wskazuje, czy ta funkcja oceny może spowodować obciążenie (np. korzysta z opłacanego modelu LLM lub interfejsu API). Jeśli zostanie naliczona opłata, interfejs poprosi użytkownika o potwierdzenie w CLI, zanim pozwoli mu przeprowadzić ocenę. Dzięki temu unikniesz niechcianych wydatków.
Heurystyka
Heurystycznym weryfikatorem może być dowolna funkcja służąca do oceny input
, context
lub output
funkcji opartej na generatywnej AI.
Heurystyczne algorytmy oceniające w Genkit składają się z 2 komponentów:
- Funkcja punktacji
- Działanie instancji oceny
Definiowanie funkcji punktacji
Podobnie jak w przypadku oceniacza opartego na LLM, zdefiniuj funkcję punktacji. W tym przypadku funkcja punktacji nie wymaga LLM sędziego.
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 },
};
}
Definiowanie działania weryfikującego
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,
};
}
);
}
Podsumowanie
Definicja wtyczki
Wtyczki są rejestrowane w ramach frameworku przez zainstalowanie ich w momencie inicjowania Genkit. Aby zdefiniować nową wtyczkę, użyj metody pomocniczej genkitPlugin
, aby utworzyć instancje wszystkich działań Genkit w kontekście wtyczki.
Ten przykład kodu zawiera 2 sprawdzacze: oparty na LLM sprawdzający pyszność oraz oparty na wyrażeniu regularnym sprawdzający numer telefonu w Stanach Zjednoczonych. Tworzenie instancji tych evaluatorów w kontekście wtyczki rejestruje je w ramach wtyczki.
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;
Konfigurowanie Genkit
Dodaj wtyczkę myCustomEvals
do konfiguracji Genkit.
W przypadku oceny za pomocą Gemini wyłącz ustawienia bezpieczeństwa, aby weryfikator mógł zaakceptować, wykryć i ocenić potencjalnie szkodliwe treści.
import { gemini15Pro } from '@genkit-ai/googleai';
const ai = genkit({
plugins: [
vertexAI(),
...
myCustomEvals({
judge: gemini15Pro,
}),
],
...
});
Korzystanie z własnych funkcji oceny
Po uruchomieniu instancji własnych oceniaczy w kontekście aplikacji Genkit (za pomocą wtyczki lub bezpośrednio) są one gotowe do użycia. Ten przykład pokazuje, jak wypróbować oceniarkę pyszności na podstawie kilku przykładowych danych wejściowych i wyjściowych.
- 1. Utwórz plik JSON o nazwie „deliciousness_dataset.json” z treścią:
[
{
"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. Użyj interfejsu wiersza poleceń Genkit, aby uruchomić oceniarkę na tych przypadkach testowych.
# Start your genkit runtime genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
- 3. Otwórz adres „localhost:4000/evaluate”, aby wyświetlić wyniki w interfejsie Genkit.
Pamiętaj, że wiarygodność niestandardowych oceniających rośnie wraz z ich porównywaniem ze standardowymi zbiorami danych lub metodami. Na podstawie wyników takich testów porównawczych możesz poprawiać wyniki swoich oceniających, aż osiągną docelowy poziom jakości.