agent-loop — как агент работает под капотом¶
Главный секрет: в агенте нет никакой магии. Это обычные запросы к LLM-серверу — как из чата — только «особенные»: в запросе перечислены инструменты, а модель в ответе может попросить один из них вызвать. Дальше — цикл.
Вспомни: модель — это угадайка текста, она физически ничего не может запустить. Всё, что она умеет — написать текст. Тогда как агент «читает файлы» и «запускает команды»?
Фокус такой:
- Агент шлёт серверу не просто твой вопрос, а вопрос + список своих инструментов: «у меня есть "прочитать файл", "запустить команду" — если надо, скажи, что вызвать».
- Модель отвечает не человеческим текстом, а запиской: «вызови "прочитать файл" с именем app.log». Это всё ещё просто текст! Модель ничего не запускала.
- Агент читает записку, сам выполняет действие на твоём компьютере и дописывает результат в разговор.
- И отправляет весь разговор заново: вопрос + записка + результат. Модель смотрит и либо просит следующий инструмент, либо отвечает по-человечески — тогда цикл кончился.
sequenceDiagram
participant U as 👤 Ты
participant A as 🤖 Агент
participant L as ☁️ LLM-сервер
U->>A: «что за ошибки в логах?»
A->>L: запрос №1: вопрос + список инструментов
L-->>A: 📝 «вызови read_file("app.log")»
Note over A: агент САМ читает файл<br/>(модель не умеет)
A->>L: запрос №2: всё то же + содержимое файла
L-->>A: «ошибки две: кончилось место и ...»
A-->>U: готовый ответ
Из этого следуют неожиданные вещи:
- Сервер не помнит агента между запросами. Каждый запрос — самодостаточный, с полной историей внутри. «Диалог» — иллюзия, которую поддерживает агент.
- Чем дольше агент работает, тем дороже каждый шаг — история-то растёт, а токены платные.
- Модель можно «обмануть» — она верит результатам инструментов. Поэтому агенту нужны ограничители (см. agents.md).
Агентный цикл — это последовательность обычных stateless HTTP-запросов к LLM-серверу. «Особенность» ровно одна: поле tools в запросе и ответ с tool call вместо (или вместе с) текстом.
Запрос №1 — сообщение + декларация тулов¶
json
{
"model": "claude-...",
"messages": [
{ "role": "user", "content": "что за ошибки в логах?" }
],
"tools": [
{
"name": "read_file",
"description": "Читает файл с диска",
"input_schema": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
}
}
]
}
Ответ — модель «просит» вызвать тул¶
json
{
"stop_reason": "tool_use",
"content": [
{ "type": "tool_use", "id": "toolu_01", "name": "read_file",
"input": { "path": "app.log" } }
]
}
Это просто JSON, сгенерированный моделью как текст. Сервер ничего не выполнял — он даже не знает, существует ли app.log.
Запрос №2 — та же история + результат тула¶
Агент выполнил чтение файла у себя и шлёт продолжение — весь диалог целиком, добавив ответ модели и результат:
json
{
"messages": [
{ "role": "user", "content": "что за ошибки в логах?" },
{ "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01", "...": "..." } ] },
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_01",
"content": "ERROR: disk full\nERROR: timeout ..." }
] }
],
"tools": [ "...тот же список..." ]
}
Цикл крутится, пока stop_reason не станет end_turn — тогда в content обычный текст, и агент показывает его пользователю.
sequenceDiagram
participant A as Агент (слой 3)
participant L as LLM-сервер (слой 2)
loop пока stop_reason == "tool_use"
A->>L: POST /v1/messages { messages[], tools[] }
L-->>A: { tool_use: name + input }
Note over A: выполняет тул локально,<br/>аппендит tool_result в messages[]
end
L-->>A: { stop_reason: "end_turn", content: текст }
(Формат выше — Anthropic Messages API; у OpenAI-совместимых серверов то же самое называется tool_calls / role: "tool" — идея идентична.)
Следствия, которые важно понимать¶
- Stateless. Сервер не хранит состояние агента: каждый запрос несёт всю историю. Память, «личность», проект — всё это агент собирает в
messagesсам. - Контекст и стоимость растут с каждым шагом. Длинная сессия = дорогие запросы; агенты сжимают/суммируют историю (context management).
- Тулы декларативны. Модель видит только
name + description + JSON Schema. Чем лучше описание — тем точнее выбор тула. MCP (см. mcp.md) — стандартный способ подсунуть агенту этот список со стороны. - Валидировать и ограничивать — обязанность агента. Модель может сгенерировать вызов несуществующего тула или опасные аргументы; агент проверяет схему, спрашивает подтверждение, режет лимиты.
Связанные страницы¶
- agents.md — что такое агент и какие бывают
- llm-server.md — API, в которое всё это шлётся
- llm-model.md — почему каждый раз нужен весь контекст
- mcp.md — стандартный способ раздать агенту тулы