MODUL 1 - LEKCIJA 2

Tokenizacija i Context Management

Kako modeli lome tekst, zašto bosanski "košta više", i kako efikasno upravljati kratkoročnom memorijom

⏱️ Trajanje: 1h 45min (11:15 - 13:00) 📚 Nivo: Srednji 🎯 Lab: Tiktoken i Kalkulacija Troškova

🔤 Šta Model Zapravo Vidi?

U prethodnoj lekciji (Modul 1.1) učili smo o Transformer arhitekturi i tenzorima. Rekli smo da se tokeni pretvaraju u embedding vektore. Ali kako tačno tekst koji pišete postaje token ID-evi? Ovaj proces je tokenizacija i od nje direktno zavise:

📌 Definicija: Token

Token je osnovna jedinica teksta kojom LLM operira. To nije uvijek cijela riječ — može biti dio riječi, cijela riječ, interpunkcija ili čak whitespace (razmak, novi red). Svaki model ima fiksan rječnik (Vocabulary) — listu svih mogućih tokena koje zna. GPT-4 ima rječnik od ~100,000 tokena. Llama 3 ima ~128,000.

Svaki token ima jedinstven Integer ID (redni broj u rječniku). Model ne vidi tekst — vidi niz Integer-a.

Vizualna Demonstracija Tokenizacije

Zamislimo da GPT tokenizira rečenicu na engleskom — česte riječi postaju jedan token:

Engleski (čest, bogat rječnik):

The firewall blocked the request .

6 tokena za 5 engleskih riječi (~1.2 tokena/riječ)

Bosanski (rjeđi u trening podacima, slova š/č/ć razbijaju BPE):

Vatrenizid blokirao zahtjev .

11 tokena za iste 3 bosanske riječi (~3.7 tokena/riječ)

🔴 Ista poruka, ali skoro 2× više tokena = 2× veći trošak + manje prostora u context window-u!

🧩 Byte-Pair Encoding (BPE) — Algoritam iza Tokenizacije

Gotovo svi moderni LLM-ovi (GPT-4, Claude, Llama 3, Mistral) koriste BPE tokenizaciju. Razumijevanje ovog algoritma objašnjava zašto neki jezici imaju više tokena od drugih.

📌 Šta je BPE (Byte Pair Encoding)?

BPE je kompresijski algoritam koji je originalno izmišljen za kompresiju podataka (1994.), a zatim adaptiran za NLP (2016.). Princip rada:

Korak 1 — Inicijalizacija: Počnemo sa svim pojedinačnim karakterima kao tokenima (a, b, c, ..., š, č, ć, ...). Svaki karakter = 1 token.

Korak 2 — Iterativno spajanje: Pronađi par znakova koji se najčešće pojavljuje zajedno u trening podacima i spoji ih u novi token. Ponavljaj.

Korak 3 — Zaustavljanje: Stani kada dostigneš željenu veličinu rječnika (npr. 100,000 tokena).

💡 Analogija: Pakovanje kofera za put

Zamislite da trebate spakovati kofer i prevoznik vam naplaćuje let po broju ubačenih predmeta (to je API naplata po Tokenu). Standardne česte stvari na engleskom jeziku ("the", "and") su kao majice - model ih smota u jedan komad i ubaci (1 token). Rijetke ili kompleksne riječi na bosanskom jeziku (npr. "preopterećen") model nije stigao naučiti izolovati. Model zato takve riječi rastavlja na manje djeliće iz svog kalupa: "pre" - "opt" - "ere" - "ć" - "en". Umjesto jedne smotane majice, sada u kofer gurate 5 odvojenih predmeta, što zauzima više mjesta u koferu (Context Window limit) i košta više na vagi (Naplata API-ja).

Byte-Pair Encoding (BPE) Spajanje t h e Karakteri (Početak) Iteracija 1: th th e Iteracija 2: the the Novi 1 Token! ć e v a p Rjeđi tekst (Nema spajanja) Nema dovoljno pojavljivanja ć e va p Više Tokena (Skuplje!)
📚 Ilustracija BPE procesa (simplificirano)
Trening korpus (milijarde Web stranica, knjiga, koda...):
...the, therapy, then, there, these, theory, theme...
...the the the...  (najčešći par: t+h)

Iteracija 1: "t" + "h" → "th"  (postaje jedan token)
Iteracija 2: "th" + "e" → "the" (postaje jedan token)
Iteracija 3: "the" + " " → "the " (postaje jedan token)
...
Nakon 100,000 iteracija → rječnik od 100,000 tokena

Rezultat za engleski: "the" je 1 token (viđeno milionima puta u treningu)
Rezultat za "preopterećen": nema u rječniku kao cijela riječ →
  razbija se na: "pre" + "opt" + "er" + "e" + "ć" + "en" = 6 tokena
  (jer "ć" je rijetko u trening podacima i nije formirano kao čest par)

🔑 Praktični zaključak za IT Inženjere i Arhitekte

💰 Troškovi API-ja: Modele u cloudu (OpenAI, Anthropic, Google) plaćate po procesiranom tokenu, a ne po broju znakova ili rijedi. GPT-4o trenutno košta $5.00 za 1 Milion Input tokena i $15.00 za 1 Milion Output tokena (april 2025).

📏 Context Window limit: Veličina "radne memorije" modela mjeri se u tokenima. Tekst koji šaljete (prompt) + historija razgovora + odgovor modela — sve mora stati u ovaj limit.

🌐 Dizajn System Promptova: Interne instrukcije AI-ju (system prompt) koje definišete pri izgradnji AI sistema uvijek pišite na engleskom — trošak je 2-4x manji, a model bolje razumije engleski. Output neka bude na bosanskom samo ako korisnik to traži.

🧠 Context Window — Radna Memorija Modela

Context Window (Kontekstni prozor ili Kontekstni limit) je ukupan broj tokena koji model može "vidjeti" i "misliti o" istovremeno u jednom pozivu. Uključuje:

VISUALIZACIJA CONTEXT WINDOW (npr. 128,000 tokena):

┌──────────────────────────────────────────────────────────────────────┐

[SYSTEM PROMPT: 500 tok] [USER: poruka 1: 200 tok] [ASSISTANT: odg 1: 300 tok]

[USER: poruka 2: 150 tok] [ASSISTANT: odg 2: 250 tok] ... [DOKUMENT: 8000 tok]

│ ↑ sve ovo su INPUT tokeni koje plaćate │

[NOVI OUTPUT: max_tokens podešeni od vas: npr 1000 tok]

└──────────────────────────────────────────────────────────────────────┘

Ukupno: moraju stati u 128,000 tokena limit

Poređenje Context Window Kapaciteta kod Popularnih Modela (2025)

Model Context Window Otprilike u A4 Stranicama Input Cijena ($/1M tok.)
GPT-4o (OpenAI) 128,000 tokena ~ 300 stranica teksta $5.00
Claude 3.5 Sonnet (Anthropic) 200,000 tokena ~ 500 stranica / cca 6h koda $3.00
Google Gemini 1.5 Pro 2,000,000 tokena Masivni repozitoriji, 1h videa $3.50
Llama 3.1 70B (Meta, Open-Source) 128,000 tokena ~ 300 stranica Besplatno (lokalno)
Mistral Large 2 (Open-Source) 128,000 tokena ~ 300 stranica Besplatno (lokalno)

💡 Analogija: Context Window kao Radna Ploča (Desk)

Zamislite da imate fizički radni sto (desk) ograničene veličine za rješavanje zadatka. Sve što stavite na njega — uputstva za rad (System Prompt), pristigli dokumenti, i historija razgovora sa klijentom — mora tu stati istovremeno da biste mogli raditi.

Kada se stol u potpunosti popuni starim papirima i listovima, novi papiri će jednostavno pasti na pod. Vi ih više ne vidite! Mreža ne može pročitati ništa što je "palo sa stola" — odnosno prešlo kapacitet zadatog Context Window-a. LLM nema dugoročno skriveno sjećanje kao mi; svaki novi API poziv model započinje potpuno iznova usred apnezije, samo s onih papirima koji su trenutno na stolu (u token listi).

Sliding Window Strategija — Upravljanje Dugim Razgovorima

Kada razvijate AI chatbot koji treba pamtiti dugačke razgovore (npr. korisnička podrška koja se odvija danima), historija razgovora će preći limit context window-a.

Strategija kojom vaš backend kod briše najstarije poruke i čuva najnovije naziva se Sliding Window:

🛠️ Ilustracija Sliding Window koncepta
Historija razgovora (svaka poruka ima broj tokena):
[SYSTEM]: "Ti si IT asistent..." (300 tok) ← NIKAD ne brišemo!
[User 28 dana}: Poruka 1: 200 tok   ← 🗑️ BRIŠI (najstarija)
[AI]:    Odg 1:   300 tok            ← 🗑️ BRIŠI
[User]:  Poruka 2: 150 tok           ← 🗑️ BRIŠI
[AI]:    Odg 2:   400 tok            ← 🗑️ BRIŠI
... (50 poruka starica)
[User]:  Poruka 51: 200 tok          ← ✅ Čuvamo (noviji razgovor)
[AI]:    Odg 51:   350 tok           ← ✅ Čuvamo
[User]:  NOVO pitanje: 180 tok       ← ✅ Ovo je zadnje pitanje!

Strategija: Kada historija pređe ~80% context limita,
brišemo najstarije PAROVE poruka (user+assistant)
dok ne ostanemo ispod limita. System prompt se NIKAD ne briše.

⚠️ Šta znači brisanje historije za korisnika?

Ako korisnik kaže "Shodno onome što smo pričali juče...", a te poruke su obrisane iz historije — AI neće "pamtiti" taj razgovor. Ovo nije greška AI-ja, to je matematička granica context window-a.

Rješenje (napredni dizajn): RAG (Retrieval Augmented Generation) — arhitektura gdje se relevantni dijelovi starijih razgovora pretražuju i ubacuju u prompt po potrebi. Ovo ćemo učiti u Modulu 6.

✅ Checkpoint — Provjera Razumijevanja

  • Pitanje: Šta je razlog što engleska rečenica "I am going home" treba oko 4 tokena, a ista na bosanskom "Ja idem kući" može uzeti 7-8 tokena?
    Odgovor: BPE tokenizer je treniran najviše na engleskom tekstu. On prepoznaje cijele engleske riječi kao jedan jedini token. Zbog slova poput 'ć' i rjeđe upotrebe jezika na internetu, bosanske se riječi lome na slogove.
  • Pitanje: Dobili ste izvještaj na našem jeziku dug 100 stranica (~30.000 riječi) u PDF formatu, i želite da API izvuče sažetak. Koji API model trebate odabrati iz tabele i zašto?
    Odgovor: 30.000 riječi na našem jeziku prelazi 100.000 tokena (zbog omjera loma). GPT-4o (128k limit) može ovo primiti na "svoj radni stol", mada je blizu granice. Raditi to sa starijim modelima od samo 8k tokena je potpuno nemoguće bez cijepanja PDF-a na više zasebnih komada.
  • Pitanje: Da li je Sliding Window rješenje ako korisnik AI agentu kaže "Provjeri onaj mail od prije pola godine" (a mi pričamo mjesecima svaki dan)?
    Odgovor: Nije. Taj mail je davno "pao sa radnog stola" zbog Sliding Window pritiska. Model ga ne može vidjeti. Razumijevanje stare historije bi ovdje zahtijevalo RAG tehniku pronalaženja i dovođenja na stol, što učimo iz Modula 6.
💻

LAB O2: Tokenizacija, Kalkulacija i Troškovi (TikToken)

Izgradićemo Python alat koji: (1) analizira tokenizaciju teksta, (2) uspoređuje bosanski i engleski po broju tokena, i (3) izračunava finansijski impakt na realnim scenarijima korištenja OpenAI API-ja.

1

Instalacija TikToken biblioteke

Nastavljamo u istom venv okruženju iz Lekcije 1.1. Aktivirajte ga ako nije aktivan:

powershell
# Aktivacija venv (ako nije aktivno)
cd C:\Users\$env:USERNAME\Desktop\AI_Kurs
.\venv\Scripts\Activate.ps1

# tiktoken je OpenAI-jeva open-source biblioteka za tokenizaciju
# Isti tokenizer koji koriste GPT-3, GPT-4, o1, o3 modeli
pip install tiktoken

# Provjera instalacije
python -c "import tiktoken; print('tiktoken OK, verzija:', tiktoken.__version__)"
2

Kreiranje analizatora u VS Code

Otvorite VS Code: File → Open Folder → Izaberite AI_Kurs folder. Napravite novi fajl: File → New File → Sačuvajte kao analyzer.py. Unesite sljedeći kod liniju po liniju, čitajući komentare:

python
"""
analyzer.py - Alat za analizu tokenizacije i troškova LLM API-ja
Autor: AI Training Kurs, Modul 1.2
"""

# Importamo tiktoken biblioteku (OpenAI tokenizer)
import tiktoken

# ================================================================
# Korak 1: Inicijalizacija tokenizera
# ================================================================
# Svaki model ima vlastiti tokenizer (encoding).
# "cl100k_base" je encoding koji koriste: gpt-4, gpt-4o, gpt-3.5-turbo
# "o200k_base" koriste noviji o1, o3, gpt-4o-mini modeli
# Koristimo encoding_for_model() da automatski odaberemo pravi encoding:

encoding = tiktoken.encoding_for_model("gpt-4o")

print(f"Tokenizer učitan: {encoding.name}")
print(f"Veličina rječnika: {encoding.n_vocab:,} tokena")
# Trebate vidjeti: 200,019 tokena u rječniku gpt-4o tokenizera

print("\n" + "="*60)

# ================================================================
# Korak 2: Naši test stringovi (isti sadržaj, dva jezika)
# ================================================================

text_english = (
    "The database migration caused significant downtime on the main "
    "production cluster due to locking issues in the transaction log."
)

text_bosnian = (
    "Migracija baze podataka uzrokovala je značajan prekid rada na "
    "glavnom produkcijskom klasteru zbog problema sa zaključavanjem "
    "u dnevniku transakcija."
)

# ================================================================
# Korak 3: Tokenizacija
# ================================================================
# encode() metoda prima string i vraća Python listu Integer-a (token ID-eva)
# Svaki integer je indeks tokena u rječniku modela

tokens_eng = encoding.encode(text_english)
tokens_bos = encoding.encode(text_bosnian)

# decode_single_token_bytes() vraća bytes reprezentaciju svakog tokena
# Ovo nam omogućava da vidimo točno kako se svaki ID mapira na tekst

print("="*60)
print("📊 ANALIZA ENGLESKOG TEKSTA")
print("="*60)
print(f"Originalni tekst ({len(text_english)} znakova):")
print(f"  '{text_english}'")
print(f"\nBroj tokena: {len(tokens_eng)}")
print(f"Token ID niz: {tokens_eng}")
print(f"\nDetaljna tokenizacija (token → tekst koji reprezentira):")
for token_id in tokens_eng:
    # decode() pretvara token ID natrag u string za čitanje
    token_text = encoding.decode([token_id])
    print(f"  [{token_id:6d}] → '{token_text}'")

print("\n" + "="*60)
print("📊 ANALIZA BOSANSKOG TEKSTA")
print("="*60)
print(f"Originalni tekst ({len(text_bosnian)} znakova):")
print(f"  '{text_bosnian}'")
print(f"\nBroj tokena: {len(tokens_bos)}")
print(f"Token ID niz: {tokens_bos}")
print(f"\nDetaljna tokenizacija:")
for token_id in tokens_bos:
    token_text = encoding.decode([token_id])
    print(f"  [{token_id:6d}] → '{token_text}'")

# ================================================================
# Korak 4: Izračunavanje finansijskog impakta
# ================================================================
# Scenario: Imamo sistem koji procesira 1,000,000 ovakvih poruka
# GPT-4o cijena: Input = $5.00 / 1M tokena, Output = $15.00 / 1M tokena
# Pretpostavimo da je output otprilike jednak inputu po dužini

INPUT_PRICE_PER_MILLION = 5.00   # USD za 1 Milion input tokena
OUTPUT_PRICE_PER_MILLION = 15.00  # USD za 1 Milion output tokena
PORUKA_COUNT = 1_000_000         # Obrađujemo 1 milion poruka

print("\n" + "="*60)
print("💰 FINANSIJSKA KALKULACIJA — 1,000,000 poruka")
print("="*60)

def calculate_cost(token_count, n_messages):
    """Izračunava ukupni trošak za n_messages identičnih poruka."""
    total_input_tokens = token_count * n_messages
    total_output_tokens = token_count * n_messages  # Pretpostavljamo isti output

    input_cost = (total_input_tokens / 1_000_000) * INPUT_PRICE_PER_MILLION
    output_cost = (total_output_tokens / 1_000_000) * OUTPUT_PRICE_PER_MILLION

    return input_cost + output_cost, total_input_tokens, total_output_tokens

cost_eng, in_tokens_eng, out_tokens_eng = calculate_cost(len(tokens_eng), PORUKA_COUNT)
cost_bos, in_tokens_bos, out_tokens_bos = calculate_cost(len(tokens_bos), PORUKA_COUNT)

print(f"\n🇺🇸 ENGLESKI:")
print(f"   Tokeni po poruci: {len(tokens_eng)}")
print(f"   Ukupni input tokeni: {in_tokens_eng:,}")
print(f"   Ukupni output tokeni: {out_tokens_eng:,}")
print(f"   💵 UKUPNI TROŠAK: ${cost_eng:,.2f}")

print(f"\n🇧🇦 BOSANSKI:")
print(f"   Tokeni po poruci: {len(tokens_bos)}")
print(f"   Ukupni input tokeni: {in_tokens_bos:,}")
print(f"   Ukupni output tokeni: {out_tokens_bos:,}")
print(f"   💵 UKUPNI TROŠAK: ${cost_bos:,.2f}")

print(f"\n📊 ZAKLJUČAK:")
ratio = len(tokens_bos) / len(tokens_eng)
extra_cost = cost_bos - cost_eng
print(f"   Bosanski tekst ima {ratio:.2f}x VIŠE tokena od ekvivalenta na engleskom")
print(f"   Na 1 milion poruka, bosanski košta EXTRA: ${extra_cost:,.2f}")
print(f"\n💡 Inženjerska preporuka: Pišite system prompts i API instrukcije na ENGLESKOM!")
print(f"   Output tražite na bosanskom samo za krajnjeg korisnika.")
3

Pokretanje i analiza rezultata

powershell
# Uvjerite se da ste u AI_Kurs folderu sa aktivnim venv-om
python analyzer.py
  • Engleski tekst: Vidjet ćete da se česte engleske riječi poput "the", "on", "main", "due" svaka mapira u jedan jedini token.
  • Bosanski tekst: Vidjet ćete da se rijeci "uzrokovala", "zaključavanjem", "produkcijskom" razbijaju na 3-5 tokena jer BPE algoritam nije vidio dovoljno bosanskih primjera u trening podacima da ih zapakovao u jedan token.
  • Finansijski impakt: Na velikim sistemima (npr. automatizovana obrada 10.000 korisničkih zahtjeva dnevno), razlika između "pisemo System Prompt na bosanskom vs. engleskom" može biti stotine dolara mjesečno ili hiljade godišnje.
  • Arhitektonski zaključak: Svaki AI sistem u produkciji koji obrađuje bosanski/srpski/hrvatski sadržaj treba imati strategiju za optimizaciju tokena. Tipično: System Prompt na engleskom (robusnost + efikasnost), User prompt na maternjem (UX), Output na maternjem (čitljivost) uz max_tokens limit za kontrolu troška.
4

Bonus: Interaktivni tokenizer u terminalu

Napravite novi fajl interactive_tokenizer.py za eksperimentisanje:

python
import tiktoken

enc = tiktoken.encoding_for_model("gpt-4o")

print("🔤 Interaktivni Tokenizer")
print("Napišite tekst (ili 'quit' za izlaz):\n")

while True:
    text = input(">>> ")
    if text.lower() in ['quit', 'exit', 'q']:
        break

    tokens = enc.encode(text)
    print(f"  Broj znakova: {len(text)}")
    print(f"  Broj tokena:  {len(tokens)}")
    print(f"  Tokenizovano:")
    for tid in tokens:
        print(f"    '{enc.decode([tid])}' (ID: {tid})")
    print(f"  Omjer tok/slovo: {len(tokens)/len(text):.2f}")
    print()
powershell
python interactive_tokenizer.py
# Probajte unijeti: "Hello" vs "Zdravo" vs "Šeširdžija"
# Vidite razliku u broju tokena za iste koncepte na različitim jezicima!

✅ Zaključak

Sada razumijemo cijeli put teksta: String → BPE tokenizacija → Token ID-evi (Integer lista) → Embedding vektori → Transformer obrada → Logits distribucija → Sljedeći token.

Naučili smo da tokenizacija direktno utiče na troškove API poziva i na kapacitet context window-a, te zašto se za sistemske prompts u poslovnim AI aplikacijama preporučuje engleski jezik.

Slijedi pauza za ručak (60 minuta). Nakon toga, nastavl jamo sa Lekcijom 1.3 — kako direktno komunicirati sa LLM API-jevima, koje hiperparametre možemo kontrolisati i kako izbjeći halucinacije u produkcijskim sistemima.