Partial outage (agent failure + degrade mode + код)

  • Побач ранні сигнали, поки рахунок не поліз вгору.
  • Зрозумій, що ламається в проді й чому.
  • Скопіюй guardrails: budgets, stop reasons, validation.
  • Знай, коли це не справжня root cause.
Сигнали виявлення
  • Tool calls на run зростають (або повторюються з args hash).
  • Витрати/токени ростуть без кращих результатів.
  • Retries стають постійними (429/5xx).
Частина tools лежить, частина працює. Агенти, які “дотискають”, починають thrash і палять бюджети. Як деградувати безпечно з partial results і stop reasons.
На цій сторінці
  1. Проблема (з реального продакшену)
  2. Чому це ламається в продакшені
  3. 1) Агент сприймає інтермітентний успіх як “продовжуй”
  4. 2) Немає поняття tool health
  5. 3) Немає safe-mode поведінки
  6. 4) “All or nothing” outputs змушують до поганої поведінки
  7. Приклад реалізації (реальний код)
  8. Реальний інцидент (з цифрами)
  9. Компроміси
  10. Коли НЕ варто
  11. Чекліст (можна копіювати)
  12. Безпечний дефолтний конфіг (JSON/YAML)
  13. FAQ (3–5)
  14. Пов’язані сторінки (3–6 лінків)
Інтерактивний флоу
Сценарій:
Крок 1/2: Execution

Normal path: execute → tool → observe.

Проблема (з реального продакшену)

Це не full outage. Це гірше.

Один tool flaky:

  • інколи 200
  • інколи timeout
  • інколи 502

Агент продовжує “доробити задачу”. Користувачі ретраять, бо бачать таймаути. Бюджети горять, бо кожен retry — це новий run.

Partial outages — це місце, де видно, агент у тебе інженер чи азартний гравець.

Чому це ламається в продакшені

Partial outages важкі, бо успіх інколи трапляється. Це провокує loops.

1) Агент сприймає інтермітентний успіх як “продовжуй”

LLM оптимістичні. Якщо вони отримали хоча б частковий результат, вони часто намагаються “дозбирати”.

У ноутбуці це ок. У проді це runaway spend.

2) Немає поняття tool health

Якщо агент не знає “tool X деградує”, він буде:

  • викликати його знову
  • ретраїти
  • переплановувати й знову викликати

Потрібен спільний health signal:

  • стан breaker’а
  • recent error rate
  • latency spikes

3) Немає safe-mode поведінки

Коли tool деградує, потрібен план, який не залежить від нього:

  • кеш
  • partial results
  • stop reason і право користувача вирішити, що далі

4) “All or nothing” outputs змушують до поганої поведінки

Якщо контракт API: “завжди поверни повну відповідь” — агент буде thrash під час partial outage.

Кращий контракт:

  • partial results + confidence + stop reason
  • опційно: async continuation

Приклад реалізації (реальний код)

Патерн “health snapshot” на старті run. Якщо критичний tool деградований:

  • вимикаємо його для run
  • переключаємось у safe-mode
  • повертаємо partial + явний stop reason
PYTHON
from dataclasses import dataclass
from typing import Any


@dataclass(frozen=True)
class Health:
  degraded_tools: set[str]


def snapshot_health() -> Health:
  # In real code: breaker states + recent error rates.
  return Health(degraded_tools=set(get_degraded_tools()))  # (pseudo)


def safe_tools_for_run(health: Health) -> set[str]:
  allow = {"search.read", "kb.read", "http.get"}
  # During outages: be conservative.
  for t in health.degraded_tools:
      allow.discard(t)
  return allow


def run(task: str) -> dict[str, Any]:
  health = snapshot_health()
  allow = safe_tools_for_run(health)

  if "kb.read" not in allow:
      return {
          "status": "degraded",
          "reason": "kb.read degraded",
          "partial": "I can’t reliably read the KB right now. Here’s what I can do without it…",
      }

  # Normal loop would run here with a tool gateway allowlist = allow.
  return agent_loop(task, allow=allow)  # (pseudo)
JAVASCRIPT
export function snapshotHealth() {
// Real code: breaker states + recent error rates.
return { degradedTools: new Set(getDegradedTools()) }; // (pseudo)
}

export function safeToolsForRun(health) {
const allow = new Set(["search.read", "kb.read", "http.get"]);
for (const t of health.degradedTools) allow.delete(t);
return allow;
}

export function run(task) {
const health = snapshotHealth();
const allow = safeToolsForRun(health);

if (!allow.has("kb.read")) {
  return {
    status: "degraded",
    reason: "kb.read degraded",
    partial: "I can’t reliably read the KB right now. Here’s what I can do without it…",
  };
}

return agentLoop(task, { allow }); // (pseudo)
}

Це навмисно консервативно. Під час partial outage мета не “успіх будь-якою ціною”. Мета — “не перетворити partial outage на full outage”.

Реальний інцидент (з цифрами)

У нас був агент, який відповідав на support‑питання через kb.read.

KB сервіс деградував (p95 latency ~300ms → 9s, інтермітентні таймаути). Агент продовжував, бо інколи працювало.

Impact:

  • average run time: 8s → 52s
  • клієнтські ретраї подвоїли трафік
  • on-call пейджили “agent timeouts”, а не реальний KB інцидент
  • витрати виросли ~$180/день лише на ретраї + більші промпти

Fix:

  1. health snapshot + degrade mode
  2. fail fast після відкриття breaker’а
  3. partial results + чіткий stop reason
  4. “retry later” підказка замість тихих таймаутів

Partial outages навчили нас: stop reasons, видимі користувачу, — це фіча.

Компроміси

  • Degrade mode відповіді менш повні.
  • Fail fast знижує success rate тут і зараз.
  • Health signals можуть помилятись (false positives). Це краще, ніж thrash.

Коли НЕ варто

  • Якщо потрібна строга повнота — запускай async і показуй прогрес, а не синхронні loops.
  • Якщо ти не можеш визначити семантику partial output — ти будеш змушений таймаутити (погано).
  • Якщо в тебе немає health signals — почни з budgets і breaker defaults.

Чекліст (можна копіювати)

  • [ ] Tool health snapshot на старті run
  • [ ] Degrade mode policy (tools disabled, read-only, cached)
  • [ ] Fail fast, коли breaker open
  • [ ] Partial results + явний stop reason
  • [ ] Budgets (time/tool calls/spend) все ще діють
  • [ ] Алерти по degraded runs vs normal runs

Безпечний дефолтний конфіг (JSON/YAML)

YAML
degrade_mode:
  enabled: true
  disable_tools_when_degraded: true
  allow_partial_results: true
health:
  breaker_open_means_degraded: true
budgets:
  max_seconds: 60
  max_tool_calls: 12

FAQ (3–5)

Чому не ретраїти, доки не спрацює?
Бо інтермітентні фейли + ретраї підсилюють аутедж. Агент стає генератором навантаження.
Що повертати в degrade mode?
Partial results, cached data або чесне ‘не можу зараз’ зі stop reason.
Чи потрібен per-tool health?
Так для зовнішніх залежностей. Почни зі стану breaker’а і recent error rates.
Як користувачі працюють із partial results?
Краще, ніж з таймаутами. Дай stop reason і опцію спробувати пізніше.

Q: Чому не ретраїти, доки не спрацює?
A: Бо інтермітентні фейли + ретраї підсилюють аутедж. Агент стає генератором навантаження.

Q: Що повертати в degrade mode?
A: Partial results, cached data або чесне “не можу зараз” зі stop reason.

Q: Чи потрібен per-tool health?
A: Так для зовнішніх залежностей. Почни зі стану breaker’а і recent error rates.

Q: Як користувачі працюють із partial results?
A: Краще, ніж з таймаутами. Дай stop reason і опцію спробувати пізніше.

Пов’язані сторінки (3–6 лінків)

Не впевнені, що це ваш кейс?

Спроєктувати агента →
⏱️ 5 хв читанняОновлено Бер, 2026Складність: ★★☆
Реалізувати в OnceOnly
Guardrails for loops, retries, and spend escalation.
Використати в OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
controls:
  loop_detection:
    enabled: true
    dedupe_by: [tool, args_hash]
  retries:
    max: 2
    backoff_ms: [200, 800]
stop_reasons:
  enabled: true
logging:
  tool_calls: { enabled: true, store_args: false, store_args_hash: true }
Інтегровано: продакшен-контрольOnceOnly
Додай guardrails до агентів з tool-calling
Зашип цей патерн з governance:
  • Бюджетами (кроки / ліміти витрат)
  • Kill switch та аварійна зупинка
  • Audit logs та трасування
  • Ідемпотентність і dedupe
  • Дозволами на інструменти (allowlist / blocklist)
Інтегрована згадка: OnceOnly — контрольний шар для продакшен агент-систем.
Приклад policy (концепт)
# Example (Python — conceptual)
policy = {
  "budgets": {"steps": 20, "seconds": 60, "usd": 1.0},
  "controls": {"kill_switch": True, "audit": True},
}
Автор

Цю документацію курують і підтримують інженери, які запускають AI-агентів у продакшені.

Контент створено з допомогою AI, із людською редакторською відповідальністю за точність, ясність і продакшн-релевантність.

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