Вы можете расширить Firebase Genkit для поддержки пользовательской оценки, используя либо LLM в качестве судьи, либо программную (эвристическую) оценку.
Определение оценщика
Оценщики — это функции, которые оценивают реакцию LLM. Существует два основных подхода к автоматизированной оценке: эвристическая оценка и оценка на основе LLM. При эвристическом подходе вы определяете детерминированную функцию. Напротив, при оценке на основе LLM контент передается обратно в LLM, и LLM предлагается оценить выходные данные в соответствии с критериями, установленными в подсказке.
Метод ai.defineEvaluator
, который вы используете для определения действия оценщика в Genkit, поддерживает любой подход. В этом документе рассматривается несколько примеров использования этого метода для эвристических оценок и оценок на основе LLM.
Оценщики на базе LLM
Оценщик на основе LLM использует LLM для оценки input
, context
и output
вашей функции генеративного ИИ.
Оценщики на основе LLM в Genkit состоят из трех компонентов:
- Подсказка
- Функция подсчета очков
- Действия оценщика
Определите подсказку
В этом примере оценщик использует LLM, чтобы определить, является ли еда ( output
) вкусной или нет. Сначала предоставьте LLM контекст, затем опишите, что вы от него хотите, и, наконец, дайте ему несколько примеров, на которых можно будет основывать его ответ.
Утилита definePrompt
Genkit предоставляет простой способ определения подсказок с проверкой ввода и вывода. Следующий код представляет собой пример настройки запроса оценки с помощью 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:
`
);
}
Определить функцию оценки
Определите функцию, которая берет пример, включающий output
, как того требует приглашение, и оценивает результат. Тестовые примеры Genkit включают input
в качестве обязательного поля, а output
и context
— в качестве необязательных полей. Оценщик несет ответственность за проверку наличия всех полей, необходимых для оценки.
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 },
};
}
Определить действие оценщика
Последний шаг — написать функцию, определяющую 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,
};
}
);
}
Метод defineEvaluator
похож на другие конструкторы Genkit, такие как defineFlow
и defineRetriever
. Этот метод требует, чтобы EvaluatorFn
был предоставлен в качестве обратного вызова. Метод EvaluatorFn
принимает объект BaseEvalDataPoint
, который соответствует одной записи в оцениваемом наборе данных, а также необязательный параметр настраиваемых параметров, если он указан. Функция обрабатывает точку данных и возвращает объект EvalResponse
.
Схемы Zod для BaseEvalDataPoint
и EvalResponse
следующие.
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(),
});
Объект defineEvaluator
позволяет пользователю указать имя, удобочитаемое отображаемое имя и определение для оценщика. Отображаемое имя и определение отображаются вместе с результатами оценки в пользовательском интерфейсе разработчика. Он также имеет необязательное поле isBilled
, которое отмечает, может ли этот оценщик привести к выставлению счетов (например, использует ли он выставленный счет LLM или API). Если оценщику выставляется счет, пользовательский интерфейс запрашивает у пользователя подтверждение в CLI, прежде чем разрешить ему выполнить оценку. Этот шаг помогает защититься от непредвиденных расходов.
Эвристические оценщики
Эвристическим оценщиком может быть любая функция, используемая для оценки input
, context
или output
вашей генеративной функции ИИ.
Эвристические оценщики в Genkit состоят из двух компонентов:
- Функция подсчета очков
- Действия оценщика
Определить функцию оценки
Как и в случае с оценщиком на основе LLM, определите функцию оценки. В этом случае для выставления оценок не требуется судья LLM.
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 },
};
}
Определить действие оценщика
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,
};
}
);
}
Собираем это вместе
Определение плагина
Плагины регистрируются в платформе путем их установки во время инициализации Genkit. Чтобы определить новый плагин, используйте вспомогательный метод genkitPlugin
для создания экземпляров всех действий Genkit в контексте плагина.
В этом примере кода показаны два оценщика: оценщик вкусности на основе LLM и оценщик телефонных номеров США на основе регулярных выражений. Создание экземпляров этих оценщиков в контексте плагина регистрирует их в плагине.
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;
Настроить Генкит
Добавьте плагин myCustomEvals
в вашу конфигурацию Genkit.
Для оценки с помощью Gemini отключите настройки безопасности, чтобы оценщик мог принимать, обнаруживать и оценивать потенциально вредный контент.
import { gemini15Pro } from '@genkit-ai/googleai';
const ai = genkit({
plugins: [
vertexAI(),
...
myCustomEvals({
judge: gemini15Pro,
}),
],
...
});
Использование собственных оценщиков
Как только вы создадите экземпляр своих пользовательских оценщиков в контексте приложения Genkit (с помощью плагина или напрямую), они будут готовы к использованию. В следующем примере показано, как опробовать оценщик вкусности с помощью нескольких примеров входных и выходных данных.
- 1. Создайте 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."
}
]
- 2. Используйте Genkit CLI, чтобы запустить оценщик для этих тестовых случаев.
# Start your genkit runtime genkit start -- <command to start your app>
genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator
- 3. Перейдите к `localhost:4000/evaluate`, чтобы просмотреть результаты в пользовательском интерфейсе Genkit.
Важно отметить, что доверие к специализированным оценщикам возрастает по мере того, как вы сравниваете их со стандартными наборами данных или подходами. Повторяйте результаты таких тестов, чтобы улучшить работу ваших оценщиков, пока она не достигнет целевого уровня качества.