← Multi-agent orchestration: LangGraph, CrewAI, and agent teams
04 / 07 · 55 min

Hand-offs, shared memory, and conflicts

Control transfer via Command and parallelism via Send, blackboard and message-passing shared-memory patterns, and conflict resolution through reducers and an arbiter.

Lesson content is currently available in Russian.

What you will learn

  • →Реализовывать hand-off между агентами через Command с передачей структурированного контекста.
  • →Строить map-reduce поверх Send и сливать параллельные результаты редьюсерами.
  • →Выбирать схему общей памяти и разрешать конфликты агентов арбитражем, а не молчаливой перезаписью.

Hand-off через Command

Hand-off — это передача управления от одного агента другому вместе с нужным контекстом. В LangGraph узел может вернуть не просто обновление состояния, а объект Command, который совмещает обновление и переход: «запиши это в состояние И иди туда».

from typing import Literal
from langgraph.types import Command

def triage(state: State) -> Command[Literal["billing", "tech"]]:
    verdict = llm.invoke(TRIAGE_PROMPT + state["question"])
    target = "billing" if "оплат" in verdict.content else "tech"
    return Command(
        goto=target,
        update={"handoff_brief": {
            "task": verdict.content,
            "constraints": state["constraints"],
            "done_criteria": "ответ со ссылкой на политику",
        }},
    )

Для swarm-архитектур hand-off оформляют инструментом: агент вызывает tool transfer_to_billing, а тот возвращает Command(goto=..., graph=Command.PARENT) — переход выполняется в родительском графе, если агенты обёрнуты в подграфы. Аннотация Command[Literal[...]] не косметика: по ней LangGraph понимает возможные переходы и корректно рисует граф.

Главная дисциплина hand-off — содержимое передачи. Плохой hand-off — голое «займись этим» или, наоборот, вся история диалога на 40 000 токенов. Хороший — структурированный бриф: задача, ограничения, критерий готовности, ссылки на артефакты. Cognition формулирует принцип жёстче: вместе с задачей передавайте контекст РЕШЕНИЙ — почему выбран этот путь, — иначе получатель молча примет собственные конфликтующие решения.

Send: параллельный fan-out и map-reduce

Command передаёт эстафету одному получателю. Когда работу надо раздать N воркерам одновременно — причём N известно только в рантайме, — используется Send: условное ребро возвращает список конвертов, и каждый конверт запускает отдельный экземпляр узла со СВОИМ приватным входом.

import operator
from typing import Annotated, TypedDict
from langgraph.types import Send

class State(TypedDict):
    sources: list[str]
    findings: Annotated[list[str], operator.add]

def fan_out(state: State):
    return [Send("researcher", {"source": s}) for s in state["sources"]]

builder.add_conditional_edges("planner", fan_out, ["researcher"])
builder.add_edge("researcher", "reduce")

Это канонический map-reduce: planner раскладывает задачу, экземпляры researcher работают параллельно в изолированных контекстах, а результаты сливаются в общий ключ findings благодаря редьюсеру operator.add — списки конкатенируются, и никакая ветка не затирает чужой вклад. Узел reduce получает уже полный список и синтезирует ответ.

Именно эта схема дала мультиагентной research-системе Anthropic выигрыш на широких запросах: суб-агенты читают источники одновременно, и время исследования на сложных запросах сокращается до 90%. Но заметьте асимметрию: Send сокращает wall-clock, а не стоимость — токены всех веток суммируются. Fan-out на 10 воркеров — это десятикратный расход на этап, поэтому ширину веера ограничивают явно и планировщику запрещают плодить воркеров без бюджета.

Общая память: blackboard против message passing

У мультиагентной системы есть два фундаментальных способа делиться знанием. Message passing — знание едет вместе с передачей управления: hand-off-бриф, результат задачи, сообщение в общей истории. Blackboard («классная доска») — знание лежит в общем хранилище, и каждый агент читает и пишет туда независимо. В LangGraph доска реализуется двумя слоями: общий объект состояния — доска внутри одного прогона, а Store — долговременная память между прогонами и тредами, с иерархическими неймспейсами вида ("project", "vorcl", "decisions") и семантическим поиском по эмбеддингам.

Компромиссы зеркальны. Message passing даёт трассируемость: всегда видно, кто, что и кому передал, — но знание доступно только по цепочке передач, и агент в соседней ветке о нём не узнает. Blackboard даёт всеобщую видимость — любой агент может подсмотреть выводы любого, — но появляются гонки записи, устаревшие чтения и главный риск: доска превращается в свалку, где полезный факт тонет в мусоре.

Рабочая схема из практики: оперативный контекст задачи — через message passing (бриф в hand-off), а на доску кладут только структурированные, подписанные записи трёх сортов — принятые решения с обоснованием («выбрали Postgres, потому что…»), контракты интерфейсов между рабочими областями и статусы задач. Свободный текст на доску не пишут. Каждая запись имеет автора и время: без этого разрешать конфликты в следующей секции будет нечем.

Конфликты: редьюсеры, арбитр, голосование

Конфликты в мультиагентной системе бывают двух уровней, и лечатся они по-разному. Технический конфликт — две параллельные ветки пишут в один ключ состояния. Здесь ответ механический и уже знакомый: редьюсер. operator.add копит вклады, кастомный редьюсер может мёржить словари по ключам или выбирать запись с поздним таймстемпом. LangGraph не даст молча потерять данные — ключ без редьюсера при конкурентной записи роняет прогон с InvalidUpdateError, и это правильное поведение: лучше упасть, чем тихо затереть.

Семантический конфликт глубже: агенты противоречат друг другу по содержанию. Ресёрчер А пишет «конкурент поднял цены», ресёрчер Б — «цены не менялись»; кодер реализовал интерфейс не так, как ожидал автор спеки. Редьюсером это не решается — нужен арбитраж. Три рабочих паттерна по возрастанию цены. Первый — правило приоритета: побеждает источник с большим доверием (свежая дата, первичный источник против пересказа); дёшево, детерминированно, покрывает большинство случаев. Второй — агент-арбитр: отдельный узел получает оба утверждения с их основаниями и выносит вердикт с обоснованием; это ещё один LLM-вызов, зато вердикт логируется и его можно ревьюить. Третий — голосование N независимых оценщиков для критичных решений; дорого (×N вызовов) и полезно только при действительно независимых оценках.

Главное правило: конфликт — это сигнал, а не шум. Система, которая молча перезаписывает противоречие, хуже системы, которая его фиксирует, потому что скрытое противоречие всплывёт у пользователя.

Practice

Map-reduce с конфликтом и арбитром

Соберите граф: planner раздаёт через Send три источника двум-трём researcher-экземплярам, результаты копятся редьюсером operator.add. Подложите в источники противоречащие факты и добавьте узел-арбитр, который находит противоречие и выносит вердикт с обоснованием до финального синтеза.

Deliverable

Код графа, лог прогона с параллельными ветками и вердикт арбитра: какие утверждения конфликтовали, какое принято и почему.

Checkpoint

Спроектируйте hand-off-бриф для передачи задачи от архитектора кодеру. Что в него входит и почему не подходит ни «одна строка», ни «вся история»?

  • ✓ Бриф содержит задачу, ограничения, критерий готовности и контекст принятых решений.
  • ✓ Объяснено, что одна строка теряет решения, а полная история раздувает контекст и стоимость.
  • ✓ Упомянут принцип Cognition: передавать не только вывод, но и почему выбран путь.

Check yourself

Чем Command отличается от обычного возврата обновления состояния из узла?

01Чем Command отличается от обычного возврата обновления состояния из узла?

Для чего нужен Send?

02Для чего нужен Send?

Что сокращает параллельный fan-out через Send?

03Что сокращает параллельный fan-out через Send?

Что стоит класть на «доску» (blackboard) в общей памяти?

04Что стоит класть на «доску» (blackboard) в общей памяти?

Как правильно обработать семантический конфликт двух агентов?

05Как правильно обработать семантический конфликт двух агентов?

Sources

Hand-offs, shared memory, and conflicts · Multi-agent orchestration: LangGraph, CrewAI, and agent teams · VORCL