
На третий день мой агент случайно раскрыл email одного клиента в переписке с другим. Это не была гипотетическая история из доклада на конференции. Это был мой код, работающий в продуктиве, выполняющий то, что я никогда не тестировал.
Я собрал support-агента на базе LangGraph и GPT-4o. Он мог искать по базе знаний, извлекать детали аккаунта и готовить ответы. В staging он работал безупречно. В продуктиве ему потребовалось ровно 72 часа, чтобы извлечь PII одного пользователя в разговор с другим. Причина оказалась до неловкости простой: модель вставила необработанный контекст из базы данных прямо в ответ, и ничто в моем пайплайне это не проверяло.
После инцидента исправление было очевидным. Фреймворки для AI-агентов предоставляют оркестрацию, вызов инструментов и память. Они не предоставляют безопасность. Это уже ваша ответственность.
Почему ваш фреймворк не включает guardrails
LangChain, CrewAI, LangGraph, Agents SDK от OpenAI. Выберите любой. Ни один из них не поставляется из коробки с валидацией входа, фильтрацией выхода или контролем расходов. Они предполагают, что вы добавите это самостоятельно.
Большинство команд так и не добавляют.
Почему это важно — объясняет простая арифметика. При точности 90% на шаг, агентный workflow из 5 шагов успешен в 59% случаев. Workflow из 10 шагов падает до 35%. На 20 шагах вы находитесь на уровне 12%. Каждый незащищенный шаг — это умножение вашей вероятности отказа.
Guardrails не исправляют точность. Они ограничивают радиус поражения, когда точность отказывает. Разница между «агент дал неправильный ответ» и «агент дал неправильный ответ, в который попал чей-то номер социального страхования» — это один output-валидатор.
Следующие две недели я строил стек guardrails. Код ниже — это то, к чему я в итоге пришел.
Четыре guardrail, нужные каждому агенту
Каждому агенту нужна защита в четырех точках:
Input guardrails ловят prompt injection и очищают чувствительные данные до того, как их увидит LLM.
Input guardrails ловят prompt injection и очищают чувствительные данные до того, как их увидит LLM.
Output guardrails валидируют ответы до того, как их увидят пользователи, блокируя галлюцинации и утекший контекст.
Output guardrails валидируют ответы до того, как их увидят пользователи, блокируя галлюцинации и утекший контекст.
Cost circuit breakers не дают счету за API улететь в космос из-за зацикливания или неожиданно длинных разговоров.
Cost circuit breakers не дают счету за API улететь в космос из-за зацикливания или неожиданно длинных разговоров.
Tool call validators подтверждают, что агент вызывает только разрешенные инструменты с параметрами, проходящими проверку схемы.
Tool call validators подтверждают, что агент вызывает только разрешенные инструменты с параметрами, проходящими проверку схемы.
Все четыре умещаются менее чем в 200 строк Python. Накладные расходы по latency — 10–50 мс на слой. Альтернатива — узнавать о сбоях от своих клиентов.
Input guardrails: ловим плохие промпты до выполнения
Ваш input-валидатор работает до того, как LLM вообще увидит промпт. Он делает две вещи: блокирует попытки инъекции и редактирует PII.
Это не пуленепробиваемо. Настойчивый атакующий обойдет основанное на regex обнаружение инъекций. Но оно ловит частые попытки, которые по моему опыту составляют около 80% реальных атак. Для продакшена с чувствительными данными добавьте поверх regex-прохода модель-классификатор (вроде Lakera Guard или дообученного DistilBERT).
Очистка PII — именно та часть, что спасла бы меня на третий день. Если бы мой input guardrail вырезал email из контекста базы данных до того, как он попал в разговор, утечки бы не случилось.
Output guardrails: останавливаем галлюцинации до пользователя
Валидация выхода — то место, где большинство команд пропускают guardrails вовсе. Модель дала ответ, выглядит разумно, отправляем. Но «выглядит разумно» — это не тот стандарт, на который можно полагаться.
Pydantic-модель делает двойную работу. Она навязывает структуру (LLM обязана вернуть JSON с answer, confidence и sources) и прогоняет контентные проверки (никакого PII в выходе). Когда валидация падает, мой агент повторяет запрос с дополнительным контекстом: «Ваш предыдущий ответ отклонен, потому что [причина]. Попробуйте снова».
Два повтора со все более конкретными инструкциями исправляют большинство падений валидации. Если падает три раза — я возвращаю заготовленный fallback-ответ и логирую инцидент.
Kill switch: circuit breaker по расходам и токенам
Это тот guardrail, про который никто не пишет, и именно он стоил мне $400.
У меня был баг, из-за которого агент входил в цикл повторов. Вызов инструмента падал, агент повторял, повтор падал чуть иначе, и агент повторял снова. Каждый повтор сжигал токены на шаге рассуждения плюс на вызове инструмента. Он крутился шесть часов ночью, прежде чем я заметил.
Per-request лимит ловит очевидный случай: одно гигантское контекстное окно, которое прожжет ваш бюджет. Session-лимит ограничивает суммарные траты на один разговор. Rate-лимит предотвращает шторм повторов. А дневной лимит трат — ваш абсолютный потолок.
Я выставил дневной лимит в $50. Если упираюсь в него — система перестает звать API и возвращает ответ «сервис временно недоступен». Я лучше получу простой, чем счет-сюрприз.
Валидация вызова инструментов: агент не должен звать то, что вы не одобрили
Когда у агента есть доступ к базе данных, файловой системе или внешнему API, валидация вызовов инструментов — не опция. Без нее взломанный или запутавшийся агент может выполнить разрушительную операцию.
Я выбрал default deny. Если инструмента нет в allowlist — агент не может его вызвать. Если параметра нет в списке разрешенных для этого инструмента — вызов отклоняется. Это ловит и попытки джейлбрейка (когда модель пытается вызвать execute_sql или delete_record), и галлюцинированные имена инструментов (что случается чаще, чем вы думаете).
Лимиты частоты вызовов держу отдельно по каждому инструменту, потому что у разных инструментов разный профиль риска. Поиск — дешево и безопасно вызвать 20 раз. Обновление аккаунта должно происходить максимум раз-два за разговор.
Собираем вместе: продакшен-стек guardrails
Вот как эти четыре части соединяются в реальном пайплайне агента:
Каждый guardrail логирует свои решения. На каждый отказ — структурированная запись с причиной, входом, который его вызвал, и таймстампом. Раз в неделю я гоняю запрос по этим логам в поисках паттернов: если одна и та же попытка инъекции встречается 50 раз — это, вероятно, автоматизированная атака. Если output guardrail отклоняет 15% ответов за низкую уверенность — это проблема качества retrieval, которую надо чинить выше по пайплайну.
Суммарный overhead по latency для всех четырех guardrails — менее 40 мс (без учета ML-классификаторов). Для пользователя это невидимо.
Что бы я сделал иначе
Если бы начинал заново, я бы добавил guardrails до написания первой строки логики агента. Описанный здесь каркас — примерно 200 строк Python. Две недели у меня ушло только потому, что я встраивал его в уже существующую систему и параллельно разбирал инцидент с PII.
Мой прогноз: в течение 12 месяцев крупные агентные фреймворки начнут поставлять guardrails как first-class функцию. LangGraph уже движется в эту сторону со своим механизмом interrupt. А пока — вы сами по себе.
Начните с input guardrails и cost circuit breaker. Только эти два предотвратили бы оба моих инцидента (утечку PII и ночной счет на $400). Добавьте валидацию выхода, когда появится продакшен-трафик, чтобы настроить пороги уверенности. Добавьте валидацию вызова инструментов, если у агента есть доступ на запись хоть к чему-нибудь.
Guardrails не сделают вашего агента умнее. Но они не дадут глупым моментам превратиться в инциденты.