Instructor: Mendapatkan Structured Output dari LLM dengan Python

# Instructor: Mendapatkan Structured Output dari LLM dengan Python Salah satu tantangan terbesar saat bekerja dengan Large Language Models (LLM) adalah mendapatkan output yang terstruktur dan konsist...

By Ruby Abdullah · · tutorial
InstructorLLMPydanticStructured OutputPython

Instructor: Mendapatkan Structured Output dari LLM dengan Python

Salah satu tantangan terbesar saat bekerja dengan Large Language Models (LLM) adalah mendapatkan output yang terstruktur dan konsisten. LLM secara default menghasilkan teks bebas, yang sulit di-parse dan diintegrasikan ke dalam aplikasi. Library Instructor hadir untuk menyelesaikan masalah ini dengan memanfaatkan Pydantic untuk validasi dan ekstraksi data terstruktur dari LLM.

Dalam tutorial ini, kita akan mempelajari cara menggunakan Instructor untuk mendapatkan output JSON/Pydantic yang reliable dari berbagai LLM provider seperti OpenAI, Anthropic, dan lainnya.

Apa Itu Instructor?

Instructor adalah library Python yang meng-patch client LLM (seperti OpenAI) agar bisa mengembalikan objek Pydantic yang tervalidasi, bukan sekadar string. Instructor bekerja dengan memanfaatkan function calling atau JSON mode dari LLM, kemudian memvalidasi hasilnya menggunakan Pydantic.

Keunggulan utama Instructor:

  • Type-safe: Output dijamin sesuai dengan schema Pydantic yang didefinisikan
  • Automatic retry: Jika validasi gagal, Instructor otomatis me-retry dengan feedback error
  • Streaming support: Mendukung partial streaming untuk objek yang kompleks
  • Multi-provider: Mendukung OpenAI, Anthropic, Google, Mistral, dan lainnya
  • Validasi kustom: Bisa menambahkan validator Pydantic untuk business logic

Instalasi

Pertama, install Instructor beserta dependensi yang diperlukan:

pip install instructor openai pydantic

Untuk provider lain, install dependensi tambahan:

# Untuk Anthropic

pip install instructor anthropic

Untuk Google Gemini

pip install instructor google-generativeai

Untuk Mistral

pip install instructor mistralai

Pastikan Anda memiliki API key dari provider yang akan digunakan:

export OPENAIAPIKEY="sk-your-api-key-here"

Penggunaan Dasar dengan OpenAI

Mari mulai dengan contoh sederhana: mengekstrak informasi pengguna dari teks.

import instructor

from openai import OpenAI

from pydantic import BaseModel

Patch client OpenAI dengan Instructor

client = instructor.fromopenai(OpenAI())

Definisikan schema output

class UserInfo(BaseModel):

name: str

age: int

email: str

Ekstrak data terstruktur dari teks

user = client.chat.completions.create(

model="gpt-4o-mini",

responsemodel=UserInfo,

messages=[

{

"role": "user",

"content": "Nama saya Budi Santoso, umur 28 tahun. "

"Email saya budi.santoso@email.com"

}

],

)

print(user)

UserInfo(name='Budi Santoso', age=28, email='budi.santoso@email.com')

print(user.name) # Budi Santoso

print(user.age) # 28

print(user.email) # budi.santoso@email.com

Perhatikan bahwa responsemodel=UserInfo adalah parameter kunci yang memberitahu Instructor schema apa yang diharapkan. Hasilnya bukan dictionary atau string, melainkan objek Pydantic yang sudah tervalidasi.

Pydantic Models yang Lebih Kompleks

Instructor mendukung model Pydantic yang kompleks termasuk nested models, optional fields, enums, dan lists.

from pydantic import BaseModel, Field

from typing import Optional, List

from enum import Enum

class JobLevel(str, Enum):

JUNIOR = "junior"

MID = "mid"

SENIOR = "senior"

LEAD = "lead"

class Skill(BaseModel):

name: str = Field(description="Nama skill atau teknologi")

yearsexperience: int = Field(

description="Tahun pengalaman", ge=0, le=50

)

proficiency: str = Field(

description="Tingkat kemahiran: beginner, intermediate, advanced"

)

class WorkExperience(BaseModel):

company: str

role: str

durationmonths: int = Field(ge=1)

description: str

class CandidateProfile(BaseModel):

name: str

currentrole: str

level: JobLevel

totalyearsexperience: int = Field(ge=0)

skills: List[Skill]

workhistory: List[WorkExperience]

education: str

summary: str = Field(

description="Ringkasan profil kandidat dalam 2-3 kalimat"

)

resumetext = """

Saya Ahmad Rizki, saat ini bekerja sebagai Senior Data Engineer di Tokopedia

selama 3 tahun. Sebelumnya saya bekerja di Gojek sebagai Data Engineer

selama 2 tahun. Saya memiliki pengalaman 5 tahun di bidang data engineering

dengan keahlian utama di Python (5 tahun, advanced), Apache Spark (4 tahun,

advanced), dan SQL (5 tahun, advanced). Saya juga familiar dengan Kafka

(2 tahun, intermediate) dan Kubernetes (1 tahun, beginner).

Pendidikan S1 Teknik Informatika dari ITB.

"""

profile = client.chat.completions.create(

model="gpt-4o",

responsemodel=CandidateProfile,

messages=[

{

"role": "system",

"content": "Ekstrak profil kandidat dari teks resume berikut."

},

{"role": "user", "content": resumetext}

],

)

print(f"Nama: {profile.name}")

print(f"Level: {profile.level.value}")

print(f"Skills:")

for skill in profile.skills:

print(f" - {skill.name}: {skill.yearsexperience} tahun "

f"({skill.proficiency})")

Validasi dengan Pydantic Validators

Salah satu fitur paling powerful dari Instructor adalah kemampuan menambahkan validasi kustom. Jika validasi gagal, Instructor akan otomatis me-retry dan mengirimkan pesan error ke LLM.

from pydantic import BaseModel, Field, fieldvalidator, modelvalidator

class SQLQuery(BaseModel):

query: str = Field(description="SQL query yang valid")

explanation: str = Field(description="Penjelasan query dalam bahasa Indonesia")

tablesused: List[str] = Field(description="Daftar tabel yang digunakan")

@fieldvalidator("query")

@classmethod

def validatenodelete(cls, v: str) -> str:

if "DELETE" in v.upper() or "DROP" in v.upper():

raise ValueError(

"Query tidak boleh mengandung DELETE atau DROP "

"demi keamanan data"

)

return v

@fieldvalidator("query")

@classmethod

def validatehasselect(cls, v: str) -> str:

if not v.strip().upper().startswith("SELECT"):

raise ValueError("Query harus dimulai dengan SELECT")

return v

@modelvalidator(mode="after")

def validatetablesmatchquery(self):

for table in self.tablesused:

if table.lower() not in self.query.lower():

raise ValueError(

f"Tabel '{table}' tidak ditemukan dalam query"

)

return self

result = client.chat.completions.create(

model="gpt-4o-mini",

responsemodel=SQLQuery,

messages=[

{

"role": "user",

"content": "Buatkan query untuk menampilkan 10 customer "

"dengan total pembelian tertinggi dari tabel "

"customers dan orders"

}

],

)

print(f"Query: {result.query}")

print(f"Penjelasan: {result.explanation}")

print(f"Tabel: {result.tablesused}")

Retry Logic dan Error Handling

Instructor memiliki mekanisme retry built-in yang sangat berguna saat LLM menghasilkan output yang tidak valid.

from instructor import retry

from tenacity import retry, stopafterattempt, waitfixed

Konfigurasi retry saat membuat client

client = instructor.fromopenai(

OpenAI(),

maxretries=3, # Maksimal 3 kali retry

)

Atau konfigurasi retry per-request

class StrictOutput(BaseModel):

sentiment: str = Field(

description="Sentiment: positive, negative, atau neutral"

)

confidence: float = Field(ge=0.0, le=1.0)

keyphrases: List[str] = Field(minlength=1, maxlength=5)

@fieldvalidator("sentiment")

@classmethod

def validatesentiment(cls, v: str) -> str:

allowed = {"positive", "negative", "neutral"}

if v.lower() not in allowed:

raise ValueError(

f"Sentiment harus salah satu dari: {allowed}"

)

return v.lower()

result = client.chat.completions.create(

model="gpt-4o-mini",

responsemodel=StrictOutput,

maxretries=5,

messages=[

{

"role": "user",

"content": "Analisis sentimen: 'Produk ini sangat bagus, "

"pengiriman cepat, tapi packaging kurang rapi'"

}

],

)

print(f"Sentimen: {result.sentiment}")

print(f"Confidence: {result.confidence}")

print(f"Key phrases: {result.keyphrases}")

Saat validasi gagal, Instructor secara otomatis:

  • Menangkap error validasi dari Pydantic
  • Mengirim ulang request ke LLM dengan pesan error sebagai feedback
  • LLM memperbaiki output berdasarkan feedback
  • Proses diulang hingga validasi berhasil atau max retry tercapai
  • Streaming dengan Partial Objects

    Untuk objek yang besar, Instructor mendukung streaming sehingga Anda bisa menerima data secara bertahap.

    from instructor import Partial
    
    

    class Article(BaseModel):

    title: str

    summary: str

    keypoints: List[str]

    conclusion: str

    Streaming dengan Partial

    stream = client.chat.completions.create(

    model="gpt-4o-mini",

    responsemodel=Partial[Article],

    stream=True,

    messages=[

    {

    "role": "user",

    "content": "Tulis artikel pendek tentang manfaat AI "

    "dalam healthcare"

    }

    ],

    )

    for partialarticle in stream:

    # Setiap iterasi mendapatkan objek Article yang partially filled

    if partialarticle.title:

    print(f"Title: {partialarticle.title}")

    if partialarticle.keypoints:

    print(f"Points so far: {len(partialarticle.keypoints)}")

    Contoh Praktis: Ekstraksi Data dari Teks

    Berikut contoh real-world untuk mengekstrak informasi produk dari deskripsi e-commerce.

    from pydantic import BaseModel, Field
    

    from typing import List, Optional

    class ProductSpec(BaseModel):

    key: str = Field(description="Nama spesifikasi")

    value: str = Field(description="Nilai spesifikasi")

    class ExtractedProduct(BaseModel):

    name: str = Field(description="Nama produk")

    brand: str = Field(description="Merek produk")

    priceidr: Optional[int] = Field(

    description="Harga dalam Rupiah", default=None

    )

    category: str = Field(description="Kategori produk")

    specifications: List[ProductSpec] = Field(

    description="Spesifikasi teknis produk"

    )

    pros: List[str] = Field(description="Kelebihan produk")

    cons: List[str] = Field(description="Kekurangan produk")

    productdescription = """

    Review Samsung Galaxy S24 Ultra - Harga Rp 19.999.000

    Samsung kembali menghadirkan flagship terbaiknya. Galaxy S24 Ultra hadir

    dengan layar Dynamic AMOLED 2X 6.8 inci, prosesor Snapdragon 8 Gen 3,

    RAM 12GB, dan penyimpanan 256GB. Kamera utama 200MP menghasilkan foto

    yang sangat detail. Baterai 5000mAh tahan seharian.

    Kelebihannya adalah performa sangat kencang, kamera luar biasa, dan

    S Pen yang berguna. Kekurangannya harga yang mahal dan bobot yang cukup

    berat di 233 gram.

    """

    product = client.chat.completions.create(

    model="gpt-4o",

    responsemodel=ExtractedProduct,

    messages=[

    {

    "role": "system",

    "content": "Ekstrak informasi produk dari review berikut."

    },

    {"role": "user", "content": productdescription}

    ],

    )

    print(f"Produk: {product.brand} {product.name}")

    print(f"Harga: Rp {product.priceidr:,}")

    print(f"Kategori: {product.category}")

    print("Spesifikasi:")

    for spec in product.specifications:

    print(f" {spec.key}: {spec.value}")

    Contoh Praktis: Klasifikasi Teks

    Instructor sangat cocok untuk tugas klasifikasi karena output dijamin sesuai dengan kategori yang ditentukan.

    from enum import Enum
    

    from typing import List

    class TicketPriority(str, Enum):

    LOW = "low"

    MEDIUM = "medium"

    HIGH = "high"

    CRITICAL = "critical"

    class TicketCategory(str, Enum):

    BUG = "bug"

    FEATUREREQUEST = "featurerequest"

    QUESTION = "question"

    COMPLAINT = "complaint"

    BILLING = "billing"

    class ClassifiedTicket(BaseModel):

    priority: TicketPriority

    category: TicketCategory

    department: str = Field(

    description="Departemen yang bertanggung jawab"

    )

    summary: str = Field(

    description="Ringkasan tiket dalam 1 kalimat", maxlength=200

    )

    suggestedresponse: str = Field(

    description="Saran respons awal untuk customer service"

    )

    requiresescalation: bool = Field(

    description="Apakah perlu dieskalasi ke level lebih tinggi"

    )

    tickettext = """

    URGENT: Sistem pembayaran error sejak kemarin. Semua transaksi gagal

    dan customer tidak bisa checkout. Sudah kehilangan potensi revenue

    sekitar Rp 500 juta. Tolong segera diperbaiki!

    """

    classified = client.chat.completions.create(

    model="gpt-4o-mini",

    responsemodel=ClassifiedTicket,

    messages=[

    {

    "role": "system",

    "content": "Klasifikasikan support ticket berikut."

    },

    {"role": "user", "content": tickettext}

    ],

    )

    print(f"Priority: {classified.priority.value}")

    print(f"Category: {classified.category.value}")

    print(f"Department: {classified.department}")

    print(f"Escalation needed: {classified.requiresescalation}")

    print(f"Summary: {classified.summary}")

    Menggunakan dengan Provider Lain

    Instructor tidak terbatas pada OpenAI. Berikut cara menggunakannya dengan Anthropic.

    import instructor
    

    from anthropic import Anthropic

    Dengan Anthropic Claude

    client = instructor.fromanthropic(Anthropic())

    class Analysis(BaseModel):

    topic: str

    mainarguments: List[str]

    conclusion: str

    result = client.messages.create(

    model="claude-sonnet-4-20250514",

    maxtokens=1024,

    responsemodel=Analysis,

    messages=[

    {

    "role": "user",

    "content": "Analisis argumen utama tentang pentingnya "

    "regulasi AI di Indonesia"

    }

    ],

    )

    Tips dan Best Practices

    Berikut beberapa tips untuk memaksimalkan penggunaan Instructor:

    1. Gunakan Field descriptions yang jelas
    class GoodModel(BaseModel):
    

    # Baik - deskripsi jelas

    revenue: float = Field(

    description="Total revenue dalam juta Rupiah (IDR)"

    )

    # Kurang baik - tanpa deskripsi

    revenue: float

    2. Pilih model yang tepat untuk task yang tepat
    • Gunakan gpt-4o-mini untuk task sederhana (klasifikasi, ekstraksi basic)
    • Gunakan gpt-4o untuk task kompleks (nested objects, reasoning)

    3. Manfaatkan system prompt
    messages=[
    

    {

    "role": "system",

    "content": "Kamu adalah asisten yang mengekstrak data. "

    "Selalu berikan output dalam bahasa Indonesia. "

    "Jika informasi tidak tersedia, gunakan null."

    },

    {"role": "user", "content": text}

    ]

    4. Handle error dengan graceful
    from instructor.exceptions import InstructorRetryException
    
    

    try:

    result = client.chat.completions.create(

    model="gpt-4o-mini",

    responsemodel=MyModel,

    max_retries=3,

    messages=[{"role": "user", "content": text}],

    )

    except InstructorRetryException as e:

    print(f"Gagal setelah retry: {e}")

    except Exception as e:

    print(f"Error: {e}")

    Kesimpulan

    Instructor adalah library yang sangat berguna untuk siapa saja yang bekerja dengan LLM dan membutuhkan output terstruktur. Dengan memanfaatkan Pydantic, Instructor memberikan type safety, validasi otomatis, dan retry logic yang membuat integrasi LLM ke dalam aplikasi produksi menjadi jauh lebih reliable.

    Fitur-fitur utama yang telah kita pelajari:

    • Penggunaan dasar dengan OpenAI dan provider lain
    • Nested Pydantic models untuk data kompleks
    • Validasi kustom dengan field dan model validators
    • Retry logic otomatis saat validasi gagal
    • Streaming dengan Partial objects
    • Contoh praktis untuk ekstraksi data dan klasifikasi

    Mulailah dengan use case sederhana seperti ekstraksi data atau klasifikasi, lalu gradually tingkatkan kompleksitas sesuai kebutuhan proyek Anda. Instructor akan menjadi tool yang sangat berharga dalam toolkit AI engineering Anda.

    Artikel Terkait

    Tutorial Lengkap Instructor: Output Terstruktur dari LLM dengan Pydantic

    Tutorial Lengkap Instructor: Output Terstruktur dari LLM dengan Pydantic Halo temen-temen, di tutorial ini aku mau ngaja...

    Tutorial PydanticAI: Framework Agent LLM yang Type-Safe

    Membangun Agen LLM yang Type-Safe dengan PydanticAI PydanticAI adalah framework agen dari tim di balik Pydantic, diranca...

    Tutorial Lengkap Guidance: Constrained Generation dan Structured Output dari LLM

    Tutorial Lengkap Guidance: Constrained Generation dan Structured Output dari LLM Halo temen-temen, di tutorial kali ini ...

    Tutorial Mirascope: Toolkit Pythonic untuk Membangun Aplikasi LLM

    Tutorial Mirascope: Toolkit Pythonic untuk Membangun Aplikasi LLM Pendahuluan Mirascope adalah library Python yang menye...