Lomo Agent: архитектура и инженерная практика творческого агента для непрерывной совместной работы
Как мы строили Lomo Agent: организация задач, маршрутизация сообщений, устойчивое выполнение, асинхронная генерация, контекст, долгосрочная память и автономия.
isboyjc4 мин чтенияНа этой странице
- Lomote AI и Lomo Agent
- Начнём с творческой задачи, которую постоянно переделывают
- 1. Организация задач: от запроса пользователя к задаче, которая продолжает двигаться
- 2. Обзор системы: уровень координации, уровень выполнения и общее рабочее пространство
- 2.1 Общая архитектура
- 2.2 Не каждому запросу нужен отдельный Worker
- 2.3 Как проходит один цикл совместной работы
- 3. Маршрутизация сообщений: как каждому сообщению найти нужного исполнителя
- 3.1 Детерминированная и семантическая маршрутизация
- 3.2 Доставлено — не значит выполнено
- 3.3 Что мы позаимствовали у продуктов для совместной работы
- 4. Устойчивое выполнение: восстановление после прерывания, повторы и управление конкурентностью
- 4.1 Сначала сохранить решение, затем вызывать инструменты
- 4.2 Перехват задачи другим Worker и Fencing Token
- 4.3 Как обрабатывать новые сообщения пользователя во время выполнения
- 4.4 Журнал выполнения и границы согласованности планирования
- 5. Асинхронная генерация: жизненный цикл задач и пробуждение по событиям
- 5.1 Отправка, ожидание и возобновление выполнения
- 5.2 Асинхронные события тоже нужно обрабатывать надёжно
- 5.3 Когда нужно будить Worker?
- 6. Контекстная инженерия: как сохранить актуальное состояние в ограниченном окне
- 6.1 Уровни контекста и Prompt Cache
- 6.2 Загрузка инструментов по требованию: чем больше возможностей, тем дороже контекст
- 6.3 Сжатие истории: помимо сводок — возможность перечитать
- 6.4 Текущая задача всегда должна оставаться в поле зрения модели
- 6.5 Практические компромиссы контекстной инженерии
- 7. Инженерия памяти: источники, версии и отзываемость
- 7.1 От истории сообщений к долгосрочной памяти
- 7.2 Чему нас научили существующие архитектуры памяти
- 7.3 Почему «забыть» сложнее, чем «запомнить»
- 7.4 Надёжность памяти и плавная деградация (graceful degradation)
- 8. Самостоятельное продвижение: план, условия остановки и полномочия пользователя
- 8.1 Превращаем план в постоянно поддерживаемый чек-лист выполнения
- 8.2 Модель перестала отвечать — нужно ли продолжать задачу?
- 8.3 Human-in-the-Loop: пусть ключевые решения принимает пользователь
- 8.4 Уровень самостоятельности, бюджет и границы полномочий
- 9. Shared Workspace: пользователь и агент правят одну и ту же работу
- 9.1 У работы — собственный жизненный цикл
- 9.2 Как агенту замечать изменения в рабочем пространстве
- 9.3 Конфликты версий и оптимистичное управление конкурентностью
- 9.4 От Artifacts и Canvas к общему творческому пространству
- 10. Оркестрация творческих возможностей: от вызова модели к сдаче готовой работы
- 10.1 Как дать агенту понимание творческого процесса и границ возможностей
- 10.2 Многоагентное видеопроизводство на примере ViMax
- 10.3 Граф производственных зависимостей: от линейного выполнения к управлению зависимостями
- 11. Инженерная проверка: от восстановления после сбоев до сквозной совместной работы
- 11.1 Проверка надёжности среды выполнения
- 11.2 Сквозные прогоны совместной работы
- 11.3 Как оценить качество совместной работы
- 12. Нерешённые вопросы и дальнейшие исследования
- Заключение
- Источники и дополнительное чтение
Чтобы снять короткий ролик о продукте, обычно нужно пройти через обсуждение требований, подбор референсов, раскадровку, генерацию кадров, генерацию видео и бесконечные правки. Одни этапы занимают секунды, другие — минуты ожидания; что-то можно доверить модели, а что-то обязательно должен подтвердить пользователь. И даже когда готова первая версия, работа далеко не закончена.
С приходом эпохи AI я почти перестал писать технические статьи: большая часть времени уходит на эксперименты с применением AI в самых разных сценариях.
Многие считают, что главные бенефициары эпохи AI — продакт-менеджеры (PM): расстояние между идеей и её воплощением резко сократилось. Но, на мой взгляд, инженерам AI даёт, пожалуй, ещё больше. Он постепенно снимает ограничения, которые техническая реализация накладывала на творчество, и даёт инженерам возможность выйти за рамки кода и инженерных деталей и заново осмыслить саму суть продукта, архитектуры и даже взаимодействия человека с компьютером.
Раньше мы тратили массу времени на то, чтобы машина правильно выполняла инструкции. Теперь наконец можно больше думать о куда более интересном вопросе: как сделать так, чтобы машина действительно понимала намерения человека и работала с ним более естественно.
Недавно мы запустили Lomo Agent в Lomote AI. Пользуясь случаем, хочу рассказать, как мы думали, какую архитектуру выбрали и какие инженерные решения приняли на пути от идеи до продакшена.
В статье речь пойдёт об организации задач, маршрутизации сообщений, устойчивом выполнении (durable execution), асинхронных задачах, контекстной инженерии (context engineering), долгосрочной памяти, самостоятельном продвижении работы и общем творческом рабочем пространстве. Часть этих решений уже работает в продакшене, часть пока исследуется и проверяется. По ходу я буду опираться на отличные продукты и инженерные практики — DeepSeek, Manus, Claude Code и другие — и делиться тем, как взвешивал разные подходы.
Lomote AI и Lomo Agent
Lomote AI — универсальная платформа для AI-творчества, которую мы создаём. Любопытно, что в этом проекте я в основном выступал в роли продакт-менеджера (PM), но при этом участвовал и в проектировании и разработке базовой инфраструктуры. Это дало мне возможность взглянуть на вопрос, какой должна быть форма и опыт работы с инструментом для AI-творчества, сразу с двух сторон — продуктовой и технической.
Сейчас в Lomote AI интегрированы разнообразные модели генерации и улучшения изображений, видео и 3D, а также есть библиотека материалов, мудборды и визуальные воркфлоу Flows. Мы хотим, чтобы авторы могли в едином рабочем пространстве управлять материалами, комбинировать модели и выстраивать творческий процесс, — чтобы AI-творчество постепенно превращалось из разрозненных вызовов моделей в цельный и связный опыт.
Эти возможности во многом решают проблему разрозненности инструментов для AI-творчества. Но чем глубже мы погружались в использование, тем яснее становилось: объединить инструменты — лишь первый шаг. Куда интереснее понять, как заставить их по-настоящему работать вместе и освободить продуктивность пользователя.
В традиционном процессе AI-творчества пользователю всё равно приходится самому планировать шаги, выбирать модели, упорядочивать материалы и раз за разом переносить контекст и промежуточные результаты между задачами. Когда процесс усложняется, на координацию всех этапов уходит огромное количество сил.
Именно в таком контексте и появился Lomo Agent. Мне хотелось, чтобы AI из простого инструмента для творчества постепенно превратился в агента, который понимает намерения, планирует задачи и работает с пользователем непрерывно. Пользователю достаточно описать цель — агент сам вызовет модели и инструменты платформы, выстроит творческий процесс и будет корректировать его по ходу выполнения с учётом обратной связи.
Чтобы получить такой опыт, обычных однократных вызовов инструментов явно недостаточно. Как организовать задачи, как управлять контекстом, как координировать асинхронное выполнение и как агенту продолжать работу, когда пользователь ушёл, — всё это стало архитектурными задачами, которые нам предстояло решить.
Начнём с творческой задачи, которую постоянно переделывают
Допустим, в понедельник пользователь говорит: «Сделай мне серию брендовых постеров в ретростиле». Агент собирает требования и начинает генерировать референсы. Тем временем пользователь вклинивается с другой задачей: «Заодно сделай из вчерашней картинки с котом пятисекундное видео». А в среду возвращается к первой: «Те постеры сделай потеплее и поправь расположение текста».
В обычной совместной работе людей это совершенно нормально. Но для агента за этим стоят параллельное управление несколькими задачами, асинхронная генерация, связывание контекста и поиск по ранее созданным материалам. По мере продвижения работы добавляются отмены, повторные попытки, контроль бюджета, меняющиеся предпочтения пользователя и работа с нескольких устройств.
Примерно месяц назад я задумался: почему агент обязательно должен строиться вокруг сессий (Session)?
Большинство агентных продуктов до сих пор следуют парадигме чат-бота: пользователь сам создаёт, ищет и поддерживает сессии. У активных пользователей сессии копятся, и управление контекстом, которое должен был взять на себя AI, ложится на плечи пользователя.
В реальной жизни никто специально не ведёт никаких «сессий». Мы в любой момент переключаемся на другую тему и через несколько дней спокойно продолжаем прежний разговор. Наверное, идеальный агент тоже должен так уметь.
Поэтому в Lomo Agent мы попробовали ослабить понятие сессии на стороне пользователя и переложить распознавание задач, изоляцию контекста и связывание с историей на систему. Конечно, это лишь одно звено общей архитектуры. Чтобы по-настоящему поддерживать непрерывное творчество, нужно решить целый ряд инженерных задач: восстановление выполнения, асинхронное планирование, долгосрочную память, авторизацию бюджета и управление общими результатами.
Пока я делал демо, мне как раз попались размышления команды DeepSeek об архитектуре Super-agent. Интересно, что они столкнулись с похожими проблемами в организации задач и управлении контекстом. Разделение ответственности Router / Worker из их статьи «Исследование дизайна Super-agent: на пути к более автономным агентным системам», а также последующий дизайн маршрутизации сообщений дали мне немало идей и помогли доработать общую архитектуру Lomo Agent.
Дальше на примере реального дизайна и реализации расскажу, как я рассуждал и какие компромиссы выбирал в ряде вопросов.
1. Организация задач: от запроса пользователя к задаче, которая продолжает двигаться
Вернёмся к примеру. Пользователь может за несколько минут переключиться между несколькими темами или вернуться к какой-то задаче через несколько дней. Для агента получение сообщения — только начало. Важнее понять, к какой цели относится это сообщение и как найти прежний прогресс выполнения и связанные материалы.
Здесь возникает архитектурный компромисс. Если все задачи делят один контекст, информация о разных целях начинает мешать друг другу, а контекстное окно модели быстро раздувается. Если же жёстко изолировать всё по сессиям, рвутся исторические связи между задачами.
Поэтому в Lomo Agent мы разделили точку входа для взаимодействия, контекст выполнения и долгосрочную память на три относительно независимых уровня:
| Понятие | Какую проблему решает | Как это сделано в Lomo Agent |
|---|---|---|
| Точка входа для взаимодействия | Где пользователь формулирует свои потребности | Единый основной диалог на стороне пользователя — не нужно вручную создавать и переключать сессии |
| Контекст выполнения | Какая информация должна вместе поступать модели для рассуждения | Система создаёт под каждую цель отдельную задачу со своими границами контекста |
| Долгосрочная память | Что стоит сохранять между задачами и во времени | Профиль, факты и эпизодические сводки с отслеживаемым источником, извлекаемые по необходимости |
Пользователю достаточно естественно выражать намерения в единой точке входа. Система сама определяет, к какой задаче относится сообщение, и для работы, которую нужно вести дальше, создаёт отдельную задачу. У каждой задачи свой контекст, своё состояние выполнения и свои ссылки на результаты; при необходимости она может получить информацию из других задач через контролируемые механизмы поиска и обмена.
Проектируя это, мы смотрели и на то, как подходят к вопросу существующие продукты.
Например, ChatGPT с помощью памяти между сессиями повышает связность разных диалогов, а Claude через Projects и подобные механизмы позволяет пользователю организовывать знания и контекст вокруг конкретного проекта. У каждого подхода свои плюсы: явные границы сессий и проектов легко понимать, ими удобно управлять и делиться, а пользователь получает более прямой контроль над областью контекста.
Но в творческих сценариях, где приходится долго и часто переключаться между задачами, часть работы по организации контекста всё равно остаётся на пользователе. Память между сессиями помогает агенту узнать предпочтения и историю пользователя, но не может полноценно описать задачу, которая выполняется прямо сейчас, вместе с её прогрессом.
Поэтому Lomo Agent берёт на себя больше работы по организации задач, по возможности избавляя пользователя от повседневного управления сессиями. Разумеется, автоматическое распознавание означает, что задача может быть определена неверно. Для этого мы оставили явные точки входа в задачи и механизмы исправления, а когда цель очевидна, отдаём предпочтение детерминированной маршрутизации, чтобы сократить лишние суждения модели.
flowchart TD
U["Пользователь"] --> E["Единая точка входа"]
E --> R["Распознавание задач и маршрутизация"]
R --> A["Задача A: брендовые постеры"]
R --> B["Задача B: создание видео"]
R --> C["Простой запрос: обрабатывается прямо в основном диалоге"]
A <--> M["Долгосрочная память"]
B <--> M
C <--> M
A --> S["Общие материалы и результаты"]
B --> S
Рис. 1. Модель организации задач в Lomo Agent
2. Обзор системы: уровень координации, уровень выполнения и общее рабочее пространство
Определившись с организацией задач, нужно было понять, как заставить эти задачи реально работать.
Агенту, способному к непрерывной совместной работе, приходится одновременно обрабатывать новые запросы пользователя, продвигать уже начатые задачи, ждать результатов внешней генерации и реагировать на правки пользователя в его работах. Если поручить всё это одному агенту, контекст быстро раздуется, а задачи начнут мешать друг другу; если же создавать отдельного агента под каждый запрос, появятся дополнительные затраты на координацию.
Исходя из этого, я спроектировал для Lomo Agent двухуровневую структуру Main / Task и добавил на уровне выполнения устойчивую среду выполнения (runtime), чтобы разные задачи могли продвигаться независимо, при этом получая доступ к творческим результатам и изменяя их через общее рабочее пространство.
2.1 Общая архитектура
Систему можно разделить на четыре основные части:
-
Main (основной диалог, main thread): точка входа, через которую пользователь постоянно взаимодействует с системой. Отвечает за понимание новых запросов, привязку к существующим задачам, обработку простых вопросов, а также за координацию выполнения и обратной связи по разным задачам.
-
Task (задача): независимое пространство выполнения, в котором живёт конкретная творческая цель. Каждая задача ведёт собственный бриф, контекст, план выполнения, входящие сообщения (Inbox) и состояние выполнения.
-
Agent Runtime (среда выполнения): управляет инференсом модели, вызовами инструментов, асинхронными ожиданиями, сохранением состояния и восстановлением выполнения, а на каждом шаге проверяет бюджет, права доступа и условия остановки.
-
Shared Workspace (общее рабочее пространство): хранит творческие результаты — изображения, видео, 3D, документы, наборы материалов и воркфлоу Flows — и позволяет пользователю и разным задачам совместно читать и изменять их в рамках своих прав.
С точки зрения ролей Main в основном выполняет функцию Router, а внутри Task конкретную работу продвигает Worker. Router и Worker — логические роли времени выполнения; они не обязаны работать на разных базовых моделях.
flowchart TB
U["Пользователь: естественный язык / действия с карточками / правки в рабочем пространстве"] --> UI["Единый интерфейс совместной работы"]
UI --> GW["Уровень доступа: внутренний API / открытый API / события в реальном времени"]
GW --> CMD["Приём команд и сообщений"]
subgraph CORE["Agent Core"]
ROUTER["Main / Router: понимание и координация"]
ROUTING["Определение принадлежности цели и детерминированная маршрутизация"]
W1["Task A / Worker: серия постеров"]
W2["Task B / Worker: короткое видео"]
CTX["Оркестрация контекста / сжатие / инструменты по требованию"]
RT["Движок устойчивого выполнения"]
GUARD["Авторизация / бюджет / уровень самостоятельности"]
end
CMD --> ROUTING
ROUTING --> ROUTER
ROUTING --> W1
ROUTING --> W2
ROUTER --> RT
W1 --> RT
W2 --> RT
RT --> CTX
RT --> GUARD
RT --> TOOLS["Творческие инструменты: изображения / видео / аудио / 3D / холст / материалы"]
RT <--> DB[("Персистентная БД: факты выполнения и состояние")]
RT <--> M["Долгосрочная память: профиль / факты / эпизодические сводки"]
TOOLS --> ASSET["Общие результаты и рабочее пространство"]
ASSET --> UI
DB --> EVENTS["Очереди / публикация событий / push в реальном времени"]
EVENTS --> UI
Рис. 2. Общая многоуровневая архитектура Lomo Agent
Важная особенность этой архитектуры — у выполнения задач и у творческих результатов свои, независимые жизненные циклы.
Например, пользователь запускает создание постера в основном диалоге, и система заводит под него отдельную задачу. Дальнейшие референсы, записи генераций и история правок хранятся в этой задаче, а итоговые результаты — в общем рабочем пространстве. Даже если пользователь переключится на другую задачу, задача с постером может продолжать ждать результатов генерации и при необходимости возобновить выполнение.
Основному диалогу достаточно хранить краткое описание и текущий статус каждой задачи — держать полную историю каждого вызова модели и каждого запуска инструмента ему не нужно. Так контекст основного диалога не разрастается бесконечно по мере использования.
Что мы почерпнули из DeepSeek Super-agent
В процессе проектирования архитектурная концепция Super-agent от команды DeepSeek дала нам немало идей.
Там тоже разделены координация и выполнение: разные цели организуются через Router / Worker, а принадлежность задач поддерживается с помощью механизмов вроде Ticket / Executor. Такой дизайн особенно хорошо подходит агентам, которые работают долго и постоянно получают новые инструкции.
Однако опубликованный DeepSeek исследовательский прототип ориентирован в основном на работу с репозиториями кода. У задач в репозитории, как правило, достаточно чёткая область работы, конкретные изменения файлов и понятный результат, а границы задач в AI-творчестве куда более размыты.
Например, простая генерация изображения может занять пару десятков секунд, но несколько последующих правок пользователя способны постепенно превратить её в долгий творческий проект. К тому же один и тот же материал могут использовать и изменять сразу несколько задач.
Поэтому мы сохранили базовое разделение труда Router / Worker и добавили решения под творческие сценарии: лёгкие задачи, идентичность общих результатов и асинхронное ожидание.
Такой подход приносит и новую сложность: определение принадлежности задачи, права доступа к общим объектам, синхронизацию состояния между задачами. Подробно разберём это в следующих разделах.
2.2 Не каждому запросу нужен отдельный Worker
Многоагентные архитектуры легко попадают в ловушку: раз задачи можно дробить, давайте отдавать каждую мелочь отдельному агенту.
Но у независимого пространства выполнения есть своя цена. Каждый новый Worker — это дополнительный контекст, состояние выполнения и связи маршрутизации сообщений, которые нужно поддерживать; вместе с ними могут вырасти и расходы на вызовы модели и координацию.
Возьмём творческие сценарии Lomo Agent:
Пользователь спрашивает: «Какие разрешения поддерживает эта видеомодель?» — Main может ответить сам;
Пользователь просит «сгенерировать картинку с рыжим котом» — возможно, хватит одной задачи генерации;
А «сделать серию брендовых постеров в едином стиле» — это планирование, работа с материалами, несколько раундов генерации и последующие правки, так что здесь уместнее отдельная задача.
Поэтому мы дополнительно разделили выполнение на лёгкие задачи (Lightweight Task) и полноценные задачи (Persistent Task).
Лёгкая задача в основном несёт одну генерацию, её состояние выполнения и карточку результата и не должна надолго занимать место в списке задач пользователя. Когда пользователь продолжает дорабатывать результат или работа постепенно перерастает в сложную творческую цель, система может повысить её до полноценной задачи, сохранив контекст и записи выполнения, нужные для дальнейшей работы.
Полноценная задача имеет относительно полный жизненный цикл: она может постоянно принимать сообщения, выполнять план, ждать внешних задач и возобновлять работу спустя дни и даже больше.
Такое разделение дополняет и отказ от сессий. Мы хотим снять с пользователя бремя управления сессиями — и уж точно не хотим просто заменить его другим списком задач, который придётся постоянно разбирать.
2.3 Как проходит один цикл совместной работы
Вернёмся к примеру с брендовыми постерами и видео с котом.
Сначала пользователь запускает создание постеров в Main. Router определяет, что это цель, требующая продолжительной работы, создаёт соответствующую задачу, и дальнейшим планированием и генерацией занимается Worker.
Пока идёт работа над постерами, пользователь просит сделать видео из той самой картинки с котом. Система распознаёт, что это отдельная работа, и создаёт для неё новое пространство выполнения. Обе задачи ведут собственный контекст и продвигаются независимо, каждая в соответствии со своим состоянием.
Когда вариант постера нужно подтвердить, агент показывает пользователю кандидатов в карточке задачи. Пользователь выбирает прямо в карточке, и сообщение по явному идентификатору задачи возвращается нужному Worker — повторный семантический анализ не требуется.
Через несколько дней пользователь пишет в основном диалоге: «Те постеры сделай чуть потеплее», и Router, опираясь на индекс задач и историю, привязывает новую инструкцию к исходной задаче с постерами.
sequenceDiagram
participant U as Пользователь
participant R as Main / Router
participant A as Task A / постеры
participant B as Task B / видео
U->>R: Сделай серию брендовых постеров
R->>A: Создать задачу и бриф
R-->>U: Показать карточку задачи в основном диалоге
U->>R: Заодно сделай из вчерашнего кота видео
R->>B: Создать ещё одну задачу
Note over A,B: Независимые контексты, параллельное выполнение
A-->>U: Варианты постера ждут подтверждения
U->>A: Выбрать в карточке вариант B
B-->>U: Генерация видео завершена
U->>R: Те постеры сделай чуть потеплее
R->>A: Вернуть уточнение в существующую задачу
Рис. 3. Диаграмма последовательности совместной работы Main / Task с несколькими задачами
Здесь стоит отметить одну деталь дизайна: определение принадлежности сообщения и его выполнение — это два разных этапа.
Router может назначить сообщение нужной задаче, но Worker не обязательно обработает его сразу. Например, он может ждать генерации видео или стоять на паузе в точке подтверждения пользователем. Новое сообщение сначала попадает в персистентный Inbox, а среда выполнения уже решает, когда его обработать, исходя из текущего состояния.
Это значит, что организация задач — только первый шаг. С ростом числа задач точное распознавание дальнейших намерений пользователя, предотвращение ошибочной маршрутизации и обработка новых сообщений, приходящих посреди выполнения, становятся важнейшими вопросами всей архитектуры.
3. Маршрутизация сообщений: как каждому сообщению найти нужного исполнителя
Единая точка входа позволяет пользователю естественно переключаться между задачами, но перекладывает на систему работу, которую раньше делал сам пользователь: решить, кому передать каждое новое сообщение.
Например, пользователь только что сгенерировал картинку и вдруг говорит: «Сделай то синим».
«То» может означать последнюю сгенерированную картинку, а может — материал из какой-то давней задачи с постерами.
Если принадлежность определена неверно, в лучшем случае появится дублирующая задача, в худшем — агент изменит не ту работу.
Поэтому точность маршрутизации сообщений напрямую влияет на надёжность всей системы. Мы не хотели, чтобы для каждого сообщения модель заново угадывала принадлежность, и приняли довольно простой принцип: всё, что можно определить по явным связям, обрабатывает код; только при отсутствии явной принадлежности решение передаётся модели для семантического анализа.
3.1 Детерминированная и семантическая маршрутизация
Приём сообщений в Lomo Agent можно условно разделить на три этапа.
Первый уровень: прямая маршрутизация при явной цели.
Когда пользователь отвечает в панели конкретной задачи, нажимает кнопку действия на карточке задачи или правит работу, явно привязанную к задаче, система уже знает, к какой цели относится сообщение, и может доставить его напрямую по Task ID или по связи с объектом.
В такой ситуации ещё один вызов модели для определения принадлежности только добавил бы задержку и риск ошибки.
Второй уровень: семантический анализ в Router.
Для сообщений из основного диалога без явной принадлежности Router принимает решение, учитывая текущий запрос, недавние взаимодействия и индекс существующих задач.
Возможные исходы:
-
Простой запрос, который Main обрабатывает сам.
-
Лёгкая задача, где нужна всего одна генерация.
-
Уточнение, которое можно привязать к одной из существующих задач.
-
Новая цель, для которой нужно создать полноценную задачу.
-
Явная неоднозначность в принадлежности, требующая уточнения у пользователя.
Есть и ещё один момент, который легко упустить: Router не нужно читать полную историю всех задач. Обычно достаточно цели задачи, краткого описания, последнего состояния и ссылок на связанные объекты, а уже после выявления кандидатов можно по необходимости прочитать подробности.
Так маршрутизация с ростом числа задач не превращается в ещё один бесконечно разбухающий контекст.
Сейчас мы в первую очередь даём Router недавно активные задачи — их цели, краткие описания, статусы и другую индексную информацию; если пользователь вспоминает о более ранней работе, система ищет по ключевым словам и векторному сходству кратких описаний и передаёт Router на решение небольшое число релевантных кандидатов. Благодаря этому рост числа задач влияет прежде всего на область поиска, а не на длину контекста, который нужно передавать при каждом инференсе.
Есть и ещё одна распространённая ошибка — принять работу, которую пользователь хотел продолжить, за новую цель. Поэтому перед созданием полноценной задачи система дополнительно проверяет её на сходство с 50 последними задачами в работе. Если обнаружена цель, которая может оказаться дублем, несколько наиболее похожих кандидатов передаются модели, и она решает, продолжить ли существующую задачу или подтвердить создание новой. Такая проверка отсекает часть дублирующих задач, но не заменяет уточнения во всех случаях смысловой неоднозначности. Когда данных недостаточно, по-прежнему нужно, чтобы пользователь сделал выбор явно.
Третий уровень: проверка принадлежности и состояния перед выполнением.
Даже после определения адресата системе нужно проверить, действительна ли задача, может ли она принять текущее действие и доступны ли ещё связанные объекты.
Например, пользователь может сослаться на уже архивированную задачу или попытаться изменить удалённый материал. То, что Router правильно определил смысловую принадлежность, ещё не значит, что последующее действие удастся выполнить.
flowchart TD
M["Получено сообщение пользователя или событие взаимодействия"] --> A{"Есть явный идентификатор задачи?"}
A -->|Да| V["Проверка существующей цели и прав доступа"]
A -->|Нет| B{"Есть явная связь с объектом?"}
B -->|Да| V
B -->|Нет| R["Семантический анализ в Router"]
R --> Q["Простой запрос: Main обрабатывает сам"]
R --> L["Одношаговая генерация: создание лёгкой задачи"]
R --> E["Существующая цель: привязка к имеющейся задаче"]
R --> N["Самостоятельная цель: подготовка к созданию полноценной задачи"]
R --> C["Неоднозначность: запрос подтверждения у пользователя"]
N --> S{"Найден дубль среди задач в работе?"}
S -->|Да| H["Передать похожих кандидатов модели на решение"]
H -->|Привязать к существующей задаче| E
H -->|Подтвердить создание новой| CREATE["Создание полноценной задачи"]
S -->|Нет| CREATE
CREATE --> NV["Проверка новой задачи и прав на операцию"]
L --> NV
NV --> I["Создать состояние выполнения и доставить входные данные"]
E --> V
V --> D{"Состояние существующей цели действительно?"}
D -->|Да| T["Запись в Inbox соответствующей задачи"]
D -->|Нет| F["Сообщить пользователю или выбрать цель заново"]
C --> U["Повторный анализ после ответа пользователя"]
U --> R
Рис. 4. Процесс маршрутизации сообщений в Lomo Agent.
3.2 Доставлено — не значит выполнено
У маршрутизации сообщений есть ещё одна легко упускаемая деталь: назначение сообщения какому-то Worker и фактическое начало его обработки этим Worker могут происходить в разное время.
Например, агент генерирует видео, а пользователь вдруг добавляет: «Фон сделай в помещении».
В этот момент запрос на генерацию видео, возможно, уже отправлен вышестоящему сервису, и изменить его сразу нельзя. Системе нужно сначала сохранить новую инструкцию, а когда Worker дойдёт до подходящей границы выполнения, решить, что делать дальше, с учётом текущего прогресса.
Поэтому для каждой задачи мы ведём персистентный ящик входящих сообщений (Inbox). Router отвечает за доставку сообщения в нужный Inbox, а среда выполнения решает, когда его прочитать, как встроить в контекст и нужно ли корректировать текущий план.
Здесь нужно различать несколько случаев:
-
сообщение, которое Worker ещё не взял в работу, при соблюдении условий можно отозвать или заменить;
-
сообщение, которое уже попало в инференс модели, ждёт текущей границы выполнения;
-
уже отправленный внешний запрос на генерацию сейчас нельзя напрямую отозвать. Новая инструкция ждёт, пока Worker дойдёт до подходящей границы выполнения, и уже тогда по прогрессу задачи решается, генерировать ли заново или применить новые ограничения на следующих шагах.
Такое разделение заодно закладывает основу для восстановления после прерывания, асинхронных задач и вмешательства пользователя в любой момент.
3.3 Что мы позаимствовали у продуктов для совместной работы
В дизайне взаимодействия мы также ориентировались на то, как организованы сообщения в Slack, Feishu и других продуктах для совместной работы.
Возьмём треды (Thread): когда пользователь отвечает в конкретном треде, система естественным образом знает, к какому обсуждению относится сообщение, и ей не нужно заново анализировать весь чат. Преимущество такой явной связи — стабильность, предсказуемость и почти нулевая стоимость семантического распознавания.
Однако у Lomo Agent сценарии взаимодействия гибче. Пользователь может ответить прямо в карточке задачи, а может вернуться в основной диалог и мимоходом упомянуть работу, начатую несколько дней назад.
Поэтому мы оставили оба способа: явные действия с задачами маршрутизируются напрямую по структурированному идентификатору, а естественный язык в основном диалоге Router связывает с прошлыми задачами.
Такая гибридная стратегия сокращает число случаев, когда модель участвует в маршрутизации, но полностью неоднозначность не устраняет. Например, пользователь делает сразу две серии постеров, а пишет только «поправь ту, прошлую», — у системы может не хватить данных, чтобы определить цель. В таком случае спросить обычно надёжнее, чем самостоятельно выбрать задачу, которая кажется наиболее подходящей.
С ростом числа задач баланс между полнотой (recall), точностью маршрутизации и стоимостью взаимодействия ещё предстоит оттачивать на реальном использовании.
Впрочем, определение принадлежности — не последний рубеж проверки во всей цепочке. Лишнюю задачу обычно можно объединить с другой или закрыть; а вот если сообщение по ошибке привязано к правке работы, удалению материала или платной генерации, цена может оказаться куда выше. Поэтому мы отделили маршрутизацию сообщений от авторизации фактического выполнения: первая решает, какой задаче передать работу, а вторая всё равно независимо проверяет права доступа и границы расходов.
Например, необратимые операции — удаление материалов, забывание воспоминаний — каждый раз требуют подтверждения пользователя; для платной генерации сначала показывается стоимость, а подтверждение запрашивается, если превышен лимит без подтверждения, дневной лимит без подтверждения или уже одобренный бюджет плана. Платные операции, запущенные через открытый API, тоже подтверждаются поштучно. Даже если Router выбрал правильную задачу, это не даёт ему права на выполнение сверх полномочий, выданных пользователем.
Даже когда сообщение попало в нужную задачу, выполнение может растянуться на минуты и даже дни и столкнуться с перезапуском процесса, сетевым таймаутом или отменой пользователем. За надёжное восстановление отвечает механизм устойчивого выполнения.
4. Устойчивое выполнение: восстановление после прерывания, повторы и управление конкурентностью
После того как сообщение правильно направлено в задачу, нужно обеспечить надёжность самого процесса выполнения.
Обычный запрос Chat Completion, как правило, укладывается в интервал от нескольких секунд до нескольких десятков секунд. Но рабочий процесс творческого агента гораздо сложнее: создание одного видео может включать несколько раундов инференса модели, десятки вызовов инструментов и несколько внешних задач генерации, каждая из которых длится по несколько минут. При этом пользователь в любой момент может изменить требования или сам остановить задачу.
Рассмотрим очень типичный сценарий сбоя: агент отправил запрос в сервис генерации видео, вышестоящий сервис фактически успешно создал задачу, но процесс нашего сервиса перезапустился как раз перед получением ответа.
Что делать после восстановления? Если просто повторить запрос, можно сгенерировать видео дважды и дважды списать оплату; если считать, что запрос прошёл успешно, можно вечно ждать задачу, которая на самом деле так и не была создана.
Подобные проблемы заставили нас понять: процессу выполнения агента нужно персистентное состояние, не зависящее ни от контекста модели, ни от процесса сервиса.
Поэтому я внедрил в Lomo Agent механизм Durable Execution (устойчивого выполнения). База данных хранит ключевые состояния выполнения, решения модели и результаты вызовов инструментов; очередь задач отвечает за планирование и восстановление выполнения; события в реальном времени сообщают пользователю о прогрессе. Даже если процесс выполнения прервётся, система по сохранённым фактам сможет определить, что делать дальше.
В дизайне мы опирались на подход Durable Execution в Temporal. Temporal с помощью истории воркфлоу, планирования задач и механизма повторов позволяет бизнес-процессам переживать жизненный цикл отдельного процесса, и особенно ярко это проявляется в сложных, долго работающих воркфлоу.
Однако у универсального движка воркфлоу есть свои издержки на моделирование и эксплуатацию. Процесс выполнения Lomo Agent должен вдобавок управлять инференсом модели, динамическими вызовами инструментов, состоянием творческой работы и взаимодействием с пользователем, поэтому мы реализовали журнал выполнения и механизм планирования событий поверх собственных структур данных агента. Мы заимствовали инженерную идею устойчивого выполнения, а не взяли готовое решение Temporal.
4.1 Сначала сохранить решение, затем вызывать инструменты
Традиционный Agent Loop обычно устроен просто:
прочитать контекст → инференс модели → вызвать инструмент → получить результат → продолжить рассуждение.
Для коротких задач такая реализация довольно прямолинейна. Но когда выполнение охватывает несколько процессов, внешние сервисы и биллинг, прерывание на любом шаге может привести к несогласованному состоянию.
Мы разбиваем выполнение на последовательность шагов (Step). Каждый Step может включать один инференс модели и набор вызовов инструментов, которые модель предложила.
Перед выполнением инструментов среда выполнения сначала сохраняет решение модели и ожидающие выполнения вызовы. У каждого вызова инструмента есть стабильный бизнес-идентификатор, а в журнале выполнения записываются его состояние, входные данные и результат.
Благодаря этому Worker при восстановлении может по журналу понять, какие операции уже завершены, какие можно повторить, а для каких сначала нужно сверить внешнее состояние, — без повторного вызова модели ради того же самого раунда решений об инструментах.
flowchart TD
S["Чтение Inbox и текущего состояния выполнения"] --> L["Инференс модели: ответ и намерения вызвать инструменты"]
L --> P["Атомарная фиксация результата модели и ожидающих вызовов"]
P --> X{"Есть сохранённый результат вызова?"}
X -->|Да| REUSE["Переиспользовать имеющийся результат"]
X -->|Нет| K{"Мог ли вызов уже выполниться раньше?"}
K -->|Нет| EXEC["Выполнить вызов инструмента"]
K -->|Да| I{"Поддерживается идемпотентный повтор?"}
I -->|Да| EXEC
I -->|Нет| UNKNOWN["Пометить результат как неизвестный и вернуть модели"]
EXEC --> SAVE["Сохранить результат инструмента и бизнес-состояние"]
REUSE --> NEXT["Перейти к следующему вызову или Step"]
SAVE --> NEXT
UNKNOWN --> CHECK["Сначала запросить текущее состояние, затем решить, повторять ли"]
CHECK --> NEXT
Рис. 5. Процесс устойчивого выполнения агента.
Здесь ключевое понятие — идемпотентность (Idempotency).
Допустим, пользователь одобрил генерацию видео, а система из-за сетевого таймаута отправила тот же запрос повторно. В идеале обе отправки должны соответствовать одному и тому же заказу на генерацию: никаких двух одинаковых видео и уж тем более двойного списания.
Для нижестоящих сервисов, поддерживающих ключи идемпотентности, можно дедуплицировать запросы по стабильному идентификатору вызова; для внутренних заказов и биллинга платформы повторный запрос не даст нового бизнес-эффекта благодаря ID вызова, ID заказа и ограничениям уникальности.
Но если вышестоящий API генерации не поддерживает ключи идемпотентности и не позволяет запросить результат, то после сетевого таймаута система фактически не может убедиться, была ли задача создана.
В этом случае устойчивое выполнение не может из ничего обеспечить гарантию Exactly-once (ровно одно выполнение).
Здесь нужно различать два случая. Для немногих неидемпотентных инструментов, если в журнале выполнения уже записано started, а результата так и нет, среда выполнения не станет выполнять вызов заново, а передаст модели результат с пометкой outcome_unknown, чтобы она сначала проверила текущее состояние и затем решила, что делать дальше. Вызовы, поддерживающие идемпотентность, можно воспроизвести повторно с прежним идентификатором вызова.
Есть и ещё один уровень неопределённости — между сервисом генерации и вышестоящим API. Здесь мы выбрали более консервативную стратегию: если результат отправки неясен, эта отправка считается неудачной, и повторный запрос к вышестоящему сервису автоматически не отправляется. Оба случая вроде бы называются «результат неизвестен», но в первом речь идёт о восстановлении вызовов инструментов агента, а во втором — о том, чтобы не создать повторно задачу генерации, за которую реально списываются деньги.
Temporal сталкивается с той же проблемой при повторах Activity. Движок воркфлоу может надёжно планировать и повторять выполнение, но побочные эффекты внешних API всё равно требуют поддержки со стороны бизнес-механизмов идемпотентности.
Для платных сервисов генерации эта граница особенно важна. Сбой выполнения можно восстановить, а вот двойное списание далеко не всегда удаётся исправить автоматически.
4.2 Перехват задачи другим Worker и Fencing Token
Устойчивое выполнение решает проблему восстановления после прерывания процесса, но есть и более коварная ситуация: прежний Worker мог на самом деле и не завершиться.
Например, Worker A во время выполнения временно теряет связь из-за сетевых сбоев. Система фиксирует таймаут, задачу перехватывает Worker B и продолжает выполнение. Через несколько секунд Worker A восстанавливает соединение и пытается зафиксировать только что полученный результат.
В этот момент в системе могут одновременно оказаться два Worker, каждый из которых считает, что право на выполнение принадлежит ему. Без ограничений старый Worker может перезаписать новое состояние или даже вернуть в процесс выполнения уже остановленную задачу.
Для этого мы используем механизм поколений выполнения по типу Fencing Token (ограждающего токена).
Каждый раз, когда задачу перехватывает новый исполнитель, среда выполнения увеличивает поколение выполнения этой задачи. Фиксируя изменение состояния, Worker обязан передать поколение, которым владеет, и оно проверяется.
Если поколение в базе данных уже обновилось, фиксация от старого Worker отклоняется.
Этот механизм можно понимать так: система разрешает новому исполнителю перехватить работу и одновременно лишает старого исполнителя права и дальше менять внутреннее состояние.
Когда пользователь сам останавливает задачу, проверка состояния выполнения и поколения тоже нужна — чтобы старый Worker после восстановления не продолжил работу.
Однако Fencing Token защищает согласованность состояния только самой системы. Если старый Worker уже отправил внешней платформе запрос на генерацию видео, утрата внутреннего права на выполнение не отменит удалённую операцию автоматически.
Поэтому даже после того, как пользователь остановил задачу, ранее отправленная внешняя генерация может продолжать работать. Сервис генерации продолжит отслеживать её результат и проведёт соответствующие расчёты по оплате, но запоздавшее событие о завершении не разбудит остановленную задачу и не изменит уже отменённое состояние выполнения.
4.3 Как обрабатывать новые сообщения пользователя во время выполнения
Ещё одна группа проблем связана с вмешательством пользователя в реальном времени.
Допустим, агент вызывает модель, собираясь сгенерировать серию постеров. В этот момент пользователь пишет: «Фон не белый, сделай тёмно-синий».
Сообщение может дойти до сервера ещё до окончания инференса, но текущий запрос к модели его на самом деле не прочитал.
Если система собирает историю просто по времени создания сообщений, возникает путаница состояний: выглядит так, будто модель приняла решение уже после изменения требований, хотя на самом деле она вообще не знала о новой инструкции.
Чтобы решить эту проблему, мы разделили приём сообщений и их потребление.
Новое сообщение сначала попадает в персистентный Inbox соответствующей задачи. На следующей безопасной границе выполнения Worker читает эти входные данные и вместе с текущим состоянием задачи встраивает их в контекст модели.
Система фиксирует, на каком Step сообщение было фактически потреблено, и не полагается только на время отправки для определения порядка.
Так можно чётко разделить этапы: инструкция пользователя доставлена, модель её поняла, связанное действие выполнено.
Разумеется, если запрос на генерацию уже отправлен вышестоящему сервису, полученная позже правка не изменит автоматически то, что сейчас генерируется. В текущей версии такой запрос нельзя и отозвать: Worker может лишь на следующей безопасной границе выполнения решить, генерировать ли заново или применить новые ограничения на следующих шагах. Если пользователь уже остановил задачу, старая генерация, даже завершившись позже, не заставит задачу снова начать выполнение.
Интерфейс тоже должен точно отражать это состояние, чтобы пользователь не путал «сообщение получено» с «текущая генерация уже скорректирована по новым требованиям».
Для долгих генераций — видео, аудио, 3D — Worker должен ещё и освобождать ресурсы выполнения на время ожидания внешнего результата и точно возобновлять работу, когда придёт событие.
4.4 Журнал выполнения и границы согласованности планирования
Выше мы обсудили персистентность решений модели и то, как поколение выполнения не даёт старому Worker продолжать менять состояние после перехвата задачи. Но на практике есть ещё одна легко упускаемая проблема: если в базе данных уже записана работа, ожидающая выполнения, а сообщение в очереди, которое должно разбудить Worker, потерялось, как восстановить задачу?
В Lomo Agent состояние выполнения поддерживается через раунды (Turn) и журнал вызовов инструментов. Раунд хранит состояние выполнения, поколение выполнения, аренду (lease) и контрольную точку восстановления (Checkpoint), а у вызова инструмента стабильный идентификатор вида «раунд + номер шага + порядковый номер вызова». Когда модель возвращает ответ, среда выполнения в одной транзакции сохраняет результат решения, ожидающие выполнения вызовы и уже потреблённые сообщения Inbox; перед выполнением инструмента записывается started, а после завершения результат сохраняется вместе с состоянием done. Благодаря этому при восстановлении можно по имеющимся записям переиспользовать результат или безопасно повторить вызов, не заставляя модель заново догадываться, что происходило раньше. Для вызовов, которые нельзя безопасно повторить, сохраняется состояние «результат неизвестен», и решение откладывается на следующие шаги.
Для планирования задач мы не стали вводить транзакционный Outbox при постановке раундов в очередь: источником фактов о выполнении служит база данных, а очередь в основном отвечает за пробуждение Worker. Сообщение планирования отправляется после фиксации раунда; если постановка в очередь не удалась, фоновый сторожевой процесс (watchdog) заново обнаружит задачу по состоянию в базе данных. Сейчас watchdog проверяет состояние каждые 10 секунд и прежде всего следит за истёкшими арендами, долгим ожиданием в очереди и задачами, у которых есть необработанные сообщения, но нет активного раунда. Задания в очереди идентифицируются ID раунда и поколением выполнения, что сокращает повторную постановку в очередь в рамках одного поколения; даже если планирование всё же повторится, среда выполнения не допустит устаревшей записи благодаря проверке состояния выполнения и поколения. Это значит, что мы принимаем ограниченную задержку восстановления в обмен на более простой механизм планирования; push-события интерфейса и внешние задачи генерации при этом сохраняют собственные механизмы надёжной доставки и компенсации.
Другой источник риска — параллельное выполнение старого и нового Worker. Fencing Token не даёт старому Worker перезаписать внутреннее состояние, но не может отменить уже отправленные внешние запросы. Поэтому ключ идемпотентности платной генерации мы зафиксировали как «раунд + номер шага + порядковый номер вызова», без поколения выполнения. Даже если старый и новый Worker отправят один и тот же вызов повторно, слой генерации платформы благодаря ограничению уникальности «рабочее пространство + ключ идемпотентности» свяжет их с одним и тем же заказом и дедуплицирует внутреннее списание. Так можно контролировать дублирующие заказы и биллинг внутри платформы, но это не значит, что внешний вышестоящий сервис сам по себе обладает гарантией Exactly-once. Сбой же до того, как решение модели сохранено, может привести к повторному инференсу этого шага. Учитывая нынешнюю структуру затрат, где основную долю составляет генерация медиа, мы решили принять эти относительно ограниченные расходы на повторный инференс.
Сложнее всего — случаи, когда результат отправки запроса вышестоящему сервису неясен. Таймаут может означать, что отправка не удалась, а может — что вышестоящий сервис уже создал задачу, но ответ не дошёл. Чтобы избежать повторной генерации, текущий сервис генерации не отправляет такие запросы повторно автоматически; получив ID задачи от вышестоящего сервиса, он сразу сохраняет его, чтобы после прерывания продолжить опрос. Но если процесс упадёт ровно между получением ID и его сохранением, найти эту задачу обратным запросом пока нельзя. Системе остаётся лишь после таймаута оформить возврат по правилам, а возможные уже возникшие расходы у вышестоящего сервиса берёт на себя платформа.
Эти решения не устраняют все окна отказов, но чётко задают границы обработки в разных ситуациях: то, что подтверждённо завершено, переиспользуется напрямую; то, что можно безопасно повторить, выполняется дальше; то, что подтвердить нельзя, не отправляется повторно вслепую. Для агента, работающего непрерывно, надёжность означает не то, что ни один шаг не может сбоить, а то, что после прерывания система способна принять контролируемое решение о восстановлении на основе имеющихся фактов.
5. Асинхронная генерация: жизненный цикл задач и пробуждение по событиям
Описанное выше устойчивое выполнение решает в основном вопросы того, как агент фиксирует состояние, обрабатывает сбои и восстанавливает выполнение. Но в AI-творчестве есть ещё одна неизбежная особенность: значительная часть вызовов инструментов асинхронна по своей природе.
Например, генерация изображения может занять всего несколько секунд, а задачи с видео и 3D — несколько минут и дольше. После отправки задачи вышестоящий сервис обычно возвращает только ID задачи, а сам результат приходится получать через опрос (polling) или колбэк.
Если заставить Worker всё время ждать завершения внешней задачи, он не только займёт ресурсы выполнения, но и свяжет инференс модели с неконтролируемой длительностью внешней генерации.
Поэтому мы управляем жизненным циклом выполнения агента и жизненным циклом внешних задач генерации раздельно.
5.1 Отправка, ожидание и возобновление выполнения
Когда Worker вызывает инструмент генерации, инструмент сначала отправляет запрос в соответствующий сервис и возвращает идентификатор задачи генерации. Затем среда выполнения сохраняет связь между этой задачей и текущей задачей агента: ID задачи, состояние ожидания и всё, что понадобится для дальнейшего выполнения.
После отправки Worker может приостановить текущее выполнение — ему не нужно постоянно вызывать модель или держать открытым долгий запрос на инференс.
Во время ожидания изменения прогресса генерации можно напрямую передавать фронтенду через бизнес-события, а карточка задачи покажет текущее состояние.
Когда задача завершится, среда выполнения решит, будить ли Worker снова, — в зависимости от того, зависят ли последующие шаги от результата генерации.
sequenceDiagram
participant W as Task Worker
participant G as Сервис генерации
participant S as Устойчивая среда выполнения
participant UI as Карточка задачи
W->>G: Отправить запрос на генерацию (стабильный бизнес-идентификатор)
G-->>W: Вернуть ID задачи
W->>S: Сохранить связь с задачей и состояние ожидания
Note over W,S: Выше — нормальный путь, сбой сохранения после отправки см. в 4.4
S-->>UI: Статус «генерируется»
Note over W: Worker приостановлен, ресурсы выполнения освобождены
G-->>S: Событие прогресса генерации
S-->>UI: Обновить прогресс (без инференса модели)
G-->>S: Событие завершения генерации
S->>S: Обновить состояние задачи и сохранить результат
S-->>UI: Показать результат генерации
alt Последующие шаги зависят от результата
S->>W: Отправить событие возобновления выполнения
W->>W: Прочитать актуальное состояние и продолжить
else Дальнейший инференс сейчас не нужен
Note over UI: Обновляются только результат и статус задачи
end
Рис. 6. Процесс выполнения асинхронной задачи генерации (нормальный путь).
Прямая выгода такого дизайна — более эффективное использование ресурсов. Пока несколько минут генерируется видео, системе нужно лишь хранить персистентное состояние и обрабатывать необходимые события, не занимая ресурсы инференса агента.
Кроме того, прогресс генерации и интерфейс пользователя могут обновляться независимо. Пользователь видит состояние задачи в реальном времени, не дожидаясь, пока Worker снова вызовет модель.
5.2 Асинхронные события тоже нужно обрабатывать надёжно
Переведя задачи генерации на событийную модель, нужно позаботиться и о надёжности самих событий.
Например, вышестоящий сервис может из-за сетевых проблем повторно прислать уведомление о завершении или после неудачного колбэка не суметь сразу повторить его. А когда параллельно идёт несколько генераций, события прогресса могут приходить не по порядку.
Поэтому обработку событий нельзя понимать упрощённо как «пришёл колбэк — продолжаем выполнение».
Обновлять состояние задачи генерации нужно, опираясь на её персистентное состояние, и гарантировать, что повторные события не приведут к повторной записи, повторному списанию или повторному пробуждению Worker.
Для событий, которые могут прийти не по порядку, нужно также не допустить, чтобы старый прогресс перезаписал итоговый результат. Например, если задача уже завершена, а следом пришло запоздавшее уведомление «генерируется», состояние не должно откатываться назад.
Кроме того, внешние колбэки не всегда надёжны. Для задач, по которым долго нет результата, можно сверять состояние по ID задачи через API запроса вышестоящего сервиса и при необходимости восстанавливать прогресс компенсирующим опросом.
Здесь всё работает в связке с описанным ранее Durable Execution: среда выполнения поддерживает достоверное состояние выполнения, а система асинхронных задач превращает изменения во внешней генерации в бизнес-события, после которых можно восстановиться.
5.3 Когда нужно будить Worker?
Есть и вопрос, напрямую связанный со стоимостью вызовов модели: нужно ли после каждого завершения генерации заново вызывать модель?
Ответ зависит от того, осталась ли в задаче работа, требующая решения агента.
Например, пользователь просто попросил «сгенерировать картинку с котом». После генерации системе достаточно показать результат — будить Worker, чтобы модель пересказывала уже готовую картинку, незачем.
Но если пользователь просит «сделать серию брендовых постеров в едином стиле», текущий результат может стать входными данными для следующих шагов. Агенту нужно проверить уже готовые результаты, прочитать чек-лист выполнения и, исходя из текущей цели, решить, генерировать ли дальше или ждать подтверждения пользователя.
Поэтому событие завершения генерации и решение агента о продолжении работы мы обрабатываем раздельно.
Сервис генерации сообщает о результате; среда выполнения по плану задачи, связям ожидания, авторизации и текущему состоянию выполнения решает, нужно ли возобновлять Worker.
Это также избавляет от необходимости отдавать модели массу событий прогресса. Видеозадача длиной в несколько минут может породить много изменений состояния на стороне вышестоящего сервиса, но подавляющему большинству из них достаточно обновить фронтенд — запускать ради них новый инференс не стоит.
Такой событийный дизайн близок к механизмам асинхронного ожидания в универсальных движках воркфлоу и очередях задач. В Lomo Agent мы дополнительно думали о том, как связать его с творческими целями, чек-листом выполнения и процессом проверки работ пользователем.
Например, когда сгенерирован один видеокадр, а впереди ещё другие кадры и пользователь уже разрешил продолжать, среда выполнения может возобновить соответствующую задачу; если же текущий этап требует проверки пользователем, задача должна оставаться в состоянии ожидания подтверждения.
Завершение генерации означает лишь то, что закончился один вызов инструмента. Продолжать ли всю творческую задачу, нужно решать с учётом плана, бюджета и авторизации.
Отсюда вытекает и механизм самостоятельного продвижения, о котором пойдёт речь дальше: как система определяет, когда агент завершил этап или даже готовится закончить текущий раунд рассуждений, действительно ли задача выполнена и стоит ли продолжать.
6. Контекстная инженерия: как сохранить актуальное состояние в ограниченном окне
Когда агент берётся за долгие многоэтапные творческие задачи, управление контекстом становится важным фактором, влияющим на качество выполнения и стоимость.
Творческая задача, которая длится несколько дней, может накопить сотни сообщений, десятки вызовов инструментов и массу состояний изображений, видео и воркфлоу. Даже при всё больших контекстных окнах моделей запихивать всё это в каждый инференс нереалистично.
С одной стороны, длинный контекст означает более высокую стоимость в токенах и задержку инференса; с другой — избыток истории может сбивать модель при оценке текущей цели. Особенно когда пользователь много раз менял требования: если давно утратившие силу ограничения всё ещё лежат в контексте, они легко влияют на последующую генерацию.
Поэтому мы рассматриваем управление контекстом как отдельную инженерную задачу: как на каждом шаге инференса дать модели достаточную, релевантную и всё ещё актуальную информацию.
6.1 Уровни контекста и Prompt Cache
Lomo Agent организует контекст модели в несколько уровней: относительно стабильный системный префикс, сжатая история, недавние взаимодействия и динамическое состояние, нужное для текущего шага выполнения.
flowchart LR
A["Стабильный префикс: системные правила / ключевые инструменты / снимок профиля"] --> B["Посегментные сводки истории"]
B --> C["Недавние сообщения и ключевые результаты инструментов"]
C --> D["Динамический хвост: бриф / чек-лист / бюджет / изменения объектов"]
D --> E["Вход текущего шага и извлечение по требованию"]
Рис. 7. Организация контекста в Lomo Agent.
Здесь есть фактор, тесно связанный с реальной стоимостью инференса: Prompt Caching (кэширование промптов). Далее под Prompt Cache понимается возможность кэширования промптов, которую провайдер модели предоставляет на уровне API; конкретная реализация и то, что именно можно переиспользовать, определяет сам провайдер.
Сервисы моделей, поддерживающие эту возможность, могут переиспользовать префиксы промптов, удовлетворяющие их правилам кэширования, и снижать затраты на повторную обработку. Поэтому мы стараемся держать системные правила, ключевые инструменты и относительно стабильные данные профиля в начале контекста, а состояние задачи, которое может меняться на каждом шаге, — в конце.
Если вставить сильно меняющуюся информацию в начало префикса, то и идущее после неё стабильное содержимое может перестать эффективно попадать в кэш.
Впрочем, реальный процент попаданий в Prompt Cache зависит ещё и от провайдера модели, маршрутизации запросов и стратегии кэширования. Раскладка контекста лишь создаёт условия для переиспользования кэша, а итоговую выгоду нужно проверять на реальных данных вызовов.
Кроме того, стабильный префикс не означает, что профиль можно долго не обновлять. Когда пользователь меняет предпочтения или просит что-то забыть, соответствующий снимок профиля должен своевременно инвалидироваться, чтобы переиспользование кэша не позволяло устаревшим данным и дальше участвовать в инференсе.
6.2 Загрузка инструментов по требованию: чем больше возможностей, тем дороже контекст
В Lomote AI интегрированы разнообразные творческие модели и инструменты из разных областей: генерация изображений и видео, 3D, управление материалами, редактирование документов, воркфлоу Flows и другое.
Если в каждый запрос к модели помещать названия, параметры и описания всех инструментов, это не только увеличит расход токенов, но и может заставить модель ошибиться при выборе среди множества похожих инструментов.
Поэтому мы делим инструменты на две категории: ключевые, которые должны быть видны всегда, и те, что подгружаются динамически по областям возможностей.
Например, пока агент занимается раскадровкой, ему могут понадобиться только инструменты для документов и материалов; на этапе создания видео подгружаются генерация видео и возможности воркфлоу.
Для областей инструментов, которые больше не активны, на границе сжатия контекста можно удалять подробные определения, уменьшая влияние нерелевантной информации на дальнейший инференс.
У такого дизайна есть цена. Когда набор инструментов меняется, может меняться и структура контекста модели, что бьёт по попаданиям в Prompt Cache; а вызовы инструментов, уже присутствующие в истории, могут не вполне совпадать с видимыми сейчас определениями.
В этом вопросе практика Context Engineering в Manus дала нам контрпример, над которым стоит подумать.
Manus предпочитает держать определения инструментов стабильными и снижать сложность выбора, ограничивая доступные в данный момент действия. Это помогает уменьшить инвалидацию префикса из-за изменения определений инструментов и упрощает согласованность между историческими вызовами инструментов и текущим контекстом. Речь здесь о том, как стабильный контекст влияет на переиспользование кэша; Prompt Caching на уровне API не отождествляется с механизмом KV Cache внутри инференса модели.
Lomo Agent выбрал загрузку по областям в первую очередь потому, что типов инструментов и структур параметров на творческой платформе довольно много, и постоянное хранение всех определений может обходиться слишком дорого с точки зрения контекста.
У каждой стратегии свои условия применимости: стабильный набор инструментов лучше для кэша и согласованности поведения, а динамическая загрузка может снизить расход токенов и помехи при выборе, которые создают нерелевантные инструменты.
Реальный эффект нашего подхода нужно постоянно оценивать по частоте смены инструментов, проценту попаданий в кэш, точности вызова инструментов и общей стоимости инференса. Кроме того, присутствие инструмента в контексте модели влияет лишь на то, что модель может выбрать в данный момент. Реальные права на выполнение инструментов по-прежнему контролирует сервер, и видимость инструмента в промпте нельзя считать границей безопасности.
6.3 Сжатие истории: помимо сводок — возможность перечитать
При долгом выполнении история сообщений и результаты инструментов постоянно растут. Даже с большим контекстным окном на определённом этапе нужно сжатие.
Обычно модель раз за разом суммирует предыдущую историю, а затем продолжает диалог с последней сводкой. Но у такого подхода есть проблема: при многократном сжатии сводка постепенно теряет детали и даже может закрепить собственные догадки модели как исторические факты.
Поэтому мы используем посегментные сводки: раннюю историю сжимаем по интервалам, а недавние взаимодействия сохраняем полностью.
Новые сводки по возможности дописываются к уже существующей последовательности сводок, чтобы не переписывать всю историю каждый раз.
Для объёмных выводов инструментов — списков материалов, информации об узлах воркфлоу или сложных результатов генерации — система может оставлять в текущем контексте лишь необходимую сводку, а полное содержимое хранить во внешнем хранилище.
Когда агенту позже понадобится проверить конкретный параметр или деталь истории, нужная информация восстанавливается через поиск или постраничное чтение.
Это перекликается с идеей Manus использовать файловую систему как внешний носитель контекста. Модели не нужно постоянно носить с собой всю информацию, но она должна уметь при необходимости снова найти надёжный первоисточник.
Здесь важно одно различие: сводка нужна, чтобы снизить нагрузку на контекст текущего инференса, и не должна становиться единственным источником исторических фактов. Особенно когда пользователь исправил какую-то информацию или какое-то прежнее ограничение утратило силу — система должна уметь найти соответствующий источник, чтобы старая сводка не продолжала влиять на дальнейшее выполнение.
6.4 Текущая задача всегда должна оставаться в поле зрения модели
Сжатие контекста решает проблему слишком длинной истории, но не менее важен другой вопрос: помнит ли агент на полпути, какую цель он вообще выполняет?
В долгих задачах модель после многих раундов вызовов инструментов может постепенно отклониться от исходной цели. Например, пользователь просит серию брендовых постеров, а агент тратит массу сил на бесконечную доводку одного референса и забывает об итоговом результате в целом.
Поэтому в динамическом контексте Worker мы постоянно предоставляем актуальный бриф, чек-лист выполнения, ключевые ограничения, состояние ожидания и последние изменения связанных работ.
Эти состояния берутся из устойчивой среды выполнения, а не просто из пересказа моделью предыдущей переписки.
Например:
-
бриф фиксирует визуальное направление, окончательно одобренное пользователем;
-
чек-лист выполнения фиксирует, какие шаги уже сделаны, а какие ещё предстоят;
-
состояние рабочего пространства сообщает модели, не заменил ли пользователь какой-то материал и какая сейчас версия документа.
Когда пользователь напрямую правит воркфлоу Flows или общий документ, следующий раунд выполнения тоже получает уведомление об изменении объекта и при необходимости читает актуальное содержимое.
Благодаря этому, даже если часть истории уже сжата, агент может продолжать работу, опираясь на достоверное состояние задачи.
Здесь нас тоже во многом вдохновил Manus, который удерживает внимание модели на цели, постоянно выводя список дел. Lomo Agent пошёл дальше и связал такие списки с состоянием среды выполнения: они не только дают модели контекст, но и поддерживают последующее автоматическое продолжение работы и решение об остановке. Подробнее об этом — в главе о самостоятельном продвижении.
6.5 Практические компромиссы контекстной инженерии
В целом контекстная инженерия Lomo Agent строится вокруг трёх целей:
-
меньше нерелевантной информации;
-
контроль стоимости инференса;
-
гарантия, что информация, на которую опирается текущее решение, всё ещё актуальна.
Но эти цели не всегда согласуются между собой.
Более агрессивное сжатие снижает расход токенов, но может упустить важные детали. Динамическая загрузка инструментов сужает пространство выбора, но может повлиять на попадания в кэш. Долгое хранение стабильного профиля усиливает преемственность, но требует обрабатывать смену предпочтений и отзыв воспоминаний.
Поэтому управление контекстом вряд ли можно решить одной фиксированной стратегией. Его нужно настраивать вместе с состоянием задач, системой памяти, правами на инструменты и реальным поведением моделей.
Для Lomo Agent важнее всего, чтобы модель могла найти нужную информацию, когда она требуется, и продолжать работу, опираясь на актуальное состояние задачи. Но когда совместная работа охватывает несколько задач, много дней и даже месяцы, одного управления контекстом внутри задачи уже мало. Сложившиеся за долгое время предпочтения пользователя, прошлые проекты и принятые когда-то решения должны организовываться и извлекаться отдельным механизмом.
7. Инженерия памяти: источники, версии и отзываемость
В предыдущей главе мы обсуждали, как организовать информацию в ограниченном контекстном окне. Но для агента, рассчитанного на долгую совместную работу, есть вопрос глубже: что система должна запомнить, когда разговор уже вышел за пределы текущего контекста, а связанная задача давно завершена?
В творческих сценариях Lomo Agent пользователь может месяцами работать с платформой, постепенно формируя довольно устойчивые визуальные предпочтения, привычные творческие процессы и правила бренда. Не стоит каждый раз заставлять его описывать всё это заново.
В то же время предпочтения пользователя меняются. Временный стиль одного проекта не обязательно подходит для всех последующих работ; правила бренда, действовавшие раньше, могут полностью утратить силу после ребрендинга.
Поэтому мы хотим, чтобы долгосрочная память обеспечивала преемственность между задачами и во времени, сохраняя при этом источник и область действия каждого факта.
7.1 От истории сообщений к долгосрочной памяти
Первое, что должна решить долгосрочная память, — отбор информации.
Например, пользователь говорит: «Я обычно предпочитаю приглушённые цвета», — это, возможно, предпочтение, которое стоит хранить долго; а вот «на этот раз попробуем насыщенный красный» скорее всего лишь локальное требование к текущей работе.
Обе фразы касаются цвета, но область их применения разная. Если система будет записывать все сообщения пользователя прямо в векторную базу данных и подтягивать их обратно семантическим поиском, она легко перепутает долгосрочные предпочтения, временные требования и утратившую силу историческую информацию.
Поэтому мы разделили память Lomo Agent на несколько уровней:
| Уровень памяти | Что хранит | Основное назначение |
|---|---|---|
| Исходные свидетельства (Evidence) | Сообщения пользователя, источник, время и статус актуальности | Основа для извлечения фактов, исправления и отзыва |
| Атомарная память (Atomic Memory) | Предпочтения пользователя, факты о проектах, привычки и т. п. с указанием источника и версии | Извлечение по требованию между задачами |
| Эпизодическая память (Episodic Memory) | Промежуточные сводки по отрезку общения или по задаче | Помогает агенту найти прошлые проекты и их прогресс |
| Профиль пользователя (User Profile) | Небольшой объём относительно стабильной и ценной информации | Недорогой фон для повседневной совместной работы |
Эти уровни связаны между собой, но выполняют разные функции.
Исходные сообщения — это отслеживаемые свидетельства. Из них система может извлекать кандидатов в память, а затем по источнику, времени, области применения и уже имеющимся записям решать, нужно ли сохранить, дополнить или обновить запись.
Эпизодическая память больше сосредоточена на том, что происходило в ходе определённого отрезка совместной работы. Например, на прошлой неделе пользователь сделал серию брендовых постеров, в итоге выбрал вариант в тёплой гамме и решил отложить создание видео. Не всё это стоит вносить в долгосрочный профиль, но может пригодиться, когда пользователь в следующий раз вспомнит про проект.
С профилем пользователя нужно быть сдержаннее. В нём должно оставаться немного относительно стабильной информации, чтобы со временем он не превратился в ещё одну бесконечно разрастающуюся историю диалогов.
При фактическом извлечении система выбирает, какие воспоминания передать модели, исходя из цели и прав текущей задачи, и не загружает всю историю в каждом раунде.
Здесь есть ещё один важный принцип: память обязана отличать факты, явно высказанные пользователем, от догадок самой модели.
Например, агент видит, что пользователь сгенерировал подряд несколько картинок в стиле киберпанк, и делает вывод, что тот давно предпочитает киберпанк. Это может быть гипотезой, но не может приравниваться к явно высказанному предпочтению пользователя.
Точно так же содержимое веб-страниц, результаты инструментов и сводки, сгенерированные самим агентом, нельзя без надёжного основания переупаковывать в личные факты о пользователе.
7.2 Чему нас научили существующие архитектуры памяти
Проектируя долгосрочную память, мы изучили несколько разных технических подходов.
Практика памяти в OpenClaw делает упор на хранение, которое можно проверять и поддерживать, а также на связь памяти с исходным контекстом. Её сильная сторона — высокая прозрачность: пользователю или разработчику легко проверить, что именно сохранено. Но по мере роста объёма памяти для поиска, поддержки и разрешения конфликтов всё равно нужны дополнительные механизмы.
Mem0 больше сосредоточен на том, как извлекать из диалогов ценную информацию и восстанавливать нужные воспоминания поиском в последующих взаимодействиях. Такой подход подходит агентам, которые работают долго и на разные темы, но качество памяти сильно зависит от точности извлечения и стратегии поиска. Если временную инструкцию ошибочно принять за долгосрочное предпочтение, последующий поиск может лишь раз за разом усиливать ошибку.
Zep / Graphiti предлагают другой путь: явно поддерживать в памяти связи между сущностями и временную информацию. Это особенно ценно для фактов, которые постоянно меняются. Например, бренд раньше использовал синий как основной цвет, а потом перешёл на зелёный; две эти записи не обязательно противоречат друг другу — у них могут быть разные периоды действия.
Однако темпоральные связи и графы знаний добавляют и сложности в поддержке: нормализация сущностей, обновление связей, разрешение конфликтов, стоимость поиска.
С учётом творческих сценариев Lomo Agent нас больше всего волновали три вопроса:
-
Есть ли у предпочтения пользователя надёжный источник
-
Действителен ли ещё исторический факт
-
Обновляются ли связанные воспоминания, когда пользователь исправляет или отзывает информацию
Поэтому, помимо привычного многоуровневого хранения и семантического поиска, мы поддерживаем для памяти источники, версии и связи происхождения.
7.3 Почему «забыть» сложнее, чем «запомнить»
У долгосрочной памяти есть ещё одна проблема, которую легко упустить: что делать системе, когда пользователь явно просит что-то забыть?
Допустим, сначала пользователь говорит: «Основной цвет нашего бренда — синий».
Потом добавляет: «Синий в паре с молочно-белым смотрится мягче».
Система памяти может объединить оба высказывания в новое предпочтение бренда:
«Бренд предпочитает сочетание синего с молочно-белым, общий визуальный стиль тяготеет к мягкости».
Если затем пользователь попросит забыть изначальную установку про синий цвет, удалить только первую запись атомарной памяти явно недостаточно: объединённая версия всё ещё содержит эту информацию.
По мере работы системы эта информация может попасть и в эпизодические сводки, и в профиль пользователя, и даже слиться с другими производными воспоминаниями.
Поэтому для памяти мы поддерживаем связи источников и происхождения, описывающие, из каких исторических свидетельств и прежних версий выросла та или иная запись.
flowchart TD
U["Пользователь указывает, что нужно забыть"] --> A["Лишить связанные свидетельства права использоваться в памяти"]
A --> B["Найти затронутые версии воспоминаний"]
B --> C["Пройти по связям происхождения к последующим версиям"]
C --> D["Отключить затронутое содержимое и проверить оставшиеся действительные свидетельства"]
D --> E["Заново извлечь на основе оставшихся действительных свидетельств"]
E --> F["Обновить атомарную память, сводки и профиль пользователя"]
F --> G["Инвалидировать связанные поисковые индексы и кэш контекста"]
Рис. 8. Процесс отзыва воспоминаний.
На практике эволюцию воспоминаний мы поддерживаем через цепочку версий. Вернёмся к примеру с основным цветом бренда: исходное высказывание про синий образует действительную версию v1, связанную с исходным свидетельством (ORIGIN); когда пользователь добавляет требование сочетать его с молочно-белым, система создаёт новую версию v2, помечает прежнюю как SUPERSEDED и записывает новое уточняющее свидетельство (REFINE). Если позже пользователь лишь подтверждает уже сделанный вывод, это можно записать как CONFIRM, не создавая новое воспоминание.
Когда пользователь просит забыть изначальную установку про синий цвет, система лишает соответствующее свидетельство права использоваться и по связям версий находит затронутые воспоминания. Версии, зависящие от этого свидетельства, удаляются с очисткой содержимого, а затем извлекаются заново из оставшихся действительных свидетельств; свидетельство, которое лишь подтверждало вывод, можно просто отвязать, не затрагивая воспоминания, которые держатся и без него.
Этот отзыв распространяется и на профиль пользователя, и на эпизодические сводки. Когда связанные записи профиля становятся недействительными, система обновляет версию профиля, чтобы дальнейший инференс больше не использовал старый снимок; затронутые сводки нужно построить заново. Одновременно система удаляет соответствующие поисковые векторы и не даёт удалённому содержимому снова попасть в извлечение, снижая риск того, что старая информация вернётся в контекст.
Стоит учитывать, что уже отправленный запрос к модели мгновенно отозвать нельзя. Поэтому мы также отслеживаем, на какие сообщения и воспоминания опирались ответы и результаты инструментов, и в дальнейшем инференсе исключаем затронутое производное содержимое, чтобы забытая информация не вернулась через собственные прошлые ответы агента.
«Забыть» здесь означает прежде всего лишить долгосрочную память и производный от неё контекст права использоваться, а не удалить всю исходную переписку, сгенерированные материалы или данные о транзакциях. Последнее относится к другому жизненному циклу данных и вопросам управления приватностью и требует отдельной обработки.
7.4 Надёжность памяти и плавная деградация (graceful degradation)
Отслеживание источников и управление версиями делают систему памяти более управляемой, но не устраняют полностью ошибки понимания у модели.
Например, в одном фрагменте диалога может быть сразу несколько фактов-кандидатов: один подходит для сохранения, а другой — лишь неверно понятая моделью временная инструкция. Если рассматривать весь пакет извлечённых результатов как неделимое целое, одна аномалия может повлиять на остальные корректные воспоминания.
Поэтому кандидатов в память нужно проверять и обрабатывать по одному, различая случаи: источник не подтверждает, содержимое конфликтует, формат вывода некорректен и т. д.
Сбой сервиса памяти тоже не должен полностью останавливать обычные творческие задачи.
Например, при таймауте извлечения можно временно обойтись без дополнения из памяти; неудачное фоновое извлечение можно повторить позже; из-за одного кандидата с некорректным форматом не обязательно терять остальные корректные записи.
Такая стратегия плавной деградации означает, что в некоторых случаях агент не сможет использовать все исторические предпочтения, но по-прежнему продолжит работу по текущему брифу и имеющемуся состоянию выполнения.
Вместо того чтобы позволить одному проблемному результату памяти повлиять на всю задачу, мы предпочитаем сохранить нормальный путь выполнения и снова предоставить нужный фон, когда память восстановится.
Ценность долгосрочной памяти в конечном счёте должна подтверждаться реальной совместной работой. Помимо точности извлечения, нам важно, не устарели ли воспоминания, можно ли объяснить их источник и влияет ли старая информация на дальнейшие решения после того, как пользователь её исправил или отозвал.
По мере того как память и контекст задач совершенствуются, агент уже может получать фоновую информацию, накопленную за долгое время. Но знать, что пользователь делал раньше, — ещё не значит уметь решить, стоит ли продолжать текущую задачу.
Задаче из нескольких творческих шагов нужны ещё чёткий план выполнения, условия остановки и границы полномочий, выданных пользователем. Именно это и решает механизм самостоятельного продвижения из следующей главы.
8. Самостоятельное продвижение: план, условия остановки и полномочия пользователя
Описанные выше механизмы позволяют агенту вести долгие задачи, восстанавливать прерванное выполнение и при необходимости находить историческую информацию. Но для творческой цели, которую нужно планомерно продвигать, остаётся нерешённый вопрос: как агенту понять, стоит ли ему продолжать работу?
Например, пользователь просит сделать короткий имиджевый ролик для бренда. Закончив сценарий и раскадровку, агент может выдать резюме и завершить текущий раунд. Модель при этом не ошиблась — она действительно выполнила работу текущего этапа, — но до готового ролика, которого в итоге ждёт пользователь, ещё очень далеко.
На ранних демо и в сценарных прогонах мы сталкивались с подобным: пользователю приходилось снова и снова писать «продолжай», чтобы агент перешёл к следующему этапу.
С другой стороны, если просто потребовать от модели «не останавливаться, пока всё не сделано», возникает другая проблема: агент может раз за разом повторять уже провалившиеся операции, генерировать ненужные материалы или без подтверждения пользователя самовольно расширять рамки работы. Поэтому цель задачи и прогресс выполнения должны существовать независимо от отдельного ответа модели, а границы автоматического продвижения должна контролировать среда выполнения.
8.1 Превращаем план в постоянно поддерживаемый чек-лист выполнения
Мы ввели Execution Checklist (структурированный чек-лист выполнения), в котором фиксируются основные шаги творческой цели от плана до сдачи результата.
В отличие от обычного TodoList, который модель пишет в чате, чек-лист выполнения — это структурированное состояние, которое система хранит и поддерживает персистентно. Каждый шаг может быть связан с состоянием выполнения, результатами, подтверждением выполнения и требованиями к подтверждению пользователем.
Например, создание короткого имиджевого ролика можно организовать так:
| Этап выполнения | Основная работа | Критерий готовности |
|---|---|---|
| Сценарий и раскадровка | Определить повествование и дизайн кадров | Документ раскадровки и подтверждение пользователя |
| Персонажи и сцены | Подготовить визуальные референсы | Соответствующие материалы и история версий |
| Генерация кадров | Сгенерировать кадры видеомоделью | Внешние задачи генерации и их результаты |
| Проверка варианта | Проверить кадры и общее направление | Отзыв пользователя или авторизованный результат приёмки |
| Финальная сдача | Подготовить готовые к сдаче результаты | Итоговая работа и связанные материалы |
Когда пользователь утвердил план выполнения, чек-лист становится основой для дальнейшего продвижения. Worker планирует и вызывает инструменты в соответствии с текущим шагом, а среда выполнения после каждого раунда может проверять прогресс.
Важно: чек-лист выполнения — не застывший план, который нельзя менять.
Творческий процесс постоянно меняется. Например, на этапе генерации кадров пользователь решает сменить концепцию персонажа, и часть исходных шагов и результатов приходится пересматривать. Тогда системе нужно обновить план по новой инструкции пользователя и поддерживать связи между шагами и результатами.
Чек-лист выполнения обеспечивает преемственность цели и прогресса. Благодаря ему агент даже после сжатия контекста, асинхронного ожидания или восстановления процесса может найти, на каком месте остановилось выполнение текущей задачи.
8.2 Модель перестала отвечать — нужно ли продолжать задачу?
После того как план стал персистентным, нужно ответить на более конкретный вопрос: что должна делать среда выполнения, когда модель закончила текущий раунд?
Проектируя этот механизм, мы опирались на идеи Codex Goals и Claude Code Hooks.
Codex Goals связывает цель с состоянием непрерывного выполнения, позволяя агенту продолжать работу над ещё не достигнутой целью; Stop Hook в Claude Code позволяет выполнить дополнительную проверку, когда модель собирается остановиться, и по её результату решить, блокировать ли эту остановку.
Из этих двух решений я вынес следующее: то, что модель закончила один ответ, ещё не значит, что задача целиком выполнена. Среде выполнения нужны независимое состояние цели и механизм проверки перед остановкой.
Однако критерии завершённости у агента для кода и у творческого агента заметно различаются.
В задачах с кодом пройденные тесты, успешная сборка или изменение нужных файлов обычно дают довольно ясное подтверждение выполнения. У творческих задач единого объективного стандарта почти не бывает. Успешно сгенерированное видео ещё не означает, что консистентность персонажей, ритм кадров и общий визуальный эффект соответствуют ожиданиям пользователя.
Поэтому мы разделяем прогресс процесса и приёмку работы.
Первое можно оценивать по чек-листу выполнения, журналу вызовов инструментов и состоянию результатов; второе требует мультимодального понимания, учёта конкретных требований задачи и при необходимости ручной проверки.
После каждого раунда выполнения среда выполнения проверяет, остались ли в задаче шаги, которые нужно продвинуть, есть ли внешние ожидания, нужно ли подтверждение пользователя и не достигнуты ли лимиты выполнения.
flowchart TD
E["Текущий раунд выполнения завершён"] --> A{"Чек-лист выполнения закрыт?"}
A -->|Да| END["Завершить выполнение, перейти к показу результата или приёмке"]
A -->|Нет| B{"Задача на паузе, заблокирована или вышла за лимиты?"}
B -->|Да| STOP["Остановить автопродвижение и записать причину"]
B -->|Нет| C{"Идёт ожидание внешнего события?"}
C -->|Да| WAIT["Приостановиться до соответствующего события"]
C -->|Нет| D{"Достигнута контрольная точка?"}
D -->|Да| GATE["Показать карточку подтверждения или выбора"]
D -->|Нет| F{"Есть существенный прогресс?"}
F -->|Нет| LIMIT["Прекратить автопродолжение, чтобы не работать вхолостую"]
F -->|Да| NEXT["Запланировать следующий раунд Worker"]
NEXT --> E
Рис. 9. Среда выполнения решает, продолжать ли выполнение, по персистентному состоянию задачи, а не только по тому, закончила ли модель вывод.
Здесь особое внимание нужно уделить существенному прогрессу (Meaningful Progress).
Например, появление нового пригодного творческого результата, закрытие шага чек-листа или успешная отправка новой задачи генерации — всё это можно считать признаками прогресса.
Но если агент лишь переписывает описание плана или постоянно переключает статус шага с «к выполнению» на «в работе», не получая новых полезных результатов, бесконечно продолжать работу он не должен.
С помощью проверки прогресса и лимитов выполнения мы стараемся минимизировать стоимость инференса, которую порождает такая работа вхолостую.
Конечно, у такой оценки есть ограничения. Сгенерированный новый материал доказывает лишь, что система выполнила операцию, но не то, что качество материала соответствует требованиям. Более полная мультимодальная приёмка качества — направление, которое нам ещё предстоит исследовать.
8.3 Human-in-the-Loop: пусть ключевые решения принимает пользователь
В процессе творчества пользователь обычно хочет переложить рутину на агента, но сохранить контроль над ключевыми творческими решениями.
Например, визуальное направление бренда, концепция персонажей, вариант раскадровки и окончательный выбор кадров, как правило, очень субъективны. Если агент будет сам принимать решения в этих точках и генерировать дальше, впоследствии может набежать много бесполезных затрат.
Поэтому мы ввели Human-in-the-Loop Checkpoint (контрольную точку с участием человека), в которой автоматическое продвижение приостанавливается на этапах, требующих участия пользователя.
Дойдя до контрольной точки, агент показывает в структурированной карточке текущий вариант, кандидатов и рекомендуемые действия. Пользователь сразу выбирает «Подтвердить и продолжить» или «Скорректировать вариант», и система обновляет состояние задачи по однозначному результату этого действия.
Такое взаимодействие тоже навеяно тем, как Claude Code и подобные продукты запрашивают разрешения: перед операциями с заметными побочными эффектами система должна заранее сообщить пользователю, что будет сделано, а решение о продолжении принимает пользователь.
Однако в творческих сценариях контрольная точка касается не только прав, но и эстетики и творческого направления. Пользователь может уже разрешить агенту генерировать видео, но всё равно хотеть сам выбрать вариант после того, как готов дизайн кадров. Поэтому контрольная точка должна храниться как самостоятельное состояние выполнения, а не держаться лишь на том, что модель написала в ответе «жду подтверждения».
На прогонах во время разработки мы как раз столкнулись с подобной проблемой: агент уже дошёл до этапа, требующего подтверждения пользователя, но ответил лишь фразой «Подтвердите, продолжать ли», не показав карточку подтверждения, с которой можно сразу работать, — и пользователю пришлось снова писать «продолжай».
Поэтому мы добавили в среду выполнения страховочный механизм: если задача остановилась на контрольной точке, а модель не задала соответствующего вопроса, система автоматически добавляет карточку подтверждения; для генераций, которые выполняются после одобрения пользователем, задача автоматически пробуждается по завершении генерации, если в чек-листе выполнения ещё остались незавершённые шаги. Так ожидание подтверждения и возобновление выполнения больше не зависят целиком от поведения модели во взаимодействии — их координирует среда выполнения по состоянию задачи.
Конечно, контрольная точка не означает, что пользователь должен вмешиваться на каждом шаге. Для необратимых операций или операций сверх выданных полномочий система всегда требует явного подтверждения, а повторяющиеся шаги с низким риском могут выполняться подряд в рамках полномочий, чтобы лишние подтверждения не прерывали творческий процесс.
8.4 Уровень самостоятельности, бюджет и границы полномочий
На платформе AI-творчества агент может вызывать генерацию текста, изображений, видео, аудио, 3D и т. д. от разных разработчиков моделей, и такие операции обычно стоят реальных денег. Поэтому уровень самостоятельности и контроль бюджета нужно проектировать раздельно.
То, что пользователь разрешил агенту работать автоматически, не означает согласия на неограниченные вызовы платных моделей; и наоборот, одобренный бюджет не означает, что все операции можно проводить без проверки.
В среде выполнения мы раздельно поддерживаем три измерения:
| Измерение контроля | Какую проблему решает | Типичные механизмы |
|---|---|---|
| Уровень самостоятельности (Autonomy Level) | Насколько далеко агент может продвигаться без остановки | Подтверждение каждого шага, подтверждение в ключевых точках, автовыполнение в рамках полномочий |
| Контроль бюджета (Budget Control) | Сколько ресурсов может потратить текущая задача | Лимит на одну операцию, бюджет плана, проверка баланса, подтверждение дополнительного бюджета |
| Полномочия на цель (Execution Scope) | Какую работу агенту разрешено выполнять | Утверждённый бриф, план и явные правки пользователя |
Например, пользователь утвердил создание трёх видеороликов и разрешил агенту выполнить их автоматически в пределах бюджета.
Если в ходе работы выяснится, что дополнительно нужны референсы персонажей, агент может определить, входят ли эти операции в исходный план и бюджет. Если же нужно расширить рамки выполнения или увеличить расходы, следует снова запросить подтверждение пользователя.
Здесь проходит важная граница безопасности: ни план, предложенный моделью, ни полномочия, на которые модель ссылается, сами по себе не становятся реальными полномочиями от пользователя.
Решение должно приниматься на основе явных действий пользователя в интерфейсе и состояния полномочий, сохранённого на сервере.
Для разных инструментов нужны и разные стратегии контроля в зависимости от реальных побочных эффектов.
-
Запросы только на чтение обычно малорискованны
-
Запись в документы требует учёта версий и прав доступа
-
Платная генерация требует проверки бюджета
-
Необратимые операции — удаление материалов или отзыв воспоминаний — требуют более строгого механизма подтверждения
По сравнению с агентами для программирования (Coding Agent) у агента творческой платформы есть особое ограничение: многие внешние генерации, однажды отправленные, уже могут стоить денег, и даже если пользователь затем остановит задачу, полностью отменить их не всегда возможно. Поэтому проверки бюджета и полномочий должны выполняться до того, как возникнет реальный побочный эффект, и работать в связке с описанным ранее механизмом устойчивого выполнения.
Судя по нашей практике, трудность самостоятельного выполнения тоже в том, чтобы понять, когда остановиться. При нехватке средств на балансе, паузе от пользователя, ожидании внешней задачи, необходимой ручной проверке или когда выполнение подряд не даёт реального прогресса среда выполнения должна сохранить текущее состояние и дождаться выполнения условий, чтобы возобновить работу.
Это также значит, что чем выше уровень самостоятельности, тем не обязательно лучше. Творческие задачи заметно различаются по риску, стоимости и проверяемости; агент должен в рамках полномочий пользователя сводить ненужное вмешательство к минимуму, сохраняя при этом за пользователем контроль в ключевых точках.
В более долгосрочной перспективе мы надеемся с помощью мультимодальной оценки качества научить систему точнее оценивать консистентность персонажей, связность кадров и визуальный эффект. По мере совершенствования автоматической приёмки часть этапов, которые сейчас требуют подтверждения человеком, можно будет доверить агенту.
Разумеется, у непрерывной совместной работы есть и другая сторона, помимо выполнения задач: пользователь может менять работу не через диалог, а прямо в рабочем пространстве. Как системе сохранять согласованность состояния, когда агент и пользователь одновременно редактируют один и тот же документ или воркфлоу?
Для этого нужно общее рабочее пространство, которое помимо выполнения задач обеспечивает совместный доступ человека и агента к работе и её совместное редактирование.
9. Shared Workspace: пользователь и агент правят одну и ту же работу
Описанные выше механизмы в основном отвечают на вопрос, как агент понимает задачи, поддерживает контекст и продолжает работу. Но в реальном творческом процессе совместная работа пользователя и агента происходит не только в окне чата.
Например, пока агент генерирует видео по раскадровке, пользователь может прямо в правой части рабочего пространства поправить промпт одного кадра, заменить референс или изменить узел модели в воркфлоу Flows.
Эти действия не проходят через диалог, но реально меняют работу. Если агент продолжит действовать на основе прежнего контекста, он может использовать устаревшие параметры или даже перезаписать только что внесённые пользователем правки.
Поэтому в Lomo Agent творческие результаты — это персистентные объекты, независимые от диалога и контекста задач, а совместная работа человека и агента организована через Shared Workspace (общее рабочее пространство).
9.1 У работы — собственный жизненный цикл
В традиционных диалоговых агентах результат генерации обычно привязан к какому-то сообщению в чате. Его можно посмотреть, скачать или задать уточняющий вопрос, но по мере роста диалога управление работами и их последующая доработка всё сильнее упираются в структуру сессии.
В творческом процессе Lomo же множество объектов, которые нужно поддерживать долго: изображения, видео, 3D-ассеты, документы раскадровки, наборы материалов и визуальные воркфлоу Flows.
Этим объектам нужны стабильная идентичность, версии и права доступа; пользователь должен иметь возможность редактировать их напрямую, а разные задачи — ссылаться на них.
Поэтому мы управляем контекстом диалога и творческими результатами раздельно:
-
Контекст диалога и задач хранит намерения пользователя, процесс выполнения и информацию, которой агент располагает в данный момент.
-
Общее рабочее пространство хранит работы, которые действительно нужно постоянно редактировать и сдавать, — с идентичностью объектов, содержимым, версиями и связанными ссылками.
Например, пользователь сначала обсуждает в Main раскадровку для имиджевого ролика, а дальше работу продолжает задача по созданию видео. Оба пространства выполнения могут в пределах своих прав ссылаться на один и тот же документ раскадровки, не создавая каждое свою копию.
Точно так же, когда задача по генерации видео завершена, её результаты по-прежнему можно использовать в новом воркфлоу — независимо от того, работает ли ещё исходный Worker.
Такой дизайн позволяет работе пережить жизненный цикл одного раунда диалога и отдельной задачи и закладывает основу для повторного использования материалов и совместной работы нескольких задач.
9.2 Как агенту замечать изменения в рабочем пространстве
Общие объекты решают вопрос хранения работ, но остаётся другой: как агенту узнать, что работу уже изменили?
Допустим, агент прочитал документ раскадровки и собирается продолжить генерацию по описаниям кадров. В этот момент пользователь прямо в рабочем пространстве меняет содержимое второго кадра.
Если система фиксирует только сообщения чата, следующий раунд инференса агента об этой правке не узнает.
Поэтому мы храним для общих объектов информацию о версиях и записываем, какую версию видела связанная задача, когда в последний раз читала или меняла объект.
Когда документ, материал или воркфлоу меняется, система может оставить связанной задаче персистентное уведомление об изменении. Например:
Документ раскадровки обновлён с v3 до v4, описание второго кадра изменилось. Перед продолжением прочитайте актуальную версию.
При этом мы не стали запускать инференс агента на каждую правку в рабочем пространстве.
Причина проста: настраивая воркфлоу, пользователь может подряд перетаскивать узлы, менять параметры или заменять материалы. Если будить Worker при каждом изменении, это не только породит массу бессмысленных вызовов модели, но и заставит агента раз за разом вмешиваться, пока пользователь ещё не закончил редактирование.
Поэтому уведомления об изменениях нужны в первую очередь для того, чтобы агент при следующем выполнении обнаружил, что его информация устарела. И только если связанную задачу действительно нужно двигать дальше, среда выполнения по текущему состоянию решает, возобновлять ли выполнение.
Для визуальных воркфлоу вроде Flows агенту тоже не нужно в каждом раунде инференса читать полные данные узлов. Система может сначала дать краткую информацию об изменениях, а актуальное содержимое подтянуть по требованию, когда модели действительно понадобится работать с этими узлами.
Здесь прослеживается связь и с описанной выше контекстной инженерией: рабочее пространство хранит полное состояние работы, а контекст даёт только ту информацию, которая нужна для текущего решения.
9.3 Конфликты версий и оптимистичное управление конкурентностью
Замечать изменения ещё недостаточно, чтобы гарантировать безопасность параллельных правок.
Рассмотрим более конкретный сценарий:
Агент прочитал документ раскадровки v3 и собирается поправить третий кадр. Одновременно пользователь сам редактирует тот же документ и сохраняет его как v4.
Если затем агент просто отправит правку, сделанную на основе v3, он может перезаписать то, что пользователь только что внёс в v4.
Для этого мы используем Optimistic Concurrency Control (оптимистичное управление конкурентностью).
При чтении объекта агент получает его текущую версию, а при отправке правки обязан передать версию, на которую опирался. Перед записью сервер проверяет, находится ли объект всё ещё в ожидаемой версии.
Если версии совпадают, правка проходит; если объект уже обновлён, система отклоняет операцию, которая может перезаписать новое содержимое, и требует от агента перечитать актуальное состояние.
Однако у разных типов творческих объектов гранулярность определения конфликтов версий разная.
Для документа полная перезапись должна передавать версию, на которую она опирается. Если пользователь уже изменил документ, система отклонит перезапись; локальные правки находят место по точному и уникальному фрагменту текста и фиксируются, только если выполнимы все ожидаемые замены.
Для воркфлоу Flows мы используем более детальную проверку конфликтов. Пользователь может настраивать один узел кадра, пока агент меняет другой узел. Если отклонять отправку при любом изменении воркфлоу, частые автосохранения сделают параллельное редактирование почти невозможным. Поэтому система прежде всего проверяет, изменились ли узлы, которые агент собирается править: если тот же узел уже изменил пользователь, агенту нужно его перечитать; если изменения затронули другие узлы, неконфликтующие правки можно применить к актуальному воркфлоу, сообщив агенту о соответствующих изменениях.
Это не автоматическое слияние разных правок одного и того же содержимого, а подходящая гранулярность версий, которая позволяет неконфликтующим правкам выполняться максимально независимо и при этом не даёт старому состоянию перезаписать новое содержимое.
sequenceDiagram
participant A as Agent
participant O as Общая работа
participant U as Пользователь
A->>O: Прочитать документ раскадровки v3
U->>O: Изменить раскадровку и сохранить как v4
O-->>A: Записать уведомление о смене версии
Note over O,A: Уведомление ждёт следующего выполнения
A->>O: Отправить правку на основе v3
O-->>A: Конфликт версий, перезапись отклонена
A->>O: Перечитать v4
A->>A: Скорректировать правку по актуальному содержимому
A->>O: Отправить заново на основе v4
Рис. 10. Управление версиями общих работ.
Здесь работают два дополняющих друг друга механизма: уведомления об изменениях позволяют агенту вовремя узнать об обновлении работы, а проверка версий даёт последнюю защиту в момент отправки. Даже если уведомление задержится, агент не сможет перезаписать правки пользователя устаревшим состоянием.
Конечно, оптимистичное управление конкурентностью решает задачу согласованности состояния, а не конфликта творческих намерений. Например, пользователь хочет для кадра холодную гамму, а агент всё ещё следует исходному плану с тёплой. Даже если конфликта версий не возникло, какой вариант принять, всё равно нужно решать с учётом актуальных намерений пользователя и ограничений задачи.
9.4 От Artifacts и Canvas к общему творческому пространству
В дизайне взаимодействия мы ориентировались на идеи таких продуктов, как Claude Artifacts и ChatGPT Canvas.
Эти продукты выносят сгенерированный контент из обычных сообщений чата: пользователь может прямо рядом с диалогом смотреть и редактировать документ или код, а не описывать каждую правку снова и снова на естественном языке.
Для Lomo Agent такой способ взаимодействия очень показателен. Авторам часто удобнее самим перетащить узел, поправить фрагмент раскадровки или заменить картинку, чем заново описывать агенту каждое действие.
Однако общему рабочему пространству Lomo Agent приходится справляться со сценариями, которых нет в обычном редактировании документов.
С одной стороны, типы творческих объектов сложнее: помимо текста, это изображения, видео, 3D-ассеты и воркфлоу Flows с зависимостями между узлами. С другой — одну и ту же работу могут использовать несколько задач, а параллельно идут асинхронные генерации и прямые правки пользователя. Системе нужно поддерживать не только содержимое работы, но и версии, связанные задачи и то, как представление о состоянии работы меняется в процессе выполнения.
Поэтому мы связали редактируемое рабочее пространство с состоянием выполнения агента: пользователь может работать с результатами напрямую, а Worker при последующем выполнении заново получает актуальное состояние объектов.
Судя по нашей практике, одно из важных изменений, которые принесло общее рабочее пространство, — пользователь может участвовать в одном и том же творческом процессе двумя способами: естественным языком и прямым редактированием. Но это же возлагает на систему больше ответственности за синхронизацию состояния и управление конкурентностью. Особенно когда один материал влияет сразу на несколько задач: как управлять зависимостями между объектами и какие нижестоящие результаты нужно перегенерировать после правки вышестоящего — это направление, которое мы продолжаем исследовать.
10. Оркестрация творческих возможностей: от вызова модели к сдаче готовой работы
На такой творческой платформе, как Lomote AI, агенту мало уметь выбирать и вызывать инструменты — нужно понимать, как разные творческие возможности связаны между собой.
Сгенерировать одну картинку относительно просто, а вот создание полноценного короткого ролика может включать сценарий, раскадровку, проработку персонажей, референсы сцен, генерацию кадров и постобработку. На каждом этапе может использоваться своя модель, а результат предыдущего этапа напрямую влияет на последующие. Важнее же всего то, что успех отдельной генерации ещё не означает, что весь творческий процесс удастся довести до конца.
Например, агент составил план: сначала сгенерировать персонажа и фон, потом вырезать персонажа и, наконец, вставить его в заданный видеокадр. Если на платформе сейчас нет нужных возможностей для вырезания или композитинга видео, то, как бы хорошо ни получились исходные материалы, итоговой работы из этого плана не выйдет.
Поэтому агент должен уже на этапе планирования понимать, какие возможности реально есть на платформе и как зависят друг от друга разные инструменты и результаты.
10.1 Как дать агенту понимание творческого процесса и границ возможностей
В Lomote AI уже интегрированы разнообразные модели генерации и улучшения изображений, видео и 3D, а также есть управление материалами и визуальные воркфлоу Flows.
У каждого инструмента свои сценарии применения, а модели различаются требованиями к входным данным, качеством генерации, стоимостью и форматом результата.
Агенту при выборе инструмента нужно учитывать текущую творческую цель, уже имеющиеся материалы и требования дальнейшего производства.
Например, делая короткий ролик с постоянным персонажем, агент должен продумать, как создать референс персонажа, какие модели поддерживают референсное изображение на входе, как сохранить визуальную консистентность в разных кадрах и смогут ли результаты генерации пойти дальше по производственному процессу. Если просто вызывать модели, глядя только на конечную цель пользователя, легко получить массу промежуточных материалов, которые выглядят неплохо, но в итоговую работу не годятся.
Поэтому мы оформили творческий опыт и ограничения инструментов в виде руководств, которые можно читать по требованию: консистентность персонажей, планирование раскадровки, работа с референсами, оркестрация воркфлоу, а также производственные возможности, которые платформа поддерживает сейчас. Эти руководства передаются агенту в соответствующих задачах и помогают ему планировать выполнимые творческие шаги, без необходимости постоянно держать все предметные знания в системном промпте.
Эта часть дизайна тоже работает в связке с описанной ранее контекстной инженерией (Context Engineering): агент получает нужные инструменты и творческие знания в зависимости от текущего этапа производства и уже с учётом реальных возможностей планирует и выполняет работу.
Конечно, у опоры на то, как модель понимает описания инструментов, есть ограничения. Модель может предложить план, который выглядит разумно, но на деле невыполним. Поэтому для ключевых возможностей инструментов, ограничений на вход и выход и условий выполнения система всё равно должна давать явные основания для проверки.
10.2 Многоагентное видеопроизводство на примере ViMax
Исследуя оркестрацию творческих возможностей, я присматривался и к многоагентным системам для генерации видео вроде ViMax.
ViMax объединяет сценарий, персонажей, раскадровку, генерацию кадров и сборку видео в единый производственный процесс и за счёт взаимодействия разных ролей или этапов выполнения решает взаимосвязанные задачи видеопроизводства.
Это очень полезный подход. Видеопроизводство по своей природе поэтапно, и на разных этапах важны разные вещи. Планирование сценария больше про структуру повествования, дизайн персонажей — про визуальную консистентность, а генерация кадров — про требования конкретной модели к входным данным и качество результата.
Профессиональное разделение труда позволяет каждому этапу работать в рамках относительно чётких обязанностей и помогает организовывать сложные производственные процессы.
Но и у таких архитектур есть свои компромиссы. С одной стороны, многоагентное взаимодействие увеличивает затраты на передачу контекста, синхронизацию состояния и координацию; с другой — заранее заданный производственный конвейер не обязательно покроет постоянно меняющиеся творческие потребности пользователя.
Сценарии Lomo Agent более открытые. Пользователю может понадобиться всего один постер, а может — видео на основе имеющихся материалов, или он прямо попросит агента изменить какой-то воркфлоу Flows. Если под все творческие запросы вводить фиксированный многоагентный конвейер, это, наоборот, может добавить лишние уровни выполнения.
Поэтому на текущем этапе мы предпочитаем единую систему выполнения задач в сочетании с подгружаемыми по требованию творческими руководствами и возможностями инструментов, чтобы Worker выстраивал производственные шаги под конкретную цель. А для сложных творческих задач, требующих долгой поддержки, постепенно исследовать более явное профессиональное разделение труда и управление зависимостями задач. Этот компромисс согласуется и с дизайном Main / Task, описанным выше: дополнительный уровень координации оправдан только тогда, когда отдельное пространство выполнения приносит реальную пользу.
10.3 Граф производственных зависимостей: от линейного выполнения к управлению зависимостями
Сейчас структурированный чек-лист выполнения уже помогает агенту отслеживать творческий прогресс, но когда задача включает много взаимосвязанных материалов и шагов генерации, у линейной структуры списка появляются ограничения.
Например, короткий ролик состоит из нескольких кадров, у которых может быть общая концепция персонажа, но разные сцены и модели генерации.
Часть кадров можно генерировать независимо, а часть должна ждать готовности вышестоящих референсов. Если пользователь посреди работы меняет внешность персонажа, связанные кадры, возможно, придётся перегенерировать, но совершенно не связанные с ним сцены это затрагивать не должно.
Такие связи удобнее выражать через Production DAG (производственный направленный ациклический граф).
flowchart LR
BR["Творческий запрос"] --> SCRIPT["Сценарий / раскадровка"]
SCRIPT --> CHAR["Концепция персонажа"]
SCRIPT --> SCENE["Концепция сцены"]
CHAR --> SHOT1["Кадр 1"]
CHAR --> SHOT2["Кадр 2"]
SCENE --> SHOT1
SCENE --> SHOT2
SHOT1 --> REVIEW["Приёмка и сборка"]
SHOT2 --> REVIEW
REVIEW --> FINAL["Финальная сдача"]
Рис. 11. Задуманный граф производственных зависимостей.
В такой структуре каждый узел — это производственный шаг или промежуточный результат, а связи между узлами описывают зависимости.
Если структурировать эти связи, система получает возможность реализовать несколько функций:
-
Планирование по зависимостям: нижестоящий шаг выполняется только после того, как готовы нужные вышестоящие материалы.
-
Параллельная генерация: кадры без прямых зависимостей можно генерировать параллельно, если позволяют ресурсы и бюджет.
-
Распространение изменений: при изменении концепции персонажа или сцены находить нижестоящие результаты, которые могут быть затронуты, и не перегенерировать всё подряд.
-
Инкрементальное производство: по возможности переиспользовать всё ещё актуальные материалы и результаты генерации, сокращая повторные вызовы и лишние производственные затраты.
Однако граф зависимостей добавляет и инженерной сложности. В частности, зависимости в творческих задачах не всегда полностью определены: нужно ли перегенерировать нижестоящий результат после изменения какого-то вышестоящего материала, приходится решать с учётом объёма правок и конкретных производственных требований.
Кроме того, даже при правильных зависимостях нельзя гарантировать, что результат генерации визуально оправдает ожидания. Консистентность персонажей, связность кадров и общий ритм всё равно требуют отдельного механизма оценки качества.
Поэтому Production DAG пока скорее направление развития, которое мы исследуем. На текущем этапе в приоритете — чтобы агент мог в рамках уже имеющихся в Lomote AI моделей и инструментов проходить выполнимый творческий процесс и контролировать стоимость и пригодность промежуточных результатов.
По мере развития зависимостей задач, автоматической приёмки и мультимодального понимания эти механизмы можно будет объединить, чтобы агент перешёл от пошагового выполнения творческих задач к управлению всем производственным процессом. Но чтобы понять, действительно ли эти решения работают, одной гладкой демонстрации совсем недостаточно. В сложных творческих задачах случаются сбои инструментов, правки пользователя, восстановление состояния и повторные генерации.
Работают ли эти решения на деле, ещё предстоит проверить учениями по восстановлению после сбоев и сквозными прогонами совместной работы.
11. Инженерная проверка: от восстановления после сбоев до сквозной совместной работы
У агентных систем есть любопытная особенность: то, что каждый модуль по отдельности работает нормально, ещё не значит, что весь процесс совместной работы надёжен.
Например, сообщение правильно попало в нужную задачу, вызов инструмента успешно вернул результат, но агент после завершения генерации может забыть продолжить работу; или каждый шаг идёт как задумано, а в итоге появляется несколько дублирующих документов, и пользователь не понимает, какой из них последний.
Такие проблемы трудно выявить классическими модульными тестами или несколькими простыми диалогами.
Поэтому при разработке Lomo Agent мы не только проверяли корректность отдельных модулей, но и пробовали прогонять агента через полный процесс в условиях, близких к реальному использованию, наблюдая, как ведут себя механизмы в связке.
11.1 Проверка надёжности среды выполнения
У механизмов устойчивого выполнения, Inbox, асинхронной генерации и полномочий пользователя многие проблемы проявляются только при определённых переходах состояний.
Например:
-
запрос на генерацию видео уже отправлен, но Worker перезапускается до сохранения результата;
-
старый Worker теряет связь, задачу перехватывает новый процесс, а потом старый снова оживает;
-
пользователь нажимает «Стоп» ровно в тот момент, когда приходит событие о завершении генерации.
В обычных демонстрациях такие ситуации почти не встречаются, но они могут напрямую повлиять на состояние выполнения, работы пользователя и даже реальные расходы. Поэтому на этапе разработки мы с помощью тестов с внедрением отказов (fault injection) прицельно проверяли, как система справляется с этими нештатными ситуациями.
Тесты охватили следующие ключевые границы выполнения:
| Сценарий проверки | На что смотрим |
|---|---|
| Прерывание и восстановление выполнения | Удаётся ли восстановиться из персистентного состояния без повторного выполнения |
| Повторная доставка и сетевые таймауты | Правильно ли обрабатываются идемпотентные запросы и случаи с неизвестным результатом |
| Параллельный перехват задачи Worker | Удаётся ли не дать старому исполнителю записать устаревшее состояние |
| Гонка отмены и асинхронных событий | Может ли запоздавшее событие снова разбудить уже остановленную задачу |
| Изменение бюджета и полномочий | Перепроверяются ли текущие права при возобновлении выполнения |
| Параллельные правки общих объектов | Может ли старая версия перезаписать новые правки пользователя |
| Исправление и отзыв воспоминаний | Используется ли недействительная информация через сводки или профиль |
Для стабильно воспроизводимых ответов модели мы можем проигрывать зафиксированные результаты и сосредоточиться на проверке переходов состояний среды выполнения. Внешние сервисы генерации можно эмулировать с таймаутами, повторными колбэками и событиями не по порядку, чтобы не зависеть каждый раз от реальных платных моделей. Главное в этих тестах — детерминированность поведения системы. Даже если сама модель отчасти случайна, у состояния задач, прав на выполнение и ограничений биллинга должны быть чёткие и проверяемые правила.
Один из показательных тестов имитирует внезапное «зависание» Worker во время выполнения инструмента. В тесте модель отдаёт зафиксированный ответ и сначала решает вызвать инструмент запроса; первый Worker уже сохранил решение модели и записал вызов инструмента как started, после чего его искусственно приостанавливают. Когда его аренда истекает, задачу перехватывает второй Worker, поколение выполнения повышается до 2, и выполнение продолжается с checkpoint: поскольку решение модели для этого шага уже сохранено, повторный инференс не нужен — вызов инструмента выполняется с тем же ключом идемпотентности. В конце первый Worker возобновляют, и он пытается зафиксировать запоздавший результат.
Результат теста совпал с ожиданиями: раунд в итоге перешёл в completed с поколением выполнения 2; модель была вызвана только один раз, результат инструмента тоже один, а запись состояния от старого Worker отклонена. Интеграционными тестами мы также покрыли повторную доставку после потери сообщения в очереди, идемпотентное восстановление задач генерации после одобрения, запрет на создание новых задач из отменённого раунда, ограничения авторизации расходов и отзыв воспоминаний.
11.2 Сквозные прогоны совместной работы
Тесты среды выполнения показывают, правильно ли система обрабатывает аномальные состояния, но не дают полной картины того, каково реально работать с агентом.
Например, агент успешно закончил раскадровку, но на следующем этапе просто остановился и начал генерировать видео, только когда пользователь снова написал «продолжай». С точки зрения выполнения программы это, может, и не сбой, но для пользователя процесс явно выглядит неестественно.
Поэтому я попробовал ввести сценарные прогоны.
В изолированной среде агент с реальной текстовой моделью, эмулированными сервисами генерации и симулированным пользователем с заранее заданным поведением проходит относительно полный творческий процесс.
Сценарии прогонов охватывают серию постеров, создание короткого ролика, правку воркфлоу Flows, 3D-творчество, переключение между несколькими задачами, возобновление старых задач, а также исправление и забывание воспоминаний. В этих сценариях есть и цепочки последовательных вызовов модели и инструментов, и подтверждения пользователя, асинхронные ожидания и связывание контекста между задачами.
Например, в сценарии с коротким роликом симулированный пользователь сначала ставит творческую цель, а агент должен составить раскадровку, подготовить материалы и отправить запросы на генерацию. По ходу выполнения пользователь может скорректировать вариант, выбрать кадры или вклиниться с другой задачей. Нас интересует не только то, удаётся ли отдельный шаг, но и то, продолжает ли агент после всех этих изменений последовательно двигаться к исходной цели.
Я в первую очередь смотрю:
-
останавливается ли агент и продолжает ли работу в правильных точках;
-
приходится ли пользователю раз за разом писать «продолжай», чтобы задача двигалась дальше;
-
нет ли лишних подтверждений или повторных генераций;
-
удаётся ли правильно привязать новое сообщение к существующей задаче;
-
получает ли агент актуальное состояние после того, как пользователь изменил работу;
-
не распадается ли одна работа случайно на несколько копий;
-
влияет ли старая информация на дальнейшую совместную работу после исправления или отзыва воспоминаний.
По сравнению с прямыми вызовами реальных сервисов генерации медиа эмулированная среда позволяет контролировать стоимость тестирования, легче раз за разом воспроизводить конкретные сценарии и наблюдать проблемы, которые могут возникать при взаимодействии модулей.
Но у этого подхода есть и явные ограничения. Эмулированные результаты генерации не позволяют оценить визуальное качество реальных работ, а симулированный пользователь с заранее заданной стратегией не может в полной мере отразить сложность поведения в реальном творчестве. Поэтому такие прогоны служат в основном для проверки непрерывности организации задач, состояния выполнения и процесса совместной работы и не отражают напрямую долю успешных реальных пользовательских задач. Качество итоговых работ и реальный опыт использования ещё нужно оценивать с учётом реальных результатов генерации и отзывов пользователей.
11.3 Как оценить качество совместной работы
Проводя эти прогоны, я постепенно понял: по одной лишь гладкости ответов модели и успешности вызовов инструментов трудно полноценно оценить агента, который работает долго.
Пользователю, возможно, важнее другое:
-
Сколько раз мне приходится объяснять одно и то же?
-
Помнит ли агент уже согласованные требования?
-
Может ли он через несколько дней естественно продолжить прежнюю работу?
-
Знает ли он, что произошло, когда я сам изменил работу?
Поэтому, помимо классической корректности выполнения, я начал следить за такими измерениями, как непрерывность задач, стоимость вмешательства пользователя, повторные действия и согласованность результатов.
Реальные прогоны вскрыли и проблемы, которые трудно заметить при тестировании модулей по отдельности.
Например, при создании серии постеров на английском Main правильно понимал запрос пользователя и создавал задачу, но Worker при дальнейшем планировании переключался на китайский. Дело было не в маршрутизации задач: задача получала лишь описание цели, переработанное основным диалогом, и в нём не было языковой информации из исходной формулировки пользователя. Затем мы изменили способ передачи языка пользователя, чтобы задача поддерживала язык ответов по фактическому вводу пользователя, а не полагалась целиком на то, что модель выведет его из описания задачи.
Другая проблема возникла на стыке контрольных точек и асинхронной генерации. Агент уже доходил до этапа, требующего подтверждения пользователя, но не показывал карточку подтверждения, с которой можно работать; или пользователь одобрял генерацию, внешняя задача нормально завершалась, а задача так и не двигалась дальше. Для таких случаев мы добавили в среду выполнения страховочную карточку подтверждения и автоматическое пробуждение после завершения генерации, уменьшив зависимость от поведения модели во взаимодействии.
Каждая из этих проблем сама по себе не обязательно выглядит как явный сбой выполнения, но напрямую влияет на то, насколько непрерывной пользователь ощущает совместную работу. Они же помогли мне чётче сформулировать: модель отвечает за понимание намерений и планирование шагов, а ключевые состояния — ожидание, авторизация и возобновление выполнения — должна детерминированно обеспечивать среда выполнения.
Конечно, сами по себе эти измерения не отражают уровень интеллекта агента. На творческой платформе завершённость процесса и качество работы всё равно нужно оценивать по отдельности. Агент может сгенерировать все кадры по плану, но не гарантировать, что итоговое видео по визуальному стилю, консистентности персонажей и ритму повествования оправдает ожидания пользователя. Более надёжная мультимодальная приёмка — возможность, которую мы хотим и дальше развивать.
Судя по нашей практике, сквозные прогоны дают нам относительно контролируемый способ проверки. Они не только помогают находить проблемы во взаимодействии модулей, но и позволяют некоторым принципам архитектурного дизайна по-настоящему пройти проверку полным процессом выполнения. Наряду с простым наращиванием возможностей моделей не меньшего постоянного внимания заслуживает и то, как обеспечить устойчивую стыковку этих возможностей в долгой совместной работе.
12. Нерешённые вопросы и дальнейшие исследования
Сейчас у Lomo Agent уже есть базовые возможности: организация нескольких задач, устойчивое выполнение, управление контекстом и общее рабочее пространство. Но до творческого агента, каким я его себе представляю, ещё немало вопросов, которые стоит исследовать.
Во-первых, взаимодействие между задачами.
Текущая структура Router / Worker позволяет нескольким задачам продвигаться независимо и при необходимости обращаться к общим результатам. Но если пойти дальше и позволить Worker самим просить друг друга о помощи, делегировать задачи и даже договариваться об общем бюджете, придётся решать куда более сложные вопросы прав доступа, синхронизации состояния и согласованности при конкурентном доступе.
Многоагентное взаимодействие выглядит заманчиво, но больше агентов само по себе не означает большей эффективности. Дополнительные затраты на координацию и передачу контекста могут свести на нет выгоду от профессионального разделения труда. Я хочу сначала найти в реальных потребностях работу, которую действительно стоит дробить, и лишь затем постепенно расширять рамки взаимодействия.
Во-вторых, управление зависимостями в творческом процессе.
Упомянутый выше Production DAG — направление, которое меня особенно интересует. Для сложных производственных задач с множеством кадров, персонажей и сцен явные зависимости помогают поддерживать параллельное выполнение, инкрементальные обновления и переиспользование результатов.
Однако зависимости в творческих задачах не всегда определены. Если пользователь изменил концепцию персонажа, какие кадры обязательно перегенерировать, а какие можно оставить, часто приходится решать, глядя на реальное содержимое.
Надёжность асинхронной генерации тоже ещё есть куда улучшать.
Сейчас система не может сама отменить задачу генерации, уже отправленную вышестоящему сервису; а если задача уже создана, но сбой произошёл до сохранения ID задачи вышестоящего сервиса, нет и возможности найти её обратным запросом и восстановить связь.
Для таких случаев уже есть соответствующая обработка состояний и страховочные механизмы по расходам, но они всё равно могут приводить к ненужным затратам на генерацию. В дальнейшем мы хотим постепенно подключать возможности отмены и запроса задач у вышестоящих сервисов, чтобы система точнее справлялась с прерываниями выполнения и внезапными изменениями требований пользователя.
Мультимодальная приёмка качества — тоже непростая задача.
Сейчас успешный ответ инструмента и закрытый шаг чек-листа доказывают лишь то, что система выполнила соответствующую операцию. Реальное качество работы зависит ещё от визуальной консистентности, плавности движения, синхронизации звука и изображения и субъективного вкуса пользователя.
С развитием мультимодальных моделей я хочу постепенно встраивать такие оценки в процесс выполнения, чтобы агент точнее понимал, когда нужно что-то скорректировать, когда стоит перегенерировать, а когда пора вернуть решение пользователю.
Кроме того, постоянной проверки требуют точность долгосрочной памяти, надёжность маршрутизации задач и границы проактивности.
Отслеживание источников и управление версиями делают память более управляемой, но не исключают полностью ошибок извлечения моделью или ошибочного извлечения исторической информации. Точно так же, хотя нынешний механизм маршрутизации уже сочетает индекс задач, поиск по истории и проверку на дубли похожих задач, чтобы сократить число ошибок, для большого массива задач, накопленного за долгое время, точность привязки к задачам и то, как часто пользователю приходится исправлять её вручную, ещё предстоит измерить на реальных данных использования.
Что касается проактивного продолжения работы, система может знать, что по задаче ещё осталась работа, но не обязательно знает, хочет ли пользователь, чтобы его беспокоили прямо сейчас. Мы хотим, чтобы будущие агенты были проактивнее, но эта проактивность должна опираться на полномочия пользователя, ценность задачи и реальные привычки использования.
Не на все эти вопросы в ближайшее время найдутся зрелые решения. По мере того как Lomo Agent будет приходить во всё новые реальные творческие сценарии, мы будем снова и снова пересматривать текущую архитектуру, проверяя, какие решения действительно работают, а какие нужно менять.
Заключение
Оглядываясь на путь Lomo Agent от первоначальной идеи до запуска, я всё больше убеждаюсь, что развитие агентов ставит перед архитектурой ПО очень интересные новые вопросы.
Раньше, проектируя ПО, мы обычно сначала чётко определяли функции, процессы и границы взаимодействия, а потом пользователь выполнял действия по этим правилам. Теперь же, когда AI научился понимать намерения, вызывать инструменты и самостоятельно принимать решения, значительную часть работы по организации и координации, которая раньше лежала на пользователе, можно постепенно передать системе.
Но рост возможностей моделей — лишь часть картины.
Агенту, способному к долгой совместной работе, нужно ещё знать, что он сейчас делает, что уже сделано, какая информация всё ещё актуальна и в каких случаях стоит продолжать или остановиться. Ему приходится справляться с прерываниями, конкурентностью, расходами и правами доступа в реальной системе — и при этом позволять пользователю в любой момент передумать.
Архитектура Lomo Agent — это практическая попытка ответить именно на эти вопросы. Многие идеи пришли из отличных продуктов и инженерных практик — DeepSeek, Manus, Claude Code, Temporal, — а что-то я постепенно нащупал в собственных творческих сценариях.
Текущая реализация — всё ещё ранний этап исследований, до идеала долгий путь, и многим решениям ещё предстоит пройти проверку реальными пользователями.
И всё же я убеждён: по мере развития моделей у будущего взаимодействия человека и ПО остаётся огромный простор для воображения.
Возможно, однажды нам не придётся учиться пользоваться программой — достаточно будет просто сказать ей, чего мы хотим добиться.
Именно к этому мы и хотим, чтобы Lomo Agent шаг за шагом приближался.
Если вы тоже строите агентов, буду рад обменяться опытом и обсудить вопросы архитектуры и инженерной практики;
если вы автор, приглашаю попробовать Lomo Agent на своих реальных творческих задачах.
Попробовать Lomo Agent: lomo agent
Источники и дополнительное чтение
Ссылки ниже приведены в основном для пояснения источников архитектурных идей и исследовательского контекста и не означают, что во всех аспектах эти проекты реализованы так же или преследуют те же цели, что и Lomo Agent.
-
Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory
-
LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory
Примечание: в тексте мы по возможности разграничиваем «уже работающие механизмы» и «направления развития». Реальные возможности в части гарантий идемпотентности внешних сервисов, объёма данных, на которые распространяется «забывание», поведения кэша моделей и эффективности автоматической приёмки зависят от конкретной реализации и внешних условий.