Як написати власного AI-агента на Python (Зустрічайте UnoClaw)

💡 Усі статті, обговорення, новини про AI — в одному місці. Приєднуйтесь до AI спільноти!

Привіт, я CoderOk.

Python Developer — а у вільний час створюю проєкти, які виходять значно за межі мого основного напрямку роботи (IoT, GameDev, BoardGames та Music).

Цього разу я вирішив хайпанути на OpenClaw :) Тому сідайте якомога комфортніше і беріть попкорн.

Ну, почнемо!

Якщо ви коли-небудь пробували розібратися з великими AI-фреймворками на Python, то знаєте це відчуття: коли вам потрібно просто попросити AI прочитати файл (або якісь вхідні дані) і отримати відповідь, а натомість доводиться встановлювати вагон додаткових бібліотек та залежностей, продиратися через 10 шарів абстракцій, промпт-темплейтів та всіляких графів. Іноді вам потрібен просто молоток, а не цілий металургійний завод.

Натхненний філософією мінімалізму проєктів-клонів OpenClaw, я вирішив перевірити: а наскільки компактним може бути повноцінний автономний AI-агент, який можна використовувати для своїх проєктів та досліджень?

Відповідь була проста: достатньо декількох сотень рядків коду. В одному файлі. Без векторних баз даних і мега-бібліотек.

Так народився UnoClaw. У цій статті я покажу вам його зсередини, щоб ви зрозуміли, як насправді працюють AI-агенти і чому зараз вам (можливо 😉) не потрібні «важкі» фреймворки для вашого наступного експерименту.

UnoClaw Logo

Що таке AI-агент простими словами?

Для розробника LLM (велика мовна модель, як GPT-4 чи Llama 3) — це просто функція f(text) -> text. Вона приймає текст і повертає текст (там все трохи складніше, але наразі цього достатньо). У LLM не має рук, очей і доступу до інтернету.

Ваш код перетворюється на Агента тоді, коли ви даєте цій моделі інструменти (Tools). Сучасні LLM вміють не просто генерувати відповідь, а повертати спеціальний текст (зазвичай у JSON-форматі), який каже: «Гей, я не знаю погоду в Києві, але я бачу, що в тебе є функція get_weather(city). Виклич її для мене з параметром Kyiv і дай результат». Потім ваш код викликає потрібну функцію і повертається назад до LLM вже з результатом її виконання.

На даний момент архітектура UnoClaw виглядає так:

UnoClaw Diagram

З чого складається цей маленький Франкенштейн?

Перш ніж дивитися детально на весь цикл роботи, давайте швидко поглянемо на основні компоненти цієї системи:

1. Конфігурація (Pydantic)

Щоб не парсити config.json руками і не ловити KeyError, ми використовуємо Pydantic. Це гарантує: якщо ви, наприклад, забули вказати base_url для LLM, програма «красиво впаде» ще на старті з чітким описом помилки. Цю частину взагалі можна було б і прибрати з UnoClaw (і він працював би так само, зекономивши ~80 рядків коду). Але під час розробки це економить багато часу і нервів у пошуках причини, чому ваш код падає. А в комерційних проєктах без цього взагалі ніяк.

2. Пам’ять (Старий добрий SQLite)

Зараз модно тягнути векторні бази даних куди треба і не треба. Можна було б додати сюди складний семантичний пошук але для персонального асистента звичайний sqlite3, який вбудований у Python, чудово справляється з історією повідомлень та фоновими задачами. Ну і не потрібно додатково використовувати Embedding Models (моделі, які трансформують у вектори такі дані як текст, аудіо і таке інше).

3. «Клішня» Агента (Tools)

Як навчити LLM виконувати команди в терміналі, працювати з файлами чи «читати» сайти? Ми пишемо звичайні Python функції і мапимо їх у словник available_tools. Потім ми додаємо до інструкцій агента інформацію про доступні функції (детальніше про це у п. 4) і за допомогою OpenAI SDK даємо змогу агенту використовувати їх.

Наприклад:

def execute_command(command: str) -> str:
    """Виконує shell команду. Досить небезпечно, але весело ))."""
    ...
    result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
    ...

4. «Мозок» та Особистість (AGENT.md та SKILLS.md)

Як агент дізнається, хто він такий і що вміє робити? Хардкодити системні промпти прямо в коді — це погана ідея. Тому зазвичай агенти використовують зовнішні інструменти для цього. Так і UnoClaw динамічно збирає свій системний промпт при старті, склеюючи два markdown-файли з папки docs/:

  • AGENT.md (Характер та Правила): Це «душа» вашого агента. Тут ви прописуєте його особистість (наприклад, «Ти саркастичний Senior DevOps»), правила форматування та дуже важливий контекст оточення («Ти працюєш на Windows 11 через PowerShell» — щоб модель використовувала команду dir, а не ls).
  • SKILLS.md (Інструкція до інструментів): А це його «резюме». Тут лежить текстовий опис ваших функцій, щоб LLM розуміла, коли їх викликати. Але найголовніша фіча цього файлу — Composite Skills (Складені навички). Оскільки сучасні моделі дуже розумні, вам не потрібно писати код для кожної нової фічі! Ви можете просто написати алгоритм звичайною мовою (поки краще це робити англійською). Наприклад: «Якщо користувач питає про ціну BTC -> виклич інструмент read_web за URL <таким-то> -> знайди в JSON поле PRICE -> видай користувачу результат». І модель сама виконає цей багатокроковий воркфлоу, без жодного нового рядка коду з вашого боку!

5. Фоновий планувальник

Класичні планувальники зазвичай просто смикають bash-скрипти. Планувальник UnoClaw працює інакше. Він крутиться як фонова асинхронна задача і кожні X секунд (scheduler_interval — параметр у конфігураційному файлі) заглядає в таблицю tasks у SQLite. Коли настає час, він не виконує жорстко заданий код, а генерує промпт і симулює повідомлення від користувачаа: [SYSTEM: AUTOMATED BACKGROUND TASK TRIGGERED]: Перевір ціну біткоїна і збережи у файл btc_price.txt. Агент прокидається і вже сам розбирається, які інструменти треба викликати, щоб виконати завдання.

6. Серце системи: The Agent Loop (Цикл Агента)

Це саме те місце, де звичайна LLM перетворюється на автономного помічника. Ми використовуємо стандартний клієнт OpenAI SDK, але загортаємо виклики у цикл з N ітерацій (agent_loop_max_iterations — параметр у конфігураційному файлі, запобіжник від нескінченного зациклення, якщо ШІ почне галюцинувати). Алгоритм доволі простий але достатній для багатьох завдань.

  1. Відправляємо весь контекст у LLM модель.
  2. Отримуємо відповідь.
  3. Якщо модель каже «Ось текст для користувача» — ми виходимо з циклу і віддаємо повідомлення.
  4. Якщо ж вона повертає нам tool_calls (наприклад, просить викликати read_web) — ми виконуємо цю функцію локально, дописуємо результат в історію чату з поміткою role: "tool" і відправляємо весь цей оновлений «пакунок» назад у модель. І так, поки задача не буде вирішена або не перевищимо максимальну кількість ітерацій.

7. Менеджмент контексту

Щоб LLM не «луснула» від перевищення ліміту токенів (Context Window Exceeded), ми вводимо «м’який бюджет токенів». Функція _est_tokens робить грубу, але швидку оцінку (len(text) / 4), викидаючи найстаріші повідомлення чату і зберігаючи системні промпти недоторканими. Таким чином ви можете ставити обмеження щодо розміру контексту на запити до LLM.

8. Два способи комунікації — CLI та Telegram

Як саме спілкуватися з цим агентом? UnoClaw має два вбудовані інтерфейси. Якщо ви додали telegram.token у свій config.json, скрипт запускає асинхронний long-polling і перетворюється на вашого кишенькового Telegram-бота. При цьому вбудований фільтр гарантує, що жоден випадковий «кулхацкер» з інтернету не зможе написати боту і виконати rm -rf / (або format C:) на вашому комп’ютері. Якщо ж токена немає, UnoClaw елегантно переходить у звичайний інтерактивний CLI-режим прямо у вашому терміналі (Wake up, Neo 😊).

Життєвий цикл UnoClaw

Давайте заглянемо під капот і прослідкуємо шлях від моменту, коли ви запускаєте unoclaw у терміналі, до моменту, коли бот надсилає вам згенероване повідомлення у термінал або Telegram.

Етап 1. Bootstrapping (Прокидаємося і читаємо інструкції)

  1. Підняття БД: Підключаємося до SQLite і створюємо дві таблиці: memory (історія) та tasks (відкладені завдання).
  2. Формування «Конституції»: скрипт читає файли AGENT.md і SKILLS.md. Він склеює їх у єдиний текст. Це стає базовим системним повідомленням ([SYSTEM] prompt), яке буде відправлятися в LLM при кожному запиті.
  3. Ініціалізація клієнта: Створюється AsyncOpenAI клієнт, налаштований на ваш локальний (Ollama, LM Studio, ...) або хмарний (OpenRouter, ...) ендпоінт.

Етап 2. Запуск фонового планувальника

Через asyncio.create_task() запускається бекграунд-цикл планувальника.

  • Він кожні X секунд перевіряє таблицю tasks.
  • Якщо час виконання завдання настав, він симулює повідомлення від користувача.
  • Замість тексту від користувача, він кидає в агентський цикл промпт. Наприклад: [SYSTEM: AUTOMATED BACKGROUND TASK TRIGGERED]: Перевір ціну біткоїна.
  • Агент прокидається та автономно виконує задачу і надсилає результат, після чого завдання видаляється (якщо воно не повинно повторюватися через заданий інтервал).

Етап 3. Інтерфейси та Контекст

Програма блокується під час отримання повідомлення у Telegram або на CLI-вводі (наприклад користувач написав: «Яка зараз погода в Києві?»):

  1. UnoClaw шукає старі діалоги в SQLite за ключовими словами і додає їх як невидиме системне повідомлення.
  2. Застосовується наш ліміт токенів (_trim_context), щоб не платити зайві гроші за переповнений контекст (якщо використовуєте не локальну LLM).

Етап 4. Серце системи: The Agent Loop

Тут відбувається виклик інструментів (Tool Calling). Цикл обмежений N ітераціями (параметр agent_loop_max_iterations у файлі конфігурації).

  1. Відправляємо підготовлену історію повідомлень в LLM.
  2. Шлях А (Відповідь): Модель повертає текст. Ми перериваємо цикл і віддаємо текст користувачу.
  3. Шлях Б (Виклик інструменту): Модель повертає tool_calls. UnoClaw розпаковує JSON-аргументи від LLM і виконує локальну функцію (через await asyncio.to_thread(), щоб не блокувати Event Loop).
  4. Результат команди пакується в нове повідомлення з role: "tool", додається в історію, і все повторюється з кроку 1.
  5. Шлях В (Забагато ітерацій): Якщо цикл перевищує максимальну кількість ітерацій, ми просимо LLM зробити підсумок з отриманої інформації і повертаємо її користувачу.

Етап 5. Запис в аннали історії

Щойно цикл завершився, спрацьовує save_memory(). Ваш запит і відповідь агента записуються в SQLite. На цьому етапі ви можете сміливо вбивати процес (Ctrl+C), перезапускати скрипт — і агент все пам’ятатиме.

Підсумок

Так і виглядає насправді простий агент. Ніякої магії...

Хоча коли користуєшся інструментами та сервісами, побудованими за допомогою агентів, іноді все ж таки приходить відчуття, що по той бік є якась жива людина, яка намагається виконати твої забаганки 😊

UnoClaw — це навчальний полігон, а не Skynet

Тому цей проєкт — максимально базова реалізація агента. Його головна мета — зірвати завісу таємничості й дати вам базове розуміння того, як агент працює під капотом.

Коли ви підете далі, ви зіштовхнетеся з «дорослими» концепціями:

  • Advanced RAG (Retrieval-Augmented Generation): Замість нашого простого SQL-пошуку, «дорослі» агенти використовують семантичний пошук, векторні бази даних (Milvus, Qdrant) та реранкери для роботи з терабайтами документів.
  • Розумна пам’ять (напр. mem0): Сучасні системи аналізують діалог у фоні, витягують факти про користувача і самостійно «забувають» та відкидають нерелевантне сміття.
  • MCP (Model Context Protocol): Стандартизація доступу до даних. Замість локальних функцій агенти підключаються до сервісів чи баз даних через єдиний протокол.
  • Multi-Agent (LangGraph, AutoGen): Розбиття задач на кількох агентів (один пише код, інший тестує, ...), якими керують складні стейт-машини.

Але щоб водити болід Формули-1, треба спочатку зрозуміти, як працює зчеплення на звичайній «механіці». UnoClaw — це базовий рівень. Це як «велосипед», який ви можете розібрати до гвинтика за одні вихідні. А далі вас чекає болід Формули-1, який ви з годом зомжете опанувати при бажанні 😉.

Сподіваюсь, що вам було цікаво і корисно. Я намагався викласти цей матеріал у доволі простій формі.

Звичайно, будуть ті, хто скаже «навіщо потрібен 100-й клон openclaw». Але сподіваюсь, що будуть і ті, хто зробить fork або PR до UnoClaw 😉.

Або поділіться своїм agentic-проєктом! Гадаю, що це буде цікаво.

Будуть питання або зауваження — будь ласка, залишайте свої коментарі.

Дякую всім за увагу!

UnoClaw доступний на GitHub, uv та PyPI: github.com/2coderok/unoclaw

Буду вдячний за зірочку ⭐ на GitHub! 😉

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Написано просто і зрозуміло. Дякую!

Підписатись на коментарі