AI для новичковИнструкция
Как устроен ИИ-агент: цикл, который запускается без ключа
ИИ-агент — это цикл: модель просит инструмент, ваш код его запускает, результат возвращается. Запустите демо на Python без ключа, потом подключите модель.

Содержание
Вы просите агента для кода поправить одну опечатку и смотрите, как прокручивается лог: прочитал файл, поискал, прочитал другой, поправил, запустил тесты. Одна мелкая просьба — и десяток обращений к модели. Откуда они берутся?
ИИ-агент — это цикл. Ваша программа отправляет модели разговор; модель либо отвечает, либо просит инструмент («прочитай этот файл»); программа выполняет инструмент, добавляет результат в разговор и снова зовёт модель. И так, пока модель не ответит обычным текстом. Агенты для кода вроде Claude Code, Codex и агентного режима Cursor построены вокруг того же цикла — только инструментов у них больше, а аккуратности тоже. Ниже вы запустите версию в 40 строк совсем без ключа, а потом направите её на настоящую модель.
Цикл на одной картинке#
Представьте начальника, который ничего не может потрогать руками. Он только пишет записки: «принеси todo.txt». Помощник (ваша программа) идёт к полке, приносит файл и кладёт на стол. Начальник читает и пишет следующую записку или итоговый ответ.
В этом весь фокус. Модель сама не открывает файлы и не запускает команды. Она пишет текст: какой инструмент вызвать и с какими аргументами. Работу делает ваш код и докладывает результат. Если вы видели вызов функций, это тот же механизм, только повторённый, пока задача не решена.

Сможете расставить один круг цикла по порядку?
Расставьте по порядку
Расставьте один круг цикла агента по порядку
Перетащите элементы или используйте стрелки
- Отправить модели задачу и список инструментов
- Модель отвечает вызовом инструмента, а не текстом
- Ваша программа сама выполняет этот инструмент
- Результат попадает в сообщения как «tool»-сообщение
- Модель вызывается снова с результатом — пока не ответит обычным текстом
Запускаем без ключа#
Допустим, задача такая: «Сколько пунктов в todo.txt?». Инструмент — функция read_file, которая смотрит не на диск, а в маленький словарь. А вместо настоящей модели — сценарная: она всегда делает две вещи, сначала просит файл, потом отвечает. Этого достаточно, чтобы увидеть все детали. Сохраните как agent_loop.py (Python 3.10+, только стандартная библиотека):
import json, os, urllib.request
NOTES = {"todo.txt": "buy milk\ncall Anna\nsend invoice"}
def read_file(name):
return NOTES.get(name, "no such file")
TOOLS = {"read_file": read_file}
TOOL_SPECS = [{"type": "function", "function": {
"name": "read_file", "description": "Read a note by file name",
"parameters": {"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"]}}}]
def scripted_model(messages):
if messages[-1]["role"] == "user": # first lap: ask for the file
return {"role": "assistant", "content": None, "tool_calls": [{
"id": "call_1", "type": "function",
"function": {"name": "read_file",
"arguments": "{\"name\": \"todo.txt\"}"}}]}
return {"role": "assistant", "content": "The file has 3 items."}
def real_model(messages):
body = {"model": os.environ["LLM_MODEL"], "messages": messages,
"tools": TOOL_SPECS}
req = urllib.request.Request(
os.environ["LLM_BASE_URL"] + "/chat/completions",
data=json.dumps(body).encode(),
headers={"Content-Type": "application/json",
"User-Agent": "agent-loop-demo/1.0",
"Authorization": "Bearer " + os.environ["LLM_API_KEY"]})
with urllib.request.urlopen(req) as resp:
data = json.load(resp)
u = data["usage"]
print(" usage: in", u["prompt_tokens"], "out", u["completion_tokens"])
return data["choices"][0]["message"]
def run(task, model, max_steps=8):
messages = [{"role": "user", "content": task}]
for step in range(1, max_steps + 1):
reply = model(messages)
messages.append(reply) # keep the model's turn in history
calls = reply.get("tool_calls")
if not calls:
print(f"step {step}: answer -> {reply['content']}")
return
for call in calls:
name = call["function"]["name"]
args = json.loads(call["function"]["arguments"])
result = TOOLS[name](**args) # YOUR code does the work
print(f"step {step}: tool {name}({args}) -> {result!r}")
messages.append({"role": "tool", "tool_call_id": call["id"],
"content": result})
print("stopped: max_steps reached")
model = real_model if os.environ.get("LLM_API_KEY") else scripted_model
run("How many items are in todo.txt?", model)Запустите python agent_. Пока в окружении нет LLM_API_KEY, работает сценарная модель, и на экране ровно два круга:

Обратите внимание на три места: в них вся идея.
messages.сохраняет собственный ход модели. Уберёте — следующий запрос будет содержать результат инструмента, на просьбу о котором API ничего не видел; большинство API отвечает на это ошибкой 400.append(reply) argumentsприходит JSON-строкой, поэтому перед вызовом функции её нужно пропустить черезjson.loads.max_steps— стоп-кран. Модель может просить инструменты бесконечно, поэтому у любого настоящего агента есть такой предел.
Тот же цикл — на настоящей модели#
Меняется одно: кто играет модель. Скрипт читает три переменные окружения, и если ключ есть, отправляет те же messages на OpenAI-совместимый адрес. Адрес CheapTokens — https:/ (совместим с OpenAI Chat Completions и Anthropic Messages), а id модели нужен полный, из документации: deepseek-.
Три способа запустить цикл
Без ключа
python agent_loop.pyСценарная модель играет оба круга. Ничего не уходит с вашего компьютера.
DeepSeek-V4-Flash
export LLM_BASE_URL=https://api.cheaptokens.dev/v1
export LLM_API_KEY=<your key>
export LLM_MODEL=deepseek-ai/DeepSeek-V4-Flash-0731
python agent_loop.pyКонтекст 380K и до 32 768 токенов в ответе: хватит места для длинного цикла.
GLM-5.3-Flash
export LLM_BASE_URL=https://api.cheaptokens.dev/v1
export LLM_API_KEY=<your key>
export LLM_MODEL=zai-org/GLM-5.3-Flash
python agent_loop.pyЛёгкая модель с контекстом 390K для первого запуска многошагового цикла.
На экране будет та же картина: step 1: tool read_, затем step 2: answer -, а под каждым обращением к модели — строка usage:. Формулировка ответа будет отличаться от сценарной, а разговорчивая модель может сделать лишний круг. Это нормально: решает модель, цикл только подчиняется. Ключ в сам скрипт не вписывайте: переменная окружения — безопасное место (как хранить ключ).
Строки usage: — самое полезное. Сложите числа in в длинном прогоне, и станет видно, куда уходят деньги.
Почему каждый круг дороже предыдущего#
Между вызовами модель ничего не помнит. Каждый круг отправляет весь список messages заново: задачу, все прежние вызовы инструментов и все результаты. (Тот же закон делает дорогим длинный чат; на обычном разговоре его показывает статья про многошаговый чат.)
На круге 1 уходит одно сообщение. Если цикл продолжается, на круге 6 уйдёт одиннадцать.

Допустим, настоящая задача занимает 10 кругов. Начинается она примерно с 2 000 токенов (инструкции, список инструментов, ваш запрос), каждый круг добавляет около 1 500 (результат инструмента и заметка модели), а модель пишет около 300 токенов за круг. Круг 1 отправляет 2 000, круг 2 — 3 500, круг 10 — 15 500. Первая часть суммы: 10 × 2,000 = 20,000 токенов; рост добавляет 1,500 × 45 = 67,500 (45 — это 0+1+…+9), значит, вход за задачу — 20,000 + 67,500 = 87,500 токенов. Выход: 10 × 300 = 3,000 токенов.
Текст самой задачи — пара предложений, а входящих токенов 87 500. При цене $0.015 за 1M входных токенов вся задача на DeepSeek-V4-Flash обойдётся в $0.0016. Ниже та же нагрузка на разных моделях, для справки — OpenRouter. В сумме, с учётом входа и выхода, CheapTokens на этой нагрузке дешевле OpenRouter:
| Модель | Вход $/1M | Выход $/1M | Стоимость | OpenRouter |
|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.0019 | $0.0038 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.0016 | $0.0046 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.0048 | $0.0299 |
87 500 входных и 3 000 выходных токенов всего. Цены за 1M токенов актуальны и меняются вместе с нашими тарифами; столбец OpenRouter показывает справочные цены для той же нагрузки.
Помогает и то, что цена одна в любое время суток: агент, который работает ночью, стоит столько же, сколько в полдень; все цены — на странице цен. А чтобы прикинуть день таких задач, подвигайте ползунки:
Калькулятор стоимости
Один день: 20 задач по 10 кругов
Входные токены на запрос: 8 750 · Выходные токены на запрос: 300 · Запросов в день: 200
| Модель | В день | В месяц |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.0374 | $1.12 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.0323 | $0.968 |
| MiniMax-M2.7$0.05$0.15 | $0.0965 | $2.90 |
Поля — на один вызов модели: 8 750 — средний вход круга из примера выше (87 500 ÷ 10), а 200 вызовов — это 20 задач × 10 кругов. Поэтому счёт агента — это про историю, а не про вопрос. Тот же эффект подробнее разобран в примере на 20 шагов.
Быстрая проверка
На круге 3 цикл агента стоит дороже, чем на круге 1, хотя вы ничего нового не спрашивали. Почему?
Показать ответ
Ответ: В messages уже два вызова инструментов и два результата, и всё это отправляется заново
API не хранит состояние. Каждый вызов несёт всю историю, поэтому входных токенов с каждым кругом больше, а выход остаётся примерно таким же.
Что дальше после 40 строк#
Скрипт выше — учебный набросок. Если нужна полная версия, есть открытый курс на GitHub: justxor/agent-harness-course (README на русском, страницу смотрели 31 октября 2026 года). Это чистый Python 3.10+ без зависимостей, код под лицензией MIT, текст — CC BY 4.0. После клонирования python examples/ запускает цикл офлайн со сценарной моделью, а python - гоняет 19 тестов без сети. Дальше в курсе — сжатие контекста, память, субагенты, подтверждение рискованных действий человеком и учёт расходов. Для настоящей модели он читает LLM_PROVIDER, LLM_MODEL, LLM_API_KEY и, для OpenAI-совместимых адресов, LLM_BASE_URL: точное значение LLM_PROVIDER возьмите из его README.
Когда цикл работает, следующий шаг — настоящий ключ и настоящая задача: в документации первый запрос занимает минуту, а DeepSeek-V4-Flash — хорошая модель, чтобы гонять на ней цикл.



