Постоянные цели (/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 проходит для этой директории
Что вы увидите:
- Цель принята —
⊙ Цель установлена (лимит: 20 шагов):<ваша цель>` - Шаг 1 выполняется — VibeOS начинает работу, как если бы вы отправили цель как обычное сообщение.
- Судья запускается — после шага модель-судья решает:
done(выполнено) илиcontinue(продолжить). - Цикл срабатывает при необходимости — если
continue, вы увидите↻ Продолжение к цели (1/20):<причина судьи>`, и VibeOS автоматически сделает следующий шаг. - Завершение — в итоге вы видите либо
✓ Цель достигнута: <причина>, либо⏸ Цель приостановлена — использовано N/20 шагов`.
Команды
| Команда | Что делает |
|---|---|
/goal <текст>` | Установить (или заменить) долгосрочную цель. Немедленно запускает первый шаг, так что вам не нужно отправлять отдельное сообщение. |
/goal draft <текст>` | Составить структурированный контракт выполнения из описания задачи на простом языке, затем установить его. См. Контракты выполнения. |
/goal show | Показать контракт выполнения активной цели. |
/goal или /goal status | Показать текущую цель, её статус и использованные шаги. |
/goal pause | Остановить цикл автоматического продолжения без очистки цели. |
/goal resume | Возобновить цикл (сбрасывает счётчик шагов до нуля). |
/goal clear | Полностью удалить цель. |
/goal wait <pid> [причина] | Приостановить цикл в ожидании фонового процесса — он перестаёт повторно обращаться к агенту на каждом шаге, пока процесс выполняется, и автоматически возобновляется, когда процесс завершается. |
/goal unwait | Убрать барьер ожидания и немедленно возобновить цикл. |
Работает одинаково в CLI и на всех платформах-шлюзах (Telegram, Discord, Slack, Matrix, Signal, WhatsApp, SMS, iMessage, Webhook, API-сервер и веб-панель).
Контракты выполнения
Простой /goal <текст> работает нормально, но *расплывчатая* цель приводит к расплывчатому суждению — судья может проверить только то, что вы сказали ему хотеть. Руководство по /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 <N> | Удалить N-ю подцель (нумерация с 1). |
/subgoal clear | Удалить все подцели, но оставить исходную цель нетронутой. |
Подцели сохраняются вместе с целью в SessionDB.state_meta, поэтому они переживают /resume. Установка новой /goal <текст> заменяет цель и очищает список подцелей; /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 <id>** — снимается, когда срабатывает *собственный триггер* процесса: он завершается **или** (если был запущен сwatch_patterns) его шаблон совпадает. Это для долгоживущего наблюдателя / сервера / опросчика, который сигнализирует **в середине выполнения** (например, процесс сборки, который выводитBUILD SUCCESSFULи продолжает работу, или наблюдательnotify_on_complete`) и может никогда не завершиться сам по себе. wait_on_pid<pid>` — снимается только при завершении процесса.wait_for_seconds<n>` — снимается после фиксированной задержки.
Вам не нужно ничего вводить для этого — это решение судьи, принятое на основе контекста процесса, который передаёт ему цикл. Ручные команды существуют как переопределение:
| Команда | Что делает |
|---|---|
/goal wait <pid> [причина] | Вручную приостановить цикл до завершения процесса с указанным 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": <bool>, "reason": "<однопредложное обоснование>"}
Судья намеренно консервативен: он помечает цель как 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 <новый текст>) отклоняется с сообщением, предлагающим сначала выполнить /stop`, чтобы старое продолжение не могло конкурировать с новым.
Сохранение состояния
Состояние цели хранится в SessionDB.state_meta по ключу goal:<session_id>. Это означает, что /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 на стороне шлюза), но идея принадлежит им. Воздаём должное, где это необходимо.