«Сделайте нам AI-агента» — теперь рядовая строчка в задаче. Беда в том, что под одним словом скрываются разные вещи: у одного заказчика это FAQ-бот на две кнопки, у другого — автономная система с доступом на запись в прод. Слово одно, а цена и риск отличаются на порядок. За год экспериментов через это прошли многие команды, и шишки у всех похожие. Поэтому давайте по существу: что такое агент, как он устроен внутри (с кодом) и почему первая версия так часто выходит дороже и хрупче, чем ждали.

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

промпт → чат-бот → агент

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

  • Промпт. Один заход: на входе вопрос, на выходе текст. «Перепиши это письмо вежливее».

  • Чат-бот. Цепочка промптов с памятью диалога. Помнит контекст, но по-прежнему только говорит.

  • Агент. Модель плюс инструменты плюс право самой выбирать шаги. Получив цель «разберись, почему упал ночной отчёт», агент читает логи, ходит в базу, проверяет гипотезы и возвращается с ответом, а не с советом «проверьте логи».

Граница проходит по двум признакам: появились ли у модели инструменты (возможность что-то сделать) и есть ли у неё автономия в выборе действий. Чат-бот отвечает, агент действует.

Цикл агента — и сразу код

Сердце любого агента — петля «подумал → сделал → посмотрел»:

  1. Рассуждение. Модель смотрит на цель и текущее состояние и решает, какой шаг сделать следующим.

  2. Действие. Вызывает инструмент с конкретными параметрами.

  3. Наблюдение. Получает результат (содержимое файла, ответ API, ошибку) и добавляет его в контекст.

  4. Повтор. С учётом нового знания планирует следующий шаг. Петля крутится, пока задача не решена либо не сработал лимит.

А теперь тот же цикл целиком. Сам агент — функция run_agent — около двадцати строк; остальное в листинге это пример инструмента и его схема. Беру OpenAI, но петля у всех провайдеров одинаковая:

import json
from openai import OpenAI

client = 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). Шаги известны заранее и зашиты в код: достать данные → суммировать → разложить по полям. Модель вызывается в фиксированных точках. Предсказуемо, дёшево, легко отлаживать. Бо́льшая часть «агентных» задач на деле решается здесь.

  • Один агент с инструментами. Маршрут заранее неизвестен, модель сама выбирает шаги. Честный агент, и для большинства реальных задач его достаточно.

  • Мультиагентная система. Оркестратор раздаёт подзадачи агентам-специалистам и собирает результат. Звучит солидно, но цена и сложность отладки растут нелинейно: ошибку приходится искать уже не в одной траектории, а в их переплетении. Оправдано, когда задача честно бьётся на независимые куски, — например, параллельный обзор большой кодовой базы.

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

Пять способов сжечь бюджет и доверие команды

  1. Накопление ошибок. Агент ошибся на третьем шаге, а следующие десять достраивал на основе неверного вывода. Длинные траектории умножают мелкие неточности, поэтому короткие проверяемые шаги надёжнее одного монолитного плана.

  2. Бесконечная петля. Агент дёргает один и тот же инструмент, не приближаясь к цели, и спокойно мотает счётчик токенов. Потолок шагов тут не лекарство, а предохранитель: он лишь оборвёт агента на 12-м повторе. Чтобы ловить именно зацикливание, добавляют детект «нет прогресса» — одинаковый вызов с теми же аргументами дважды подряд останавливает петлю.

  3. Незаметная стоимость. Каждый шаг пересылает модели весь накопленный контекст, и задача на 15 действий легко обходится в 10–20 раз дороже одиночного запроса — без кэширования. Prompt caching сбивает цену повторно отправляемого префикса (у OpenAI включается автоматически, у Anthropic — явным cache_control), и для растущего контекста агента это главный рычаг экономии: множитель проседает в разы. Но кэш живёт минутами и греет только неизменный префикс — перетасовал историю в середине, и платишь за неё заново.

  4. Права без границ. Агенту с доступом на запись в прод и без подтверждений однажды хватит одной уверенной галлюцинации, чтобы устроить инцидент. Песочница, режим «только чтение» по умолчанию и человек в контуре на необратимых действиях — базовая гигиена.

  5. Оценка на глазок. «Вроде работает» на пяти примерах — не метрика. Без набора тестовых задач с понятным критерием успеха каждое изменение промпта или модели превращается в лотерею, а деградацию замечают по жалобам пользователей.

Когда агент не нужен

Самая дорогая ошибка — собрать автономного агента там, где хватало куда меньшего:

  • Задача решается одним промптом. Если на входе текст и на выходе текст, агентный цикл — лишние затраты и лишние точки отказа.

  • Маршрут известен заранее. Когда шаги предсказуемы, жёсткий сценарий с вызовами модели в нужных точках надёжнее и дешевле.

  • Цена ошибки высока, а отката нет. Там, где неверное действие необратимо, разумнее оставить решение человеку, а агенту отдать только подготовку.

Если можешь за минуту назвать инструмент, который агент дёрнет на втором шаге — агент возможно оправдан. Если не можешь — тебе нужен не агент, а промпт или жёсткий сценарий.

? Скрытый текст

Если понравилось — подписывайся на мой Telegram

Там регулярно выходят посты про AI, разработку и практическое применение LLM.

Комментарии (0)