Function calling (atau tool use, tergantung API yang Anda gunakan) adalah fitur yang mengubah LLM dari generator teks menjadi agen yang mampu mengambil tindakan dunia nyata. Saya telah mengimplementasikan integrasi function calling dengan API OpenAI dan Anthropic untuk sistem produksi — pencarian data ERP, panggilan API eksternal, penulisan database, dan orkestrasi multi-langkah. API terlihat serupa di permukaan tetapi memiliki perbedaan bermakna dalam cara menangani panggilan paralel, penggunaan alat yang diperlukan vs opsional, dan pelaporan error.
Function calling OpenAI (sekarang disebut 'tools' di API v2) dan tool use Anthropic keduanya didasarkan pada konsep yang sama: Anda mendefinisikan sekumpulan fungsi dengan deskripsi JSON Schema, model memutuskan kapan memanggilnya, dan Anda mengeksekusi panggilan dan mengembalikan hasil. Perbedaan filosofisnya ada pada kontrol. OpenAI menawarkan tool_choice: 'required', 'auto', atau pemaksaan alat tertentu. Anthropic menawarkan tool_choice dengan 'auto', 'any' (harus menggunakan setidaknya satu alat), dan pemaksaan alat tertentu.
Tool use Anthropic mengembalikan hasil sebagai content block daripada field function_call terpisah. Respons mungkin berisi blok teks dan tool_use yang dicampur. Kode Anda harus mengiterasi blok konten, mengidentifikasi blok tool_use, mengeksekusi masing-masing, dan mengembalikan blok tool_result dalam pesan berikutnya.
┌─────────────────────────────────────────────────────────────┐
│ Parallel Tool Call Flow (Anthropic) │
│ │
│ User: "What is the status of order #123 and invoice #456?" │
│ │ │
│ ▼ │
│ Model returns TWO tool_use blocks in one response: │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ get_order_status │ │ get_invoice_status │ │
│ │ id: "tool_abc" │ │ id: "tool_def" │ │
│ │ order_id: "123" │ │ invoice_id: "456" │ │
│ └─────────┬──────────┘ └──────────┬─────────┘ │
│ │ parallel execution │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ Promise.all([getOrder(123), getInv(456)]) │ │
│ └────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ Return BOTH tool_result blocks → Model synthesizes answer │
└─────────────────────────────────────────────────────────────┘Dari pengalaman saya membangun agen multi-alat: implementasikan dispatcher eksekusi alat generik dari awal, bukan rantai if/else per alat. Petakan nama alat ke fungsi handler dalam kamus, validasi input terhadap skema sebelum mengeksekusi, dan bungkus setiap handler dalam try/catch dengan pengembalian error terstruktur.
Kedua API mendukung tool call paralel — model mengembalikan beberapa blok tool_call/tool_use dalam satu respons, dan Anda mengeksekusinya secara bersamaan sebelum mengembalikan semua hasil. Ini secara dramatis mengurangi latensi untuk operasi yang dapat berjalan secara paralel. Tanpa panggilan alat paralel, agen yang membutuhkan 3 sumber data akan memerlukan 3 round trip. Dengan panggilan paralel, ini adalah 1 round trip untuk pemilihan alat ditambah 1 untuk hasil — pengurangan latensi sekitar 60%.
Eksekusi alat produksi akan gagal. Timeout jaringan, error database, batas rate API eksternal — semua ini harus dikomunikasikan kembali ke model sehingga dapat menanganinya dengan baik. Kembalikan error terstruktur dalam hasil alat daripada menyebarkan pengecualian. Sertakan: error_type, message, dan boolean retry_suggested.
import Anthropic from "@anthropic-ai/sdk"
const client = new Anthropic()
// Generic tool dispatcher
const toolHandlers: Record<string, (params: unknown) => Promise<unknown>> = {
get_order: async (p) => fetchOrder(p as { order_id: string }),
get_invoice: async (p) => fetchInvoice(p as { invoice_id: string }),
list_products: async (p) => fetchProducts(p as { category?: string }),
}
// Agentic loop with parallel tool execution
async function runAgent(userMessage: string, maxIterations = 10) {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: userMessage }
]
for (let i = 0; i < maxIterations; i++) {
const response = await client.messages.create({
model: "claude-opus-4-5",
max_tokens: 4096,
tools: Object.keys(toolHandlers).map(name => ({
name,
description: toolDescriptions[name],
input_schema: toolSchemas[name],
})),
messages,
})
// Collect all tool_use blocks
const toolUseBlocks = response.content.filter(b => b.type === "tool_use")
if (toolUseBlocks.length === 0 || response.stop_reason === "end_turn") {
// No more tool calls — return final text response
const textBlock = response.content.find(b => b.type === "text")
return textBlock?.type === "text" ? textBlock.text : ""
}
// Execute all tool calls in PARALLEL
const toolResults = await Promise.all(
toolUseBlocks.map(async (block) => {
if (block.type !== "tool_use") return null
try {
const handler = toolHandlers[block.name]
if (!handler) throw new Error(`Unknown tool: ${block.name}`)
const result = await handler(block.input)
return { type: "tool_result" as const, tool_use_id: block.id, content: JSON.stringify(result) }
} catch (err) {
return {
type: "tool_result" as const,
tool_use_id: block.type === "tool_use" ? block.id : "",
content: JSON.stringify({ error: String(err), retry_suggested: true }),
is_error: true,
}
}
})
)
// Append assistant response + all tool results to messages
messages.push({ role: "assistant", content: response.content })
messages.push({ role: "user", content: toolResults.filter(Boolean) as Anthropic.Messages.ToolResultBlockParam[] })
}
throw new Error("Max iterations reached")
}Pola kuat yang sering diabaikan: gunakan tool calling semata-mata untuk ekstraksi output terstruktur, tanpa eksekusi fungsi aktual. Definisikan alat dengan skema struktur yang ingin Anda ekstrak dari teks, atur tool_choice untuk memaksa alat tertentu itu, dan model akan selalu mengembalikan struktur JSON yang valid sesuai skema Anda.
Definisi alat dihitung sebagai token input di setiap panggilan API, bahkan ketika model tidak menggunakan alat tersebut. Sekumpulan 20 definisi alat yang detail dapat menambahkan 2.000-5.000 token per panggilan. Optimasi: gunakan perutean alat untuk hanya mengirim definisi alat yang relevan berdasarkan niat pesan pengguna. Panggilan pra-klasifikasi dengan model murah dapat merutekan ke subset alat yang sesuai sebelum memanggil model yang mahal.
Agen kompleks memerlukan beberapa putaran penggunaan alat. Polanya adalah loop agentik: panggil model, periksa penggunaan alat, eksekusi alat, tambahkan hasil, panggil model lagi, ulangi hingga model mengembalikan respons hanya teks. Implementasikan batas iterasi maksimum (biasanya 10-15 langkah) untuk mencegah loop tak terbatas.
Untuk proyek baru, saya merekomendasikan tool use Anthropic ketika Anda membutuhkan model paling mampu (Claude Opus 4) atau ketika kasus penggunaan Anda mendapat manfaat dari model yang secara alami mencampur penjelasan dengan tool call. Gunakan function calling OpenAI ketika Anda membutuhkan kompatibilitas ekosistem terluas atau ketika Anda menggunakan model yang di-fine-tune.
Sumber & Bacaan Lanjutan