Что значит «агент» и почему это важно

Слово «агент» превратилось в маркетинговый термин: агентом называют и простую цепочку промптов, и сложную мультиагентную систему. Из-за этого возникает путаница — непонятно, что строить, где граница между «умным пайплайном» и «настоящим агентом», и почему одно сложнее другого.

Чёткое определение: AI-агент — это программа, которая в цикле воспринимает состояние среды, рассуждает о нём с помощью LLM и предпринимает действия для достижения цели. Три ключевых слова: цикл, рассуждение, действие.

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

ℹ️ Chatbot vs Pipeline vs Agent

Chatbot — LLM отвечает на вопросы. Нет инструментов, нет памяти между сессиями, нет целей.
Pipeline — фиксированная цепочка шагов: ввод → шаг 1 → шаг 2 → вывод. Логика жёстко задана разработчиком.
Agent — LLM сам решает, какие инструменты вызывать, в каком порядке и когда остановиться. Логика гибкая, определяется задачей.

Четыре компонента любого агента

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

100%
колёсико — масштаб  ·  зажать и тянуть — перемещение
ВНЕШНИЙ МИР 🌐 Web / API 🗄 Базы данных 📁 Файловая система 📧 Email / Slack 🔧 Shell / Код AI-АГЕНТ Инструменты • search_web(query) • query_database(sql) • send_email(to, body) • run_code(script) Python-функции + JSON Schema tool_use tool_result LLM Claude · GPT · Gemini рассуждает · планирует принимает решения system prompt + messages history retrieve store Память • история сообщений • embedding-векторы • entity facts / профили • scratchpad (chain-of-thought) in-context + external (pgvector) Цикл управления PERCEIVE получить ввод обновить контекст THINK LLM рассуждает tool_use или ответ? ACT вызвать инструмент или завершить OBSERVE получить результат добавить в память ← повтор до достижения цели или лимита итераций

Каждый компонент независим и может быть заменён. Другой LLM, другая память, другие инструменты — цикл управления остаётся тем же. Именно поэтому архитектура важнее выбора конкретного провайдера.

LLM как движок рассуждений

LLM в агенте — это не просто «вызов API». Это единственный компонент, который принимает решения: что делать дальше, какой инструмент вызвать, достаточно ли информации для ответа.

Модель получает на вход весь накопленный контекст — system prompt с инструкциями, историю сообщений, результаты предыдущих инструментов — и возвращает либо финальный ответ (stop_reason: "end_turn"), либо запрос на вызов инструмента (stop_reason: "tool_use").

stop
end_turn — модель решила, что задача выполнена. Агент возвращает ответ пользователю и останавливает цикл.
tool
tool_use — модель хочет вызвать один или несколько инструментов. Агент выполняет их и добавляет результаты в контекст.
limit
max_tokens — контекстное окно переполнено. Нужно суммаризировать историю или включить механизм сжатия памяти.
guard
Лимит итераций — защита от бесконечного цикла. Всегда устанавливай max_iterations, иначе агент может зациклиться.

Качество рассуждений напрямую зависит от system prompt. Именно там задаётся: какова цель агента, какие инструменты доступны и когда их использовать, как форматировать ответы, какие ограничения и границы поведения.

⚠️ LLM не детерминирован

При одном и том же вводе модель может принять разные решения о порядке вызова инструментов. Агент должен корректно работать при любом разумном порядке действий — не рассчитывай на фиксированную последовательность шагов.

Инструменты: как агент действует

Инструменты — это мост между LLM и реальным миром. Сам LLM ничего не «делает»: он только решает, какую функцию вызвать и с какими аргументами. Реальное действие происходит в Python-функции, которую ты пишешь сам.

Каждый инструмент — это пара: JSON Schema (описание для LLM) + Python-функция (реализация). Подробнее о JSON Schema — в уроке «Описание инструментов».

поиск
Информационные — поиск в интернете, запросы к БД, чтение файлов, семантический поиск в векторном хранилище.
действие
Исполнительные — отправка email, запись в БД, вызов внешнего API, выполнение shell-команды, управление файлами.
код
Вычислительные — выполнение Python-кода, математические расчёты, анализ данных, преобразование форматов.
агент
Агентские — вызов другого агента как инструмента. Основа мультиагентных систем (об этом — в Модуле 06).

Хорошее правило: один инструмент — одна ответственность. Не делай «универсальный инструмент на все случаи» — LLM хуже справляется с выбором аргументов для перегруженных инструментов.

Память агента: четыре уровня

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

L1
In-context memory — список messages, передаваемый в каждый запрос к LLM. Быстрая, но ограничена контекстным окном. Исчезает после сессии.
L2
External memory — pgvector, Chroma, Redis. Семантический поиск по истории. Переживает перезапуски, масштабируется на миллионы записей.
L3
Entity memory — структурированные факты о сущностях: пользователь предпочитает Python, проект использует PostgreSQL. Хранится в БД, обновляется по ходу разговора.
L4
Scratchpad — рабочая область для промежуточных рассуждений (chain-of-thought). Помогает модели «думать вслух» до финального ответа, не засоряя основной контекст.

Для большинства production-агентов достаточно L1 + L2: история в контексте плюс векторный поиск по долгосрочной памяти через pgvector. L3 добавляют, когда агент должен «знать» конкретного пользователя. L4 — для сложных задач с многошаговым планированием.

ℹ️ Переполнение контекста

In-context память растёт с каждым сообщением. При длинных сессиях контекстное окно переполняется. Типичные решения: суммаризация старой истории через LLM, скользящее окно последних N сообщений, или вынос истории в external memory с retrieval по релевантности.

Реактивный цикл управления

Цикл управления — это код, который связывает все четыре компонента. Это обычный while-loop, который работает до тех пор, пока агент не решит остановиться или не превысит лимит итераций.

perceive
Воспринять
Получить ввод от пользователя или предыдущего шага. Обновить контекст новыми данными.
think
Рассуждать
Отправить контекст в LLM. Получить решение: вызвать инструмент или ответить.
act
Действовать
Выполнить запрошенные инструменты. Если ответ — завершить цикл.
observe
Наблюдать
Добавить результаты инструментов в контекст. Вернуться к THINK.
↺  повтор до end_turn или max_iterations

Одна итерация цикла — это один запрос к LLM плюс все инструменты, которые он запросил в этом ответе. Число итераций = число «раундов» рассуждения. Типичный агент делает от 2 до 10 итераций; больше — повод оптимизировать промпт или декомпозировать задачу.

Минимальный агент на Python

Абстракции понятны — время написать код. Ниже — полный рабочий агент с двумя инструментами, без фреймворков, ~60 строк. Именно так работают LangGraph, AutoGen и другие фреймворки под капотом:

import anthropic
import json

client = anthropic.Anthropic()

# ── Инструменты ──────────────────────────────────────────────

def get_weather(city: str) -> str:
    """Заглушка: в реальности — вызов API."""
    return f"В {city} сейчас +18°C, облачно."

def calculate(expression: str) -> str:
    """Безопасный eval для простых арифметических выражений."""
    try:
        result = eval(expression, {"__builtins__": {}})
        return str(result)
    except Exception as e:
        return f"Ошибка вычисления: {e}"

TOOLS = [
    {
        "name": "get_weather",
        "description": "Получить текущую погоду в городе. Используй когда пользователь спрашивает о погоде.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "Название города на русском или английском"}
            },
            "required": ["city"]
        }
    },
    {
        "name": "calculate",
        "description": "Вычислить математическое выражение: сложение, умножение, степени и т.д.",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {"type": "string", "description": "Математическое выражение, например '2 ** 10 + 5'"}
            },
            "required": ["expression"]
        }
    }
]

TOOL_FUNCTIONS = {
    "get_weather": get_weather,
    "calculate": calculate,
}

# ── Цикл управления ──────────────────────────────────────────

def run_agent(user_message: str, max_iterations: int = 10) -> str:
    messages = [{"role": "user", "content": user_message}]

    for iteration in range(max_iterations):
        # THINK: отправляем контекст в LLM
        response = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=1024,
            system="Ты полезный ассистент. Используй инструменты когда нужны актуальные данные.",
            tools=TOOLS,
            messages=messages,
        )

        # Добавляем ответ модели в историю
        messages.append({"role": "assistant", "content": response.content})

        # ACT: если модель закончила — возвращаем ответ
        if response.stop_reason == "end_turn":
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text
            return ""

        # ACT: выполняем все запрошенные инструменты
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                fn = TOOL_FUNCTIONS.get(block.name)
                result = fn(**block.input) if fn else f"Инструмент {block.name!r} не найден"
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })

        # OBSERVE: добавляем результаты инструментов в контекст
        messages.append({"role": "user", "content": tool_results})

    return "Достигнут лимит итераций."


# ── Запуск ───────────────────────────────────────────────────
if __name__ == "__main__":
    answer = run_agent("Какая погода в Москве? И сколько будет 2 в степени 16?")
    print(answer)

Обрати внимание на структуру: цикл for iteration in range(max_iterations), два условия выхода (end_turn и лимит итераций), и то, что результаты инструментов добавляются в messages как сообщение роли user — именно такой формат ожидает Anthropic API.

Это и есть основа всех фреймворков

LangGraph, AutoGen, OpenAI Agents SDK — всё это надстройки над этим же циклом. Они добавляют: граф состояний вместо простого while, checkpointing, параллельные ветки, human-in-the-loop. Но ядро — тот же while: think → act → observe.

Когда нужен агент, когда хватит пайплайна

Агент — не всегда правильный выбор. Он сложнее в отладке, дороже в токенах, менее предсказуем. Вот критерий выбора: если шаги решения можно определить заранее — используй пайплайн. Если нет — нужен агент.

📋 Используй пайплайн когда:
Шаги фиксированы: всегда один и тот же маршрут
Инструменты известны заранее и всегда используются
Нет ветвлений на основе промежуточных результатов
Важна предсказуемость: аудит, compliance, финансы
• Пример: извлечь данные → форматировать → сохранить
🤖 Используй агента когда:
Задача непредсказуема: шаги зависят от результатов
Нужна декомпозиция: задача слишком сложна для одного промпта
Много инструментов: агент сам выбирает нужные
Нужна адаптация: план меняется по ходу работы
• Пример: исследуй тему, найди источники, напиши отчёт
⚠️ Типичная ошибка: «агентизация» простых задач

Оборачивать в агента каждый LLM-вызов — антипаттерн. Если задача решается одним промптом — это пайплайн. Агент добавляет сложность, стоимость и непредсказуемость. Используй его только там, где гибкость цикла действительно нужна.

Шпаргалка

Анатомия AI-агента — коротко

LLM          мозг агента; получает весь контекст; решает что делать;
             stop_reason = "end_turn" | "tool_use" | "max_tokens"

Инструменты  Python-функции + JSON Schema; один инструмент = одна задача;
             типы: информационные / исполнительные / вычислительные / агентские

Память       L1 in-context (messages list)  — быстро, ограничено окном
             L2 external (pgvector, Redis)  — медленно, неограниченно
             L3 entity facts               — структурированные знания
             L4 scratchpad (CoT)           — промежуточные рассуждения

Цикл         while not done and iterations < MAX:
               perceive → think (LLM) → act (tools) → observe → repeat

Agent vs Pipeline
  Pipeline  — шаги фиксированы, маршрут известен заранее
  Agent     — шаги определяются LLM на основе промежуточных результатов

Защита от зацикливания
  Всегда:  max_iterations (обычно 10–20)
  Всегда:  обработка stop_reason != "end_turn" && != "tool_use"
  Опцион.: timeout на весь цикл агента

Практическое задание

  1. Расширь минимального агента. Добавь третий инструмент — search_memory(query: str), который ищет по заранее заполненному словарю фактов о пользователе (имя, город, предпочтения). Убедись, что агент использует его когда нужен персональный контекст.
  2. Визуализируй итерации. Добавь в цикл вывод в консоль: номер итерации, название вызванного инструмента и его аргументы. Запусти агента с вопросом, который требует 2–3 итерации, и проследи за циклом.
  3. Сломай цикл намеренно. Убери ограничение max_iterations и создай ситуацию, в которой агент никогда не получит удовлетворительный результат от инструмента (инструмент всегда возвращает «данные недоступны»). Что происходит? Добавь обработку этого случая.
← Следующий раздел
Как LLM вызывает функции
Tool Calling в деталях