Files
wiki/tasks/multi-agent/proposal_v1/08_interaction_protocol.md
2026-05-15 00:50:01 +03:00

22 KiB
Raw Blame History

08. Протокол взаимодействия агентов

Назначение: строго описать, как агенты «общаются» — через какие артефакты, события, сообщения. Кто, кого и когда вызывает. Что происходит, если агент сломался / не уверен / превысил бюджет.


Простым языком

Агенты не разговаривают напрямую. Они общаются как сотрудники почтового отделения: пишут письма (артефакты в Git), кладут их в нужный ящик (папку, статус, лейбл), а почтальон (Orchestrator) разносит. Агент никогда не «помнит» предыдущий разговор — каждый раз он читает контекст с нуля из Git.

Преимущество: процесс детерминирован. Любое состояние можно воспроизвести. Любое действие отслеживается. Никакой «таинственной памяти» агента, в которую нельзя заглянуть.


Принципы протокола

  1. Артефакт — единственный язык. Всё, что нужно следующему агенту, лежит в файле в Git. Никакого state-вне-Git.
  2. Orchestrator — единственный диспетчер. Только он решает, какого агента запустить, когда и с каким контекстом.
  3. Каждое действие — событие. Изменения статуса в Plane, push в Git, лейбл на PR — всё генерирует событие.
  4. События обрабатываются идемпотентно. Повторный запуск того же события приводит к тому же результату (никаких «дубликатов комментариев»).
  5. Агент знает только свой scope. Analyst не знает, как deployer разворачивает; reviewer не знает, как tester настраивает Playwright. Минимизация контекста = удешевление и устойчивость.
  6. Эскалация явная. Если агент не уверен — он останавливается с конкретным вопросом, не «угадывает».

Карта событий и реакций

События Plane → Orchestrator

Событие Условие Реакция
work_item.created (type=Feature) новый Feature QG-0 → init: ветка + 7 подзадач + папка docs/work-items/<id>/ + 00-business-request.md → запустить analyst
comment.created с :approved: автор имеет роль Stakeholder определить, на каком этапе → проверить QG → запустить следующего агента
comment.created с :rejected: автор Stakeholder поставить статус подзадачи в blocked, лейбл back-to:<previous-stage>
comment.created с :break-glass: автор Owner, на Work Item типа qg-override разрешить override; залогировать
comment.created с :final-approved: на Work Item после деплоя в prom закрыть Work Item
work_item.updated (status: To Do → In Progress) подзадача стартовала если ещё не запущен — запустить соответствующего агента
work_item.updated (status: blocked) задача заблокирована проверить, есть ли причина (комментарий); пинг ответственному
work_item.updated (priority: urgent) поднят приоритет ускорить очередь агентов для этой задачи

События Forge → Orchestrator

Событие Условие Реакция
pull_request.opened новый PR связать с Plane (по <plane-id> в имени ветки), записать repo_pr field, поставить stage:dev
pull_request.synchronize новый push в ветку PR пере-запустить QG-4 (CI)
check_suite.completed CI завершилось если зелёное и stage:dev → QG-4 → запустить reviewer
pull_request_review.submitted (APPROVED) Reviewer одобрил проверить, что reviewer ≠ developer → QG-5 → лейбл stage:test → запустить tester
pull_request_review.submitted (REQUEST_CHANGES) request-changes лейбл back-to:dev, статус подзадачи «Разработка» → in_progress, запустить developer
pull_request.merged PR замержен запустить deployer (deploy-test)
release.published (tag v*) Создан тег запустить deploy-test workflow

События CI → Orchestrator

Событие Реакция
qg-1: green (spec-lint, req-coverage, approved) подзадача «Анализ» → done, лейбл PR stage:arch, запустить architect
qg-2: green подзадача «Архитектура» → done, лейбл stage:design или stage:dev (зависит от ui_affected)
qg-3: green подзадача «Дизайн» → done, лейбл stage:dev, запустить developer
qg-4: green подзадача «Разработка» → done, лейбл stage:review, запустить reviewer
qg-5: green подзадача «Code Review» → done, лейбл stage:test, запустить tester
qg-6: green подзадача «Тестирование» → done, лейбл stage:ready-to-deploy, запустить deployer
qg-7: green (deploy in test smoke) статус подзадачи «Внедрение» → awaiting-prom-approval, прокомментировать «прошу :approved: для prom»
qg-final: green подзадача «Внедрение» → done, Work Item → Done
qg-N: red статус подзадачи → blocked, комментарий с конкретной причиной (текст QG-проверки), пинг владельцу подзадачи

Hand-off между агентами

Hand-off — момент передачи задачи от одного агента другому. Всегда происходит через Orchestrator, не напрямую.

Шаги hand-off

  1. Агент A заканчивает работу: создаёт/обновляет артефакты, делает commit и push, ставит статус подзадачи в done через MCP.
  2. CI запускает QG для этого этапа.
  3. QG: green → Orchestrator получает webhook → меняет лейбл, обновляет статус следующей подзадачи на in_progress, запускает следующего агента.
  4. QG: red → Orchestrator ставит подзадачу A в blocked, оставляет комментарий с конкретной причиной (output линтера/теста), агент A может попробовать снова.
  5. Агент B запускается с чистого листа — единственный контекст, который у него есть, это Git и Plane через MCP. Никакого state передачи в обход.

Контекст, который получает агент при запуске

Orchestrator формирует startup-prompt для агента:

[System prompt из .openclaw/agents/<role>.md]

[User prompt]
Work Item: <plane-id>
Project: <project-name>
Repo: <repo-url>
Branch: feature/<plane-id>-<slug>
Plane URL: <url>

Прочитай артефакты предыдущих этапов в `docs/work-items/<plane-id>/`.
Прочитай `CLAUDE.md`.
Прочитай комментарии в Plane через Plane MCP (since: <last-handoff-timestamp>).

Произведи свой артефакт согласно своей роли.

Бюджет: $<X>, max iterations: <N>.

Никакого «вот что сказал предыдущий агент» — всё через Git.


Передача замечаний (back-to)

Когда Reviewer/Tester возвращает задачу:

  1. Reviewer оставляет комментарии в PR со ссылкой на конкретные строки и правила.
  2. Reviewer пишет 12-review.md со списком findings (severity + ссылка).
  3. Reviewer ставит review-статус REQUEST_CHANGES.
  4. Orchestrator получает событие → лейбл back-to:dev, статус подзадачи «Разработка» → in_progress, запускает developer снова.
  5. Developer запускается с чистого листа, читает 12-review.md и комментарии PR (через Forge MCP), правит, делает commit, push.
  6. Цикл повторяется до approve.

Лимит итераций: ≤3 цикла back-to-dev на задачу. После 3-го — лейбл escalation:human-needed, статус blocked, ожидание человека.


Эскалация

Эскалация — явный сигнал «дальше без человека нельзя». Поводы:

  • Превышен бюджет токенов (hard_kill_at_usd).
  • Агент 3-й раз возвращается на предыдущий этап.
  • Стейкхолдер не отвечает ≥48 часов.
  • Conflict между ТЗ и ADR/архитектурой, который нельзя решить в рамках задачи.
  • Найдена security-уязвимость уровня critical.
  • Падение деплоя в prom (всегда эскалация).
  • Override QG.

Процедура эскалации

  1. Агент ставит лейбл escalation:<reason> на Work Item.
  2. Статус подзадачи → blocked.
  3. Комментарий с описанием проблемы (формат: «Что произошло / что я попробовал / что нужно от человека»).
  4. Plane уведомляет всех с ролью Owner (через notifications или mention).
  5. Человек разрешает: либо снимает блок (комментарий + reaction :unblock:), либо закрывает Work Item с :wont-fix: или :duplicate:.

Идемпотентность

Любое событие может быть доставлено более одного раза (web-hook'и не гарантируют exactly-once). Все обработчики Orchestrator — идемпотентны:

  • При создании ветки — проверка существования; если есть — пропустить.
  • При создании подзадачи — проверка external_id (хэш от plane_id + subtask_type); если есть — пропустить.
  • При запуске агента — проверка, не запущен ли уже (lock в Redis или Postgres advisory lock).
  • При комментировании — проверка через idempotency_key (хэш комментария).

Это критично для надёжности: при повторе webhook'а не должно быть «двух Analyst'ов одновременно» или «семи копий BRD».


Сериализация и параллельность

В рамках одной Work Item этапы строго последовательны. Никакого параллельного «Анализ + Архитектура».

Между разными Work Item — параллельность поощряется. Один проект может одновременно иметь:

  • 3 фичи на этапе разработки,
  • 1 на ревью,
  • 2 на тесте,
  • 1 на деплое.

Лимит параллельных задач на проект — настраивается в .openclaw/budget.yaml (max_concurrent_subtasks: 5 по умолчанию). Чтобы не перегружать LLM-API и не плодить flaky preview-окружения.


Контракт MCP-tools (нормативный)

Все агенты используют один и тот же набор MCP-серверов. Нормативный список:

plane (тонкая обёртка над Plane REST API)

Tool Аргументы Возвращает
plane_get_work_item id объект Work Item с подзадачами и комментариями
plane_search_items query, project?, labels? массив Work Item (минимальная информация)
plane_update_status id, status (backlog|to_do|in_progress|blocked|in_review|done|cancelled) { ok: true }
plane_set_label id, label, op (add|remove) { ok: true }
plane_add_comment id, body (markdown) { comment_id }
plane_get_comments id, since? (ISO timestamp) массив комментариев
plane_check_reaction comment_id, emoji, min_role? { found, by, at } или { found: false }
plane_create_issue parent_id?, type, title, body, labels[] { id, url }
plane_link_pr id, pr_url { ok: true }
plane_set_custom_field id, field, value { ok: true }

forge (GitHub/Gitea, через стандартный MCP)

Стандартный набор: create_pull_request, create_or_update_file, get_file_contents, list_pull_requests, pulls.create_review, pulls.list_review_comments, issues.create, tags.create, etc.

playwright (тестер)

playwright_run_spec, playwright_screenshot, playwright_compare_visual.

filesystem (встроенный в Claude Code)

Read, Write, Edit, Glob, Grep.

bash (встроенный)

Команды строго ограничены allowlist'ом (см. .openclaw/permissions.yaml):

  • read-only: git status, git log, git diff, ls, find, grep (через CLI), cat запрещён (использовать Read tool)
  • read-write для соотв. ролей: make *, pytest, playwright test, npm install, pip install --user, docker compose up/down, git commit, git push
  • запрещено всем: rm -rf, chmod 777, sudo, dd, mkfs, > /etc/*

Журнал и трассировка

Orchestrator пишет в Postgres-журнал каждое действие:

Поле Описание
event_id UUID события
timestamp UTC
source plane / forge / ci / agent
event_type work_item.created, qg.passed, agent.started, ...
work_item_id Plane ID
subtask название подзадачи
agent_role если применимо
model LLM, использованная агентом
tokens_in / tokens_out счётчики
cost_usd стоимость вызова
duration_ms длительность
result ok / error / escalated
payload JSON с дополнительной информацией

Этот журнал — источник для дашбордов и для разбора инцидентов («почему задача застряла на этапе X?»).


Сценарий «happy path» end-to-end

Иллюстративный пример: фича PROJ-123 «Добавить визуальную зону частоты полётов на карту» в проекте fr24-noisemap.

T+0:00  Stakeholder создаёт Work Item в Plane:
        title="Add noise zones layer to map"
        description="Хочу видеть на карте зоны с разной частотой полётов..."

T+0:01  Plane webhook → Orchestrator:
        - QG-0 ✅
        - git: создана ветка feature/PROJ-123-add-noise-zones-layer
        - git: создан docs/work-items/PROJ-123/00-business-request.md
        - plane: созданы 7 подзадач (Анализ, Архитектура, Дизайн, Разработка, Review, Тест, Внедрение)
        - запущен agent:analyst

T+0:08  Analyst:
        - прочёл BR, CLAUDE.md, текущую архитектуру
        - сформулировал 3 уточняющих вопроса (по unit'ам частоты, по диапазону цветов, по mobile)
        - создал docs/work-items/PROJ-123/01-questions.md
        - подзадача "Анализ" → needs-clarification

T+8:00  Stakeholder ответил в Plane на вопросы.

T+8:05  Analyst подхватил ответы, написал BRD/ТЗ/AC/TestPlan.
        Подзадача "Анализ" → in_review.
        Комментарий: "BRD/ТЗ готовы, прошу :approved:".

T+9:00  Stakeholder поставил :approved:.

T+9:01  Orchestrator: QG-1 ✅. Подзадача "Анализ" → done.
        Запущен agent:architect.

T+9:30  Architect:
        - прочёл ТЗ, текущую архитектуру
        - решение: использовать существующий Mapbox-tile-builder, добавить новый layer
        - создал ADR-0034
        - обновил c4-component.mmd
        - создал 07/08/09/10-*.md (UI-требования есть)
        Подзадача "Архитектура" → done.

T+9:31  Orchestrator: QG-2 ✅. ui_affected=true, запущен agent:designer.

T+11:00 Designer:
        - сделал wireframes (mermaid), mockups (PNG в Figma → экспорт)
        - описал states (loading/empty/error)
        - заполнил a11y
        Подзадача "Дизайн" → in_review.
        Stakeholder проверил, поставил :approved:.

T+11:30 Orchestrator: QG-3 ✅. Запущен agent:developer.

T+14:00 Developer:
        - реализовал layer (frontend) + endpoint /api/noise-zones (backend)
        - написал unit-тесты, integration, e2e
        - обновил OpenAPI, CHANGELOG, CLAUDE.md
        - открыл PR #142 с лейблом stage:dev
        - CI зелёный
        Подзадача "Разработка" → done.

T+14:05 Orchestrator: QG-4 ✅. Запущен agent:reviewer.

T+14:25 Reviewer:
        - сравнил с ТЗ → все REQ покрыты
        - сравнил с ADR → используется правильный builder
        - нашёл 1 P2-finding (мелкий комментарий о naming)
        - approved
        - 12-review.md записан
        Подзадача "Code Review" → done.

T+14:26 Orchestrator: QG-5 ✅. Лейбл stage:test. Запущен agent:tester.

T+14:30 CI поднял preview-окружение.

T+14:55 Tester:
        - все TC из 04-test-plan.yaml выполнены
        - e2e зелёные
        - visual regression: 0 diffs
        - a11y: 0 violations
        - perf: p95=180ms (порог 500ms)
        - 13-test-report.md готов
        Подзадача "Тестирование" → done.
        Лейбл stage:ready-to-deploy.

T+15:00 Orchestrator: QG-6 ✅. Запущен agent:deployer.

T+15:01 Deployer: merge PR в main → tag v1.4.0 → deploy в test.
T+15:05 Smoke-test на test ✅.
        Подзадача "Внедрение" → awaiting-prom-approval.
        Комментарий: "test зелёный, прошу :approved: для prom".

T+15:30 Stakeholder: :approved:.

T+15:31 Deployer: deploy в prom.
T+15:33 Smoke на prom ✅. Метрики ok.
        14-deploy-log.md записан, CHANGELOG обновлён.
        Комментарий: "prom стабилен, прошу :final-approved:".

T+16:00 Stakeholder: :final-approved:.

T+16:01 Orchestrator: QG-final ✅. Work Item → Done.

Total: 16 часов. Стоимость LLM ≈ $8.

Сценарий «обратной волны» (back-to)

Та же фича, но Reviewer нашёл P0:

T+14:25 Reviewer: REQUEST_CHANGES, P0 finding:
        "REQ-F-2 не реализовано: фильтр по времени не работает".

T+14:26 Orchestrator: лейбл back-to:dev. Подзадача "Разработка" → in_progress.
        Запущен agent:developer (вторая итерация).

T+14:50 Developer:
        - прочёл 12-review.md и комментарии PR
        - реализовал недостающее
        - push
        - CI зелёный

T+14:55 Orchestrator: QG-4 ✅. Запущен agent:reviewer (вторая итерация).

T+15:10 Reviewer: approved (проблема устранена). 12-review.md обновлён.

... дальше как обычно

При 3-й итерации back-to-dev — escalation:human-needed.


Ограничения и предположения

  • Plane self-hosted доступен для webhook'ов.
  • Forge поддерживает webhooks и API (GitHub Actions / Gitea Actions).
  • Anthropic API доступно для агентов; в случае использования локальных моделей (Qwen, GLM) — через Ollama / vLLM, формат запроса унифицирован.
  • Orchestrator имеет stable URL и сертификат для приёма webhook'ов.
  • Postgres для журнала Orchestrator (можно использовать тот же Postgres, что у Plane, в отдельной схеме).