Chapter 4 of 8
Structured and JSON output
Prose is for humans. Software needs structure it can load. If your app takes a model's answer and does something with it, storing it, routing it, calling another function, then a paragraph of text is a problem you have to scrape, and scraping breaks. Ask the model for JSON instead and you get an object another program can parse, validate, and act on directly. In this lab you extract facts from a sentence two ways, once as free text that no parser can load, and once as JSON that loads cleanly into the exact fields you expected. The difference is the difference between a demo and a system.
The lab: read it, then run it
#!/usr/bin/env python3
"""
LAB PE4: Structured and JSON output.
Prose is for humans. Software needs structure it can parse. When you ask a model
for JSON, you get output another program can load, validate, and act on, instead
of a paragraph you have to scrape. In this lab you extract facts as JSON, load it
with a real json.loads, and prove it parses into the exact fields you expected,
while a free-text answer to the same content does not parse at all.
Run: python3 modules/academy-content/labs/prompt-engineering/pe4-structured-output.py
"""
import sys, os, json
_cands = [os.path.join(os.path.dirname(__file__), "..") if "__file__" in globals() else None,
os.path.join(os.getcwd(), "..", "labs"), os.path.join(os.getcwd(), "labs")]
for _c in _cands:
if _c and os.path.exists(os.path.join(_c, "academy_llm.py")):
sys.path.insert(0, os.path.abspath(_c)); break
from academy_llm import complete, chat, embed, cosine, tool_route
text = "My name is Ada and the number is 42 and my email is ada@example.com"
# 1. FREE TEXT: no structure asked for, so the reply is not machine-parseable.
free = complete(text)
print("STEP 1: free-text prompt (no structure)")
print(f" output : {free!r}")
try:
json.loads(free)
free_parses = True
except (json.JSONDecodeError, TypeError):
free_parses = False
# 2. STRUCTURED: ask for JSON. Now the output is a real object with named fields.
structured = complete("Extract JSON from this: " + text)
print("")
print("STEP 2: ask for JSON")
print(f" output : {structured!r}")
obj = json.loads(structured) # a real parse; raises if it is not JSON
print(f" parsed : name={obj.get('name')!r} number={obj.get('number')!r} email={obj.get('email')!r}")
# 3. The structured output parses into the exact fields; the free text does not.
fields_ok = (obj.get("name") == "Ada" and obj.get("number") == 42
and obj.get("email") == "ada@example.com")
print("")
print(f"STEP 3: JSON parsed into expected fields : {fields_ok}")
print(f" free text was parseable JSON : {free_parses}")
ok = fields_ok and not free_parses
print("")
print(f"OUTPUT PARSES AS JSON: {'YES' if ok else 'NO'}")
if not ok:
sys.exit(1)
print("Ask for structure and software can trust the output. Next: XML tagging.")
Runnable lab
pe4-structured-output.pyExtract facts as JSON, load it with a real parser, and prove it has the exact fields you expected.
Proves: OUTPUT PARSES AS JSON: YES
Runs in your browser via Pyodide. First run loads the runtime once; no install, no server.
The free-text answer could not be parsed, the JSON answer loaded into name, number, and email exactly as expected. That is why production systems ask for structure: the output becomes data, not prose. In real work you also define a schema so the shape is guaranteed, and you always wrap the parse in error handling, because a model can still return malformed output and your code must fail safely rather than crash. Next you learn to shape and separate the parts of a prompt with tags.
Check your understanding
1. Why ask a model for JSON output?
2. In the lab, what could you do with the JSON that you could not do with the free text?
3. Why still wrap the parse in error handling?