Ваш AI-агент зависает без человека? Фоновые автолупы доводят задачи до конца и зовут вас только на спорных шагах
Я — Denis Shokhirev, Agentic AI Systems Architect из Freiburg im Breisgau, Germany, и в DennisCraft AI Studio я собираю production-grade AI-системы на Claude, Supabase, n8n, Doppler и self-hosted Postgres. Самая частая поломка, которую я вижу не в демо, а в живом контуре, проста: агент делает 80% работы, доходит до неоднозначного шага, останавливается и ждет человека часами, хотя 15 предыдущих шагов он мог спокойно дожать сам в фоне. Проблема не в том, что модели "недостаточно умные". Проблема
Я — Denis Shokhirev, Agentic AI Systems Architect из Freiburg im Breisgau, Germany, и в DennisCraft AI Studio я собираю production-grade AI-системы на Claude, Supabase, n8n, Doppler и self-hosted Postgres. Самая частая поломка, которую я вижу не в демо, а в живом контуре, проста: агент делает 80% работы, доходит до неоднозначного шага, останавливается и ждет человека часами, хотя 15 предыдущих шагов он мог спокойно дожать сам в фоне.
Проблема не в том, что модели "недостаточно умные". Проблема в архитектуре управления задачей. Если у вас один синхронный вызов модели и один блок "approve / reject", вы строите чат-бота с дорогой обвязкой, а не автономную систему. В production я почти всегда заменяю такой паттерн на фоновые autoloops: агент берет задачу, сам проходит повторяемые этапы, пишет состояние в Postgres, ставит таймеры, валидирует промежуточные артефакты и зовет человека только там, где реально есть спорное решение, доступ к чувствительным данным или риск для бизнеса.
Почему агент зависает и почему человек оказывается в каждом цикле
Типичный сценарий выглядит так: агент получил задачу, сходил в API, собрал данные, подготовил SQL, сформировал письмо клиенту, а затем уперся в неполный контекст. Вместо того чтобы запросить недостающий факт, перейти к следующему безопасному шагу или поставить задачу в retry, система блокируется на "manual review". После этого SLA уже зависит не от архитектуры, а от того, кто и когда увидит уведомление в Slack.
Что именно ломается
| Симптом | Причина | Что делать |
|---|---|---|
| Задача висит после 1 ошибки API | Нет retry и backoff | Вынести шаги в loop с ограничением попыток |
| Человек подтверждает очевидные шаги | Нет классификации риска | Разделить safe / review / blocked действия |
| Потеря контекста после рестарта | Состояние хранится в памяти процесса | Хранить state machine в Postgres или Supabase |
| Нельзя понять, почему агент встал | Нет event log | Писать transition и reason_code на каждом шаге |
На практике агенту не нужен человек "на всякий случай". Ему нужен человек на ограниченном наборе развилок. Например: подтвердить платеж, одобрить изменение договора, выбрать между 2 правдоподобными интерпретациями клиентского запроса, дать доступ к таблице с персональными данными. Все остальное должно идти через цикл планирования, выполнения, проверки и повторной попытки.
Как я строю фоновые autoloops в production
Autoloop — это не магия и не отдельный продукт. Это pattern: задача живет как запись в БД, воркер периодически подбирает активные задачи, выполняет 1 шаг, фиксирует новый статус и либо продолжает, либо эскалирует человеку. Такой подход проще тестировать, проще объяснять аудитору и проще восстанавливать после сбоя.
Минимальная state machine
Я обычно начинаю с 6 состояний: queued, running, waiting_external, waiting_human, done, failed. Поверх них добавляю поля risk_level, attempt_count, next_run_at, reason_code. Этого хватает, чтобы агент не жил "внутри prompt", а был наблюдаемой системой.
import os, time, psycopg
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
conn = psycopg.connect(os.environ["DATABASE_URL"])
with conn.cursor() as cur:
cur.execute("""
select id, payload, attempt_count
from agent_tasks
where status in ('queued','running')
and next_run_at <= now()
order by created_at
limit 10
for update skip locked
""")
tasks = cur.fetchall()
for task_id, payload, attempts in tasks:
msg = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=400,
messages=[{"role": "user", "content": payload}]
)
outcome = msg.content[0].text
next_status = "waiting_human" if "NEEDS_APPROVAL" in outcome else "done"
with conn.cursor() as cur:
cur.execute(
"update agent_tasks set status=%s, result=%s, attempt_count=%s where id=%s",
(next_status, outcome, attempts + 1, task_id)
)
conn.commit()Этот пример короткий, но идея в нем правильная: модель не завершает "всю работу мира", а двигает задачу на 1 контролируемый шаг. Если нужен еще шаг, его берет следующий проход loop. Если решение спорное, задача уходит в waiting_human.
Где ставить границы: что агент делает сам, а что эскалирует
Без этого autoloop превращается либо в дорогой ручной процесс, либо в неконтролируемый автомат. Я использую матрицу по риску и обратимости действия.
| Тип действия | Автономно | С человеком |
|---|---|---|
| Сбор данных из внутренних API | Да | Только при ошибке доступа |
| Классификация входящих заявок | Да, с логированием | При низкой уверенности |
| Черновик письма клиенту | Да | Если есть юридические формулировки |
| Изменение записей в Postgres | Да, через allowlist SQL | При массовом обновлении |
| Платеж, контракт, удаление данных | Нет | Всегда |
Если работаете в regulated market, не передавайте модели право на необратимое действие без явного policy gate. Для SQL это может быть allowlist таблиц и запрет на drop, truncate, массовые update без фильтра. Для сообщений клиентам — обязательный human review при определенных тегах.
#!/usr/bin/env bash
set -euo pipefail
bandit -r app/ -f json -o bandit.json
gitleaks detect --source . --report-format json --report-path gitleaks.json
semgrep --config p/owasp-top-ten --json --output semgrep.json
if jq -e '.results | length > 0' semgrep.json >/dev/null; then
echo "Semgrep found issues"
exit 1
fi
echo "Static checks passed"Я часто ставлю такие проверки не только в CI, но и как gate перед тем, как агент применяет сгенерированный код или SQL. Это особенно полезно там, где агент пишет вспомогательные скрипты, миграции или integration glue.
Наблюдаемость важнее красивого prompt
Когда команда говорит "агент иногда тормозит", почти всегда оказывается, что никто не может ответить на 3 вопроса: на каком шаге он остановился, почему он остановился, и сколько раз до этого он уже пытался. Хороший autoloop отвечает на это без чтения логов контейнера.
Что логировать обязательно
- task_id, step_name, status_before, status_after
- reason_code для каждого перехода
- attempt_count и next_run_at
- какие tools были вызваны и с какими правами
- кто одобрил human escalation и за сколько минут
Для команд, которые работают с европейскими клиентами, это еще и вопрос аудита. OWASP в LLM Top 10 2025 отдельно указывает на risks around excessive agency, prompt injection и insecure output handling: https://genai.owasp.org/. Если ваш агент может вызвать tool, изменить запись и отправить письмо, но вы не видите цепочку решений, у вас не AI-система, а черный ящик с привилегиями.
Практический контур на n8n, Supabase и Postgres
Если нужен быстрый и понятный контур, я обычно делаю так: входящая задача падает в Supabase/Postgres, n8n ставит событие и wake-up, Python-воркер вызывает модель через Anthropic SDK, затем пишет результат обратно в БД. Для секретов — Doppler. Для публично наблюдаемых контуров — отдельный read-only слой и вычищенные event payloads.
name: agent-loop
on:
schedule:
- cron: "*/2 * * * *"
jobs:
run-loop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install anthropic psycopg[binary]
- run: python workers/run_loop.py
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}Это не единственный способ, но он дает важное свойство: задача переживает рестарт, очередь не теряется, а человек видит реальное состояние. Именно поэтому такие системы доживают до production, а не заканчиваются на красивом скриншоте в Notion.
FAQ
Когда вообще нужен autoloop, а не обычный чат?
Когда задача длится дольше 1 запроса, зависит от внешних API, требует retry или промежуточных проверок. Если у вас есть статусы и побочные эффекты, вам почти всегда нужен loop.
Можно ли сделать это только на n8n?
Для простых процессов — да. Но как только появляются ветвления, retries, policy gates и аудит, я советую хранить состояние в Postgres, а n8n использовать как orchestration layer, а не как единственный source of truth.
Нужен ли human-in-the-loop всегда?
Нет. Нужен human-on-uncertainty или human-on-risk. Человек должен приходить туда, где цена ошибки выше цены задержки.
Какой самый частый анти-паттерн?
Хранить весь state в prompt или в памяти одного процесса. После рестарта вы теряете трассировку и не можете безопасно продолжить задачу.
Как измерять, что autoloop реально помог?
Смотрите на 4 метрики: median time to completion, доля задач без участия человека, время до эскалации и доля повторных попыток, завершившихся успешно. Если метрик нет, вы спорите мнениями, а не управляете системой.
В каком месте вашего LLM-контура задачи чаще всего зависают в production: на tool calls, на retries внешних API, на policy gate или на человеческом согласовании? Мне правда интересно сравнить это с тем, что я вижу у DACH B2B команд. I run a free 30-min stack audit for DACH founders building AI in regulated markets. DM me on LinkedIn or write to @ger_dennis_ai.
Такие агенты я делаю для бизнеса в Германии и ЕС.
Опишите в двух предложениях, что отнимает у команды время, — отвечу в течение суток.
Превратить процесс в систему, которая работает
Продакшн-качество, а не демо.