Паттерны мультиагентной отладки: разделение ролей между генератором, исследователем и ревьюером

Одно из самых неприятных свойств больших языковых моделей при расследовании багов — склонность к подтверждению собственной первой догадки (Confirmation Bias). Если агент в начале сессии предположил, что проблема кроется в таймауте Redis, он может потратить десять итераций на переписывание настроек пула соединений, игнорируя очевидную блокировку цикла событий в соседнем модуле.

1. Почему агент плохо проверяет собственный код

Когда вы просите ту же самую сессию агента, которая только что написала 400 строк кода, «проверить себя на баги», в её контекстном окне уже лежат все промежуточные рассуждения, оправдывающие принятые решения. Чтобы ревью было объективным, проверяющий агент должен видеть только итоговый git diff и спецификацию требований — без истории того, как этот код рождался.

2. Архитектурный паттерн «Инженерная триада»

На сложных задачах в Треке 03 мы разделяем работу между тремя специализированными ролями субагентов:

  • Researcher (Read-Only разведчик): не имеет прав на запись файлов. Его задача — быстро просканировать кодовую базу, граф вызовов и логи, вернув главному агенту точные ссылки на файлы и строки (file.py#L40-L75);
  • Builder (Исполнитель): получает компактный отчёт от разведчика, вносит точечные изменения в код и прогоняет локальный тест-раннер;
  • Adversarial Auditor (Критик-аудитор): запускается в отдельном чистом контексте, читает только git diff и ищет способы сломать решение (граничные значения, пустые списки, отмена корутин).
Экономия контекста: вынос этапа поиска по репозиторию в отдельного Read-Only субагента сокращает расход токенов основной сессии в 3–4 раза и сохраняет высокую скорость отклика до самого конца задачи.

3. Протокол отладки по методу научных гипотез

При расследовании плавающих багов в продакшене мы запрещаем агентам вносить правки «наугад». Сначала формулируются 3 конкурирующие гипотезы и пишется минимальный воспроизводящий тест:

tests/integration/test_webhook_concurrency.py
import asyncio
import pytest
from httpx import AsyncClient

@pytest.mark.asyncio
async def test_duplicate_webhook_delivery_is_idempotent(
    api_client: AsyncClient,
    signed_webhook_payload: dict[str, object],
) -> None:
    # Отправляем 10 идентичных вебхуков одновременно для проверки гонки
    tasks = [
        api_client.post("/api/v1/webhooks/payment", json=signed_webhook_payload)
        for _ in range(10)
    ]
    responses = await asyncio.gather(*tasks)
    status_codes = [r.status_code for r in responses]

    # Все ответы должны завершиться корректно, но начисление происходит ровно 1 раз
    assert all(code == 200 for code in status_codes)

4. Параллельные ветки и Git Worktree

Когда перед архитектором стоит выбор между двумя нетривиальными подходами к рефакторингу, лучшая стратегия — запустить двух независимых субагентов в изолированных рабочих деревьях (git worktree), после чего сравнить объём диффа, результаты бенчмарков и чистоту прохождения mypy --strict.


Читайте также в инженерном блоге