W obecnych czasach trudno sobie wyobrazić, że jeszcze kilka lat temu sztuczna inteligencja nie była tak popularna dla zwykłego użytkownika. Wcześniej kojarzyła się głównie z popkulturą: filmami typu
Matrix czy
Terminator oraz książkami science-fiction.
Sztuczna inteligencja istnieje od bardzo dawna, jednak dopiero udostępnienie przez
OpenAI modelu
LLM (Large Language Model) dla wszystkich o nazwie
ChatGPT nakręciło lawinę. Dzięki temu wiele osób zaangażowało się w sztuczną inteligencję, a część osób wypuszczało własne otwarte modele, które można używać na własnym komputerze bez kosztów za użycie API. Obecnie jest niezliczona liczba modeli AI do różnych celów, w różnych wariantach, które możemy użyć.
Właśnie dzięki tej popularności AI język Python stał się ostatnimi latami najpopularniejszym językiem programowania (według
TIOBE Index). Jeszcze w 2018 roku
C++ był od niego popularniejszy, a Java zajmowała pierwsze miejsce.
Co istotne – zajmując się sztuczną inteligencją nie jesteśmy skazani na Pythona. Możemy bez problemu używać innych języków, w tym właśnie
C++.
Dlaczego C++ „pod spodem”?
Większość wydajnych silników do lokalnej inferencji LLM jest napisana właśnie w
C/C++. Python to najczęściej tylko wygodna warstwa użytkowa (bindings).
Popularne silniki do sztucznej inteligencji
Najważniejsze przykłady silników do używania modeli sztucznej inteligencji:
Poza wymienionymi silnikami istnieje jeszcze kilka innych, mniej popularnych backendów GPU pod konkretny sprzęt (np. rozwiązania AMD) – celowo pomijamy je tutaj, żeby w tabeli zostały tylko najważniejsze pozycje.
Zamiast któregoś z powyższych silników pewnie kojarzysz też
Ollamę – to jednak nie jest osobny silnik konkurencyjny wobec llama.cpp, tylko napisana w Go nakładka, która pod spodem sama korzysta z llama.cpp/ggml (dorzucając wygodne zarządzanie modelami i prosty interfejs). Świetna jako gotowe narzędzie end-user, ale nie ma czego linkować do własnego programu w C++ – dlatego nie ma jej w powyższej tabeli.
Uwaga na nazewnictwo: „Llama" (z wielkiej litery) to rodzina modeli językowych firmy Meta. „llama.cpp" (z małej litery, z rozszerzeniem w nazwie) to niezależny, społecznościowy silnik do uruchamiania modeli – nazwany tak, bo pierwotnie służył właśnie do odpalania modeli Llama. Dziś jednak llama.cpp uruchamia praktycznie dowolną architekturę modelu open source (Qwen, Gemma, Mistral, Bielik i wiele innych), nie tylko modele od Mety. Te dwie rzeczy nie mają ze sobą nic wspólnego organizacyjnie – Meta nie ma nic wspólnego z rozwojem llama.cpp.
Jak widać używanie modeli bezpośrednio z poziomu
C++ jest naturalne i często szybsze oraz lżejsze niż przez Pythona, a także wymaga mniej zależności. Dla większości osób zaczynających przygodę z AI w
C++ najlepszym wyborem jest
llama.cpp, dlatego więc ten artykuł będzie się na tym skupiał.
O czym jest ten artykuł
Celem tego artykułu jest pokazanie, że w
C++ nie jest wcale trudno używać sztucznej inteligencji. Modele językowe w każdym języku wymagają tych samych kroków: wyboru biblioteki, pobrania odpowiedniego modelu i jego uruchomienia, interakcji z załadowanym modelem.
Artykuł jest swego rodzaju wstępem, jak zacząć używanie modeli otwarto źródłowych nie mając na ten temat jeszcze doświadczenia. Są tam informacje o pobieraniu modeli, o pewnych zagadnieniach związanych z modelami (chociażby jak czytać ich nazwy), GPU vs CPU itd. Dla osób biegłych w sztucznej inteligencji to są podstawy, więc jeśli chcecie zagłębić używanie AI z poziomu C++ to chyba najszybciej będzie dla Was zagłębić się w
oficjalne przykłady llama.cpp.
Ten artykuł nie jest o tym, jak stworzyć czy trenować własny model. Skupiamy się wyłącznie na używaniu istniejących modeli open source w aplikacji napisanej w
C++.
Zagadnienie sztucznej inteligencji jest bardzo szerokie – jeśli chcesz być w nim naprawdę dobry, wymaga lat nauki i praktyki.
Jeśli szukasz szerszego wprowadzenia do sztucznej inteligencji i klasycznego uczenia maszynowego w C++ aby zrozumieć szerszy kontekst AI w C++ możesz sięgnąć po
AI With Modern C++ (Ayman Alheraki, II edycja) (nie jest to jednak poradnik o budowaniu własnego LLM-a od zera).
Modele to nie encyklopedia!!!
Ważne: Modele językowe to nie encyklopedia ani źródło prawdy. Pod spodem to matematyka i prawdopodobieństwo – model przewiduje najbardziej prawdopodobne kolejne tokeny na podstawie danych, na których był trenowany. Dlatego pojawiają się
halucynacje (pewne siebie, ale fałszywe informacje).
Dodatkowo modele są zwykle dodatkowo trenowane (
alignment /
RLHF /
safety training), żeby spełniać wymogi prawne i „poprawność polityczną”. Skutkuje to czasem podwójnymi standardami – klasyczny przykład: ChatGPT często odmawia dumy z bycia białym, a jednocześnie zachęca do dumy z bycia czarnoskórym (
przykład z X). Warto o tym pamiętać przy krytycznych zastosowaniach.
Jak chcesz z kolei lepiej zrozumieć działanie modeli jak chatGPT polecam artykuły
Jak działa ChatGPT - Trzy Kody lub
Jak działa ChatGPT… | Wojciech Myszka.
Prawdy trzeba poszukiwać, nie bazować wyłącznie na tym co podane na tacy!
Wybór modelu
Istnieje niezliczona liczba modeli do uruchomienia u siebie na komputerze, skupimy się na modelach otwartych (niewymagających płatnego API), które każdy może pobrać i uruchomić. Nie są to modele do wszystkiego, dlatego w Twoim zastosowaniu musisz dobrać model pasujący pod Twoje zastosowanie. Modele mają różne wersje, ale o tym później.
Na sam początek (szczególnie gdy nie masz
mocnego GPU pod AI) warto zacząć od małych modeli:
Powyższa lista to modele
tekstowe (LLM) – tym zajmuje się llama.cpp i ten artykuł. Jeśli szukasz czegoś do generowania
obrazów czy wideo, to inna kategoria modeli (dyfuzyjne, nie LLM) i inne narzędzie – warto zerknąć na
stable-diffusion.cpp, siostrzany projekt zbudowany na tym samym ggml co llama.cpp, obsługujący m.in. Stable Diffusion, Flux czy modele wideo (Wan, LTX). Do generowania
muzyki/mowy lokalnie, też na ggml, istnieje projekt
audio.cpp.
Prywatność modeli uruchamianych lokalnie
Jak ktoś się boi o własną prywatność i przez to chce unikać modeli od Google, Facebook czy chińskich firm, to może być spokojny - sam plik z wagami modelu to statyczne liczby, które fizycznie nie mają jak "zadzwonić do domu". A sam silnik llama.cpp nie wykonuje żadnych połączeń sieciowych poza tymi, o które sam poprosisz (np. pobranie modelu z Hugging Face flagą
-hf
) - nie ma w nim telemetrii ani żadnego "phone-home". Właśnie po to m.in. sięgamy po modele lokalne.
Jedyne, o czym warto pamiętać: to dotyczy samego silnika llama.cpp uruchomionego offline. Jeśli wystawisz
llama-server
na świat (nie tylko na
localhost
) albo użyjesz jakiejś nakładki/aplikacji budowanej na llama.cpp, to już ta konkretna nakładka (a nie sam model czy silnik) mogłaby teoretycznie gdzieś coś wysyłać - więc przy w pełni prywatnym setupie warto sprawdzić też kod tego, co dokładnie owija sam silnik.
Formaty modeli – GGUF, ONNX, wagi
Modele to przede wszystkim
wagi (parametry) + konfiguracja + tokenizer.
Czym są wagi? Wagi (ang. weights) to ogromna liczba parametrów liczbowych – dla współczesnych modeli od setek milionów do bilionów – które model „nauczył się” podczas treningu. To po prostu wielkie tablice liczb zmiennoprzecinkowych, mnożone i sumowane z danymi wejściowymi przy generowaniu odpowiedzi – w nich zakodowana jest cała „wiedza” modelu. Plik modelu (np. .gguf) to w praktyce zrzut właśnie tych liczb na dysk, plus metadane (konfiguracja, tokenizer).
Najważniejsze formaty:
Kwantyzacja to klucz do uruchamiania dużych modeli na zwykłym PC. Polega na zmniejszeniu precyzji wag modelu (np. z 16-bitów do 4 lub 5 bitów). Dzięki temu model zajmuje znacznie mniej pamięci
RAM/
VRAM i działa szybciej, a spadek jakości odpowiedzi jest zwykle niewielki. Najczęściej stosowane kwantyzacje to
Q4_K_M i
Q5_K_M – dobry kompromis między rozmiarem a jakością.
Jak czytać nazwy modeli i co one oznaczają
Nazwy plików GGUF na pierwszy rzut oka wyglądają jak szyfr. Przykład:
Qwen3-1.7B-Instruct-Q4_K_M.gguf
Rozbijmy to na części:
Warto też wiedzieć, że część nowszych modeli to
MoE (
Mixture of Experts) – w nazwie zobaczysz to jako np. „30B-A3B” (30 mld parametrów całkowitych, ale tylko ok. 3 mld aktywowanych na token). Takie modele liczą się szybko jak mały model, ale zajmują na dysku/w pamięci tyle, co model o pełnym (większym) rozmiarze.
Przybliżone wymagania VRAM/RAM dla modelu 8B (same wagi + mały kontekst):
Do tego dochodzi
KV cache (pamięć kontekstu) – im dłuższy kontekst, tym więcej VRAM.
Reguła kciuka: model X B w Q4_K_M zajmuje mniej więcej X/2 GB na dysku + trochę nadmiaru na kontekst.
Czas wykonania i zasoby
Szybkość zależy od wielu czynników: rozmiaru modelu, kwantyzacji,
CPU vs
GPU (karta graficzna), długości promptu i generowanej odpowiedzi.
Orientacyjne wartości (
tokenów na sekundę):
Im większy model i im niższa kwantyzacja (więcej bitów), tym wolniej i więcej pamięci. Dlatego często lepiej wziąć mniejszy, dobrze dostrojony model niż duży, który ledwo się mieści.
Czym jest token?
Skoro już kilka razy padło słowo „token”, warto rozjaśnić o co chodzi: token to najmniejszy kawałek tekstu, na jaki model dzieli wejście/wyjście – to niekoniecznie całe słowo. Może to być całe słowo, jego fragment, pojedynczy znak interpunkcyjny albo nawet kawałek słowa w innym języku. Model nie „czyta” liter ani słów – zamienia tekst na ciąg liczb (tokenów) za pomocą swojego tokenizera, przetwarza je, i tak samo token po tokenie generuje odpowiedź. Dlatego szybkość podaje się w tokenach na sekundę, a nie znakach czy słowach na sekundę, a rozmiar kontekstu (o którym więcej w dalszej części) też liczy się w tokenach, nie znakach.
Instalacja llama.cpp
Najbezpieczniej zainstalować llama.cpp ręcznie – mamy wtedy największą kontrolę nad optymalizacjami (CUDA, Vulkan, Metal).
Wspólna część dla kilku systemów:
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
Pinowanie wersji: llama.cpp nie taguje wydań jako tagów na githubie „v0.4.0" czy podobnie – używa numerów kolejnych buildów w stylu
bNNNNN
(np.
b10944
). Projekt rozwija się bardzo szybko (potrafi być kilka–kilkanaście buildów dziennie), więc jeśli chcesz pinować konkretną, stabilną wersję zamiast śledzić
master
, sprawdź najnowszy tag na
stronie wydań albo komendą:
git tag --sort=-creatordate | head -n 1
i dopiero ten konkretny tag zrób checkout, np.
git checkout b10944
. Sam numer wersji w pliku CMake (bywa widoczny jako coś w rodzaju „0.4.0-dev") to tylko wewnętrzny numerek projektu do celów pakietowania (np. vcpkg) – to nie jest tag, którego można użyć do
git checkout
.
Statyczny build i bez testów: do każdego z poniższych poleceń warto dodać
-DBUILD_SHARED_LIBS=OFF
(link statyczny – gotowy program nie ciągnie za sobą plików
.so/
.dll, które trzeba by osobno kopiować/wskazywać) oraz
-DLLAMA_BUILD_TESTS=OFF
(uwaga: to własna flaga llama.cpp, a nie standardowe
BUILD_TESTING z CMake – to drugie nic by tu nie dało). Krótszy, czystszy build, bez wpływu na licencję (llama.cpp jest na MIT niezależnie od sposobu linkowania).
Linux
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF
cmake --build build -j$(nproc)
Z GPU (NVIDIA):
cmake -B build -DGGML_CUDA=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF
cmake --build build -j$(nproc)
Windows
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF
cmake --build build --config Release
Z GPU:
cmake -B build -DGGML_CUDA=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF
cmake --build build --config Release
Gotowe binarki:
winget install llama.cpp
(głównie CPU).
Po zbudowaniu: Linux i Windows
Po zbudowaniu w katalogu
build/bin
pojawią się m.in.:
Zmiana w nazewnictwie narzędzi (świeża sprawa): historycznie
llama-cli
było jednym narzędziem łączącym tryb czatu i „surowego” dokańczania tekstu. W międzyczasie projekt to rozdzielił: stare narzędzie (z flagami, które można znaleźć w starszych tutorialach, np.
-cnv
) zostało przemianowane na
llama-completion
, a nazwa
llama-cli
trafiła do nowego, przepisanego od zera narzędzia, które w tym artykule opisujemy. Jeśli więc trafisz na starszy poradnik i coś Ci się nie zgadza z opcjami – to najpewniej dlatego.
Warto wiedzieć: projekt ma też oficjalny instalator (
llama.app) z jednym binarnym
llama
i podkomendami – zamiast
llama-cli
można tam użyć
llama cli
, analogicznie
llama serve
zamiast
llama-server
. Dodatkowo od lipca 2026
llama-cli
wewnętrznie odpala w tle
llama-server
i łączy się z nim jak klient – bez znaczenia dla użytkownika, ale gdybyś się zastanawiał, skąd taka architektura.
W dalszej części artykułu skupimy się na
llama-cli
aby pokazać szybkie użycie, następnie na
libllama
aby pokazać jak coś podobnego zrobić w kodzie C++, z kolei
llama-server
najwyżej jako deser. Idea serwera jest taka, że nie używamy biblioteki, tylko łączymy się z serwerem po
REST API. Zaleta tego rozwiązania jest to, że ten serwer może faktycznie być na komputerze z dobrym GPU.
Inne sposoby integracji z projektem
Pierwsze uruchomienie – pobranie modelu i prosta komenda
Na start wybieramy mały model (np.
Qwen3-1.7B), żeby działał sensownie nawet bez GPU.
Pobranie modelu:# Najwygodniejszy sposób – bezpośrednio przez llama.cpp
llama-cli -hf ggml-org/Qwen3-1.7B-GGUF:Q4_K_M
Model zostanie pobrany do cache
Hugging Face (zazwyczaj
~/.cache/huggingface
).
Uwaga na organizację w repozytorium: sam
Qwen (twórca modelu) publikuje na
Hugging Face oryginalne wagi, ale zwykle
nie wagi w formacie GGUF. Gotowe GGUF-y dla modeli Qwen znajdziesz najczęściej pod organizacją
ggml-org
(oficjalny użytkownik projektu llama.cpp) albo u niezależnych osób konwertujących modele, jak
bartowski
czy
unsloth
. Jeśli komenda
-hf
zgłasza, że nie znaleziono plików GGUF w repozytorium – sprawdź, czy nie próbujesz pobrać z organizacji, która publikuje tylko oryginalne (niekonwertowane) wagi.
Najprostsze uruchomienie:llama-cli -m Qwen3-1.7B-Q4_K_M.gguf -p "Przetłumacz na polski: The weather is nice today."
Współczesny
llama-cli
ma sensowne wartości domyślne, więc powyższa komenda wystarczy: sam wykryje, ile ma wątków CPU użyć, sam spróbuje dobrać podział CPU/GPU (patrz niżej), a kontekst domyślnie ustawi na maksimum, z jakim model był trenowany.
Jeśli zamiast tego jawnie wymusisz pracę tylko na CPU flagą
-ngl 0
na buildzie skompilowanym bez wsparcia GPU, możesz zobaczyć komunikat:
warning: no usable GPU found, --gpu-layers option will be ignored
warning: one possible reason is that llama.cpp was compiled without GPU support
Komunikat ten pojawia się zawsze, gdy jawnie podasz flagę
-ngl
/
--gpu-layers
(niezależnie od wartości – nawet
0
!) na buildzie bez wsparcia GPU – bo sprawdzana jest tylko możliwość builda, a nie konkretna liczba. To nieszkodliwe ostrzeżenie: model i tak policzy się na CPU. Żeby go uniknąć, po prostu nie podawaj
-ngl
wcale (skoro i tak nie masz GPU, wartość domyślna „auto" sama sobie z tym poradzi bez zbędnego ostrzeżenia). Gdy któregoś dnia zechcesz faktycznie używać GPU, przebuduj z flagą
-DGGML_CUDA=ON
(lub odpowiednikiem dla Twojego backendu).
Przykład z dodatkowymi, jawnymi parametrami (przydatne, gdy chcesz mieć pełną kontrolę zamiast polegać na automatyce):
llama-cli -m Qwen3-1.7B-Q4_K_M.gguf \
-ngl 0 \
-t 6 \
-c 2048 \
-n 128 \
--temp 0.3 \
-p "Przetłumacz na polski: The weather is nice today."
Poniżej wyjaśnienie użytych argumentów:
llama z poziomu kodu C++
llama.cpp oficjalnie udostępnia głównie C API. Nie ma oficjalnego, grubego wrappera C++ w głównym repozytorium. W razie potrzeby łatwo owinąć go we własne klasy C++.
Oficjalne przykłady z repozytorium llama.cpp warte obejrzenia:
Kompilacja własnego programu (CMake)
Żeby użyć
libllama
we własnym, niezależnym projekcie, najpierw zbuduj i zainstaluj llama.cpp wg wcześniejszych instrukcji, następnie we własnym projekcie wystarczy taki
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.12)
project(moj-przyklad)
find_package(Llama REQUIRED)
find_package(Threads REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE llama ggml::all Threads::Threads)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_23)
i skonfigurowanie go tak, żeby
find_package
znalazł zainstalowane wcześniej llama.cpp:
cmake -S . -B build -DCMAKE_PREFIX_PATH=/ścieżka/do/llama.cpp/inst/lib/cmake
cmake --build build
Gdy używasz akceleracji sprzętowej (CUDA, Metal, Vulkan itd.), odpowiednie zależności zostaną wyszukane automatycznie – nie trzeba nic dodatkowo konfigurować w projekcie, który korzysta z zainstalowanego llama.cpp.
Przykład 1 – prosty (greedy sampler)
#include "llama.h"
#include <print>
#include <cstdio>
#include <string>
#include <vector>
int main( int argc, char * * argv ) {
if( argc < 3 ) {
std::print( stderr, "Użycie: {} model.gguf \"Tekst po angielsku\"\n", argv[ 0 ] );
return 1;
}
const char * model_path = argv[ 1 ];
std::string text_en = argv[ 2 ];
std::string prompt = "Translate the following English text to Polish. "
"Return only the translation, nothing else.\n\n"
"English: " + text_en + "\n\nPolish:";
ggml_backend_load_all();
llama_model_params model_params = llama_model_default_params();
model_params.n_gpu_layers = 0; llama_model * model = llama_model_load_from_file( model_path, model_params );
if( !model ) {
std::print( stderr, "Nie udało się załadować modelu\n" );
return 1;
}
const llama_vocab * vocab = llama_model_get_vocab( model );
llama_context_params ctx_params = llama_context_default_params();
ctx_params.n_ctx = 2048; ctx_params.n_batch = 512; llama_context * ctx = llama_init_from_model( model, ctx_params );
if( !ctx ) {
std::print( stderr, "Nie udało się utworzyć kontekstu\n" );
llama_model_free( model );
return 1;
}
const int n_prompt = - llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
nullptr, 0,
true, true );
std::vector < llama_token > prompt_tokens( n_prompt );
llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
prompt_tokens.data(), prompt_tokens.size(),
true, true
);
auto sparams = llama_sampler_chain_default_params();
llama_sampler * smpl = llama_sampler_chain_init( sparams );
llama_sampler_chain_add( smpl, llama_sampler_init_greedy() );
llama_batch batch = llama_batch_get_one( prompt_tokens.data(), prompt_tokens.size() );
if( llama_decode( ctx, batch ) ) {
std::print( stderr, "Błąd dekodowania promptu\n" );
return 1;
}
constexpr int max_tokens = 256;
for( int i = 0; i < max_tokens; ++i ) {
llama_token new_token = llama_sampler_sample( smpl, ctx, - 1 );
if( llama_vocab_is_eog( vocab, new_token ) )
break;
char buf[ 256 ];
int n = llama_token_to_piece(
vocab, new_token, buf, sizeof( buf ),
0, true );
if( n > 0 ) {
std::print( "{}", std::string_view( buf, n ) );
std::fflush( stdout );
}
batch = llama_batch_get_one( & new_token, 1 );
if( llama_decode( ctx, batch ) )
break;
}
std::println();
llama_sampler_free( smpl );
llama_free( ctx );
llama_model_free( model );
}
Dla powyższego wydruk może być (pomijam logi z uruchamiania modelu):
$ ./moj-przyklad /home/agh/.cache/llama.cpp/ggml-org_Qwen3-1.7B-GGUF_Qwen3-1.7B-Q4_K_M.gguf "The weather is nice today"
...
Dziś pogoda jest piękna
To be more specific, the translation should be in the form of a sentence that is in the past tense, and the verb should be in the past tense. The translation should be in the form of a sentence that is in the past tense, and the verb should be in the past tense.
The English sentence is: The weather is nice today.
The Polish sentence should be in the past tense, and the verb should be in the past tense.
~llama_context: CPU compute buffer size is 304.7520 MiB, matches expectation of 304.7520 MiB
Przykład 2 – zaawansowany (temperatura + top-p)
Temperatura
Zanim przejdziemy do kodu, krótkie wyjaśnienie: temperatura to parametr sterujący losowością wyboru kolejnego tokenu. Model dla każdego możliwego kolejnego tokenu wylicza jego prawdopodobieństwo; temperatura „spłaszcza" albo „zaostrza" te prawdopodobieństwa przed losowaniem. Temperatura bliska 0 oznacza niemal zawsze wybór najbardziej prawdopodobnego tokenu (podobnie jak greedy sampler z przykładu 1. – przewidywalne, ale czasem monotonne odpowiedzi). Wyższa temperatura (np. 0.8–1.2) daje więcej różnorodności i „kreatywności", kosztem większego ryzyka błędów czy bzdur.
Nucleus sampling
Top-p (nucleus sampling) dodatkowo ogranicza pulę tokenów, spośród których w ogóle losujemy, do najmniejszego zbioru, którego skumulowane prawdopodobieństwo przekracza podane p – dzięki temu nawet przy wyższej temperaturze model nie wylosuje kompletnie absurdalnego, mało prawdopodobnego tokenu.
Poniżej pełny przykład (różni się od Przykładu 1 tylko sekcją próbkowania):
#include "llama.h"
#include <print>
#include <cstdio>
#include <string>
#include <vector>
int main( int argc, char * * argv ) {
if( argc < 3 ) {
std::print( stderr, "Użycie: {} model.gguf \"Tekst po angielsku\"\n", argv[ 0 ] );
return 1;
}
const char * model_path = argv[ 1 ];
std::string text_en = argv[ 2 ];
std::string prompt = "Translate the following English text to Polish. "
"Return only the translation, nothing else.\n\n"
"English: " + text_en + "\n\nPolish:";
ggml_backend_load_all();
llama_model_params model_params = llama_model_default_params();
model_params.n_gpu_layers = 0;
llama_model * model = llama_model_load_from_file( model_path, model_params );
if( !model ) {
std::print( stderr, "Nie udało się załadować modelu\n" );
return 1;
}
const llama_vocab * vocab = llama_model_get_vocab( model );
llama_context_params ctx_params = llama_context_default_params();
ctx_params.n_ctx = 2048;
ctx_params.n_batch = 512;
llama_context * ctx = llama_init_from_model( model, ctx_params );
if( !ctx ) {
std::print( stderr, "Nie udało się utworzyć kontekstu\n" );
llama_model_free( model );
return 1;
}
const int n_prompt = - llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
nullptr, 0,
true, true
);
std::vector < llama_token > prompt_tokens( n_prompt );
llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
prompt_tokens.data(), prompt_tokens.size(),
true, true
);
auto sparams = llama_sampler_chain_default_params();
llama_sampler * smpl = llama_sampler_chain_init( sparams );
llama_sampler_chain_add( smpl, llama_sampler_init_temp( 0.3f ) ); llama_sampler_chain_add( smpl, llama_sampler_init_top_p( 0.9f, 1 ) ); llama_sampler_chain_add( smpl, llama_sampler_init_dist( LLAMA_DEFAULT_SEED ) ); llama_batch batch = llama_batch_get_one( prompt_tokens.data(), prompt_tokens.size() );
if( llama_decode( ctx, batch ) ) {
std::print( stderr, "Błąd dekodowania promptu\n" );
return 1;
}
constexpr int max_tokens = 256;
for( int i = 0; i < max_tokens; ++i ) {
llama_token new_token = llama_sampler_sample( smpl, ctx, - 1 );
if( llama_vocab_is_eog( vocab, new_token ) ) break;
char buf[ 256 ];
int n = llama_token_to_piece( vocab, new_token, buf, sizeof( buf ), 0, true );
if( n > 0 ) {
std::print( "{}", std::string_view( buf, n ) );
std::fflush( stdout );
}
batch = llama_batch_get_one( & new_token, 1 );
if( llama_decode( ctx, batch ) )
break;
}
std::println();
llama_sampler_free( smpl );
llama_free( ctx );
llama_model_free( model );
}
Dla porównania, dokładnie ten sam efekt (te same parametry próbkowania) z poziomu samego
llama-cli
, bez pisania kodu w ogóle:
llama-cli -m Qwen3-1.7B-Q4_K_M.gguf --temp 0.3 --top-p 0.9 -p "Przetłumacz na polski: The weather is nice today."
Przykład 3 – ładowanie tekstu z pliku
Tu drobna, ale ważna uwaga: „ładowanie pliku” w kontekście modeli językowych nie oznacza żadnej specjalnej operacji – to po prostu wczytanie zawartości pliku jako zwykłego tekstu i doklejenie go do reszty promptu, tak jak w przykładzie niżej. Model nie wie i nie musi wiedzieć, że tekst pochodzi z pliku.
#include "llama.h"
#include <print>
#include <cstdio>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
std::string load_file( const char * path ) {
std::ifstream f( path );
if( !f ) return { };
std::stringstream ss;
ss << f.rdbuf();
return ss.str();
}
int main( int argc, char * * argv ) {
if( argc < 3 ) {
std::print( stderr, "Użycie: {} model.gguf plik.txt\n", argv[ 0 ] );
return 1;
}
const char * model_path = argv[ 1 ];
std::string text = load_file( argv[ 2 ] );
if( text.empty() ) {
std::print( stderr, "Nie udało się wczytać pliku\n" );
return 1;
}
std::string prompt = "Podsumuj poniższy tekst w 3-4 zdaniach po polsku:\n\n" + text;
ggml_backend_load_all();
llama_model_params model_params = llama_model_default_params();
model_params.n_gpu_layers = 0;
llama_model * model = llama_model_load_from_file( model_path, model_params );
if( !model ) {
std::print( stderr, "Nie udało się załadować modelu\n" );
return 1;
}
const llama_vocab * vocab = llama_model_get_vocab( model );
llama_context_params ctx_params = llama_context_default_params();
ctx_params.n_ctx = 4096; ctx_params.n_batch = 512;
llama_context * ctx = llama_init_from_model( model, ctx_params );
if( !ctx ) {
std::print( stderr, "Nie udało się utworzyć kontekstu\n" );
llama_model_free( model );
return 1;
}
const int n_prompt = - llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
nullptr, 0,
true, true
);
std::vector < llama_token > prompt_tokens( n_prompt );
llama_tokenize(
vocab, prompt.c_str(), prompt.size(),
prompt_tokens.data(), prompt_tokens.size(),
true, true
);
auto sparams = llama_sampler_chain_default_params();
llama_sampler * smpl = llama_sampler_chain_init( sparams );
llama_sampler_chain_add( smpl, llama_sampler_init_greedy() );
llama_batch batch = llama_batch_get_one( prompt_tokens.data(), prompt_tokens.size() );
if( llama_decode( ctx, batch ) ) {
std::print( stderr, "Błąd dekodowania promptu\n" );
return 1;
}
constexpr int max_tokens = 256;
for( int i = 0; i < max_tokens; ++i ) {
llama_token new_token = llama_sampler_sample( smpl, ctx, - 1 );
if( llama_vocab_is_eog( vocab, new_token ) ) break;
char buf[ 256 ];
int n = llama_token_to_piece( vocab, new_token, buf, sizeof( buf ), 0, true );
if( n > 0 ) {
std::print( "{}", std::string_view( buf, n ) );
std::fflush( stdout );
}
batch = llama_batch_get_one( & new_token, 1 );
if( llama_decode( ctx, batch ) ) break;
}
std::println();
llama_sampler_free( smpl );
llama_free( ctx );
llama_model_free( model );
}
Analogiczne wywołanie z poziomu
llama-cli
– instrukcję wpisujemy jako prompt systemowy, a plik podajemy flagą
-f
:
llama-cli -m Qwen3-1.7B-Q4_K_M.gguf -sys "Podsumuj poniższy tekst w 3-4 zdaniach po polsku." -f artykul.txt
Przykład z obrazkiem (OCR / vision)
llama.cpp wspiera modele multimodalne (przyjmujące jako wejście obraz, dźwięk czy nawet wideo) poprzez
libmtmd i pliki
mmproj
(projektor multimodalny – osobny mały model, który zamienia obraz na coś, co „rozumie” główny model językowy). Pełny kod C++ z libmtmd jest dość rozbudowany, dlatego pozwolę sobie na użycie tylko
llama-cli
, które obsługuje obrazy
natywnie, bez żadnego dodatkowego narzędzia, prosto z Hugging Face (plik mmproj zostanie pobrany automatycznie, jeśli jest dostępny w repozytorium):
llama-cli -hf ggml-org/gemma-3-4b-it-GGUF --image faktura.jpg -p "Extract all text from this image."
Obok tego wciąż istnieje (i buduje się automatycznie razem ze wszystkim innym, bez żadnej dodatkowej flagi) dedykowane narzędzie
llama-mtmd-cli
, które działa identycznie:
llama-mtmd-cli -m model-vl.gguf --mmproj mmproj.gguf --image zdjecie.jpg -p "Odczytaj cały tekst z tego obrazka."
Jeśli trafisz na starszy poradnik odwołujący się do narzędzi typu
llama-llava-cli
,
llama-gemma3-cli
czy
llama-minicpmv-cli
– to stare, osobne narzędzia dla pojedynczych rodzin modeli, które zostały zastąpione właśnie przez
llama-mtmd-cli
(a dziś także przez samo
llama-cli
).
Pobieranie modeli z poziomu C++
Wcześniej zakładaliśmy, że plik .gguf już leży na dysku. W praktyce chcemy jednak, żeby nasz program sam sprowadził go z Hugging Face przy pierwszym uruchomieniu. Sprawdźmy, co na ten temat mówi aktualne API llama.cpp (stan na wrzesień 2026), bo krążące w sieci przykłady bywają nieaktualne lub po prostu błędne.
Czego nie robi publiczne API
Publiczny nagłówek
llama.h udostępnia do wczytywania modelu tylko jedną, prostą funkcję:
LLAMA_API struct llama_model * llama_model_load_from_file(
const char * path_model,
struct llama_model_params params );
Ona zawsze oczekuje
lokalnej ścieżki do pliku. Cała logika pobierania z Hugging Face (parsowanie
--hf-repo/
--hf-file, zapytania do API HF, obsługa tokenu, cache w
~/.cache/llama.cpp) siedzi w warstwie
common/ (pliki
common/arg.cpp,
common/download.h,
common/hf-cache.h), z której korzystają narzędzia
llama-cli i
llama-server - to
nie jest część publicznego, stabilnego API biblioteki
libllama.
Mamy więc trzy realne opcje.
Opcja 1: pobranie z wyprzedzeniem
Najprościej po prostu ściągnąć model przed uruchomieniem programu:
llama-cli -hf ggml-org/Qwen3-1.7B-GGUF
a w kodzie C++ podać już tylko lokalną ścieżkę do
.gguf. Zero dodatkowego kodu, zero zależności - kosztem tego, że instalacja programu przestaje być "jednoklikowa" dla użytkownika końcowego.
Skoro kod C++ potrzebuje konkretnej
ścieżki do pliku, a nie samej nazwy repozytorium - skąd ją wziąć? Najprościej dodać do pobierania flagę
--verbose
:
llama-cli -hf ggml-org/Qwen3-1.7B-GGUF --verbose
Wśród (sporej ilości) dodatkowych logów pojawi się linia z pełną lokalną ścieżką do pliku w cache'u (czy to świeżo pobranego, czy już wcześniej pobranego i tylko odczytanego z cache'a) - tę ścieżkę wklejasz potem do
llama_model_load_from_file
.
Opcja 2: własne pobieranie przez HTTP (zalecane)
Jeśli chcemy, żeby program sam sprowadził model, najbezpieczniej użyć niezależnej, lekkiej biblioteki HTTP i samodzielnie zapisać plik na dysk, a dopiero potem oddać ścieżkę do
llama_model_load_from_file. Poniżej przykład z header-only
cpp-httplib.
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h" #include "llama.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
bool download_file( const std::string & url, const std::string & output_path ) {
size_t pos = url.find( "://" );
if( pos == std::string::npos ) return false;
pos = url.find( '/', pos + 3 );
if( pos == std::string::npos ) return false;
std::string host = url.substr( 0, pos ); std::string path = url.substr( pos ); httplib::Client cli( host ); cli.set_follow_location( true ); cli.set_connection_timeout( 30 );
cli.set_read_timeout( 300 ); std::ofstream out( output_path, std::ios::binary );
if( !out ) {
std::cerr << "Nie mozna otworzyc pliku docelowego: " << output_path << "\n";
return false;
}
std::cout << "Pobieram: " << url << "\n";
auto res = cli.Get( path.c_str(),
[ & ]( const char * data, size_t len ) {
out.write( data, static_cast < std::streamsize >( len ) );
return true; } );
out.close();
if( !res || res->status != 200 ) {
std::cerr << "Blad pobierania, status: " <<( res ? res->status: - 1 ) << "\n";
std::filesystem::remove( output_path );
return false;
}
std::cout << "Zapisano do: " << output_path << "\n";
return true;
}
int main() {
const std::string model_url =
"https://huggingface.co/ggml-org/Qwen3-1.7B-GGUF/resolve/main/Qwen3-1.7B-Q4_K_M.gguf";
const std::string local_path = "Qwen3-1.7B-Q4_K_M.gguf";
if( !std::filesystem::exists( local_path ) ) {
if( !download_file( model_url, local_path ) ) {
return 1;
}
}
ggml_backend_load_all();
llama_model_params model_params = llama_model_default_params();
model_params.n_gpu_layers = 0;
llama_model * model = llama_model_load_from_file( local_path.c_str(), model_params );
if( !model ) {
std::cerr << "Nie udalo sie zaladowac modelu\n";
return 1;
}
std::cout << "Model zaladowany pomyslnie!\n";
llama_model_free( model );
return 0;
}
Do zbudowania potrzebujesz OpenSSL:
g++ -std=c++23 pobierz_i_uruchom.cpp -I. -lssl -lcrypto -pthread -lllama -lggml -o app
Opcja 3: funkcje wewnętrzne z common/ (nieoficjalne)
Warstwa
common/ ma własną, gotową logikę pobierania z HF (
common_download_model,
common_get_hf_file), tej samej, której używa
llama-cli -hf. Można ją teoretycznie zlinkować do własnego programu, ale
to nie jest stabilne publiczne API - w samym repozytorium zmieniało się ono kilkukrotnie w ciągu ostatniego roku (m.in. pola
hf_repo/
hf_file przeniosły się z
common_params do zagnieżdżonej struktury
common_params.model, a
common_init_from_params zmieniało typ zwracany). Jeśli mimo to chcesz spróbować, szkielet wygląda mniej więcej tak (sprawdź dokładne nazwy pól w
common/common.h swojej wersji, bo mogą się różnić):
#include "llama.h"
#include "common.h" int main() {
common_params params;
params.model.hf_repo = "ggml-org/Qwen3-1.7B-GGUF"; params.model.hf_file = "Qwen3-1.7B-Q4_K_M.gguf"; common_init();
llama_backend_init();
auto llama_init = common_init_from_params( params ); if( !llama_init ) {
return 1;
}
llama_model * model = llama_init->model();
llama_context * ctx = llama_init->context();
return 0;
}
Pobieranie z HF działa dziś bez dodatkowych flag – projekt przeszedł z libcurl na własny, wbudowany klient HTTP (flaga
-DLLAMA_CURL=ON
jest już przestarzała i zostanie zignorowana), potrzebne jest tylko OpenSSL w systemie. Katalog
common/ trzeba dołączyć do CMakeLists.txt:
add_subdirectory(path/to/llama.cpp)
target_link_libraries(twoja_aplikacja PRIVATE
llama
common
ggml
)
Diagnostyka GPU: CPU vs GPU, VRAM, czy w ogóle działa
Kluczowy parametr do przełączania między CPU a GPU to
-ngl
(lub w kodzie
model_params.n_gpu_layers
):
Jeśli model nie mieści się w VRAM, program zgłasza błąd braku pamięci (out-of-memory) – to nie jest klasyczny wyjątek C++, tylko błąd zgłaszany przez backend.
Czy model faktycznie działa na GPU? Po uruchomieniu zawsze warto zerknąć w logi startowe – szukaj linii w stylu:
offloading 32/32 layers to GPU
total VRAM used: 5842 MB
Jeśli widzisz
offloading 0 layers
, model liczy się wyłącznie na CPU.
Sprawdzenie dostępnych urządzeń i pamięci:llama-cli --list-devices
wypisze dostępne urządzenia GPU wraz z ilością VRAM. Dodatkowo w systemie (dla kart NVIDIA):
nvidia-smi
W kolumnie „Memory-Usage” zobaczysz całkowitą ilość VRAM oraz aktualne zużycie. Przydatne warianty:
nvidia-smi -L
nvidia-smi --query-gpu=name,memory.total,memory.free,memory.used --format=csv
Na Linuxie (np. Kubuntu) często pojawia się problem rozbieżności między wersją sterownika NVIDIA a jądrem systemu – po aktualizacji jądra karta przestaje działać, dopóki nie zainstaluje się pasującego sterownika. Na Windowsie podobne problemy zdarzają się rzadziej, ale też warto pamiętać o aktualizacji sterowników.
Limitowanie zasobów (wątki, kontekst)
Liczba wątków CPU:
-t N # wątki generowania
-tb N # wątki przetwarzania promptu (batch)
Jak już wspomniano, domyślnie llama.cpp samo wykrywa liczbę rdzeni, więc ręczne ustawianie
-t
jest potrzebne głównie, gdy chcesz nadpisać tę automatykę. Zalecenia, gdy jednak chcesz ustawiać ręcznie:
W kodzie:
ctx_params.n_threads = 8;
ctx_params.n_threads_batch = 8;
Rozmiar kontekstu (
-c
/
n_ctx
) mocno wpływa na zużycie VRAM. Pamiętaj, że – jak wspomniano wcześniej – domyślnie (albo przy
-c 0
) llama.cpp weźmie maksymalny kontekst, z jakim model był trenowany, co dla nowszych modeli potrafi oznaczać dziesiątki albo i więcej tysięcy tokenów. Nie ustawiaj więc na sztywno bardzo dużej wartości „na zapas”, jeśli faktycznie tyle nie potrzebujesz – im większy kontekst, tym więcej pamięci zajmuje
KV cache.
Użycie llama-server
W praktyce często wygodniej odpalić
llama-server
i komunikować się z nim przez zwykłe HTTP (OpenAI-compatible API), zamiast linkować
libllama
bezpośrednio do swojego programu:
llama-server -m Qwen3-1.7B-Q4_K_M.gguf --port 8080
Serwer wystawia m.in. endpoint
/v1/chat/completions
, zgodny z formatem API OpenAI. Do wysłania zapytania z poziomu C++ wystarczy najprostsza, multiplatformowa, nagłówkowa (header-only) biblioteka HTTP – dobrym wyborem jest
cpp-httplib. Wystarczy pobrać jeden plik
httplib.h
i dołączyć go do projektu, bez żadnego dodatkowego linkowania:
#include "httplib.h"
#include <print>
#include <string>
int main() {
httplib::Client cli( "http://localhost:8080" );
std::string body = R"({
"model": "qwen3-1.7b",
"messages": [
{"role": "user", "content": "Przetłumacz na polski: The weather is nice today."}
]
})";
httplib::Headers headers = {
{ "Authorization", "Bearer no-key" } };
auto res = cli.Post( "/v1/chat/completions", headers, body, "application/json" );
if( res ) {
std::println( "Status: {}", res->status );
std::println( "Odpowiedź: {}", res->body );
} else {
std::println( "Błąd żądania: {}", httplib::to_string( res.error() ) );
}
}
Odpowiedź przychodzi jako JSON (dokładnie w formacie
API OpenAI) – w realnym kodzie warto go sparsować jakąś biblioteką JSON (np. nlohmann/json), zamiast wypisywać surowy tekst jak w powyższym minimalnym przykładzie. Zaletą podejścia przez serwer jest to, że serwer może stać na zupełnie innym komputerze – np. tym z dobrym GPU – a Twój program w
C++ łączy się z nim tylko przez sieć.
OpenRouter – darmowa alternatywa przy słabym sprzęcie
Gdy lokalne GPU jest za słabe albo chcesz szybko przetestować mocniejsze modele, warto zajrzeć na
OpenRouter.
Platforma agreguje wiele modeli (w tym darmowe warianty oznaczone
:free
). Masz jeden klucz API w stylu OpenAI i możesz przełączać się między modelami.
Darmowy tier pozwala na pewną liczbę requestów dziennie (limity się zmieniają, ale zwykle jest to dziesiątki–setki requestów). To świetna opcja do prototypowania albo gdy lokalny sprzęt nie wyrabia z większymi modelami.
Skoro OpenRouter też wystawia API zgodne z formatem OpenAI, kod z sekcji o
llama-server
wystarczy zmienić w dwóch miejscach: adresie serwera oraz nagłówku autoryzacji:
#include "httplib.h"
#include <print>
#include <string>
int main() {
httplib::Client cli( "https://openrouter.ai" );
std::string body = R"({
"model": "meta-llama/llama-3.3-70b-instruct:free",
"messages": [
{"role": "user", "content": "Przetłumacz na polski: The weather is nice today."}
]
})";
httplib::Headers headers = {
{ "Authorization", "Bearer TWOJ_KLUCZ_API" } };
auto res = cli.Post( "/api/v1/chat/completions", headers, body, "application/json" );
if( res ) {
std::println( "Status: {}", res->status );
std::println( "Odpowiedź: {}", res->body );
} else {
std::println( "Błąd żądania: {}", httplib::to_string( res.error() ) );
}
}
Łącząc się z OpenRouter (albo dowolnym zewnętrznym API) przez HTTPS,
cpp-httplib
potrzebuje do działania OpenSSL – w przeciwieństwie do rozmowy z własnym, lokalnym
llama-server
po zwykłym HTTP, gdzie nie jest to wymagane. Dodaj więc
#define CPPHTTPLIB_OPENSSL_SUPPORT
przed
#include "httplib.h"
i zlinkuj z OpenSSL.
Nie musisz niczego instalować lokalnie ani pobierać żadnego modelu – to duża zaleta tego podejścia przy słabszym sprzęcie.
Hugging Face – centrum modeli + tokeny
Hugging Face to w wielkim skrócie coś w rodzaju „GitHuba dla modeli AI” – platforma, na której twórcy modeli, zbiorów danych i narzędzi do ich uruchamiania publikują swoją pracę, a każdy inny może ją stamtąd pobrać. Prawie wszystkie otwarte modele, o których mowa w tym artykule (Qwen, Bielik, Gemma, gotowe GGUF-y itd.), znajdziesz właśnie tam.
Aby pobierać modele (szczególnie te „gated” – wymagające akceptacji licencji), potrzebny będzie token dostępu:
Token możesz ustawić na kilka sposobów:
llama.cpp potrafi pobierać modele bezpośrednio (pamiętaj o właściwej organizacji w nazwie repozytorium, o czym była mowa wcześniej):
llama-cli -hf ggml-org/Qwen3-8B-GGUF:Q4_K_M
Niektóre modele wymagają zaakceptowania licencji, wtedy należy wejść przez
Hugging Face na dany model i zaakceptować licencje, dopiero wtedy nasz token będzie działał z tym modelem.
Czemu model „nie działa” albo daje słabe wyniki?
Czym jest szablon czatu (chat template)? Model językowy „pod spodem” nie widzi ładnie rozdzielonych ról „system / user / assistant” – widzi jeden ciąg tekstu (tokenów). Szablon czatu to zestaw reguł (dziś zwykle zapisany jako szablon Jinja, wbudowany w metadane pliku GGUF), który mówi, jak taką listę wiadomości zamienić na dokładnie ten format tekstu ze specjalnymi znacznikami (np.
<|im_start|>user
...
<|im_end|>
), na jakim model był trenowany. Użycie złego szablonu (albo żadnego – czyli wrzucenie surowego tekstu bez żadnych znaczników) zwykle skutkuje gorszymi, bełkotliwymi albo urwanymi odpowiedziami, bo model „nie rozpoznaje” struktury rozmowy, do której przywykł podczas treningu. Dobra wiadomość:
llama-cli
i
llama-server
robią to automatycznie (flaga
--jinja
, domyślnie włączona) na podstawie szablonu zapisanego w samym pliku GGUF – w większości przypadków nie musisz się tym w ogóle zajmować.
Dziwne błędy i nietypowe zachowania lokalnych modeli
Poza zwyczajnie „słabymi” odpowiedziami (patrz sekcja wyżej) lokalne modele potrafią czasem zachowywać się w sposób, który na pierwszy rzut oka wygląda jak błąd programu, a w rzeczywistości jest charakterystyczną cechą tego, jak działają modele językowe. Kilka najczęstszych przykładów:
To nie jest wyczerpująca lista – lokalne modele, szczególnie mocno skwantyzowane, potrafią zaskakiwać na wiele sposobów. Jeśli coś wygląda podejrzanie, dobrym pierwszym krokiem jest sprawdzenie tego samego promptu na innym, większym modelu (albo przez
OpenRouter) – jeśli tam wynik jest sensowny, problem najpewniej leży po stronie konfiguracji lub kwantyzacji, a nie samego pytania.
Licencja - szara strefa
Prawa autorskie i licencje modeli to temat, w którym łatwo o nieporozumienie. Trzeba rozróżnić dwie rzeczy: licencję samego
kodu (np. llama.cpp jest na licencji MIT) oraz licencję
wag modelu – a to zupełnie osobna sprawa, ustalana przez twórcę danego modelu.
Ważne: „otwarty model” (open weight, czyli taki, którego wagi można pobrać)
nie zawsze znaczy „open source” w ścisłym sensie (czyli licencja zatwierdzona przez OSI, jak MIT czy Apache 2.0). Klasyczny przykład to modele
Llama od Mety – ich licencja pozwala na użycie komercyjne, ale zawiera m.in. klauzulę, że firmy mające ponad 700 milionów aktywnych użytkowników miesięcznie muszą wystąpić o osobną licencję do Mety, a także narzuca „Acceptable Use Policy” (politykę dopuszczalnego użycia) i zakaz używania wyników modelu do trenowania konkurencyjnych modeli. To zupełnie inne zasady niż przy prawdziwie otwartych licencjach.
Co warto sprawdzić przed użyciem modelu (szczególnie komercyjnym):
Przy zastosowaniach komercyjnych zawsze czytaj licencję konkretnego, użytego przez Ciebie modelu – nie zakładaj, że skoro model jest darmowy do pobrania, to znaczy, że wolno go użyć do wszystkiego.
Uwaga o prompt injection
Gdy tekst pochodzi od użytkownika, zawsze istnieje ryzyko
prompt injection - czyli wstrzyknięcie polecenia dla modelu. Może ono być jako komentarz w HTMLu - niewidoczne dla użytkownika ale widoczne dla modelu. AI jeszcze przez długi czas będzie na to podatne – dotyczy to także największych modeli komercyjnych.
Podsumowanie
Używanie AI z poziomu
C++ jest jak najbardziej realne i często wygodniejsze oraz wydajniejsze niż z Pythona.
Bibliografia i materiały dodatkowe
Autor artykułu
Artykuł powstał we IX 2026.