Projekty
3 w trakcieNie lista umiejętności — rzeczy, przy których faktycznie pracowałem, w większości wciąż z dużym polem do nauki.
aioperational
LifeOS — samodzielnie hostowany asystent AI
Osobisty asystent AI, który buduję dla siebie: backend w FastAPI za botem na Telegramie i małą aplikacją webową w React, przełączający się między lokalnym modelem Ollama a OpenRouter w zależności od zadania.
- ›Obsługuje cele, kalendarz i budżet przez rozmowę, z wejściem/wyjściem głosowym i integracją z Google Calendar.
- ›Samodzielnie hostowany na klastrze k3s w Oracle Cloud, przygotowanym w Terraformie, z CI/CD wdrażającym przy każdym pushu do main — zaczęło się jako skrzynka WSL2 w domu, teraz działa jako cold-standby na wypadek rollbacku, zamiast być wyłączona.
- ›Własna domena (lifeos.michalzawadzki.dev) oparta na rekordzie DNS w Cloudflare zarządzanym przez Terraform — zaimportowanym z ręcznie utworzonego wpisu do IaC zamiast zostawiać go nieudokumentowanym, DNS-only/bez proxy, żeby wyzwanie HTTP-01 cert-managera i streaming SSE/głosowy aplikacji nie przechodziły przez dodatkową warstwę proxy.
- ›Wybiera model per żądanie, nie per rozmowa: lokalny model Ollama obsługuje transakcyjne wywołania narzędzi (wydatki, kalendarz, wpisy zdrowotne), a zbudowana od zera bramka anonimizacji usuwa imiona domowników, zanim cokolwiek naprawdę otwartego trafi do modelu w chmurze.
- ›90 testów pytest i CI w GitHub Actions pokrywają tabelę wywołań narzędzi, klientów finansów/kalendarza/zdrowia (mockowanych na realnych odpowiedziach API) i cały interfejs sesji — złapały prawdziwe błędy zanim trafiły na produkcję, w tym pomyłkę w jednostkach, która zawyżyła dzienny licznik kalorii siedmiokrotnie.
- ›Dane zdrowotne synchronizują się same: automatyzacja na iPhonie (Health Auto Export) wysyła payload JSON do endpointu zabezpieczonego tokenem bearer za każdym razem, gdy pojawią się nowe dane, sparsowane do dziennych metryk i treningów, żeby model mógł je wykorzystać w porannym/wieczornym podsumowaniu albo odpowiedzieć na pytanie "jak minął tydzień" — bez ręcznego eksportu.
- ›Bezpieczeństwo jest świadomie zawężone, nie doklejone na końcu: Google OAuth za allow-listą e-maili z podpisanym ciasteczkiem sesji, porównanie tokena bearer w czasie stałym zabezpieczające jedyny webhook wystawiony publicznie przez tunel (endpoint synchronizacji zdrowia powyżej), bramka anonimizacji z własnym zestawem testów oraz UI administracyjne bazy (Adminer) związane wyłącznie z localhost, nigdy nie routowane przez tunel.
#ai#llm#self-hosted#automation
aioperational
System rezerwacji z AI dla małej firmy usługowej
Narzędzie do zarządzania klientami i kalendarzem, które zbudowałem dla kogoś bliskiego, kto prowadzi jednoosobową firmę usługową — wprowadzanie danych językiem naturalnym zamiast formularzy, na bazie samodzielnie hostowanego stacku.
- ›To, co działa dziś, nie zaczęło się na Vercel — pierwsza wersja to samodzielnie hostowany stack FastAPI + Postgres + Redis w Docker Compose, wystawiony przez tunel ngrok z komputera w domu, więc cały system rezerwacji padał, gdy tylko ta maszyna była wyłączona albo zabrakło prądu.
- ›Przepisałem to jako pojedynczą aplikację Next.js (API routes i frontend razem) hostowaną na Vercel z Supabase jako zarządzaną bazą danych, żeby dostępność przestała zależeć od domowego peceta — lokalny model czatu Ollama jest teraz jedynym elementem wciąż związanym z tą maszyną, wystawionym przez własny zabezpieczony tunel, ten sam wzorzec co w LifeOS.
- ›To przepisanie wymagało też przerobienia auth: zamiast ręcznie napisanego flow OAuth (surowe wywołania httpx plus ciasteczka podpisywane przez itsdangerous) wszedł Auth.js — ten sam model zaufania oparty na allow-liście e-maili i ta sama nazwa zmiennej środowiskowej dla ciągłości, ale dużo mniej kodu auth do utrzymania.
- ›Pipeline w GitHub Actions odpala zestaw testów backendu na jednorazowej bazie Postgres przy każdym pushu, bo bug tutaj wpływa na prawdziwy dochód kogoś innego.
- ›Dwukierunkowe SMS-y z przypomnieniami przez samodzielnie hostowaną bramkę działającą na karcie SIM w prawdziwym telefonie Android, a nie płatne API rozliczane za wiadomość.
- ›Asystent AI oparty na lokalnym modelu, żeby dane klientów nigdy nie wychodziły poza maszynę — trudniejsze niż oczekiwałem było uzyskanie tool-callingu wystarczająco pewnego, by ufać mu przy prawdziwych rezerwacjach.
- ›Poza rezerwacjami — dokumenty klientki z podpisem na canvasie zapisywanym jako wygenerowany PDF oraz widok finansów biznesowych (przychody, koszty, sumy okresowe) całkowicie osobny od prywatnego budżetu właścicielki.
- ›Przedpłacone karnety i vouchery rozliczane stopniowo przy kolejnych wizytach oraz log zdjęć przed/po dla każdej klientki — oba w prywatnych bucketach Supabase, nigdy publiczne.
- ›Ewidencja sprzedaży produktów — katalog ze stanami magazynowymi i wpis dla każdej sprzedaży zapisujący koszt z chwili sprzedaży, żeby marża zostawała dokładna nawet po późniejszej zmianie cen u dostawcy.
- ›Drugie środowisko Supabase + Vercel, zasiane realistycznymi, ale fikcyjnymi danymi po polsku, dzięki czemu ten opis mógł powstać z prawdziwymi zrzutami ekranu bez ujawniania danych żadnej prawdziwej klientki — jego migracje schematu stosują się automatycznie w CI przy każdym mergu, celowo osobno od ręcznego, recenzowanego wdrożenia na produkcji.
#ai#automation#self-hosted
devopsoperational
Własny pipeline publikacji tej strony
Merge posta w Markdown do main uruchamia deploy, a strona sama publikuje się na LinkedIn z linkiem zwrotnym — bez ręcznego kopiowania i wklejania.
- ›GitHub Actions ogarnia cały flow: build, deploy, czekanie na żywy URL, a potem publikację na LinkedIn przez oficjalne REST API.
- ›Sprawdzanie deployu wcześniej tylko odpytywało o status 200 — co przechodziłoby nawet dla nieodświeżonego cache'a. Teraz dodatkowo potwierdza, że tytuł posta faktycznie pojawia się w treści odpowiedzi, zanim publikacja ruszy dalej, z testami jednostkowymi pilnującymi tej logiki.
- ›Wszystko jest wersjonowane i przechodzi normalny pull request, tak jak każda inna zmiana na tej stronie — CI odpala typecheck, pełny zestaw testów jednostkowych i produkcyjny build przy każdym pushu.
- ›Cotygodniowe zadanie pobiera kilka kanałów RSS, tworzy dwujęzycznego draft posta modelem LLM według ustalonego promptu stylu/głosu i otwiera PR — nic nie publikuje się bez wcześniejszego przeczytania przez człowieka.
- ›Panel administracyjny, zablokowany logowaniem przez GitHub OAuth, daje ręczną ścieżkę obok automatycznej — pisanie i edytowanie postów wprost, a także moderację komentarzy — bez PR-a przy każdej drobnej poprawce.
- ›Sama strona jest teraz porządnie wykrywalna: dane strukturalne JSON-LD, dynamicznie generowane obrazki OG dla każdego posta, sitemap.xml/robots.txt oraz tagi canonical/hreflang w obu językach; publiczny kanał RSS i Vercel Web Analytics pokazują, kto faktycznie czyta.
- ›Udogodnienia dla czytelników poszły tą samą ścieżką co reszta pipeline'u — jasny/ciemny motyw (przekolorowany przez zmienne CSS, nie komponent po komponencie), pasek postępu czytania, powiązane wpisy dobierane po wspólnych tagach i pełny indeks tagów — wszystko wdrożone przez ten sam PR + CI, co każda inna zmiana na stronie.
- ›Sentry pilnuje teraz błędów po stronie klienta, serwera i edge runtime — samo wyłapywanie błędów, bez tracingu i session replay, bo żadne z nich nie zwraca kosztu w rozmiarze bundle'a przy ruchu, jaki ma ta strona. Bezpiecznie nie robi nic, jeśli DSN nie jest ustawiony, więc nigdy nie psuje builda.
#devops#automation#ci-cd#linkedin