oracle / README.md
vivek gangadharan
readme update
70a2974
|
Raw
History Blame Contribute Delete
8.32 kB
---
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
[![Demo Video - Youtube](https://img.youtube.com/vi/U5UNzHBfJ1k/maxresdefault.jpg)](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 `<dir>/models/` |
| `ORACLE_MODEL_DIR` | _(`<ORACLE_DATA_DIR>/models`)_ | override where the GGUF model is downloaded/cached |
| `ORACLE_LLAMA_REPO` / `ORACLE_LLAMA_FILE` | Llama-3.2-3B GGUF | swap the model (e.g. the 1B for more speed) |
| `ORACLE_LLAMA_URL` | `http://localhost:8080/v1/chat/completions` | external llama-server endpoint (HTTP fallback) |
| `ORACLE_LLAMA_THREADS` | _(cores available, capped at 4)_ | llama.cpp threads โ€” keep small on CPU Spaces |
| `ORACLE_LLM_TIMEOUT` | `25` | seconds before a slow generation gives up and uses built-in phrasing |
| `ORACLE_MAX_QUESTIONS` | `20` | force a guess after this many questions |
---
**MIT**