Lewati ke konten
18/30Bab 18 dari 30

Tool Calling dan Structured Outputs: Kontrak yang Tetap Kokoh

24 panggilan, nol JSON rusak, dan dua tanggal bisa dipakai. Lalu endpoint yang sama dengan deskripsi lebih baik—dan batas skema.

Di halaman ini

Beri sebuah model tool pencarian penerbangan dan minta ia mencari penerbangan dari Madrid ke Berlin. Inilah yang kembali:

TEXT
<tool_call>
{"name": "search_flights",
 "arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>

JSON-nya valid. Nama tool-nya benar. Setiap field wajib ada. Dan panggilannya tidak berguna: tidak ada API penerbangan yang menerima "Madrid" ketika yang diminta adalah kode bandara, atau "3rd October 2026" ketika yang diminta adalah tanggal.

Celah itu — sempurna secara sintaksis, tidak bisa dipakai secara semantik — adalah inti bab ini, dan hal pertama yang perlu ditegaskan adalah bahwa ini bukan masalah JSON. Dalam dua puluh empat request dengan tool ini, model menghasilkan 24 tool call valid dan nol JSON rusak. Ia tidak pernah sekali pun gagal pada bagian yang biasanya semua orang debug.

Sebelum mekaniknya, kalimat yang mencegah kebingungan paling banyak: tool call adalah request, bukan aksi.

Model memancarkan pesan terstruktur yang berkata saya ingin search_flights dipanggil dengan argumen ini. Lalu ia berhenti. Kode kamu menerima pesan itu, memutuskan apakah akan mematuhinya, memanggil apa pun yang perlu dipanggil, dan mengirim hasilnya kembali sebagai pesan lain. Model tidak pernah menyentuh database kamu, tidak pernah membuat request HTTP, tidak pernah memegang kredensial.

Semua tentang keamanan agent di Bab 30 mengikuti pembagian itu, begitu juga semua tentang desain agent di Bab 23: model mengusulkan dan kode kamu yang menentukan, dan di kodelah setiap jaminan berada.

Jadi sebuah tool, jika dibuang kosakatanya, terdiri dari dua hal:

Sebuah skema. JSON Schema yang mendeskripsikan sebuah fungsi: namanya, apa yang dilakukan, dan argumen apa yang diterima beserta tipe dan batasannya. Inilah yang masuk ke prompt, dan inilah satu-satunya hal yang pernah dilihat model.

Sebuah endpoint. Fungsi di kode kamu yang menerima argumen tersebut dan mengembalikan sesuatu. Model tidak pernah melihatnya, tidak pernah tahu bahasa apa yang dipakai, dan tidak bisa membedakan query database dari string hardcoded.

Definisi tool masuk ke prompt, diserialisasi ke format apa pun yang dipakai untuk melatih model. Definisi itu memakan token di setiap panggilan — fakta yang nanti kembali dengan angka di bab ini.

Model menjawab dengan panggilan, bukan teks

Tautan ke bagian: Model menjawab dengan panggilan, bukan teks

Alih-alih prosa, respons berisi request terstruktur, dan API melaporkan alasan selesai yang menyatakannya. Alasan itu penting: dari situlah kode kamu tahu harus menjalankan tool, bukan menampilkan jawaban ke pengguna.

Kode kamu menjalankannya — atau menolak

Tautan ke bagian: Kode kamu menjalankannya — atau menolak

Ini adalah langkah yang tidak melibatkan model. Validasi argumen terhadap skema, putuskan apakah pemanggil ini diizinkan melakukan ini, lalu eksekusi.

Kamu mengirim hasilnya kembali sebagai pesan

Tautan ke bagian: Kamu mengirim hasilnya kembali sebagai pesan

Hasil menjadi giliran lain dalam percakapan, dalam role yang disediakan untuknya. Model membacanya seperti konteks lain.

Model menjawab, atau meminta tool lain

Tautan ke bagian: Model menjawab, atau meminta tool lain

Itulah loop Bab 23, dan alasan satu request bisa berubah menjadi selusin round trip.

Tidak ada yang emergent di sini. Seperti yang ditetapkan Bab 11, tool calling adalah perilaku yang dilatih:1 selama post-training, model melihat ribuan percakapan yang bentuknya persis seperti ini. Itulah sebabnya formatnya spesifik per model, mengapa reliabilitas sangat berbeda antara model berukuran mirip, dan mengapa sebuah model bisa memanggil tool yang belum pernah dilihatnya — bentuknya dilatih, tool spesifiknya berasal dari prompt kamu.

Inilah tool seperti yang biasanya pertama kali ditulis orang. Perhatikan bahwa tidak ada yang salah dengannya; ia hanya tipis:

tools/badFlights.tsTS
{
  name: "search_flights",
  description: "Search for flights.",
  parameters: {
    type: "object",
    properties: {
      from: { type: "string", description: "Airport." },   
      to:   { type: "string", description: "Airport." },   
      date: { type: "string", description: "The date." },  
    },
    required: ["from", "to", "date"],
  },
}

Dua puluh empat request, enam pasangan kota disilangkan dengan empat cara menyatakan tanggal ("tanggal 3 bulan depan", "Jumat depan", "15 Desember", "besok"), greedy decoding agar hasilnya dapat direproduksi:

tool dipanggilJSON rusaktanggal dalam ISObandara sebagai IATAsemuanya benar
skema di atas24/2402/244/241/24

Baca dua kolom pertama sebelum tiga kolom terakhir. Model memanggil tool yang benar setiap kali dan menghasilkan JSON yang tersusun baik setiap kali. Kegagalannya sepenuhnya ada pada nilai, dan nilai itu tidak bisa dipakai: "Madrid" alih-alih MAD, "3rd October 2026" alih-alih 2026-10-03.

Ini perlu ditekankan karena menentukan ke mana kamu melihat ketika sesuatu rusak. Instingnya adalah menambahkan parser JSON dengan retry, atau meminta model lebih tegas untuk menghasilkan JSON valid. Keduanya tidak menangani apa pun yang terjadi di sini.

Endpoint yang sama. Kode di belakangnya sama. Model yang sama, prompts yang sama, decoding yang sama. Satu-satunya yang berubah adalah teks di skema:

tools/goodFlights.tsTS
{
  name: "search_flights",
  description: "Search scheduled flights between two airports on a given day.",
  parameters: {
    type: "object",
    properties: {
      from: {
        type: "string",
        description: "Departure airport as a three-letter IATA code, e.g. MAD for Madrid. Never a city name.",   
        pattern: "^[A-Z]{3}$",
      },
      to: { /* same */ },
      date: {
        type: "string",
        description: "Departure date as an ISO 8601 calendar date, YYYY-MM-DD. Resolve relative dates against today before calling.",   
        format: "date",
        pattern: "^\\d{4}-\\d{2}-\\d{2}$",
      },
    },
    required: ["from", "to", "date"],
  },
}
FORMAT tanggalNILAI tanggalFORMAT bandaraNILAI bandara
skema tipis2/241/244/244/24
skema berdeskripsi24/2412/2416/248/24

Format tanggal naik dari 2 dari 24 menjadi 24 dari 24. Sempurna, hanya dari perubahan teks, tanpa menyentuh kode dan tanpa logika retry. Jika ada satu kebiasaan operasional yang kamu ambil dari bab ini, inilah dia: ketika tool dipanggil dengan salah, perbaikannya hampir selalu ada di deskripsi, dan itu perbaikan termurah dalam sistem.

Sekarang baca kolom kedua, yang merupakan separuh yang lebih penting.

Skema membatasi bentuk. Ia tidak bisa memasok pengetahuan.

Tautan ke bagian: Skema membatasi bentuk. Ia tidak bisa memasok pengetahuan.

Tanggal berada dalam format ISO 24 dari 24 kali. Ia adalah hari yang benar 12 dari 24 kali.

Jadi setengah panggilan kini membawa tanggal berformat sempurna yang merupakan tanggal yang salah. Deskripsi memberi tahu model bentuk apa yang harus dihasilkan, dan model menghasilkannya tanpa cela — tetapi mengubah "Jumat depan" menjadi 2026-09-11 membutuhkan pengetahuan tentang tanggal hari ini dan aritmetika kalender, dan tidak ada deskripsi sebanyak apa pun yang memasok itu. Cerita yang sama berlaku untuk bandara: format naik dari 4 ke 16, tetapi nilai hanya dari 4 ke 8, karena menulis MAD membutuhkan pengetahuan bahwa bandara Madrid adalah MAD.

Pembedaan itu adalah gagasan penopang bab ini:

Skema adalah kontrak tentang bentuk. Ia bisa membuat output model dapat di-parse, bertipe, dan konsisten. Ia tidak bisa membuatnya benar, dan setiap mode kegagalan yang bertahan melewati skema yang baik adalah kegagalan pengetahuan, bukan kegagalan format.

Keduanya membutuhkan perbaikan yang berbeda, dan mencampuradukkannya membuang minggu-minggu kerja. Kegagalan format diperbaiki di deskripsi atau dengan constrained decoding, di bawah. Kegagalan pengetahuan diperbaiki dengan memasukkan pengetahuan ke prompt — tanggal saat ini di system message, lookup bandara sebagai tool kedua yang dipanggil model lebih dulu, enum dalam skema ketika set-nya cukup kecil untuk dienumerasi. Perhatikan kesamaan ketiganya: semuanya memindahkan masalah keluar dari memori model dan masuk ke input-nya, yang merupakan keseluruhan Bab 24.

Structured outputs, dan apa sebenarnya "constrained decoding"

Tautan ke bagian: Structured outputs, dan apa sebenarnya "constrained decoding"

Semua di atas masih bergantung pada model yang memilih untuk menghasilkan bentuk yang benar. Ada jaminan yang lebih kuat, dan itulah payoff terbaik dari Bab 17.

Ingat kembali cara generasi bekerja: di setiap langkah, model menghasilkan logit untuk setiap token dalam kosakata, dan sampler memilih salah satunya. Constrained decoding menyisipkan satu langkah di antaranya. Dengan grammar — yang diturunkan dari JSON Schema kamu — ia menghitung token mana yang secara legal bisa muncul berikutnya, mengatur logit semua yang lain menjadi negatif tak hingga, lalu membiarkan sampler memilih dari yang tersisa.

Jika skema berkata hal berikutnya harus berupa {, maka setiap token yang bukan { memiliki probabilitas nol. Bukan "kecil kemungkinannya": nol. Model tidak bisa memancarkan JSON tidak valid karena token yang tidak valid telah dihapus dari distribusi sebelum sampling.

Itulah yang ada di balik "structured outputs", "JSON mode", dan "guided generation", dan itu menjelaskan dua propertinya. Jaminannya total untuk apa pun yang bisa diekspresikan grammar — tipe, field wajib, enum, nesting — karena ditegakkan secara mekanis, bukan diminta dengan sopan. Dan ia tidak mengatakan apa pun tentang konten: grammar bisa memaksa "date" menjadi string yang cocok dengan pola tanggal, dan tidak bisa memaksanya menjadi hari yang benar. Itu tembok yang sama seperti bagian sebelumnya, dicapai dari sisi lain.

Dua catatan praktis. Ini tidak gratis: mask harus dihitung di setiap langkah, dan grammar kompleks memakan latensi yang terukur. Dan ini mengubah apa yang dilakukan model — model yang diarahkan menjauh dari token pilihannya bisa menghasilkan konten yang lebih buruk sambil menghasilkan struktur sempurna, itulah sebabnya "minta baik-baik dan validasi" masih menjadi default yang masuk akal untuk bentuk sederhana, dan constrained decoding layak dibayar ketika bentuknya kompleks atau konsumennya ketat.

Side effect, dan satu properti yang penting

Tautan ke bagian: Side effect, dan satu properti yang penting

Bab 14 mengukur timeout yang diikuti retry dan menagih dua generasi untuk satu jawaban. Dengan tools, kegagalan yang sama menjadi lebih buruk, karena sebuah tool bisa melakukan sesuatu.

Jika kode kamu memanggil charge_card, timeout, lalu retry, kamu punya dua tagihan. Model tidak tahu semua ini terjadi; ia melihat satu hasil tool. Perbaikannya sama seperti di sistem terdistribusi mana pun dan bukan masalah model: buat operasi itu idempotent dengan memberi panggilan sebuah key, sehingga eksekusi kedua mengenali yang pertama dan mengembalikan hasilnya alih-alih melakukan pekerjaan lagi.

Aturan desain yang mengikuti layak dinyatakan dengan jelas. Pisahkan read dari write di katalog tool kamu. Read bisa di-retry bebas, dijalankan paralel, dan di-cache. Write tidak bisa, dan harus membawa key, pemeriksaan izin, serta — untuk apa pun yang ingin diketahui pengguna sebelum terjadi — langkah approval yang menempatkan manusia di antara request dan aksi. Langkah approval itu bukan basa-basi: ia adalah salah satu dari sedikit hal yang berdiri di antara prompt injection dan konsekuensi nyata — dan, seperti diukur Bab 30, yang paling lemah di antaranya.

Berapa banyak tool sebelum kualitas menurun?

Tautan ke bagian: Berapa banyak tool sebelum kualitas menurun?

Folklore mengatakan bahwa memuat banyak tool membuat model memilih dengan buruk. Ini layak diukur, bukan diulang begitu saja, jadi: dua puluh empat request yang sama, dengan tool penerbangan plus set tool lain yang terus bertambah — termasuk tiga yang sengaja dibuat mudah tertukar (jadwal kereta, penyeberangan feri, rute bus).

tools dimuattoken promptmemilih search_flightstanggal dalam ISO
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/24

Seleksi tidak menurun. Dengan dua puluh tool, tiga di antaranya masuk akal untuk tertukar, model setengah miliar parameter memilih yang benar dua puluh empat dari dua puluh empat kali. Penurunan di sepuluh adalah tiga panggilan yang menamai tool berbeda, dan itu tidak bertahan ketika naik ke dua puluh.

Itu hasil negatif dan harus dilaporkan sebagai hasil negatif: pada tugas ini, dengan tools ini, "terlalu banyak tools" bukan masalahnya. Yang memang tumbuh, secara monoton dan dengan faktor enam, adalah prompt: 353 token menjadi 2,119, dibayar di setiap request dalam percakapan, selamanya, baik tool digunakan maupun tidak.

Jadi versi jujur dari folklore itu adalah tentang biaya dan konteks, bukan akurasi. Dua puluh tool adalah pajak permanen pada setiap pesan, dan Bab 16 sudah menunjukkan apa yang dilakukan prefix permanen terhadap tagihan selama empat puluh giliran. Ketika orang melaporkan bahwa banyak tool merusak kualitas, mekanismenya biasanya karena definisi-definisi itu menyingkirkan konteks yang penting — masalah Bab 24 yang memakai kostum Bab 18. Tools yang benar-benar hampir duplikat satu sama lain juga masalah nyata, dan perbaikannya bukan mengurangi jumlah tool, melainkan deskripsi dan namespace yang lebih baik: beri prefix berdasarkan sistem (crm.search_customer, billing.search_customer) agar dua katalog yang digabung dari dua tim tidak bertabrakan, dan agar model punya sesuatu untuk dibedakan.

Tiga jenis tool, dan satu yang membuka bagian berikutnya

Tautan ke bagian: Tiga jenis tool, dan satu yang membuka bagian berikutnya

Akan membantu jika tools disortir berdasarkan apa yang mereka lakukan terhadap dunia, karena rekayasanya berbeda untuk masing-masing.

Data tools membaca: search, fetch, query. Bisa di-retry, bisa diparalelkan, bisa di-cache. Mereka gagal dengan mengembalikan sesuatu yang tidak berguna, dan risiko utamanya adalah membawa teks yang tidak tepercaya ke dalam konteks — yang merupakan seluruh attack surface Bab 30.

Action tools menulis: mengirim, membuat, menagih, menghapus. Tidak bisa di-retry tanpa key, tidak aman diparalelkan, dan menjadi alasan approval flow ada.

Orchestration tools memanggil model lain. Tool yang implementasinya adalah agent lain, dengan prompt sendiri, tools sendiri, dan loop sendiri — dan bagi model pemanggil, ia terlihat persis seperti dua jenis lain, karena skema dan endpoint adalah semua yang pernah dilihatnya.

Jenis ketiga itu bukan keanehan. Itulah mekanisme di balik separuh agent-as-a-tool dari Bab 25 — topologi lainnya, handoff, menyerahkan percakapan dan tidak pernah mendapatkannya kembali — dan ia bekerja persis karena interface di bab ini cukup sempit sehingga satu agent utuh bisa muat di belakangnya.

Sekarang kamu punya model yang bisa meminta hal-hal, dan kontrak yang membuat permintaan itu dapat di-parse. Yang belum kamu punya adalah apa pun untuk ia tanyakan tentang, di luar yang muat dalam prompt-nya.

Tool paling umum di production, dengan selisih besar, adalah pencarian di atas kumpulan teks yang tidak pernah dilihat model selama training: dokumentasi kamu, tiket kamu, kontrak kamu. Kedengarannya seperti masalah yang sudah selesai — buat embedding, temukan tetangga terdekat, tempelkan — dan bagian yang belum selesai adalah bagian yang menentukan apakah jawaban bisa dipercaya: bagaimana teks dipotong sebelum di-embedding, ambang similarity seberapa rendah yang cukup untuk berarti saya tidak tahu, dan bagaimana sitasi ditempelkan ke sebuah klaim agar pembaca bisa memeriksanya.

Bab 19 adalah retrieval, dan itulah bab ketika jawaban yang salah berhenti menjadi keanehan dan mulai menjadi liabilitas.


Pengukuran dalam bab ini berasal dari Qwen/Qwen2.5-0.5B-Instruct dengan greedy decoding, atas 24 request yang dihasilkan dengan menyilangkan enam pasangan kota dan empat frasa tanggal, menggunakan chat template milik model sendiri untuk definisi tool. Hasilnya tereproduksi persis, dan modelnya kecil: baca pemisahan format/nilai sebagai demonstrasi mekanisme, bukan sebagai benchmark tentang apa yang dilakukan model saat ini. Frontier model menyelesaikan "Jumat depan" dengan benar jauh lebih sering — dan tetap tidak bisa dipaksa oleh skema, bagian itulah yang berlaku umum.

Kosakata JSON Schema yang digunakan di atas (type, properties, required, pattern, format, enum) dispesifikasikan dalam draft JSON Schema yang disebut dokumentasi provider kamu; subset yang berguna kecil dan sama di seluruh provider, dan perbedaan yang memang ada — keyword mana yang ditegakkan oleh constrained decoding alih-alih sekadar diteruskan ke model — layak dibaca di panduan structured-output provider, bukan diasumsikan.

Untuk constrained decoding sebagai teknik, library bergaya guidance dan proyek outlines mendokumentasikan konstruksi grammar-to-logit-mask dengan cara yang langsung memetakan ke sampler Bab 17. Dan untuk round trip itu sendiri, spesifikasi paling jelas bukan tutorial melainkan protokol: Bab 26 membacanya baris demi baris.

  1. Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). Paper yang membuat resep post-training menjadi standar; bentuk tool call dipelajari di sana, dari demonstrasi, persis seperti bentuk sebuah jawaban.


Dibuat oleh

David Vicente Campos

Pendiri NeuraLIA Labs & salah satu pendiri MyRealFood

Saya seorang insinyur komputer lulusan Universitas León. Saya ikut mendirikan MyRealFood, tempat saya sebagai CTO membangun aplikasi yang telah digunakan jutaan orang untuk makan lebih sehat, dan saya mendirikan NeuraLIA Labs, tempat saya membangun produk AI. Di sini saya menulis tentang hal-hal yang harus saya pahami sepanjang perjalanan, sebagaimana dulu saya berharap ada yang menjelaskannya kepada saya.

Selengkapnya tentang penulis

Diterbitkan oleh NeuraLIA Labs.

Dapatkan postingan baru di inbox kamu

Berita AI, panduan, dan update produk — email singkat saat kami menerbitkan sesuatu yang layak kamu baca.

Indeks kursus

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev11 menit baca

Model AI Jev dibuat untuk keputusan, bukan prosa

Jev dari TypeSafe AI menarik perhatian karena memperlakukan kecerdasan software sebagai persoalan probabilitas: pilih cabang yang tepat, sertakan keyakinan, dan hindari membayar LLM untuk menulis teks saat kode membutuhkan keputusan.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering11 menit baca

Rekayasa konteks untuk agen AI jangka panjang

Agen yang berjalan lama tidak gagal hanya karena window-nya kecil. Mereka gagal ketika file, output tool, dan riwayat lama menggeser tugas yang seharusnya diselesaikan agen.

Siap membiarkan LIA yang memilih?

Berkarya dengan semua model AI dalam satu tempat — mulai gratis hari ini.