Każdy projekt AI, od prostego skryptu po zaawansowany model językowy, opiera się na danych. Sposób ich przechowywania, zarządzania i odpytywania może zadecydować o sukcesie lub porażce. W świecie zdominowanym przez potężne systemy jak PostgreSQL czy MongoDB, często zapominamy o prostszym, ale niezwykle skutecznym rozwiązaniu: SQLite. W połączeniu z nowoczesnym ORM-em, jakim jest Prisma, tworzy duet, który jest wręcz stworzony do prototypowania i mniejszych wdrożeń AI.

Dlaczego SQLite? Zalety w kontekście AI

SQLite to nie jest zabawka. To w pełni transakcyjna baza danych SQL, która nie wymaga osobnego serwera. Cała baza to pojedynczy plik na dysku. Ta prostota jest jej największą siłą, zwłaszcza w projektach AI.

  • Zero konfiguracji: Zapomnij o zarządzaniu użytkownikami, portami i usługami działającymi w tle. Inicjalizacja bazy to kwestia jednej linijki w kodzie. To pozwala skupić się na logice aplikacji i modelu, a nie na infrastrukturze.
  • Niezwykła przenośność: Skoro baza to plik, możesz ją łatwo kopiować, przesyłać i współdzielić. Cały stack (kod + dane) staje się samowystarczalny. To idealne rozwiązanie dla lokalnych skryptów do trenowania modeli czy narzędzi analitycznych.
  • Wydajność dla odczytu: W zastosowaniach AI, zwłaszcza na etapie inferencji, operacje odczytu często dominują. SQLite jest w tym zaskakująco szybki, często przewyższając sieciowe bazy danych dzięki eliminacji narzutu komunikacyjnego.
  • Idealny do prototypowania: Chcesz szybko przetestować nowy pomysł? Zbudować MVP? SQLite pozwala na błyskawiczne iteracje bez konieczności konfigurowania środowiska deweloperskiego i produkcyjnego.

Gdzie Prisma wchodzi do gry?

Prisma to ORM (Object-Relational Mapper) nowej generacji, który idealnie uzupełnia SQLite. To nie jest kolejny ociężały framework. Prisma stawia na bezpieczeństwo typów i doświadczenie dewelopera.

  • Pełne bezpieczeństwo typów: Prisma generuje klienta TypeScript na podstawie Twojego schematu bazy danych. Każde zapytanie, każdy wynik jest w 100% otypowany. W złożonych strukturach danych, typowych dla AI, to absolutny game-changer, eliminujący całą klasę błędów.
  • Intuicyjne API: Zamiast pisać surowe SQL lub uczyć się skomplikowanego języka zapytań, używasz prostych obiektów JavaScript/TypeScript. Kod staje się czytelniejszy i łatwiejszy w utrzymaniu.
  • Inteligentne migracje: Zmiana schematu bazy danych to historycznie bolesny proces. prisma migrate automatyzuje tworzenie plików migracji SQL, śledzi zmiany i pozwala na bezproblemowe aktualizowanie struktury bazy.
  • Prisma Studio: To wbudowane, wizualne narzędzie do przeglądania i edycji danych. Niezastąpione podczas debugowania, inspekcji wyników działania modelu czy ręcznego dodawania danych testowych.

Krok po kroku: Konfiguracja projektu

Zobaczmy, jak proste jest rozpoczęcie pracy. Zakładamy, że masz już zainstalowany Node.js.

1. Inicjalizacja projektu i zależności

Stwórz nowy folder i zainicjuj projekt Node.js. Od razu dodajmy TypeScript.

npm init -y
npm install typescript ts-node @types/node --save-dev
npm install prisma --save-dev
npm install @prisma/client

2. Inicjalizacja Prismy

Teraz powiedzmy Prismie, że chcemy używać SQLite.

npx prisma init --datasource-provider sqlite

To polecenie tworzy folder prisma z plikiem schema.prisma oraz plik .env do przechowywania zmiennych środowiskowych, jak connection string do bazy.

3. Definicja schematu danych

Otwórz plik prisma/schema.prisma. Zdefiniujmy prosty model do przechowywania promptów i odpowiedzi wygenerowanych przez model AI.

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Prompt {
  id        Int        @id @default(autoincrement())
  createdAt DateTime   @default(now())
  text      String     @unique
  responses Response[]
}

model Response {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  text        String
  modelUsed   String   // Jaki model został użyty, np. "gpt-4-turbo"
  metadata    Json?    // Idealne do przechowywania token_usage, logprobs etc.

  prompt   Prompt @relation(fields: [promptId], references: [id])
  promptId Int
}

Zwróć uwagę na typ Json?. Jest idealny do przechowywania nieustrukturyzowanych metadanych zwracanych przez API modeli AI.

4. Konfiguracja połączenia

Otwórz plik .env i upewnij się, że zawiera poprawną ścieżkę do pliku bazy danych.

DATABASE_URL="file:./dev.db"

5. Uruchomienie pierwszej migracji

To polecenie stworzy plik bazy danych dev.db oraz wygeneruje kod SQL potrzebny do stworzenia tabel na podstawie Twojego schematu.

npx prisma migrate dev --name init

Prisma wygeneruje również klienta TypeScript. Jesteś gotowy do pisania zapytań!

Interakcja z bazą danych

Stwórz plik index.ts i zobacz, jak prosto można operować na danych.

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
  // Stwórz nowy prompt i powiązaną z nim odpowiedź
  const newPrompt = await prisma.prompt.create({
    data: {
      text: 'Napisz krótki wiersz o kodowaniu w TypeScript.',
      responses: {
        create: {
          text: 'Linijki kodu, typów straż, błędy znikają w mig, ach!',
          modelUsed: 'local-poet-v1',
          metadata: { tokens: 15, temperature: 0.7 },
        },
      },
    },
  });

  console.log('Stworzono nowy prompt:', newPrompt);

  // Znajdź wszystkie odpowiedzi dla danego promptu
  const promptWithResponses = await prisma.prompt.findUnique({
    where: {
      text: 'Napisz krótki wiersz o kodowaniu w TypeScript.',
    },
    include: {
      responses: true, // Dołącz powiązane odpowiedzi
    },
  });

  console.log('Znaleziono prompt z odpowiedziami:', promptWithResponses);
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Uruchom skrypt za pomocą npx ts-node index.ts. Zwróć uwagę na autouzupełnianie i sprawdzanie typów, które dostajesz od edytora kodu – to właśnie magia Prismy.

Pułapki i najlepsze praktyki

To połączenie nie jest idealne na każdą okazję. Oto, na co musisz uważać.

Współbieżność zapisu

Największym ograniczeniem SQLite jest obsługa współbieżnych operacji zapisu. Baza danych jest blokowana podczas zapisu, co oznacza, że w danym momencie tylko jeden proces może modyfikować dane. Dla aplikacji webowej z dużym ruchem to problem. Ale dla skryptów AI, narzędzi CLI czy lokalnych aplikacji, gdzie operacje zapisu są sekwencyjne lub rzadkie, nie stanowi to żadnej przeszkody.

Skalowanie

SQLite skaluje się pionowo (mocniejsza maszyna), ale nie poziomo (rozproszenie na wiele serwerów). Jeśli Twój projekt urośnie do rozmiarów wymagających rozproszonej architektury, będziesz musiał migrować. Dobra wiadomość jest taka, że Prisma to ułatwia – wystarczy zmienić provider w schema.prisma na postgresql lub mysql i dostosować connection string. Schemat i kod zapytań pozostają w większości takie same.

Backup i wersjonowanie

Nigdy nie commituj pliku .db do Gita! To plik binarny, który często się zmienia, co rozdęłoby Twoje repozytorium. Zamiast tego:

  • Do struktury bazy używaj migracji (prisma migrate dev). Pliki migracji (zwykły SQL) commituj do repozytorium.
  • Do backupu danych po prostu kopiuj plik .db w bezpieczne miejsce lub użyj wbudowanego w SQLite polecenia .backup.

Ograniczenia typów danych

SQLite ma bardziej dynamiczny i uproszczony system typów niż np. PostgreSQL. Prisma świetnie to mapuje, ale warto mieć świadomość, że np. typ DateTime jest przechowywany jako tekst w formacie ISO 8601. Dla większości zastosowań to bez znaczenia, ale warto o tym pamiętać przy pisaniu surowych zapytań SQL.

Kiedy Prisma + SQLite to strzał w dziesiątkę?

Podsumowując, ten duet sprawdza się rewelacyjnie w konkretnych scenariuszach:

  • Szybkie prototypy i projekty MVP: Testowanie pomysłów bez narzutu infrastruktury.
  • Narzędzia CLI i skrypty: Przetwarzanie danych, ewaluacja modeli, generowanie raportów.
  • Aplikacje desktopowe (np. w Electronie): Baza danych dostarczana razem z aplikacją.
  • Projekty badawczo-rozwojowe: Gdy liczy się szybkość eksperymentowania, a nie skalowalność.
  • Małe i średnie projekty z jednym serwerem: Prosty blog, system CMS, wewnętrzne narzędzie firmowe.
  • Przechowywanie embeddingów: Dla małej skali możesz przechowywać wektory (jako Bytes lub Json) i wykonywać proste wyszukiwanie podobieństwa w kodzie aplikacji, zanim zdecydujesz się na dedykowaną bazę wektorową.