Membuat output model dapat dipakai mesin: JSON mode, structured output, dan function calling. Kalian membangun agent pertama yang bisa memanggil tools, memahami kapan output bebas vs terstruktur, serta pola loop tool-calling yang menjadi dasar agentic systemstype: text

Di episode 4 kalian mengandalkan prompt untuk mengarahkan format jawaban model — pendekatan yang rapuh di produksi. Episode ini mengganti fondasi itu dengan mekanisme bawaan API: structured output dan tool use (function calling). Dua fitur inilah yang mengubah LLM dari mesin tulis teks menjadi komponen yang bisa diintegrasikan ke sistem.
Mengapa episode ini penting? Karena produk AI nyata tidak menampilkan teks mentah ke pengguna — mereka memproses hasilnya: menyimpan ke database, memicu action, atau mengisi UI. Tanpa output terstruktur, kalian akan menghabiskan waktu men-parse teks bebas dengan regex yang rapuh. Tanpa function calling, kalian tidak akan bisa membangun agent (episode 11).
| Situasi | Pendekatan yang Tepat |
|---|---|
| Jawaban bebas untuk dibaca manusia | Teks biasa |
| Ekstraksi data, isi form, output ke DB | JSON mode / structured output |
| Menentukan satu tindakan dari daftar opsi | Tool calling |
| Flow multi-langkah dengan keputusan | Tool calling + loop |
Aturannya sederhana: jika hasilnya akan diproses kode, jadikan terstruktur. Teks bebas adalah perangkap — satu perubahan format model membuat parser kalian rusak.
Semua penyedia LLM besar punya mode JSON. Dengan pydantic, kalian mendefinisikan skema dan model mengikuti skema tersebut:
from typing import Literal
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class Transaksi(BaseModel):
jenis: Literal["belanja", "pemasukan", "transfer"]
nominal: int
keterangan: str
r = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Ekstrak transaksi dari teks pengguna."},
{"role": "user", "content": "Tadi siang transfer ke Rina 150 ribu buat utang."},
],
response_format=Transaksi,
)
tx = r.choices[0].message.parsed
print(tx.jenis, tx.nominal, tx.keterangan)Keuntungannya besar: skema divalidasi otomatis, tipe data aman (int, enum), dan tidak ada parsing manual. Ini adalah cara modern mengganti few-shot + regex dari episode 4.
Important
Structured output memastikan format valid, tetapi tidak menjamin isi benar. Model bisa menghasilkan JSON yang sesuai skema dengan nilai yang keliru secara faktual. Validasi konten tetap tanggung jawab kalian (episode 9 dan 19).
Tool calling adalah kemampuan model untuk memilih dan meminta pemanggilan fungsi yang kalian definisikan. Model tidak mengeksekusi fungsi — ia hanya mengembalikan argumen; kalian yang mengeksekusinya lalu mengirim balik hasilnya.
import json
from openai import OpenAI
client = OpenAI()
TOOLS = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Cek cuaca sebuah kota.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Nama kota"}
},
"required": ["city"],
},
},
}]
def get_weather(city: str) -> str:
return json.dumps({"city": city, "suhu": "32C", "kondisi": "cerah"})Sekarang jalankan loop tool-calling — pola yang menjadi jantung semua agent:
messages = [{"role": "user", "content": "Cuaca hari ini di Bandung gimana?"}]
for _ in range(5):
r = client.chat.completions.create(
model="gpt-4o-mini", messages=messages, tools=TOOLS
)
msg = r.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
if call.function.name == "get_weather":
result = get_weather(json.loads(call.function.arguments)["city"])
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
else:
print(msg.content)
breakAlurnya: model meminta get_weather dengan argumen {"city": "Bandung"}, kalian mengeksekusi, lalu mengembalikan hasil sebagai pesan ber-role tool. Loop berhenti saat model memberi jawaban final. Ini adalah ReAct tanpa label — dan akan kita perluas menjadi agent sungguhan di episode 11.
Tool call bisa gagal di lapisan nyata: API eksternal down, argumen aneh, atau timeout. Pola yang benar: kembalikan pesan error ke model sebagai hasil tool, bukan langsung melempar exception:
def get_weather(city: str) -> str:
try:
result = cuaca_api(city) # placeholder
return json.dumps(result)
except Exception as e:
return json.dumps({"error": str(e), "city": city})Dengan pola ini, model bisa memutuskan sendiri: mencoba kota lain, meminta klarifikasi, atau memberi jawaban parsial. Inilah sumber robustness agent — topik kita dalami di episode 25.
Inti yang harus dibawa pulang:
tool.Di episode 6 selanjutnya kita membangun kemampuan knowledge: embeddings & vector database — mengubah teks menjadi vektor, mencari dengan kesamaan semantik, dan menyiapkan pipeline embeddings untuk RAG. Sampai jumpa di episode 6!