karawaci.kode

← Semua snippet

TypeScript Lanjut AI

Tool use pattern dengan Claude SDK

Bikin Claude bisa panggil function kamu — search produk Tokopedia, cek saldo BCA, query database. Pattern tool-call loop yang clean.

Dipublikasikan 3 Juni 2026

Pakai LLM cuma buat ngobrol itu sayang. Tool use bikin Claude bisa ambil keputusan — kapan harus cek inventory Bukalapak, kapan harus query database, kapan harus hitung dengan kalkulator. Pattern di bawah loop sampai Claude bilang “selesai”. Production-ready, tidak ribet.

Kode

import Anthropic from "@anthropic-ai/sdk";
import type { MessageParam, Tool } from "@anthropic-ai/sdk/resources/messages";

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });

// Definisi tool yang Claude bisa panggil
const tools: Tool[] = [
  {
    name: "cek_stok_produk",
    description: "Cek stok produk di gudang berdasarkan SKU",
    input_schema: {
      type: "object",
      properties: {
        sku: { type: "string", description: "Kode SKU produk, contoh: TKP-12345" },
      },
      required: ["sku"],
    },
  },
  {
    name: "hitung_ongkir",
    description: "Hitung ongkos kirim antar kota di Indonesia",
    input_schema: {
      type: "object",
      properties: {
        kota_asal: { type: "string" },
        kota_tujuan: { type: "string" },
        berat_gram: { type: "number" },
      },
      required: ["kota_asal", "kota_tujuan", "berat_gram"],
    },
  },
];

// Implementasi tool — di production ini panggil DB / API beneran
const toolImplementations: Record<string, (args: any) => Promise<unknown>> = {
  cek_stok_produk: async ({ sku }) => {
    // simulasi query DB
    const stok: Record<string, number> = { "TKP-12345": 42, "TKP-99999": 0 };
    return { sku, stok: stok[sku] ?? 0, gudang: "Cikarang" };
  },
  hitung_ongkir: async ({ kota_asal, kota_tujuan, berat_gram }) => {
    const tarif = Math.ceil(berat_gram / 1000) * 12000;
    return { kota_asal, kota_tujuan, ongkir: tarif, kurir: "JNE Reguler" };
  },
};

export async function chatWithTools(userMessage: string): Promise<string> {
  const messages: MessageParam[] = [{ role: "user", content: userMessage }];

  // Loop sampai Claude tidak request tool lagi
  for (let iteration = 0; iteration < 10; iteration++) {
    const response = await client.messages.create({
      model: "claude-opus-4-5",
      max_tokens: 1024,
      tools,
      messages,
    });

    // Append response Claude ke history
    messages.push({ role: "assistant", content: response.content });

    if (response.stop_reason !== "tool_use") {
      // Selesai — ambil text final
      const textBlock = response.content.find((b) => b.type === "text");
      return textBlock?.type === "text" ? textBlock.text : "";
    }

    // Eksekusi semua tool_use block, kumpulkan hasil
    const toolResults: MessageParam = { role: "user", content: [] };

    for (const block of response.content) {
      if (block.type === "tool_use") {
        const impl = toolImplementations[block.name];
        let result: unknown;

        try {
          result = await impl(block.input);
        } catch (err) {
          result = { error: err instanceof Error ? err.message : String(err) };
        }

        (toolResults.content as any[]).push({
          type: "tool_result",
          tool_use_id: block.id,
          content: JSON.stringify(result),
        });
      }
    }

    messages.push(toolResults);
  }

  throw new Error("Max iteration tool loop terlewati");
}

Pemakaian

const jawaban = await chatWithTools(
  "Stok SKU TKP-12345 berapa? Kalau ada, hitung ongkir 2kg dari Jakarta ke Surabaya"
);
console.log(jawaban);
// "Stok produk TKP-12345 di gudang Cikarang ada 42 unit. Ongkir 2kg
//  dari Jakarta ke Surabaya via JNE Reguler sebesar Rp 24.000."
// Pattern: log setiap tool call untuk debugging / audit
const jawaban2 = await chatWithTools(
  "Bandingkan ongkir 1kg dan 5kg dari Bandung ke Medan"
);
// Behind the scene: hitung_ongkir dipanggil 2 kali dengan parameter beda

Kapan dipakai

  • AI agent yang harus akses sistem internal (CRM, inventory, billing).
  • Customer service bot yang butuh action nyata, bukan cuma jawab text.
  • Workflow automation — Claude jadi orchestrator yang panggil tool sesuai context.
  • Data analyst bot — query DB, run kalkulasi, generate chart.

Catatan

  • Iteration limit — selalu kasih max iteration (snippet ini 10). Tanpa itu, kalau Claude loop infinite, biayanya bisa meledak.
  • Tool result harus string — JSON.stringify dulu sebelum kirim, walaupun field nya content.
  • Error handling per tool — kalau satu tool fail, kirim error message-nya ke Claude. Claude bisa decide retry atau jawab user dengan info error.
  • Concurrent tool execution — kalau Claude return multiple tool_use sekaligus, parallelize dengan Promise.all untuk speed.
  • Schema strict — pakai additionalProperties: false di input_schema kalau mau ketat. Validasi lagi server-side karena LLM bisa hallucinate field.

Hati-hati pasang tool yang side-effect (delete, transfer dana). Selalu pasang konfirmasi manual untuk action irreversible. Claude sometimes overconfident dan execute aja.

# tags

claudetool-usefunction-callingagentai

Ditulis oleh Asti Larasati · 3 Juni 2026