Назад до блогу

AI для початківцівПосібник

Як працює AI-агент: цикл, запущений без API-ключа

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

Автор: CheapTokens TeamОпубліковано 6 хв читання

Обкладинка: цикл агента від моделі до виклику інструменту до результату
На цій сторінці
  1. Цикл в одній картині
  2. Запустіть без ключа
  3. Націльте той самий цикл на справжню модель
  4. Чому кожен оберт коштує більше за попередній
  5. Куди рухатися від 40 рядків

Ви просите агента для кодування виправити одну друкарську помилку і спостерігаєте, як прокручується журнал: прочитати файл, пошукати, прочитати інший файл, відредагувати, запустити тести. Один маленький запит — десяток викликів моделі. Звідки вони беруться?

AI-агент — це цикл. Ваша програма надсилає розмову моделі; модель або відповідає, або просить інструмент ("прочитай цей файл"); ваша програма запускає інструмент, додає результат до розмови і знову викликає модель. Це повторюється, доки модель не відповість звичайним текстом. Агенти для кодування, такі як Claude Code, Codex і режим агента в Cursor, побудовані навколо цього самого циклу, з більшою кількістю інструментів і більшою обережністю. Нижче ви запустите версію на 40 рядків взагалі без ключа, а потім націлите її на справжню модель.

Цикл в одній картині#

Уявіть менеджера, який не може нічого торкатися. Менеджер лише пише записки: "будь ласка, принеси мені todo.txt". Помічник (ваша програма) йде до полиці, приносить файл і кладе його на стіл. Менеджер читає його і пише наступну записку або остаточну відповідь.

Ось і весь трюк. Модель ніколи не відкриває файл і не запускає команду. Вона пише текст, у якому вказано, який інструмент викликати і з якими аргументами. Ваш код виконує роботу і доповідає назад. Якщо ви бачили function calling, це той самий механізм, що повторюється, доки роботу не буде виконано.

Цикл агента: повідомлення надходять до моделі, модель просить інструмент, програма запускає його, і результат повертається до повідомлень
Цикл: повідомлення → модель → виклик інструменту → результат інструменту → знову модель

Чи можете ви розставити один оберт циклу в правильному порядку?

Розставте по порядку

Розставте один оберт циклу агента по порядку

  1. Надіслати завдання та список інструментів моделі
  2. Модель відповідає викликом інструменту, а не текстом
  3. Ваша програма сама запускає цей інструмент
  4. Результат додається до повідомлень як повідомлення "tool"
  5. Модель викликається знову з результатом, доки не відповість звичайним текстом

Запустіть без ключа#

Скажімо, завдання таке: "Скільки елементів у todo.txt?". Інструмент — це функція read_file, яка шукає у маленькому словнику замість диска. А замість справжньої моделі є сценарна, яка завжди робить ті самі дві речі: просить файл, потім відповідає. Цього достатньо, щоб побачити кожну рухому частину. Збережіть це як agent_loop.py (Python 3.10+, лише стандартна бібліотека):

python
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_loop.py. Без LLM_API_KEY у вашому середовищі ви отримаєте сценарну модель, і екран покаже рівно два оберти:

Вивід термінала сценарного демо: крок 1 запускає інструмент read_file, крок 2 друкує відповідь
Два оберти: виклик інструменту, потім відповідь

Подивіться на три рядки, бо в них уся ідея:

  • messages.append(reply) зберігає власний хід моделі. Пропустіть його, і наступний виклик міститиме результат інструменту, що відповідає на запит, якого API ніколи не бачив; більшість API відхилять це з помилкою 400.
  • arguments надходить як JSON рядок, тому він проходить через json.loads, перш ніж ви викличете функцію.
  • max_steps — це аварійне гальмо. Модель може нескінченно просити інструменти, тому кожен справжній агент має такий ліміт.

Націльте той самий цикл на справжню модель#

Змінюється лише одне: хто грає роль моделі. Скрипт читає три змінні середовища, і якщо ключ присутній, він надсилає той самий messages на сумісну з OpenAI кінцеву точку. Адреса CheapTokens — https://api.cheaptokens.dev/v1 (сумісна з OpenAI Chat Completions та Anthropic Messages), а ідентифікатор моделі має бути повним з документації: deepseek-ai/DeepSeek-V4-Flash-0731.

Запустіть цикл трьома способами

Без ключа

bash
python agent_loop.py

Сценарна модель грає обидва оберти. Нічого не залишає вашу машину.

DeepSeek-V4-Flash

bash
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

bash
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_file(...), потім step 2: answer -> ..., з рядком usage: під кожним викликом моделі. Формулювання відповіді відрізнятиметься від сценарного, і балакуча модель може зробити додатковий оберт. Це нормально: модель вирішує, цикл лише підкоряється. Тримайте ключ поза самим скриптом; змінна середовища — безпечне місце (ось як його зберігати).

Рядки usage: — це корисна частина. Додайте числа in довшого запуску, і ви побачите, куди йдуть гроші.

Чому кожен оберт коштує більше за попередній#

Модель нічого не пам'ятає між викликами. Кожен оберт знову надсилає весь список messages: завдання, кожен попередній виклик інструменту, кожен результат інструменту. (Те саме правило робить довгий чат дорогим; багатоходовий чат показує це на звичайній розмові.)

Оберт 1 надсилає одне повідомлення. Якщо цикл продовжується, оберт 6 надсилає одинадцять.

Оберт 1 надсилає лише завдання; оберт 6 надсилає завдання плюс п'ять викликів інструментів і п'ять результатів інструментів
Що модель отримує на оберті 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 на цьому навантаженні:

Модель Вартість OpenRouter
GLM-5.3-Flash $0.0019 $0.0038
DeepSeek-V4-Flash $0.0016 $0.0046
MiniMax-M2.7 $0.0048 $0.0299

87 500 вхідних і 3 000 вихідних токенів загалом. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.

Одна ціна в будь-який час доби також допомагає тут: агент, який працює всю ніч, коштує стільки ж, скільки опівдні, і ви можете побачити всі ціни на сторінці цін. Для дня таких завдань посувайте повзунки:

Калькулятор вартості

Один день: 20 завдань по 10 обертів кожне

Вхідні токени на запит: 8 750 · Вихідні токени на запит: 300 · Запитів на день: 200

МодельЗа деньЗа місяць
GLM-5.3-Flash$0.0374$1.12
DeepSeek-V4-Flash$0.0323$0.968
MiniMax-M2.7$0.0965$2.90

Поля вказуються на виклик моделі: 8 750 — це середній вхід одного оберту з прикладу вище (87 500 ÷ 10), а 200 викликів — це 20 завдань × 10 обертів. Ось чому рахунки агентів стосуються історії, а не питання. Приклад на 20 кроків заглиблюється в той самий ефект.

Швидка перевірка

Оберт 3 циклу агента коштує більше за оберт 1, хоча ви нічого нового не питали. Чому?

Показати відповідь

Відповідь: Список повідомлень тепер містить два виклики інструментів і два результати, і все це надсилається знову

API не має стану. Кожен виклик несе повну історію, тому вхідні токени зростають з кожним обертом, тоді як вихідні залишаються приблизно тими самими.

Куди рухатися від 40 рядків#

Скрипт вище — це начерк для навчання. Якщо ви хочете довшу версію, є відкритий курс на GitHub, justxor/agent-harness-course (README російською, перевірено 31 жовтня 2026 року). Це чистий Python 3.10+ без залежностей, код під ліцензією MIT, текст під CC BY 4.0. Після клонування python examples/02_agent_loop.py запускає цикл офлайн зі сценарною моделлю, а python -m unittest discover -s tests запускає його 19 тестів без мережі. Курс продовжується ущільненням контексту, пам'яттю, субагентами, схваленням ризикованих дій людиною та відстеженням витрат. Для справжньої моделі він читає LLM_PROVIDER, LLM_MODEL, LLM_API_KEY і, для сумісних з OpenAI кінцевих точок, LLM_BASE_URL: візьміть точне значення LLM_PROVIDER з його README.

Коли цикл працює, наступний крок — справжній ключ і справжнє завдання: у документації є перший запит за хвилину, а DeepSeek-V4-Flash — гарна модель для запуску циклу.