«Сделайте нам AI-агента» — теперь рядовая строчка в задаче. Беда в том, что под одним словом скрываются разные вещи: у одного заказчика это FAQ-бот на две кнопки, у другого — автономная система с доступом на запись в прод. Слово одно, а цена и риск отличаются на порядок. За год экспериментов через это прошли многие команды, и шишки у всех похожие. Поэтому давайте по существу: что такое агент, как он устроен внутри (с кодом) и почему первая версия так часто выходит дороже и хрупче, чем ждали.
Тезис сразу: сама петля агента простая. Сложное — в инженерии вокруг неё: границы прав, лимиты, наблюдаемость, оценка качества. Грубо говоря, хороший агент процентов на двадцать про саму модель и на восемьдесят — про то, что вы построили вокруг.
промпт → чат-бот → агент
Самое короткое определение: агент — это языковая модель, которой дали инструменты и запустили в цикле. Чтобы почувствовать разницу, посмотрим на три ступени.
-
Промпт. Один заход: на входе вопрос, на выходе текст. «Перепиши это письмо вежливее».
-
Чат-бот. Цепочка промптов с памятью диалога. Помнит контекст, но по-прежнему только говорит.
-
Агент. Модель плюс инструменты плюс право самой выбирать шаги. Получив цель «разберись, почему упал ночной отчёт», агент читает логи, ходит в базу, проверяет гипотезы и возвращается с ответом, а не с советом «проверьте логи».
Граница проходит по двум признакам: появились ли у модели инструменты (возможность что-то сделать) и есть ли у неё автономия в выборе действий. Чат-бот отвечает, агент действует.
Цикл агента — и сразу код
Сердце любого агента — петля «подумал → сделал → посмотрел»:
-
Рассуждение. Модель смотрит на цель и текущее состояние и решает, какой шаг сделать следующим.
-
Действие. Вызывает инструмент с конкретными параметрами.
-
Наблюдение. Получает результат (содержимое файла, ответ API, ошибку) и добавляет его в контекст.
-
Повтор. С учётом нового знания планирует следующий шаг. Петля крутится, пока задача не решена либо не сработал лимит.
А теперь тот же цикл целиком. Сам агент — функция run_agent — около двадцати строк; остальное в листинге это пример инструмента и его схема. Беру OpenAI, но петля у всех провайдеров одинаковая:
import jsonfrom openai import OpenAIclient = OpenAI() # ключ берётся из переменной OPENAI_API_KEY# Инструмент — обычная функция. Модель видит его по JSON-схеме (tools_schema)# и вызывает по имени из словаря TOOLS.def read_file(path: str) -> str: with open(path, encoding="utf-8") as f: return f.read()TOOLS = {"read_file": read_file}tools_schema = [{ "type": "function", "function": { "name": "read_file", "description": "Прочитать файл по пути", "parameters": { "type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"], }, },}]def run_agent(goal: str, max_steps: int = 12) -> str: messages = [{"role": "user", "content": goal}] for _ in range(max_steps): reply = client.chat.completions.create( model="gpt-4.1", messages=messages, tools=tools_schema, ).choices[0].message messages.append(reply) # сохраняем рассуждение и запросы на вызовы if not reply.tool_calls: # модель решила, что задача закрыта return reply.content for call in reply.tool_calls: # вызовы из одного ответа можно слать и параллельно try: args = json.loads(call.function.arguments) result = TOOLS[call.function.name](**args) if isinstance(result, str): content = result else: content = json.dumps(result, ensure_ascii=False, default=str) except Exception as e: # ошибку отдаём модели как наблюдение, не роняя агента content = f"Ошибка инструмента: {e!r}" messages.append({"role": "tool", "tool_call_id": call.id, "content": content}) return "Достигнут лимит шагов: задача не закрыта"
Здесь всё, что делает агента агентом. Остальное — обвязка: схемы инструментов, обработка ошибок, таймауты. Эти двадцать строк заодно объясняют, почему фреймворк на старте чаще мешает: он прячет ровно ту петлю, которую вам и надо прочувствовать руками.
Пара уточнений, чтобы код выше не выглядел наивным. Раньше выбор инструмента выбивали из модели текстом — формат Thought / Action / Observation (паттерн ReAct) разбирали вручную. Сегодня модели отдают вызовы нативным полем tool_calls, и парсить ничего не нужно: наш цикл опирается ровно на это. А когда от модели нужен строгий JSON под схему (вытащить поля, заполнить форму), берут structured outputs — ответ гарантированно валиден по схеме, без «почти валидного» текста, который потом падает на парсинге.
Кирпичики агента
За любым агентом, от простого до мультиагентной системы, стоят одни и те же части.
-
Модель — мозг. Рассуждает и принимает решения. На длинной цепочке действий слабая модель накапливает ошибки быстрее, чем успевает их исправлять, поэтому для агентных задач берут модели посильнее.
-
Инструменты — руки. Поиск, чтение файлов, запросы к API, выполнение кода. Чтобы не писать интеграцию под каждый сервис заново, инструменты всё чаще подключают через MCP — стандартный протокол, которым к модели подключают инструменты и источники данных.
-
Память — знания. Контекстное окно держит короткую память текущей сессии. Когда знаний больше, чем влезает в контекст, подключают RAG: агент достаёт нужные куски из векторной базы ровно тогда, когда они нужны.
-
Цикл — то, что связывает остальное. Без петли всё перечисленное осталось бы умным автодополнением.
Один агент или несколько
Не каждой задаче нужен полноценный автономный агент. Полезно держать в голове шкалу и брать минимально достаточный уровень.
-
Жёсткий сценарий (workflow). Шаги известны заранее и зашиты в код: достать данные → суммировать → разложить по полям. Модель вызывается в фиксированных точках. Предсказуемо, дёшево, легко отлаживать. Бо́льшая часть «агентных» задач на деле решается здесь.
-
Один агент с инструментами. Маршрут заранее неизвестен, модель сама выбирает шаги. Честный агент, и для большинства реальных задач его достаточно.
-
Мультиагентная система. Оркестратор раздаёт подзадачи агентам-специалистам и собирает результат. Звучит солидно, но цена и сложность отладки растут нелинейно: ошибку приходится искать уже не в одной траектории, а в их переплетении. Оправдано, когда задача честно бьётся на независимые куски, — например, параллельный обзор большой кодовой базы.
Здравое правило: начинать с жёсткого сценария и поднимать автономию ровно тогда, когда без неё перестаёт получаться. Лишняя свобода агента — это лишние способы ошибиться.
Пять способов сжечь бюджет и доверие команды
-
Накопление ошибок. Агент ошибся на третьем шаге, а следующие десять достраивал на основе неверного вывода. Длинные траектории умножают мелкие неточности, поэтому короткие проверяемые шаги надёжнее одного монолитного плана.
-
Бесконечная петля. Агент дёргает один и тот же инструмент, не приближаясь к цели, и спокойно мотает счётчик токенов. Потолок шагов тут не лекарство, а предохранитель: он лишь оборвёт агента на 12-м повторе. Чтобы ловить именно зацикливание, добавляют детект «нет прогресса» — одинаковый вызов с теми же аргументами дважды подряд останавливает петлю.
-
Незаметная стоимость. Каждый шаг пересылает модели весь накопленный контекст, и задача на 15 действий легко обходится в 10–20 раз дороже одиночного запроса — без кэширования. Prompt caching сбивает цену повторно отправляемого префикса (у OpenAI включается автоматически, у Anthropic — явным
cache_control), и для растущего контекста агента это главный рычаг экономии: множитель проседает в разы. Но кэш живёт минутами и греет только неизменный префикс — перетасовал историю в середине, и платишь за неё заново. -
Права без границ. Агенту с доступом на запись в прод и без подтверждений однажды хватит одной уверенной галлюцинации, чтобы устроить инцидент. Песочница, режим «только чтение» по умолчанию и человек в контуре на необратимых действиях — базовая гигиена.
-
Оценка на глазок. «Вроде работает» на пяти примерах — не метрика. Без набора тестовых задач с понятным критерием успеха каждое изменение промпта или модели превращается в лотерею, а деградацию замечают по жалобам пользователей.
Когда агент не нужен
Самая дорогая ошибка — собрать автономного агента там, где хватало куда меньшего:
-
Задача решается одним промптом. Если на входе текст и на выходе текст, агентный цикл — лишние затраты и лишние точки отказа.
-
Маршрут известен заранее. Когда шаги предсказуемы, жёсткий сценарий с вызовами модели в нужных точках надёжнее и дешевле.
-
Цена ошибки высока, а отката нет. Там, где неверное действие необратимо, разумнее оставить решение человеку, а агенту отдать только подготовку.
Если можешь за минуту назвать инструмент, который агент дёрнет на втором шаге — агент возможно оправдан. Если не можешь — тебе нужен не агент, а промпт или жёсткий сценарий.
ссылка на оригинал статьи https://habr.com/ru/articles/1053862/