Оценивайте outcome и взаимодействие отдельно
Качество каждого worker не гарантирует качество системы. Evals должны проверять итог, маршрутизацию, использование tools, стоимость, конфликты и остановку. Для одного test case полезны несколько graders: code-based проверяет schema, state и artifacts; model-based rubric оценивает открытый synthesis; human reviewer калибрует спорные критерии и находит неожиданные failure modes. Ни один тип не покрывает всё.
Слой · Проверка · Пример
- Outcome - Изменилось ли внешнее состояние правильно; Release report одобрен только при всех evidence
- Artifact - Schema, completeness, provenance; Finding содержит source и severity
- Trajectory - Routes, tools, attempts, forbidden calls; Reviewer не редактировал source artifact
- Economics - Tokens, calls, wall time, accepted cost; Cost per accepted task не превысил gate
- Human - Полезность и скрытый rework; Время инженера на сборку результата
В companion-проекте outcome и порядок ветвей проверяются обычными тестами, а не доверием к сообщению supervisor.
test("runs independent branches and an evidence-gated review", async () => {
const report = await new Orchestrator({
tasks: releaseTasks,
handlers: createReleaseHandlers(),
limits,
maxConcurrency: 3
}).run("success");
assert.equal(report.status, "completed");
assert.equal(report.taskStatus.review, "completed");
assert.ok(report.artifacts.some((artifact) => artifact.key === "report/release.json"));
assert.equal(report.evidence.at(-1)?.name, "independent-review");
assert.equal(report.evidence.at(-1)?.passed, true);
});
test("parallel branches start after plan and before review", async () => {
const report = await new Orchestrator({
tasks: releaseTasks,
handlers: createReleaseHandlers(),
limits,
maxConcurrency: 3
}).run("ordering");
const completed = report.events
.filter((record) => record.event.type === "task.completed")
.map((record) => record.event.type === "task.completed" ? record.event.taskId : "");
assert.equal(completed[0], "plan");
assert.equal(completed.at(-1), "review");
assert.deepEqual(new Set(completed.slice(1, 4)), new Set(["tests", "security", "docs"]));
});
test("retries one transient worker failure within task budget", async () => {
const report = await new Orchestrator({
tasks: releaseTasks,
handlers: createReleaseHandlers(["security:security"]),
limits,
maxConcurrency: 3
}).run("retry");
assert.equal(report.status, "completed");Держите capability и regression suites отдельно: capability-набор содержит трудные задачи и показывает потенциал новой топологии, regression-набор защищает уже работающие сценарии и должен проходить почти всегда. При каждой смене model, prompt, tool description или routing policy прогоняйте оба.
Читайте traces, а не только aggregate score. Средний балл сообщает, что стало хуже; sampled transcript и trace показывают почему. Без просмотра trajectories легко оптимизировать judge, а не полезный процесс.
Оценка отделена от исполнения. Чтобы она пережила смену SDK, control plane строят независимо от провайдера модели.