Планирование изменения
Дайте планировщику запрос и handbook — получите байт-точный план правок и машиночитаемую декларацию того, чего он касается.
handbook plan --source <repo> --handbook <dir> --request "<text>" --out plan.mdПланировщик — агент только для чтения. Он просматривает каталоги, читает файлы и ищет по grep — у него вообще нет инструмента записи, даже отключённого, — а его результат — план, который выполнит кто-то другой.
Цикл
- Маршрутизация по handbook: какие файлы, функции и состояние в зоне охвата?
- Чтение реального исходника по каждому найденному адресу.
- Вывод блоков
### EDIT nс байт-точным текстомoldиnew. - Завершение JSON-блоком деклараций.
Два артефакта, две роли
Handbooks — это индекс местоположений: он показывает разбросанные, неочевидные места, которые пропускает текстовый поиск, — зеркальные реализации, каждое чтение и каждую запись элемента состояния, точки соприкосновения подсистем. Реальный исходник — истина о том, что менять. Handbooks даёт адрес; код по этому адресу даёт байты.
Как написать хороший запрос
| Слабо | Сильно |
|---|---|
| «Почини баг с загрузкой» | «Загрузки, завершившиеся ошибкой 503, должны повторяться три раза с экспоненциальной выдержкой, прежде чем показывать ошибку» |
| «Добавь логирование» | «Логировать id запроса и длительность на уровне INFO для каждого завершённого HTTP-запроса, используя существующий логгер» |
| «Сделай быстрее» | «Кешировать результат resolveTenant на 60 секунд с ключом по id арендатора» |
Формулируйте желаемое поведение, а не файл, в котором оно, по-вашему, находится. Название файла сужает поиск планировщика до места, о котором вы уже подумали, — а это обесценивает саму затею.
Чтение плана
### EDIT 1
- file: `src/upload.py`
- where: `Uploader.send (~88)` — wrap the request in the retry helper
```old
response = self._client.put(url, data)
```
```new
response = self._retry(lambda: self._client.put(url, data), attempts=3)
```
### EDIT 2
- file: `src/upload.py`
- where: `Uploader` — add the helper
```old
def send(self, url, data):
```
```new
def _retry(self, call, attempts):
last = None
for _ in range(attempts):
try:
return call()
except TransientError as exc:
last = exc
raise last
def send(self, url, data):
```
Both call sites now share one retry policy.
```json
{ "will_modify": ["Uploader.send"], "will_add": ["Uploader._retry"], "will_remove": [] }
```Правила, которым подчиняется формат:
oldдолжен быть байт-точным и встречаться в файле ровно один раз.- Пустой
oldозначает «создать этот файл». - Правки нумеруются и идут по возрастанию, сверху вниз.
- Завершающий блок
jsonпотребляется командойresync, чтобы уточнить охват обновления.
Прочитайте план, прежде чем применять. Пробный прогон скажет, применится ли он; только вы можете сказать, стоит ли.
Когда он сдаётся
plan выходит с ненулевым кодом — он не пишет извинение в plan.md, чтобы скрипт
скормил его apply.
aborted | Что случилось | Что делать |
|---|---|---|
fabrication | Ответ трижды изобретал секции ## Tool result — модель рассуждала над воображаемым содержимым файлов | Используйте более сильную модель. Ничему из этого запуска нельзя доверять |
turn-limit | Ходы закончились без единого блока EDIT | Повысьте --max-turns или сузьте запрос |
no-plan | Вызвал finish без чего-либо пригодного | Обычно запрос, не требующий изменений кода, или слишком расплывчатый для локализации |
Почему фабрикация отклоняется целиком
Один наблюдавшийся ответ содержал тринадцать сфабрикованных результатов инструментов и план, построенный на строке, которой нет в файле. Планировщик отклоняет такой ответ целиком — включая план в его конце, потому что план выведен из вымысла.
Настройка
| Флаг | По умолчанию | Когда менять |
|---|---|---|
--max-turns <n> | 30 | Повышайте для большого репозитория или широкого изменения; понижайте, чтобы ограничить стоимость |
--model <id> | gpt-4o-mini | Это команда, которая больше всех выигрывает от более сильной модели |
--handbook <dir> | — | Передавайте всегда. Без него планировщик исследует вслепую |
--out <file> | (stdout) | Опустите, чтобы перенаправлять вывод дальше |
Без handbook
handbook plan --source ~/code/api --request "…"Это работает — планировщик откатывается к прямому исследованию исходника, — но это деградированный режим. Handbooks существует именно потому, что ненаправленное исследование находит очевидные места и пропускает разбросанные.
Что разрешает песочница
list_dir(path)
read_file(path, start_line?, end_line?)
grep(pattern, path)
finish(plan)- Каждый путь разрешается внутри корня песочницы; побеги, в том числе через симлинки, отклоняются.
- Handbooks смонтирован только для чтения в
__handbook__/— это отдельная песочница, не та, что для исходника. - Чтение ограничено 60 000 символами; grep ограничен 100 совпадениями и пропускает файлы больше 5 МБ.
- Катастрофические регулярные выражения — неограниченный квантификатор над группой,
которая сама его содержит, вроде
(a+)+или(.*)*— отклоняются аккуратной ошибкой инструмента, а не подвешивают запуск.
Дальше
Упаковка для вашего агента
Превратите отрендеренный handbook в пакет SKILL с обнаружением дрейфа и подключите его к кодинг-агенту.
Применение и откат
Механический исполнитель с четырьмя правилами безопасности, резервная копия, способная доказать, что она восстанавливает, и парсер, отвергающий всё неоднозначное.