Контекст: что и зачем
Агентные системы на LLM — это не просто «GPT с инструментами». Это архитектурный подход, где модель сама принимает решения о следующем шаге: какой инструмент вызвать, как интерпретировать результат, нужно ли повторить попытку. Звучит красиво. Первый месяц работы с такими системами показал, где граница между реальной пользой и переусложнением.
Рассматриваем сценарии на Claude API и OpenAI Function Calling. Архитектура стандартная: модель + набор инструментов + loop обработки до получения финального ответа.
Tool use: что работает хорошо
Вызов инструментов — самая зрелая часть агентных систем на сегодня. Если инструмент хорошо описан (чёткое имя, понятный description, аккуратная JSON-схема параметров), модель вызывает его корректно в 90%+ случаев. Ошибки почти всегда можно отследить к плохому описанию, а не к модели.
Несколько правил, которые дали наибольший эффект:
- Имя инструмента — глагол + существительное:
search_web,read_file,send_message. - Description описывает когда использовать инструмент, а не только что он делает.
- Параметры с типами и enum там, где они есть — меньше галлюцинаций.
- Отдельный инструмент «завершить задачу» с полем для финального ответа убирает петли.
# Пример описания инструмента для 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 токенов проблема контекста никуда не делась — она сместилась. Раньше контекст заканчивался. Теперь он не заканчивается, но модель начинает «терять нить» на длинных задачах: повторяет уже сделанные шаги, игнорирует ранние инструкции, циклится.
Практические решения, которые помогли:
- Суммаризация истории. После каждых N шагов агент пишет краткое резюме прогресса в отдельное поле, и дальше работает с резюме вместо полной истории.
- Явный scratchpad. Отдельный инструмент
update_plan, куда агент пишет текущий план. При каждом вызове system prompt содержит этот план. - Лимит шагов. Жёсткий максимум итераций (обычно 20–30). Без него агенты иногда уходят в бесконечный поиск «лучшего» решения.
Multi-step задачи: где агенты выигрывают
Агенты хороши для итерационных задач, где следующий шаг зависит от результата предыдущего и заранее неизвестен. Примеры из практики:
- Исследование незнакомой кодовой базы: агент сам решает, какой файл читать следующим.
- Отладка по стектрейсу с доступом к инструментам поиска и чтения файлов.
- Сбор данных из нескольких источников с адаптацией стратегии под доступность.
- Заполнение форм или работа с API с валидацией и повторными попытками.
Агенты плохи для задач с жёстким форматом вывода и детерминированными шагами. Если шаги известны заранее — пишите обычный пайплайн, не агента. Агент добавит непредсказуемость туда, где она не нужна.
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 "Превышен лимит шагов"
Выводы
Агентные системы работают и дают реальную ценность там, где задача итерационная и заранее не поддаётся полной декомпозиции. Ключевые факторы успеха: хорошо описанные инструменты, явное управление контекстом, лимиты на шаги и структурированные ответы инструментов.
Не стоит использовать агентов, когда пайплайн можно написать статически — это добавляет непредсказуемость без выигрыша. Агент — это инструмент для неопределённости, а не замена обычного кода.