W świecie sztucznej inteligencji, zwłaszcza w kontekście modeli językowych (LLM), Python przez wiele lat dominował jako język programowania. Wiele kursów, tutoriali oraz przykładów kodu zakłada, że prace nad AI wykonuje się głównie w Pythonie. Jednak dla programistów osadzonych w ekosystemie JavaScript, rzeczywistość jest nieco inna. Wdrażanie AI przy użyciu Node.js oraz JavaScript nie tylko jest możliwe, ale może być wyjątkowo efektywne i korzystne. Dlaczego warto postawić na JavaScript przy pracy z AI? Oto kilka kluczowych powodów, które sprawiają, że JavaScript to świetny wybór do implementacji nowoczesnych rozwiązań opartych na sztucznej inteligencji.
Większość procesów związanych z AI, w tym tych opartych na dużych modelach językowych, polega na prostym schemacie: „wysyłaj zapytanie → odbieraj odpowiedź → przetwarzaj odpowiedź”. W tym kontekście Node.js okazuje się być doskonałym wyborem, zwłaszcza w sytuacjach, gdy potrzebujemy efektywnego, skalowalnego środowiska do obsługi usług API.
W przypadku Node.js, uruchomienie serwera czy wysłanie zapytania do modelu AI wymaga zaledwie kilku linijek kodu, co znacząco przyspiesza proces developmentu. Dzięki asynchronicznej naturze Node.js, mamy do czynienia z wyjątkową wydajnością, szczególnie przy zadaniach opartych na API, które mogą wymagać obsługi wielu jednoczesnych zapytań w czasie rzeczywistym. To sprawia, że Node.js jest szybkie, skalowalne i idealnie nadaje się do tworzenia aplikacji, które potrzebują natychmiastowej reakcji na zapytania użytkowników – cechy, które są kluczowe przy pracy z dużymi modelami językowymi (LLM).
JavaScript to nie tylko język frontendowy, ale także potężne narzędzie do pracy z danymi w czasie rzeczywistym, do obsługi WebSocketów, integracji z popularnymi frameworkami frontendowymi jak React, Vue czy Angular oraz pracy z serwisami zewnętrznymi. Kiedy połączymy to z szeroką gamą dostępnych bibliotek i narzędzi do obsługi sztucznej inteligencji, JavaScript staje się naturalnym wyborem do budowy nowoczesnych, inteligentnych aplikacji.
W ekosystemie JavaScript znajdziemy biblioteki, które umożliwiają łatwą integrację z modelami opartymi na sztucznej inteligencji, takimi jak OpenAI, TensorFlow.js, a także Hugging Face. Tego typu narzędzia pozwalają na budowę chatów, asystentów głosowych, aplikacji rozpoznających obrazy, czy nawet na pełnoprawne systemy rekomendacyjne. W przypadku JavaScriptu, ogromną zaletą jest również możliwość natychmiastowego podglądu wyników w przeglądarkach internetowych. Dzięki temu, zarówno proces rozwoju, jak i testowania aplikacji staje się znacznie prostszy i szybszy.
Chociaż Python jest często wybieranym językiem w kontekście AI, w tym dużych modeli językowych (LLM), warto zauważyć, że JavaScript również doskonale sprawdza się w tej roli. Dzięki popularnym API, takim jak OpenAI, i wsparciu dla frameworków takich jak HuggingFace, integracja z tymi technologiami w JavaScript odbywa się w sposób szybki i bezproblemowy.
Node.js zapewnia programistom elastyczność w integracji z różnymi technologiami i usługami zewnętrznymi, a jego ekosystem stale rośnie, dostosowując się do potrzeb nowoczesnych aplikacji AI. W przeciwieństwie do Pythona, który mimo swojej potężnej roli w dziedzinie sztucznej inteligencji, może wymagać bardziej rozbudowanego środowiska oraz większej liczby zewnętrznych narzędzi, JavaScript ma bardzo zwartą i efektywną strukturę do integracji w różnych środowiskach.
W poniższych dwóch przykładach pokażę, jak łatwo można połączyć się za pomocą Node.js API znanych modeli: OpenAI i różnych bibliotek z HuggingFace.
Po pierwsze: należy utworzyć plik .env, w którym zapiszesz klucze do połączenia. Powinien on znajdować się w katalogu głównym projektu.
Jeśli brakuje kluczy do API OpenAI - oto dobry artykuł na Medium, który krok po kroku pokazuje proces rejestracji na platformie OpenAI i tworzenia kluczy dla API.
Po drugie: zainicjuj projekt Node.js. Można to zrobić za pomocą terminala:
npm init
Odpowiedz na kilka pytań w terminalu, a system utworzy plik package.json. W rzeczywistości, jeśli mówimy o pracy z API, wystarczy zainstalować:
Aby pracować z OpenAI, należy dodatkowo dodać pakiet openai, natomiast jeśli chodzi o HuggingFace, to w poniższym przykładzie użyję również @huggingface/inference i multer.
Nie będę szczegółowo opisywał działania strony HTML i dołączonego do niej skryptu JS, ponieważ jej jedyną funkcją jest wysłanie zapytania do naszego serwera i wyrenderowanie odpowiedzi. Wystarczy do tego użyć standardowej funkcji fetch.
Więc piszemy nasz serwer na Node.js. W tym celu nie zapomnijmy zainstalować wszystkich pakietów wymienionych powyżej. Następnie uruchamiamy sam serwer i czytamy zawartość pliku .env (w którym przechowywane są klucze do API) i od razu rozwyązujemy ewentualny problemem z CORS.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const OpenAI = require('openai');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
app.use(bodyParser.json());
app.use(cors());
const openai = new OpenAI({
apiKey: process.env['OPENAI_API_KEY'],
});
Wskazujemy, z którym modelem OpenAI będziemy pracować:
const MODEL = 'gpt-5-nano';
Pozostało tylko dodać przetwarzanie przychodzącego requestu i uruchomić sam serwer. Zadaniem serwera jest odbieranie polecenia (prompt) i przekazywanie go do OpenAI.
app.post('/test-server', async (req, res) => {
const userPrompt = req.body.prompt || 'Hello!';
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.setHeader('Transfer-Encoding', 'chunked');
try {
const stream = await openai.chat.completions.create({
model: MODEL,
stream: true,
messages: [
{ role: 'developer', content: 'You are a helpful assistant' },
{ role: 'user', content: userPrompt },
],
});
for await (const chunk of stream) {
const content = chunk.choices?.[0]?.delta?.content;
if (content) res.write(content);
}
res.end();
} catch (error) {
console.error('Stream error:', error);
res.status(500).send('Error streaming OpenAI response');
}
});
Należy pamiętać, że w zapytaniu do OpenAI znajduje się nie tylko polecenie (userPrompt), ale także rola, w której OpenAI będzie odpowiadać (role: 'developer'). Domyślnie jest to „You are a helpful assistant” (Jesteś pomocnym asystentem). Jest to odpowiednie w większości przypadków. Jeśli jednak chcemy przyjąć inną rolę (bardziej odpowiadającą naszym celom komercyjnym), można to zmienić bez żadnych problemów.
I druga rzecz, na którą mogliście zwrócić uwagę w kodzie: używam chunk i stream. Dlaczego? Chodzi o to, że OpenAI nie odpowiada zbyt szybko (generowanie odpowiedzi może zająć nawet minutę, zwłaszcza jeśli mówimy o obszernej odpowiedzi). Jeśli będziemy po prostu czekać na odpowiedź, może się wydawać, że czekasz wiecznie, aż system odpowie (lub że aplikacja w ogóle nie działa). Biorąc pod uwagę, że modele GPT generują odpowiedzi na podstawie tokenów (nie są to dokładnie sylaby, ale coś zbliżonego), serwer przyjmuje ten token i natychmiast przekazuje go na stronę. Nie zmienia to działanie aplikacji, ale jest znacznie bardziej przyjazne dla użytkownika, ponieważ użytkownik końcowy nie patrzy na wiecznie ładujący się loader, ale widzi przyjemną animację generowania tekstu.
Właściwie to wszystko. Pozostaje tylko „słuchać” przychodzących zapytań (jeśli port jest określony w pliku .env, należy go stamtąd pobrać, a jeśli nie, należy wpisać własną wartość; w tym projekcie używam 4001.):
app.listen(process.env.PORT || 4001, () => {
console.log('run here: http://localhost:4001');
});
Następnie wystarczy wykonać standardowe "npm run start", aby serwer był gotowy do przyjmowania przychodzących żądań.
No i oczywiście, jeśli chcecie dowiedzieć się więcej, przeczytajcie dokumentację.
Połączenie z OpenAi nie jest zbyt skomplikowane, zwłaszcza że potrzebujemy jedynie odpowiedzi tekstowej. Przejdźmy do nieco bardziej złożonego zadania – generowania obrazów, a także podłączmy nie jedną, ale kilka modeli dostępnych bezpłatnie za pośrednictwem biblioteki HuggingFace.
Pierwszą rzeczą, którą musimy zrobić, jest zarejestrowanie się w serwisie HuggingFace i utworzenie klucza (mniej więcej tak samo jak w przypadku OpenAI). Zakładka z kluczami znajduje się w sekcji Settings -> Access Tokens. Podczas tworzenia klucza wystarczy nadać uprawnienie READ.
Okej, jeśli wszystko jest gotowe, zaczynamy. Po pierwsze, szybko stwórzmy interfejs HTML.
Można napisać go według własnego uznania i pozostawić jedynie miejsce na wynik. Jednak zalecam najpierw dodać wybór modeli:
Dlaczego właśnie te modele? Wszystkie te modele są wywoływane za pośrednictwem Hugging Face Inference API (bezserwerowego) i można z nich korzystać w ramach bezpłatnego limitu.
Również polecam dodać mały fragment dotyczący wyboru parametrów. To da trochę więcej elastyczności podczas pracy z obrazami.
Dla sekcji z wynikiem wystarczy zwykły div.
Wynik będzie wyglądał mniej więcej tak:
Jeśli chodzi o skrypty, ich zadaniem jest pobranie wszystkich naszych parametrów i wysłanie ich na serwer, a także ewentualna obsługa błędów (tutaj są one znacznie bardziej prawdopodobne, choćby dlatego, że może się skończyć darmowy limit HuggingFace). Podobnie jak w przypadku OpenAI, wystarczy użyć zwykłego fetch.
Przejdźmy do serwera. Najpierw zbieramy sam serwer (i importujemy port, jeśli jest określony w .env) i podłączamy klucze HuggingFace, nie zapominając, że należy zaimportować samą bibliotekę HuggingFace.
import express from "express";
import cors from "cors";
import "dotenv/config";
import { InferenceClient } from "@huggingface/inference";
const app = express();
const port = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static("public"));
const HF_TOKEN = process.env.HF_ACCESS_TOKEN;
const hf = HF_TOKEN ? new InferenceClient(HF_TOKEN) : null;
Następnie dodajemy wybór modelu, jeśli został on dodany na stronie:
const MODELS = {
"flux-dev": {
label: "FLUX.1-dev",
provider: "hf-inference",
hfModelId: "black-forest-labs/FLUX.1-dev",
defaultSteps: 30,
defaultGuidance: 3.5
},
"flux-schnell": {
label: "FLUX.1-schnell",
provider: "hf-inference",
hfModelId: "black-forest-labs/FLUX.1-schnell",
defaultSteps: 18,
defaultGuidance: 3.0
},
"sdxl-base": {
label: "SDXL Base 1.0",
provider: "hf-inference",
hfModelId: "stabilityai/stable-diffusion-xl-base-1.0",
defaultSteps: 28,
defaultGuidance: 7.0
},
"sd3-medium": {
label: "Stable Diffusion 3 Medium",
provider: "hf-inference",
hfModelId: "stabilityai/stable-diffusion-3-medium-diffusers",
defaultSteps: 28,
defaultGuidance: 7.0
}
};
Właściwie zbieramy dane z naszego interfejsu i wysyłamy je do HuggingFace. Ważne - coś może się zepsuć na każdym kroku, dlatego należy się zabezpieczyć i dodać domyślne stany, a także wyświetlać błędy w konsoli, aby zrozumieć, na jakim etapie coś poszło nie tak.
app.post("/api/generate", async (req, res) => {
try {
if (!HF_TOKEN || !hf) {
return res.status(500).json({
error: "HF_ACCESS_TOKEN не настроен на сервере"
});
}
const prompt = (req.body.prompt || "").trim();
const modelKey = (req.body.model || "flux-dev").trim();
const modelConfig = MODELS[modelKey] || MODELS["flux-dev"];
const steps =
req.body.steps !== undefined
? Number(req.body.steps)
: modelConfig.defaultSteps;
const guidance =
req.body.guidance !== undefined
? Number(req.body.guidance)
: modelConfig.defaultGuidance;
if (!prompt) {
return res.status(400).json({ error: "No prompt" });
}
console.log(
`text-to-image: model=${modelConfig.hfModelId}, provider=${modelConfig.provider}, steps=${steps}, guidance=${guidance}`
);
const resultBlob = await hf.textToImage({
provider: modelConfig.provider,
model: modelConfig.hfModelId,
inputs: prompt,
parameters: {
num_inference_steps: steps,
guidance_scale: guidance,
width: 1024,
height: 1024
}
});
const arrayBuffer = await resultBlob.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
res.set("Content-Type", resultBlob.type || "image/png");
res.send(buffer);
} catch (err) {
console.error("Generation error:", err);
res.status(500).json({
error: "Generation Error",
details: err?.message || String(err)
});
}
});
Właściwie to wszystko. Słuchamy serwera na wskazanym porcie.
app.listen(port, () => {
console.log(`Multi-model server is here: http://localhost:${port}`);
});
I uruchamiamy przez npm run start.
Zobaczmy, czy to działa. Poprosimy model o wygenerowanie uroczego kotka, który patrzy przez okno na zaśnieżoną ulicę.
Nie wiem jak uwazacie, ale moim zdaniem wyszło bardzo ładnie.
JavaScript i Node.js to świetny wybór do pracy z AI, zwłaszcza jeśli zależy Ci na efektywności, integracji z aplikacjami webowymi i łatwej współpracy z zespołem frontendowym. Dzięki asynchronicznej naturze Node.js, programiści mogą szybko implementować i testować rozwiązania AI, zachowując dużą skalowalność aplikacji. Jeśli poszukujesz prostego, elastycznego i wydajnego rozwiązania do wdrażania modeli AI w produkcji, JavaScript będzie doskonałym wyborem. Nie musisz rezygnować z nowoczesnych rozwiązań AI, aby pozostać w ekosystemie JavaScript. Jeśli chcesz zgłębić temat jeszcze bardziej i odkryć, jak rozpocząć pracę z AI w Node.js, zapraszam do śledzenia mojego #AIEngineerJourney.