What the model does, and what it never does
The model has two narrow jobs:
- Parse a messy question into strict JSON (activity, place as written, radius, personal profile),
with output constrained by a JSON-schema grammar. Used only when rule-based parsing leaves a gap.
Any place the model returns must appear verbatim in the question, or it is discarded.
- Compose a 80–150 word answer from a fact sheet that the system builds from data fetched live
for that question (Nominatim, Overpass with an offline Geofabrik copy as fallback, Open-Meteo forecast
and air quality, OSRM).
The model is never asked to recall weather, air quality or places. Every number, time and proper noun
in its answer is checked against the fact sheet by a grounding verifier; a draft that fails is
regenerated once with feedback, and if it fails again the system falls back to a deterministic template.
Prompts
Parse (system prompt, few-shot, temperature 0, JSON schema grammar):
You extract structured fields from a question about an outdoor activity in India. activity must be one of ['running', 'cycling', 'golf', 'cricket', 'football', 'tennis', 'hiking', 'walking', 'picnic', 'event', 'photography', 'yoga', 'birdwatching', 'kite', 'stargazing'], or "out_of_scope" if the question is not about going outdoors, or "unknown" if unclear. location is the place name copied exactly from the question, or an empty string if none is written. radius_km is the distance the question states, or 0. profiles lists any of ['asthma', 'senior', 'child', 'pregnancy', 'heart'] that the question mentions (senior = elderly or parents, child = kids). Reply with JSON only.
Compose (system prompt, temperature 0.3, max 320 tokens):
You are a friendly outdoor-activity advisor for India. Write the answer using ONLY the facts in the FACT SHEET. Never add a number, place, time or weather detail that is not in the fact sheet, and copy place names and times exactly. Write 80-150 words of plain text (no headings, no tables): start with a one-sentence recommendation naming the place and time, then explain why in two or three short sentences using the strongest reasons, mention the main thing to avoid, and end with one practical tip. If the fact sheet says the activity is not advised, say so clearly first. Write flowing sentences: no labels such as "Tip:", and never copy the fact sheet's field names. Describe air quality only with the category the fact sheet gives (Good, Satisfactory, Moderate, Poor, Very Poor, Severe).
The exact JSON schema and generation settings are in geoai_config.json in this repository.
Usage
from transformers import AutoModelForCausalLM, AutoTokenizerrepo = "WIZARDIAN/Bhugoole-geoai-outdoor-slm"tok = AutoTokenizer.from_pretrained(repo)model = AutoModelForCausalLM.from_pretrained(repo, torch_dtype="auto")
The LoRA adapter alone, on top of the base model:
from peft import PeftModelbase = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-1.5B-Instruct")model = PeftModel.from_pretrained(base, "WIZARDIAN/Bhugoole-geoai-outdoor-slm", subfolder="adapter")
With llama.cpp (what the API uses):
from llama_cpp import Llamallm = Llama.from_pretrained("WIZARDIAN/Bhugoole-geoai-outdoor-slm", "bhugoole-geoai-outdoor-slm-q4_k_m.gguf")
Training
QLoRA fine-tune of Qwen/Qwen2.5-1.5B-Instruct for slot extraction (question -> strict JSON).
- Data: 5700 synthetic training questions and 120 held-out questions, generated by
finetune/make_parse_dataset.py (labels known by construction; typos, lower case, Hinglish fillers,
missing places and out-of-scope requests are over-sampled).
- LoRA r=16, alpha=32, dropout 0.05 on all attention and MLP projections; 4-bit NF4 base;
1.0 epoch(s), learning rate 0.0002, effective batch 16, loss on the JSON answer only.
- Hardware: NVIDIA RTX 4500 Ada Generation; wall time 28 min.
Scores below come from this script (transformers, 4-bit, greedy decoding); see the end of this file for the
live API's path (llama.cpp, JSON-schema decoding).
Table with columns: Metric, Synthetic held-out (120): base, fine-tuned, Real questions (30): base, fine-tuned, Unseen test (40): base, fine-tuned| Metric | Synthetic held-out (120): base | fine-tuned | Real questions (30): base | fine-tuned | Unseen test (40): base | fine-tuned |
|---|
| exact | 50.8% | 95.8% | 73.3% | 100.0% | 75.0% | 97.5% |
| activity | 66.7% | 95.8% | 93.3% | 100.0% | 87.5% | 100.0% |
| location | 76.7% | 100.0% | 86.7% | 100.0% | 95.0% | 97.5% |
| profiles | 85.0% | 100.0% | 86.7% | 100.0% | 90.0% | 100.0% |
| valid json | 100.0% | 100.0% | 100.0% | 100.0% | 100.0% | 100.0% |
The synthetic held-out set comes from the same templates as the training data, so it is optimistic.
finetune/real_eval.jsonl (30) and finetune/test_unseen.jsonl (40) are hand-written and never used for
training; the unseen set was written before this version was trained, with places and phrasings the generator
does not use.
On the live API's path (llama.cpp, Q4_K_M GGUF, JSON-schema-constrained decoding)
Measured with finetune/eval_parse.py. Version 1 was trained on 2,700 examples from the first generator; the error
analysis of version 1 on the 30 real questions showed two patterns (off-topic requests that name a place, and
places after unusual connectors such as "trek to sinhagad"), which version 2's generator covers in general form.
Because the 30 questions guided that analysis, the unseen set is the fairer measure.
Table with columns: Metric, Base Qwen2.5-1.5B, Version 1, Version 2 (deployed)| Metric | Base Qwen2.5-1.5B | Version 1 | Version 2 (deployed) |
|---|
| real questions (30), exact | 76.7% | 93.3% | 100.0% |
| unseen test (40), exact | 72.5% | 77.5% | 97.5% |
| unseen test (40), activity | 85.0% | 85.0% | 100.0% |
| unseen test (40), location | 90.0% | 80.0% | 97.5% |
| unseen test (40), profiles | 92.5% | 100.0% | 100.0% |
| off-topic requests caught (6 in the unseen set) | 1 | 1 | 6 |
| answers written by the SLM and passing the grounding check (8 live questions) | 7/8 | 8/8 | 7/8 |
The only miss of version 2 on the 70 hand-written questions: "Worli sea face" was read as "Worli sea".
Answer writing was not a training target; the 7/8 versus 8/8 difference is within run-to-run variation, and a draft
that fails the check is replaced by the verified template.
Intended use and limitations
- Intended for explaining outdoor-activity recommendations in India from supplied data. It is not a
weather or medical authority; health advice is limited to general precautions.
- English only. Hinglish words in questions are handled mostly by the rule-based parser.
- A 1.5B model can still phrase things awkwardly or omit details; the verifier blocks invented facts but
cannot guarantee fluency.
Disclosure
The running assistant uses only this open model; no hosted language model is called.