Головна / Статті

Зупинка циклів неконтрольованих AI-агентів: чотири механізми захисту для продакшну.

Керування капом, токенами та часом, перевірка цілей за допомогою реальних тестів, використання інструментів хешування для виявлення циклів типу «пінг-понг», а також безпечне скасування дій у разі, якщо автономний агент застряг.

Видавець: REACTAPP.TOP14 хв читання3289 слів

Джерела: Don’t Let Your AI Agents Loop Forever: An Engineering Guide to Termination Criteria

Рахунок за API у розмірі 3 800 доларів від автономного агента, який протягом дванадцяти годин застряг у циклі виправлення синтаксису, — це процес, якого ніхто не хотів би пройти. Ось як створити правила зупинки промислового рівня для агентської системи.

Сценарій п’ятничного полудня

Це п’ятниця, 17:00. Новий агент для програмування потрапляє у тестове середовище. Його завдання — перевіряти запити на зміни, виправляти помилки в типах TypeScript та перезапускати тести. Інструкції здаються правильними, ноутбук закривається, і починається вихідні.

До ранку суботи телефон засипається сповіщеннями від PagerDuty та системи билінгу. Агент виправив файл user.service.ts через помилку в типі, тим самим пошкодивши auth.service.ts. Виправлення цього файлу знову пошкодило перший, і обидва файли продовжували скасовувати один одного зміни. Після чотирнадцяти сотень викликів інструментів рахунок за токени сягнув 3 800 доларів, а набір тестів пошкодився.

Більша частина уваги інженерів приділяється запитам, вибору інструментів та діаграмам оркестрації. У продакшені рішення з найбільшими наслідками — це те, як агент зупиняється, а не те, як він починає роботу.

Що таке цикл агента

Цикл агента — це контрольний цикл, у якому ШІ сприймає стан, обирає дію, викликає інструмент, читає результат та вирішує, чи продовжувати.

                   ┌──────────────────────────────────────┐
                   │        Agent Perception Loop         │
                   │       (Perceive → Plan → Act)        │
                   └──────────────────┬───────────────────┘
                                      │
           ┌──────────────────────────┼──────────────────────────┐
           ▼                          ▼                          ▼
┌────────────────────┐    ┌────────────────────┐    ┌────────────────────┐
│ 1. Success Guard   │    │ 2. Resource Caps   │    │ 3. Progress Guard  │
│ (Programmatic Test)│    │ (Tokens/Turns/Time)│    │ (Loop/Hash Detect) │
└────────────────────┘    └────────────────────┘    └────────────────────┘
                                      │
                                      ▼
                   ┌──────────────────────────────────────┐
                   │  4. Human Handoff / Safe Rollback    │
                   └──────────────────────────────────────┘

Якщо керувальна система навколо неї не має чітких, багатошарових правил завершення, агент зрештою потрапляє в один із трьох режимів збою:

  • Безкінечна екзекуція — постійне виконання ілюзорних або повторюваних викликів інструментів.
  • Раннє завершення — припинення роботи та оголошення про перемогу, коли робота ще не завершена або вже зазнає невдачі.
  • Вичерпання бюджету — витрачання всіх виділених токенів до завершення багатоетапного завдання.

Чотири типи критеріїв зупинки охоплюють ці випадки, і кожному агенту потрібні всі чотири перед тим, як його випустити у продакшн.

1. Критерії успіху: детермінована перевірка мети

Підступність ситуації. Ніколи не приймайте власний вердикт агента, запитуючи у ШІ, чи він завершив роботу. Моделі схильні до упередження щодо завершення та лестощів, і часто повідомляють про успіх, коли робота пошкоджена або файл вихідних даних порожній.

Рішення. Розглядайте результати роботи моделі як неперевірені гіпотези та нехай цикл порівнює фактичний стан із чимось детермінованим:

  • запустіть npm test або pytest у дочірньому процесі та прочитайте код вихіду
  • запустіть перевірку типів та лінтинг із суворими кодами вихіду, наприклад tsc --noEmit
  • проаналізуйте результати через схеми Zod або Pydantic
import { execSync } from 'node:child_process';

export interface VerificationResult {
  success: boolean;
  message: string;
  stepFailed?: string;
}

/** execSync throws on non-zero exit; diagnostics may land on either stream. */
function runOrCapture(command: string, cwd: string): string | null {
  try {
    execSync(command, { cwd, stdio: 'pipe' });
    return null;
  } catch (err: unknown) {
    const e = err as { stdout?: Buffer; stderr?: Buffer };
    const out = e.stdout?.toString() ?? '';
    const errOut = e.stderr?.toString() ?? '';
    return [out, errOut].filter(Boolean).join('\n') || String(err);
  }
}

export class GoalVerifier {
  public static verify(workspacePath: string): VerificationResult {
    const steps: Array<[string, string]> = [
      ['tsc', 'npx tsc --noEmit'],
      ['npm_test', 'npm test'],
    ];

    for (const [stepId, command] of steps) {
      const failure = runOrCapture(command, workspacePath);
      if (failure !== null) {
        return {
          success: false,
          message: `\`${command}\` failed:\n${failure}`,
          stepFailed: stepId,
        };
      }
    }

    return { success: true, message: 'All typechecks and tests passed cleanly.' };
  }
}

Основне правило просте. Якщо людський рецензент виконає команду для перевірки запиту на з’єднання коду, ця сама команда має стати умовою завершення циклу.

2. Ліміти ресурсів та бюджету: жорсткі обмеження

Проблема. Коли агент стикається з екстремальним випадком чи несподіваною помилкою інструменту, його стандартною дією є повторна спроба з незначними змінами запиту. Без обмежень на рівні двигуна один застряглий цикл може спожити мільйони токенів протягом кількох хвилин.

Рішення. Встановити жорсткі обмеження у трьох незалежних аспектах: кількості кроків, кількості токенів та часу, що йде. Підраховувати кроки та токени окремо, адже один крок може включати кілька викликів моделі, і їх змішування приховано збільшує кількість кроків.

export interface ResourceLimits {
  maxTurns: number;       // e.g. 10 iterations
  maxTotalTokens: number; // e.g. 100_000 input + output
  timeoutMs: number;      // e.g. 120_000 (2 minutes)
}

export class ResourceGuard {
  private readonly startTime = Date.now();
  private totalTokensUsed = 0;
  private currentTurn = 0;

  constructor(private readonly limits: ResourceLimits) {}

  /** Call once per loop iteration, before the model call. */
  public beginTurn(): void {
    this.currentTurn += 1;
  }

  /** Call for every model call, including retries inside a turn. */
  public recordUsage(tokens: number): void {
    this.totalTokensUsed += tokens;
  }

  public getTurnCount(): number {
    return this.currentTurn;
  }

  public checkShouldTerminate(): { terminate: boolean; reason?: string } {
    if (this.currentTurn >= this.limits.maxTurns) {
      return {
        terminate: true,
        reason: `Exceeded turn cap (${this.limits.maxTurns})`,
      };
    }
    if (this.totalTokensUsed >= this.limits.maxTotalTokens) {
      return {
        terminate: true,
        reason: `Exceeded token budget (${this.totalTokensUsed}/${this.limits.maxTotalTokens})`,
      };
    }
    const elapsed = Date.now() - this.startTime;
    if (elapsed >= this.limits.timeoutMs) {
      return {
        terminate: true,
        reason: `Wall-clock timeout reached (${elapsed}ms/${this.limits.timeoutMs}ms)`,
      };
    }
    return { terminate: false };
  }
}

Ліміти є запобіжним заходом, а не елементом дизайну. Якщо ваш агент регулярно зупиняється через досягнення значення maxTurns, проблема не в лімітах, а у розбитті завдання на кроки.

3. Механізми контролю прогресу: виявлення агентів, які застрягли чи відхилилися від маршруту

Три патерни постійно зустрічаються:

  • Пінг-понг. Агент використовує інструмент A, стикається з помилкою X, намагається її виправити за допомогою інструменту B, стикається з помилкою Y, а потім знову використовує інструмент A з тими самими вхідними даними.
  • Безладні дії. Редагування файлу A пошкоджує файл B, а редагування файлу B пошкоджує файл A. Робоче середовище постійно змінюється, тоді як загальний прогрес залишається нульовим.
  • Семантичне відхилення. Агент стикається з несподіваним попередженням лінтера, витрачає п’ять кроків на форматування та забуває про початкову мету.

Щоб виявити застрягання ще до того, як буде використано всі maxTurns, зберігайте «скользяче вікно» хешів викликів інструментів разом із хешіми відмінностей робочого простору. Повторення однакових хешів означає, що агент обертається у кругу.

import { createHash } from 'node:crypto';

export interface ToolCall {
  name: string;
  args: Record<string, unknown>;
}

export class ProgressGuard {
  private readonly recentActionHashes: string[] = [];

  constructor(
    private readonly windowSize = 5,
    private readonly repeatThreshold = 3,
  ) {}

  /** Stable stringify: key order must not change the hash. */
  private hashToolCall(call: ToolCall): string {
    const args = JSON.stringify(call.args, Object.keys(call.args).sort());
    return createHash('sha256').update(`${call.name}:${args}`).digest('hex');
  }

  /** Returns true when the same call has appeared `repeatThreshold` times in the window. */
  public trackAndCheckStuck(call: ToolCall): boolean {
    const actionHash = this.hashToolCall(call);
    const priorOccurrences = this.recentActionHashes.filter((h) => h === actionHash).length;

    this.recentActionHashes.push(actionHash);
    if (this.recentActionHashes.length > this.windowSize) {
      this.recentActionHashes.shift();
    }

    return priorOccurrences + 1 >= this.repeatThreshold;
  }
}

Не кожне повторення заслуговує на однакову підозру. Виклик зі правом лише на читання, здійснений тричі, зазвичай нешкідливий, тоді як операція запису, повторена тричі, майже ніколи не є такою. Якщо доступні метадані інструменту, слід скоригувати поріг відповідно, замість того щоб ставитися до всіх викликів однаково.

4. Передача справи людині та безпечне скасування змін

Коли цикл закінчується через обмеження бюджету або застрягання, найгіршим наслідком є напівзламана система. Механізм переривання має виконати три кроки по черзі: зібрати діагностичну інформацію (який інструмент зламався та які останні помилки були), повернути робоче середовище у відомий стан та передати справу людині з чітким оглядом ситуації.

Будьте обережні під час виконання другого кроку. Виконання команд git reset --hard за підтримки git clean -fd не можна скасувати. Команда git stash --include-untracked є безпечнішою, оскільки зберігає роботу агента для подальшого перегляду та дозволяє її пізніше видалити. Використовуйте повне скидання лише у тимчасових средових контейнерах.

import { execSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';

export class AgentEscalationRequiredError extends Error {
  constructor(message: string, public readonly reportPath?: string) {
    super(message);
    this.name = 'AgentEscalationRequiredError';
  }
}

export class AgentCircuitBreaker {
  constructor(
    private readonly workspaceDir: string,
    private readonly reportDir: string, // keep reports OUTSIDE the workspace
  ) {}

  public handleAbort(reason: string, history: unknown[] = []): never {
    console.error(`[CIRCUIT BREAKER] Terminating agent loop: ${reason}`);

    // 1. Park workspace changes recoverably.
    try {
      execSync('git stash push --include-untracked -m "agent-abort"', {
        cwd: this.workspaceDir,
        stdio: 'pipe',
      });
    } catch (err) {
      console.error('git stash failed during abort; workspace left as-is:', err);
    }

    // 2. Write a diagnostic trace for human review.
    const reportPath = this.writeFailureReport(reason, history);

    // 3. Signal the orchestrator.
    throw new AgentEscalationRequiredError(`Agent failed safely. Reason: ${reason}`, reportPath);
  }

  private writeFailureReport(reason: string, history: unknown[]): string {
    const reportPath = join(this.reportDir, `agent_failure_${Date.now()}.json`);
    writeFileSync(
      reportPath,
      JSON.stringify(
        {
          timestamp: new Date().toISOString(),
          workspace: this.workspaceDir,
          reason,
          historyLength: history.length,
          history: history.slice(-10),
        },
        null,
        2,
      ),
      'utf-8',
    );
    return reportPath;
  }
}

Кейс-стаді: генератор додатків з відкритим форматом

Для виправлення багу існує готовий набір тестів для перевірки. Складнішим є випадок з відкритим, суб’єктивним запитом, наприклад: створити сучасний веб-додаток локально, який керуватиме завданнями, працюватиме автономно до завершення роботи, буде досконалим та повністю функціональним, а також вирішити самостійно, коли його можна зупинити.

Такі слова, як „досконалим“ та „завершення“, мають надзвичайно нечітке значення. Якщо залишити все як є, агент або зупиниться після створення простого файлу index.html, або буде вічно налаштовувати маржі CSS.

 ┌──────────────────────────────────────────────────────────┐
 │ Vague prompt ("build a modern web app locally")          │
 └────────────────────────────┬─────────────────────────────┘
                              ▼
 ┌──────────────────────────────────────────────────────────┐
 │ Phase 1: Dynamic spec synthesis (`ac-matrix.json`)       │
 └────────────────────────────┬─────────────────────────────┘
                              ▼
 ┌──────────────────────────────────────────────────────────┐
 │ Phase 2: Multi-agent execution loop                      │
 │ (Coder agent + design critic + headless E2E verifier)    │
 └────────────────────────────┬─────────────────────────────┘
                              │
    ┌─────────────────────────┼─────────────────────────┐
    ▼                         ▼                         ▼
┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│ Gate 1: Goal     │    │ Gate 2: Resource │    │ Gate 3: Progress │
│ verification     │    │ caps (turns/     │    │ guard (deadlock/ │
│ (build/E2E/ACs)  │    │ token budget)    │    │ repetition)      │
└─────────┬────────┘    └─────────┬────────┘    └─────────┬────────┘
          └───────────────────────┼───────────────────────┘
                                  ▼
 ┌──────────────────────────────────────────────────────────┐
 │ Gate 4: Safe exit OR circuit-breaker rollback            │
 └──────────────────────────────────────────────────────────┘

Спочатку синтезуємо специфікацію

На першому кроці, ще до написання будь-якого коду, агент створює матрицю критеріїв прийнятності та зберігає її як ac-matrix.json. Він групує ці критерії на три категорії:

  • Критерії функціоналу, такі як робота з локальним SQLite чи JSON, операції CRUD та можливість перемикання у темний режим.
  • Інженерні критерії, такі як відсутність попереджень TypeScript та lint, а також сервер розробки, який запускається без помилок у консолі.
  • Дизайнерські критерії, такі як адаптивний форматування, послідовні дизайнерські елементи, стани порожнечі та прості взаємодії.

Той самий модель, який створює матрицю, пізніше оцінюватиме себе за нею, тому її слід розглядати як контракт щодо обсягу роботи, а не як об’єктивний стандарт. Її справжня цінність полягає у тому, що вона фіксується на ранній стадії, ще до того, як у агента з’являться причини змінювати цілі.

Розподіл ролей

Щоб запобігти ситуації, коли агент оцінює власну роботу як досконалу, розділіть завдання:

  • Підагент-кодер створює функції, маршрутизацію та стилізацію.
  • Перевірювач безголового браузера запускає сервер розробки, робить скріншоти за допомогою Playwright та перевіряє помилки в консолі.
  • Підагент-критик дизайну аналізує інтерфейс користувача згідно зі стандартами дизайн-системи щодо ієрархії, відстаней та контрасту.

Інструментарій

Наведений нижче код передбачає використання вбудованих залежностей (llmAgent, executeTool, runHeadlessBrowserCheck та runDesignCriticAgent); їх можна замінити на власний шар оркестрації.

import { execSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { ResourceGuard, ProgressGuard, AgentCircuitBreaker } from './guards';

interface AcceptanceCriterion {
  id: string;
  description: string;
  status: 'PENDING' | 'IN_PROGRESS' | 'DONE';
}

interface AcMatrix {
  items: AcceptanceCriterion[];
}

export interface RunResult {
  success: true;
  turns: number;
  summary: string;
}

export async function runOpenEndedWebAppGenerator(
  userPrompt: string,
  workspacePath: string,
  reportDir: string,
  devServerUrl = 'http://localhost:5173',
  options = { maxTurns: 15, maxTotalTokens: 200_000, timeoutMs: 300_000 },
): Promise<RunResult> {
  const resources = new ResourceGuard(options);
  const progress = new ProgressGuard();
  const circuitBreaker = new AgentCircuitBreaker(workspacePath, reportDir);
  const acMatrixPath = join(workspacePath, 'ac-matrix.json');

  let currentPrompt = userPrompt;
  let designRetries = 0;
  const maxDesignRetries = 3;

  while (true) {
    // GUARD 1: resource ceilings
    const resourceCheck = resources.checkShouldTerminate();
    if (resourceCheck.terminate) {
      circuitBreaker.handleAbort(resourceCheck.reason!);
    }
    resources.beginTurn();

    const turnResult = await llmAgent.step(currentPrompt);
    resources.recordUsage(turnResult.tokensUsed);

    // GUARD 2: deadlock detection (only meaningful when a tool was called)
    let toolOutput = '(no tool call this turn)';
    if (turnResult.toolCall) {
      if (progress.trackAndCheckStuck(turnResult.toolCall)) {
        circuitBreaker.handleAbort('Repeating tool call detected (stuck agent)');
      }
      toolOutput = await executeTool(turnResult.toolCall);
    }

    // GUARD 3: deterministic convergence check
    const verification = await evaluateConvergence(workspacePath, acMatrixPath, devServerUrl);

    if (verification.gateFailed === 'design') {
      designRetries += 1;
      if (designRetries > maxDesignRetries) {
        circuitBreaker.handleAbort(
          `Design gate never converged after ${maxDesignRetries} refinement passes`,
        );
      }
    }

    if (verification.isConverged) {
      return {
        success: true,
        turns: resources.getTurnCount(),
        summary: 'Web app built, tested, and design-reviewed cleanly.',
      };
    }

    currentPrompt = `Tool output:\n${toolOutput}\n\nConvergence status:\n${verification.statusMessage}`;
  }
}

interface ConvergenceResult {
  isConverged: boolean;
  statusMessage: string;
  gateFailed?: 'build' | 'runtime' | 'acs' | 'design';
}

async function evaluateConvergence(
  workspacePath: string,
  acMatrixPath: string,
  devServerUrl: string,
): Promise<ConvergenceResult> {
  // Gate 1: build and typecheck
  try {
    execSync('npx tsc --noEmit && npm run build', { cwd: workspacePath, stdio: 'pipe' });
  } catch (err: unknown) {
    const e = err as { stdout?: Buffer; stderr?: Buffer };
    const output = [e.stdout?.toString(), e.stderr?.toString()].filter(Boolean).join('\n');
    return {
      isConverged: false,
      gateFailed: 'build',
      statusMessage: `Gate 1 failed (build/typecheck):\n${output || String(err)}`,
    };
  }

  // Gate 2: dev server and runtime health
  const e2eResult = await runHeadlessBrowserCheck(devServerUrl);
  if (!e2eResult.noConsoleErrors) {
    return {
      isConverged: false,
      gateFailed: 'runtime',
      statusMessage: `Gate 2 failed (console errors): ${e2eResult.errors.join(', ')}`,
    };
  }

  // Gate 3: acceptance criteria fully complete
  let acMatrix: AcMatrix;
  try {
    acMatrix = JSON.parse(readFileSync(acMatrixPath, 'utf-8')) as AcMatrix;
  } catch {
    return {
      isConverged: false,
      gateFailed: 'acs',
      statusMessage: 'Gate 3 incomplete: `ac-matrix.json` missing or unparseable.',
    };
  }

  const pending = acMatrix.items.filter((ac) => ac.status !== 'DONE');
  if (pending.length > 0) {
    return {
      isConverged: false,
      gateFailed: 'acs',
      statusMessage: `Gate 3 incomplete: ${pending.length} ACs remaining (${pending
        .map((a) => a.id)
        .join(', ')})`,
    };
  }

  // Gate 4: design audit (soft gate — see retry cap in the caller)
  const criticVerdict = await runDesignCriticAgent(e2eResult.screenshots);
  if (criticVerdict.score < 8.5) {
    return {
      isConverged: false,
      gateFailed: 'design',
      statusMessage: `Gate 4 incomplete (design ${criticVerdict.score}/10): ${criticVerdict.feedback}`,
    };
  }

  return { isConverged: true, statusMessage: 'All four convergence gates passed.' };
}

Зверніть увагу, що ці „брами“ не є однаковими. Перші дві – компілятор та браузер – є детермінованими, оскільки жоден з них не має власної думки. Останні дві оцінюються за допомогою моделей, що означає, що вони можуть постійно змінюватися туди-сюди. Саме тому „брама“ проектування потребує власного ліміту на спроби. Суб’єктивний поріг без обмежень на кількість спроб – це просто повільний нескінченний цикл.

Багаторазовий основний запит

Щоб запустити цей процес у Claude Code, Copilot CLI, Cursor чи Aider, наведена нижче шаблонна структура наказує агенту створити власну матрицю прийнятності, виконувати ітерації в циклах розробки, перевіряти результати локально, оцінювати якість дизайну та зупинятися лише після завершення додатку.

You are an autonomous lead software engineer, UX designer, and QA verifier. Your goal
is to build a production-quality web application locally, from scratch.

You must operate in a self-terminating agentic loop, running iteratively until the
application is complete, polished, functional, and verified.

================================================================================
1. TARGET APPLICATION SPECIFICATION
================================================================================

[DESCRIBE YOUR APP IDEA HERE — e.g. "A task management web app with local SQLite
persistence, a kanban board with drag-and-drop, priority tags, search/filter
controls, and a dark mode theme."]

================================================================================
2. EXECUTION PROTOCOL
================================================================================

PHASE 1 — DYNAMIC SPEC SYNTHESIS (TURN 1)
Before writing application code or installing dependencies:
  1. Initialize the local project structure (e.g. Vite + React, Next.js, or Node).
  2. Create `ac-matrix.json` in the workspace root defining explicit acceptance
     criteria:
       - Feature ACs: persistence, full CRUD, interactive components, error
         handling, edge cases.
       - Engineering ACs: strict TypeScript (`npx tsc --noEmit`), zero build
         errors, zero linter warnings, dev server boots cleanly.
       - Design ACs: visual hierarchy, responsive layout, dark/light toggle,
         empty states, micro-interactions.

Format:
  {
    "project": "<app-name>",
    "items": [
      { "id": "FEAT-1", "category": "feature",     "description": "Local database persistence for tasks", "status": "PENDING" },
      { "id": "FEAT-2", "category": "feature",     "description": "Drag-and-drop kanban re-ordering",      "status": "PENDING" },
      { "id": "ENG-1",  "category": "engineering", "description": "Clean TypeScript build, zero errors",   "status": "PENDING" },
      { "id": "ENG-2",  "category": "engineering", "description": "Dev server starts with 0 console errors","status": "PENDING" },
      { "id": "DSGN-1", "category": "design",      "description": "Responsive UI with dark/light mode",    "status": "PENDING" }
    ]
  }

Once written, treat `ac-matrix.json` as frozen scope. Do not delete or weaken an
AC to make a gate pass. If an AC turns out to be genuinely infeasible, mark it
BLOCKED with a reason and surface it in the final summary.

PHASE 2 — AUTONOMOUS DEVELOPMENT LOOP
In each turn:
  - Implement features, components, schemas, and routes incrementally.
  - Run local validation after code changes (`npx tsc --noEmit`, `npm run build`).
  - Update AC statuses (PENDING → IN_PROGRESS → DONE) as work is verified.
  - Self-correction rule: if a command fails, read the exact error, fix the root
    cause, and re-verify. Do not repeat the same failing command or edit more
    than twice — change approach instead.

PHASE 3 — THE 4-GATE CONVERGENCE CHECK (MANDATORY)
Do not end execution or declare the project finished until all four gates pass
in the same turn:

Gate 1 — Compiler and build
    `npx tsc --noEmit` and `npm run build` both exit 0 with zero errors.

Gate 2 — Dev server and runtime health
    `npm run dev` boots cleanly with zero unhandled console or network errors.

Gate 3 — Acceptance criteria complete
    Every item in `ac-matrix.json` has "status": "DONE".

Gate 4 — Design audit score >= 8.5/10
    Audit visual hierarchy, color consistency, typography scale, spacing,
    transitions, responsive behavior, and empty states. Score out of 10. If
    below 8.5, refine and re-audit — but no more than 3 design passes total.
    After 3 passes, stop and report the final score as-is.

================================================================================
3. FINAL COMPLETION OUTPUT
================================================================================

Only when all four gates pass, output:
  - Final status: PROJECT COMPLETE & VERIFIED
  - App summary and architecture overview
  - Build and test commands executed, with exit codes
  - Completed acceptance-criteria summary (including any BLOCKED items)
  - Final design score (X/10) and UX highlights
  - Instructions for running the app locally

Then stop.

Чек-лист перед-продакшну

Перш ніж запустити цикл агента, перевірте його за допомогою цих критеріїв:

  1. Перевірка мети за допомогою детерміністичного тесту чи компілятора та зовнішньої перевірки коду завершення. Обов’язково.
  2. Ліміт кількості ходів — фіксований ліміт ітерацій, наприклад currentTurn >= maxTurns. Обов’язково.
  3. Бюджет токенів — кумулятивний ліміт, наприклад totalTokens >= tokenLimit. Обов’язково.
  4. Тайм-аут — ліміт у часі, наприклад elapsed >= timeoutMs. Обов’язково.
  5. Захист прогресу — детектор дублікатів підписів інструментів за принципом ковзної вікна. Рекомендується.
  6. Захист стану — хеш різниці робочого простору, який виявляє відсутність змін. Рекомендується.
  7. Безпечний запасний варіант — це механізм аварійного вимкнення з можливістю скасування змін за допомогою git stash у разі переривання. Обов’язково.

Межі перед інтелектом

Надійні автономні агенти створюються не стільки шляхом роботи моделі розумнішою, скільки завдяки розробці чітких операційних обмежень. Чотири фактори визначають різницю між крихким експериментом та системою, яку можна залишити у роботі протягом усих вихідних: перевірювачі, які не залежать від моделі, жорсткі ліміти ресурсів, хешиування прогресу та можливість скасування змін для відновлення стану системи.