Компоненты кодирующего агента
@ai_longreadsКак кодирующие агенты используют инструменты, память и контекст репозитория, чтобы заставить большие языковые модели работать эффективнее на практике
Это AI-перевод статьи, сделанный каналом Про AI: Лучшие Статьи и Исследования.
Компоненты кодирующего агента
Components of A Coding Agent Автор: Sebastian Raschka, PhD Оригинальный текст:
В этой статье я хочу рассмотреть общий дизайн кодирующих агентов и агентных обвязок: что они собой представляют, как работают и как различные компоненты сочетаются друг с другом на практике. Читатели моих книг Build a Large Language Model (From Scratch) и Build a Large Reasoning Model (From Scratch) часто спрашивают об агентах, поэтому я решил написать справочный материал, на который можно ссылаться.
В более широком смысле агенты стали важной темой, потому что значительная часть недавнего прогресса в практических системах на основе больших языковых моделей связана не только с улучшением самих моделей, но и с тем, как мы их используем. Во многих реальных приложениях окружающая система — использование инструментов, управление контекстом и память — играет не меньшую роль, чем сама модель. Это также объясняет, почему такие системы, как Claude Code или Codex, могут ощущаться значительно более способными, чем те же модели, используемые в обычном чат-интерфейсе.
В этой статье я рассмотрю шесть основных строительных блоков кодирующего агента.
Claude Code, Codex CLI и другие кодирующие агенты
Вы, вероятно, знакомы с Claude Code или Codex CLI, но для контекста — это, по сути, агентные (агентный) инструменты для программирования, которые оборачивают большую языковую модель в прикладной слой, так называемую агентную обвязку (agentic harness), чтобы быть более удобными и производительными для задач программирования.
Кодирующие агенты спроектированы для работы с программным обеспечением, где важными частями являются не только выбор модели, но и окружающая система, включая контекст репозитория, дизайн инструментов, стабильность prompt caching (кэширование промптов), память и непрерывность длительных сессий.
Это разграничение важно, потому что когда мы говорим о возможностях программирования у больших языковых моделей, люди часто смешивают модель, поведение reasoning (рассуждение, цепочка мыслей) и агентный продукт в одно целое. Но прежде чем перейти к специфике кодирующих агентов, позвольте кратко описать разницу между более широкими концепциями: большими языковыми моделями, моделями рассуждений и агентами.
О взаимосвязи между большими языковыми моделями, моделями рассуждений и агентами
Большая языковая модель — это базовая модель следующего токена (токены, единицы текста). Модель рассуждений — это по-прежнему большая языковая модель, но обычно обученная и/или настроенная на то, чтобы тратить больше вычислений на этапе inference (инференс, вывод модели) на промежуточные рассуждения, верификацию или перебор кандидатов-ответов.
Агент — это слой поверх модели, который можно понимать как управляющий цикл вокруг неё. Как правило, получив цель, агентный слой (или обвязка) решает, что исследовать дальше, какие инструменты вызвать, как обновить своё состояние и когда остановиться.
Грубо говоря, взаимосвязь можно представить так: большая языковая модель — это двигатель, модель рассуждений — это усиленный двигатель (более мощный, но и более дорогой в использовании), а агентная обвязка помогает нам использовать модель эффективнее. Аналогия не идеальна, потому что мы можем использовать обычные и рассуждающие модели и как автономные (в чат-интерфейсе или Python-сессии), но надеюсь, она передаёт суть.
Иными словами, агент — это система, которая многократно вызывает модель внутри окружения.
Итого, можно подвести итог так:
- Большая языковая модель: базовая модель
- Модель рассуждений: большая языковая модель, оптимизированная для вывода промежуточных цепочек рассуждений и более тщательной самопроверки
- Агент: цикл, использующий модель плюс инструменты, память и обратную связь от окружения
- Агентная обвязка (agent harness): программный каркас вокруг агента, управляющий контекстом, использованием инструментов, промптами, состоянием и потоком управления
- Кодирующая обвязка (coding harness): частный случай агентной обвязки; специализированная обвязка для разработки ПО, управляющая контекстом кода, инструментами, выполнением и итеративной обратной связью
Как указано выше, в контексте агентов и инструментов для программирования также встречаются два популярных термина — agent harness и (агентная) coding harness. Кодирующая обвязка — это программный каркас вокруг модели, помогающий ей эффективно писать и редактировать код. Агентная обвязка — несколько более широкое понятие, не привязанное к программированию (например, OpenClaw). Codex и Claude Code можно считать кодирующими обвязками.
В любом случае, лучшая большая языковая модель обеспечивает лучшую основу для модели рассуждений (которая предполагает дополнительное обучение), а обвязка извлекает из этой модели рассуждений больше пользы.
Разумеется, большие языковые модели и модели рассуждений способны решать задачи программирования и самостоятельно (без обвязки), однако программирование лишь отчасти связано с генерацией следующего токена. Значительная часть работы — это навигация по репозиторию, поиск, просмотр функций, применение дифов, запуск тестов, анализ ошибок и удержание всей релевантной информации в контексте. (Программисты знают, что это тяжёлая умственная работа, поэтому мы не любим, когда нас отвлекают во время сессий программирования :))
Вывод таков: хорошая кодирующая обвязка способна заставить как рассуждающую, так и обычную модель ощущаться значительно сильнее, чем в простом чат-окне, благодаря помощи с управлением контекстом и другими аспектами.
Кодирующая обвязка
Как упоминалось в предыдущем разделе, говоря «обвязка», мы обычно имеем в виду программный слой вокруг модели, который собирает промпты, предоставляет инструменты, отслеживает состояние файлов, применяет правки, выполняет команды, управляет разрешениями, кэширует стабильные префиксы, хранит память и многое другое.
Сегодня при использовании больших языковых моделей этот слой формирует большую часть пользовательского опыта по сравнению с прямым обращением к модели через промпт или использованием веб-чата (что ближе к «чату с загруженными файлами»).
Поскольку, на мой взгляд, базовые версии современных больших языковых моделей обладают очень схожими возможностями (например, базовые версии GPT-5.4, Opus 4.6 и GLM-5 и подобные), обвязка зачастую может стать тем самым решающим фактором, делающим одну модель более эффективной, чем другую.
Это спекулятивно, но я подозреваю, что если мы поместим одну из последних, наиболее мощных моделей с открытыми весами, например GLM-5, в аналогичную обвязку, она, вероятно, могла бы работать наравне с GPT-5.4 в Codex или Claude Opus 4.6 в Claude Code. Тем не менее, определённое пост-обучение, специфичное для обвязки, обычно полезно. Например, OpenAI исторически поддерживала отдельные варианты GPT-5.3 и GPT-5.3-Codex.
В следующем разделе я хочу углубиться в детали и обсудить основные компоненты кодирующей обвязки на примере моего Mini Coding Agent: https://github.com/rasbt/mini-coding-agent.
Кстати, в этой статье я использую термины «кодирующий агент» и «кодирующая обвязка» в значительной степени взаимозаменяемо для простоты. (Строго говоря, агент — это управляемый моделью цикл принятия решений, тогда как обвязка — это окружающий программный каркас, обеспечивающий контекст, инструменты и поддержку выполнения.)
Ниже приведены шесть основных компонентов кодирующих агентов. Вы можете ознакомиться с исходным кодом моего минимального, но полностью рабочего, написанного с нуля Mini Coding Agent (реализованного на чистом Python) для более конкретных примеров кода. Код аннотирует шесть компонентов, обсуждаемых ниже, через комментарии:
############################## #### Six Agent Components #### ############################## # 1) Live Repo Context -> WorkspaceContext # 2) Prompt Shape And Cache Reuse -> build_prefix, memory_text, prompt # 3) Structured Tools, Validation, And Permissions -> build_tools, run_tool, validate_tool, approve, parse, path, tool_* # 4) Context Reduction And Output Management -> clip, history_text # 5) Transcripts, Memory, And Resumption -> SessionStore, record, note_tool, ask, reset # 6) Delegation And Bounded Subagents -> tool_delegate
1. Живой контекст репозитория
Это, пожалуй, наиболее очевидный компонент, но он также один из самых важных.
Когда пользователь говорит «исправь тесты» или «реализуй xyz», модель должна знать, находится ли она внутри Git-репозитория, на какой ветке, какие документы проекта могут содержать инструкции, и так далее.
Дело в том, что эти детали часто меняются или влияют на то, каким будет правильное действие. Например, «Исправь тесты» — это не самодостаточная инструкция. Если агент видит AGENTS.md или README проекта, он может узнать, какую команду для запуска тестов использовать, и т.д. Если он знает корень и структуру репозитория, он может искать в нужных местах, а не угадывать.
Кроме того, ветка git, статус и коммиты помогают предоставить больше контекста о том, какие изменения сейчас в процессе и на чём следует сосредоточиться.
Вывод: кодирующий агент собирает информацию («стабильные факты» в виде сводки рабочего пространства) заранее, прежде чем приступить к работе, чтобы не начинать каждый промпт с нуля, без контекста.
2. Форма промпта и повторное использование кэша
Как только у агента есть представление о репозитории, следующий вопрос — как подать эту информацию модели. Предыдущая иллюстрация показывала упрощённый вид этого процесса («Объединённый промпт: префикс + запрос»), но на практике было бы довольно расточительно объединять и повторно обрабатывать сводку рабочего пространства при каждом запросе пользователя.
Сессии программирования повторяющиеся, и правила агента обычно остаются прежними. Описания инструментов тоже обычно не меняются. И даже сводка рабочего пространства обычно остаётся (по большей части) прежней. Основные изменения — это обычно последний запрос пользователя, недавняя транскрипция и, возможно, краткосрочная память.
«Умные» рантаймы не пересобирают всё в один гигантский недифференцированный промпт на каждом шаге.
Главное отличие от раздела 1 в том, что раздел 1 был о сборе фактов о репозитории. Здесь же нас интересует упаковка и кэширование этих фактов для эффективного повторного использования при многократных вызовах модели.
«Стабильный» «Стабильный префикс промпта» означает, что содержащаяся там информация не слишком меняется. Обычно он включает общие инструкции, описания инструментов и сводку рабочего пространства. Мы не хотим тратить вычисления на его пересборку с нуля при каждом взаимодействии, если ничего важного не изменилось.
Другие компоненты обновляются чаще (обычно на каждом шаге). Сюда входят краткосрочная память, недавняя транскрипция и последний запрос пользователя.
Короче говоря, аспект кэширования для «Стабильного префикса промпта» заключается в том, что умный рантайм стремится переиспользовать эту часть.
3. Доступ к инструментам и их использование
Доступ к инструментам и их использование — это тот момент, когда система начинает ощущаться не как чат, а скорее как агент.
Обычная модель может предлагать команды в виде текста, но большая языковая модель в кодирующей обвязке должна делать кое-что более узкое и полезное — реально выполнять команду и получать результаты (в отличие от ситуации, когда мы вызываем команду вручную и вставляем результаты обратно в чат).
Но вместо того чтобы позволять модели импровизировать с произвольным синтаксисом, обвязка обычно предоставляет предопределённый список разрешённых и именованных инструментов с чёткими входными данными и чёткими границами. (Хотя, конечно, что-то вроде Python subprocess.call может быть частью этого, чтобы агент мог выполнять и произвольно широкий список shell-команд.)
Для наглядности ниже приведён пример того, как это обычно выглядит для пользователя при работе с Mini Coding Agent. (Это не так красиво, как Claude Code или Codex, потому что реализация очень минимальна и использует чистый Python без внешних зависимостей.)
Здесь модель должна выбрать действие, которое обвязка распознаёт: перечислить файлы, прочитать файл, выполнить поиск, запустить shell-команду, записать файл и т.д. Она также должна предоставить аргументы в формате, который обвязка может проверить.
Когда модель запрашивает какое-либо действие, рантайм может остановиться и выполнить программные проверки:
- «Это известный инструмент?»
- «Аргументы валидны?»
- «Требуется ли одобрение пользователя?»
- «Запрошенный путь вообще находится внутри рабочего пространства?»
Только после прохождения всех этих проверок что-либо реально выполняется.
Хотя запуск кодирующих агентов, безусловно, несёт определённые риски, проверки обвязки также повышают надёжность, потому что модель не выполняет абсолютно произвольные команды.
Помимо отклонения некорректных действий и шлюза одобрения, доступ к файлам можно ограничить пределами репозитория, проверяя пути к файлам.
В определённом смысле обвязка даёт модели меньше свободы, но одновременно повышает удобство использования.
4. Минимизация раздувания контекста
Раздувание контекста — это не уникальная проблема кодирующих агентов, а проблема больших языковых моделей в целом. Да, модели поддерживают всё более длинные контексты (и я недавно писал о вариантах механизмов внимания, делающих это вычислительно более осуществимым), но длинные контексты по-прежнему дороги и могут привносить дополнительный шум (если содержат много нерелевантной информации).
Кодирующие агенты ещё более подвержены раздуванию контекста, чем обычные большие языковые модели в многошаговых чатах, из-за повторных чтений файлов, объёмных выводов инструментов, логов и т.д.
Если рантайм сохраняет всё это с полной точностью, доступные контекстные токены закончатся довольно быстро. Поэтому хорошая кодирующая обвязка обычно достаточно изощрённа в обращении с раздуванием контекста — помимо простого обрезания или суммаризации информации, как в обычных чат-интерфейсах.
Минимальная обвязка использует как минимум две стратегии уплотнения для решения этой проблемы.
Первая — обрезка (clipping), которая укорачивает длинные фрагменты документов, объёмные выводы инструментов, заметки памяти и записи транскрипции. Иными словами, она не позволяет ни одному фрагменту текста занять весь бюджет промпта просто потому, что он оказался многословным.
Вторая стратегия — сжатие или суммаризация транскрипции, которая превращает полную историю сессии (подробнее об этом в следующем разделе) в более компактную сводку, пригодную для промпта.
Ключевой приём здесь — сохранять недавние события более подробными, потому что они с большей вероятностью важны для текущего шага. А более старые события сжимаются агрессивнее, потому что, вероятно, менее релевантны.
Кроме того, мы также дедуплицируем более ранние чтения файлов, чтобы модель не видела одно и то же содержимое файла снова и снова просто потому, что он был прочитан несколько раз ранее в сессии.
В целом, я считаю, что это одна из недооценённых, «скучных» частей хорошего дизайна кодирующих агентов. Значительная доля кажущегося «качества модели» — это на самом деле качество контекста.
5. Структурированная память сессии
На практике все шесть рассматриваемых здесь концепций тесно переплетены, и разные разделы и иллюстрации освещают их с различных ракурсов и на разных уровнях детализации. В предыдущем разделе мы рассмотрели использование истории на этапе промпта и то, как строится компактная транскрипция. Вопрос там был: сколько прошлого должно вернуться в модель на следующем шаге? Акцент был на сжатии, обрезке, дедупликации и приоритете свежести.
Теперь, в этом разделе — о структурированной памяти сессии — речь идёт о структуре хранения истории. Вопрос здесь: что агент сохраняет с течением времени как постоянную запись? Акцент на том, что рантайм хранит более полную транскрипцию как долговременное состояние наряду с более лёгким слоем памяти — он меньше и модифицируется и уплотняется, а не просто дополняется.
Подводя итог, кодирующий агент разделяет состояние как минимум на два слоя:
- рабочая память: небольшое, дистиллированное состояние, которое агент поддерживает явным образом
- полная транскрипция: охватывает все запросы пользователя, выводы инструментов и ответы модели
На иллюстрации показаны два основных файла сессии — полная транскрипция и рабочая память — которые обычно хранятся как JSON-файлы на диске. Как упоминалось ранее, полная транскрипция хранит всю историю и позволяет возобновить работу, если мы закроем агента. Рабочая память — это более дистиллированная версия с наиболее важной на данный момент информацией, что несколько связано с компактной транскрипцией.
Но компактная транскрипция и рабочая память выполняют несколько разные задачи. Компактная транскрипция предназначена для реконструкции промпта. Её задача — дать модели сжатое представление недавней истории, чтобы она могла продолжить разговор, не видя полной транскрипции на каждом шаге. Рабочая память больше предназначена для непрерывности задачи. Её задача — хранить небольшую, явно поддерживаемую сводку того, что важно между шагами: текущую задачу, важные файлы и недавние заметки.
Следуя шагу 4 на иллюстрации, последний запрос пользователя вместе с ответом модели и выводом инструмента будет записан как «новое событие» и в полную транскрипцию, и в рабочую память на следующем раунде.
6. Делегирование с (ограниченными) подагентами
Как только у агента есть инструменты и состояние, одной из следующих полезных возможностей становится делегирование.
Причина в том, что оно позволяет распараллелить определённую работу на подзадачи через подагентов и ускорить основную задачу. Например, основной агент может быть в процессе работы над одной задачей и при этом нуждаться в побочном ответе — например, какой файл определяет символ, что говорит конфигурация или почему падает тест. Полезно выделить это в ограниченную подзадачу, вместо того чтобы заставлять один цикл нести все нити работы одновременно.
(В моём mini coding agent реализация проще — дочерний процесс всё ещё работает синхронно, но основная идея та же.)
Подагент полезен только если он наследует достаточно контекста для реальной работы. Но если мы не ограничиваем его, то получаем несколько агентов, дублирующих работу, затрагивающих одни и те же файлы или порождающих ещё подагентов, и так далее.
Поэтому непростая задача дизайна — не только в том, как породить подагента, но и в том, как его ограничить :).
Приём здесь в том, что подагент наследует достаточно контекста, чтобы быть полезным, но при этом ограничен (например, режим только для чтения и ограничение глубины рекурсии).
Claude Code давно поддерживает подагентов, а Codex добавил их относительно недавно. Codex, как правило, не принуждает подагентов к режиму только для чтения. Вместо этого они обычно наследуют большую часть песочницы и настроек одобрения основного агента. Таким образом, граница больше касается определения области задачи, контекста и глубины.
Итоги по компонентам
Предыдущие разделы постарались охватить основные компоненты кодирующих агентов. Как упоминалось ранее, в реализации они более или менее тесно переплетены. Тем не менее, я надеюсь, что рассмотрение их по одному помогает сформировать общую ментальную модель того, как работают кодирующие обвязки и почему они могут сделать большую языковую модель более полезной по сравнению с простыми многошаговыми чатами.
Если вам интересно увидеть это реализованным в чистом, минималистичном Python-коде, вам может понравиться мой Mini Coding Agent.
Как это соотносится с OpenClaw?
OpenClaw представляет собой интересное сравнение, но это не совсем тот же тип системы.
OpenClaw — это скорее локальная универсальная агентная платформа, которая также может программировать, нежели специализированный (терминальный) помощник для программирования.
Тем не менее, есть несколько пересечений с кодирующей обвязкой:
- использует файлы промптов и инструкций в рабочем пространстве, такие как AGENTS.md, SOUL.md и TOOLS.md
- хранит файлы сессий в формате JSONL и включает уплотнение транскрипции и управление сессиями
- может порождать вспомогательные сессии и подагентов
- и т.д.
Однако, как упоминалось выше, акценты расставлены по-разному. Кодирующие агенты оптимизированы для человека, работающего в репозитории и просящего помощника для программирования проверять файлы, редактировать код и эффективно запускать локальные инструменты. OpenClaw больше оптимизирован для запуска множества долгоживущих локальных агентов через чаты, каналы и рабочие пространства, где программирование является одной из важных нагрузок среди нескольких других.
Подпишитесь на канал и каждый день читайте лучшие материалы про AI переведенные на русский!
Нашли интересную статью для перевода? Пришлите нашему боту: @ailongreadsbot