Open WebUI — web interfejs za tvoje lokalne modele

Model koji radi u terminalu je dokaz da postavka valja. Model koji koriste ljudi u firmi mora da izgleda kao nešto što svi već znaju da koriste — sa nalozima, istorijom razgovora i mogućnošću da se priloži fajl.

Open WebUI je upravo to: veb sloj iznad tvoje Ollame ili vLLM-a. Ovim tekstom se zatvara Faza 3, jer je postavka od ovog trenutka upotrebljiva i za nekog ko nikad neće otvoriti terminal.

Šta dobijaš

  • Razgovor u pregledaču, sa istorijom koja se čuva
  • Korisničke naloge i prava pristupa po modelu
  • Prilaganje dokumenata i pitanja nad njihovim sadržajem
  • Sopstvene sistemske promptove sačuvane kao modeli
  • Rad sa više servisa odjednom — lokalni model i spoljni API na istom mestu

Ono što ne dobijaš je bilo kakvo ubrzanje. Interfejs samo prosleđuje zahteve; brzina i dalje zavisi od toga šta je ispod, kako je opisano u tekstu o proceni VRAM-a.

Ovo je dodatni servis sa sopstvenom bazom i nalozima. Znači i sopstvenu površinu za napad, i sopstvene rezervne kopije. Ako si jedini korisnik, terminal je i dalje sasvim u redu.

Instalacija

Uz Ollamu koja već radi na domaćinu:

$ docker run -d \
    --name open-webui \
    -p 127.0.0.1:3000:8080 \
    -v /data/open-webui:/app/backend/data \
    -e OLLAMA_BASE_URL=http://host.docker.internal:11434 \
    --add-host=host.docker.internal:host-gateway \
    --restart unless-stopped \
    ghcr.io/open-webui/open-webui:main

Zapis host.docker.internal uz --add-host je način da kontejner dođe do servisa na domaćinu. Bez toga localhost unutar kontejnera pokazuje na sam kontejner.

Ako je Ollama takođe u kontejneru, čistije je kroz compose iz prethodnog teksta:

services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    restart: unless-stopped
    volumes:
      - /data/ollama:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=-1
      - OLLAMA_CONTEXT_LENGTH=16384
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

  webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: unless-stopped
    depends_on:
      - ollama
    ports:
      - "127.0.0.1:3000:8080"
    volumes:
      - /data/open-webui:/app/backend/data
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - WEBUI_AUTH=true
$ docker compose up -d
$ docker compose logs -f webui

Prvo podizanje traje duže jer se preuzima i model za embeddinge. Otvori zatim http://localhost:3000.

Prvi napravljeni nalog dobija administratorska prava. Napravi ga odmah, pre nego što servis izložiš bilo kome — inače to uradi neko drugi.

Bez Dockera

$ pipx install open-webui
$ open-webui serve --port 3000

Radi, ali povlači podosta Python zavisnosti i ažurira se teže. Kontejner je ovde jasno bolji izbor.

Povezivanje sa vLLM-om

Open WebUI govori i OpenAI protokolom, pa vLLM ide bez ikakvog posrednika:

environment:
  - OPENAI_API_BASE_URL=http://vllm:8000/v1
  - OPENAI_API_KEY=tajni-kljuc-ovde

Oba se mogu postaviti istovremeno — Ollama i vLLM tada stoje u istom padajućem meniju za izbor modela. Isto važi i za spoljne komercijalne servise, ako ih koristiš uporedo.

Podešavanje se može i kroz sam interfejs, pod Settings → Connections, što je zgodnije za probanje nego menjanje compose fajla.

Korisnici i prava

Ovo je razlog zbog koga se interfejs uopšte uvodi u firmu.

Podrazumevano novi korisnici čekaju odobrenje administratora, i tako treba da ostane. Pod Admin Panel → Settings:

Podešavanje Preporuka
Enable New Sign Ups Isključi kad svi imaju nalog
Default User Role pending, nikako user
Model Whitelist Ograniči ko koji model vidi
Allow Chat Deletion Zavisi od toga da li ti treba trag

Isto kroz okruženje, ako voliš da postavka stoji u fajlu:

environment:
  - WEBUI_AUTH=true
  - ENABLE_SIGNUP=false
  - DEFAULT_USER_ROLE=pending

Vrednost WEBUI_AUTH=false isključuje prijavu u potpunosti. Naići ćeš na nju u uputstvima za lokalno probanje. Na serveru koji je dostupan bilo kome van tvoje mašine to ne dolazi u obzir.

Sopstveni modeli

Isto što i Modelfile u Ollami, samo kroz interfejs. Pod Workspace → Models praviš varijantu sa svojim sistemskim promptom:

Naziv:        Linux pomoćnik
Osnova:       qwen3:8b
Temperatura:  0.1
Sistemski prompt:

Ti si pomoćnik za administraciju Linux servera.
Odgovaraj kratko i konkretno, na srpskom.
Uz svaku komandu navedi šta tačno radi.
Nikada ne predlaži komandu koja briše podatke bez izričitog upozorenja.

Prednost nad Modelfile pristupom je što se ovako napravljen model može podeliti sa ostalim korisnicima ili zadržati samo za sebe. U timu je to praktičnije nego da svako pravi svoj.

Rad sa dokumentima

Fajl se priloži u razgovoru znakom # ili spajalicom, a za trajnu zbirku ide Workspace → Knowledge. Dokumenti se seku na delove, pretvaraju u embeddinge i pri svakom pitanju se relevantni delovi ubacuju u prompt.

To je RAG, i radi bez ijedne linije koda. Ograničenja su ista kao kod svake RAG postavke:

  • Kvalitet zavisi od toga koliko su dokumenti uredno strukturirani
  • Skenirani PDF bez teksta ne daje ništa
  • Pronađeni delovi troše kontekst, pa sa velikom zbirkom razgovor brzo naraste

Podrazumevani model za embeddinge je mali i radi na procesoru. Za ozbiljniju upotrebu zameni ga pod Admin Panel → Settings → Documents onim koji vrtiš na Ollami:

$ docker exec ollama ollama pull nomic-embed-text

Kako sve ovo radi ispod haube i kada je bolje sagraditi sopstveno rešenje, tema je teksta o RAG-u.

Izlaganje prema mreži

Interfejs traži lozinku, pa je za razliku od Ollame bar donekle zaštićen. To i dalje ne znači da sme direktno na internet — kolačići sesije preko HTTP-a putuju u čitljivom obliku.

Postavka koja se koristi:

Internet → Nginx (TLS) → Open WebUI → Ollama
                          :3000        :11434
                          lokalno      lokalno

Oba servisa slušaju samo na 127.0.0.1, a napolju stoji jedino proxy sa sertifikatom. Postavka je opisana u tekstu o reverse proxy-ju.

Jedna stvar koja se ovde često previdi — bez podrške za WebSocket odgovori ne stižu postepeno, nego se čeka pa sve odjednom:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 300s;
}

Dugačak proxy_read_timeout je takođe potreban, jer veći model ume da razmišlja duže od podrazumevanog roka.

Rezervne kopije

Sve što interfejs zna stoji u jednom direktorijumu — nalozi, razgovori, prilozi, sopstveni modeli:

$ ls /data/open-webui
webui.db
uploads/
vector_db/
cache/

Baza je SQLite, pa kopija ide pri zaustavljenom kontejneru:

$ docker compose stop webui
$ sudo tar -czf /backup/open-webui-$(date +%F).tar.gz -C /data open-webui
$ docker compose start webui

Ako servis ne sme da stane, koristi ugrađenu komandu koja pravi ispravnu kopiju baze u radu:

$ docker compose exec webui sqlite3 /app/backend/data/webui.db ".backup '/tmp/webui.db'"
$ docker compose cp webui:/tmp/webui.db /backup/

Kopiranje SQLite fajla dok se u njega piše daje oštećenu bazu. Ovo je greška koja se otkriva tek kad kopija zatreba.

Automatizacija preko cron zadatka je posao od pet minuta i vredi je odraditi odmah.

Ažuriranje

$ docker compose pull
$ docker compose up -d

Projekat se menja brzo i podešavanja povremeno menjaju mesto u interfejsu. Napravi kopiju pre ažuriranja — vraćanje na staru sliku je lako, vraćanje pokvarene baze nije.

$ docker image prune -a

Praktični scenariji

Scenario 1: interfejs radi, ali nema nijednog modela

Veza sa Ollamom nije uspostavljena. Proveri iz samog kontejnera:

$ docker compose exec webui curl -s http://ollama:11434

Ako odgovora nema, greška je u nazivu servisa ili u tome što Ollama sluša na pogrešnoj adresi.

Scenario 2: odgovor se pojavljuje tek na kraju, odjednom

Reverse proxy ne propušta WebSocket. Dodaj zaglavlja Upgrade i Connection.

Scenario 3: prekid veze usred dužeg odgovora

Podigni proxy_read_timeout na pet minuta ili više.

Scenario 4: neko je napravio nalog bez odobrenja

Registracija je bila otvorena, a podrazumevana uloga postavljena na user. Isključi registraciju i pregledaj spisak naloga u administratorskom panelu.

Scenario 5: priloženi PDF daje besmislene odgovore

Verovatno je skeniran, bez tekstualnog sloja. Proveri:

$ pdftotext dokument.pdf - | head

Ako je ispis prazan, treba OCR pre nego što dokument ima smisla priložiti.

Scenario 6: posle ažuriranja nema razgovora

Volumen nije bio podešen, pa je baza ostala u starom sloju kontejnera. Ovo je razlog zbog koga se rezervna kopija radi pre ažuriranja, a ne posle.

Kratka referenca

  • ghcr.io/open-webui/open-webui:main — zvanična slika
  • -v /data/open-webui:/app/backend/data — sve što servis pamti
  • OLLAMA_BASE_URL — adresa Ollame
  • OPENAI_API_BASE_URL — vLLM ili spoljni servis
  • --add-host=host.docker.internal:host-gateway — pristup domaćinu
  • WEBUI_AUTH=true — prijava uključena; nikad na false van lokalne mašine
  • ENABLE_SIGNUP=false — zatvaranje registracije
  • DEFAULT_USER_ROLE=pending — novi nalozi čekaju odobrenje
  • Prvi nalog je administratorski — napravi ga odmah
  • Workspace → Models — sopstveni sistemski promptovi
  • Workspace → Knowledge — RAG nad zbirkom dokumenata
  • proxy_set_header Upgrade — bez toga nema postepenog ispisa
  • sqlite3 ... ".backup" — ispravna kopija baze u radu
  • docker compose pull && up -d — ažuriranje

Vežba

  1. Pokreni Open WebUI uz Ollamu kroz compose i napravi administratorski nalog.
  2. Proveri sa ss -tulpn da je vidljiv samo port interfejsa, ne i Ollamin.
  3. Napravi sopstveni model sa svojim sistemskim promptom i uporedi odgovore sa osnovnim.
  4. Priloži jedan tekstualni dokument i postavi tri pitanja čiji su odgovori u njemu.
  5. Napravi drugi nalog i ograniči mu spisak modela koje vidi.
  6. Napravi rezervnu kopiju, obriši volumen, vrati kopiju i potvrdi da su razgovori tu.

Sledeći tekst u serijalu: OpenAI-kompatibilan API na svom serveru

Povezano:

Comments

Popular posts from this blog

Početak u Linuxu — šta je i zašto se uči

Konverzija tipova podataka u Pythonu

groupadd