Membangun function calling secara utuh: structured tool schema, loop eksekusi tool, serta tool retry & error handling. Episode ini berisi praktik membangun agen yang bisa query database, melakukan kalkulasi, dan memanggil API eksternal — fondasi dari agents di episode 10.

Setelah di episode 5 kalian melihat sekilas function calling, dan di episode 6-8 membangun RAG yang memberi model "pengetahuan", episode ini mengangkat kemampuan yang membuat model benar-benar bertindak: memanggil tool. Karena LLM tidak bisa menghitung secara andal (episode 2), tidak tahu data terbaru, dan tidak bisa mengakses database kalian — kecuali kalian memberinya alat.
Mengapa ini penting? Ini adalah jembatan antara LLM sebagai mesin menjawab dan LLM sebagai agen yang bekerja. Di produksi, aplikasi AI yang berguna hampir selalu memanggil sesuatu: API pembayaran, query database, kalkulator, search engine. Tanpa tool use, semua itu mustahil dilakukan model.
Ingat prinsip dari episode 5: model tidak mengeksekusi apa pun — ia mengembalikan keputusan berupa nama fungsi + argumen JSON. Aplikasi kalian yang mengeksekusi, lalu mengirim hasilnya kembali ke model. Inilah yang disebut tool loop:
Loop berhenti ketika model memutuskan tidak perlu tool lagi dan menghasilkan jawaban final.
import json
from openai import OpenAI
client = OpenAI()
tools = [
{"type": "function", "function": {
"name": "search_products",
"description": "Cari produk di database internal",
"parameters": {"type": "object",
"properties": {"keyword": {"type": "string"}},
"required": ["keyword"]}}},
{"type": "function", "function": {
"name": "calculate",
"description": "Kalkulasi aritmatika presisi",
"parameters": {"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"]}}},
]
def call_tool(name, args):
if name == "search_products":
return {"items": [{"id": 1, "name": "Keyboard MX", "price": 1250000},
{"id": 2, "name": "Mouse MX", "price": 850000}]}
if name == "calculate":
return {"result": eval(args["expression"])}
raise ValueError(f"tool tidak dikenal: {name}")
def agent(question):
messages = [{"role": "user", "content": question}]
for _ in range(5): # batasi iterasi (safety)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
)
msg = resp.choices[0].message
messages.append(msg) # beri tahu model keputusan tool-nya
if msg.tool_calls:
for tc in msg.tool_calls:
result = call_tool(tc.function.name,
json.loads(tc.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result),
})
continue # loop lagi dengan hasil tool
return msg.contentEmpat detail yang mudah salah:
messages.append(msg) — pesan yang memuat tool_calls harus dikembalikan apa adanya (dengan tool_call_id).role: "tool" dengan tool_call_id — hasil eksekusi diikat ke pemanggilan tertentu.Kualitas schema menentukan kualitas pemanggilan. Aturan praktis:
required selalu diisi: hindari argumen opsional yang ambigu.{
"name": "get_order_status",
"description": "Ambil status pesanan. Pakai jika user menanyakan keberadaan/nomor resi pesanan.",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "ID pesanan, contoh ORD-2026-0042"}
},
"required": ["order_id"]
}
}Tool akan gagal: database down, API eksternal timeout, input tidak valid. Pola yang benar: jangan pernah biarkan error menggagalkan seluruh percakapan — kembalikan error sebagai hasil tool agar model bisa menjelaskan/memperbaiki.
import traceback
def safe_call_tool(name, args):
try:
return {"ok": True, "data": call_tool(name, args)}
except Exception as e:
return {"ok": False, "error": str(e), "trace": traceback.format_exc()[-400:]}
# hasil error ini dikirim ke model dengan role: "tool"
# → model bisa menjawab "terjadi kesalahan sementara, coba lagi" atau
# memperbaiki argumen lalu memanggil ulangKelebihannya: model yang kompeten akan menyesuaikan diri — mengubah argumen, mencoba tool alternatif, atau meminta klarifikasi pengguna — alih-alih crash. Ini pola yang sama dipakai oleh semua framework agent (episode 10).
Satu agen yang menggabungkan ketiganya:
tools = [
{"type": "function", "function": {
"name": "query_db", "description": "Jalankan SQL SELECT pada database penjualan",
"parameters": {"type": "object", "properties": {"sql": {"type": "string"}},
"required": ["sql"]}}},
{"type": "function", "function": {
"name": "get_weather", "description": "Suhu & cuaca suatu kota",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}},
"required": ["city"]}}},
]
def call_tool(name, args):
if name == "query_db":
# dalam praktik: sqlite3 / psycopg, dengan allowlist read-only!
return run_sql(args["sql"])
if name == "get_weather":
return requests.get("https://api.weather.com/...").json()User request: "Berapa rata-rata penjualan 3 bulan terakhir? Lalu kalikan dengan 1.1." Model akan: query_db → calculate → jawaban final. Satu permintaan, tiga tool, satu alur otomatis.
Danger
Tool yang mengeksekusi SQL, shell, atau menulis file adalah serangan potensial — prompt injection (episode 16) bisa memanipulasi model memanggil tool berbahaya. Untuk query DB: gunakan koneksi read-only, batasi schema, dan log semua pemanggilan. Untuk shell: jangan pernah tanpa sandbox.
tool_call_id di pesan role: tool → API menolak.Inti yang harus dibawa pulang:
role: tool → ulang.required + enum + satu tanggung jawab.Di episode 10 selanjutnya kita akan membangun Agents: Agent Loop & LangGraph — ReAct loop (reason + act), state machine, multi-agent orchestration, dan perbandingan LangChain/LangGraph, LlamaIndex, serta MCP untuk tools dan data. Sampai jumpa di episode 10!