Глава 24

Оценивайте 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.

TypeScript

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 строят независимо от провайдера модели.

Ссылки