oracle / README.md
vivek gangadharan
readme update
70a2974
|
Raw
History Blame Contribute Delete
8.32 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade
metadata
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 (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 (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

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.

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)

  • 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 โ€” 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 โ€” 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

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):

ORACLE_QUESTION_LLM=1 python app.py

(Alternatively, run a local llama-server and set ORACLE_LLAMA_URL.)

Test & validate

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 โ€” 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