Sebelum memulai praktik, pastikan Anda telah memahami konsep dasar pada materi kelas AI Agent & PHP di infokoding.

BAB 14

Membuat Tool REST API Eksternal untuk AI Agent PHP

Membangun tool REST API untuk AI Agent PHP: mengintegrasikan berbagai API eksternal seperti GitHub API, Currency API, dan Weather API dengan client HTTP yang robust.

Terakhir diperbarui:

Langkah Singkat: Alur Pembuatan Tool REST API AI Agent PHP
  1. Bangun Helper HttpClient Reusable: Mengisolasi cURL native dengan timeout (10–15s), header autentikasi Bearer, dan validasi status kode HTTP.
  2. Rancang JSON Schema Spesifik: Definisikan nama endpoint, parameter wajib (required), dan deskripsi semantik untuk inferensi Function Calling.
  3. Implementasikan Kontrak ToolInterface: Tulis class tool (GitHub, Currency, Weather, Google Maps) yang mengeksekusi request HTTP dan menyaring data sensitif.
  4. Tangani Rate Limiting & JSON Decoding Error: Kembalikan response ['status' => 'error', 'message' => '...'] secara terstruktur agar AI Agent dapat melakukan refleksi (Self-Correction).
  5. Daftarkan ke Agent Loop: Injeksi instance tool ke objek Agent menggunakan $agent->registerTool() untuk eksekusi tugas multi-langkah.

TL;DR — Ringkasan Tool REST API Eksternal AI Agent PHP

  • Pemisahan Network Layer: Gunakan class helper HttpClient untuk memusatkan timeout, SSL verification, dan error logging.
  • Rahasia API Key: Simpan seluruh token (GitHub Token, Google Maps Key) di environment variable (.env / getenv()), hindari hardcode di kode PHP.
  • Payload Filtering: Saring hanya kolom data relevan sebelum dikirimkan ke model AI untuk menjaga efisiensi context window dan menghemat biaya token.
  • Resiliensi Jaringan: Antisipasi kegagalan DNS, HTTP 429 (Rate Limit), dan HTTP 500 dengan graceful fallback payload.
Diagram Arsitektur: Integrasi REST API Tools pada PHP AI Agent Core
WebP Lossless • 86 KB • Schema Ready
Diagram arsitektur integrasi REST API Tools pada PHP AI Agent: OpenAI LLM mengalirkan tool_calls ke Agent Orchestrator dan HttpClient Helper, yang menghubungkan 4 endpoint API eksternal mencakup GitHub Search, Open-Meteo Weather, Currency Rate, dan Google Maps Geocoding
Gambar 14.1: Pipeline Arsitektur Eksternal API Tools — OpenAI LLM mengevaluasi parameter intent pengguna, meneruskannya ke Router Tool PHP, mengeksekusi request HTTP terenkripsi via HttpClient Helper, dan mengembalikan data terformat ke Context Manager.

Membangun HttpClient Helper untuk Tool AI Agent

Menghubungkan agent ke lusinan endpoint API luar membutuhkan wrapper HTTP yang tangguh, mendukung timeout ketat, custom headers, dan penanganan status response non-200 secara terpusat:

<?php

namespace App\Http;

class HttpClient
{
    private int $timeout;
    private array $defaultHeaders;

    public function __construct(int $timeout = 15, array $defaultHeaders = [])
    {
        $this->timeout        = $timeout;
        $this->defaultHeaders = $defaultHeaders;
    }

    /**
     * Mengeksekusi HTTP GET Request dengan proteksi cURL.
     */
    public function get(string $url, array $params = [], array $headers = []): array
    {
        if (!empty($params)) {
            $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($params);
        }

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => $this->buildHeaders($headers),
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 3,
            CURLOPT_USERAGENT      => 'InfoKoding-AIAgent/1.0 (PHP-Tool-Client)',
        ]);

        $rawBody  = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_error($ch);
        curl_close($ch);

        if ($curlErr) {
            return [
                'ok'      => false,
                'status'  => 0,
                'error'   => "cURL Connection Error: {$curlErr}",
                'data'    => null,
            ];
        }

        // Defensive JSON Decoding
        $decoded = json_decode($rawBody, true);
        $isJson  = (json_last_error() === JSON_ERROR_NONE);

        return [
            'ok'      => ($httpCode >= 200 && $httpCode < 300),
            'status'  => $httpCode,
            'error'   => ($httpCode >= 400) ? "HTTP {$httpCode} API Error" : null,
            'data'    => $isJson ? $decoded : $rawBody,
        ];
    }

    private function buildHeaders(array $extra): array
    {
        $merged = array_merge($this->defaultHeaders, $extra);
        $result = [];
        foreach ($merged as $k => $v) {
            $result[] = is_numeric($k) ? $v : "{$k}: {$v}";
        }
        return $result;
    }
}

Membuat Tool GitHub API untuk AI Agent PHP

Tool ini memungkinkan AI Agent melakukan audit repository, membaca profil developer, dan mencari repositori open source secara terstruktur:

<?php

namespace App\Tools;

use App\Http\HttpClient;

class GitHubTool implements ToolInterface
{
    private HttpClient $http;

    public function __construct(?string $token = null)
    {
        $token = $token ?? getenv('GITHUB_TOKEN') ?: '';
        $headers = [
            'Accept' => 'application/vnd.github.v3+json',
        ];
        if (!empty($token)) {
            $headers['Authorization'] = "Bearer {$token}";
        }

        $this->http = new HttpClient(timeout: 10, defaultHeaders: $headers);
    }

    public function getDefinition(): array
    {
        return [
            'type'     => 'function',
            'function' => [
                'name'        => 'github_search',
                'description' => 'Mencari repositori atau pengguna publik di platform GitHub.',
                'parameters'  => [
                    'type'       => 'object',
                    'properties' => [
                        'type'  => [
                            'type'        => 'string',
                            'enum'        => ['repositories', 'users'],
                            'description' => 'Tipe pencarian: repositories atau users.',
                        ],
                        'query' => [
                            'type'        => 'string',
                            'description' => 'Kata kunci pencarian repositori atau nama username GitHub.',
                        ],
                    ],
                    'required' => ['type', 'query'],
                ],
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        $type  = $args['type'] ?? 'repositories';
        $query = trim($args['query'] ?? '');

        if (empty($query)) {
            return ['status' => 'error', 'message' => 'Parameter query tidak boleh kosong.'];
        }

        $res = $this->http->get("https://api.github.com/search/{$type}", [
            'q'        => $query,
            'per_page' => 5,
        ]);

        if (!$res['ok']) {
            return [
                'status'  => 'error',
                'message' => $res['error'] ?? 'Gagal mengakses GitHub API (mungkin terkena Rate Limit).',
            ];
        }

        $items = $res['data']['items'] ?? [];
        $clean = array_map(function ($item) use ($type) {
            return ($type === 'repositories') ? [
                'name'        => $item['full_name'] ?? '',
                'url'         => $item['html_url'] ?? '',
                'stars'       => $item['stargazers_count'] ?? 0,
                'description' => $item['description'] ?? '',
                'language'    => $item['language'] ?? '',
            ] : [
                'username' => $item['login'] ?? '',
                'url'      => $item['html_url'] ?? '',
                'avatar'   => $item['avatar_url'] ?? '',
            ];
        }, array_slice($items, 0, 5));

        return [
            'status'      => 'success',
            'total_count' => $res['data']['total_count'] ?? count($clean),
            'results'     => $clean,
        ];
    }
}

Membuat Tool Currency API (Konversi Mata Uang)

Memberikan kemampuan kalkulasi finansial real-time menggunakan kurs valuta asing terkini:

<?php

namespace App\Tools;

use App\Http\HttpClient;

class CurrencyTool implements ToolInterface
{
    private HttpClient $http;

    public function __construct()
    {
        $this->http = new HttpClient(timeout: 10);
    }

    public function getDefinition(): array
    {
        return [
            'type'     => 'function',
            'function' => [
                'name'        => 'convert_currency',
                'description' => 'Mengonversi nominal mata uang asing berdasarkan kurs kurs terkini.',
                'parameters'  => [
                    'type'       => 'object',
                    'properties' => [
                        'amount' => ['type' => 'number', 'description' => 'Jumlah nominal yang ingin dikonversi.'],
                        'from'   => ['type' => 'string', 'description' => 'Kode mata uang asal (misal: USD, EUR, JPY).'],
                        'to'     => ['type' => 'string', 'description' => 'Kode mata uang tujuan (misal: IDR, SGD).'],
                    ],
                    'required' => ['amount', 'from', 'to'],
                ],
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        $amount = (float) ($args['amount'] ?? 0);
        $from   = strtoupper(trim($args['from'] ?? 'USD'));
        $to     = strtoupper(trim($args['to'] ?? 'IDR'));

        $res = $this->http->get("https://api.exchangerate-api.com/v4/latest/{$from}");

        if (!$res['ok']) {
            return ['status' => 'error', 'message' => "Gagal mengambil kurs valuta untuk mata uang {$from}."];
        }

        $rate = $res['data']['rates'][$to] ?? null;
        if (!$rate) {
            return ['status' => 'error', 'message' => "Kode mata uang tujuan '{$to}' tidak ditemukan."];
        }

        $converted = $amount * $rate;
        return [
            'status'         => 'success',
            'base_currency'  => $from,
            'target_currency'=> $to,
            'nominal_input'  => $amount,
            'exchange_rate'  => $rate,
            'result'         => round($converted, 2),
            'formatted'      => number_format($converted, 2, ',', '.'),
        ];
    }
}

Membuat Tool Weather API (Prakiraan Cuaca Real-Time)

Memberikan data kondisi cuaca waktu-nyata (suhu, kelembapan, kecepatan angin) menggunakan endpoint Open-Meteo API yang cepat dan bebas biaya lisensi:

<?php

namespace App\Tools;

use App\Http\HttpClient;

class WeatherTool implements ToolInterface
{
    private HttpClient $http;

    public function __construct()
    {
        $this->http = new HttpClient(timeout: 10);
    }

    public function getDefinition(): array
    {
        return [
            'type'     => 'function',
            'function' => [
                'name'        => 'get_weather',
                'description' => 'Mendapatkan data prakiraan cuaca, temperatur, dan kecepatan angin terkini berdasarkan koordinat garis lintang (latitude) dan bujur (longitude). Masukkan koordinat kota.',
                'parameters'  => [
                    'type'       => 'object',
                    'properties' => [
                        'latitude'  => ['type' => 'number', 'description' => 'Garis lintang lokasi (misal: -6.2088 untuk Jakarta).'],
                        'longitude' => ['type' => 'number', 'description' => 'Garis bujur lokasi (misal: 106.8456 untuk Jakarta).'],
                        'city_name' => ['type' => 'string', 'description' => 'Nama kota referensi (opsional, misal: Jakarta).'],
                    ],
                    'required' => ['latitude', 'longitude'],
                ],
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        $lat  = (float) ($args['latitude'] ?? 0);
        $lon  = (float) ($args['longitude'] ?? 0);
        $city = $args['city_name'] ?? "Lokasi ({$lat}, {$lon})";

        $res = $this->http->get('https://api.open-meteo.com/v1/forecast', [
            'latitude'      => $lat,
            'longitude'     => $lon,
            'current_weather' => 'true',
            'hourly'          => 'relativehumidity_2m',
        ]);

        if (!$res['ok']) {
            return ['status' => 'error', 'message' => 'Gagal mengambil data cuaca dari Open-Meteo API.'];
        }

        $current = $res['data']['current_weather'] ?? [];

        return [
            'status'       => 'success',
            'location'     => $city,
            'temperature'  => ($current['temperature'] ?? 'N/A') . ' °C',
            'windspeed'    => ($current['windspeed'] ?? 'N/A') . ' km/h',
            'weathercode'  => $current['weathercode'] ?? 0,
            'observation_time' => $current['time'] ?? date('Y-m-d H:i'),
        ];
    }
}

Membuat Tool Google Maps API (Geocoding & Lokasi)

Mengonversi nama alamat atau tempat wisata menjadi koordinat geografis presisi untuk dikombinasikan dengan WeatherTool:

<?php

namespace App\Tools;

use App\Http\HttpClient;

class GoogleMapsTool implements ToolInterface
{
    private HttpClient $http;
    private string $apiKey;

    public function __construct(?string $apiKey = null)
    {
        $this->apiKey = $apiKey ?? getenv('GOOGLE_MAPS_API_KEY') ?: '';
        $this->http   = new HttpClient(timeout: 10);
    }

    public function getDefinition(): array
    {
        return [
            'type'     => 'function',
            'function' => [
                'name'        => 'geocode_location',
                'description' => 'Mencari koordinat lintang/bujur (latitude & longitude) dan alamat lengkap berdasarkan nama kota, tempat wisata, atau alamat jalan.',
                'parameters'  => [
                    'type'       => 'object',
                    'properties' => [
                        'address' => ['type' => 'string', 'description' => 'Alamat atau nama lokasi yang ingin dicari (misal: Monas Jakarta, Malioboro Yogyakarta).'],
                    ],
                    'required' => ['address'],
                ],
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        $address = trim($args['address'] ?? '');
        if (empty($address)) {
            return ['status' => 'error', 'message' => 'Alamat pencarian tidak boleh kosong.'];
        }

        $res = $this->http->get('https://maps.googleapis.com/maps/api/geocode/json', [
            'address' => $address,
            'key'     => $this->apiKey,
        ]);

        if (!$res['ok']) {
            return ['status' => 'error', 'message' => 'Gagal terhubung ke Google Maps Geocoding API.'];
        }

        $results = $res['data']['results'] ?? [];
        if (empty($results)) {
            return ['status' => 'error', 'message' => "Lokasi '{$address}' tidak ditemukan."];
        }

        $top = $results[0];
        $loc = $top['geometry']['location'] ?? [];

        return [
            'status'            => 'success',
            'formatted_address' => $top['formatted_address'] ?? $address,
            'latitude'          => $loc['lat'] ?? 0.0,
            'longitude'         => $loc['lng'] ?? 0.0,
            'place_id'          => $top['place_id'] ?? '',
        ];
    }
}
Matriks Komparasi Endpoint REST API Tools untuk AI Agent PHP
Nama Tool ClassURL Endpoint TargetMetode AutentikasiOutput Data Relevan
GitHubToolapi.github.com/search/{type}Bearer Token (Opsional / Header)full_name, html_url, stars, language, description
CurrencyToolapi.exchangerate-api.com/v4/latest/{from}No Auth (Free Public Tier)base_currency, target_currency, exchange_rate, result
WeatherToolapi.open-meteo.com/v1/forecastNo Auth (Open Source API)temperature, windspeed, weathercode, observation_time
GoogleMapsToolmaps.googleapis.com/maps/api/geocode/jsonAPI Key (?key= parameter)formatted_address, latitude, longitude, place_id

API Integration Takeaway: Penggunaan header terpusat dan penanganan JSON defensif memastikan kestabilan koneksi saat model AI memanggil beberapa tools secara paralel.

Eksekusi Paralel Multiple REST API Tools Menggunakan curl_multi

Ketika model AI mengembalikan beberapa pemanggilan fungsi sekaligus (Parallel Tool Calling) — misalnya ingin mengecek cuaca di Jakarta, Surabaya, dan Bali bersamaan — mengeksekusi request HTTP secara sekuensial (satu per satu) akan melipatgandakan latensi (misal: 3 × 500ms = 1.500ms). Gunakan ParallelHttpClient berbasis curl_multi native PHP untuk mengeksekusi seluruh request secara serentak (asinkron) dalam rentang waktu request terlambat tunggal (~500ms):

<?php

namespace App\Http;

class ParallelHttpClient
{
    /**
     * Mengeksekusi array URL endpoint secara serentak via non-blocking cURL Multi.
     *
     * @param array<string, array{url: string, params?: array, headers?: array}> $requests
     * @return array<string, array{ok: bool, status: int, data: mixed}>
     */
    public function getParallel(array $requests, int $timeout = 10): array
    {
        $multiHandle = curl_multi_init();
        $handles     = [];
        $responses   = [];

        // 1. Inisialisasi masing-masing cURL channel
        foreach ($requests as $key => $req) {
            $url = $req['url'];
            if (!empty($req['params'])) {
                $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($req['params']);
            }

            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => $timeout,
                CURLOPT_SSL_VERIFYPEER => true,
                CURLOPT_USERAGENT      => 'InfoKoding-ParallelAgent/1.0',
            ]);

            curl_multi_add_handle($multiHandle, $ch);
            $handles[$key] = $ch;
        }

        // 2. Eksekusi seluruh request HTTP secara paralel (Non-blocking loop)
        $running = null;
        do {
            $status = curl_multi_exec($multiHandle, $running);
            if ($running > 0) {
                curl_multi_select($multiHandle, 0.1); // Sleep ringan mencegah CPU spike
            }
        } while ($running > 0 && $status === CURLM_OK);

        // 3. Kumpulkan hasil response dan tutup handle
        foreach ($handles as $key => $ch) {
            $rawBody  = curl_multi_getcontent($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $curlErr  = curl_error($ch);

            curl_multi_remove_handle($multiHandle, $ch);
            curl_close($ch);

            $decoded = json_decode($rawBody, true);
            $isJson  = (json_last_error() === JSON_ERROR_NONE);

            $responses[$key] = [
                'ok'     => ($httpCode >= 200 && $httpCode < 300 && empty($curlErr)),
                'status' => $httpCode,
                'error'  => $curlErr ?: (($httpCode >= 400) ? "HTTP Error {$httpCode}" : null),
                'data'   => $isJson ? $decoded : $rawBody,
            ];
        }

        curl_multi_close($multiHandle);
        return $responses;
    }
}

Contoh Eksekusi Parallel Multi-Tool Request:

<?php
// Contoh pemanggilan paralel 3 kota sekaligus dalam 1 siklus tool_calls
$parallelHttp = new App\Http\ParallelHttpClient();

$batchRequests = [
    'jakarta'  => ['url' => 'https://api.open-meteo.com/v1/forecast', 'params' => ['latitude' => -6.2088, 'longitude' => 106.8456, 'current_weather' => 'true']],
    'surabaya' => ['url' => 'https://api.open-meteo.com/v1/forecast', 'params' => ['latitude' => -7.2575, 'longitude' => 112.7521, 'current_weather' => 'true']],
    'denpasar' => ['url' => 'https://api.open-meteo.com/v1/forecast', 'params' => ['latitude' => -8.6705, 'longitude' => 115.2126, 'current_weather' => 'true']],
];

$start = microtime(true);
$results = $parallelHttp->getParallel($batchRequests);
$duration = round((microtime(true) - $start) * 1000, 2);

echo "Selesai mengambil 3 endpoint cuaca dalam waktu: {$duration} ms (Paralel).\n";

Latency Benchmark: Penggunaan curl_multi memangkas total latensi jaringan hingga 65–75% saat menangani multi-tool calls array.

Panduan Function Calling REST API PHP & Integrasi API curl_multi AI Agent:

1. Pendaftaran ke OpenAI Function Calling Loop: Setelah class tool dibuat, daftarkan instance objek ke agent via $agent->registerTool(new GitHubTool()). Di balik layar, loop agen mengekstrak skema getDefinition() untuk dikirimkan pada parameter tools payload OpenAI API. Saat model mengembalikan pemanggilan fungsi (tool_calls), orchestrator PHP secara otomatis mengeksekusi $tool->execute($args) dan mengembalikan jawabannya dengan role tool dan tool_call_id yang sesuai.

2. Best Practice Penanganan Rate Limiting (HTTP 429): API publik sering menerapkan kuota rate limit per menit. Jika response mengembalikan status 429 Too Many Requests, baca header Retry-After atau terapkan mekanisme Exponential Backoff dengan jeda waktu adaptif. Jangan mematikan loop agent, melainkan kembalikan notifikasi error terstruktur agar model AI dapat menginformasikan waktu tunggu ke pengguna.

3. Resiliensi JSON Decoding Error: Endpoint eksternal adakalanya mengembalikan halaman HTML 502 Cloudflare saat server mereka down. Helper HttpClient memeriksa json_last_error() === JSON_ERROR_NONE sebelum mengakses payload array untuk mencegah fatal error tipe data pada PHP.

FAQ & Troubleshooting cURL, API Key, dan Koneksi REST API

1. Bagaimana cara mengatasi cURL Error 60 (SSL certificate problem)?

Ringkasan Jawaban:Unduh sertifikat CA bundle terbaru dari curl.se/ca/cacert.pem, lalu daftarkan path file tersebut pada baris curl.cainfo = "/path/cacert.pem" di file php.ini.

Jangan menonaktifkan CURLOPT_SSL_VERIFYPEER => false di server produksi karena akan membuka celah serangan Man-in-the-Middle (MitM).

2. Di mana sebaiknya menyimpan API Key GitHub dan Google Maps?

Ringkasan Jawaban:Simpan seluruh kredensial di file environment .env dan baca menggunakan getenv('KEY_NAME') atau library vlucas/phpdotenv.

Pastikan file .env telah didaftarkan ke dalam .gitignore agar API Key tidak bocor ke repositori publik.

3. Bagaimana cara menangani respons API eksternal yang sangat besar?

Ringkasan Jawaban:Lakukan pemfilteran (*field filtering*) di level class PHP dengan hanya mengambil properti data yang krusial sebelum dikonversi ke JSON dan dikirim ke model AI.

Mengirimkan payload ribuan baris mentah dari REST API dapat menghabiskan kuota context window LLM dan melipatgandakan biaya token.

RA

Rusmawan Abdullah Sani

Lead Software Engineer & System Architect

Praktisi pengembangan backend PHP modern, arsitektur AI Agent, microservices, dan otomasi server Linux. Terhubung melalui profil LinkedIn.