Контекст: что и зачем

Агентные системы на LLM — это не просто «GPT с инструментами». Это архитектурный подход, где модель сама принимает решения о следующем шаге: какой инструмент вызвать, как интерпретировать результат, нужно ли повторить попытку. Звучит красиво. Первый месяц работы с такими системами показал, где граница между реальной пользой и переусложнением.

Рассматриваем сценарии на Claude API и OpenAI Function Calling. Архитектура стандартная: модель + набор инструментов + loop обработки до получения финального ответа.

Tool use: что работает хорошо

Вызов инструментов — самая зрелая часть агентных систем на сегодня. Если инструмент хорошо описан (чёткое имя, понятный description, аккуратная JSON-схема параметров), модель вызывает его корректно в 90%+ случаев. Ошибки почти всегда можно отследить к плохому описанию, а не к модели.

Несколько правил, которые дали наибольший эффект:

# Пример описания инструмента для Claude API
tools = [
    {
        "name": "search_codebase",
        "description": (
            "Ищет по исходному коду проекта. Используй, когда нужно найти "
            "определение функции, класса или переменной. НЕ используй для "
            "поиска по документации — для этого есть search_docs."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Поисковый запрос, например имя функции"
                },
                "file_pattern": {
                    "type": "string",
                    "description": "Glob-паттерн, например '*.py'. По умолчанию все файлы."
                }
            },
            "required": ["query"]
        }
    }
]

Context window: главное ограничение

Даже у моделей с контекстом 200k токенов проблема контекста никуда не делась — она сместилась. Раньше контекст заканчивался. Теперь он не заканчивается, но модель начинает «терять нить» на длинных задачах: повторяет уже сделанные шаги, игнорирует ранние инструкции, циклится.

Практические решения, которые помогли:

Multi-step задачи: где агенты выигрывают

Агенты хороши для итерационных задач, где следующий шаг зависит от результата предыдущего и заранее неизвестен. Примеры из практики:

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

Failure modes: что ломается

За месяц встретились следующие типичные сбои:

Зацикливание. Агент вызывает один и тот же инструмент снова и снова с немного разными параметрами. Лечится лимитом на повторные вызовы одного инструмента с одинаковыми аргументами и явным инструктажем в system prompt: «если инструмент не помог — попробуй другой подход или сообщи о невозможности».

Галлюцинация результатов. Модель «запоминает» вывод инструмента неточно и в следующем шаге работает с искажёнными данными. Особенно часто при длинных выводах. Решение: инструменты возвращают структурированный JSON с явными полями, а не plain text.

Преждевременная остановка. Агент объявляет задачу выполненной, когда сделал только часть. Помогает checklist в system prompt: «Перед завершением убедись, что выполнено: пункт 1, пункт 2, пункт 3».

Игнорирование ошибок инструментов. Агент получает ошибку, но продолжает как будто всё хорошо. Инструменты должны явно возвращать {"error": "...", "success": false}, а не просто пустой результат или исключение.

import anthropic

def run_agent(task: str, tools: list, max_steps: int = 25):
    client = anthropic.Anthropic()
    messages = [{"role": "user", "content": task}]
    step = 0

    while step < max_steps:
        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

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

        if response.stop_reason == "end_turn":
            # Финальный текстовый ответ
            for block in response.content:
                if hasattr(block, 'text'):
                    return block.text
            break

        if response.stop_reason == "tool_use":
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = call_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": str(result)
                    })
            messages.append({"role": "user", "content": tool_results})

        step += 1

    return "Превышен лимит шагов"

Выводы

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

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