22 KiB
08. Протокол взаимодействия агентов
Назначение: строго описать, как агенты «общаются» — через какие артефакты, события, сообщения. Кто, кого и когда вызывает. Что происходит, если агент сломался / не уверен / превысил бюджет.
Простым языком
Агенты не разговаривают напрямую. Они общаются как сотрудники почтового отделения: пишут письма (артефакты в Git), кладут их в нужный ящик (папку, статус, лейбл), а почтальон (Orchestrator) разносит. Агент никогда не «помнит» предыдущий разговор — каждый раз он читает контекст с нуля из Git.
Преимущество: процесс детерминирован. Любое состояние можно воспроизвести. Любое действие отслеживается. Никакой «таинственной памяти» агента, в которую нельзя заглянуть.
Принципы протокола
- Артефакт — единственный язык. Всё, что нужно следующему агенту, лежит в файле в Git. Никакого state-вне-Git.
- Orchestrator — единственный диспетчер. Только он решает, какого агента запустить, когда и с каким контекстом.
- Каждое действие — событие. Изменения статуса в Plane, push в Git, лейбл на PR — всё генерирует событие.
- События обрабатываются идемпотентно. Повторный запуск того же события приводит к тому же результату (никаких «дубликатов комментариев»).
- Агент знает только свой scope. Analyst не знает, как deployer разворачивает; reviewer не знает, как tester настраивает Playwright. Минимизация контекста = удешевление и устойчивость.
- Эскалация явная. Если агент не уверен — он останавливается с конкретным вопросом, не «угадывает».
Карта событий и реакций
События 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
- Агент A заканчивает работу: создаёт/обновляет артефакты, делает commit и push, ставит статус подзадачи в
doneчерез MCP. - CI запускает QG для этого этапа.
- QG: green → Orchestrator получает webhook → меняет лейбл, обновляет статус следующей подзадачи на
in_progress, запускает следующего агента. - QG: red → Orchestrator ставит подзадачу A в
blocked, оставляет комментарий с конкретной причиной (output линтера/теста), агент A может попробовать снова. - Агент 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 возвращает задачу:
- Reviewer оставляет комментарии в PR со ссылкой на конкретные строки и правила.
- Reviewer пишет
12-review.mdсо списком findings (severity + ссылка). - Reviewer ставит review-статус
REQUEST_CHANGES. - Orchestrator получает событие → лейбл
back-to:dev, статус подзадачи «Разработка» →in_progress, запускаетdeveloperснова. - Developer запускается с чистого листа, читает
12-review.mdи комментарии PR (через Forge MCP), правит, делает commit, push. - Цикл повторяется до approve.
Лимит итераций: ≤3 цикла back-to-dev на задачу. После 3-го — лейбл escalation:human-needed, статус blocked, ожидание человека.
Эскалация
Эскалация — явный сигнал «дальше без человека нельзя». Поводы:
- Превышен бюджет токенов (
hard_kill_at_usd). - Агент 3-й раз возвращается на предыдущий этап.
- Стейкхолдер не отвечает ≥48 часов.
- Conflict между ТЗ и ADR/архитектурой, который нельзя решить в рамках задачи.
- Найдена security-уязвимость уровня critical.
- Падение деплоя в prom (всегда эскалация).
- Override QG.
Процедура эскалации
- Агент ставит лейбл
escalation:<reason>на Work Item. - Статус подзадачи →
blocked. - Комментарий с описанием проблемы (формат: «Что произошло / что я попробовал / что нужно от человека»).
- Plane уведомляет всех с ролью
Owner(через notifications или mention). - Человек разрешает: либо снимает блок (комментарий + 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, в отдельной схеме).