Перейти к основному содержимому

Постоянные цели (/goal)

/goal даёт VibeOS долгосрочную задачу, которая сохраняется между шагами. После каждого шага лёгкая модель-судья проверяет, достигнута ли цель последним ответом ассистента. Если нет, VibeOS автоматически подаёт промпт продолжения обратно в тот же сеанс и продолжает работу — пока цель не будет достигнута, вы не приостановите или не очистите её, или не закончится лимит шагов.

Это наша реализация цикла Ralph, напрямую вдохновлённая /goal из Codex CLI 0.128.0 Эрика Траута (OpenAI). Основная идея — поддерживать цель активной на протяжении шагов и не останавливаться, пока она не будет достигнута — принадлежит им. Реализация здесь независима и адаптирована под архитектуру VibeOS.

Когда использовать​

Используйте /goal для задач, где вы хотите, чтобы VibeOS выполняла итерации самостоятельно, без вашего повторного ввода на каждом шаге:

  • «Исправь все ошибки линтинга в src/ и проверь, что ruff check проходит»
  • «Перенеси функцию X из репозитория Y, включая тесты, и добейся зелёного CI»
  • «Выясни, почему ID сессий иногда дрейфуют при сжатии в середине выполнения, и напиши отчёт»
  • «Создай небольшую утилиту CLI для переименования файлов по их EXIF-датам, затем протестируй её на папке photos/»

Задачи, где агенту достаточно одного шага, не требуют /goal. Задачи, где вам пришлось бы трижды сказать «продолжай» — вот где эта функция сияет.

Быстрый старт​

/goal Исправь все падающие тесты в tests/vibeos_cli/ и убедись, что scripts/run_tests.sh проходит для этой директории

Что вы увидите:

  1. Цель принята — ⊙ Цель установлена (лимит: 20 шагов): <ваша цель>`
  2. Шаг 1 выполняется — VibeOS начинает работу, как если бы вы отправили цель как обычное сообщение.
  3. Судья запускается — после шага модель-судья решает: done (выполнено) или continue (продолжить).
  4. Цикл срабатывает при необходимости — если continue, вы увидите ↻ Продолжение к цели (1/20): <причина судьи>`, и VibeOS автоматически сделает следующий шаг.
  5. Завершение — в итоге вы видите либо ✓ Цель достигнута: &lt;причина&gt;, либо ⏸ Цель приостановлена — использовано N/20 шагов`.

Команды​

КомандаЧто делает
/goal <текст>`Установить (или заменить) долгосрочную цель. Немедленно запускает первый шаг, так что вам не нужно отправлять отдельное сообщение.
/goal draft <текст>`Составить структурированный контракт выполнения из описания задачи на простом языке, затем установить его. См. Контракты выполнения.
/goal showПоказать контракт выполнения активной цели.
/goal или /goal statusПоказать текущую цель, её статус и использованные шаги.
/goal pauseОстановить цикл автоматического продолжения без очистки цели.
/goal resumeВозобновить цикл (сбрасывает счётчик шагов до нуля).
/goal clearПолностью удалить цель.
/goal wait &lt;pid&gt; [причина]Приостановить цикл в ожидании фонового процесса — он перестаёт повторно обращаться к агенту на каждом шаге, пока процесс выполняется, и автоматически возобновляется, когда процесс завершается.
/goal unwaitУбрать барьер ожидания и немедленно возобновить цикл.

Работает одинаково в CLI и на всех платформах-шлюзах (Telegram, Discord, Slack, Matrix, Signal, WhatsApp, SMS, iMessage, Webhook, API-сервер и веб-панель).

Контракты выполнения​

Простой /goal &lt;текст&gt; работает нормально, но *расплывчатая* цель приводит к расплывчатому суждению — судья может проверить только то, что вы сказали ему хотеть. Руководство по /goal` из Codex указывает на то же самое: долгосрочная задача работает лучше всего, когда она называет что означает «готово», как это доказать, что нельзя ломать, что входит в область действия и когда остановиться. VibeOS адаптирует это как опциональный контракт выполнения, наложенный поверх существующего цикла целей.

Контракт имеет пять полей, все опциональны:

ПолеЗначение
outcomeЕдинственное конечное состояние, которое должно быть истинным по завершении.
verificationКонкретный тест / команда / артефакт, который доказывает результат.
constraintsЧто не должно измениться или регрессировать.
boundariesКакие файлы, директории, инструменты или системы входят в область действия.
stop_whenУсловие, при котором VibeOS должен остановиться и запросить ввод.

Когда контракт установлен, оба промпта меняются: промпт продолжения указывает агенту нацелиться на поверхность верификации и соблюдать ограничения, а промпт судьи решает done только когда критерий верификации выполнен с конкретными доказательствами (результат команды, фрагмент файла, вывод теста) — а не на основе расплывчатого утверждения «выглядит готовым». Это напрямую устраняет самый распространённый режим отказа /goal (преждевременное завершение или бесконечное продолжение при недостаточно конкретной задаче).

Два способа установить контракт​

1. Позволить VibeOS составить его (рекомендуется — адаптировано из совета Codex «позвольте агенту составить цель»):

/goal draft Мигрируй сервис аутентификации с сессионных кук на JWT

VibeOS разворачивает вашу однострочную задачу в полный контракт через вспомогательную модель goal_judge, устанавливает его и показывает результат, чтобы вы могли просмотреть или уточнить любое поле. Если вспомогательная модель недоступна, он возвращается к обычной свободной цели — составление контракта никогда не блокирует установку цели.

2. Написать его в строке со строками поле: значение:

/goal Мигрируй аутентификацию на JWT
verify: pytest tests/auth проходит
constraints: сохрани неизменной структуру ответа /login
boundaries: трогай только services/auth и его тесты
stop when: требуется миграция схемы БД

Первая строка (или строки) без поля — это заголовок цели; распознанные префиксы полей (verify:, verified by:, constraints:, preserve:, boundaries:, scope:, stop when:, blocked:, …) заполняют контракт. Обычная цель с двоеточием внутри (Исправь баг: парсер пропускает запятые) не искажается — извлекаются только известные префиксы полей.

Используйте /goal show для просмотра активного контракта. Контракты сохраняются в SessionDB.state_meta вместе с целью, поэтому они переживают /resume. Старые цели, установленные до появления этой функции, загружаются без изменений (без контракта). Контракты и критерии /subgoal комбинируются: подцели включаются в контракт как дополнительные критерии, которые судья также должен удовлетворить.

Добавление критериев в процессе: /subgoal​

Пока цель активна, вы можете добавить дополнительные критерии приемки с помощью /subgoal <текст>` без сброса цикла. Каждый вызов добавляет один пронумерованный элемент в список подцелей цели; промпт продолжения, который агент видит на следующем шаге, включает исходную цель плюс блок «Дополнительные критерии, добавленные пользователем в процессе», а промпт судьи переписывается так, чтобы вердикт учитывал каждую подцель — цель не считается выполненной, пока не выполнены исходная задача и все подцели.

КомандаЧто делает
`/subgoal <текст>Добавить новый критерий к активной цели. Требует активной /goal.
/subgoal (без аргументов)Показать текущий нумерованный список подцелей.
/subgoal remove &lt;N&gt;Удалить N-ю подцель (нумерация с 1).
/subgoal clearУдалить все подцели, но оставить исходную цель нетронутой.

Подцели сохраняются вместе с целью в SessionDB.state_meta, поэтому они переживают /resume. Установка новой /goal &lt;текст&gt; заменяет цель и очищает список подцелей; /goal clear` делает то же самое.

Используйте это, когда вы запускаете цикл («исправь падающие тесты») и замечаете в процессе, что хотите также «и добавь регрессионный тест для бага, который ты только что исправил» — /subgoal add a regression test ужесточает критерии успеха, не прерывая работающий цикл.

Ожидание фонового процесса: автоматически, с ручным управлением​

Некоторые цели зависят от чего-то, что занимает минуты и выполняется самостоятельно — CI на отправленном PR, долгая сборка, матрица тестов, развёртывание, ожидание снятия ограничения скорости. Без помощи цикл целей будет на каждом шаге обращаться к агенту с вопросом «уже готово?», занимаясь бесполезной работой во время ожидания.

Это обрабатывается автоматически. На каждом шаге судье показываются активные фоновые процессы агента (реестр terminal(background=true) — pid, id сессии, команда, время работы, последний вывод и любые триггеры watch_patterns / notify_on_complete) вместе с целью и ответом агента. Когда прогресс агента действительно заблокирован одним из них, судья возвращает вердикт wait вместо continue, и цикл приостанавливается: следующие шаги пропускаются (без вызова судьи, без продолжения, без расхода шагов), пока ожидание не будет удовлетворено — затем он возобновляется нормально с полученным результатом. Судья также может приостановить на временной основе (wait_for_seconds) для ожидания отката/паузы. /goal status показывает ⏳ Цель (приостановлена …) во время ожидания.

Судья выбирает правильный тип ожидания на основе сигнала самого процесса:

  • **wait_on_session &lt;id&gt;** — снимается, когда срабатывает *собственный триггер* процесса: он завершается **или** (если был запущен с watch_patterns) его шаблон совпадает. Это для долгоживущего наблюдателя / сервера / опросчика, который сигнализирует **в середине выполнения** (например, процесс сборки, который выводит BUILD SUCCESSFULи продолжает работу, или наблюдательnotify_on_complete`) и может никогда не завершиться сам по себе.
  • wait_on_pid <pid>` — снимается только при завершении процесса.
  • wait_for_seconds <n>` — снимается после фиксированной задержки.

Вам не нужно ничего вводить для этого — это решение судьи, принятое на основе контекста процесса, который передаёт ему цикл. Ручные команды существуют как переопределение:

КомандаЧто делает
/goal wait &lt;pid&gt; [причина]Вручную приостановить цикл до завершения процесса с указанным PID.
/goal unwaitУбрать любой барьер ожидания (установленный судьёй или вручную) и немедленно возобновить.

Барьер (на основе PID или времени) сохраняется вместе с целью в SessionDB.state_meta, поэтому он переживает /resume. /goal pause, /goal resume и /goal clear — все сбрасывают его. Если PID уже мёртв, когда барьер устанавливается (или умирает во время ожидания), или срок времени истёк, барьер очищается при следующей проверке — устаревший барьер никогда не может заблокировать цикл.

Типичный поток: агент отправляет PR, запускает наблюдатель CI с terminal(background=true, notify_on_complete=true) и сообщает «наблюдаю за CI». Судья видит, что процесс наблюдателя всё ещё работает, возвращает wait по его pid, и цикл затихает — затем возобновляется в тот же момент, когда CI завершается, и оценивает цель по фактическому результату.

Детали поведения​

Судья​

После каждого шага VibeOS вызывает вспомогательную модель с:

  • Текстом долгосрочной цели
  • Самым последним финальным ответом агента (последние ~4 КБ текста)
  • Системным промптом, предписывающим судье отвечать строгим JSON: {"done": &lt;bool&gt;, "reason": "&lt;однопредложное обоснование&gt;"}

Судья намеренно консервативен: он помечает цель как done только тогда, когда ответ явно подтверждает завершение цели, когда конечный результат чётко получен или когда цель недостижима/заблокирована (рассматривается как DONE с причиной блокировки, чтобы не тратить бюджет на невыполнимые задачи).

Отказоустойчивая семантика​

Если судья ошибается (сетевая проблема, некорректный ответ, недоступный вспомогательный клиент), VibeOS трактует вердикт как continue — сломанный судья никогда не блокирует прогресс. Лимит шагов является реальной подстраховкой.

Лимит шагов​

По умолчанию — 20 шагов продолжения (goals.max_turns в config.yaml). Когда лимит исчерпан, VibeOS автоматически приостанавливается и сообщает вам, как действовать дальше:

⏸ Цель приостановлена — использовано 20/20 шагов. Используйте /goal resume для продолжения или /goal clear для остановки.

/goal resume сбрасывает счётчик до нуля, так что вы можете продолжать работу измеренными порциями.

Сообщения пользователя всегда имеют приоритет​

Любое реальное сообщение, которое вы отправляете, пока активна цель, имеет приоритет над циклом продолжения. В CLI ваше сообщение попадает в _pending_input перед поставленным в очередь продолжением; в шлюзе оно проходит через адаптер FIFO таким же образом. Судья запускается снова после вашего шага — так что если ваше сообщение случайно завершает цель, судья это заметит и остановится.

Безопасность во время выполнения (шлюз)​

Пока агент уже работает, /goal status, /goal pause, /goal clear, /goal wait и /goal unwait безопасны для выполнения — они затрагивают только состояние плоскости управления и не прерывают текущий шаг. Установка новой цели во время выполнения (/goal &lt;новый текст&gt;) отклоняется с сообщением, предлагающим сначала выполнить /stop`, чтобы старое продолжение не могло конкурировать с новым.

Сохранение состояния​

Состояние цели хранится в SessionDB.state_meta по ключу goal:&lt;session_id&gt;. Это означает, что /resumeпродолжает ровно с того места, где вы остановились — установите цель, закройте ноутбук, вернитесь завтра, выполните/resume`, и цель всё ещё будет активна ровно в том состоянии, в котором вы её оставили (активна, приостановлена или выполнена).

Кэш промптов​

Промпт продолжения — это обычное сообщение с ролью пользователя, добавленное в историю. Он не изменяет системный промпт, не меняет набор инструментов и не затрагивает разговор каким-либо образом, который мог бы инвалидировать кэш промптов VibeOS. Выполнение 20-шаговой цели стоит с точки зрения кэша столько же, сколько 20 шагов обычного разговора.

Конфигурация​

Добавьте в ~/.vibeos/config.yaml:

goals:
# Максимальное количество шагов продолжения, после которого VibeOS
# автоматически приостанавливается и просит вас выполнить
# /goal resume. По умолчанию 20. Уменьшите для более коротких циклов;
# увеличьте для длительных рефакторингов.
max_turns: 20

Выбор модели-судьи​

Судья использует вспомогательную задачу goal_judge. По умолчанию она разрешается в вашу основную модель (см. Вспомогательные модели). Если вы хотите направить судью на дешёвую быструю модель для снижения затрат, добавьте переопределение:

auxiliary:
goal_judge:
provider: openrouter
model: google/gemini-3-flash-preview

Вызов судьи небольшой (~200 выходных токенов) и выполняется один раз за шаг, поэтому дешёвая быстрая модель обычно является правильным выбором.

Пример пошагового выполнения​

Вы: /goal Создай четыре файла /tmp/note_{1..4}.txt, по одному за шаг, каждый содержащий свой номер в виде текста

⊙ Цель установлена (лимит: 20 шагов): Создай четыре файла /tmp/note_{1..4}.txt, по одному за шаг, каждый содержащий свой номер в виде текста

VibeOS: Создаю /tmp/note_1.txt сейчас.
💻 echo "1" > /tmp/note_1.txt (0.1s)
Я создал /tmp/note_1.txt с содержимым «1». Я продолжу с остальными файлами на следующем шаге, как вы указали.

↻ Продолжение к цели (1/20): Создан только 1 из 4 файлов; осталось 3 файла.

VibeOS: [Продолжение к вашей долгосрочной цели]
💻 echo "2" > /tmp/note_2.txt (0.1s)
Создан /tmp/note_2.txt. Осталось ещё два.

↻ Продолжение к цели (2/20): Создано 2 из 4 файлов; осталось 2.

VibeOS: [Продолжение к вашей долгосрочной цели]
💻 echo "3" > /tmp/note_3.txt (0.1s)
Создан /tmp/note_3.txt.

↻ Продолжение к цели (3/20): Создано 3 из 4 файлов; остался 1.

VibeOS: [Продолжение к вашей долгосрочной цели]
💻 echo "4" > /tmp/note_4.txt (0.1s)
Все четыре файла созданы: /tmp/note_1.txt – /tmp/note_4.txt, каждый содержит свой номер.

✓ Цель достигнута: Все четыре файла были созданы с указанным содержимым, цель выполнена.

Вы: _

Четыре шага, один вызов /goal, ноль ваших запросов «продолжай».

Когда судья ошибается​

Ни один судья не идеален. Следите за двумя режимами отказа:

Ложноотрицательный — судья говорит «продолжить», хотя цель фактически достигнута. Лимит шагов подстрахует. Вы увидите ⏸ Цель приостановлена и сможете выполнить /goal clear или просто отправить новое сообщение.

Ложноположительный — судья говорит «выполнено», хотя работа осталась. Вы увидите ✓ Цель достигнута, но знаете, что это не так. Отправьте последующее сообщение, чтобы продолжить, или переустановите цель более точно: /goal <более конкретный текст>`. Системный промпт судьи намеренно консервативен, чтобы ложноположительные результаты были реже ложноотрицательных.

Если вы считаете вердикт судьи неубедительным, текст причины в строке ↻ Продолжение к цели или ✓ Цель достигнута скажет вам именно то, что увидел судья. Обычно этого достаточно, чтобы диагностировать, был ли текст цели неоднозначным или ответ модели был таковым.

Атрибуция​

/goal — это реализация VibeOS паттерна цикла Ralph. Дизайн, ориентированный на пользователя — поддержание цели активной на протяжении шагов, остановка только после достижения, с командами создания/паузы/возобновления/очистки — был популяризирован и реализован в Codex CLI 0.128.0 Эриком Траутом из команды Codex в OpenAI. Наша реализация независима (центральный реестр CommandDef, сохранение в SessionDB.state_meta, судья через вспомогательный клиент, продолжение через адаптер FIFO на стороне шлюза), но идея принадлежит им. Воздаём должное, где это необходимо.