Strona główna / Artykuły / Java jako język do produkcji w obszarze sztucznej inteligencji: Spring Boot, RAG i pgvector

Java jako język do produkcji w obszarze sztucznej inteligencji: Spring Boot, RAG i pgvector

Praktyczna mapa budowania usług RAG na JVM przy użyciu Spring AI lub LangChain4j, wyboru magazynu wektorów oraz zarządzania dzierżawą, kosztami, oceną i rozwiązaniami awaryjnymi.

2624 słów

Python służy do trenowania modeli, natomiast Java stopniowo staje się językiem, który umożliwia ich wdrożenie. Przez około trzy lata środowisko AI przypominało konferencję poświęconą Pythonowi, tylko z większym budżetem: dema w notatnikach, tutoriale rozpoczynające się od pip install, a także diagramy architektury, na których mała ramka o nazwie „Python LLM service” znajdowała się obok rzeczywistego backendu. Tym rzeczywistym backendem zazwyczaj był Spring Boot, który już zajmował się autoryzacją, fakturowaniem, logami audytowymi oraz bazą danych.

Mała ramka nie jest już obowiązkowa. Spring AI przekroczył wersję 1.0 i znajduje się teraz na poziomie 2.0. LangChain4j to pełnoprawna biblioteka JVM z ponad dwudziestoma dostawcami modeli oraz ponad trzydziestoma magazynami wektorowymi. PostgreSQL przechowuje embeddingi za pomocą pgvector. Usługa Spring Boot, którą już uruchamiasz, może wywołać model LLM, pobrać odpowiedni kontekst, użyć różnych narzędzi, przesyłać tokeny oraz publikować metryki Micrometer, wszystko to wewnątrz JVM.

Czy to czyni z Javy nowego króla sztucznej inteligencji? Nie w sensie zwykłych walk językowych. Nikt nie zastępuje PyTorchem Javy. To, w czym Java odnosi sukcesy, to obszar, gdzie jest pieniądz: aplikacje biznesowe, które muszą być poprawne, przejrzyste i jednocześnie jak najmniej atrakcyjne. Jeśli pracujesz w tym obszarze, zwycięzcą jest to, co już uruchamia twój klastr produkcyjny, a coraz częściej jest to Java.

Pozostała część tego artykułu opisuje tę zmianę. Omawia, dlaczego do niej doszło, jak wygląda usługa RAG w Spring Boot, jak wybrać magazyn wektorów oraz jakie szczegóły produkcyjne odróżniają wersję demonstracyjną od rozwiązania, któremu można zaufać przy przechowywaniu danych klientów.

Zaletą Pythona zawsze były badania naukowe

Python zdominował dziedzinę badań naukowych z uzasadnionych powodów, które nadal obowiązują: NumPy, PyTorch, Hugging Face, Jupyter oraz cała generacja inżynierów biegle posługujących się tensorami. Dostosowywanie modeli, testowanie nowych architektur embeddingów czy szkolenie na kartach graficznych nadal odbywa się w Pythonie.

Rozwój produktów to zupełnie inna sprawa. Większość firm nigdy nie szkoli modeli podstawowych. Wzywają takie modele, a dopiero wtedy zaczyna się prawdziwa praca: pobieranie dokumentów, wprowadzanie zasad działania, oddzielanie użytkowników, rejestrowanie zapytań, kontrolowanie wydatków oraz upewnianie się, że nikt nie wymyśli fałszywej kwoty zwrotu. To właśnie projektowanie API, dostęp do danych, bezpieczeństwo i zarządzanie operacjami – obszary, którymi Spring Boot zajmuje się od 2014 roku.

Popularne rozwiązanie polegające na umieszczeniu modelu za pomocą FastAPI wiązało się z dodatkowymi kosztami:

  • dwa języki w jednym produkcie
  • oddzielne procesy wdrażania
  • oddzielne systemy monitoringu
  • umowa interfejsu, która nieco się zmieniała z każdym sprintem
  • przeglądy bezpieczeństwa, które nagle obejmowały usługę w Pythonie przechowującą własne sekrety
  • Ludzie to akceptowali, ponieważ Java nie posiadało solidnych narzędzi do pracy z LLM. Do 2025 roku sytuacja się zmieniła. Spring AI 1.0 stał się powszechnie dostępny w maju 2025 roku, a wersja 2.0 pojawiła się w czerwcu 2026 roku razem z Spring Boot 4. LangChain4j osiągnął wersję 1.0 w tym samym okresie. Stosunek JVM już nie jest w tyle. Dla zespołów już korzystających ze Spring to rozsądny standard.

    Co oznacza „dodanie AI” w aplikacji Spring

    Nie jest to konieczne przepisywanie kodu, wystarczą trzy funkcjonalności.

    1. Szyna czatowa. ChatClient w Spring AI lub AiService w LangChain4j przekształca prompt oraz historię wiadomości na odpowiedź modelu. Strumieniowanie danych jest standardem, a ustrukturyzowany wynik może zostać przechowywany w rekordzie Java, co oszczędza czas potrzebny na przetwarzanie JSON z tekstu za pomocą regularnych wyrażeń.
    2. Dostęp do własnych danych. Obliczasz embeddingi i przechowujesz je w bazie wektorowej, dzięki czemu odpowiedzi pochodzą z twoich polityk, faktur, instrukcji obsługi oraz dokumentacji produktu, a nie z tego, co model półpamięta. To właśnie jest Retrieval-Augmented Generation (RAG).
    3. Wywoływanie narzędzi. Model może wykorzystywać metody Java zgodnie z twoimi regułami autoryzacji, na przykład do wyszukiwania zamówień, otwierania przypadków lub sprawdzania salda. W tym przypadku system typów oraz usługi, które już posiadasz, okazują się przydatne. Model nie jest twoim źródłem prawdy – on tylko na jego podstawie wyciąga wnioski.

    Z użyciem Spring Security, Spring Data, Actuator oraz PostgreSQL mamy tu kolejny typ klienta, podobnie jak wtedy, gdy pojawiły się Redis czy Kafka.

    RAG, wyjaśnione dla programisty Java

    Gdy usuniemy otoczkę tajemniczości, RAG to pipeline wyszukiwania kończący się modelem językowym. Kroki są następujące:

    1. Rozdzielamy dokumenty na fragmenty.
    2. Każdy fragment przekształcamy w embedding – wektor liczb zmiennoprzecinkowych, który koduje jego znaczenie.
    3. Zapisujemy te wektory wraz z metadanymi, takimi jak identyfikator użytkownika, ID dokumentu, adres źródłowy i data.
    4. Gdy pojawia się pytanie, embeddingujemy je i szukamy najbliższych fragmentów.
    5. wstawiamy te fragmenty do promptu.
    6. polecamy modelowi, aby odpowiedział wyłącznie na podstawie tych fragmentów oraz przyznał, gdy nie zawierają one odpowiedzi.

    Baza danych wektorowych nie zastępuje bazy transakcyjnej. Jest to indeks semantyczny, nieco podobny do Elasticsearch, ale z bardziej zaawansowaną leksyką.

    Dla projektów opartych na Spring Boot zazwyczaj punktem wyjścia jest PostgreSQL w połączeniu z pgvector. Postgres jest już wdrożony, zapewniony o kopie zapasowe i zarządzany jako zasób puli, więc dodanie jednej rozszerzenia kosztuje mniej niż natychmiastowe wdrożenie Pinecone, Weaviate lub Qdrant. Specjalistyczne silniki są korzystne, gdy skalowanie jest ekstremalne lub potrzebna jest hybrydowa wyszukiwarka, jednak większość wewnętrznych aplikacji RAG funkcjonuje daleko poniżej takich wymagań.

    Skelet, który działa

    Poniższa konfiguracja łączy Spring AI 2.0 z Boot 4; w przypadku Boot 3.4 lub 3.5 należy użyć wersji 1.1.x. Najpierw należy dodać plik BOM, aby wszystkie elementy startowe były na tej samej wersji:

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.springframework.ai</groupId>
          <artifactId>spring-ai-bom</artifactId>
          <version>1.1.8</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    

    Następnie podaj tylko te zależności, które są potrzebne:

    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    

    JDBC został umieszczony tam celowo. Narzędzie pgvector starter działa poprzez JdbcTemplate, a zapomnienie o tej zależności jest zaskakująco częstą przyczyną nieudanego uruchomienia.

    Ustawienia znajdują się w pliku application.yml. W tym właśnie polega idea Spring AI: model powinien być konfigurowany tak samo jak źródło danych.

    spring:
      datasource:
        url: jdbc:postgresql://localhost:5432/app
        username: app
        password: ${DB_PASSWORD}
      ai:
        openai:
          api-key: ${OPENAI_API_KEY}
          chat:
            options:
              model: gpt-4o-mini
              temperature: 0.2
          embedding:
            options:
              model: text-embedding-3-small
        vectorstore:
          pgvector:
            initialize-schema: true
            index-type: HNSW
            distance-type: COSINE_DISTANCE
            dimensions: 1536
    

    Dwa aspekty w tym pliku są ważniejsze, niż się wydaje. Wymiar embeddingu musi być taki sam jak ten generowany przez model embeddingowy, a text-embedding-3-small wytwarza wartość 1536. Jeśli zmienisz model i zapomnisz o tej liczbie, wyszukiwanie podobieństw będzie działać gorzej, choć bez żadnych błędów. Drugim istotnym elementem jest niska temperatura w przypadku RAG – chcesz bowiem, aby wynik był bliski pobranemu tekstu, a nie kreatywną wersją bilansu.

    Traktuj proces przyjmowania danych jako odrębną usługę, a nie prosty skrypt kontrolny. Funkcja VectorStore.add(...) przyjmuje fragmenty typu Document, a każdy z tych fragmentów powinien zawierać metadane, na których później będziesz filtrować, takie jak tenantId, source, docType oraz effectiveDate. Bez filtrów wyszukiwanie semantyczne może pokazać umowę jednego klienta w odpowiedzi innego klienta.

    Zapytania są równie proste w użyciu. QuestionAnswerAdvisor w Spring AI to określona wersja tego procesu: pobieranie danych, dodawanie kontekstu oraz wywoływanie modelu. Gdy potrzebujesz filtrów, ponownego sortowania lub cytatów, użyj sam similaritySearch i traktuj filterExpression jako klauzulę WHERE, ponieważ to właśnie jest jego zadaniem.

    Wybór magazynu wektorowego bez dogmatów

    Będziesz miał do wyboru cztery opcje:

    • Postgres z pgvector. To rozsądny standard, dobrze znany pod względem operacyjnym. Dzięki indeksowaniu HNSW, monitorowaniu dokładności wyników oraz kompaktowym blokom danych radzi sobie z dziesiątkami milionów wektorów. Okazuje się niewystarczający, gdy potrzebujesz zaawansowanego hybrydowego wyszukiwania lub zespołu zajmującego się utrzymaniem infrastruktury do szukania najbliższych sąsiadów.
  • Redis. Szybki, często już obecny w stackach Spring, nadający się do przechowywania pamięci sesji oraz małych baz wiedzy. To koszt RAM stanowi główny punkt do rozważenia.
  • Specjalnie zaprojektowane silniki (Qdrant, Weaviate, Milvus, Pinecone). Są sensowne, gdy sam produkt polega na wyszukiwaniu: duże zbiory danych, zapytania hybrydowe typu leksykalno-wektorowe, kilka indeksów lub zespół chcący oprogramowania zaprojektowanego specjalnie do ANN. Spring AI bezpośrednio obsługuje te popularne rozwiązania.
  • Silnik wyszukiwania, który już posiadasz. Elasticsearch i OpenSearch radzą sobie z algorytmem kNN, a jeśli twoje dokumenty tam się znajdują, gęste pole wektorowe może być lepsze niż dodawanie czwartego systemu danych.
  • Sposób podejmowania decyzji jest prosty, i to jego mocna strona. Zacznij od pgvector, gdy Postgres już działa w środowisku produkcyjnym. Zmień strategię, gdy szybkość odpowiedzi, opóźnienia lub wyszukiwanie hybrydowe nie spełniają rzeczywistych standardów jakości, a nie dlatego, że jakaś prezentacja twierdziła, iż zaawansowane aplikacje AI używają danego produktu. Pamiętaj również o przechowywaniu odniesienia do oryginalnego zapisu obok każdego wektora, abyś mógł go ponownie włączyć w razie zmiany zasad.

    To ostatnie punkt jest pomijany w tutorialach. Modele do włączania są zastępowane, metody dzielenia na fragmenty stają się bardziej zaawansowane, a dokumenty prawne są modyfikowane. Zadanie importu, które jest idempotentne i oparte na wersji dokumentu, pozwala uniknąć problemów związanych z próbami ustalenia, dlaczego bot korzysta z zeszłorocznego harmonogramu opłat.

    Spring AI kontra LangChain4j

    Oba są gotowe do użycia w środowisku produkcyjnym. To, który się nadaje, zależy od aktualnego poziomu rozwoju twojego zespołu.

    Spring AI bierze znany z Springa koncepcję przenośnych abstrakcji i stosuje ją do modeli. Jeśli wyobrażysz to sobie jako Spring Data dla LLM, będzie to naturalne – autokonfiguracja, ChatClient, doradcy, Actuator oraz Micrometer doskonale pasują do tego podejścia. Używaj wersji 2.0 z Boot 4. W przypadku Boot 3 należy pozostać przy wersji 1.1.x, ponieważ 2.0 nie będzie mogła zostać bezpośrednio zainstalowana.

    LangChain4j został napisany przede wszystkim dla Javy i oferuje dodatkowo starter Spring. Obsługuje większą liczbę dostawców, jego typowane AiServices przypominają klienty Feign skierowane do modeli, a nadaje się dla zespołów używających również Quarkus lub zwykłej Javy. Osoby, które chcą mieć pełną kontrolę nad pętlą agenta, zazwyczaj wybierają tę opcję.

    Nic nie stoi na przeszkodzie, by używać obu rozwiązań jednocześnie. Jednym sprawdzonym podziałem jest wykorzystanie Spring AI do rozmów i RAG w usługach skierowanych do klientów, przy czym LangChain4j może uzupełniać braki w przypadku konkretnego dostawcy lub wzorca agenta. Trzeba utrzymywać ich komponenty oddzielnie i nigdy nie pozwalać dwóm różnym modelom embeddingowym zapisywać niekompatybilnych wektorów do jednej tabeli.

    Infrastruktura oparta wyłącznie na Spring Boot, która może przeprowadzić aktualizację na Boot 4, powinna korzystać z Spring AI. Środowisko mieszane z JVM lub takie, które od roku pozostaje na wersji Boot 3, prawdopodobnie okaże się bardziej zależne od wersji frameworka, dlatego lepiej wybrać LangChain4j.

    To w produkcji JVM pokazuje swoją przewagę

    Każdy może zaprezentować przykład odpowiedzi. Produkt natomiast musi wytrzymać audyt. Oto obszary, które należy uwzględnić:

    • Związywanie z kontekstem i odrzucanie żądań. Ogranicz model do uzyskanego kontekstu i podawaj odniesienia. Gdy wyniki porównania podobieństwa spadną poniżej określonego progu, odpowiedź powinna brzmieć „to nie znajduje się w bazie wiedzy”, a nie być domysłem. W sektorach finansowym, operacyjnym i opieki zdrowotnej błąd popełniony z przekonaniem kosztuje więcej niż proste odrzucenie żądania.
    • Wynajem i cenzura. Filtruj wektory według poszczególnych użytkowników oraz usuwaj poufne informacje z zapytań. Unikaj umieszczania numerów kart, numerów kont i danych zdrowotnych w surowych zapisach zapytań. Jeśli konieczne jest ich zapisanie w celu debugowania, najpierw tokenizuj je i przechowuj zgodnie z zasadami przechowywania stosowanymi przez system źródłowy.
    • Budżety na koszty i opóźnienia. Tokeny kosztują pieniędzy, dlatego rejestruj tokeny wejściowe, tokeny wyjściowe, czas pobierania danych oraz czas pracy modelu. Ustal maksymalną liczbę tokenów na każde wezwanie, przechowuj w pamięci tymczasowej embeddingi dokumentów, które się powtarzają, oraz przesyłaj zadania klasyfikacji lub kierowania do mniejszego modelu, używając większego tylko w razie potrzeby.
  • Czas wygaśnięcia i izolacja. Wywołanie modelu to zdalna operacja wejścia/wyjścia o długim czasie realizacji. Ograniczone grupy wątków, mechanizmy ochronne i alternatywne ścieżki (szukanie według słów kluczowych lub kolejka ludzka) zapobiegają temu, by awaria modelu pochłonęła wszystkie wątki Tomcat lub Netty, powodując problemy z procesem płatności.
  • Ocena. Prompt nie może być testowany jednostkowo jak obliczenia podatkowe, ale zbiór trzydziestu pytań o znanych odpowiedziach może pomóc w wykryciu błędów przy niedokładnościach liczbowych podczas budowania aplikacji. Należy również sprawdzać wyniki oceny w systemie RAG, czy właściwy fragment tekstu trafił do promptu. Wiele rzekomych problemów z jakością modeli to w rzeczywistości błędy przy pobieraniu danych.
  • Ludzka weryfikacja. W oprogramowaniu korporacyjnym najcenniejszą funkcją sztucznej inteligencji jest często wersja robocza, a nie chatbot: zalecana notatka do sprawy, z góry uzupełniony powód wyjątku lub streszczenie długiego, 40-stronicowego raportu. Należy zachować udział człowieka na etapie zatwierdzania, dopóki błędy nie staną się rzadkością.
  • To wszystko to znajome zadania w Springu: otoczenie odległego wywołania, oznaczenie metryki, odrzucenie nieautoryzowanych żądań. LLM to nowa zależność o gorszych warunkach SLA, więc należy nią zarządzać tak samo jak innymi zależnościami.

    Gdzie stwierdzenie „Java jest królem” jest prawdziwe

    Java stanowi lepszy punkt wyjścia, gdy system danych już działa na JVM, gdy funkcja AI musi dzielić identyfikator, kontekst użytkownika oraz dane audytowe z istniejącymi API, gdy potrzebne jest wywoływanie wewnętrznych usług Java jako narzędzi, gdy wymogi regulacyjne nie pozwalają na użycie alternatywnego stacku Python, oraz gdy osoby pełniące dyżur o 2 w nocy już znają Spring Boot.

    Python nadal przewyższa w przypadku szkolenia lub dopracowywania modeli, gdy potrzebna jest biblioteka badawcza w tydzień po jej wydaniu, lub gdy sam model jest produktem, a nie tylko jedną z funkcji w większym oprogramowaniu.

    To są odrębne zadania, a przez dwa lata branża zachowywała się tak, jakby nie istniały. Skutkiem tego było pojawienie się mnóstwa architektur wykorzystujących Pythona jako narzędzie pomocnicze, wraz z nadzieją, że jego specyfikacja pozostanie niezmieniona. Wzorzec, który wydaje się interesujący na rok 2026, jest zmienny: Python lub hostowany laboratorium modeli zajmuje się szkoleniem i oceną, Java służy do obsługi, wyszukiwanie wektorowe funkcjonuje obok danych operacyjnych, model pochodzi od dostawcy, a logika biznesowa pozostaje w twoich rękach.

    Rozsądny pierwszy projekt

    Nie zaczynaj od nieograniczonego chatbota dla całej firmy. Wybierz wąski, cenny zakres treści oraz pytanie, które można ocenić. Oto kilka przykładów skutecznych rozwiązań:

    • Podręcznik z pytaniami i odpowiedziami dla inżynierów pełniących dyżury, powiązany z odpowiednią stroną źródłową
    • Pomocnik do zarządzania politykami, który przytacza dokładne postanowienia
    • Pytania i odpowiedzi dotyczące faktury lub wykazu, który użytkownik właśnie przesłał
  • Streszczenia biletów zapisywane w aktach sprawy, a nie w oddzielnym module czatu
  • Zastosuj to za zautoryzowanym API, które już posiadasz: rejestruj każde pobranie, wyświetlaj cytaty w interfejsie użytkownika, dodaj funkcję wyłączania konfiguracji oraz sprawdź koszt za zapytanie przed rozszerzaniem zbioru danych. Pierwszy projekt uczy dzielenia na fragmenty, metadanych, oceny oraz kontroli zmian w promptach – te umiejętności są przydatne później, natomiast nazwa frameworku nie ma takiego znaczenia.

    Sprzęt, któremu już ufasz

    Zdanie „Java to nowy król sztucznej inteligencji” nadaje się jako nagłówek, ale nie jako teza. Java nie zwyciężyło w dziedzinie uczenia maszynowego – raczej wprowadziło zastosowania sztucznej inteligencji na platformę, która już obsługuje światowe systemy transakcyjne.

    Inżynier Spring Boot nie potrzebuje żadnej zgody, by tu pracować, ani dyplomu z nauk o danych. Potrzebny jest dostawca modeli, model embeddingowy, indeks wektorowy oraz ta sama staranność operacyjna stosowana przy transakcjach płatniczych i komunikacji. Python nadal szkoli „gwiazdy” laboratorium. Java staje się liderem na drodze od laboratorium do rejestru, gdzie odpowiedzi muszą mieć podstawy, wywołania narzędzi wymagają autoryzacji, a każda rachunek potrzebuje wyjaśnienia. Dla większości zespołów to jedyna godna posiadania korona.

    Jeśli Twój serwis Spring Boot już kontroluje tożsamość, pieniądze i ślady audytowe, nie ma powodu, by czekać na dodatkowe narzędzie. Wybierz w tym tygodniu jeden wąski zbiór danych, umieść ChatClient przed pgvector i wymagaj od modelu podawania źródeł.

    Literatura pokrewna

  • Architektura obsługi modeli: Dlaczego API jest tylko bramą wejściową — Przewodnik po platformie stojącej za punktem końcowym inferencji: routowanie, skalowanie, wersjonowanie, obserwowalność, bezpieczeństwo oraz wywoływanie modeli z Spring AI lub LangChain4j.
  • Spring AI kontra LangChain4j: Konfiguracja, pierwszy punkt końcowy i styl projektowania — Porównanie Spring AI i LangChain4j pod względem architektury, starterów Spring Boot, konfiguracji oraz pierwszego punktu końcowego typu REST do czatowania, a także informacje o tym, który styl pasuje do Twojego zespołu.
  • Spring AI RAG z wbudowanym magazynem wektorowym w JVM: jak porzucić Qdrant i Docker — Zastąpienie Qdrant biblioteką Vectors w ramach aplikacji Spring AI RAG: zależności, flagi JVM, ustawienia persistencji oraz zalety i wady działania wewnątrz JVM.
  • Diagnozowanie niepowodzeń budowy w CI za pomocą reguł, RAG i lokalnego LLM — System napisany w Javie do analizy niepowodzeń w procesach CI, który deterministycznie analizuje logi, pobiera notatki pomocnicze z PGVector przy użyciu Ollamy, a następnie prosi lokalny LLM o podjęcie decyzji.