Паттерны мультиагентной отладки: разделение ролей между генератором, исследователем и ревьюером
Одно из самых неприятных свойств больших языковых моделей при расследовании багов — склонность к подтверждению собственной первой догадки (Confirmation Bias). Если агент в начале сессии предположил, что проблема кроется в таймауте Redis, он может потратить десять итераций на переписывание настроек пула соединений, игнорируя очевидную блокировку цикла событий в соседнем модуле.
1. Почему агент плохо проверяет собственный код
Когда вы просите ту же самую сессию агента, которая только что написала 400 строк кода, «проверить себя на баги», в её контекстном окне уже лежат все промежуточные рассуждения, оправдывающие принятые решения. Чтобы ревью было объективным, проверяющий агент должен видеть только итоговый git diff и спецификацию требований — без истории того, как этот код рождался.
2. Архитектурный паттерн «Инженерная триада»
На сложных задачах в Треке 03 мы разделяем работу между тремя специализированными ролями субагентов:
- Researcher (Read-Only разведчик): не имеет прав на запись файлов. Его задача — быстро просканировать кодовую базу, граф вызовов и логи, вернув главному агенту точные ссылки на файлы и строки (
file.py#L40-L75); - Builder (Исполнитель): получает компактный отчёт от разведчика, вносит точечные изменения в код и прогоняет локальный тест-раннер;
- Adversarial Auditor (Критик-аудитор): запускается в отдельном чистом контексте, читает только
git diffи ищет способы сломать решение (граничные значения, пустые списки, отмена корутин).
3. Протокол отладки по методу научных гипотез
При расследовании плавающих багов в продакшене мы запрещаем агентам вносить правки «наугад». Сначала формулируются 3 конкурирующие гипотезы и пишется минимальный воспроизводящий тест:
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.