Tutorial — Local Inference for Offline Venues
Augmented Traveler local tutorial. Back to AI Mind — Tutorials · the decision: Tutorial — Choosing Cloud or Local Models · GPU profiling: Tutorial — Performance Profiling · hardware: Hardware — Deployment Specifications
How to run an open-weight language model on the kiosk PC (or a small second PC in the kiosk cabinet) for offline venues and schools. It covers the runtime (Ollama or llama.cpp server), the OpenAI-compatible endpoint, model choice, quantisation, the VRAM budget next to Unreal, local embeddings, structured output for answer_meta, benchmarks, updates and licences.
Draft. Written from the runtime docs listed under Sources and the model table in AI Mind — Tutorials section 2. The AI conversation engineer and DevOps engineer validate it on the kiosk PC. Correct flags and setting names here after the first install.
1. Where it runs
| Layout | For | Against |
|---|---|---|
| Same PC as Unreal (RTX 5090, 32 GB) | One box; no extra cost | Unreal, ASR, TTS, lip sync and the model share one GPU; frame drops if we overfill it |
| Second small PC in the cabinet | Unreal keeps the whole 5090; model can be larger | Extra cost, heat, noise; one more machine to update |
Decide after section 7. Start on the same PC; move out if the frame rate or first-token time suffers.
2. Runtime: Ollama or llama.cpp server
Both run GGUF models and expose an OpenAI-compatible API, so the conversation service only changes its base URL.
| Ollama | llama.cpp llama-server | |
|---|---|---|
| Set-up | Easiest; installer; pulls models by name | Download a build; point at a GGUF file |
| Endpoint | http://localhost:11434/v1/ (chat, completions, embeddings, responses, models) | Port 8080 by default; chat, responses, embeddings |
| Structured output | format with a JSON schema (native API); response_format for JSON mode on /v1 | JSON schema and grammar constraints |
| Key settings | OLLAMA_KEEP_ALIVE, OLLAMA_CONTEXT_LENGTH (default 4096), OLLAMA_NUM_PARALLEL (default 1), OLLAMA_KV_CACHE_TYPE (f16, q8_0, q4_0), OLLAMA_FLASH_ATTENTION | -m, -c, -ngl, --parallel, -fa, --jinja, --api-key |
| Monitoring | ollama ps shows GPU/CPU placement | /health; /metrics with --metrics; timings in each response |
| Licence | MIT | MIT |
vLLM (Apache 2.0) suits many requests at once. It reserves 90% of GPU memory by default (--gpu-memory-utilization 0.9, in older docs; check current), which does not suit sharing a GPU with Unreal. LM Studio is fine for trying models on a desktop; it is proprietary.
Suggested: Ollama for the first venue (simplest to operate); llama.cpp server if we need finer control.
Install on the kiosk (Ollama):
- Install Ollama as a service (Windows installer).
- Keep it on localhost. Ollama binds to
127.0.0.1:11434by default. Do not setOLLAMA_HOSTto expose it unless the model runs on the second PC; then allow only the kiosk PC's address through the firewall. - Set
OLLAMA_KEEP_ALIVE=-1so the model stays loaded (a cold load in front of a visitor is too slow). - Set
OLLAMA_CONTEXT_LENGTHto what the persona and retrieved passages need (measure; start at 8192). - Pull the chosen model by exact tag. Record the tag and digest in the venue config.
- Run
ollama ps; it must say 100% GPU.
3. Choosing the model
From AI Mind — Tutorials section 2 (September 2026; check each model card):
| Family | Size to try first | Licence | Note |
|---|---|---|---|
| Gemma 4 | 12B (fits easily); 26B-A4B MoE | Apache 2.0 | Good first candidate |
| gpt-oss | 20B (about 16 GB) | Apache 2.0 | Tight on a shared GPU |
| Qwen 3.6 / 3.8 ⚠️ | 35B-A3B MoE (3B active) | Apache 2.0 | Fast first token; version names unverified |
| Meta Muse Glimmer ⚠️ | 30B | Apache 2.0 (reported) | Single report; verify licence and card first |
| Llama 4 | — | Llama Community Licence | Conditions apply; counsel reads first |
Models marked ⚠️ come from one source. Do not ship one until the model card and licence have been read by the CTO.
4. Quantisation
- Q4_K_M: about 0.6 GB per billion parameters. The usual default.
- Q5_K_M: a little larger, a little closer to full quality.
- Q8_0: about 1.05 GB per billion; mostly a reference.
- A 2026 study on Llama-3.1-8B found Q5_K_M near baseline and Q4_K_M with minimal loss at about 69% smaller. Results vary by task, so test on our golden set, not on general benchmarks.
- AWQ is a GPU quantisation format used by vLLM, not by Ollama or llama.cpp.
- KV cache can also be quantised (
OLLAMA_KV_CACHE_TYPE=q8_0) to save context memory. Test quality after.
5. VRAM budget on one RTX 5090 (32 GB)
Estimates to replace with measurements from Tutorial — Performance Profiling:
| Item | Estimate | How to measure |
|---|---|---|
| Unreal: life-size MetaHuman, place kit, lighting | Measure first; likely the largest item | Unreal stats and GPU memory tools, Shipping build |
| ASR (Whisper large) | about 3.9 GB (whisper.cpp figure); int8 less | nvidia-smi with the service loaded |
| TTS | Cloud: 0. Local Piper/Kokoro: small | nvidia-smi |
| Lip sync (Audio2Face-3D, if used) | Unknown; measure | nvidia-smi |
| Embedding model | Small (under 1 GB for common models; check) | ollama ps |
| LLM weights | 12B Q4_K_M ≈ 7 GB; 27B Q4_K_M ≈ 16 GB | ollama ps |
| LLM context (KV cache) | 2–6 GB | ollama ps |
| Headroom | 2–3 GB | — |
Rule: if Unreal plus speech needs more than about 14 GB, move the model to a second PC or drop to a 12B model.
6. Local embeddings and structured output
Embeddings: Ollama serves them at /api/embed (and /v1/embeddings). Its docs name embeddinggemma, qwen3-embedding and all-minilm. Use the same embedding model for indexing and querying. If the offline index was built with a cloud embedding model, rebuild it locally (Tutorial — Retrieval with Citations).
Structured output for answer_meta: the service needs answer ID, gesture tag, refusal flag and citation IDs before the audio starts (Tutorial — Conversation Client Plugin). Two ways:
- Two calls: a short constrained call returns JSON (
gesture,citation_ids,confidence) using a JSON schema; then the answer text streams. Adds a little latency; simplest to validate. - One streamed call: the answer streams as plain text with inline citation markers (e.g.
[A-0142]); the service strips the markers before TTS and buildsanswer_metafrom them. Faster; needs careful parsing.
Try option 2 first; fall back to option 1 if parsing proves unreliable. The policy layer checks the result either way (Tutorial — Policy Layer with Guardrails).
7. Benchmarks to run
Run with Unreal running the figure in a Shipping build, at kiosk settings.
- Time to first token (TTFT): 50 golden questions, median and 95th percentile. Target: under 600 ms (AI Mind — Process).
- Tokens per second: published 5090 figures (40–90 tokens/s) come from one blog; measure ours.
- Frame rate of the figure while the model generates. It must stay at target.
- Golden-set quality and a blind historian review of 50 answers (Tutorial — Evaluating Answers).
- Cold start: time from boot to first answer.
# skeleton — test before use. Needs: pip install openai
import time
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="local")
def bench(model, messages):
t0 = time.perf_counter()
first, chunks = None, 0
stream = client.chat.completions.create(model=model, messages=messages, stream=True)
for part in stream:
if part.choices and part.choices[0].delta.content:
chunks += 1
if first is None:
first = time.perf_counter() - t0
total = time.perf_counter() - t0
return {"ttft_s": first, "chunks_per_s": chunks / max(total - (first or 0), 1e-6)}
Chunks are close to tokens for most runtimes; for exact counts use the timings llama.cpp returns.
8. Update procedure
- Never update at a venue without a tested build.
- New model or runtime version → install on the dev workstation → run section 7 → historian review.
- Record the model tag, digest, quantisation, runtime version and settings in the venue config.
- Turn off auto-update on kiosk PCs (Ollama auto-updates on Windows; check how to disable it). Update in a maintenance window.
- Keep the previous model on disk for one week to roll back.
9. Licences
- Runtimes: llama.cpp MIT; Ollama MIT; vLLM Apache 2.0; LM Studio proprietary (read its terms before commercial use).
- Models: each model card's licence governs. Apache 2.0 families are simplest. Llama licence conditions need counsel.
- Keep a copy of each model's licence with the build.
10. Practice exercise
On a dev workstation with the Mitchell figure running in Unreal, load a Gemma 4 12B Q4_K_M in Ollama. Run the benchmark on 20 golden questions. Then load a 27B-class model and repeat. Record TTFT, tokens/s, frame rate and VRAM for both on one page.
11. Check before an offline venue opens
Sources
- Ollama, OpenAI compatibility: https://docs.ollama.com/api/openai-compatibility
- Ollama, Structured outputs: https://docs.ollama.com/capabilities/structured-outputs
- Ollama, Embeddings: https://docs.ollama.com/capabilities/embeddings
- Ollama, FAQ: https://docs.ollama.com/faq
- ggml-org, llama.cpp server README: https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md
- vLLM, Engine arguments (v0.6.4): https://docs.vllm.ai/en/v0.6.4/models/engine_args.html
- LM Studio, OpenAI compatibility: https://lmstudio.ai/docs/developer/openai-compat
- arXiv, Which Quantization Should I Use? (2601.14277): https://arxiv.org/html/2601.14277v1
- ggml-org, whisper.cpp (model memory): https://github.com/ggml-org/whisper.cpp
- Full list: Sources — 2026-09-20