0% found this document useful (0 votes)
3 views5 pages

Pydantic JSON Notes

The document discusses the importance of structured output for LLMs, emphasizing that free-form text is difficult for computers to parse reliably. It introduces JSON as a solution for organizing data in key-value pairs, making it easier for both humans and machines to read. Pydantic is presented as a tool to enforce data structure, allowing for the definition of required fields and automatic generation of JSON schemas to ensure consistent output.

Uploaded by

shauryaspeaks99
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views5 pages

Pydantic JSON Notes

The document discusses the importance of structured output for LLMs, emphasizing that free-form text is difficult for computers to parse reliably. It introduces JSON as a solution for organizing data in key-value pairs, making it easier for both humans and machines to read. Pydantic is presented as a tool to enforce data structure, allowing for the definition of required fields and automatic generation of JSON schemas to ensure consistent output.

Uploaded by

shauryaspeaks99
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Structured Output: Pydantic & JSON

AI Engineer Course — Episode 05 (Free 8-Week Track) | Notes with diagrams, analogies & code panels

1. Why Structured Output?


So far, every LLM response was just a big string (paragraph) — fine for a human reading a chat window,
but "parsing" meaning out of free-form paragraphs is one of the hardest things for a computer program to
do reliably.

ANALOGY — The Watchman's Logbook

A watchman is told to record who enters a building and their car number. Option A: write a full
sentence in his diary — "Pratyush came at 2pm in a DL-plated car." This is unstructured —
readable by a human, but tedious for anyone else (or a computer) to search through consistently.
Option B: write fixed fields every time — Name: Pratyush, Number Plate: DLXE7338. This
is structured — short to write, trivial to read, and any later reader (human or program) knows
exactly where to look for each piece of information.

In real companies, an LLM's output is almost never read by a human directly — it feeds into another piece
of code (which could be written in C++, Java, JavaScript, Go, anything) that has to make a decision based
on it. That code cannot reasonably run 1 million times a day with a human manually reading each reply,
and it cannot reliably "parse" a rambling paragraph either.

Customer Email Response (must Downstream


LLM
(free text) be parseable) Code (decision)

Even if the LLM correctly extracts name, email, phone, and issue from a messy complaint email, if it wraps
the answer in chatty filler ("Sure! Let me help you with that...") the response is still just a string, and the
downstream code has to do fragile, error-prone string-matching to dig the real values out. This kind of string
parsing is one of the hardest and most brittle things to get right in a computer program.

2. JSON — The Fix


JSON (JavaScript Object Notation) predates LLMs entirely — it's a long-standing, widely used format for
how different computers/servers (e.g., one in the US, one in Mumbai, one in Delhi) exchange data with
each other reliably.
{
"name": "Pratyush"

"email": "abc@[Link]"

"issue": "Electronics"

Instead of a paragraph, data is organized as explicit key : value pairs inside curly braces. This is both easy
for a human to scan and, more importantly, trivial for code to read — no manual parsing algorithm needed.
A program can just ask for [Link] or [Link] and get the exact value directly, instead of hunting
through a paragraph.

• Unstructured output = free-form paragraph/string; no guaranteed shape; hard for code to reliably
extract values from.
• Structured output = fixed key-value shape (like JSON); trivial for code to extract exact fields with
zero ambiguity.
• Plan going forward: always convert the LLM's raw output into JSON format before passing it
downstream — never pass the raw string as-is.

3. Pydantic — Enforcing the Shape


Knowing you want JSON isn't enough — you need a way to tell the LLM exactly which fields you require
(and implicitly, which ones to ignore, since a real user's message may contain irrelevant extra details like
their address, father's name, or Aadhaar number). Pydantic is a Python library used to define that exact
shape.

Define Ticket Generate Pass schema LLM returns


class (Pydantic) JSON schema in system prompt JSON matching schema

How it fits together


• A Pydantic BaseModel class (e.g., Ticket) declares only the fields you actually need, each with its
type (e.g., name: str, email: str, category: str).
• Ticket.model_json_schema() converts that class definition into a JSON schema — a
machine-readable description of the required shape.
• That schema is embedded inside the system prompt, explicitly instructing the model: extract
information and return it strictly in JSON matching this schema, nothing extra.
• The API call also sets response_format to a JSON object type, reinforcing (at the API level) that
the reply must be JSON.
• Any extra details the user included (girlfriend's name, address, etc.) are simply ignored because they
aren't part of the declared schema.
4. Code Walkthrough
CODE — Defining the schema with Pydantic

from pydantic import BaseModel

class Ticket(BaseModel):
name: str
email: str
category: str

schema = Ticket.model_json_schema()

response_format = {"type": "json_object"}

CODE — System prompt that references the schema

system_prompt = f"""
Extract the personal information and, strictly based on
this schema, return the output in JSON format matching
this schema: {schema}
And give me a JSON output.
"""

message_system = {"role": "system", "content": system_prompt}

Gotcha hit in the demo: passing response_format={"type": "json_object"} alone was NOT
enough — Groq's API rejected it with an error saying the messages must contain the word "json"
somewhere. response_format only tells the API layer the output type; the system prompt itself must
explicitly ask for JSON output, or the call fails.

CODE — User message + calling the model

text = """
Hello, my name is Pratyush. I purchased an iPhone from
your store and it stopped working. My address is Delhi.
Please contact me at abc@[Link].
"""

prompt = f"This is a customer ticket. Please extract the personal information from this: {tex

message = {"role": "user", "content": prompt}


messages = [message_system, message]

response = [Link](
model=model,
messages=messages,
response_format=response_format
)
CODE — Parsing the JSON reply

import json

raw_json = [Link][0].[Link]
data_file = [Link](raw_json)

ticket = Ticket(**data_file)

print([Link])
print([Link])
print([Link])

Result: extra fields the user typed (address, an unrelated personal remark) were automatically ignored,
since they weren't part of the Ticket schema — only name, email, and category came through, cleanly.

5. Errors Hit & Fixes (from the live demo)


• Class definition missed the parent: error was "Ticket has no attribute model_json_schema" —
caused by writing class Ticket: instead of class Ticket(BaseModel):. Forgetting to inherit
from BaseModel means Pydantic's schema methods don't exist on the class.
• API rejected response_format alone: "messages must contain the word 'json' in some form to use
response_format" — fixed by explicitly asking for JSON output inside the system prompt text itself, not
just via the parameter.
• Undefined variable name typo: wrote data instead of the actual variable data_file when
constructing the Ticket object — another reminder that most errors are simple naming mismatches,
not deep logic bugs.

6. Homework: Resume-to-JD Matcher (Mini Project)


ANALOGY — The HR Screener

An HR person downloads a resume, reads it, compares it against the job description in her head,
and decides who to call. Your mini-project automates that: build a program that reads a resume
(PDF/Word) via file handling, takes a job description (required experience, skills, project types) as
input, and asks an LLM to output a structured match score.

• Read a resume file (PDF or Word) using file handling.


• Accept a JD-style requirement list (e.g., experience: 2 years, skills: Python/C++/Java/JS, project type:
ML project).
• Extract the resume's actual experience, skills, and project sections (structured, via Pydantic/JSON —
not raw string parsing).
• Compare extracted resume data against the JD requirements.
• Output a match percentage, e.g., "82% good fit" → HR calls the candidate; "38% match" → HR skips
them.

7. Key Takeaways
• Free-form string output works fine for humans reading a chat, but is fragile and hard to parse reliably
for downstream code.
• JSON (key:value format) is the standard fix — easy for both humans and computers to read, and
requires no manual parsing algorithm.
• Pydantic lets you define exactly which fields you need (a BaseModel class) and auto-generate a
JSON schema from it.
• The system prompt must explicitly ask for JSON output AND reference the schema —
response_format alone isn't sufficient.
• Fields not declared in your Pydantic model are automatically dropped/ignored, even if the user's raw
input included them.
• This pattern (LLM output → JSON → downstream code) is the standard way structured AI agents are
built in real companies.

You might also like