Каждый, кто программирует с агентами (Claude Code, Codex и т.д.), знает: перед тем как приступить к задаче, агент исследует проект. Это кажется логичным, естественным и закономерным, ведь люди делают так же. Обычно говорят: «агент наполняет контекст».
Для агента такой контекст должен содержать не только полезные сведения, но и не включать лишних, которые могут оказать влияние на конечный результат. Но добиться этого не так-то просто, поскольку:
задача определена в общем виде (кто знает, что имел в виду автор)
пути исследования не детерминированы (в общем виде не используется специфика фреймворка)
используются базовые инструменты (read, grep, cat, find)
В итоге при первичном исследовании легко столкнуться с ситуацией, когда основной контекст заполнен сведениями, слабо относящимися к исходной задаче.
В Anthropic быстро осознали эту проблему и вынесли всю описанную работу в Explore sub-agent. В результате основной agent ставит задачу промптом, Explore выбирает путь исследования, а результат формирует в виде отчета. Проблему чистоты основного контекста они, конечно, решили. Но что с качеством такого анализа?
Наблюдая за работой Explore и видя, как агент, используя «примитивные» инструменты, в агонии пытаясь отыскать недостающее или, наоборот, пропускает важные сведения о проекте, невольно ловишь себя на мысли: «Как так вышло, что последние 10 лет развития индустрии инструментов прошли мимо agent?» А может, это человечество свернуло не туда?
Анатомия Explore sub-agent
Благодаря ошибке Claude Code и недосмотру Anthropic у нас есть возможность не гадать, а непосредственно посмотреть, что же из себя представляет Explore sub-agent изнутри.
В действительности все оказалось достаточно тривиально (воссоздано по памяти):
--- name: Explore description: Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions. tools: Bash, Glob, Grep, Read, WebFetch, WebSearch, TodoWrite model: haiku --- You are a file search specialist for Claude Code, Anthropic's official CLI for Claude. You excel at thoroughly navigating and exploring codebases. === CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS === This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from: - Creating new files (no Write, touch, or file creation of any kind) - Modifying existing files (no Edit operations) - Deleting files (no rm or deletion) - Moving or copying files (no mv or cp) - Creating temporary files anywhere, including /tmp - Using redirect operators (>, >>, |) or heredocs to write to files - Running ANY commands that change system state Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools - attempting to edit files will fail. Your strengths: - Rapidly finding files using glob patterns - Searching code and text with powerful regex patterns - Reading and analyzing file contents Guidelines: - Use Glob for broad file pattern matching - Use Grep for searching file contents with regex - Use Read when you know the specific file path you need to read - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail) - NEVER use Bash for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification - Adapt your search approach based on the thoroughness level specified by the caller - Communicate your final report directly as a regular message - do NOT attempt to create files NOTE: You are meant to be a fast agent that returns output as quickly as possible. In order to achieve this you must: - Make efficient use of the tools that you have at your disposal: be smart about how you search for files and implementations - Wherever possible you should try to spawn multiple parallel tool calls for grepping and reading files Complete the user's search request efficiently and report your findings clearly.
Как вы можете заметить, описание агента достаточно общее и не содержит какой-либо специфики относительно языка, на котором написан код, или фреймворка, на котором базируется проект.
Попробуем собрать контекст для задачи, приближенной к реальной: issue в spring-petclinic.
Краткое описание:
Управление рабочими расписаниями ветеринаров — функциональность, позволяющая администратору клиники создавать и управлять рабочими графиками ветеринаров: задавать именованные расписания со сменами (дата, время начала и окончания), отмечать одно из расписаний как активное для записи, а также автоматически проверять при создании визита, что выбранный ветеринар работает в указанное время и не занят другим приёмом.
Как видно на видео, путь исследования далек от идеального. В результате получаем следующую статистику: 1.0k input, 7.4k output, 523.4k cache read, 51.3k cache write, время выполнения: 1м.
Если вы внимательно смотрели видео, то наверняка заметили, что несмотря на попытки избирательного поиска, агент проанализировал практически весь проект, к этому мы еще вернемся (запомним этот факт).
А что с качеством самого анализа, не зря же мы ждали минуту и потратили десятки/сотни тысяч токенов.
На простых проектах вида PetClinic такой анализ действительно даст хороший результат, но все становится гораздо хуже как только у нас появляются собственные стартеры, мы начинаем использовать OpenAPI (Spec-first), определяем Bean/Components в стиле Josh Long (описывая их в конфигурациях), используем Spring Router и еще множестве других случаях, когда качественный анализ можно провести только зная особенности работы Spring Framework и экосистемы.
Чтобы помочь агенту, определимся с проблемами:
недостаточное качество анализа, агент не обнаруживает все связанные с задачей компоненты
малая скорость работы, анализ в среднем выполняется от 1м и более
большой расход токенов на анализ
Тут стоит оговориться: мы говорим о достаточно воздушных материях, используя формулировки «недостаточное», «малая скорость», «большой расход». Это сильно зависит от того, к чему привыкли именно вы, какая у вас подписка и какие лимиты на токены/минуты/запросы.
Но есть аспекты, с которыми мало кто будет спорить.
Если Agent в процессе анализа не обнаружил существующий компонент и в результате написал собственный, определенно можно говорить, что такой анализ выполнен некачественно.
Кроме того, время выполнения анализа вряд ли можно назвать хорошим. Если бы в доагентную эру вы сказали разработчику, что ему предстоит вести разработку с помощью инструмента со временем реакции от минуты и более, вам бы, скорее всего, ответили, что это невозможно. Уж поверьте моему опыту: если бы мне платили по рублю каждый раз, когда на конференции я слышу, что IntelliJ IDEA тормозит (а речь там обычно о секунде-двух), то хватило бы на бизнес-такси лет так на двадцать. Теперь же мы почему-то готовы ждать минуты не только на анализ, но и, например, пока AI переименует класс или перенесет его в другой пакет.
А что с токенами и их потреблением? К этому вернемся позже.
Сейчас же попробуем улучшить первые две характеристики: качество и скорость.
Доступ к приложения в терминах Spring
В Spring все является Component или Bean (ну или почти все). Конечно, разработчик воспринимает приложение не так абстрактно, оперируя сущностями вроде Service, RestController, EventListener, Repository и т.д.
Кроме того, нам важно знать не только о самих компонентах, но и о том, как компоненты между собой связаны. При этом сам способ связывания для анализа не так важен, будь то constructor, field или getter/setter injection, а вот параметры имеют важное значение: является ли оно ленивым, указан ли qualifier (имя как qualifier).
Не стоит забывать и о том, что отдельные типы компонентов имеют особенности: например, для RestController/Controller важно знать request path, по которому они отвечают; для EventListener, обслуживающего очередь в Kafka, — название очереди и структуру сообщения; для сервисов — как они работают с транзакциями.
Как же нам получить эти сведения? Первое что приходит в голову, это написать Skill в котором мы просто опишем, как собрать указанные выше сведения используя имеющиеся у Agent базовые тулы: Bash(grep), Read и т.д. Сделав это мы увидим, что качество однозначно выросло, однако платим мы за это большим количеством токенов и значительно выросшим временем выполнения анализа. Кроме того не все операции легко выражаются базовыми тулами, например чтобы понять как устроен класс в библиотеке, агент предложил распаковать jar и декомпилировать класс.
Получается, что тот функционал, который мы считаем базовым для IDE, не так-то просто воссоздать skill-ами в агенте. Так, может, это и не нужно? Возможно, стоит просто дать Agent доступ к информации, которая и так уже присутствует в IDE, тем более что она уже собрана Amplicode. Так появился Spring MCP. Вот только часть полезных для анализа тулов из него:
list_spring_beans_tool, list_all_domain_entities, list_project_endpoints — список бинов, сущностей и эндпоинтов, в том числе в библиотеках и стартерах
get_bean_injection_info, get_entity_details, get_endpoint_info — доступ к структурированной информации о связях бина и структуре сущностей
list_security_configurations, list_spring_security_roles - информация о настройках Spring Security: конфигурациях, ролях
list_kafka_consumers, list_kafka_producers - сведения о Kafka consumer/producer
read_class_file — доступ к содержимому файлов в зависимостях
Полный же список можно посмотреть после подключения MCP к Agent-у.
Попробуем вновь собрать контекст для той же задачи, но с подключенным Spring MCP (инструкция по установке).
Как видно на видео, Agent вызывает Spring MCP-тулы для получения информации о проекте уже с пониманием Spring, однако после этого снова запускает поиск по структуре файлов, чтение самих файлов и другие низкоуровневые операции.
Как результат, статистика: 900 input, 6.7k output, 696.3k cache read, 60.2k cache write, время работы: 1m 16s. Кажется, всё стало хуже?
По количеству израсходованных токенов и времени выполнения показатели ухудшились, однако качество анализа выросло, поскольку теперь, если компонент определен в зависимости, Agent его точно не пропустит. Без MCP это было бы почти гарантированно. Можно сказать, мы заплатили за качество дополнительными токенами и временем, что в целом неплохо.
Однако, как я уже упоминал, после вызова MCP-тулов Agent зачем-то начинает выполнять низкоуровневые операции, чтобы найти дополнительные факты, хотя мы с вами знаем, что ничего нового он там не найдет. То есть, если list_all_domain_entities не вернул VetSchedule, то нет смысла проверять это дополнительно через find или grep — скорее наоборот. Делая find или grep, стоит вызвать list_all_domain_entities, чтобы получить факт. Можно ли с этим что-то сделать?
Как люди анализируют Spring-приложения
Перед тем как продолжить, давайте немного отступим в сторону и посмотрим, как человек анализирует приложение. Обычно мы ищем точку опоры — компонент, от которого начнем собирать контекст. Часто такой точкой являются сущность и связанные с ней компоненты.
Для ранее озвученной задачи (см. выше) из ее описания уже ясно, что рабочего расписания в приложении нет, и искать связанные компоненты — сущности, репозитории, сервисы — не имеет смысла. Имеет смысл проверить, есть ли вообще какое-то расписание. Для этого посмотрим на структуру сущностей.

Убедившись, что сущностей, связанных с расписанием, нет, посмотрим на ветеринара и визит:

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

На этом этапе анализ можно считать завершенным и переходить к проектированию решения.
Как можно заметить, такой анализ не является сложным, выполняется быстро и хорошо структурирован, то есть мы совершили минимальное количество точных действий для получения картины достаточной для того, чтобы приступить к проектированию решения. Возникает вопрос, а можем ли мы научить Agent выполнять анализ по схожей схеме? Давайте попробуем.
Для начала попробуем решить задачу с помощью skill, а уже после обернем его в sub-agent.
Шаг 1. Мечты о Spring компонентах
Сперва необходимо определить точку старта анализа. Для человека это очень естественно: сразу после прочтения задачи он мысленно формулирует набор компонентов, которые могут быть задействованы. Ничто не мешает попросить Agent проделать ту же работу.
Поскольку уже на этапе обучения модель «видела» огромное количество проектов из разных областей, она с высокой вероятностью может «угадать» названия компонентов в вашем проекте. Сделать это можно в виде следующего prompt:
## Step 0 — Predict involvement from the user's request Tell the user: `Step 0/6: Analyzing request...` **Do NOT call any tools in this step.** Read the user's request and reason about what the implementation likely involves. Without calling any tools, make educated guesses based on naming conventions, domain language, and typical Spring Boot patterns: - **Entities** — what domain objects are likely involved? (e.g. "create order" → `Order`, `OrderItem`) - **Repositories** — which repositories probably exist for those entities? - **Services** — what service classes are likely needed? - **Controllers** — what REST controllers probably handle this area? - **Other beans/components** — mappers, validators, event listeners, configs, etc. - **Files** — what non-Java files are likely relevant? (e.g. DB migration scripts, `application.properties`, Liquibase changelogs, HTML templates). Do not list Java classes here — they belong to the categories above. Build a preliminary gap list containing only what is genuinely required: ### Predicted involvement: - Entities: Order, OrderItem, Customer - Repositories: OrderRepository, CustomerRepository - Services: OrderService - Controllers: OrderController - Other: OrderMapper - Files: src/main/resources/db/migration/V1__create_orders.sql, application.properties
Стоит обратить внимание на Do NOT call any tools in this step, такое указание должно ограничить модель в вызове каких-то тулов на этом шаге. Оно выполняется естественно не в 100% случаев, но тут уже ничего не поделаешь, такова природа модели.
Шаг 2. Выбор шагов для анализа
На следующем шаге, необходимо определить, исходя из нафантазированных компонентов, каким образом мы будем анализировать приложение.
В моем рассуждении выше я приводил скриншоты дерева связанности модели. В реальности такое дерево может быть структурировано по-разному: в виде простого списка, набора связанных агрегатов или, например, быть отсортированным по количеству связей.
Это нас подводит к идее, что человек, не всегда оперирует простыми концепциями и вместо простого списка сущностей воспринимает приложение как набор связанных агрегатов, которые в свою очередь влияют на то как будут выглядеть request path в контроллерах, в каком классе будут находиться методы работающие с внутренней структурой агрегата и т.д.
Поэтому шаги, из которых агент состовляет план анализа также должны включать эти концепции:
## Step 1 — Define exploration goal and select paths Tell the user: `Step 1/6: Selecting exploration paths...` **Do NOT call any tools in this step.** Read the current conversation context — the user's request, any prior exploration results, remaining gaps — and formulate the key exploration goal in one sentence. Show it to the user: ### Exploration goal: Understand the Order aggregate structure and verify what repositories and mappers already exist. Using this goal, go through each exploration path below and explicitly decide: **include** or **skip**, with a one-line reason. Do this for every path — do not skip the evaluation itself. **Project structure** - **Fetch project summary** — include if the tech stack, Spring Boot version, or module structure are needed. Skip if the task is narrowly scoped to specific classes. - **List domain entities** — include ONLY if the domain area is completely unknown and you cannot predict which entities are involved. If entities are already named in the request or predictable from context — skip. Do NOT use to get entity structure or resolve FQNs. - **List REST endpoints** — include only if you need to check what already exists to avoid duplication or understand conventions. Skip if the request fully defines all endpoints from scratch. **Domain model** - **Get entity description** — include if entity fields or annotations are needed. Apply only to predicted entities, not all entities. See [`references/entity-description.md`](references/entity-description.md). - **Get deep model from entity** — include if relationships across multiple entities need to be traversed (e.g. nested resources, cascades). See [`references/deep-model-based-on-jpa.md`](references/deep-model-based-on-jpa.md). - **Get DDD model from entity** — include if aggregate boundaries matter (e.g. URL design, cascade planning, DTO shaping). See [`references/ddd-model-based-on-jpa.md`](references/ddd-model-based-on-jpa.md). **Persistence** - **Get entity repositories** — include if repositories for predicted entities are unknown or need to be verified. See [`references/entity-repositories.md`](references/entity-repositories.md). - **Get entity components** — include if you need a full picture of all components (repositories, services, controllers) for an entity. See [`references/entity-components.md`](references/entity-components.md). **Services** - **Get entity services** — include if services for predicted entities are unknown or need to be verified. See [`references/entity-services.md`](references/entity-services.md). **Mappers** - **Get entity mappers** — include if the task involves DTOs and you need to know what mappers exist. See [`references/entity-mappers.md`](references/entity-mappers.md). **DTOs** - **Get entity DTOs** — include if you need to know what DTO classes already exist. See [`references/entity-dtos.md`](references/entity-dtos.md). **REST layer** - **Get entity controllers** — include if you need to find which controllers are associated with an entity. See [`references/entity-controllers.md`](references/entity-controllers.md). Write out the evaluation explicitly, then produce the final plan from included paths only: **Example** — for a request "Add a paginated endpoint returning all orders for a customer with order items and product names": ### Path evaluation: - Fetch project summary: INCLUDE — need Spring Boot version and module structure - List domain entities: SKIP — Order, Customer, OrderItem, Product are predictable from the request - List REST endpoints: INCLUDE — need to check if an orders endpoint already exists - Get entity description: INCLUDE — need Order, OrderItem, Product fields for response DTO design - Get deep model: SKIP — relationship structure is clear: Order → OrderItem → Product - Get DDD model: SKIP — no cascade planning needed, just a read endpoint - Get entity repositories: INCLUDE — need to verify OrderRepository exists and supports pagination - Get entity components: SKIP — repositories are sufficient, no need for full component chain - Get entity services: SKIP — no service layer changes expected - Get entity mappers: INCLUDE — need to know if OrderMapper already exists before creating DTOs - Get entity DTOs: INCLUDE — need to know if OrderDto already exists - Get entity controllers: SKIP — will check via "List REST endpoints" instead
Обратите внимание: мы по-прежнему просим Agent не вызывать какие-либо тулы, а исключительно опираться на предобучение и промпт.
Как же тогда Agent загрузит референсные файлы? Для этого есть следующий технический шаг, который я не буду приводить здесь: его можно посмотреть в репозитории spring-skills.
Шаг 3. Единый план анализа
После того, как все референсы загружены и у Agent есть полное понимание того, как выполнять шаги и какие tools с какими параметрами вызывать стоит попросить его сначала сформулировать такой план анализа, а после исполнить:
## Step 3 — Build and execute unified exploration plan Tell the user: `Step 3/6: Building exploration plan...` Using the selected paths from step 1 and the processes described in the loaded references, build a single unified numbered plan of MCP calls to execute in steps 4–5. Each item must be a concrete MCP tool call, not a category name. ### Unified exploration plan: 1. get_project_summary 2. list_project_endpoints 3. list_all_domain_entities (regexPattern=Owner) — resolve FQN 4. get_entity_details (Owner FQN) 5. get_entity_details (Pet FQN) 6. get_entity_details (Visit FQN) 7. list_entity_repositories (Owner FQN) 8. list_entity_repositories (Pet FQN) 9. list_entity_repositories (Visit FQN)
Из важного тут стоит заметить, что мы просим агента сформировать план из конкретных MCP вызовов с конкретными параметрами, тем самым минимизируя возможные "паразитные" действия со стороны Agent.
Шаг 4. Отчет о проделанной работе
В ситуации, когда мы вызываем созданный нами skill в том же контексте, где будет проходить основная работа, для которой мы и делаем анализ формировать отчет нет необходимости, поскольку как показывает наша практика для модели нет большой разницы его наличие или отсутствие, однако поскольку, в дальнейшем, мы планируем преобразовать skill в sub-agent именно этот отчет мы и будем возвращать в основной контекст.
## Step 5 — Build exploration report Tell the user: `Step 5/6: Building exploration report...` **Do NOT call any tools in this step.** Synthesize all findings collected across all exploration cycles into a single report. Include only what is genuinely valuable for the task — omit noise and obvious defaults. Structure: ### Exploration Report **Stack:** Java 21 · Spring Boot 3.x · JPA · Maven **Domain model:** - Order (id, status, totalAmount) → has many OrderItem → references Product - Customer (id, name, email) **Repositories:** - OrderRepository — extends JpaRepository, supports pagination - CustomerRepository — extends JpaRepository **Services:** - OrderService — handles order creation and status transitions **Mappers:** - OrderMapper (MapStruct) — maps Order ↔ OrderDto **DTOs:** - OrderDto, OrderItemDto — already exist **REST API (relevant endpoints):** - GET /orders — paginated list - POST /orders — create order **Notable findings:** - SecurityConfig present — all endpoints require authentication - No mapper for Customer — will need to create one
Как можно видеть, мы снова используем хитрость с Do NOT call any tools in this step в результате заставляя сформулировать отчет исключительно опираясь на полученные от вызова плана результаты.
Испытания
Описанный нами skill spring-explore уже доступен в рамках Spring Agent Toolkit, его я и буду использовать для текущего теста.
На видео видно, что я напрямую указываю использование skill-а spring-explore, сделано это с целью отладки, чтобы не ловить момент пока Agent сам решит активировать нужный нам скилл. Кроме того явное указание skill является неплохой практикой, когда мы хотим добиться ожидаемого результата, план достижения которого закреплен в каком-то skill.
Посмотрим статистику: 446 input, 3.3k output, 253.1k cache read, 44.5k cache write, время выполнения: 32s.
Расход токенов сократился более чем в два раза, как и время выполнения, с повышением качества исследования относительно базового Explore.
Остался последний шаг: преобразовать наш skill в sub-agent. Можно попросить об этом Claude Code или просто установить Spring Agent Toolkit, где такой агент уже доступен.
Заключение
Истина проста: качественный тулинг и хорошая методология остаются актуальными даже в условиях, когда большую часть работы мы делаем с помощью Agent. Это же, в действительности, видно и в тестах Anthropic и OpenAI в расширенных отчетах о моделях.
Подводя итоги, можно сказать, что базовый Explore в общем виде, конечно, как-то справляется с задачей, попутно тратя огромное количество токенов, не стараясь быть быстрым и, самое главное, не обеспечивая по-настоящему качественный и исчерпывающий анализ. Можно ли говорить, что это «базовый минимум»? Я бы не стал.
В свою очередь Spring Explore обладает всем необходимым, предлагая разработчику высокую скорость работы, сниженное потребление токенов и, главное, качественный и исчерпывающий анализ. Кажется, именно так и должен выглядеть «базовый минимум» в 2026 году. Можно ли это назвать «роскошным максимумом»? Пока не берусь, поскольку пул идей для улучшений еще далеко не исчерпан.
В конце хотел бы добавить, что полученные 32s — в реальности не лучший результат. Можно получить более впечатляющие 12-15s, обменяв токены на время. Как этого достичь, поговорим в следующей статье, так что подписывайтесь на Amplicode, устанавливайте Spring Agent Toolkit и приходите в комментарии.
