--- title: The Oracle emoji: ๐ฎ colorFrom: indigo colorTo: yellow sdk: gradio sdk_version: 6.16.0 python_version: '3.13' app_file: app.py pinned: false license: mit short_description: Oracle An Akinator-style guessing game models: - bartowski/Llama-3.2-1B-Instruct-GGUF - bartowski/Llama-3.2-3B-Instruct-GGUF tags: - build-small-hackathon - thousand-token-wood - llama - llama-cpp - gradio - game - guessing-game - offline-first - on-device --- # ๐ฎ The Oracle Think of an **animal, a fruit, or a vegetable**. The Oracle asks simple yes/no questions and divines exactly what you're thinking โ an Akinator-style mind-reader. **Built with Llama** ๐ฆ and designed to run **fully offline**. Built for the **Build Small Hackathon ยท An Adventure in Thousand Token Wood**. ## Model - **[Llama 3.2 1B Instruct](https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF)** (`Llama-3.2-1B-Instruct-Q4_K_M.gguf`) โ what the live Space runs โ through the **llama.cpp** runtime (in-process via `llama-cpp-python`). Just **1B parameters**, far under the โค32B limit and the โค4B Tiny Titan bar. It's used only to phrase questions and to derive attributes when learning a new item; all deduction is pure deterministic code, so a 1B model is plenty. - Also supports **[Llama 3.2 3B Instruct](https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF)** (the default) for slightly richer phrasing โ set via `ORACLE_LLAMA_REPO` / `ORACLE_LLAMA_FILE`. Because questions are pre-generated once at boot and cached (persisted in the Storage Bucket), the model size barely affects gameplay โ the live cache holds all 42 questions, so each turn is an instant lookup. ## Badges & prizes we're targeting | Target | Status | How | |--------|--------|-----| | ๐ **Off the Grid** | โ | no cloud APIs; the engine + cached questions run fully offline | | ๐จ **Off-Brand** | โ | fully custom crystal-ball UI (art, buttons, reveal), not default Gradio | | ๐ **Field Notes** | โ | the design journey above + the two deep-dive docs / this write-up | | ๐ฆ **Llama Champion** | โ | the model runs through the llama.cpp runtime (`ORACLE_QUESTION_LLM=1`) | | ๐ **Tiny Titan** *(special award)* | ๐ฏ | built on a genuinely tiny โค4B model (Llama-3.2-3B) | | ๐ก Sharing is Caring | optional | the attribute database is shareable as a dataset | | ๐ฏ Well-Tuned | โ๏ธ n/a | deliberately no fine-tuned model โ accuracy comes from the engine | ## Demo [](https://youtu.be/U5UNzHBfJ1k) ## X post https://x.com/i/status/2064387556376981633 ## Why it fits the track The game spends almost no tokens per turn. A **deterministic engine** does the deduction over a small attribute database, and a tiny **Llama 3.2 3B** model is used only to phrase questions in natural language (pre-generated at boot, so gameplay is instant). Small model, small token budget, no network required. ## Design journey **Attempt 1 โ one model did everything (ask *and* guess).** It asked repetitive, elimination-style questions, never narrowed down, and games didn't end. **Attempt 2 โ two models: one writes questions, one eliminates items from a list.** Questions got better, but elimination was slow and wrong on large lists, and the model's facts couldn't be trusted (it called a tomato a root vegetable). **Final โ facts in a JSON attribute database; a deterministic engine does all the elimination; the small model only phrases the questions** (generated once at boot and cached, so gameplay is instant). A Teach mode lets it learn new items from Wikipedia, so the database grows on its own. ```mermaid flowchart TD A[Boot] --> B(Model pre-generates natural questions, cached) B --> C(Gameplay: deterministic elimination engine) C --> D(Learning: model extracts facts from Wikipedia) ``` ## How it works ``` answers so far โโบ engine.filter_candidates โโบ engine.choose_attribute โโบ look up cached question โโบ ask โโบ 1 left โ guess 0 left โ discovery/teach ``` (the cached questions are written by Llama once at boot โ see **[How question generation works](./docs/QUESTION_GENERATION.md)**) - **engine.py** โ pure-Python core. Filters candidates by the answers (exact, no model), then picks the attribute that best splits the set (max info gain). Auto-reloads the JSON if it changes on disk. - **data/\*.json** โ the attribute database (animals / fruits / vegetables); the single source of truth. Every item defines every attribute. - **question_maker.py** โ the only place the LLM is used: turn an attribute into a natural yes/no question. Pre-generated at boot and cached (instant in-game), with built-in phrasing as fallback. Never decides elimination. - **llm.py** โ runs the model through the **llama.cpp** runtime (in-process via `llama-cpp-python`, or an HTTP `llama-server` for local dev), with thread caps, a timeout, and warmup so it can never hang the game. - **discovery.py** โ when the Oracle doesn't know an item, the player teaches it; attributes are filled from the player's answers, the LLM, and the existing DB, so the new item is complete and guessable next time. Also explains *why* a wrong answer threw it off. - **app.py** โ `gradio.Server`: `@app.api("next")` per turn, `@app.api("learn")` for teaching, serves `index.html`. - **index.html** โ a fully custom crystal-ball UI (image art, category pick, image answer buttons, "I'm not sure", dramatic reveal, Teach mode). - **check_db.py** โ validator: completeness, uniqueness, guessability, balance. ## Deep dives - **[How question generation works](./docs/QUESTION_GENERATION.md)** โ the engine picks the attribute, the model only phrases it, and questions are pre-generated and cached at boot for instant gameplay. - **[How Teach / discovery works](./docs/TEACH_MODE.md)** โ how the Oracle learns a new item (player answers + Llama + Wikipedia + the existing DB) and explains why a wrong answer threw it off. ## Run locally ```bash pip install -r requirements.txt python app.py # plays immediately, offline (built-in question phrasing) ``` For natural, model-written questions (runs the 3B model in-process via llama.cpp โ the GGUF downloads once from the Hub): ```bash ORACLE_QUESTION_LLM=1 python app.py ``` (Alternatively, run a local `llama-server` and set `ORACLE_LLAMA_URL`.) ## Test & validate ```bash pytest -q # 20 offline tests python check_db.py # validate the attribute database ``` ## Deploy (Hugging Face Spaces ยท Gradio) Push these files to a Space under `build-small-hackathon`; the Gradio SDK runs `app.py` on port 7860. The game works offline out of the box. To persist items taught during play and to run the Llama model on the Space (for the **Llama Champion** badge), see **[DEPLOY.md](./DEPLOY.md)** โ in short: mount a Storage Bucket at `/data`, set `ORACLE_DATA_DIR=/data`, and set `ORACLE_QUESTION_LLM=1`. ## Config (env vars) | Var | Default | Purpose | |-----|---------|---------| | `ORACLE_QUESTION_LLM` | `1` | `1` = model-written questions via llama.cpp; `0` = built-in phrasing (no model) | | `ORACLE_REVEAL_LLM` | `0` | `1` = model writes the pre-guess reveal line; `0` = instant templated line | | `ORACLE_DISCOVERY_WEB` | `1` | `1` = allow Wikipedia grounding when teaching a new item | | `ORACLE_DATA_DIR` | _(bundled `data/`)_ | point at a mounted HF Storage Bucket (e.g. `/data`) to persist learned items; the model also caches in `