Планирование: разбиение цели на подзадачи
Большую цель не решить одним действием. Планирование — это разбить её на подзадачи и выполнять их по очереди.
Планирование (planning) — приём, при котором агент сначала строит список подзадач для достижения цели, а затем выполняет их по порядку, отмечая прогресс.
Зачем планировать
Реактивный ReAct-агент решает «по шагу за раз», глядя только на последнее наблюдение. Для коротких задач этого хватает. Но для длинной цели («подготовь отчёт») полезно сначала составить план — иначе агент легко забудет часть работы или начнёт метаться. План задаёт структуру: что сделать и в каком порядке.
Два режима
- План заранее (plan-and-execute) — сначала весь список подзадач, потом исполнение. Предсказуемо, видно целиком.
- План по ходу (ReAct) — следующий шаг выбирается реактивно. Гибко, но легче сбиться на длинной дистанции.
На практике их часто сочетают: общий план + реактивное выполнение каждой подзадачи.
Запускаемый планировщик
Сделаем планировщик-заглушку (разбивает цель на подзадачи) и исполнитель, который идёт по плану и отмечает выполненное.
def plan(goal):
# детерминированный планировщик: цель -> список подзадач
catalog = {
"опубликовать статью": [
"собрать материал",
"написать черновик",
"отредактировать текст",
"опубликовать",
],
}
# для неизвестной цели — универсальный шаблон
return catalog.get(goal.lower(), ["проанализировать", "выполнить", "проверить"])
def execute(subtasks):
done = []
for i, task in enumerate(subtasks, 1):
# здесь настоящий агент решал бы подзадачу (например, циклом ReAct)
print(f" {i}. выполняю: {task}")
done.append(task)
return done
goal = "Опубликовать статью"
subtasks = plan(goal)
print(f"Цель: {goal}")
print(f"План из {len(subtasks)} подзадач:")
done = execute(subtasks)
print("Готово:", len(done) == len(subtasks))
Вывод:
Цель: Опубликовать статью План из 4 подзадач: 1. выполняю: собрать материал 2. выполняю: написать черновик 3. выполняю: отредактировать текст 4. выполняю: опубликовать Готово: True
В настоящем агенте plan — это вызов LLM с промптом «разбей цель на шаги», а каждая подзадача внутри execute может решаться собственным циклом ReAct с инструментами. Структура же — «спланировать, потом исполнить по списку» — остаётся.
Зависимости между подзадачами
Иногда подзадачи нельзя делать в любом порядке: «опубликовать» обязано идти после «написать». Тогда план — это не просто список, а порядок с зависимостями. Простой случай — выполнять строго по очереди; сложный — граф задач (DAG), где видно, что от чего зависит.
tasks = {
"написать": [], # ни от чего не зависит
"проверить": ["написать"], # после «написать»
"опубликовать": ["проверить"], # после «проверить»
}
done = []
def ready(task):
# подзадача готова к запуску, если все её зависимости выполнены
return all(dep in done for dep in tasks[task])
# простейший проход: повторяем, пока не выполним всё
while len(done) < len(tasks):
for task in tasks:
if task not in done and ready(task):
done.append(task)
print("выполнено:", task)
print("Порядок:", done)
Вывод:
выполнено: написать выполнено: проверить выполнено: опубликовать Порядок: ['написать', 'проверить', 'опубликовать']
Итог
- Планирование разбивает большую цель на подзадачи и задаёт порядок их выполнения.
- Plan-and-execute предсказуем, чистый ReAct гибок; на практике их сочетают.
- Подзадачи бывают зависимыми — тогда порядок определяется зависимостями (вплоть до графа задач).