STACHKA
Ограничения AI-агентов и мультиагентных систем
Ожидания и инженерная реальность
Уроки пилота: пять агентов в Сбере
Суриков Антон · Сбер
ИНЦИДЕНТ · С ЧЕГО НАЧАЛОСЬ
Сообщение приняли. Ответ потеряли.
02
STACHKA · Суриков Антон · Сбер
Подтверждать получение можно только после того, как работа сохранена
Offset сохраняется до ответа. Если ответ не ушёл, а процесс перезапустился — Telegram этот update уже не пришлёт. «Принято» и «сделано» здесь не одно и то же.
UPDATE
HANDOFF
OFFSET
REPLY
ПРОТОТИП · ФИКС
ACK только после сохранённого результата
03
STACHKA · Суриков Антон · Сбер
Повторная доставка стала возможной — значит, нужна идемпотентность
async def on_update(update: Update) -> None: task_id = await store.register(update.update_id) # запись в БД до работы try: result = await handle(task_id, update) await store.save_result(task_id, result) await bot.reply(update, result) except Exception: await store.rollback_offset(update.update_id) # update придёт снова raise if await store.task_id_for(update.update_id) == task_id: await store.ack(update.update_id)
В БД до работы
task_id создаётся заранее
Откат offset
ошибка → update снова
Сверка
свой task_id, не чужой
ИТОГ · О ЧЁМ ДОКЛАД
LLM-агент — ненадёжный компонент
04
STACHKA · Суриков Антон · Сбер
Это доклад про уроки и поломки, а не про готовый продукт
Надёжность дают не промпты, а обычная инженерия на Python.
Метки на слайдах: ПРОТОТИП — работает · ТЗ — целевое требование · ИНЦИДЕНТ — реально ломалось. • контракт задачи · состояние в БД · идемпотентность • права вне текста · сквозные тесты
КОНТЕКСТ · СБЕР
Почему здесь вообще нужны несколько агентов
05
STACHKA · Суриков Антон · Сбер
Повод для сети агентов — не число систем, а разные владельцы и права
Владельцы
за данные и решения отвечают разные команды
Системы
HR, Finance, Docs, почта, календарь
Идентичность
одной техучётки уже мало
Время
задача живёт дольше сессии
ПРОТОТИП · УСТРОЙСТВО
Пять агентов: один руководитель и четыре блока
06
STACHKA · Суриков Антон · Сбер
Руководитель ставит поручение, блоки 1–4 держат свои данные. Шлюз A2A и хранилище взаимодействий отвечают за доставку, состояния, защиту от дублей и аудит. • ✓ Каждое сообщение идёт через шлюз • ✓ Права у каждого агента свои • ✓ Шлюз не читает текст поручения
ИНЦИДЕНТ · КАРТА СБОЕВ ПИЛОТА
Что ломалось при работе с агентами
07
STACHKA · Суриков Антон · Сбер
Проверять нужно и каждого агента, и передачу задачи между ними
ИнцидентЧто произошло
КОНТЕКСТ106 081 токенов при лимите 65 536
ВЫЗОВЫHTTP 400, XML вместо вызова
SAFETY413 / DNS названы нарушением политики
ДОСТАВКАACK и конфликт 409
РЕЗЕРВобщая квота легла одной волной
РЕЛИЗ6 из 9 тестов падали
ИНЦИДЕНТ 1 · КОНТЕКСТ
Сколько стоил один неудачный цикл
08
STACHKA · Суриков Антон · Сбер
Задаче нужны лимиты: вызовы, токены, время, повторы, глубина делегирования
Лимит 65 536 не придуман: сервер запущен с контекстом 131 072 и --parallel 2, то есть по 65 536 на слот.
МетрикаЗначение
Запрос на ревью106 081 токенов
Лимит65 536
Не помещается+40 545
Вход за неудачный цикл46 583
Ответ локальной модели3–5 минут
ИНЦИДЕНТ 2 · ВЫЗОВЫ
Вызов инструмента пришёл пользователю текстом
09
STACHKA · Суриков Антон · Сбер
LEAK = re.compile(r'<tool_call>|<function|"arguments"\s*:') def call_llm(prompt: str) -> str: for _ in range(3): out = llm(prompt) if not LEAK.search(out): return out raise ToolCallLeak(prompt)
Симптом
tool_call в тексте, иногда 400
Причина
шаблон чата не совпал с моделью
Фикс
детектор и контрактный тест
ИНЦИДЕНТ 3 · SAFETY
Сбой сети назвали нарушением политики
10
STACHKA · Суриков Антон · Сбер
class Kind(Enum): RETRYABLE = "retry" # 429, 5xx, таймаут INFRA = "infra" # DNS, 413, квота SAFETY = "safety" # только решение фильтра INVALID = "invalid" # нарушен контракт match classify(exc): case Kind.RETRYABLE: retry() case _: raise exc
Симптом
«нарушение safety» и ноль ретраев
Причина
413 и DNS в общем except Exception
Фикс
типизированные классы ошибок
ИНЦИДЕНТ 4 · ДОСТАВКА
Когда можно подтверждать обработку
11
STACHKA · Суриков Антон · Сбер
At-least-once: дубли будут, порядок не гарантирован. ACK — про сообщение, а завершение задачи — отдельный статус
• OUTBOX — состояние и событие в одной транзакции • INBOX — UNIQUE(idempotency_key), дубль вернёт прежний результат • ПОВТОРЫ — 0/5/30/120 с + jitter → DLQ
В ОЧЕРЕДИ
ДОСТАВЛЕНО
ОБРАБОТКА
РЕЗУЛЬТАТ СОХРАНЁН
ACK
ИНЦИДЕНТ 5 · РЕЗЕРВ
Общая квота легла одной волной
12
STACHKA · Суриков Антон · Сбер
Резерв, который делит квоту с основным путём, — не резерв
Запасной путь без изолированной квоты не является запасным.
Основная и резервная модели недоступны одновременно — они делят один и тот же лимит.
ИНЦИДЕНТ 6 · РЕЛИЗ
6 из 9 тестов падали, агент коммитил дальше
13
STACHKA · Суриков Антон · Сбер
Запрет коммита в красную ветку — правило CI, а не просьба в промпте
«Да, я способен переписывать свой код» — а агент ещё не проверил файлы и инструменты. • Самоотчёт модели — не доказательство • Красные тесты он не счёл причиной остановиться • Пользователь об этом вообще не узнал
ТЗ · КАРТА СИСТЕМЫ
Три слоя и кто за что отвечает
14
STACHKA · Суриков Антон · Сбер
• A2A — сообщения, идентичность, статусы • CONTROL PLANE — права, сроки, повторы, аудит • MCP — бизнес-запросы и действия
A2A
CONTROL PLANE
MCP
ТЗ · ПРОВЕРКА КАЧЕСТВА
Тестируем весь путь поручения, а не одного агента
15
STACHKA · Суриков Антон · Сбер
Один trace на всю цепочку: запрос → исполнители → действия → результат
ПроверкаЧто смотрим
Доставкарестарт без потерь, повторных действий 0
Смыслцель сохранена, пропуски видны
Права0 критичных нарушений
Маршрутпуть восстановим
Релиз3–5 прогонов, снимок skills/tools совпал
ИТОГ · ЧТО УНЕСТИ
Три вывода для Python-разработчика
16
STACHKA · Суриков Антон · Сбер
LLM-агент — ненадёжный компонент. Надёжность даёт инженерия вокруг него.
1. Состояние задачи — в БД, а не в asyncio.Task: рестарт не должен терять задачи. 2. Идемпотентность доказывает тест с падением в каждой точке: эффект → commit → ACK. 3. Права, сроки и лимиты проверяет код, а не промпт.
ТЗ · ПЕРЕД ЗАПУСКОМ
Что ещё открыто
17
STACHKA · Суриков Антон · Сбер
Слайды и код — по QR. Суриков Антон · Сбер. • контекст сотрудника и OBO — главная открытая проблема • надёжность шлюза: сейчас SQLite на одном узле • данные для проверки смысла поручения • замер успешного прогона