При разработке продуктов на базе LLM (особенно автономных кодинг-агентов) классическое детерминированное тестирование (TDD) оказывается недостаточным. Большие языковые модели функционируют как стохастические генераторы: один и тот же системный промпт может возвращать синтаксически корректный компонент при одних выборках и производить деградацию логики или интерфейса при изменении температуры, контекстного окна или внутренних весов на стороне провайдера API.
Eval-Driven Development (EDD) — это строгая инженерная методология, в которой качество, надежность и архитектурные инварианты кодогенерации непрерывно измеряются с помощью автоматизированных наборов метрик (Evals) на репрезентативных золотых датасетах (Golden Datasets). Пайплайн непрерывной интеграции (CI/CD) кодогенерации оценивает устойчивость к регрессиям промптов (Prompt Regression / Drift) и гарантирует выполнение SLA до деплоя в продакшен.
В EDD система тестируется не на точное посимвольное совпадение строк, а на синтаксическую валидность абстрактного синтаксического дерева (AST), покрытие типами и функциональную эквивалентность сгенерированного артефакта в изолированной среде исполнения.
Автоматизированные пайплайны и метрики для количественной и качественной оценки генераций LLM на основе детерминированных тестов, AST-анализаторов и статистических судей. Статистическая метрика в бенчмарках кодогенерации, отражающая вероятность того, что как минимум 1 изk сгенерированных вариантов программы успешно компилируется и проходит все юнит-тесты.
Непреднамеренное падение точности или деградация контрактов кодогенерации при модификации промптов, RAG-контекста или обновлении базовой LLM-модели.
[Golden Dataset: Prompts & Contracts]
│
▼
(LLM Generation Runs) ── x N samples
│
▼
[Eval Engine: TypeScript AST, Vitest Sandbox]
│
▼
[Metrics Dashboard: Pass@1, Syntax Validity, Latency, Token Cost]Формирование золотого датасета (50–200 задач) с входными контрактами и тестовыми наборами.
Фиксация эталонных значений метрик (Pass@1, Pass@5, P95 Latency, Token Overhead).
Итеративная оптимизация промптов, контекстного RAG-пайплайна или замена базовой LLM.
Запуск CI-пайплайна Evals с блокировкой слияния PR при падении целевых метрик ниже SLA.
Почему для оценки автономных кодинг-агентов в CI/CD детерминированные проверки строкового равенства (snapshot testing) неприменимы, в отличие от Pass@k и AST-верификации?
Пример архитектуры Eval-раннера для TypeScript-генераций с проверкой AST-дерева и функциональной корректности:
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import ts from 'typescript';
interface GoldenTestCase {
prompt: string;
testCases: Array<{ input: unknown[]; expected: unknown }>;
}
const goldenDataset: GoldenTestCase[] = [
{
prompt: "Напиши чистую функцию sum(a: number, b: number): number, возвращающую сумму.",
testCases: [{ input: [1, 2], expected: 3 }, { input: [-1, 1], expected: 0 }]
}
];
function isSyntaxAndTypeValid(code: string): boolean {
const sourceFile = ts.createSourceFile('generated.ts', code, ts.ScriptTarget.ES2024, true);
const diagnostics = (sourceFile as unknown as { parseDiagnostics?: ts.Diagnostic[] }).parseDiagnostics || [];
return diagnostics.length === 0;
}
function verifyExecutionInSandbox(code: string, testCases: Array<{ input: unknown[]; expected: unknown }>): boolean {
try {
// В продакшене: выполнение строго в изолированном V8 isolate или Docker microVM
const factory = new Function(`return (${code})`);
const fn = factory();
return testCases.every(tc => fn(...tc.input) === tc.expected);
} catch {
return false;
}
}
async function runCodeEvalSuite(): Promise<{ passAt1: number; syntaxRate: number }> {
let passedCount = 0;
let validSyntaxCount = 0;
for (const item of goldenDataset) {
const { text } = await generateText({
model: openai('gpt-4o-mini'),
prompt: `Сгенерируй только чистую JavaScript/TypeScript функцию. Без Markdown-обертки. ${item.prompt}`
});
const cleanCode = text.replace(/```typescript|```javascript|```/g, '').trim();
const syntaxOk = isSyntaxAndTypeValid(cleanCode);
if (syntaxOk) validSyntaxCount++;
const runtimeOk = syntaxOk && verifyExecutionInSandbox(cleanCode, item.testCases);
if (runtimeOk) passedCount++;
}
const passAt1 = (passedCount / goldenDataset.length) * 100;
const syntaxRate = (validSyntaxCount / goldenDataset.length) * 100;
return { passAt1, syntaxRate };
}С какой главной угрозой безопасности сопряжено наивное вычисление функционального прохождения тестов сгенерированного LLM кода через `new Function` или `eval()`?
Оптимизация промпта исключительно под 30 фиксированных задач тестового датасета. Агент безупречно проходит тестовый набор, но ломается на реальных запросах пользователей с граничными условиями. Необходимо разделять выборки на train, validation и строго скрытый holdout-набор.
Оценка алгоритмической корректности промптом «Оцени код от 1 до 10» приводит к высокой дисперсии и слепоте к граничным багам. Для проверки кодогенерации фундаментом должны служить детерминированные тесты (Vitest/Jest, компилятор tsc, линтеры AST).
Новый многоступенчатый промпт с CoT (Chain-of-Thought) повышает Pass@1 на 3%, но увеличивает задержку TTFT в 4 раза и стоимость выполнения в 5 раз. Evals обязаны профилировать P95 Latency и Token Consumption.