Model chat cuma bisa memprediksi token berikutnya. Dia nggak bisa query database kamu, manggil API, atau ngecek cuaca. Itulah jarak antara chatbot dan agent. Tool calling (atau function calling) yang nutup jarak itu. Modelnya nggak ngejalanin apa-apa sendiri; dia cuma balikin nama fungsi dan argumen yang dia mau. Kode kamu yang ngejalanin fungsi itu dan ngasih hasilnya balik. Ulang terus sampai model berhenti minta tool.
Bagian yang paling saya suka: loop ini bisa jalan sepenuhnya lokal pake Ollama dan model open kecil kayak qwen3. Nggak perlu API key, nggak perlu internet, nggak ada biaya per-call. Panduan ini bikin loop itu dari nol di Python, lalu nunjukin ke mana lanjut kalau loop aja udah nggak cukup.
Prerequisites
- Ollama terinstall (dari https://ollama.com/download)
- Satu model yang support tool calling, di-pull lokal:
ollama pull qwen3
- Python 3.9+ dan SDK ollama:
pip install -U ollama
Langkah 1: Jalankan Ollama dan cek model
ollama serve
Pastikan qwen3 ngerespon:
ollama run qwen3 "say hi"
Ollama nyediain HTTP API di localhost:11434. Setiap request cuma POST ke /api/chat berisi daftar messages plus array tools opsional.
Langkah 2: Definisikan tool
Tool itu cuma fungsi plus signature-nya. Di Python SDK kamu nulis fungsi biasa dengan docstring dan nge-pass langsung. Ollama yang ngubah docstring dan type hints jadi JSON Schema yang dipahami model.
from ollama import chat
def get_temperature(city: str) -> str:
"""Get the current temperature for a city.
Args:
city: The name of the city
Returns:
The current temperature for the city
"""
temps = {"New York": "22C", "London": "15C", "Tokyo": "18C"}
return temps.get(city, "Unknown")
messages = [{"role": "user", "content": "What is the temperature in New York?"}]
response = chat(model="qwen3", messages=messages, tools=[get_temperature], think=True)
Dua hal yang gampang kelewat di sini. Pertama, pas model mau pake tool, dia nggak nulis prosa. Dia balikin message dengan field tool_calls berisi nama tool dan argumen yang udah di-parse. Belum ada apa-apa yang beneran jalan. Kedua, SDK vendor (OpenAI, Anthropic) dan yang lokal (Ollama) pake schema tool yang beda, tapi bentuk loop-nya sama di mana pun: declare, decide, execute, feed back.
Langkah 3: Jalankan tool dan kirim hasilnya balik
messages.append(response.message)
if response.message.tool_calls:
call = response.message.tool_calls[0]
result = get_temperature(**call.function.arguments)
messages.append({"role": "tool", "tool_name": call.function.name, "content": str(result)})
final = chat(model="qwen3", messages=messages, tools=[get_temperature], think=True)
print(final.message.content)
Hasil tool ditambahin sebagai message dengan role tool, di-key pake tool_name, biar model bisa baca output tool-nya sendiri. Lalu kamu manggil model lagi dan dia nulis jawaban final yang grounded sama hasil itu.
Langkah 4: Agent loop penuh
Satu hasil tool itu satu round trip. Agent beneran itu loop: model bisa manggil beberapa tool, berurutan atau paralel, sampai dia cukup buat jawab. Kontrol alurnya cuma while loop.
from ollama import chat, ChatResponse
def add(a: float, b: float) -> float:
"""Add two numbers. Args: a (first), b (second)."""
return a + b
def multiply(a: float, b: float) -> float:
"""Multiply two numbers. Args: a (first), b (second)."""
return a * b
available = {"add": add, "multiply": multiply}
messages = [{"role": "user", "content": "What is (11434+12341)*412?"}]
while True:
response: ChatResponse = chat(model="qwen3", messages=messages, tools=[add, multiply], think=True)
messages.append(response.message)
if not response.message.tool_calls:
break
for tc in response.message.tool_calls:
if tc.function.name in available:
result = available[tc.function.name](**tc.function.arguments)
messages.append({"role": "tool", "tool_name": tc.function.name, "content": str(result)})
print(messages[-1].content)
qwen3 juga support parallel tool calls. Butuh cuaca dua kota? Daftarin tool suhu dan tool kondisi sekaligus; model bakal balikin beberapa entri di tool_calls dalam satu turn, kamu jalanin masing-masing, append semua hasil, lalu manggil model lagi.
Kesalahan yang sering bikin nyangkut
- Nggak semua model support tool calling. qwen3, qwen2.5, llama3.1+, mistral, command-r, dan granite bisa; llama3 lama dan gemma2 nggak. Cek halaman model sebelum pull. Model yang nggak bisa handle tool nggak bakal error, dia cuma hallucinate JSON blob.
- Jaga deskripsi tool tetap konkret. Yang vague bikin model manggil tool yang nggak dia butuhin.
- Di model kecil, batasi jumlah tool. Lima-enam tool yang dideskripsiin jelas lebih bagus dari dua puluh yang ambigu.
- Ollama nge-key role
toolpaketool_name, bukanname. Beberapa client pake field milik OpenAI. Kalau loop diem-diem ngabaikan hasil, cek dulu field mana yang kepake buat key result kamu.
Kapan loop aja nggak cukup
While loop ini nutupin agent tunggal yang manggil tool. Begitu kamu butuh graph yang persisten dengan branch, gate persetujuan manusia, atau memory yang selamat pas restart, pindah ke LangGraph. LangGraph nge-model loop yang sama sebagai graph eksplisit dari node dan edge, pake checkpointer buat state, dan tool kamu tetep sama. Ollama duduk di bawah sebagai provider model; LangGraph sebagai lapisan orchestration-nya. Ini keliatan jelas di quickstart LangGraph.
Kesimpulan
Sekarang kamu punya agent lokal yang jalan: Ollama nyediain tools, qwen3 yang mutusin, kode kamu yang ngejalanin, dan hasilnya balik sampai model jawab. Semua jalan di mesin kamu tanpa API key. Next steps: tambah tool yang beneran nyentuh sistem kamu, misalnya penyimpanan notes berbasis SQLite atau HTTP endpoint, lalu pindahin loop-nya ke LangGraph pas branching dan persistence mulai dibutuhin.
Referensi
- Dokumentasi tool calling Ollama: https://docs.ollama.com/capabilities/tool-calling
- Unduh Ollama: https://ollama.com/download
- Quickstart LangGraph: https://docs.langchain.com/oss/python/langgraph/quickstart