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