Turning unstructured text — emails, tickets, resumes, log excerpts, form responses — into clean structured data is where AI saves the most drudgery for IT professionals. The technique that makes it reliable is schema-first extraction: you define the exact output shape and null-handling rules before the model sees any data, then validate the output mechanically.
Tier: Foundation · Hands-on time: ~30 minutes
python3 or jq on your machine for validation.Write the schema and rules first. Be explicit about types, enums, and what to do with missing data:
You are an extraction engine. Extract data from the text I provide into
JSON matching EXACTLY this schema:
{
"ticket_id": "string or null",
"customer": "string or null",
"category": "one of: billing | access | bug | feature-request | other",
"severity": "one of: low | medium | high | unknown",
"component": "string or null",
"requested_action": "string, max 20 words, or null",
"dates_mentioned": ["YYYY-MM-DD"]
}
Rules:
- Output ONLY a JSON array of objects, no markdown fences, no commentary.
- If a field is not present in the text, use null. NEVER invent values.
- Normalize dates to YYYY-MM-DD; if the year is ambiguous, use null.
- If a text item is not a ticket at all, still emit an object with
ticket_id null and category "other".
Reply "READY" and wait for the data.
Calibrate on 3 samples. Paste three items, including your ugly edge case. Check the output by hand against the source text, field by field. Fix the rules (not the data) where it goes wrong — e.g., add "phone numbers are not ticket_ids".
Run the batch. Paste items in chunks of 10–20, numbered, so you can map outputs back to inputs. Keep chunks small: accuracy degrades quietly on long batches.
Validate mechanically. Save the output as out.json and run:
python3 - <<'EOF'
import json
rows = json.load(open("out.json"))
allowed = {"billing","access","bug","feature-request","other"}
for i, r in enumerate(rows):
assert set(r) == {"ticket_id","customer","category","severity",
"component","requested_action","dates_mentioned"}, (i, "keys")
assert r["category"] in allowed, (i, r["category"])
print(f"OK: {len(rows)} rows, schema valid")
EOF
A parse error or assertion failure means regenerate that chunk — never hand-patch model output, you will miss the second error.
Human-audit a sample. Randomly pick 10% of rows (minimum 5) and compare every field against the source text. If more than one field in the sample is wrong, tighten rules and re-run the batch.
Item counts are the cheapest correctness check — 17 inputs must produce exactly 17 output objects. Models silently drop or merge items on long batches.
format json (Ollama flag) to force valid JSON; keep chunks at ~5 items for 8B-class models.python3 validation passes: parseable JSON, exact key set, enum values legal.``` in your validator anyway.CUST_001, a***@***.com); keep the mapping in a local file and rejoin after extraction — the structure survives pseudonymization intact.format json runs this entire playbook locally; accuracy at 8B is close to hosted models for extraction because the task requires no external knowledge.