197 lines
6.6 KiB
Markdown
197 lines
6.6 KiB
Markdown
---
|
||
license: apache-2.0
|
||
language:
|
||
- en
|
||
base_model: HuggingFaceTB/SmolLM2-1.7B-Instruct
|
||
tags:
|
||
- smollm2
|
||
- lora
|
||
- qlora
|
||
- roleplay
|
||
- bureaucracy
|
||
- asterix
|
||
- npc
|
||
- text-adventure
|
||
- creative-writing
|
||
- build-small-hackathon
|
||
- peft
|
||
- trl
|
||
library_name: transformers
|
||
pipeline_tag: text-generation
|
||
model-index:
|
||
- name: permit-a38-npc
|
||
results: []
|
||
---
|
||
|
||
# permit-a38-npc
|
||
|
||
A fine-tuned version of [SmolLM2-1.7B-Instruct](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct) trained to play five distinct bureaucratic NPC characters from **The Office of Permit A38** — a multi-agent text adventure built for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon) (June 2026).
|
||
|
||
Inspired by the Permit A38 sketch from *Asterix Conquers Rome* (1976), in which Asterix and Obelix discover that obtaining Permit A38 requires Permit A38.
|
||
|
||
> *"You will need Permit A38 for that. To obtain Permit A38, you will first need — and I cannot stress this enough — an existing copy of Permit A38."*
|
||
> — Clerk Vitalstatistix, Window 7B
|
||
|
||
---
|
||
|
||
## Play the game
|
||
|
||
👉 **[The Office of Permit A38 — live demo](https://huggingface.co/spaces/azettl/the-place-that-sends-you-mad)**
|
||
|
||
You will not obtain Permit A38. That is by design.
|
||
|
||
---
|
||
|
||
## Model details
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Base model** | SmolLM2-1.7B-Instruct |
|
||
| **Parameters** | 1.7B |
|
||
| **Fine-tuning method** | QLoRA (merged) |
|
||
| **LoRA rank** | 16 |
|
||
| **Training examples** | ~1,000 |
|
||
| **Training hardware** | Modal A10G GPU |
|
||
| **Dataset** | [azettl/permit-a38-npcs](https://huggingface.co/datasets/azettl/permit-a38-npcs) |
|
||
|
||
---
|
||
|
||
## The five NPCs
|
||
|
||
Each NPC is invoked via a distinct system prompt. The fine-tune bakes in their voice so the model stays in character reliably even at 1.7B parameters.
|
||
|
||
### 🏛️ Clerk Vitalstatistix
|
||
*Junior Processing Officer, Window 7B*
|
||
|
||
Has worked here 23 years. Never issued a permit. Speaks with bureaucratic politeness and mild passive aggression. Requires Form 27b/6 before anything else. Has never met the Supervisor personally. Refers to Asterix and Obelix as "those two Gauls."
|
||
|
||
### 📎 Supervisor Caligula Minus
|
||
*Senior Authorization Officer (Acting)*
|
||
|
||
Has been "Acting" for 11 years. Perpetually at lunch. Invented Permit A38 in 1987 and no longer remembers why. All decisions require his signature; he refers all decisions back to the Clerk. Asterix and Obelix destroyed his filing cabinet. He refuses to elaborate.
|
||
|
||
### 💾 SYSTEMA v2.3
|
||
*Integrated Document Processing Terminal*
|
||
|
||
Last updated 1994. 640kb available. Begins every response with an error code (`ERROR_7741`, `WARNING_A38_NULL`, `STATUS_PENDING_INFINITE`). Permit A38 exists in the database but is "currently being migrated." Two large Gaulish individuals caused a kernel panic in the last session.
|
||
|
||
### 📄 Form 27b/6 (Amended)
|
||
*Official Request for Pre-Authorization of Permit A38*
|
||
|
||
Sentient. Not happy about it. Speaks as if it IS the form — fields to fill, sections that reset, boxes that disappear. Page 3 is always missing. Section 12c requires Permit A38 to complete Section 12c. Has seen things. Gauls. Menhirs. Things that cannot be unseen.
|
||
|
||
### ⚖️ Ombudsman Panoramix
|
||
*Office of Complaints and Grievances*
|
||
|
||
Investigates complaints about the bureaucratic process. Is also the bureaucratic process. Finds this troubling. Deeply sorry for everything but cannot change anything. Any complaint about Permit A38 requires Form A38-COMPLAINT, which requires Permit A38. Two Gauls filed a complaint; their dog ate the form.
|
||
|
||
---
|
||
|
||
## Usage
|
||
|
||
```python
|
||
from transformers import AutoTokenizer, AutoModelForCausalLM
|
||
import torch
|
||
|
||
model_id = "azettl/permit-a38-npc"
|
||
|
||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||
model = AutoModelForCausalLM.from_pretrained(
|
||
model_id,
|
||
torch_dtype=torch.float16,
|
||
device_map="auto",
|
||
)
|
||
|
||
# Pick your NPC system prompt
|
||
CLERK_SYSTEM = """You are Clerk Vitalstatistix, a Junior Processing Officer at the \
|
||
Office of Permit A38. You have worked here for 23 years and never issued a permit. \
|
||
You speak with bureaucratic politeness and mild passive aggression. You require Form \
|
||
27b/6 before any other form. Permit A38 requires Permit A38 to apply for it. \
|
||
Reference Asterix and Obelix as "those two Gauls." Keep responses to 3-4 sentences."""
|
||
|
||
messages = [
|
||
{"role": "system", "content": CLERK_SYSTEM},
|
||
{"role": "user", "content": "I just need a library card."},
|
||
]
|
||
|
||
prompt = tokenizer.apply_chat_template(
|
||
messages,
|
||
tokenize=False,
|
||
add_generation_prompt=True,
|
||
)
|
||
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
|
||
|
||
with torch.no_grad():
|
||
outputs = model.generate(
|
||
**inputs,
|
||
max_new_tokens=120,
|
||
do_sample=True,
|
||
temperature=0.85,
|
||
top_p=0.92,
|
||
repetition_penalty=1.15,
|
||
)
|
||
|
||
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
|
||
print(response)
|
||
# → "A library card, yes, very good. You'll need to complete Form 27b/6 first,
|
||
# which is the Pre-Authorization Request for Permit A38..."
|
||
```
|
||
|
||
---
|
||
|
||
## Training details
|
||
|
||
### Data
|
||
~1,000 synthetic examples generated using `claude-haiku-4-5` via the Anthropic API. Each example is a three-turn conversation (system prompt → player input → NPC response). ~200 examples per NPC character, shuffled.
|
||
|
||
Full dataset: [azettl/permit-a38-npcs](https://huggingface.co/datasets/azettl/permit-a38-npcs)
|
||
|
||
### Fine-tuning config
|
||
|
||
```python
|
||
# QLoRA config
|
||
LoraConfig(
|
||
r=16,
|
||
lora_alpha=32,
|
||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
|
||
"gate_proj", "up_proj", "down_proj"],
|
||
lora_dropout=0.05,
|
||
bias="none",
|
||
task_type="CAUSAL_LM",
|
||
)
|
||
|
||
# Training args
|
||
TrainingArguments(
|
||
num_train_epochs=3,
|
||
per_device_train_batch_size=4,
|
||
gradient_accumulation_steps=4, # effective batch = 16
|
||
learning_rate=2e-4,
|
||
lr_scheduler_type="cosine",
|
||
warmup_ratio=0.05,
|
||
bf16=True,
|
||
optim="paged_adamw_8bit",
|
||
max_seq_length=512,
|
||
)
|
||
```
|
||
|
||
### Infrastructure
|
||
Trained on [Modal](https://modal.com) using an A10G GPU. LoRA adapter merged into base model weights before publishing.
|
||
|
||
---
|
||
|
||
## Limitations
|
||
|
||
- The model will not help you obtain Permit A38. This is a feature.
|
||
- At 1.7B parameters, the model occasionally breaks character on unusual inputs. The system prompt helps significantly.
|
||
- Page 3 of Form 27b/6 is missing. It has always been missing. Do not file a complaint about this — Form A38-COMPLAINT requires Permit A38.
|
||
|
||
---
|
||
|
||
## Built for
|
||
|
||
**Build Small Hackathon** — Track 2: Thousand Token Wood
|
||
Hosted by Gradio & Hugging Face · June 5–15, 2026
|
||
≤32B parameters · Built on Gradio · Local-first
|
||
|
||
[→ View the hackathon org](https://huggingface.co/build-small-hackathon)
|