Редакция по расписанию: пайплайн «источники → отбор → текст → проверка → публикация» на OpenClaw

Практика. Как собрать выпуск, который выходит сам, а человека зовёт только на спорные материалы.

· 13 мин чтения

Коротко

  • Расписание — automation в постоянной сессии, шаги без модели — Lobster, учёт прогона — Task Flow, суждения — субагенты и llm-task со схемой.
  • Человек одобряет только рискованное: чистые материалы уходят сами, спорные ждут решения в отдельном workflow.
  • Честная граница: упавший шаг Lobster сам не перезапускается, таймеров в Task Flow нет, exactly-once нет, записи живут 7 дней.

Кому и когда

Вам нужен регулярный выпуск: дайджест для канала, лента новостей по нише, еженедельная сводка для команды. Источники известны, форма выпуска тоже, а читать всё руками вы больше не хотите.

Сборка подходит, если в пайплайне есть решения, которые не пишутся правилом: что из двадцати находок важно, как пересказать, нет ли в пересказе ошибки. Если решений нет и всё сводится к «скачать, отфильтровать, отправить», модель не нужна. Об этом в конце.

Всё ниже собрано по документации OpenClaw 2026.9.5. Команды и конфиги взяты из неё и собраны в одну схему; в живой инсталляции эту схему мы не прогоняли. Утилита newsroom в примерах — ваш собственный набор скриптов, как market-intel в примере из документации. Её подкоманды придуманы для иллюстрации.

Общая картина механизмов — во флагманской статье. Здесь собираем один конкретный пайплайн.

Схема потока

  1. Будильник. Automation (встроенный планировщик OpenClaw) по cron-выражению будит агента в именованной постоянной сессии.
  2. Сбор. Агент запускает Lobster-workflow (цепочку шагов-команд, которую Lobster выполняет за один вызов инструмента): проверка готовности → сбор с полями происхождения → дедупликация → отсев устаревшего → ранжирование.
  3. Текст. На каждую отобранную тему агент поднимает субагента (дочернюю сессию со своей задачей и, при желании, другой моделью).
  4. Проверка. Каждый текст проходит через llm-task (одиночный вызов модели, который обязан вернуть JSON по схеме) как судья, плюс скрипт сверяет числа с источником.
  5. Развилка. Чистые материалы уходят в workflow публикации без одобрения. Рискованные — в отдельный workflow с шагом approval: required, он встаёт на паузу.
  6. Отчёт. Итог прогона приходит в чат через announce (доставку финального ответа задания). Если делать было нечего, агент отвечает NO_REPLY и в чат ничего не уходит.

Весь прогон отслеживается как Task Flow — долговечная запись о многошаговой работе с номером ревизии, которая переживает рестарт Gateway.

Из чего собрано

  • Automations: расписание, сессии, доставка, контракт работы без присмотра.
  • Task Flow, раздел «Reliable scheduled workflow pattern». Это готовый каркас нашей схемы в четыре слоя: automation для времени, постоянная сессия для контекста, Lobster для детерминированных шагов, Task Flow для учёта.
  • Lobster: workflow-файлы, одобрения, resume.
  • LLM Task: JSON-судья.
  • Субагенты: тяжёлый шаг.
  • Standing orders: полномочия агента в AGENTS.md.

Шаги

1. Отдельный агент и его полномочия

Заведите пайплайну своего агента. Причин две: у него свой workspace, и по нему потом считается стоимость (раздел ниже).

Правила редакции запишите в его AGENTS.md как standing orders — постоянные поручения, которые агент загружает в каждую сессию. В документации каждая такая программа состоит из четырёх частей: что агенту разрешено, когда он действует, что требует одобрения, когда он останавливается и зовёт человека. Каркас:

## Программа: выпуск по расписанию

- Разрешено: собирать источники, писать тексты, публиковать чистые материалы.
- Триггер: automation «Редакция», три раза в день.
- Одобрение человека: смерть, криминал, обвинения реальных людей, медицинские советы,
  материалы с одним источником, любые расхождения чисел с источником.
- Эскалация: preflight не прошёл, судья вернул ошибку, публикация упала.
- Правило: выполнить → проверить → отчитаться. «Готово» без проверки не считается.

2. Плагины

Lobster и llm-task — опциональные плагины. По документации Lobster ставится командой openclaw plugins install @openclaw/lobster. Оба инструмента добавляются агенту через tools.alsoAllow, llm-task дополнительно включается в plugins.entries. В sandbox-контексте, то есть когда инструменты агента работают в песочнице, Lobster отключён целиком. Режим с Task Flow требует и несандбоксного контекста, и привязанной сессии.

{
  plugins: { entries: { "llm-task": { enabled: true } } },
  agents: {
    entries: {
      newsroom: { tools: { alsoAllow: ["lobster", "llm-task"] } }
    }
  }
}

Фрагмент собран по tools/lobster.md и tools/llm-task.md. Для llm-task там же описан allowedCompletionModels — список моделей, которые судье разрешено вызывать.

3. Будильник

openclaw automations add \
  --name "Редакция: выпуск" \
  --agent newsroom \
  --cron "0 7,13,19 * * *" \
  --tz "Europe/Moscow" \
  --session session:newsroom \
  --timeout-seconds 1800 \
  --announce \
  --channel telegram \
  --to "<id чата редактора>" \
  --disabled \
  --message "Выпуск по standing orders. Прогон без присмотра: итоговый ответ — отчёт о выпуске, а не план. Если новых тем нет, ответь ровно NO_REPLY. Спорные материалы не публикуй: запусти newsroom-publish-risky и включи в отчёт approvalId и превью. Отчёт по-русски."

Что здесь важно:

  • --tz обязателен. Без него cron-выражение считается по часовому поясу хоста Gateway.
  • session:newsroom — постоянная именованная сессия: агент видит прошлые прогоны. Документация советует её, когда выпуск опирается на историю. Если каждый прогон должен начинаться с чистого листа, берите isolated.
  • Контракт работы без присмотра документация формулирует для изолированных заданий: финальный ответ — результат, а не план или вопрос; NO_REPLY, когда делать нечего; повторы — забота планировщика. Мы работаем в именованной сессии, поэтому повторяем этот контракт прямо в --message.
  • Язык отчёта планировщик не угадывает, его надо написать в сообщении.
  • --disabled создаёт задание выключенным. Сначала запустите его вручную (раздел «Как проверить»).

4. Сбор: workflow без модели

# workflows/newsroom-collect.lobster
name: newsroom-collect
steps:
  - id: preflight
    command: newsroom check --json
  - id: harvest
    command: newsroom harvest --json
    stdin: $preflight.json
  - id: dedup
    command: newsroom dedup --by url,title --json
    stdin: $harvest.json
  - id: fresh
    command: newsroom drop-stale --max-age 48h --json
    stdin: $dedup.json
  - id: rank
    command: newsroom rank --top 5 --json
    stdin: $fresh.json

В шаге preflight (проверка готовности перед запуском) документация советует проверять ключи и квоты источников, сеть, включённые инструменты агента и то, что у задания есть куда слать ошибки. Каждая запись после harvest несёт поля происхождения из того же раздела документации:

{
  "sourceUrl": "https://example.com/report",
  "retrievedAt": "2026-09-28T04:00:00Z",
  "asOf": "2026-09-27",
  "title": "…",
  "content": "…"
}

sourceUrl — откуда взято, retrievedAt — когда скачано, asOf — на какую дату верны данные. По asOf шаг fresh выкидывает старьё до того, как его увидит модель.

Дедупликацию делайте по нормализованному адресу и заголовку, а не только по id записи в источнике. Иначе одна история из трёх лент превращается в три темы. Публичный openclaw-newsroom дедуплицирует как раз по URL и заголовку, в SQLite между прогонами.

Агент вызывает workflow в управляемом режиме Task Flow:

{
  "action": "run",
  "pipeline": "workflows/newsroom-collect.lobster",
  "timeoutMs": 120000,
  "flowControllerId": "newsroom",
  "flowGoal": "Выпуск 07:00"
}

По умолчанию timeoutMs — 20 секунд, для сбора из сети этого мало. Вывод шага ограничен 512 000 байт (maxStdoutBytes), поэтому тексты статей лучше держать в файлах, а по конвейеру гнать ссылки на них.

5. Текст: субагенты

На каждую тему из rank агент вызывает sessions_spawn. Задание субагенту: входной JSON темы и требование сохранить в выходе sourceUrl, retrievedAt и asOf. Параметр model позволяет писать на одной модели, а судить на другой. runTimeoutSeconds не даст субагенту висеть вечно. Опрашивать детей в цикле не надо: результаты приходят событиями.

Для изолированных заданий документация обещает, что при работе с субагентами announce берёт итоговый ответ, а промежуточные сообщения родителя подавляет. Для именованной сессии это прямо не сказано, поэтому пропишите в --message: промежуточных сообщений не слать, только итоговый отчёт.

6. Проверка: судья со схемой

Документация Task Flow советует llm-task для шага модели со схемой. Но в tools/lobster.md прямо написано: вызов llm-task изнутри встроенного Lobster через openclaw.invoke сейчас ненадёжен. Поэтому судью агент вызывает сам, как обычный инструмент, между двумя workflow:

{
  "prompt": "Сверь текст с источниками. Каждое утверждение: есть ли оно в источнике. Верни JSON.",
  "input": { "draft": "…", "sources": [ { "sourceUrl": "…", "content": "…" } ] },
  "schema": {
    "type": "object",
    "properties": {
      "verdict": { "enum": ["clean", "risky", "reject"] },
      "unsupported": { "type": "array", "items": { "type": "string" } },
      "reasons": { "type": "array", "items": { "type": "string" } }
    },
    "required": ["verdict", "unsupported", "reasons"],
    "additionalProperties": false
  },
  "timeoutMs": 60000
}

Судья работает без транскрипта агента, без инструментов и без запасной модели. Ответ обязан пройти схему. Если вызов вернул ошибку, считайте материал отклонённым, а не чистым. Документация велит считать вывод недоверенным без схемы и ставить одобрение перед любым шагом с побочным эффектом.

Числа сверяйте кодом, а не судьёй: скрипт вытаскивает цифры из текста и ищет их в content источника. Любое расхождение — risky.

И предупреждение про стиль-линтер. Механические правила вроде «не больше N слов в предложении» режут хорошие тексты. Пусть линтер помечает, а решает судья или человек.

7. Публикация: две дорожки

# workflows/newsroom-publish.lobster — чистые материалы, без одобрения
name: newsroom-publish
args:
  batch:
    default: "var/clean.json"
steps:
  - id: publish
    command: newsroom publish --from "$LOBSTER_ARG_BATCH" --json
# workflows/newsroom-publish-risky.lobster — только спорное
name: newsroom-publish-risky
args:
  batch:
    default: "var/risky.json"
steps:
  - id: preview
    command: newsroom publish --from "$LOBSTER_ARG_BATCH" --preview --json
    approval: required
  - id: publish
    command: newsroom publish --from "$LOBSTER_ARG_BATCH" --json
    condition: $preview.approved

Аргументы workflow Lobster передаёт в шаги переменными LOBSTER_ARG_<ИМЯ>. Второй файл агент запускает, только если судья вернул хоть один risky. Workflow встаёт на паузу со статусом needs_approval и отдаёт approvalId, а Task Flow записывает паузу как waiting.

Где человек

Только на спорном. Чистые материалы выходят без вас: так записано в standing orders, и так устроены два workflow.

Спорное приходит в отчёте прогона: превью и approvalId, короткий номер паузы, по которому вы понимаете, о каком материале речь. Ваше «да» или «нет» должно попасть к агенту в сессию session:newsroom. Как именно его туда доставить, документация не расписывает: это ваша часть сборки. Получив решение, агент вызывает resume у Lobster с flowId, текущей ревизией flowExpectedRevision и approve. В управляемом режиме Task Flow токен и approvalId можно не передавать: сохранённую точку паузы OpenClaw восстановит из самого flow. Ранние шаги при этом не повторяются.

Две оговорки из документации:

  • Одобрение workflow — не слушатель ответов в мессенджере. «Да» в канале доставки само ничего не возобновит, это должен сделать агент в сессии.
  • Не путайте с exec approvals — одобрениями отдельных shell-команд. Если прогон по расписанию упрётся в такое одобрение, запрос уйдёт только в Control UI и приложения, в чат-каналы он не приходит никогда. Если ни один такой клиент не подключён, запрос сразу отклоняется. Кнопка «Always allow» выдаёт постоянный грант, привязанный к агенту, заданию и команде; список — openclaw approvals grants list.

Сколько стоит и как посчитать

Цифр за вас не назовём: стоимость зависит от моделей, числа источников и длины текстов. Посчитать можно встроенными средствами.

  1. Отдельный агент (шаг 1) даёт отдельную строку в учёте.
  2. Неделю пусть пайплайн работает, потом:
openclaw gateway usage-cost --agent newsroom --days 7

Команда собирает сводку из логов сессий. Если провайдер вернул токены без суммы, OpenClaw оценивает стоимость по ценам из локального models.json агента. Проверьте, что цены там заполнены, иначе оценка будет неполной.

  1. В чате с агентом есть /usage cost — локальная сводка по логам сессий. Квоты провайдеров показывает openclaw status --usage.

Как снижать. Документация советует рутинные задачи — отбор, классификацию — отдавать лёгкой модели через --model, а тяжёлую держать на написание. --fallbacks позволяет поднять задачу на модель посильнее, если лёгкая упала. Сбор, дедупликация и публикация в Lobster не тратят токенов вовсе.

Для ориентира — чужой опыт. Автор openclaw-newsroom оценивает свой пайплайн примерно в $5 в месяц: API Gemini Flash Lite плюс бесплатный тариф поискового сервиса Tavily. Но у него модель выбирает до семи историй из собранного и пишет к каждой аннотацию в одно предложение. Полные тексты с проверкой обойдутся дороже. Сколько — покажет usage-cost через неделю.

Жёсткого потолка расходов в этой схеме нет. Нужен лимит — ставьте его на стороне провайдера или прокси.

Как проверить, что работает

openclaw automations list
openclaw automations run <jobId> --wait     # код 0 только при completionStatus: succeeded
openclaw automations runs <jobId> --limit 20
openclaw tasks flow list
openclaw tasks flow show <flowId>
openclaw tasks audit                        # зависшие и сломанные задачи и flow

В истории прогонов status показывает исполнение, completionStatus — прогон целиком, вместе с доставкой. Прогон с NO_REPLY пишется как успешный и с причиной подавления доставки: так видно, что агент молчал намеренно.

Когда всё сходится, openclaw automations enable <jobId>.

Как поставить на паузу и откатить

  • Пауза пайплайна: openclaw automations disable <jobId>.
  • Остановить застрявший прогон: openclaw tasks flow cancel <flowId>.
  • Отозвать выданный грант: openclaw approvals grants revoke <id>.
  • Выключить все automations на Gateway: cron.enabled: false в конфиге или переменная OPENCLAW_SKIP_CRON=1.
  • Откат опубликованного — забота вашего newsroom publish. OpenClaw не отменяет побочные эффекты, которые уже случились. Делайте публикацию через выпуски и переключение ссылки, тогда откат — одно переключение.

Что ломается и когда брать другой инструмент

Что говорит сама документация:

  • Шаг Lobster не повторяется. Если вызов упал или истёк после отправки, Lobster его не повторит: Gateway мог успеть выполнить действие. Повтор — решение агента или ваше.
  • Task Flow хранит записи, а не время. Состояние ожидания не заводит таймер. Одобрение, которое никто не дал, будет висеть. После рестарта flow надо явно поднять и продолжить с текущей ревизией.
  • Exactly-once нет. Announce повторяет отправку, только если до получателя точно ничего не дошло. Доставку со статусом unknown не пересылают, чтобы не дублировать. Публикация в newsroom должна быть идемпотентной: повторный запуск с тем же материалом не создаёт второй.
  • Сроки хранения. Законченные flow и записи задач живут 7 дней, история прогонов automations — тоже 7 дней и не больше 2000 строк на задание. Нужен архив выпусков — пишите его сами.
  • Gateway должен работать. Расписания живут в его процессе. Слоты, пропущенные, пока Gateway лежал, по умолчанию догоняются при старте; cron.skipMissedJobs: true их пропускает.
  • Автоотключение. После 10 падений подряд задание выключается само. Оповещение об ошибках по умолчанию приходит после 2 падений подряд, не чаще раза в час, в тот же канал announce.
  • Бюджет времени. Без --timeout-seconds изолированные прогоны ограничены часовым сторожем планировщика. Ставьте таймаут явно, как в шаге 3.

Когда брать другое:

  • Temporal — если нужен replay по истории событий и гарантия, что workflow дойдёт до конца ровно один раз. Например, когда публикация — это деньги или юридически значимое действие.
  • n8n — если граф фиксирован, модель нужна в одном узле, а основная работа — коннекторы к сервисам.
  • systemd-таймер и скрипт — если модели в пайплайне нет вовсе: скачать, отфильтровать правилом, выложить. Если такой скрипт всё же зовёт openclaw agent, документация советует timeout -k 60 600 openclaw agent … или TimeoutStopSec в юните.

OpenClaw здесь оправдан, пока суть пайплайна в суждениях: что важно, как сказать, где соврал черновик. Детерминированную обвязку он держит достаточно, чтобы суждения работали по расписанию.