Mekanisme Tool Calling & Integrasi Fungsi Eksternal
Membongkar Cara LLM Menerjemahkan Bahasa Alami Menjadi Pemanggilan Fungsi JSON Deterministik
01. Target Pembelajar & Prasyarat
Developer pemula-menengah, praktisi otomasi, dan pembangun produk yang ingin menghubungkan LLM dengan database atau API dunia nyata.
02. Kompetensi Konseptual (Level 1ā2)
- āMenjelaskan mengapa LLM murni tidak pernah mengeksekusi fungsi atau memanggil API secara langsung di server model.
- āMemetakan siklus pertukaran 4 pesan (user -> assistant [tool_calls] -> tool [observation] -> assistant [final]) beserta peran pengikat tool_call_id.
03. Kompetensi Praktis & Artefak Nyata (Level 3ā4)
- āMengonfigurasi definisi skema fungsi menggunakan JSON Schema yang ketat (required, enum, additionalProperties: false).
- āMengimplementasikan Agent Execution Loop di Node.js yang menangkap sinyal tool_calls, memvalidasi argumen, menjalankan fungsi lokal/API, dan mengembalikan hasil observasi ke model.
04. Aplikasi Nyata & Verifikasi
Penerapan Industri: Arsitektur inti di balik asisten triase prospek WhatsApp, agen pengecek status pesanan e-commerce, dan bot operasional internal.
Metode Verifikasi: Menjalankan skrip melalui terminal untuk 3 skenario uji (pertanyaan harga tunggal, perbandingan multi-paket, dan penolakan parameter tidak valid) serta memeriksa jejak JSON pada setiap putaran loop.
Mengapa Subjek Ini Krusial dalam Rekayasa Nyata?
Pelajari protokol Tool Calling dua arah: dari deklarasi JSON Schema fungsi, sinyal tool_calls, eksekusi di sisi host, hingga observasi balik ke model.
Dalam lingkungan produksi, kegagalan memahami batas-batas arsitektur sistem sering kali menyebabkan bug yang sulit dilacak, pemborosan biaya komputasi, atau antarmuka yang lambat bagi pengguna.
Bagaimana Sistem Sebenarnya Bekerja di Tingkat Protokol
Ilusi Eksekusi: Mengapa LLM Membutuhkan Jembatan Runtime
Banyak pemula mengira bahwa ketika sebuah agen AI memeriksa stok barang di database atau mengirim email, model di cloud itulah yang membuka koneksi jaringan. Ini adalah ilusi visual. Model bahasa pada hakikatnya tidak memiliki akses soket jaringan keluar ke infrastruktur internal Anda.
Yang terjadi sebenarnya adalah sebuah protokol delegasi: model mendeteksi kebutuhan data, menghentikan penulisan kalimat biasa, dan mengeluarkan perintah pemanggilan alat (tool call) dalam format JSON. Aplikasi runtime Anda bertindak sebagai 'tangan dan kaki' yang mengeksekusi perintah tersebut dan menyuapkan fakta hasilnya kembali ke konteks model.
Jangan pernah menggunakan eval() untuk menjalankan fungsi berdasarkan teks nama alat dari model. Gunakan pemetaan eksplisit (dictionary lookup) terhadap fungsi yang terdaftar.
Siklus 5 Tahap Protokol Tool Calling
1. Deklarasi Skema: Aplikasi mengirimkan deskripsi alat melalui parameter `tools`.
2. Keputusan Model: Model memutuskan memanggil alat dan mengeluarkan `tool_calls` dengan `id` unik.
3. Eksekusi Runtime: Aplikasi mem-parsing argumen, memvalidasi keamanan, dan menjalankan fungsi lokal.
4. Observasi: Hasil dikemas ke dalam pesan ber-role `tool` dengan `tool_call_id` yang cocok.
5. Sintesis Akhir: Model membaca hasil observasi dan menyusun jawaban akurat bagi pengguna.
Diagram Sekuens Protokol Tool Calling 5 Tahap
Aplikasi mengirim pesan pengguna bersama array spesifikasi fungsi (JSON Schema).
{
"tools": [
{
"type": "function",
"function": {
"name": "cek_detail_layanan",
"parameters": {
"type": "object",
"properties": {
"kode_paket": {
"type": "string"
}
}
}
}
}
]
}Koreksi Asumsi Umum vs Realitas Rekayasa
āModel AI mengeksekusi kode database secara langsung di dalam server model.ā
Model hanya mengeluarkan teks JSON terstruktur yang meminta fungsi dipanggil. Runtime kitalah yang mengeksekusi fungsi di server lokal.
āArgumen JSON yang dikeluarkan model pasti selalu benar sesuai skema.ā
Model tetap bersifat probabilistik; runtime wajib memvalidasi tipe data argumen sebelum menjalankan aksi kritis.
Implementasi Langkah-demi-Langkah
Hasil akhir yang diharapkan: Skrip Node.js mandiri yang mampu melayani kueri harga dan perhitungan bundel layanan Z-Automation secara otonom.
lab-a1-m3/ āāā agent-tool-runner.mjs
1Membuat Skrip Agent Tool Runner
Kode lengkap dengan registri fungsi lokal dan emulator penalaran terstruktur.
| 1 | // agent-tool-runner.mjs |
| 2 | // Status: ā Tested Successfully (Node.js v20+) |
| 3 | |
| 4 | const KATALOG_Z_AUTOMATION = { |
| 5 | "landing-roas": { nama: "Landing Page High-ROAS", hargaRp: 1500000, estimasiHari: 5, fitur: ["Copywriting Konversi", "Integrasi Pixel", "Kecepatan < 1.5s"] }, |
| 6 | "corporate-3d": { nama: "Corporate WebGL & 3D Experience", hargaRp: 5500000, estimasiHari: 14, fitur: ["Three.js Custom Scene", "CMS Katalog", "Optimasi SEO SSR"] }, |
| 7 | "ai-agent-crm": { nama: "Otomasi AI Agent WhatsApp & CRM", hargaRp: 3500000, estimasiHari: 7, fitur: ["Hermes Reasoning", "OpenClaw Webhook", "Human-in-the-Loop"] } |
| 8 | }; |
| 9 | |
| 10 | const EXECUTABLE_TOOLS = { |
| 11 | cek_detail_layanan({ kode_paket }) { |
| 12 | const paket = KATALOG_Z_AUTOMATION[kode_paket]; |
| 13 | if (!paket) { |
| 14 | return { sukses: false, error: `Kode paket '${kode_paket}' tidak ditemukan. Pilihan valid: ${Object.keys(KATALOG_Z_AUTOMATION).join(", ")}` }; |
| 15 | } |
| 16 | return { sukses: true, data: paket }; |
| 17 | }, |
| 18 | |
| 19 | hitung_estimasi_bundel({ daftar_kode_paket, diskon_persen = 0 }) { |
| 20 | if (!Array.isArray(daftar_kode_paket) || daftar_kode_paket.length === 0) { |
| 21 | return { sukses: false, error: "Parameter 'daftar_kode_paket' harus berupa array yang tidak kosong." }; |
| 22 | } |
| 23 | if (typeof diskon_persen !== "number" || diskon_persen < 0 || diskon_persen > 30) { |
| 24 | return { sukses: false, error: "Diskon hanya diizinkan antara 0% hingga 30%." }; |
| 25 | } |
| 26 | let subtotal = 0; |
| 27 | const rincian = []; |
| 28 | for (const kode of daftar_kode_paket) { |
| 29 | const item = KATALOG_Z_AUTOMATION[kode]; |
| 30 | if (!item) return { sukses: false, error: `Paket '${kode}' tidak valid di dalam bundel.` }; |
| 31 | subtotal += item.hargaRp; |
| 32 | rincian.push({ kode, nama: item.nama, hargaRp: item.hargaRp }); |
| 33 | } |
| 34 | const potongan = Math.round(subtotal * (diskon_persen / 100)); |
| 35 | return { |
| 36 | sukses: true, |
| 37 | rincian, |
| 38 | subtotalRp: subtotal, |
| 39 | diskonPersen: diskon_persen, |
| 40 | totalAkhirRp: subtotal - potongan |
| 41 | }; |
| 42 | } |
| 43 | }; |
| 44 | |
| 45 | const TOOL_DEFINITIONS = [ |
| 46 | { |
| 47 | type: "function", |
| 48 | function: { |
| 49 | name: "cek_detail_layanan", |
| 50 | description: "Mengambil informasi harga resmi, estimasi waktu pengerjaan, dan fitur dari paket layanan Z-Automation.", |
| 51 | parameters: { |
| 52 | type: "object", |
| 53 | properties: { |
| 54 | kode_paket: { |
| 55 | type: "string", |
| 56 | enum: ["landing-roas", "corporate-3d", "ai-agent-crm"], |
| 57 | description: "Kode unik paket layanan yang ditanyakan pengguna." |
| 58 | } |
| 59 | }, |
| 60 | required: ["kode_paket"], |
| 61 | additionalProperties: false |
| 62 | } |
| 63 | } |
| 64 | }, |
| 65 | { |
| 66 | type: "function", |
| 67 | function: { |
| 68 | name: "hitung_estimasi_bundel", |
| 69 | description: "Menghitung total biaya gabungan dari beberapa paket layanan beserta potongan diskon resmi.", |
| 70 | parameters: { |
| 71 | type: "object", |
| 72 | properties: { |
| 73 | daftar_kode_paket: { |
| 74 | type: "array", |
| 75 | items: { type: "string", enum: ["landing-roas", "corporate-3d", "ai-agent-crm"] }, |
| 76 | description: "Daftar kode paket yang ingin digabungkan." |
| 77 | }, |
| 78 | diskon_persen: { |
| 79 | type: "number", |
| 80 | description: "Persentase diskon (0 hingga 30). Gunakan 10 untuk bundel 2 paket atau lebih." |
| 81 | } |
| 82 | }, |
| 83 | required: ["daftar_kode_paket"], |
| 84 | additionalProperties: false |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | ]; |
| 89 | |
| 90 | async function panggilModelPenalaran(messages, tools) { |
| 91 | const pesanTerakhir = messages[messages.length - 1]; |
| 92 | |
| 93 | if (pesanTerakhir.role === "user") { |
| 94 | const teks = pesanTerakhir.content.toLowerCase(); |
| 95 | if (teks.includes("gabung") || (teks.includes("corporate") && teks.includes("agent"))) { |
| 96 | return { |
| 97 | role: "assistant", |
| 98 | content: null, |
| 99 | tool_calls: [ |
| 100 | { |
| 101 | id: "call_hermes_901", |
| 102 | type: "function", |
| 103 | function: { |
| 104 | name: "hitung_estimasi_bundel", |
| 105 | arguments: JSON.stringify({ |
| 106 | daftar_kode_paket: ["corporate-3d", "ai-agent-crm"], |
| 107 | diskon_persen: 10 |
| 108 | }) |
| 109 | } |
| 110 | } |
| 111 | ] |
| 112 | }; |
| 113 | } |
| 114 | return { |
| 115 | role: "assistant", |
| 116 | content: null, |
| 117 | tool_calls: [ |
| 118 | { |
| 119 | id: "call_hermes_101", |
| 120 | type: "function", |
| 121 | function: { |
| 122 | name: "cek_detail_layanan", |
| 123 | arguments: JSON.stringify({ kode_paket: "ai-agent-crm" }) |
| 124 | } |
| 125 | } |
| 126 | ] |
| 127 | }; |
| 128 | } |
| 129 | |
| 130 | if (pesanTerakhir.role === "tool") { |
| 131 | const hasilObservasi = JSON.parse(pesanTerakhir.content); |
| 132 | if (!hasilObservasi.sukses) { |
| 133 | return { |
| 134 | role: "assistant", |
| 135 | content: `Maaf, terjadi kendala saat memeriksa data: ${hasilObservasi.error}` |
| 136 | }; |
| 137 | } |
| 138 | if (hasilObservasi.totalAkhirRp) { |
| 139 | return { |
| 140 | role: "assistant", |
| 141 | content: `Berdasarkan perhitungan sistem katalog kami, bundel **${hasilObservasi.rincian.map(r => r.nama).join(" + ")}** memiliki subtotal Rp${hasilObservasi.subtotalRp.toLocaleString("id-ID")}. Dengan diskon bundel ${hasilObservasi.diskonPersen}%, total investasi akhir Anda adalah **Rp${hasilObservasi.totalAkhirRp.toLocaleString("id-ID")}**.` |
| 142 | }; |
| 143 | } |
| 144 | const d = hasilObservasi.data; |
| 145 | return { |
| 146 | role: "assistant", |
| 147 | content: `Paket **${d.nama}** tersedia dengan biaya **Rp${d.hargaRp.toLocaleString("id-ID")}** (estimasi pengerjaan ${d.estimasiHari} hari kerja), mencakup fitur: ${d.fitur.join(", ")}.` |
| 148 | }; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | async function jalankanAgen(pertanyaanPengguna) { |
| 153 | const MAX_TURNS = 5; |
| 154 | const messages = [ |
| 155 | { |
| 156 | role: "system", |
| 157 | content: "Anda adalah Agen Konsultan Teknis Z-Automation. Jangan pernah menebak harga layanan. Selalu gunakan fungsi alat yang tersedia untuk mengambil data akurat." |
| 158 | }, |
| 159 | { role: "user", content: pertanyaanPengguna } |
| 160 | ]; |
| 161 | |
| 162 | console.log(`\n=============================================================`); |
| 163 | console.log(`[USER INPUT]: "${pertanyaanPengguna}"`); |
| 164 | console.log(`=============================================================`); |
| 165 | |
| 166 | for (let turn = 1; turn <= MAX_TURNS; turn++) { |
| 167 | console.log(`\n--- [PUTARAN LOOP #${turn}] Mengirim ${messages.length} pesan ke Model ---`); |
| 168 | const responsAsisten = await panggilModelPenalaran(messages, TOOL_DEFINITIONS); |
| 169 | messages.push(responsAsisten); |
| 170 | |
| 171 | if (!responsAsisten.tool_calls || responsAsisten.tool_calls.length === 0) { |
| 172 | console.log(`\n[FINAL AGENT RESPONSE]:\n${responsAsisten.content}`); |
| 173 | return responsAsisten.content; |
| 174 | } |
| 175 | |
| 176 | for (const toolCall of responsAsisten.tool_calls) { |
| 177 | const namaFungsi = toolCall.function.name; |
| 178 | const raWArgs = toolCall.function.arguments; |
| 179 | console.log(`[SIGNAL DARI MODEL] Meminta eksekusi fungsi: ${namaFungsi}`); |
| 180 | console.log(`[PAYLOAD ARGUMEN JSON]: ${raWArgs}`); |
| 181 | |
| 182 | let hasilEksekusi; |
| 183 | try { |
| 184 | const argumenTerurai = JSON.parse(raWArgs); |
| 185 | const fn = EXECUTABLE_TOOLS[namaFungsi]; |
| 186 | if (!fn) throw new Error(`Fungsi '${namaFungsi}' tidak terdaftar.`); |
| 187 | hasilEksekusi = fn(argumenTerurai); |
| 188 | } catch (err) { |
| 189 | hasilEksekusi = { sukses: false, error: err.message }; |
| 190 | } |
| 191 | |
| 192 | console.log(`[OBSERVASI WORKER]: ${JSON.stringify(hasilEksekusi)}`); |
| 193 | |
| 194 | messages.push({ |
| 195 | role: "tool", |
| 196 | tool_call_id: toolCall.id, |
| 197 | name: namaFungsi, |
| 198 | content: JSON.stringify(hasilEksekusi) |
| 199 | }); |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | await jalankanAgen("Berapa biaya dan fitur paket otomasi AI Agent WhatsApp?"); |
| 205 | await jalankanAgen("Jika saya gabung pembuatan website Corporate 3D dengan AI Agent, berapa total biayanya?"); |
Paket Otomasi AI Agent WhatsApp & CRM tersedia dengan biaya Rp3.500.000... Total investasi akhir bundel Corporate 3D + AI Agent: Rp8.100.000 (setelah diskon 10%).
Mengantisipasi Masalah Umum & Tantangan Ekstensi
Tambahkan paket baru 'undangan-vip' ke dalam katalog dan verifikasi respons kuerinya.
Uji Kompetensi Mandiri & Verifikasi Hasil
Standar kelulusan Z-Automation mewajibkan skor minimum 80 / 100 Poin dengan komponen Implementasi Praktis minimal 21 / 35 Poin (60%). Jawab soal diagnostik dan centang hasil pengujian praktis Anda.
BAGIAN A // PERTANYAAN DIAGNOSTIK KONSEPTUAL & TEKNIS (40 POIN)
01.Mengapa pesan balasan dari worker wajib menyertakan properti tool_call_id?
02.Di mana letak bahaya jika fungsi alat dijalankan menggunakan eval(toolCall.function.name + '(' + args + ')')?
BAGIAN B // DAFTAR PERIKSA HASIL IMPLEMENTASI PRAKTIS (35 POIN ā MIN. 21 POIN)
Centang item di bawah ini hanya jika Anda telah menjalankan kode di komputer Anda dan mengamati output yang benar: