Как создать ИИ-агента: от цикла в двадцать строк до рабочего помощника по коду
Про ИИ-агентов пишут так, будто это отдельная технология со своей теорией. Читаешь пять статей подряд и выносишь оттуда схему с четырьмя прямоугольниками, слово «оркестрация» и ощущение, что без фреймворка на десять зависимостей тут не обойтись.
Потом открываешь исходники любого агента и видишь цикл while. Внутри — вызов модели, проверка «не попросила ли она вызвать функцию», вызов функции, запись результата обратно в историю. Всё.
15 октября в 19:00 — бесплатный воркшоп «Команда ИИ-агентов в OpenCode». Разделим задачу между ролями агентов и доведём её до пул-реквеста, файлы ролей заберёте в свой проект. Записаться на воркшоп →
Мы соберём такого агента с нуля: он будет отвечать на вопросы про незнакомый проект, читая его код. Не игрушку на один вопрос, а работающую заготовку — с песочницей, обработкой ошибок, лимитом шагов и тестами. Ядро занимает меньше сотни строк, и именно на них уходит основное время статьи. Остальное — про то, где такой агент ломается, сколько стоит его содержать и почему отладка агента непохожа на отладку обычной программы.
Код на Python, из внешних зависимостей — ничего, кроме стандартной библиотеки. Фреймворки я оставил за бортом сознательно: пока не увидишь цикл своими глазами, любой фреймворк выглядит магией, а после — тонкой обёрткой, которую можно и не брать.
Агент, чат-бот и обычная автоматизация: где граница
Три слова, которые в статьях путают чаще всего.
Чат-бот отвечает. Вы спрашиваете, он генерирует текст. Даже если он умеет вызвать одну функцию — например, посмотреть погоду, — решение о вызове принимает человек, задавший вопрос про погоду.
Автоматизация выполняет заранее описанный сценарий. Пришло письмо → распарсить → положить в таблицу → отправить в чат. Порядок шагов задал разработчик. Модель внутри может быть, но она не выбирает маршрут.
Агент сам решает, что делать дальше. Вы даёте цель и набор инструментов, а последовательность шагов агент определяет по ходу дела. Спросили «почему падает тест на скидках» — он сам догадается сначала найти файл, потом прочитать его, потом, может быть, залезть в соседний модуль.
Разница не в сложности кода, а в том, кто держит управление. В автоматизации маршрут в коде, у агента — в голове модели. Отсюда все плюсы и вся боль: агент справляется с задачами, которых вы не предусмотрели, но и провалиться может там, где сценарий никогда бы не провалился.
Простой признак: если из системы убрать модель и заменить её на if/else, автоматизация продолжит работать, а агент перестанет существовать.
Из чего он состоит
Четыре части, и три из них — обычный код.
Модель решает, что делать дальше. Единственная часть, которую мы не пишем.
Инструменты — функции, которые агент может вызвать. Прочитать файл, сделать запрос к базе, отправить письмо. Модель не выполняет их сама: она возвращает «хочу вызвать read_file с такими аргументами», а выполняем мы.
Память — история переписки. Никакой отдельной базы на старте не нужно: память агента — это массив сообщений, который растёт с каждым шагом. Модель не помнит ничего между вызовами, всю историю мы отправляем заново каждый раз. Из этого следует неприятное: чем дольше работает агент, тем дороже каждый следующий шаг.
Цикл соединяет остальное. Спросить модель → выполнить, что она попросила → вернуть результат → спросить снова.
Схема одного шага:
Всё остальное, что встречается в статьях про агентов — планировщики, рефлексия, мультиагентные схемы, — надстройки над этим циклом. Они бывают полезны, но начинать с них — как учить фреймворк, не зная языка.
Что агенту стоит поручать, а что нет
Здесь у меня есть позиция, и она расходится с тем, что обещают на лендингах.
Агент хорошо работает там, где много однотипной возни и есть способ проверить результат. Разобраться в незнакомом проекте, собрать данные из десяти файлов, найти все места, где используется устаревший метод, прогнать рутинную проверку по списку. Ошибку в такой задаче видно сразу: либо файл нашёлся, либо нет.
Агент плохо работает там, где ошибку не видно. Он с уверенным лицом сошлётся на функцию, которой нет. Придумает флаг командной строки, которого не существует. Причём чем длиннее цепочка шагов, тем выше шанс, что где-то в середине он свернул не туда — а вы увидите только финальный ответ, гладкий и убедительный.
Отдельно про необратимые действия. Дать агенту право удалять файлы, писать в прод-базу или отправлять деньги — плохая идея не потому, что модель глупая, а потому что у неё нет понятия «этого нельзя откатить». В нашем агенте инструменты только читают. Это не упрощение для статьи, а нормальный подход к первой версии: сначала пусть агент смотрит, потом, когда вы увидите его логи за неделю, решите, что ему можно менять.
Заметьте: агент не станет умнее модели, которая внутри. Если модель не знает вашу предметную область, никакой цикл этого не исправит — он лишь даст ей возможность прочитать нужные файлы.
Писать код или взять no-code
Честный ответ: зависит от того, что вы делаете — прототип или систему, которую придётся поддерживать.
No-code и low-code (Flowise, Botpress, платформы облачных провайдеров) дадут результат за вечер. Вы соберёте схему мышкой, подключите модель, получите работающего агента. Для проверки идеи или для несложного сценария этого хватает.
Потолок появляется в двух местах. Первое — отладка: когда агент делает не то, вы видите красивую схему, а не то, что происходило внутри. Второе — нестандартные инструменты: пока вам нужны готовые блоки, всё хорошо, а свою логику приходится вписывать в чужие ограничения.
Свой код дороже на старте и дешевле дальше. Вы видите каждый шаг, ставите отладчик там, где нужно, и пишете тесты. Заготовка из этой статьи — примерно тот объём, который придётся написать: около двухсот строк с инструментами и обработкой ошибок.
Про n8n скажу отдельно, потому что его упоминают почти в каждой статье про агентов. Инструмент рабочий, но интерес к нему заметно сходит: в октябре 2025 года его искали в Яндексе 80 331 раз за месяц, в июне 2026 — 34 819. Падение на 57% за восемь месяцев. Это не повод его избегать, но строить на нём долгую стратегию я бы не советовал.
Мой выбор для обучения — свой код, даже если в продакшене вы потом возьмёте фреймворк. Причина простая: разобравшись в цикле, вы сможете читать чужие фреймворки. Начав с фреймворка, вы будете гадать, почему он ведёт себя странно.
На чём собирать агента из России
Тема, которую большинство статей аккуратно обходит: примеры пишут под сервисы, к которым у читателя нет доступа.
Что реально доступно:
Российские модели. GigaChat от Сбера и модели Yandex AI Studio. У обоих есть API, документация на русском и оплата с российской карты. Главное для нас — оба умеют вызывать функции, без этого агента не собрать. В документации GigaChat сказано прямо: пользовательские функции поддерживают все модели. У Яндекса подтверждение ещё нагляднее — в тарифах отдельной строкой идёт «цена за 1000 токенов инструментов», то есть вызовы инструментов там не просто есть, а тарифицируются отдельно.
Названия моделей для запроса: у Сбера — GigaChat-2, GigaChat-2-Pro, GigaChat-2-Max, у Яндекса — YandexGPT Lite, YandexGPT Pro 5.1, Alice AI LLM и открытые модели вроде Qwen и gpt-oss.
Локальные модели. Открытые модели через Ollama или vLLM на своём железе. Плюс: данные не уходят наружу, платить за токены не надо. Минус: нужна видеокарта, а маленькие модели заметно хуже держат длинные цепочки вызовов инструментов. Для агента, который делает десять шагов подряд, разница между большой и маленькой моделью видна невооружённым глазом.
Зарубежные модели — если у вашей компании есть легальный доступ через корпоративный договор или партнёра. Что из этого во что обходится, мы разбирали отдельно: .
Если ещё не выбрали, с какой моделью работать в коде, поможет наш обзор — .
Хорошая новость: большинство провайдеров говорят на одном языке. Формат chat completions, придуманный OpenAI, поддерживают и российские сервисы, и Ollama, и почти всё остальное. Поэтому в нашем коде модель спрятана за одним классом: поменять провайдера — значит поменять две строки конфигурации.
Дальше агенту всё равно, кто отвечает.
Собираем агента
Задача, которую он будет решать: отвечать на вопросы про незнакомый проект. «Где считается скидка», «что делает функция total», «где используется этот метод». Полезно и проверяемо: правильность ответа видно, открыв файл.
Шаг 1. Обёртка над моделью
Начнём с того, что агенту нужно от модели. А нужно немного: отправить историю сообщений и список инструментов, получить либо текст, либо просьбу вызвать инструмент.
Опишем ответ модели двумя классами:
Дальше — сам запрос. Ничего интереснее обычного HTTP здесь нет, кроме двух мест.
tool_choice: auto означает «решай сама, вызывать инструмент или ответить текстом». Именно эта строка отличает агента от программы, которая всегда дёргает функцию.
Второе место — разбор аргументов:
Модель присылает аргументы строкой с JSON внутри. Иногда — сломанным JSON: обрезанным, с лишней запятой, с комментарием. Первая версия моего кода падала на этом, и падала не сразу, а через несколько минут работы агента, когда уже потрачены токены. Поэтому здесь не исключение, а передача проблемы дальше: пусть агент сообщит модели, что аргументы не разобрались, и та попробует снова.
Шаг 2. Инструменты
Инструмент — функция плюс описание, по которому модель поймёт, когда её звать.
Описание уходит в модель, функция выполняется у нас. Из этого следует правило, которое я бы повесил на стену: всё, что агент может сделать с внешним миром, проходит через файл с инструментами. Значит, там же живут и ограничения.
Самое важное ограничение — песочница:
Восемь строк, и они обязательны. Без них модель однажды попросит прочитать ../../../.ssh/id_rsa — и получит. Не из злого умысла: она просто ищет конфигурацию и делает это широко. resolve() до проверки нужен именно затем, чтобы .. в пути раскрылись до сравнения.
Сами инструменты — три штуки. list_files показывает структуру, read_file читает диапазон строк, search ищет строку по файлам. В каждом есть ограничение на объём вывода:
MAX_FILES = 60 # чтобы огромный проект не вынес контекст целиком
Это не микрооптимизация. Если list_files вернёт двадцать тысяч путей, они займут весь контекст, и модель забудет исходную задачу. Агент после этого начинает вести себя необъяснимо — а причина в том, что начало разговора вытеснено списком файлов.
Мелочь, которая экономит время: в read_file мы отдаём строки с номерами.
Без номеров модель ссылается на код приблизительно: «где-то в середине файла». С номерами — «app/billing.py:13». Одна строка кода, а полезность ответов меняется заметно.
Шаг 3. Цикл
Теперь то, ради чего всё затевалось. Вся агентность целиком:
Двадцать строк. Читается сверху вниз: спросили модель; если она ответила текстом — работа закончена; если попросила инструменты — выполнили каждый, дописали результаты в историю, пошли на следующий круг.
Три детали, каждая из которых важнее, чем кажется.
for _ in range(self.max_steps) вместо while True. Лимит шагов существует не для аккуратности. Модель умеет зацикливаться: просит один и тот же файл, не находит, просит снова. С while True это будет продолжаться, пока не кончатся деньги на счету. Двенадцать шагов по умолчанию хватает на любую разумную задачу.
Ответ модели дописывается в историю до выполнения инструментов. Порядок сообщений должен совпадать с тем, что ждёт API: сначала «ассистент попросил вызвать вот это», потом результаты вызовов. Перепутаете — получите ошибку валидации, причём с формулировкой, по которой причину не угадать.
Результат инструмента возвращается модели, даже если инструмент упал. Об этом ниже.
Шаг 4. Ошибки как часть разговора
Обычная программа на ошибке останавливается. Агент — нет, и это самое непривычное в его устройстве.
Модель передала не тот набор аргументов — это ошибка, но поправимая. Мы не роняем агента: текст ошибки уходит обратно в модель как результат вызова. В следующем шаге она почти всегда исправляется сама.
То же с несуществующим инструментом:
Модель регулярно придумывает инструменты, которых вы ей не давали — особенно если название очевидное вроде write_file. Список доступных в тексте ошибки помогает ей вернуться в рамки.
Перехватывать Exception целиком я тут не стал. Ошибка в наших инструментах — это наш баг, и он должен всплыть на поверхность, а не растворяться в переписке с моделью.
Шаг 5. Память, которая не растёт бесконечно
Про память я говорил вначале: это массив сообщений, и мы отправляем его целиком на каждом шаге. Пока шагов три, беспокоиться не о чем. На десятом-двенадцатом начинается неприятное.
История распухает, и происходит одновременно две вещи. Каждый следующий шаг дорожает — платим-то за всё, что отправили. И, что хуже, начало разговора вытесняется из контекста: агент забывает исходную задачу и начинает отвечать на что-то своё. Выглядит это загадочно — вроде работал нормально, а к концу поехал.
Лечится обрезкой:
messages[:2] — системный промпт и задача пользователя. Их не трогаем никогда: без них агент перестаёт понимать, зачем он работает. Выкидываем самое старое из середины.
Вторая петля выглядит странно, но она обязательна. Сообщения идут парами: «ассистент попросил вызвать инструмент» и следом «вот результат». Если выбросить первое и оставить второе, API вернёт ошибку валидации — ответ инструмента, к которому нет вызова. Я на это наступил и минут двадцать искал причину в своём коде, а причина была в порядке сообщений.
Обрезка по числу символов — грубая мерка: платим мы за токены, а не за символы. Для контроля контекста этого хватает, для точного учёта денег считайте токены библиотекой провайдера.
Шаг 6. Запускаем
Собираем всё вместе:
Вот настоящий вывод на маленьком проекте из двух файлов:
Обратите внимание на порядок шагов: агент сначала поискал, потом прочитал найденное. Мы этот маршрут не программировали — мы дали инструменты и подсказку в системном промпте: «сначала осмотрись, потом читай нужное». Маршрут выбрала модель.
Системный промпт здесь работает так же, как файлы правил в готовых ИИ-инструментах: вы описываете рамки, а решения внутри них принимает модель. Как эти правила пишут для редакторов вроде Cursor, мы разбирали подробно — . Приёмы оттуда переносятся на свой агент почти без изменений.
Полный код — в репозитории [СВЕРИТЬ: ссылка на репозиторий]. Там же инструкция по запуску.
Как это отлаживать
Раздел, которого нет почти нигде, а он важнее половины остальных.
Агент недетерминирован. Один и тот же вопрос дважды даст разные маршруты — иногда правильные оба, иногда только один. Обычные приёмы отладки об это спотыкаются: воспроизвести баг «он выбрал не тот инструмент» вы не сможете по требованию.
Отсюда два вывода.
Первый: логируйте каждый шаг. У нас для этого класс Step — номер, инструмент, аргументы, результат, признак ошибки. Когда агент выдал ерунду, вопрос «на каком шаге он свернул не туда» решается чтением списка шагов. Без логов вы видите только финальный ответ и остаётесь с бесполезным «ну он неправильно понял».
Второй, и он же главный трюк: тестируйте на заглушке вместо живой модели. Живая модель для тестов не подходит по трём причинам: отвечает по-разному, стоит денег, требует сети. Но модель — не то, что мы писали. Мы писали цикл, диспетчер инструментов, обработку ошибок и лимит. Всё это проверяется без неё.
Двенадцать строк, а тестировать становится можно всё. Записываем сценарий — и проверяем поведение агента:
Заодно FakeLLM копит все запросы в self.seen — проверить можно и результат, и то, что именно ушло в модель. Ошибка «результат инструмента не дописался в историю» иначе не ловится: агент продолжает работать, просто отвечает хуже, и понять почему невозможно.
Что стоит покрыть тестами в первую очередь — по опыту этой сборки:
обычный путь: вызвал инструмент, получил ответ;
несколько шагов подряд;
модель просит инструмент, которого нет;
модель передаёт неверные аргументы;
зацикливание обрывается лимитом;
выход за пределы рабочего каталога запрещён.
Последний — тот самый тест, который однажды спасёт вам данные:
Девять таких тестов прогоняются за 0,08 секунды и не требуют ни ключа, ни сети.
Сколько это стоит?
Считать надо за шаг — здесь новичков ждёт сюрприз.
Модель не помнит предыдущие сообщения, поэтому на каждом шаге мы отправляем всю историю заново. Шаг первый — задача и описания инструментов. Шаг второй — то же плюс ответ модели и результат первого инструмента. Шаг третий — всё предыдущее плюс новое. История растёт, и каждый следующий шаг дороже предыдущего.
Я измерил, сколько наш агент отправляет за прогон из примера выше — тот самый, с двумя вызовами инструментов:
Две вещи бросаются в глаза. Первая: запросы растут — 1 661, 1 952, 2 588. Это и есть та самая история, которую мы отправляем заново.
Вторая неожиданнее. Схемы инструментов занимают 1 172 знака и уходят в каждом запросе. Три запроса — три с половиной тысячи знаков только на описания трёх функций. Это 57% всего исходящего трафика. Отсюда правило, которое я не встречал в статьях: не давайте агенту инструменты про запас. Каждый лишний инструмент вы оплачиваете на каждом шаге, даже если он ни разу не вызвался.
Практический вывод про инструменты: длина их ответов влияет на стоимость сильнее, чем длина вопроса. Один read_file, вернувший файл на тысячу строк, будет тянуться через все оставшиеся шаги и оплачиваться на каждом. Отсюда MAX_CHARS = 4000 из третьего шага — ограничение не ради аккуратности, а ради счёта.
Что снижает расход, по убыванию эффекта:
Ограничить объём вывода инструментов. Читать диапазоны строк, а не файлы целиком.
Снизить
max_steps. Разница между 12 и 30 шагами на счёте видна.Отдавать в
searchкороткие фрагменты строк, а не абзацы вокруг совпадения.Для простых задач брать модель поменьше, для длинных цепочек — побольше. Дешёвая модель, которая путается на шестом шаге и уходит в цикл, обойдётся дороже.
Теперь в рублях. Переведём знаки в токены: для смеси русского текста и кода это примерно 3–4 знака на токен, то есть наш прогон — около 1 550–2 070 входящих токенов и 60–80 исходящих. По тарифам Yandex AI Studio на 1 августа 2026 года (цены с НДС, синхронный режим):
Модель | Вход, ₽/1000 токенов | Выход, ₽/1000 | Один прогон агента |
|---|---|---|---|
Alice AI LLM Flash | 0,1 | 0,2 | 0,17–0,22 ₽ |
YandexGPT Lite | 0,2 | 0,2 | 0,32–0,43 ₽ |
YandexGPT Pro 5.1 | 0,8 | 0,8 | 1,29–1,72 ₽ |
Порядок величины — рубль за задачу. Кажется, что мелочь, но агент, которого дёргают тысячу раз в день, обойдётся уже в тысячу рублей, а мы посчитали прогон всего с двумя шагами.
Начать можно бесплатно. У GigaChat есть Freemium: 365 миллионов токенов на 12 месяцев, из них 250 миллионов на моделях Lite. Для обучения и первых экспериментов это практически безлимит — наш прогон съедает около двух тысяч токенов.
Цены живут своей жизнью, поэтому сверяйтесь с тарифами провайдера: у Сбера они лежат в разделе «Тарифы GigaChat API для физлиц», у Яндекса — в «Правилах тарификации AI Studio».
Пять ошибок, на которых спотыкаются все
Слишком широкая задача. «Разберись в проекте и улучши его» — не задача. Агент сделает что-то бессвязное, и виновата будет формулировка. «Найди все места, где скидка применяется без проверки процента» — задача.
Нет лимита шагов. Про while True я уже говорил, но повторю: это самая дорогая ошибка из списка, потому что счёт приходит потом.
Инструменты без ограничений. Дали доступ ко всей файловой системе, потому что «так проще для теста». Тест закончится, а код останется.
Вера в ответ без проверки. Агент назвал файл и номер строки — откройте и посмотрите. В нашем примере он сослался на app/billing.py:13, и там действительно apply_discount. Но модель с такой же уверенностью назовёт строку 13 в файле, который не читала.
Секреты в контексте. Всё, что попало в историю сообщений, ушло провайдеру модели. Файл .env агент прочитает с удовольствием, если ему разрешить. Мы фильтруем скрытые файлы в list_files — этого мало для продакшена, но лучше, чем ничего:
Куда двигаться дальше
Заготовка из статьи закрывает базу. Дальше есть три направления, и они разной сложности.
Инструменты, которые меняют мир, а не просто читают. Самый естественный следующий шаг и самый опасный. Минимум, который стоит сделать до него: подтверждение от человека на необратимые операции и запись всех изменений в лог.
MCP — общий протокол для инструментов. Вместо того чтобы писать обёртку под каждый сервис, вы подключаете готовый сервер. Наши три инструмента по такому протоколу переписываются почти без изменений, а взамен агент получает доступ к чужим готовым серверам. Тема на подъёме: в январе 2026 года «mcp сервер» искали 2 772 раза за месяц, в июне — 5 841, рост вдвое за полгода. Яндекс уже завёл у себя MCP Hub и MCP Gateway API, а русскоязычных разборов почти нет.
Несколько агентов вместо одного. Работает, когда задача честно делится на независимые части. Не работает как способ починить одного агента, который плохо справляется: два плохих агента дают не лучший результат, а два набора ошибок.
Если хочется копнуть глубже в устройство самих обвязок — есть отдельный разбор: . Там про то, что происходит вокруг цикла, который мы написали.
Практику с ИИ в разработке мы собрали в двух курсах: — про повседневную работу с моделями в коде, и — про сборку систем на LLM, включая агентов. Если с ИИ пока знакомы поверхностно, начните с бесплатного .
Короткие ответы на частые вопросы
Нужно ли уметь программировать?
Для агента из этой статьи — да, на уровне уверенного чтения Python. Без кода тоже можно, через визуальные платформы, но отладка там устроена хуже.
Можно ли бесплатно?
Да. Локальная модель через Ollama — если есть подходящая видеокарта. Либо Freemium у GigaChat: 365 миллионов токенов на год, чего с запасом хватит и на обучение, и на первые рабочие задачи.
Сколько времени занимает сборка?
Код из статьи — вечер, если Python знакомый. Доведение до состояния, в котором агенту доверяют рабочие задачи, — недели, и уйдут они не на код, а на выяснение, где он врёт.
Чем это отличается от RPA?
RPA повторяет записанные действия и ломается от смены интерфейса. Агент каждый раз решает заново — гибче, но и предсказуемости меньше.
Обязателен ли фреймворк?
Нет. Двадцати строк цикла хватает надолго. Фреймворк берите, когда упрётесь в конкретное ограничение, а не заранее.
Какую модель выбрать?
Ту, что поддерживает вызов функций и доступна вам без ухищрений. Для длинных цепочек шагов разница между большой и маленькой моделью существенна, для двух-трёх шагов — почти нет. Если выбираете между конкретными, мы сравнивали их на задачах с кодом: .






