Автономия с поводком: что агент делает сам, что под ревьюером, что только с вами

Как разложить действия агента на три уровня доверия и выразить каждый настройками OpenClaw, а не просьбами в AGENTS.md.

· 6 мин чтения

Коротко

  • У каждого действия агента свой поводок. Повторяющееся агент делает сам, новую команду сначала смотрит LLM-ревьюер (модель, которая оценивает команду до запуска), отправку наружу и необратимое решаете вы.
  • В OpenClaw это список разрешённых команд (allowlist) и постоянные разрешения для задач cron (standing grants), режим tools.exec.mode: "auto" и хук before_tool_call, который требует вашего одобрения. Запрещённое насовсем снимает политика инструментов.
  • Где не держит: ревьюер смотрит только exec и сам решает, что рискованно; карточки одобрения от cron в Telegram не приходят; «разрешить всегда» у одобрений из плагина по умолчанию ничего не запоминает.

Кому и когда

Агент работает по расписанию или подолгу без присмотра. Режим ask шлёт карточку на каждую новую команду, и через день вы жмёте «разрешить», не читая. Режим full, умолчание хоста, не спрашивает ничего. Нужна середина: звать редко, но по делу.

Собрано по документации и исходникам OpenClaw 2026.9.8 (октябрь 2026); конфиги запуском не проверялись.

Три длины поводка

Уровень Что сюда Чем выражается
Сам Известные команды, одна и та же задача cron allowlist, standing grant
С ревьюером Новые команды exec, которых нет в allowlist tools.exec.mode: "auto"
Только человек Отправка сообщений, публикации, платежи; классы команд, названные заранее (git push, rm -r) хук before_tool_call с requireApproval для message и для exec

Вокруг всех трёх забор: чего агент не должен делать никогда, убирается через tools.deny. Правило в AGENTS.md модель может не выполнить, политику инструментов Gateway исполняет, даже если агента уговорили. Сначала запреты, потом расписание.

Как выбрать уровень

  1. Эффект выходит наружу? Сообщение, пост, платёж — только человек: отправленное не отзовёшь.
  2. Откатывается одной командой? Правка файла под git — да. rm -rf, миграция базы, git push --force — нет: только человек.
  3. Повторяется в одном и том же виде? Тогда в «сам». Остальное, дешёвое по ошибке, — ревьюеру.

Уровень 2: ревьюер

В режиме auto команда из allowlist выполняется сразу, остальные получают вердикт ревьюера:

  • allow — команда низкого или среднего риска выполняется один раз, без запоминания;
  • deny — причина уходит агенту, и он обязан выбрать заметно более безопасный путь или спросить вас;
  • ask — карточка вам. Туда же идут сбой ревьюера, невалидный ответ, таймаут (до 30 секунд на каждый из двух этапов) и третий отказ подряд в одной сессии.

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

Для одной сессии похожее даёт режим workspace в меню Execution permissions в Control UI. Он ещё ограничивает корнем сессии файловые инструменты, но не exec.

Уровень 3: человек

ask и auto — значения одного поля tools.exec.mode, вместе их не включить. Поэтому команды, которые у агента в auto всегда должны идти к вам, назовите хуку before_tool_call с matcher: ["exec"]. Сообщения ревьюер не видит вообще, для них тот же хук на message. /exec ask=always действует только на одно ваше сообщение.

Наружу у message отправляет не только send, поэтому хук ловит явный набор действий. Где отправка агенту не нужна, надёжнее убрать message через tools.deny.

import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";

const OUTBOUND = new Set([
  "send", "broadcast", "reply", "thread-reply", "thread-create",
  "sendWithEffect", "sendAttachment", "upload-file", "poll", "sticker",
  "edit", "delete",
]);
const RISKY_EXEC = [/\bgit\s+push\b/, /\brm\s+-\w*r/];

const ask = (title: string, description: string) => ({
  requireApproval: {
    title,
    description: description.slice(0, 500),
    severity: "critical" as const,
    allowedDecisions: ["allow-once", "deny"],
    timeoutMs: 600_000,
  },
});

export default definePluginEntry({
  id: "leash",
  name: "Leash",
  register(api) {
    api.on(
      "before_tool_call",
      (event) => {
        if (event.toolName === "message") {
          const action = String(event.params.action ?? "");
          if (OUTBOUND.has(action)) return ask("Отправка сообщения", `message.${action}`);
          return;
        }
        const command = String(event.params.command ?? "");
        if (RISKY_EXEC.some((re) => re.test(command))) return ask("Команда", command);
      },
      { matcher: ["message", "exec"], priority: 50 },
    );
  },
});

Хук добавляет вас поверх ревьюера, но сверх exec-политики не разрешит. Регулярки — грубый фильтр: скрипт с git push внутри они не увидят.

Конфиг: поводок, ревьюер и карточки в Telegram. Это для интерактивных сессий, карточки cron туда не приходят.

{
  tools: { exec: { grantExpiryDays: 30 } },
  agents: {
    entries: {
      ops: {
        tools: {
          deny: ["browser"],
          exec: {
            mode: "auto",
            reviewer: { model: "<другой провайдер>/<модель>", thinking: "low" },
          },
        },
      },
    },
  },
  approvals: {
    exec: { enabled: true, mode: "targets", agentFilter: ["ops"], targets: [{ channel: "telegram", to: "<ваш chat id>" }] },
    plugin: { enabled: true, mode: "targets", agentFilter: ["ops"], targets: [{ channel: "telegram", to: "<ваш chat id>" }] },
  },
  plugins: { load: { paths: ["~/.openclaw/policies/leash.ts"] } },
}
  • deny: ["browser"] убирает инструмент у агента совсем, браузер тут для примера.
  • approvals.exec и approvals.plugin независимы, нужны оба.
  • В Telegram карточка приходит с кнопками. Ответ /approve <id> allow-once в том же чате сработает, если вы в списке approvers канала. Выдуманный id ничего не одобряет.
  • Изменили файл политики — openclaw plugins reload leash.

Проверить: openclaw approvals get показывает запрошенную и итоговую политику, openclaw exec-policy show --agent ops — что видит агент. Затем попросите агента отправить сообщение и дождитесь карточки. Откатить: уберите exec.mode у агента и путь из plugins.load.paths. Учтите, что агент вернётся к умолчанию хоста, то есть к full без одобрений. Промежуточный вариант — mode: "ask".

Уровень 1: повторяющееся — в «сам»

Карточка на каждый ночной запуск приучит жать кнопку не глядя. Путей два.

Без клиента одобрений — allowlist. Шаблон — глоб пути к бинарнику или имени команды.

openclaw approvals allowlist add --agent ops "/usr/bin/rsync"

Запись разрешает бинарник с любыми аргументами, сузить их можно полем argPattern в файле одобрений.

Через карточку cron. Она приходит только в Control UI, приложения macOS, iOS и Android или API-клиент с поддержкой одобрений, не в Telegram. Откройте Control UI на время первого запуска и нажмите Always allow: Gateway выпустит standing grant на этого агента, эту задачу и именно эту команду с этой папкой и окружением. Грант слетает, если задачу удалили или изменили, команда отличается хоть на байт, грант отозвали или он истёк. Пауза задачи его не сбрасывает. По умолчанию грант бессрочный, grantExpiryDays: 30 заставит одобрять новые раз в месяц.

openclaw approvals grants list
openclaw approvals grants revoke <grant-id>

Где не держит

  • Ревьюер только для exec. Браузер, сообщения и MCP-инструменты он не смотрит. У агента на Codex-харнессе ревьюер OpenClaw не запускается, команды идут человеку.
  • Cron без клиента одобрений получает отказ сразу. Не подключены ни Control UI, ни приложение — запрос отклоняется. Exec на нодах из автоматизаций карточек не получает вообще.
  • Обёртки требуют человека. sh -c '…', env FOO=1 …, bash -lc нельзя надёжно привязать к исполняемому файлу, поэтому ревьюер их пропускает к вам на разовое одобрение, а в cron без клиента они получают отказ. Попросите в AGENTS.md писать команды без обёрток.
  • «Разрешить всегда» из хука ничего не запоминает. Оба «разрешить» значат «на этот раз», поэтому в примере этой кнопки нет.
  • Тишина значит «нет». Карточка плагина ждёт 2 минуты по умолчанию, максимум 10, потом вызов блокируется. Нет маршрута к человеку — блок сразу.
  • Одобрения не разделяют людей. Любой авторизованный на Gateway для него доверенный оператор. Это защита от случайности, а не граница прав.

Когда не надо

  • Вы и так сидите рядом. В короткой сессии ask проще, ревьюер ничего не сэкономит.
  • Агент только читает и пишет черновики. Уберите у него отправку, и поводок не нужен.
  • Есть только Telegram. Карточки cron не придут: держите ночные задачи на allowlist.