← До списку уроків
Заняття 06 з 13Бот питає ШІ: запит до API за потреби
курс 0% виконано0%
Місяць 2 · Заняття 06

Бот питає ШІ: запит до API за потреби

Зрозуміти, як одна програма просить іншу через API: розібрати запит до ШІ по рядках, навчити свого бота відповідати на команду /ask за допомогою безкоштовної ШІ-моделі й побачити, як працює ШІ-помічник для програмістів OpenCode.

90 хвилин теорія 25 хв практика 55 хв GitHub і перевірка 10 хв

Що ти зробиш сьогодні

Досі твій бот відповідав лише тим, що ти сам у нього вписав. Сьогодні він навчиться питати розумну програму — ШІ (штучний інтелект) — і передавати людині її відповідь. У бота з’явиться команда /ask: пишеш /ask що таке порт? — і отримуєш пояснення, якого в коді немає.

Заодно ти вперше розглянеш запит до API по рядках: що таке адреса, метод, заголовки, тіло запиту і код відповіді. Це та сама мова, якою програми розмовляють між собою всюди: у Telegram, у картах, у відео-сервісах.

Наприкінці заняття викладач покаже OpenCode — безкоштовного ШІ-помічника для програмістів, який працює в терміналі. Ти побачиш, чим він відрізняється від звичайного чату з ШІ, і зможеш вдома спробувати сам.

Про ШІШІ часто відповідає цікаво, але іноді впевнено каже неправду. Бот лише передає його слова, і за те, що він відповів, відповідаєш ти, бо це твій бот. Тому сьогодні ми ще й перевіряємо, що бот не ламається, коли ШІ недоступний.

Теорія

1. Запит до API по рядках

Запит за потреби, а не серверУ твоєму боті немає власного «сервера API», який постійно слухає й займає пам’ять. Бот — клієнт: він звертається до Telegram чи до ШІ лише тоді, коли треба (прийшло повідомлення — зробили запит, отримали відповідь, забули). Постійно ввімкнений сервер, навпаки, цілодобово чекає на чужі запити й їсть пам’ять, а ще відчиняє двері назовні. Тому в тебе на сервері такого немає.

Сьогодні ти вчишся саме бути клієнтом.

API — спосіб, яким одна програма просить іншу щось зробити (так само, як у словничку заняття 02): Telegram має своє API, ШІ має своє. Ти надсилаєш запит у певному порядку, а у відповідь отримуєш дані. Запит іде за правилами, які називають HTTP — це ті самі правила, за якими браузер відкриває сайти. Коли бот питає ШІ, він посилає саме такий запит. Тільки замість гарної сторінки у відповідь приходять дані. Ось запит, який посилає твій бот (скорочено):

POST /llm/v1/chat/completions HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Authorization: Bearer мій-ключ

{"model": "openrouter/free",
 "messages": [{"role": "user", "content": "що таке порт?"}]}

Розберемо рядок за рядком:

2. Відповідь: код і JSON

На кожен запит сервер відповідає кодом — числом, яке коротко каже, як усе минуло. Ці коди ти вже зустрічав:

КодЩо означаєЩо робить наш бот
200Усе добре, ось відповідьДістає текст і віддає людині
401, 403Ключ не підійшов або його немаєКаже: «ШІ не пускає, перевір ключ у .env»
429Забагато запитів — ліміт вичерпаноКаже: «Спробуй пізніше»
503ШІ ще не підключено або тимчасово не працюєКаже: «ШІ ще не підключено, скажи викладачу»

Коли код 200, у тілі відповіді приходить JSON. Потрібний нам текст лежить глибоко, тому до нього йдемо вкладеністю — так само, як на занятті 02 діставали ім’я з оновлення Telegram:

{"choices": [
   {"message": {"role": "assistant",
                "content": "Порт — це номер «дверей» програми на сервері."}}
]}

У Python це data["choices"][0]["message"]["content"]: «візьми список choices, його перший елемент (рахунок іде з нуля), потім message, потім content».

Бот отримав від сервера ШІ код 429. Що це означає?

3. Адреса, ключ і модель живуть у .env, а не в коді

Куди ходити по ШІ, з яким ключем і яку модель вибрати — це три налаштування. Їх ми кладемо у файл ~/app/.env (той самий, де лежить токен бота), а в коді лише читаємо:

LLM_BASE_URL=http://127.0.0.1/llm/v1
LLM_API_KEY=school
LLM_MODEL=openrouter/free

Є дві причини так робити. Перша — безпека: ключ — це пароль, а паролі не лежать у програмі, яку ти відправляєш на GitHub. Друга — гнучкість: захочеш інший ШІ — змінюєш три рядки у .env і перезапускаєш службу; програму не чіпаєш. Так програмісти змінюють ШІ чи його адресу, не переписуючи код.

Слово «модель»Тут «модель» — це сам ШІ. Не плутай із заняттям 05, де «модель» означала опис полів даних.

Рядки починаються з LLM_ — це скорочення від «великі мовні моделі» (Large Language Models), так називають ШІ, що вміють писати текст. Слово free в назві моделі означає «безкоштовна».

4. Безкоштовний ШІ: ліміти й шлюз школи

Безкоштовне — не означає безмежне. Сервіс OpenRouter, через який ми користуємося безкоштовними моделями, дає без поповнення рахунку лише 50 запитів на добу на один ключ (а з разовим поповненням — до тисячі). Тому ми не ставимо по ключу кожному: ключ один, і він у викладача, бо ключ від такого сервісу оформлює доросла людина.

Щоб ключ не дістався учням, на сервері стоїть шлюз школи — «швейцар», який приймає запити від твого бота й сам додає справжній ключ. Адреса шлюзу — http://127.0.0.1/llm/v1 (127.0.0.1 означає «цей самий комп’ютер»), а ззовні, з інтернету, туди не достукатись. Ключ знає лише швейцар, тому ти його не бачиш і не можеш загубити. Щоб запитів вистачало всім, шлюз пропускає не більше десяти на хвилину на весь клас.

Якщо ШІ відповідає «ще не підключено»Поки викладач не вставив у шлюз ключ, шлюз відповідає кодом 503. Це не твоя помилка. Усі кроки уроку все одно можна пройти: автоперевірка має власну «тестову модель» і не витрачає спільний ліміт.

5. Коли ШІ мовчить, бот не має падати

Інтернет, ліміти, чужі сервери — усе може зламатися. Хороший бот переживає це спокійно: пробує запит і, якщо щось пішло не так, прямо каже про це людині. У Python для цього є try / except — «спробуй» / «якщо не вийшло». Ось головна думка з файлу llm.py, який ти скопіюєш (це схема, а не код для вставки):

try:
    відповідь = питаємо_ШІ(питання)
except:
    відповідь = "Не вдалося дістатися до ШІ. Спробуй пізніше."

Без цього одна несправність чужого сервера зупинила б обробку повідомлення — і людина отримала б мовчання або «Ой, щось пішло не так». А з цим — зрозуміле пояснення. Автоперевірка L06.4 навмисно «ламає» адресу ШІ й дивиться, що твій бот не впав.

Навіщо в боті try / except навколо запиту до ШІ?

6. ШІ-агент: чим OpenCode відрізняється від чату

У чаті ти питаєш — ШІ відповідає текстом, а копіювати його в код ти мусиш сам. Агент — це ШІ, якому дали «руки»: він сам читає файли твого проєкту, пропонує зміни й за твоєю згодою запускає команди. OpenCode — безкоштовна відкрита програма-агент для терміналу. Вона не має власного «мозку»: ти підключаєш до неї будь-який ШІ — наприклад, безкоштовні моделі OpenRouter, ті самі, що й твій бот, або моделі, що працюють на твоєму комп’ютері.

Не плутайЄ ще платна послуга OpenCode Zen, яку треба оплачувати, — нам вона не потрібна. Безкоштовний шлях — підключити безкоштовні моделі OpenRouter або локальні моделі.

Чим агент (як OpenCode) відрізняється від звичайного чату з ШІ?

Слова цього уроку

СловоЩо означає
API, запит, відповідьСпосіб, яким одна програма просить іншу щось зробити, і те, що приходить у відповідь
метод (GET, POST)Слово на початку запиту: «дай» (GET) або «ось дані, прийми їх» (POST)
заголовокСлужбова записка до запиту: формат даних, ключ тощо
тіло запитуСамі дані, які надсилаємо, зазвичай у форматі JSON
код відповідіЧисло, що коротко каже, як усе минуло: 200, 401, 429, 503
ключ (API-ключ)Пароль програми: за ним сервіс розуміє, хто питає
модельКонкретний ШІ, до якого звертаємося
ліміт запитівСкільки звернень сервіс дозволяє за добу чи за хвилину
шлюз«Швейцар»: приймає запити й сам додає справжній ключ, не показуючи його
try / exceptСпробуй виконати; якщо не вийшло — зроби це замість падіння
ШІ-агент, OpenCodeШІ, який сам читає файли й пропонує зміни; програма для терміналу з відкритим кодом

Практика

Виконуй кроки по черзі. Якщо ШІ школи ще не підключено (код 503), усе одно йди далі: у кроках 2 і 6 побачиш пояснення замість відповіді ШІ, і це нормально.

1 Зайти на сервер і перевірити, що бот живий

ssh <логін>@91.219.61.4
cd ~/app
systemctl --user is-active club-bot
tail -n 5 ~/logs/club-bot.log

Має бути active і кілька рядків журналу. Якщо ні — спершу полагодь бота за уроками 02–05.

2 Послати запит до ШІ вручну — як це робитиме бот

Програма curl дозволяє надіслати такий самий запит з терміналу. Спробуй (це одна довга команда):

curl -s http://127.0.0.1/llm/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"openrouter/free","messages":[{"role":"user","content":"Скажи одне слово: привіт"}]}'

Знайди у своїй команді метод (у curl з -d це POST), адресу, заголовок і тіло — усе, що розбирали в теорії. У відповідь прийде або JSON із полем choices (ШІ відповів — знайди в ньому свій «привіт»), або {"error":{"message":"шлюз ШІ школи ще не налаштовано…"}} — шлюз чекає на ключ викладача. Обидва варіанти правильні.

3 Скопіювати файл llm.py і прочитати його

cp /opt/club/kurs/06/llm.py ~/app/
nano ~/app/llm.py

Знайди у файлі: де читаються LLM_BASE_URL, LLM_API_KEY, LLM_MODEL (з .env), де формується тіло запиту (json.dumps), де стоїть заголовок Authorization, і де обробляються коди 429, 401, 503. Це та сама теорія, тільки мовою Python. Вийти з редактора: Ctrl+X.

4 Записати адресу, ключ і модель у .env

nano ~/app/.env

Допиши внизу три рядки (токен бота вище не чіпай):

LLM_BASE_URL=http://127.0.0.1/llm/v1
LLM_API_KEY=school
LLM_MODEL=openrouter/free

Збережи: Ctrl+O, Enter, Ctrl+X. Перевір, що файл і далі закритий для інших і що там три нових рядки:

chmod 600 ~/app/.env
grep -c "^LLM_" ~/app/.env

Друга команда має показати 3. Ключ school — просто слово: справжній ключ додає шлюз.

5 Навчити бота команди /ask

Відкрий ~/app/bot.py. У функції handle_update знайди рядок if text.startswith("/"): — він відповідає «Не знаю такої команди». Блок для /ask вставляй перед ним, одразу після блоку /help. Порядок має бути такий: /start, /help, /ask, потім «будь-яка інша команда», і наприкінці return f"Ти написав: …". Якщо поставити /ask нижче, повідомлення «/ask …» перехопить рядок «Не знаю такої команди» і до твого блоку не дійде.

    if text.startswith("/ask"):
        question = text[len("/ask"):].strip()
        if not question:
            return "Напиши питання після команди, наприклад: /ask що таке API?"
        return answer(question)

А під функцією handle_update, на початку рядка (без відступу), додай нову функцію:

def answer(question):
    import llm
    return llm.ask(question)

text[len("/ask"):] відрізає слово /ask і залишає лише питання; .strip() прибирає зайві пробіли. У тексті підказки /help додай рядок /ask питання — відповім за допомогою ШІ. Якщо заплутався, порівняй зі зразком: diff /opt/club/kurs/06/bot.py ~/app/bot.py (покаже лише відмінності; твої власні тексти там теж відрізнятимуться — це нормально).

6 Перевірити /ask без Telegram

cd ~/app
python3 try_bot.py "/ask що таке API?"
python3 try_bot.py "/ask"

Перша команда — або відповідь ШІ, або «ШІ школи ще не підключено. Скажи викладачу.». Друга — підказка «Напиши питання після команди…». Кілька рядків з Traceback — це помилка: читай останній рядок. No module named 'llm' — ти не в ~/app або не скопіював llm.py (крок 3).

7 Навмисно зламати адресу й побачити, що бот не падає

LLM_BASE_URL=http://127.0.0.1:9/v1 python3 try_bot.py "/ask привіт"

Ми на одну команду підмінили адресу на неіснуючу (порт 9 — там нічого не слухає). Бот має відповісти Не вдалося дістатися до ШІ. Спробуй пізніше. — і не впасти. Саме це перевіряє критерій L06.4. Файл .env від цього не змінився.

8 Перезапустити службу й спитати живого бота

/opt/club/venv/bin/python3 -c "import bot" && echo готово
systemctl --user restart club-bot
sleep 3
systemctl --user is-active club-bot
tail -n 8 ~/logs/club-bot.log

Спочатку готово, потім active. У Telegram напиши своєму боту /ask що таке порт?. Якщо відповів — зроби скріншот для критерію L06.5, який перевіряє викладач. Без Telegram — покажи викладачу вивід try_bot.py з кроку 6.

9 Побачити OpenCode у справі (показує викладач; вдома — за бажанням)

Викладач на екрані підключає OpenCode до безкоштовної моделі й просить його пояснити файл bot.py. Слідкуй за тим, що він робить сам: читає файл, пропонує зміну, питає дозвіл. А якщо захочеш спробувати вдома:

npm install -g opencode-ai
mkdir opencode-demo
cd opencode-demo
opencode

Для npm потрібна програма Node.js (nodejs.org). В інтерфейсі набери /connect, вибери OpenRouter (потрібен безкоштовний ключ із сайту openrouter.ai; за правилами сервісу ключ оформлює людина від 18 років, тож роби це разом із дорослим або підключи локальну модель — її викладач покаже окремо), потім /models і вибери модель із позначкою :free. Попроси: «Створи файл hello.py, який друкує привіт, і поясни кожен рядок». Правило: запускай агента лише у порожній навчальній теці, як opencode-demo вище, — ніколи там, де лежать паролі й токени.

Зберегти роботу на GitHub

10 Переконатися, що ключ не їде в Git, і зробити коміт

cd ~/app
git status
git check-ignore -v .env

У git status має бути llm.py (новий) і bot.py (змінений), а .env — відсутній. Тоді:

git add -A
git commit -m "Заняття 06: команда /ask питає ШІ"
git push

Онови сторінку репозиторію на GitHub: llm.py там є, а .env — ні.

Що перевіряє автоматика

Автоперевірка заходить на сервер від твого імені й перевіряє чотири автоматичні пункти (п’ятий — людський). L06.1 і L06.2 — що у ~/app/.env є рядки LLM_BASE_URL= і LLM_MODEL=. L06.3 — що /ask справді йде в ШІ: перевірка підставляє власну «тестову модель» за адресою з .env, і спільний ліміт не витрачається. L06.4 — що бот не падає, коли адреса ШІ неправильна. L06.5 — живу відповідь у Telegram — оцінює викладач. Натисни «Перевірити практичну частину» внизу сторінки.

КодЩо перевіряєтьсяЯкщо не зараховано
L06.1у .env задано, куди ходити по ШІ (LLM_BASE_URL)
автоперевірка
У ~/app/.env немає LLM_BASE_URL. Учням до 18 років — шлюз школи: LLM_BASE_URL=http://127.0.0.1/llm/v1
L06.2у .env задано модель (LLM_MODEL)
автоперевірка
У ~/app/.env немає LLM_MODEL. Безкоштовний варіант: LLM_MODEL=openrouter/free
L06.3/ask іде в ШІ й повертає його відповідь
автоперевірка
Перевірка підставила свій «ШІ» за адресою з LLM_BASE_URL і не дочекалась правильного запиту або його відповіді в боті. Потрібен POST {LLM_BASE_URL}/chat/completions з JSON {model, messages}, а текст відповіді — з choices[0].message.content.
L06.4коли ШІ недоступний, бот не падає
автоперевірка
Коли ШІ не відповідає, бот має сказати це людині, а не впасти. Обгорни запит у try/except і поверни зрозумілий текст.
L06.5живий бот у Telegram відповідає на /ask
викладач
Позначку ставить викладач на занятті або після нього.

Твої відповіді на питання уроку

Кожне питання оцінюється відразу в браузері — це лишається так само. Ця кнопка лише надсилає підсумок учителю, щоб відповіді не зникли разом із закритою вкладкою.

Перевірка виконаних завдань

Список критеріїв і що саме вони означають — у таблиці вище, «Що перевіряє автоматика». Тут — та сама перевірка, але прямо зараз, без очікування на найближчий прохід за розкладом.

Домашнє завдання

  1. П’ять питань боту. Постав своєму боту п’ять питань через /ask і запиши відповіді. Познач, у яких ШІ помилився чи вигадав. Підготуй відповідь: чому не можна сліпо вірити ШІ?
  2. Характер бота. У llm.py знайди текст SYSTEM — це інструкція, яку ШІ отримує перед кожним питанням. Зміни її (наприклад, щоб бот відповідав як веселий астроном) і перевір через try_bot.py.
  3. Схема шляху. Намалюй на папері, як питання з Telegram доходить до ШІ й повертається: Telegram → твій бот (служба club-bot) → шлюз → ШІ. Підпиши, де API, де ключ, де ліміт, і постав помітку там, де запит іде «за потреби».
  4. OpenCode вдома (за бажанням): встанови за кроком 9 і попроси його пояснити простий файл у порожній теці. Запиши одне речення: що він зробив сам, а що спитав у тебе.
  5. Ще один коміт. Збережи зміни: git add -A, git commit -m "…", git push.

Якщо щось не працює

Бот на /ask відповідає «ШІ школи ще не підключено»

Це код 503: шлюз чекає ключа викладача. Твій код у порядку. Покажи викладачу вивід try_bot.py — критерії L06.1–L06.4 зарахуються й без справжньої відповіді ШІ.

«ШІ не пускає: перевір LLM_API_KEY у файлі .env»

Код 401 або 403. Відкрий ~/app/.env і перевір, що рядок такий: LLM_API_KEY=school — без пробілів навколо знака = і без лапок. Після правки .env перезапусти службу: systemctl --user restart club-bot.

«ШІ зараз перевантажений або вичерпано ліміт»

Код 429: безкоштовний ліміт на сьогодні (або на цю хвилину) вичерпано всім класом. Почекай хвилину й повтори. Це нормальна поведінка безкоштовного сервісу, а не помилка твого коду.

try_bot.py пише No module named 'llm' або Traceback

Виконай cd ~/app && ls llm.py. Немає файлу — скопіюй його знову (крок 3). Файл є — прочитай останній рядок Traceback: SyntaxError чи IndentationError означають помилку у bot.py: перевір відступи нового блоку /ask (чотири пропуски від краю, як в іншого if поруч) і порівняй зі зразком: diff /opt/club/kurs/06/bot.py ~/app/bot.py.

/ask відповідає «Не знаю такої команди»

Блок /ask стоїть нижче за рядок if text.startswith("/"):. Перенеси його вище: порядок має бути /start, /help, /ask, потім «будь-яка інша команда». Перевір: python3 try_bot.py "/ask привіт".

L06.3 не зараховано

Перевірка підставила власну «тестову модель» за адресою з LLM_BASE_URL і дивиться три речі. Бот має звернутися за цією адресою (/ask дійшов до llm.py); запит має бути POST …/chat/completions з JSON, де є model і messages; відповідь ШІ має дійти до людини — answer() повертає те, що повернув llm.ask(), нічого не гублячи.

L06.4 не зараховано

Перевірка підміняє адресу ШІ на неіснуючу й чекає, що бот спробує звернутися і чемно відповість замість мовчання чи помилки. Якщо не зараховано, спершу переконайся, що /ask узагалі доходить до llm.py (див. картку вище), а потім що запит в llm.py обгорнуто в try / except (він уже є у файлі, який ти скопіював).

L06.1 або L06.2 не зараховано

Перевірка шукає рядки саме з такими іменами: LLM_BASE_URL= і LLM_MODEL= на початку рядка, без пробілу перед ними й без «#». Подивися: grep -n "^LLM_" ~/app/.env — має бути три рядки.

OpenCode: команда не знайдена або не відповідає

opencode: command not found — програма не встановилася або термінал треба закрити й відкрити знову. npm: command not found — спершу встанови Node.js. Агент мовчить або пише про ліміт — безкоштовні моделі мають обмеження; спробуй іншу модель із :free через /models або повтори пізніше.