Пошкодження відповіді: коли результати агента ламаються

Response corruption виникає, коли вихід агента стає неповним, пошкодженим або логічно зламаним між кроками. Чому це трапляється у production.
На цій сторінці
  1. Проблема
  2. Чому це стається
  3. Які збої трапляються найчастіше
  4. Успішний 2xx, але зламаний payload
  5. Частковий або обрізаний JSON (Partial payload)
  6. Тихий schema drift
  7. Логічна корупція після parse (Semantic corruption)
  8. Як виявляти ці проблеми
  9. Як відрізнити response corruption від просто tool failure
  10. Як зупиняти такі збої
  11. Де це реалізується в архітектурі
  12. Самоперевірка
  13. FAQ
  14. Пов'язані сторінки

Проблема

Запит виглядає простим: оновити CRM-поле після перевірки профілю користувача.

У трейсах видно інше: за 11 хвилин один run зробив 18 кроків, отримав 6 відповідей 200 OK, але 4 з них мали пошкоджений payload (HTML замість JSON, обрізаний body або невалідні поля).

Сервіс формально "живий": timeout немає, статуси ніби успішні. Але агент приймає рішення на зламаних даних і робить хибні дії.

Система не падає.

Вона просто тихо псує результат під виглядом "успішної" відповіді.

Аналогія: уяви касира, який сканує пошкоджений штрихкод. Каса не вимикається, але в чек потрапляє не той товар. Response corruption в агентних системах виглядає так само: процес іде далі, але дані вже зламані.

Чому це стається

Response corruption зазвичай виникає не через один збій API, а через слабкий контроль якості tool output у runtime.

LLM має сильний bias до "завершеної" відповіді. Тому без schema-гейтів агент частіше продовжує run на "майже валідних" даних, ніж зупиняється з помилкою.

У production зазвичай так:

  1. інструмент повертає формально успішний статус, але пошкоджений body;
  2. runtime перевіряє лише status code і пропускає payload далі;
  3. schema/invariant checks відсутні або надто м'які;
  4. агент інтерпретує "майже валідні" дані як реальні факти;
  5. без fail-closed поламані дані доходять до write-дій.

У trace це видно як ріст tool_output_invalid_rate при одночасно високому tool_2xx_rate.

Проблема не в одному битому JSON.

Runtime не відсікає пошкоджений tool output до того, як він стає рішенням або write-дією.

Які збої трапляються найчастіше

У production найчастіше видно чотири патерни response corruption.

Успішний 2xx, але зламаний payload

Інструмент повертає 200, але body не відповідає контракту.

Типова причина: контроль побудований лише на HTTP-статусі.

Частковий або обрізаний JSON (Partial payload)

Відповідь приходить не повністю: бракує полів або JSON обривається в середині.

Типова причина: немає strict parse і size/content-type gate.

Тихий schema drift

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

Типова причина: немає версіонування schema і контролю обов'язкових полів.

Логічна корупція після parse (Semantic corruption)

JSON формально валідний, але значення суперечать інваріантам (currency="USD" + amount=-15, status="active" + deleted_at заповнений, status="paid" + paid_at=null).

Типова причина: перевіряють синтаксис, але не бізнес-інваріанти.

Як виявляти ці проблеми

Response corruption добре видно по комбінації data-quality і runtime-метрик.

МетрикаСигнал response corruptionЩо робити
tool_output_invalid_rateросте частка невалідних payloadввести strict parse + schema/invariant gate
tool_2xx_with_invalid_payload_rateбагато 2xx, але payload не проходить перевіркуне довіряти лише status code, перевіряти content-type і schema
schema_mismatch_rateчасті невідповідності контрактуверсіонувати schema і блокувати невідомі формати
write_blocked_by_validation_rateчасто блокується write після валідаціїперевірити залежність, ввімкнути degraded mode
recovery_fallback_rateчасті fallback через неякісні даніоновити контракт інструмента і runbook відновлення

Як відрізнити response corruption від просто tool failure

Не кожен збій інструмента означає пошкодження даних. Ключове питання: чи проблема в доступності інструмента, чи в якості payload.

Нормально для tool failure, якщо:

  • інструмент повертає 5xx/timeout, і виклик навіть не доходить до даних;
  • причина зупинки виглядає як tool_timeout або tool_unavailable;
  • після retry той самий запит дає валідний payload.

Небезпечно для response corruption, якщо:

  • багато 2xx, але payload не проходить parse/schema/invariant checks;
  • агент продовжує run на "майже валідних" даних;
  • інцидент проявляється як хибні бізнес-дії, а не як явна помилка API.

Як зупиняти такі збої

Практично це виглядає так:

  1. ставиш size і content-type gate до будь-якого parse;
  2. робиш strict parse без "best effort" відновлення JSON;
  3. перевіряєш schema й бізнес-інваріанти перед використанням даних;
  4. при порушенні повертаєш stop reason і блокуєш write-дії.

Мінімальний guard для перевірки tool output:

PYTHON
import json
from dataclasses import dataclass
from typing import Any


@dataclass(frozen=True)
class OutputLimits:
    max_chars: int = 200_000
    required_content_type: str = "application/json"


def parse_json_strict(raw: str, max_chars: int) -> Any:
    if len(raw) > max_chars:
        raise ValueError("output_too_large")
    return json.loads(raw)


def validate_profile(obj: Any) -> None:
    if not isinstance(obj, dict):
        raise ValueError("schema:expected_object")
    if not isinstance(obj.get("user_id"), str):
        raise ValueError("schema:user_id_missing")
    if obj.get("plan") not in {"free", "pro", "enterprise"}:
        raise ValueError("schema:plan_invalid")
    if obj.get("quota", 0) < 0:
        raise ValueError("invariant:quota_negative")


def verify_tool_output(raw: str, content_type: str, limits: OutputLimits = OutputLimits()) -> str | None:
    if content_type != limits.required_content_type:
        return "response_corruption:content_type_mismatch"

    try:
        obj = parse_json_strict(raw, limits.max_chars)
        validate_profile(obj)
    except json.JSONDecodeError:
        return "response_corruption:invalid_json"
    except ValueError as e:
        return f"response_corruption:{e}"

    return None

Це базовий guard. У production його зазвичай доповнюють schema versioning, пер-інструментними validators і quarantine-потоком для підозрілих payload. verify_tool_output(...) викликають до будь-якої write-дії, щоб пошкоджені дані не потрапляли у зовнішні системи.

Де це реалізується в архітектурі

У production контроль response corruption майже завжди розкладений між трьома шарами системи.

Tool Execution Layer відповідає за першу лінію: content-type, size limits, strict parse, schema checks і версіонування контрактів. Саме тут формується якісна межа між "дані валідні" і "дані пошкоджені".

Agent Runtime приймає рішення, що робити далі: stop reasons, fail-closed, fallback і блокування write-дій. Якщо runtime не дисциплінований, пошкоджений payload швидко стає бізнес-інцидентом.

Policy Boundaries задає, коли система має завершувати run fail-closed і які дії заборонені при невалідних даних. Це критично для будь-яких write-інструментів.

Самоперевірка

Швидка перевірка перед релізом. Відмічайте пункти і дивіться статус нижче.
Це короткий sanity-check, а не формальний аудит.

Прогрес: 0/8

⚠ Є сигнали ризику

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

FAQ

Q: Якщо інструмент внутрішній, можна послабити перевірки?
A: Ні. Внутрішні інструменти теж дрейфують і повертають пошкоджений payload. Валідація потрібна так само, як і для зовнішніх API.

Q: Чому не дати моделі "виправити" невалідний JSON?
A: Бо модель не відновлює істину — вона лише генерує правдоподібну версію пошкоджених даних. Для write-сценаріїв безпечніше зупинити run і повернути явний stop reason.

Q: Чи обов'язкова повна JSON schema бібліотека з першого дня?
A: Ні. Почни з strict parse і критичних інваріантів, а schema-level покриття нарощуй у зонах з великим blast radius.

Q: Що показувати користувачу, коли payload невалідний?
A: Причину зупинки, що вже перевірили, і безпечний наступний крок: часткова відповідь або повторний запуск після відновлення залежності.


Response corruption майже ніколи не виглядає як гучна аварія. Це тиха деградація якості даних, яка непомітно ламає рішення агента. Тому production-агентам потрібні не лише доступні tools, а й жорстка валідація їхніх відповідей.

Пов'язані сторінки

Якщо ця проблема виникла у production, корисно також подивитися:

  • Чому AI агенти ламаються — загальна карта збоїв у production.
  • Tool failure — як відмови інструментів відрізняються від корупції payload.
  • Hallucinated sources — як невалідні дані перетворюються на недовірені посилання.
  • Prompt injection — чому untrusted tool output не можна трактувати як інструкції.
  • Agent Runtime — де ставити stop reasons, fail-closed і fallback.
  • Tool Execution Layer — де робити parse/schema/invariant validation.
⏱️ 6 хв читанняОновлено 12 березня 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-агентів.

Фокус: патерни агентів, режими відмов, контроль рантайму та надійність систем.

🔗 GitHub: https://github.com/mykolademyanov


Редакційна примітка

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

Контент базується на реальних відмовах, постмортемах та операційних інцидентах у розгорнутих AI-агентних системах.