The Idea

I was building an AI agent that sends a big chunk of context every turn — a dungeon state, a codebase, a document — and I kept hitting the same wall: tokens are expensive, and they run out. The agent burns through its budget fast, and suddenly it's blind, forgetful, dead.

Then I asked the obvious question: what if running out of tokens was the game?

What if your token budget was your health bar? What if every turn, the entire world-state got compressed before the LLM saw it — and the compression savings were literally refunded to your health? What if efficient play meant playing longer?

That's when I found Paritok. It compresses context in transit — real compression, not truncation — and has a shadow-storage expand_context recall mechanic where the LLM can pull back compressed details when it needs them. It was exactly the piece I needed: a real compression layer that turns token efficiency into a survival mechanic.

So I built a dungeon crawler around it.


How It Works

┌─────────────────────────────────────────────────────────────────────┐
│                      A SINGLE TURN IN THE GAME                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. Player types: "go east"                                         │
│                         │                                           │
│  2. Engine builds: system prompt                                    │
│                    + world-state tool result (~5,000 chars)         │
│                    + "go east" user message                         │
│                         │                                           │
│  3. Paritok intercepts the tool result:                             │
│     ┌───────────────────────────────────────────┐                   │
│     │  ORIGINAL:  ~1,350 tokens                  │                   │
│     │  COMPRESSED: ~80 tokens  (94% smaller)     │                   │
│     │  The LLM sees: a compact reference         │                   │
│     │  + [REF:id] tags for shadow storage        │                   │
│     └───────────────────────────────────────────┘                   │
│                         │                                           │
│  4. LLM reads compressed state → narrates the turn                  │
│     (needs exact detail? calls expand_context — costs tokens)       │
│                         │                                           │
│  5. Memory ledger: 1,350 orig − saved = actual cost                │
│     Compression savings are refunded to your Memory bar             │
│                         │                                           │
│  6. GM emits <<STATE>> block: move, take, combat, victory...        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

The Token Economy

Your Memory starts at 20,000 tokens. Every turn:

What happens Tokens
Full world-state sent to GM (before Paritok) ~1,350
Paritok compresses it (after Paritok) ~80
You pay the compressed amount ~80
Savings returned to Memory ~1,270

Without Paritok: each turn costs ~1,350 tokens → you die in ~15 turns. With Paritok: each turn costs ~80 tokens → you survive ~250 turns.

That's a 12× longer game from compression alone.


Challenges I Faced

1. The compression query problem. Paritok uses the last user message as its compression query — the text that tells it what to keep. A terse "go east" made it keep the entire map (2% savings). The fix: a carefully worded keep-list directive in the query that tells the compressor exactly what to retain and what to drop. Took hours of A/B testing against real dungeon content to find phrasing that works across exploration, combat, and puzzle turns.

2. The stats tax. Paritok injects a virtual expand_context tool into the tool list, which adds a fixed ~271 tokens to the "compressed" side every turn. The token ledger showed negative savings — the math looked broken. Had to reverse-engineer the proxy's tool-schema accounting and subtract the constant overhead in the stats delta so the ledger reflects content-only compression.

3. Combat never ending. The GM narrated "Combat — Round 6" forever because the game state had no way to track a slain monster. The snapshot kept re-introducing the dead guardian as alive, the GM kept fighting, and the player just burned vigor to death. Had to add a slain field to game state, the <<STATE>> schema, the snapshot renderer, and the DB persistence layer — all wired together so the GM can end fights and the snapshot stops showing dead monsters.

4. Hosted GPU variance. The Paritok hosted endpoint sometimes returns different compression results for the same content — 90% one run, 10% the next. Different server replicas have different compression behaviors. The aggressive keep-list query mostly fixes this, but occasional slow replicas (~6s vs ~2.5s) still slip through.


Tech Stack

Layer Technology
Game Master NVIDIA NIM (z-ai/glm-5.2) — free, OpenAI-compatible
Compression Paritok paritok-4b-v1 (hosted GPU) — real context compression
Backend Python 3.12, FastAPI, uvicorn
Database SQLite (runs, turns, leaderboard, resume snapshots)
Frontend Vanilla HTML/CSS/JS — no build step, no frameworks
Proxy Embedded in-process (paritok.proxy.server.create_app)

No paid infrastructure. No deployment services. Runs on any machine with Python 3.11+.

Built With

Share this project:

Updates