Подробные статьи о технологиях, определяющих будущее.

Внутри агентного CLI для кода: циклы, контекст, права

Что делает агентный CLI для кода рабочим: циклы вызова инструментов, управление контекстом, модели разрешений и архитектура клиент/сервер.

Небольшое светящееся ядро, окружённое слоями шестерён, труб и клапанов, иллюстрирует разницу между обвязкой (harness) и моделью.
В агентном CLI модель — это маленькое ядро; обвязка вокруг неё — большая часть машины.

Модель — это простая часть. Так, по крайней мере, утверждается, и после того как я последние пару лет наблюдал, как поднимаются, форкаются и спотыкаются десятки агентных CLI для кода, я думаю, что это правда: агентный CLI для кода выигрывает или проигрывает за счёт своей обвязки (harness), а не модели. Выход OpenCode 2.0 — open-source агента для кода в терминале, который на этой неделе набрал серьёзную популярность, — хороший повод сказать это вслух. Самое интересное в этом релизе — не новые интеграции с моделями. Это разделение на клиент и сервер, горячая перезагрузка плагинов, фоновые задачи и переписанный цикл агента. Иначе говоря, инфраструктура. Инфраструктура и есть продукт.

Я бывший ML-инженер, так что этот вывод меня раздражал. Я хотел, чтобы решающим фактором было качество модели. Но когда я смотрю, почему я продолжаю пользоваться такими инструментами или бросаю их, ответ никогда не звучит как «модель стала умнее». Он звучит так: «инструмент перестал просить подтверждать каждую команду», или «наконец-то перечитывает конфиг без перезапуска», или «я могу подключить GUI к сессии, которая работает на удалённой машине». Давайте разберём три подсистемы, которые реально определяют, полезен ли агент для кода: цикл вызова инструментов, управление контекстом и модель разрешений. А потом я признаю, где контринтуитивный тезис ломается.

Цикл вызова инструментов — это распределённая система

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

Подумайте, что цикл на самом деле должен обрабатывать. Выполнение инструментов падает: компилятор выдаёт ошибки, тесты зависают, файл по этому пути не существует, потому что модель его выдумала. Модель генерирует некорректный JSON в аргументах инструмента. Вызов занимает сорок секунд, и пользователь нажимает Escape посреди выполнения. Два вызова приходят за один ход, и если первый упал, второй не должен запускаться. Окно контекста заполняется посреди задачи, и что-то должно уступить. Каждая из этих проблем — задача про конкурентность и отказоустойчивость, а не про интеллект. Я уже писал, что LLM-агенты — это просто распределённые системы, и CLI для кода — самый чистый пример: есть ненадёжный, недетерминированный удалённый компонент (модель), который координируется с локальными компонентами с побочными эффектами (shell, файловая система, LSP), а цикл — это протокол консенсуса между ними.

Поэтому важно, что OpenCode 2.0, по сообщениям, переписал цикл на основе Effect — TypeScript-фреймворка для структурированной конкурентности и типизированных ошибок. Этот выбор показывает, что команда перестала считать цикл «while-циклом с fetch внутри» и начала относиться к нему как к тому, чем он и является: системе прерываемых, компонуемых, повторяемых эффектов с явными каналами ошибок. Когда у цикла агента есть настоящая семантика отмены, «пользователь нажал Escape» становится полноценным событием, а не порчей состояния. Когда результаты инструментов типизированы, можно валидировать вывод модели до выполнения — это хорошо сочетается с техниками вроде constrained decoding, где вы ограничиваете пространство вывода модели, и некорректные вызовы инструментов становятся невозможными, а не просто отловленными.

Клиент/сервер — функция, о которой никто не рассказывает в маркетинге

Одно из самых важных архитектурных решений CLI для кода — является ли агент процессом или сервисом. Наивный дизайн — один процесс, один терминал, сессия умирает вместе с терминалом — так начинали почти все эти инструменты, и это потолок. Модель клиент/сервер в OpenCode (долгоживущий сервер владеет сессиями, а к нему подключаются любое число клиентов — TUI, десктопное приложение, веб-интерфейс) снимает этот потолок. На практике это значит: сессия переживает закрытие терминала, можно запускать агента на удалённой dev-машине и управлять им из нативного приложения на ноутбуке, а несколько интерфейсов могут наблюдать за одной и той же сессией.

Если вы когда-нибудь пытались запустить агента на VM по SSH и сессия слетала при переподключении, вы понимаете, что это не удобство. Это разница между игрушкой и инфраструктурой. Долгоживущие агенты относятся к той же категории, что сборки и наборы тестов: их запускаешь, уходишь по делам и проверяешь потом. Фоновые задачи в OpenCode 2.0 движутся в том же направлении — агент перестаёт быть синхронным диалогом и становится раннером задач с чат-интерфейсом.

Теперь оговорка, потому что я придерживаюсь этого мнения последовательно: серверная архитектура не означает постоянного демона. Несколько комментаторов с подозрением отметили, что каждая обвязка агента внезапно хочет постоянный фоновый процесс на вашей машине. Этот инстинкт правильный. Я уже писал, что CLI-инструменты не должны по умолчанию ставить фоновых демонов, и я на этом стою. Есть разумный компромисс: запускать сервер по требованию, в рамках директории проекта, и останавливать, когда отключается последний клиент. Вы получаете сохранение сессии и подключение нескольких клиентов без постоянно работающего процесса, который что-то передаёт с вашей машины. То, что OpenCode загружает конфиг из папки сессии, а не из рабочей директории сервера — ещё одно изменение 2.0, — говорит о том, что сервер должен следовать за проектом, а не наоборот.

Управление контекстом — там, где живёт качество

Спросите любого практика, почему агент «поглупел» после часа работы, и ответ почти никогда не будет регрессией модели. Это context rot — «гниение контекста». Окно заполнено устаревшим выводом инструментов, тупиками и полным текстом файлов, которые были релевантны сорок ходов назад. Модель теперь принимает решения, копаясь в стоге сена, и точность поиска иголки деградирует задолго до номинального лимита токенов.

Хорошая обвязка рассматривает контекст как бюджет с политикой распределения, а не как трубу. Конкретно это означает несколько вещей, работающих вместе:

  • Обрезка вывода инструментов с возможностью получить остальное. Лог тестов на 5000 строк должен попадать в контекст как сорок упавших строк плюс заметка, что полный лог лежит по пути, который модель может перечитать. Обвязка решает, что важно; модель решает, когда нужно больше.
  • Подсессии и субагенты. «Изучи, как устроена авторизация в этом репозитории, и вернись с отчётом» должно выполняться в изолированном контексте, который возвращает краткую сводку, а не всю стенограмму исследования. Улучшенная коммуникация между подсессиями в OpenCode 2.0 — ровно про эту идею.
  • Компакция, сохраняющая намерение. Когда окно заполняется, кто-то должен суммаризировать разговор. Обвязка, которая суммаризирует наивно, теряет ограничения пользователя («не трогай модуль биллинга») вместе с шумом. Хорошая компакция сохраняет решения и ограничения дословно и сжимает только повествование.
  • Конфиги и навыки как контекст, а не код. Инструкции на уровне проекта, инструменты, определённые плагинами, и файлы навыков — всё это занимает окно. Горячая перезагрузка — ещё одно исправление 2.0 — важна, потому что редактирование контекста агента без перезапуска сессии — это и есть способ итерировать над поведением самого агента.

Ни одно из этого не является интеллектом модели. Но всё это определяет, насколько умной модель кажется. Фронтирная модель с небрежной политикой контекста проиграет более дешёвой модели с дисциплинированной обвязкой в любой задаче длиннее двадцати минут.

Стеклянная воронка сжимает потоки документов в тонкий луч света, символизируя сжатие контекста.
Управление контекстом — это сжатие с суждением: всё попадает внутрь, наружу выходит только важное.

Модели разрешений: скучная функция, которая решает, примут ли инструмент

Вот вопрос из обсуждения на HN, который я считаю самым важным продуктовым вопросом в этой области: есть ли что-то между «подтверждать каждую команду» и «режимом yolo»? Полное автоматическое одобрение действительно опасно: агент для кода с доступом к shell может за один ход выполнить rm -rf, git push --force и утащить ваши переменные окружения. Полное ручное подтверждение действительно невыносимо: если подтверждать восемьдесят команд в час, вы уже не контролируете агента, а работаете медленной человеческой клавиатурой для прерываний. Инструмент, который найдёт середину, выиграет у прагматиков.

Середина — это градуированная модель разрешений, и её пространство проектирования понято лучше, чем многие думают:

  • Только чтение, запись и выполнение. Чтение почти бесплатно; запись должна быть ограничена деревом проекта; выполнение — там, где политика важнее всего.
  • Allowlist с учётом аргументов. git status одобряется автоматически; git push запрашивает подтверждение. Наивные инструменты сопоставляют имя бинарника; хорошие — глагол и флаги, потому что npm test и npm publish — это один и тот же бинарник с совершенно разным радиусом поражения.
  • Разрешения в рамках сессии. «Всегда разрешать эту команду в этой сессии» — самая ценная функция разрешений из всех. Она превращает семьдесят запросов в один.
  • Изоляция для необратимого. Сетевой доступ, всё за пределами директории проекта и всё, что касается учётных данных, должно ограничиваться структурно, а не просто запрашиваться подтверждением.

Именно последний пункт чаще всего недоинвестируют обвязки, потому что запросить подтверждение легко, а изоляция сложна. Но запрос — это вопрос, на который модель надеется, что вы ответите правильно в шесть вечера в пятницу. Песочница для дерева процессов агента — ограниченная сеть, ограниченные монтирования файловой системы — делает целые классы сбоев невозможными независимо от того, что сделают модель или пользователь. Я подробно разобрал компромисс в статье изоляция vs. мониторинг для ИИ-агентов; если коротко: запросы — это мониторинг, песочница — это изоляция, и для всего необратимого нужна изоляция. Экосистема CLI медленно это понимает. Те, кто поймёт быстрее других, заработают доверие, которое позволит пользователям включить автоподтверждение для скучных 90%.

Расширяемость и проблема самохостинговых моделей

Два меньших урока из обсуждения 2.0 заслуживают внимания. Во-первых: «всё — это плагин» побеждает. Пользователи строят навыки, редакторы конфигов и хуки для рабочих процессов поверх этих обвязок, и та обвязка, которая делает плагины первоклассными — с горячей перезагрузкой, возможностью наблюдать за сессиями и менять поведение, — накапливает усилия сообщества, а не борется с ними. Признак того, что OpenCode относится к этому серьёзно: их собственное GitHub-приложение сортирует баг-репорты, воспроизводя проблему и оставляя комментарии с находками, по сути используя агента, чтобы поддерживать самого агента. Это и есть та «самосознательность», которую хвалили пользователи, и это результат плагинной архитектуры, а не модели.

Во-вторых: независимость от модели сложнее, чем кажется, и это проблема формата контекста. Один пользователь попробовал отличную локальную модель, обученную выдавать команды в bash, а обвязка упрямо ожидала вызовы инструментов в стиле JavaScript — и две стороны просто не могли взаимодействовать. Это скрытая связность в каждой обвязке агента: формат вызова инструментов, допущения системного промпта и распределение обучающих данных модели должны совпадать. Обвязка, которая зашивает соглашения одного семейства моделей, не независима от модели, а в лучшем случае терпима к ней. Если локальные модели будут важны для агентов для кода — а по соображениям стоимости и приватности они должны быть важны, — обвязкам нужны подключаемые диалекты вызовов инструментов, а не только подключаемые эндпоинты API.

Где контринтуитивный тезис ломается

Я сказал, что модель — простая часть. Пора признать границы. Тезис верен для дизайна обвязки — циклов, контекста, разрешений, — но есть места, где модель действительно является узким местом. Надёжность использования инструментов сама по себе зашита в модели; модель, которая не может стабильно выдавать корректные вызовы функций, делает любую обвязку похожей на сломанную, и никакая структурированная конкурентность на основе Effect не исправит модель, которая спорит со своей собственной схемой. Многошаговое планирование на длинной дистанции всё ещё разделяет уровни моделей так, что это ощущается внутри одной сессии. И рамка «обвязка против модели» имеет срок годности: по мере того как модели впитывают агентное поведение в обучение (нативное использование инструментов, нативное долгосрочное планирование), возможности переезжают из обвязки в веса, и сегодняшняя умная фича обвязки становится завтрашними потраченными усилиями. Команды, которые пишут обвязки, должны считать, что строят леса, которые модели в итоге сделают ненужными. Честно говоря, это аргумент в пользу open-source обвязок с экосистемами плагинов, потому что такие проекты живут за счёт сообщества, а не за счёт временного разрыва в возможностях.

Есть ещё и пол UX, который никакая архитектура не оправдывает. Некоторые пользователи попробовали OpenCode 2.0 и всё равно нашли его неуклюжим; другие жаловались на медленный запуск и растущую базу мёртвой истории чатов. Это не глубокие проблемы, но именно они заставляют людей бросить инструмент в первые десять минут, ещё до того как элегантный цикл агента успеет себя показать. Полировка тоже фича, и её чаще всего недофинансируют open-source проекты.

Модель пишет код, но обвязка решает, доверяете ли вы ей достаточно, чтобы её отпустить.

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