Files
DBAdmin/inbox/todo/artykuł-jak-zbudować-projekt-generative-ai-llm-rag-w-sposób-produkcyjny.md
2026-05-18 06:40:19 +00:00

13 KiB
Raw Permalink Blame History

type
type
Note

Artykuł: „Jak zbudować projekt Generative AI (LLM + RAG) w sposób produkcyjny”

Pewnie — zrobię z tej struktury (1) artykuł (czytelny, edukacyjny) oraz (2) procedurę operacyjną (SOP/runbook), tak żeby dało się to wdrożyć i utrzymywać w zespole.


1) Artykuł: „Jak zbudować projekt Generative AI (LLM + RAG) w sposób produkcyjny”

Wstęp

Zbudowanie działającego prototypu opartego na modelu językowym (LLM) i architekturze RAG (Retrieval-Augmented Generation) w notatniku Jupyter zajmuje dziś zaledwie kilka godzin. Jednak droga od "działającego dema" do stabilnego, skalowalnego i bezpiecznego systemu produkcyjnego to zupełnie inne wyzwanie techniczne.

Wiele projektów Generative AI umiera na etapie Proof of Concept (PoC) lub szybko przeradza się w trudną do utrzymania plątaninę skryptów. Dlaczego? Ponieważ na etapie skalowania zaczyna brakować w nich wyraźnego podziału odpowiedzialności, powtarzalnego pipeline'u danych, wersjonowania promptów oraz twardych zasad testowania i automatyzacji.

W tym artykule omówimy, jak ustrukturyzować produkcyjne repozytorium dla projektu AI. Pokażę Ci sprawdzony szkielet architektury, który:

  • bezboleśnie obsługuje wielu dostawców modeli (OpenAI, Anthropic, modele lokalne),
  • posiada dedykowany moduł RAG i uporządkowany pipeline ekstrakcji danych,
  • separuje warstwę inżynierii promptów od logiki biznesowej aplikacji,
  • jest w pełni gotowy do konteneryzacji (Docker) i wdrażania w cyklach CI/CD.

Jeśli chcesz, aby Twoje systemy sztucznej inteligencji opierały się na solidnych fundamentach inżynierii oprogramowania, a nie tylko "magii promptów" ten przewodnik jest dla Ciebie.


Cel architektury: porządek zamiast „magii”

W systemach GenAI najbardziej kosztowne błędy biorą się z:

  1. braku wersjonowania i powtarzalności danych / indeksów,
  2. braku kontroli nad promptami (zmiany „na żywo” bez śladu),
  3. braku testów regresji (model/prompt/dane zmieniają zachowanie),
  4. mieszania logiki RAG z logiką aplikacji.

Ta struktura rozwiązuje to przez jasne granice między warstwami.


Omówienie katalogów „co gdzie mieszka i dlaczego”

config/ konfiguracja projektu

Tu trzymasz pliki konfiguracyjne:

  • model_config.yaml wybór modelu, ustawienia (temperatura, max tokens), klucze API lub referencje do secret managera,
  • logging_config.yaml format logów, poziomy, destynacje, korelacja requestów.

Dlaczego to ważne?

Konfiguracja nie powinna być „zaszyta” w kodzie, bo będziesz przełączać modele i tryby (dev/stage/prod), a logowanie jest podstawą operacyjności.


data/ dane projektu (artefakty)

Podział:

  • cache/ przetworzone dane, cache odpowiedzi, snapshoty,
  • embeddings/ wygenerowane embeddingi (np. batch do dalszego indeksowania),
  • vectordb/ pliki/bazy vector store (w zależności od technologii).

Zasada: to nie jest „źródło prawdy” (source of truth), tylko artefakty powstające z danych wejściowych i pipelineu.

W produkcji warto dodać:

  • politykę retencji,
  • wersjonowanie indeksów,
  • metadane: data budowy, model embeddingów, parametry chunkingu.

src/ kod aplikacji (serce systemu)

src/core/ abstrakcja LLM i integracje

Pliki typu:

  • base_llm.py wspólny interfejs (send(), generate(), stream(), itp.),
  • gpt_client.py klient do OpenAI,
  • claude_client.py klient do Anthropic,
  • local_llm.py klient do modeli lokalnych,
  • model_factory.py fabryka wybierająca backend na podstawie configu.

Korzyść: aplikacja nie jest „przyspawana” do jednego dostawcy. Zmiana modelu = zmiana configu.


src/prompts/ narzędzia do promptów

  • templates.py szablony promptów (np. Jinja), stałe sekcje system/developer/user,
  • chain.py chaining: wieloetapowe promptowanie (np. plan → wykonanie → walidacja).

Dyscyplina: prompty są „kodem”, więc:

  • powinny mieć wersjonowanie,
  • powinny mieć testy,
  • powinny mieć style guide.

src/rag/ moduł Retrieval-Augmented Generation

  • embedder.py generowanie embeddingów dla chunków,
  • retriever.py wyszukiwanie dokumentów (top-k, MMR, filtry),
  • vector_store.py wrapper na bazę wektorową,
  • indexer.py indeksowanie dokumentów (ETL → chunk → embed → store).

To jest warstwa, która „przynosi wiedzę” do LLM.

RAG powinien być odseparowany, bo ma osobny cykl życia: indeksy budujesz/odświeżasz niezależnie od inference.


src/processing/ przygotowanie danych (pipeline)

  • chunking.py dzielenie tekstu na segmenty,
  • tokenizer.py pomocnicze funkcje tokenizacji,
  • preprocessor.py czyszczenie danych (usuwanie śmieci, normalizacja, deduplikacja).

Dlaczego osobno?

Bo zmiany w chunkingu/tokenizacji potrafią totalnie zmienić jakość RAG i koszt embeddingów. To musi być testowalne i mierzalne.


src/inference/ wykonanie i format odpowiedzi

  • inference_engine.py spina prompt + RAG + LLM, zarządza parametrami,
  • response_parser.py walidacja/formatowanie outputu (np. JSON schema, ekstrakcja pól).

Klucz: inference to osobny etap od budowy indeksu.


tests/ testowanie (w tym regresja promptów)

  • unit/
    • test_llm_clients.py testy klientów (mocki, retry, limity),
    • test_prompts.py testy promptów (np. czy zawierają wymagane sekcje).
  • integration/
    • test_end_to_end.py e2e: pipeline od query do odpowiedzi,
    • test_api_integration.py testy integracji API.

W GenAI testy powinny obejmować:

  • „czy to się uruchamia” (klasyczne),
  • „czy to daje sensowny wynik” (regresja jakości, choćby heurystyczna),
  • „czy output ma poprawny format” (parser/validator).

scripts/ automatyzacja (operacyjnie krytyczna)

  • setup_env.sh setup środowiska,
  • run_tests.sh uruchomienie testów,
  • build_embeddings.py budowa embeddingów,
  • cleanup.py sprzątanie artefaktów.

Ten katalog to most między repo a operacjami: CI/CD, cron, joby w platformie.


Pliki root: .gitignore, Dockerfile, docker-compose.yml, requirements.txt

To zapewnia:

  • przenośność,
  • powtarzalność środowiska,
  • szybki start dla dev/test.

Podsumowanie: jak myśleć o tym repo

Masz tu trzy kluczowe „pętle życia”:

  1. Data/Index lifecycle: processing → embeddings → vectordb
  2. Inference lifecycle: query → retriever → prompt → LLM → response_parser
  3. Ops lifecycle: config → logging → scripts → tests → docker/compose

Takie podejście skraca czas debugowania, ogranicza ryzyko regresji i ułatwia wejście nowych osób do projektu.



2) Procedura operacyjna (SOP): Utrzymanie i rozwój projektu Generative AI (LLM + RAG)

Poniższe SOP jest „do użycia” — możesz wkleić do wewnętrznej dokumentacji zespołu.

2.1 Cel SOP

Zapewnić powtarzalny, bezpieczny i kontrolowany proces:

  • budowy indeksów RAG,
  • uruchamiania inference,
  • wdrażania zmian w promptach/modelach,
  • monitorowania i diagnostyki.

2.2 Zakres

Dotyczy repozytorium o strukturze:

  • config/, data/, src/, tests/, scripts/ oraz plików root.

2.3 Role i odpowiedzialności

  • Operator/On-call: uruchamia procedury, reaguje na alerty, wykonuje rollback.
  • Maintainer: zatwierdza zmiany w promptach/pipeline, dba o standardy testów.
  • Owner produktu: zatwierdza zmiany jakościowe (np. zmiana modelu).

2.4 Definicje

  • Index build: proces zasilenia vectordb/ na podstawie dokumentów.
  • Embedding model: model użyty do wektorów (wpływa na kompatybilność indeksu).
  • Chunking params: parametry dzielenia tekstu (wpływ na recall/precision).

2.5 Procedura: Setup środowiska (pierwsze uruchomienie)

Wejście: czyste środowisko dev/stage/prod

Wyjście: działające środowisko uruchomieniowe

  1. Sklonuj repo i przejdź do katalogu projektu.
  2. Uruchom:
scripts/setup_env.sh
  1. Zweryfikuj, że konfiguracje istnieją:
    • config/model_config.yaml
    • config/logging_config.yaml
  2. Uruchom testy smoke:
scripts/run_tests.sh

Kryterium akceptacji: testy przechodzą; logi zapisują się zgodnie z config.


2.6 Procedura: Budowa / odświeżenie indeksu RAG

Wejście: dane źródłowe (dokumenty), parametry chunkingu, model embeddingów

Wyjście: nowy indeks w data/vectordb/ oraz artefakty w data/embeddings/

Krok po kroku

  1. Pre-flight (kontrola przed startem):
    • sprawdź wolne miejsce na dysku,
    • potwierdź wybrany embedding model w model_config.yaml,
    • sprawdź parametry chunkingu w src/processing/chunking.py (lub config jeśli tak ustalisz).
  2. Uruchom budowę embeddingów:
python scripts/build_embeddings.py
  1. Walidacja:
    • czy data/embeddings/ nie jest puste,
    • czy data/vectordb/ zawiera nowy stan indeksu,
    • czy logi nie pokazują błędów rate-limit/timeout.
  2. (Opcjonalnie) Test integracyjny RAG:
pytest -q tests/integration/test_end_to_end.py

Kryterium akceptacji: indeks zbudowany, test e2e przechodzi.

Rollback

  • zachowuj poprzedni katalog indeksu jako vectordb_prev/ (lub snapshot wolumenu).
  • rollback polega na podmianie wskazania na poprzedni indeks (symlink/konfiguracja).

2.7 Procedura: Uruchomienie inference (obsługa zapytań)

Wejście: zapytanie użytkownika, konfiguracja modelu, dostęp do vectordb

Wyjście: odpowiedź sformatowana przez response_parser.py

  1. Sprawdź, czy indeks istnieje i jest dostępny.
  2. Sprawdź, jaki model jest aktywny (config).
  3. Uruchom komponent aplikacji (zależnie od tego, jak startujesz projekt: CLI/API).
  4. W logach weryfikuj:
    • czas retrieval,
    • liczba pobranych dokumentów (top-k),
    • czas odpowiedzi LLM,
    • błędy parsera odpowiedzi.

Kryterium akceptacji: odpowiedź spełnia format, brak błędów w logach.


2.8 Procedura: Zmiana promptów (kontrolowana)

Ryzyko: prompt changes = natychmiastowa zmiana zachowania systemu.

Zasady

  1. Każda zmiana w src/prompts/ wymaga:
    • PR,
    • testu tests/unit/test_prompts.py,
    • krótkiego opisu „dlaczego zmiana” i „jak mierzymy efekt”.
  2. Minimalna walidacja:
    • „zachowanie” na 510 przykładowych zapytaniach (golden set),
    • poprawność formatu outputu (parser/validator).

Kroki

  1. Zmiana w templates.py / chain.py.
  2. Testy:
scripts/run_tests.sh
  1. E2E:
pytest -q tests/integration/test_end_to_end.py
  1. Deploy (zgodnie z pipeline).
  2. Monitoring po wdrożeniu: error-rate i czas odpowiedzi.

Rollback

  • revert commit / przywrócenie poprzednich promptów,
  • jeśli zmiana promptów wymagała zmiany parsera — rollback obu.

2.9 Procedura: Zmiana modelu (GPT ↔ Claude ↔ Local)

Ryzyko: inne modele mają inne limity, styl, deterministykę.

  1. Zmiana tylko w config/model_config.yaml (preferowane).
  2. Wykonaj test klientów:
pytest -q tests/unit/test_llm_clients.py
  1. Sprawdź rate limit / retry policy w logach.
  2. Walidacja jakości na golden set.
  3. Deploy etapami (canary jeśli możesz).

Rollback: powrót do poprzedniego configu.


2.10 Monitoring i diagnostyka (minimum operacyjne)

W logach muszą się pojawić (jako pola lub jednoznaczne wpisy):

  • request_id / korelacja,
  • czas: retrieval_ms, llm_ms, total_ms,
  • top_k, source_count,
  • model_id, embedding_model_id,
  • błędy parsera i błędy narzędzi.

W razie incydentu:

  1. Sprawdź logi inference_engine.py (czy problem po stronie RAG czy LLM).
  2. Jeśli RAG:
    • czy retriever zwraca wyniki,
    • czy vectordb nie jest uszkodzone,
    • czy filtry nie wykluczają wszystkiego.
  3. Jeśli LLM:
    • rate limit / auth / timeout,
    • zmiana modelu lub parametrów.

2.11 Sprzątanie (retencja)

  1. Uruchom:
python scripts/cleanup.py
  1. Zasady:
    • cache starszy niż X dni usuwać,
    • embeddings trzymać wersjami (jeśli wspierasz multi-index),
    • vectordb snapshotować przed nadpisaniem.

2.12 Checklisty (do wykorzystania „na szybko”)

Checklist: Index build

  • Dysk OK / miejsce OK
  • Embedding model potwierdzony
  • Chunking parametry potwierdzone
  • Build embeddings wykonany
  • Vectordb zaktualizowane
  • Test e2e OK
  • Snapshot/rollback przygotowany

Checklist: Prompt deploy

  • PR + opis zmiany
  • Unit test promptów OK
  • Parser kompatybilny
  • Golden set OK
  • Monitoring po deploy OK
  • Plan rollback gotowy

Co dalej?

Jeśli chcesz, dopracuję to pod Twoje standardy operacyjne:

  1. Wersja w org-mode (wiem, że to Twój „single source of truth”).
  2. Dodanie sekcji „Bezpieczeństwo i sekrety”: gdzie trzymać API keys (vault/ENV), co w .gitignore.
  3. Dodanie „Quality Gates”: golden set, metryki RAG (recall@k), regresja promptów.
  4. Gotowy „Runbook incidentowy”: RAG returns empty, LLM timeouts, parser errors, vectordb corruption.

Powiedz tylko: czy to ma być SOP pod dev/test, czy pod produkcję (on-call, incident response, change management) i w jakim środowisku odpalasz (Docker/VM/K8s)?