Планирование, память и мультиагентность
Как агенты справляются с длинными задачами — планирование (Plan-and-Solve), рефлексия (Reflexion), краткосрочная и долгосрочная память, оркестратор с субагентами и Agent Swarm в Kimi K2.5 — и когда всё это лишнее.
Зачем это нужно
Заголовок раздела «Зачем это нужно»ReAct из урока 8.3.1 хорошо работает на задачах в 3–10 шагов. На задачах в 50–200 шагов (починить баг в большом репозитории, собрать отчёт по десяткам источников) он начинает сыпаться:
- теряет цель — каждый шаг выбирается «жадно», без взгляда вперёд;
- повторяет ошибки — неудачная попытка ничему не учит следующую;
- переполняет контекст — история растёт квадратично по токенам (урок 8.3.1), а важное тонет в середине;
- работает медленно — всё строго последовательно.
Против каждой проблемы есть приём: планированиепланирование (у агента)EN planning смотрит вперёд, рефлексиярефлексия (самокритика агента)EN reflectionМодель оценивает свою попытку и записывает вывод «что исправить», который используется в следующей попытке (Reflexion, Self-Refine). — назад, память агентапамять агентаEN agent memoryКраткосрочная (short-term memory) — то, что в контекстном окне; долгосрочная (long-term memory) — внешнее хранилище, откуда нужное извлекается по запросу. хранит важное вне окна, а мультиагентная системамультиагентная системаEN multi-agent system делит работу. У каждого приёма есть цена, поэтому в конце урока разберём, когда без них лучше обойтись.
Планирование
Заголовок раздела «Планирование»Plan-and-Solve: сначала план, потом решение
Заголовок раздела «Plan-and-Solve: сначала план, потом решение»Статья Plan-and-Solve (Wang и др., ACL 2023) начинала с наблюдения: в задачах с цепочкой рассуждений (цепочка рассужденийцепочка рассужденийEN chain of thought (CoT)) модель часто пропускает шаги. Лекарство оказалось простым — заменить «давай подумаем шаг за шагом» на промпт, который требует сначала составить план:
Let's first understand the problem and devise a plan to solve the problem.Then, let's carry out the plan and solve the problem step by step.
(Сначала поймём задачу и составим план решения. Затем выполним план шаг за шагом.)В версии PS+ к этому добавляются указания «выдели нужные переменные и промежуточные величины» и «считай аккуратно». На задачах по математике это заметно сократило ошибки пропущенных шагов по сравнению с обычным zero-shot CoT.
Plan-and-Execute: план как структура агента
Заголовок раздела «Plan-and-Execute: план как структура агента»Для агентов идею развили в архитектуру из трёх ролей. Ими может быть одна и та же модель с разными промптами:
flowchart LR
T["Задача"] --> P["Планировщик:<br/>список шагов"]
P --> E["Исполнитель (ReAct):<br/>шаг i с инструментами"]
E --> R{"Перепланировщик:<br/>готово?"}
R -->|"нет: правим план"| E
R -->|"да"| A["Ответ"]Плюсы: модель видит задачу целиком, прогресс измерим («выполнено 3 из 7»), исполнителем может быть модель поменьше и подешевле. Минус: план, составленный до первого наблюдения, почти всегда неверен в деталях. Поэтому без перепланирования схема хрупкая.
Современные кодовые агенты используют облегчённый вариант: план — это инструмент. Модель ведёт список задач (todo list), отмечает выполненные пункты и дописывает новые, а список постоянно лежит в контексте. Это дёшево и не мешает гибкости ReAct.
Рефлексия: учиться на своих ошибках без обучения
Заголовок раздела «Рефлексия: учиться на своих ошибках без обучения»Self-Refine и Reflexion
Заголовок раздела «Self-Refine и Reflexion»Self-Refine (Madaan и др., 2023): модель пишет черновик, сама его критикует и переписывает. Цикл «генерация → отзыв → исправление» без внешних инструментов.
Reflexion (Shinn и др., 2023) делает шаг дальше. После проваленной попытки агент пишет словесный вывод («тест на пустом списке упал, нужно обработать этот случай») и кладёт его в эпизодическую память. На следующей попытке этот вывод стоит в промпте. Авторы называют это словесным подкреплением (verbal reinforcement): сигнал об ошибке превращается не в градиент, а в текст. На HumanEval (задачи на программирование с тестами, урок 7.3.1) такой агент получил 91% pass@1 против 80% у GPT-4 без рефлексии.
Главная оговорка: нужен внешний сигнал
Заголовок раздела «Главная оговорка: нужен внешний сигнал»Reflexion работает, потому что узнаёт о провале извне: из тестов, компилятора, среды. Если же модель оценивает свой ответ сама, без проверки, она часто «исправляет» верное на неверное. Этому посвящена работа «Large Language Models Cannot Self-Correct Reasoning Yet» (Huang и др., 2023). Практическое правило: рефлексия полезна ровно настолько, насколько надёжен сигнал о неудаче.
def evaluate(code): """Внешний сигнал: исполняем код и прогоняем тесты (вместо LLM-самооценки).""" env = {} # exec выполняет строку как Python-код; созданные им функции попадают в словарь env. # Код написала модель — значит, он недоверенный (урок 8.3.2). exec(code, env) # в реальной системе — только в песочнице! for xs, want in [([1, 2, 3], 2.0), ([], None), ([5], 5.0)]: try: got = env["mean"](xs) except Exception as e: return False, f"mean({xs}) → {type(e).__name__}: {e}" if got != want: return False, f"mean({xs}) = {got!r}, ожидалось {want!r}" return True, "все тесты пройдены"
def fake_llm(task, reflections): """Заглушка LLM: «читает» рефлексии и учитывает их. Настоящая модель делает это сама.""" code = "def mean(xs):\n return sum(xs) / len(xs)\n" if any("ZeroDivisionError" in r for r in reflections): code = "def mean(xs):\n if not xs:\n return None\n return sum(xs) / len(xs)\n" return code
def reflexion(task, generate, max_trials=3): memory = [] # эпизодическая память: выводы из неудач for trial in range(1, max_trials + 1): code = generate(task, memory) ok, feedback = evaluate(code) print(f"попытка {trial}: {'успех' if ok else 'провал'} — {feedback}") if ok: return code memory.append(f"Попытка {trial}: {feedback}. Учти этот случай в следующий раз.") return None
reflexion("Напиши mean(xs); для пустого списка верни None", fake_llm)попытка 1: провал — mean([]) → ZeroDivisionError: division by zeroпопытка 2: успех — все тесты пройденыЭто паттерн evaluator-optimizer: генератор и оценщик в цикле. Замените fake_llm на вызов модели
из урока 8.3.1, и получите настоящего агента, который чинит код по тестам.
Краткосрочная: всё, что в окне
Заголовок раздела «Краткосрочная: всё, что в окне»Краткосрочная память — это просто контекстное окноконтекстное окноEN context window: системный промпт, история шагов, наблюдения. Она дорогая (каждый токен оплачивается на каждом шаге) и ненадёжная: модели хуже используют информацию из середины длинного контекста, чем из начала и конца (эффект «lost in the middle», Liu и др., 2023). Поэтому окно нужно активно чистить — это часть контекст-инжинирингконтекст-инжиниринг (context engineering)EN context engineeringРешение, какие токены и в каком порядке попадают в окно на каждом вызове — инструкции, примеры, документы, история, результаты инструментов. (урок 8.1.2):
| Приём | Что делаем | Чем платим |
|---|---|---|
| Скользящее окно | держим последние шагов | теряем раннее, в том числе важное |
| Сжатие (compaction) | старые шаги заменяем сводкой от LLM | лишний вызов модели, детали теряются |
| Обрезка наблюдений | от вывода инструмента оставляем суть | можно отрезать нужное |
| Блокнот (scratchpad) | агент сам пишет заметки в файл и перечитывает их | нужен инструмент и дисциплина |
Anthropic в статье «Effective context engineering for AI agents» (2025) называет три главных приёма для долгих задач: сжатие, структурированные заметки и мультиагентную архитектуру. До последней мы дойдём ниже.
Долгосрочная: внешнее хранилище
Заголовок раздела «Долгосрочная: внешнее хранилище»Долгосрочная память живёт вне модели — в файлах, базе данных, векторном индексе — и подгружается в окно по запросу. По сути это генерация с дополнением извлечёнными даннымигенерация с дополнением извлечёнными данными (RAG)EN retrieval-augmented generation (RAG) поверх собственного опыта агента. Обычно различают три вида:
- эпизодическая — что происходило: прошлые сессии, удачные и неудачные траектории (как память Reflexion);
- семантическая — факты: «пользователь пьёт чай без сахара», «в проекте используется PostgreSQL 16»;
- процедурная — как делать: инструкции и навыки, например файл с правилами проекта, который агент читает при старте.
Самые трудные вопросы — не «как искать», а что записывать, когда забывать и как обновлять устаревшее.
MemGPT (Packer и др., 2023) предложил аналогию с операционной системой: контекст — это оперативная память, внешнее хранилище — диск, а модель сама управляет «подкачкой» с помощью вызовов функций (сохранить, найти, вытеснить).
Что достать из памяти: свежесть, важность, релевантность
Заголовок раздела «Что достать из памяти: свежесть, важность, релевантность»В Generative Agents (Park и др., 2023, «симулятор деревни» из 25 агентов) все наблюдения пишутся в поток памяти, а для каждого запроса воспоминания ранжируются по сумме трёх оценок:
Свежесть затухает экспоненциально (в статье множитель 0.995 за каждый игровой час с последнего обращения), важность от 1 до 10 оценивает сама LLM при записи, релевантность — косинусное сходство эмбеддингов. Каждую оценку нормируют в , в статье все (веса слагаемых) равны 1.
Пример с числами для свежести: воспоминанию 49 часов, его свежесть ; воспоминанию 2 часа — . Нормировка min-max переводит набор чисел в по формуле : самое маленькое значение станет 0, самое большое — 1. Соберём такую память:
import mathfrom collections import Counter
def embed(text): """Заглушка эмбеддинга: символьные триграммы. Терпима к падежам: «чай» ≈ «чаю».""" t = f" {text.lower()} " return Counter(t[i:i + 3] for i in range(len(t) - 2))
def cosine(a, b): dot = sum(a[g] * b[g] for g in a) norm = math.sqrt(sum(v * v for v in a.values())) * math.sqrt(sum(v * v for v in b.values())) return dot / norm if norm else 0.0
def minmax(xs): lo, hi = min(xs), max(xs) return [(x - lo) / (hi - lo) if hi > lo else 1.0 for x in xs]
class LongTermMemory: """Поток памяти по мотивам Generative Agents: свежесть + важность + релевантность."""
def __init__(self, decay=0.995): self.decay, self.items = decay, []
def add(self, text, hour, importance): # importance 1..10 обычно оценивает сама LLM self.items.append({"text": text, "hour": hour, "imp": importance, "vec": embed(text)})
def retrieve(self, query, now, k=2): q = embed(query) # в статье свежесть считают от последнего обращения к воспоминанию, здесь для простоты — от записи rec = minmax([self.decay ** (now - m["hour"]) for m in self.items]) imp = minmax([m["imp"] for m in self.items]) rel = minmax([cosine(q, m["vec"]) for m in self.items]) scored = sorted(zip(self.items, rec, imp, rel), key=lambda x: -(x[1] + x[2] + x[3])) for m, r, i, s in scored: print(f"{r + i + s:4.2f} = {r:.2f} + {i:.2f} + {s:.2f} {m['text']}") return [m["text"] for m, *_ in scored[:k]] # m, *_ — берём первый элемент кортежа, остальные отбрасываем
mem = LongTermMemory()mem.add("Пользователь пьёт зелёный чай без сахара", hour=1, importance=6)mem.add("Созвон с командой перенесли на пятницу", hour=20, importance=4)mem.add("Пользователь купил лунцзин в Ханчжоу", hour=30, importance=5)mem.add("Спрашивал погоду в Москве", hour=47, importance=2)mem.add("У пользователя аллергия на орехи", hour=48, importance=9)
print("В контекст попадут:", mem.retrieve("Какой чай предложить пользователю к десерту?", now=50))2.72 = 1.00 + 1.00 + 0.72 У пользователя аллергия на орехи1.69 = 0.59 + 0.43 + 0.67 Пользователь купил лунцзин в Ханчжоу1.57 = 0.00 + 0.57 + 1.00 Пользователь пьёт зелёный чай без сахара0.98 = 0.98 + 0.00 + 0.00 Спрашивал погоду в Москве0.72 = 0.38 + 0.29 + 0.06 Созвон с командой перенесли на пятницуВ контекст попадут: ['У пользователя аллергия на орехи', 'Пользователь купил лунцзин в Ханчжоу']Посмотрите на результат внимательно. Аллергия на орехи к десерту — отличная находка, её вытащила важность. А самое релевантное воспоминание («зелёный чай без сахара») не попало в топ-2: оно самое старое, и min-max нормировка дала ему свежесть ровно 0. Это типичная проблема настройки. Лечится весами (поднять вес релевантности), более медленным затуханием или большим . В реальной системе вместо триграмм будет эмбеддинг-модель вроде BGE (урок 8.2.1), а важность оценит LLM.
Мультиагентные системы
Заголовок раздела «Мультиагентные системы»Зачем несколько агентов
Заголовок раздела «Зачем несколько агентов»- Параллельность. Пять независимых подзадач можно решать одновременно.
- Чистые контексты. Каждый субагентсубагентEN subagent получает только свою подзадачу и возвращает короткую сводку. Главный агент не тонет в сотнях страниц сырых результатов поиска.
- Специализация. Разные промпты, инструменты и даже модели: дорогая модель планирует, дешёвые ищут.
Основные схемы
Заголовок раздела «Основные схемы»flowchart TB U["Задача"] --> O["Оркестратор:<br/>план и раздача подзадач"] O --> W1["Субагент 1"] O --> W2["Субагент 2"] O --> W3["Субагент 3"] W1 --> S["Оркестратор:<br/>сводит результаты"] W2 --> S W3 --> S S --> A["Ответ"]
- оркестратороркестратор (ведущий агент)EN orchestratorАгент, который разбивает задачу на подзадачи, раздаёт их субагентам и собирает результаты. и исполнители — ведущий агент сам решает, на что разбить задачу и сколько субагентов запустить. Так устроены системы «глубокого исследования».
- Конвейер ролей — агенты передают работу по цепочке: «аналитик → архитектор → программист → тестировщик». Это подход китайских проектов MetaGPT и ChatDev (о них ниже). Он предсказуем, но по сути это workflow.
- Оценщик и исполнитель — как Reflexion, только роли разнесены по разным агентам.
- Дебаты и голосование — несколько агентов решают независимо, затем спорят или голосуют.
Сколько это стоит и что даёт
Заголовок раздела «Сколько это стоит и что даёт»Anthropic в разборе своей исследовательской системы (2025) приводит показательные цифры. Связка из ведущего агента (Claude Opus 4) и субагентов (Claude Sonnet 4) превзошла одиночного Claude Opus 4 на 90.2% во внутреннем исследовательском эвале. Цена — токены: агенты тратят примерно в 4 раза больше токенов, чем обычный чат, а мультиагентные системы — примерно в 15 раз. На бенчмарке BrowseComp (поиск трудно находимых фактов в вебе) один только объём потраченных токенов объяснил 80% разброса качества. Вывод авторов: мультиагентность окупается на ценных задачах, которые хорошо распараллеливаются, и не окупается там, где агентам нужен общий контекст.
Противоположную позицию сформулировала Cognition (авторы агента Devin) в заметке «Don’t Build Multi-Agents» (2025): субагенты, не видящие решений друг друга, принимают несовместимые решения. Пример оттуда: задачу «сделай клон Flappy Bird» делят пополам. Один субагент рисует фон в стиле Super Mario Bros, другой — птицу, которая не похожа на игровой спрайт и двигается не так, как в Flappy Bird. Склеить такие части в одну игру почти невозможно. Их принципы: передавайте субагенту полный контекст и историю, а не одну короткую задачу, и помните, что любое действие несёт неявные решения. Обе позиции совместимы: распараллеливайте чтение и поиск, а запись и согласованные изменения (код, документ) делайте одним агентом.
Задержка: критический путь
Заголовок раздела «Задержка: критический путь»Если субагенты работают параллельно, задержку определяет самый медленный из них. Moonshot в описании PARL (об этом ниже) формализует это метрикой Critical Steps: для каждого этапа берётся число шагов оркестратора плюс максимум шагов среди параллельных субагентов, и всё суммируется:
def critical_steps(stages): """Длина критического пути: этап = (шаги оркестратора, [шаги каждого субагента]).""" return sum(orch + max(subs, default=0) for orch, subs in stages)
subtasks = [12, 8, 15, 6, 9] # шагов на каждую из 5 подзадачsingle = 2 + sum(subtasks) + 4 # один агент: план, всё подряд, итогswarm = critical_steps([(2, subtasks), (4, [])]) # оркестратор: план → 5 субагентов параллельно → итогprint(f"один агент: {single} шагов, рой: {swarm} шагов, ускорение ×{single / swarm:.2f}")
print("Вероятность пройти n шагов без единой ошибки:")for p in (0.99, 0.95, 0.90): print(f" p = {p:.2f}: " + " ".join(f"n={n}: {p ** n:.3f}" for n in (5, 10, 20, 50)))один агент: 56 шагов, рой: 21 шагов, ускорение ×2.67Вероятность пройти n шагов без единой ошибки: p = 0.99: n=5: 0.951 n=10: 0.904 n=20: 0.818 n=50: 0.605 p = 0.95: n=5: 0.774 n=10: 0.599 n=20: 0.358 n=50: 0.077 p = 0.90: n=5: 0.590 n=10: 0.349 n=20: 0.122 n=50: 0.005Ускорение ×2.67, хотя подзадач пять: всё упирается в самую длинную (15 шагов) и в последовательные этапы оркестратора. Это закон Амдала в мире агентов: ускорение от параллельности ограничено той частью работы, которую распараллелить нельзя. Вторая половина вывода — к следующему разделу.
Kimi K2.5: Agent Swarm и PARL (обзорно)
Заголовок раздела «Kimi K2.5: Agent Swarm и PARL (обзорно)»Обычно мультиагентную схему проектирует человек: сколько субагентов, какие роли. В Kimi K2.5 (январь 2026) Moonshot учит этому саму модель. По заявлению компании, в режиме Agent Swarm K2.5 сама разбивает задачу и запускает до 100 субагентов, выполняющих до 1500 вызовов инструментов, а время выполнения сокращается до 4.5 раза по сравнению с одиночным агентом. Это цифры разработчиков, независимой проверки мы не нашли.
Метод обучения называется PARL (Parallel-Agent Reinforcement Learning). Интересны три инженерных решения из техотчёта:
- Обучается только оркестратор. Субагенты — «замороженные» копии модели из промежуточных чекпоинтовчекпоинт (контрольная точка)EN checkpointСохранённое состояние обучения: веса модели, состояние оптимизатора, номер шага. Позволяет продолжить обучение после сбоя., их траектории не участвуют в оптимизации. Так проще распределить заслугу (credit assignment): за успех или провал отвечает декомпозиция задачи.
- «Последовательный коллапс» (serial collapse). Без специальных стимулов оркестратор скатывается к привычному одиночному режиму и почти не запускает субагентов параллельно. Поэтому награда на ранних этапах отдельно поощряет параллельный запуск, а затем этот бонус постепенно убирают, и остаётся награда за решённую задачу.
- Оптимизация задержки. Целевая метрика — критические шаги из формулы выше, а не общее число шагов.
О том, как вообще устроено обучение агентов с подкреплением, — в следующем уроке.
Когда агенты не нужны
Заголовок раздела «Когда агенты не нужны»Вернитесь к выводу программы выше. Если каждый шаг агента успешен с вероятностью , а ошибки не исправляются, то вероятность пройти шагов без единой ошибки равна . Даже при за 20 шагов это всего 36%. Агентность — это умножение рисков. Рефлексия и проверки поднимают эффективное , но не бесплатно.
Поэтому поднимайтесь по лестнице сложности только при необходимости:
| Уровень | Когда достаточно | Пример |
|---|---|---|
| Один вызов LLM | задача решается за один ответ | классификация обращений, перевод, извлечение полей |
| Один вызов + RAG | нужны знания, но путь фиксирован | ответы по документации компании |
| Workflow (цепочка, маршрутизация, параллельные вызовы) | шаги известны заранее | «распознать → проверить → сформировать ответ» |
| Один агент с инструментами | путь заранее неизвестен, результат можно проверить | исправить баг, пока тесты не пройдут |
| Мультиагентная система | задача ценная, широкая и хорошо параллелится | обзор рынка по сотне источников |
Вопросы перед тем, как строить агента:
- Можно ли описать путь решения заранее? Если да — нужен workflow, а не агент.
- Можно ли автоматически проверить результат (тесты, схема, сверка)? Без этого агента трудно и отлаживать, и улучшать.
- Какова цена ошибки и нужно ли подтверждение человека?
- Укладываются ли 5–15-кратный расход токенов и задержка в бюджет?
Проверь себя
Заголовок раздела «Проверь себя»- Планирование смотрит вперёд: Plan-and-Solve в промпте, Plan-and-Execute с перепланированием или план-список задач как инструмент.
- Рефлексия смотрит назад: Reflexion записывает выводы из неудач в память. Работает, когда есть внешний сигнал (тесты, среда).
- Память: краткосрочная — окно, которое надо чистить; долгосрочная — внешнее хранилище с извлечением по свежести, важности и релевантности.
- Мультиагентность даёт параллельность и чистые контексты ценой в разы большего расхода токенов и риска несогласованных решений. Kimi K2.5 учит оркестрацию через RL (PARL).
- Ошибки в цепочке перемножаются (). Начинайте с простейшей схемы и усложняйте по измеримой необходимости.
Схемы из этого урока на настоящих фреймворках (LangGraph, OpenAI Agents SDK, Claude Agent SDK) и то, как держать мультиагентную систему в продакшене — бюджеты, ретраи, трассы, оценка траекторий, — разобраны в модуле 8.5, особенно в уроке 8.5.4.
Дальше: как обучают агентные модели (синтез сред в Kimi K2, асинхронный RL в slime у GLM), почему промпт-инъекции — главная угроза агентам и как оценивать агентов.