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

BAB 5

Membuat AI Chat Sederhana dengan PHP

Langkah praktis membuat AI chat pertama dengan PHP: simpan API Key via .env, penanganan error API, request OpenAI cURL, dan integrasi frontend AJAX/Fetch API.

Terakhir diperbarui:

TL;DR (Ringkasan Cepat)
  • Keamanan API Key: Simpan OPENAI_API_KEY di file .env menggunakan vlucas/phpdotenv, jangan pernah hardcode key di dalam skrip PHP.
  • Struktur Request: OpenAI Chat Completions API menerima array messages berisikan role system, user, dan assistant.
  • Error Handling: Wajib menangani HTTP Status 401 (Unauthorized) dan 429 (Rate Limit/Quota) dengan teknik Exponential Backoff Retry.
  • Frontend Asinkron: Gunakan JavaScript Fetch API (AJAX) untuk mengirim prompt dan menampilkan pesan AI tanpa reload halaman.

Repositori Kode Tutorial Bab 5

Source code lengkap project AI Chat PHP (termasuk OpenAIClient, .env config, api/chat.php, dan Fetch API UI) tersedia di GitHub.

GitHub Repository

Live Demo Simulator: AI Chat PHP

Interactive Preview
🤖 Asisten AI PHP: Halo! Ketik pertanyaan atau prompt di bawah untuk mencoba simulasi pengiriman pesan AI Chat.
Diagram Alur Data: Frontend Fetch API → PHP Backend → OpenAI API Frontend Browser HTML / CSS / JavaScript JavaScript Fetch API • Render UI Chat • Asynchronous Request POST JSON JSON Response PHP 8.3 Backend api/chat.php & OpenAIClient • Load .env & Config • Error Handling (401/429) • Exponential Backoff cURL HTTPS Payload JSON OpenAI API Server gpt-4o-mini Model Chat Completions • Process Messages • Return Token Usage

1. Belajar AI Agent PHP: Pengelolaan & Keamanan API Key via File .env

Sebelum menuliskan baris kode komunikasi dengan OpenAI, prinsip pertama dan paling krusial dalam belajar AI Agent PHP adalah keamanan credential. Jangan pernah menuliskan langsung (hardcode) API Key di dalam kode PHP yang di-commit ke sistem kontrol versi seperti Git.

Gunakan library populer vlucas/phpdotenv untuk membaca kredensial dari file environment (.env):

composer require vlucas/phpdotenv

Buat file .env di direktori utama project Anda:

OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_MODEL=gpt-4o-mini
OPENAI_MAX_TOKENS=4096

Proteksi File .gitignore

Pastikan file .env telah ditambahkan ke file .gitignore Anda. Hanya sertakan file template seperti .env.example (tanpa isi key asli) ke repositori GitHub Anda.

Selanjutnya, buat file config/config.php untuk memuat variabel environment secara terpusat:

<?php
// config/config.php
require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;

// Muat variabel environment dari file .env
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->safeLoad();

return [
    'openai_api_key'    => $_ENV['OPENAI_API_KEY'] ?? getenv('OPENAI_API_KEY') ?? '',
    'openai_model'      => $_ENV['OPENAI_MODEL'] ?? 'gpt-4o-mini',
    'openai_max_tokens' => (int)($_ENV['OPENAI_MAX_TOKENS'] ?? 4096),
];

2. Arsitektur AI Agent & PHP: Menyusun Class OpenAIClient dengan Penanganan Error API

OpenAI Chat Completions API menerima payload JSON melalui HTTP POST request ke endpoint https://api.openai.com/v1/chat/completions. Di dunia produksi, request API dapat mengalami kegagalan akibat masalah jaringan, kesalahan API Key, atau batasan rate limit.

Mari kita buat class App\OpenAIClient di file src/OpenAIClient.php yang dilengkapi dengan validasi dan mekanisme Exponential Backoff Retry untuk menangani skenario error API secara robust:

<?php

namespace App;

use RuntimeException;

class OpenAIClient
{
    private string $apiKey;
    private string $model;
    private int $maxTokens;
    private string $baseUrl = 'https://api.openai.com/v1';

    public function __construct(
        string $apiKey,
        string $model = 'gpt-4o-mini',
        int $maxTokens = 4096
    ) {
        if (empty($apiKey)) {
            throw new RuntimeException('API Key OpenAI belum dikonfigurasi di file .env!');
        }

        $this->apiKey    = $apiKey;
        $this->model     = $model;
        $this->maxTokens = $maxTokens;
    }

    /**
     * Kirim pesan ke OpenAI dengan penanganan error & retry otomatis (Exponential Backoff).
     *
     * @param array $messages Array percakapan [{role, content}]
     * @param array $tools    Optional: daftar tools untuk function calling
     * @param int   $maxRetries Maksimal percobaan ulang jika terjadi Rate Limit (429/50x)
     * @return array          Parsed response JSON dari API
     * @throws RuntimeException Jika terjadi error permanen
     */
    public function chat(array $messages, array $tools = [], int $maxRetries = 3): array
    {
        $payload = [
            'model'      => $this->model,
            'messages'   => $messages,
            'max_tokens' => $this->maxTokens,
        ];

        if (!empty($tools)) {
            $payload['tools']       = $tools;
            $payload['tool_choice'] = 'auto';
        }

        $retryCount = 0;
        $delaySeconds = 2; // Waktu jeda awal (detik)

        while (true) {
            $ch = curl_init($this->baseUrl . '/chat/completions');
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => json_encode($payload),
                CURLOPT_HTTPHEADER     => [
                    'Content-Type: application/json',
                    'Authorization: Bearer ' . $this->apiKey,
                ],
                CURLOPT_SSL_VERIFYPEER => true,
                CURLOPT_TIMEOUT        => 60,
            ]);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error    = curl_error($ch);
            curl_close($ch);

            // Skenario Error 1: Gagal jaringan / cURL error
            if ($error) {
                throw new RuntimeException("Gagal terhubung ke OpenAI (cURL Error): $error");
            }

            $data = json_decode($response, true) ?? [];

            // Respons Berhasil (HTTP 200 OK)
            if ($httpCode === 200) {
                return $data;
            }

            $errorMessage = $data['error']['message'] ?? "HTTP Error Code: $httpCode";

            // Skenario Error 2: HTTP 401 Unauthorized (API Key salah/hilang)
            if ($httpCode === 401) {
                throw new RuntimeException("HTTP 401 Unauthorized: API Key tidak valid. Periksa nilai OPENAI_API_KEY di file .env Anda.");
            }

            // Skenario Error 3: HTTP 429 (Rate Limit) atau HTTP 50x (Server Downtime) -> Coba Retry
            if (($httpCode === 429 || $httpCode >= 500) && $retryCount < $maxRetries) {
                $retryCount++;
                sleep($delaySeconds);
                $delaySeconds *= 2; // Jeda meningkat: 2s, 4s, 8s...
                continue;
            }

            // Skenario Error 4: HTTP 429 permanen setelah max retries
            if ($httpCode === 429) {
                throw new RuntimeException("HTTP 429 Quota Exceeded / Rate Limit: Saldo OpenAI Anda habis atau request terlalu intensif.");
            }

            // Error umum API lainnya
            throw new RuntimeException("OpenAI API Error [$httpCode]: $errorMessage");
        }
    }

    /**
     * Memangkas (slice) array $messages agar token tidak melebihi batas model saat percakapan memanjang.
     * Metode Sliding Window ini selalu mempertahankan System Prompt di index 0 dan N pesan terakhir.
     *
     * @param array $messages   Array percakapan [{role, content}]
     * @param int   $maxHistory Maksimal jumlah pesan terbelakang yang dipertahankan
     * @return array            Array $messages yang sudah dipangkas
     */
    public function trimMessages(array $messages, int $maxHistory = 10): array
    {
        if (count($messages) <= $maxHistory + 1) {
            return $messages;
        }

        $systemMessage = [];
        if (isset($messages[0]) && ($messages[0]['role'] ?? '') === 'system') {
            $systemMessage = [array_shift($messages)];
        }

        // Ambil $maxHistory percakapan paling baru
        $recentMessages = array_slice($messages, -$maxHistory);

        return array_merge($systemMessage, $recentMessages);
    }

    /**
     * Ekstrak teks balasan AI dari response array.
     */
    public function extractContent(array $response): string
    {
        return $response['choices'][0]['message']['content'] ?? '';
    }
}

3. Matrix Penanganan Skenario Error API OpenAI (HTTP Status)

Berikut adalah tabel acuan lengkap mengenai penanganan status error HTTP dari OpenAI API beserta akar penyebab dan solusi penangannya di aplikasi PHP:

HTTP Status Nama Error Penyebab Utama Solusi & Mitigasi PHP
401 Unauthorized API Key belum diisi, salah ketik, atau di-revoke di OpenAI Dashboard. Periksa file .env, pastikan variabel OPENAI_API_KEY terisi tanpa spasi tersembunyi.
429 Too Many Requests / Quota Exceeded RPM (Rate Limit) terlampaui atau saldo billing prepaid OpenAI habis ($0 balance). Gunakan Exponential Backoff Retry pada skrip PHP dan isi saldo billing di platform.openai.com.
500 / 503 Internal Server Error / Service Unavailable Server OpenAI mengalami kelebihan beban atau pemeliharaan sementara. Implementasikan automatic retry 2-3 kali dengan penundaan jeda bertahap (sleep 2s, 4s).
cURL Error Connection / Timeout Failure Koneksi jaringan server terputus atau request melebihi batas waktu (timeout). Atur CURLOPT_TIMEOUT (60 detik) dan tangkap exception dengan blok try-catch.

4. Integrasi Backend AI Agent PHP: Endpoint API Chat dengan Validasi CSRF & Rate Limiting IP

Untuk mengamankan endpoint api/chat.php dari serangan pembengkakan tagihan billing atau penyalahgunaan request (DDoS), kita menambahkan dua lapisan keamanan penting:

  • IP Throttling / Rate Limiting: Membatasi jumlah request maksimum per alamat IP client (misal max 10 request per menit).
  • CSRF Token Validation: Memastikan request yang dikirim ke backend berasal dari halaman web kita yang sah dengan mencocokkan header X-CSRF-TOKEN.
<?php
// api/chat.php
header('Content-Type: application/json; charset=utf-8');

require_once __DIR__ . '/../vendor/autoload.php';

use App\OpenAIClient;

session_start();

try {
    // 1. IP Throttling / Rate Limiting (Maks 10 Request / Menit per IP)
    $clientIp = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
    $rateKey  = 'rate_limit_' . md5($clientIp);
    $now      = time();

    if (!isset($_SESSION[$rateKey])) {
        $_SESSION[$rateKey] = ['count' => 1, 'start_time' => $now];
    } else {
        if ($now - $_SESSION[$rateKey]['start_time'] < 60) {
            if ($_SESSION[$rateKey]['count'] >= 10) {
                http_response_code(429);
                echo json_encode([
                    'success' => false,
                    'error'   => 'Terlalu banyak request. Silakan tunggu 1 menit lagi.'
                ]);
                exit;
            }
            $_SESSION[$rateKey]['count']++;
        } else {
            $_SESSION[$rateKey] = ['count' => 1, 'start_time' => $now];
        }
    }

    // 2. Validasi CSRF Token dari Header Request
    $headers   = getallheaders();
    $csrfToken = $headers['X-CSRF-TOKEN'] ?? $headers['x-csrf-token'] ?? '';

    if (empty($csrfToken) || empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $csrfToken)) {
        http_response_code(403);
        echo json_encode([
            'success' => false,
            'error'   => 'Access Denied: Invalid atau Missing CSRF Token.'
        ]);
        exit;
    }

    // 3. Tangkap payload JSON dari request body AJAX
    $rawInput  = file_get_contents('php://input');
    $inputData = json_decode($rawInput, true) ?? [];

    $userPrompt = trim(htmlspecialchars($inputData['prompt'] ?? '', ENT_QUOTES, 'UTF-8'));

    if (empty($userPrompt)) {
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'error'   => 'Pesan prompt tidak boleh kosong.'
        ]);
        exit;
    }

    // 4. Load Konfigurasi & Inisialisasi OpenAIClient
    $config = require __DIR__ . '/../config/config.php';
    $client = new OpenAIClient(
        apiKey:    $config['openai_api_key'],
        model:     $config['openai_model'],
        maxTokens: $config['openai_max_tokens']
    );

    // 5. Susun array percakapan & pangkas (slice) histori agar tidak melebihi batas token model
    $rawMessages = $_SESSION['chat_history'] ?? [
        ['role' => 'system', 'content' => 'Kamu adalah asisten AI yang ramah, sopan, dan solutif. Jawab dalam Bahasa Indonesia.']
    ];
    $rawMessages[] = ['role' => 'user', 'content' => $userPrompt];

    // Pangkas histori ke 10 pesan terakhir + system prompt
    $messages = $client->trimMessages($rawMessages, 10);

    // 6. Eksekusi request ke OpenAI API
    $response = $client->chat($messages);
    $answer   = $client->extractContent($response);
    $tokens   = $response['usage']['total_tokens'] ?? 0;

    // 7. Kembalikan respons JSON ke frontend
    echo json_encode([
        'success' => true,
        'answer'  => $answer,
        'tokens'  => $tokens
    ]);

} catch (\Throwable $e) {
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'error'   => $e->getMessage()
    ]);
}

5. Integrasi Frontend Sederhana dengan JavaScript Fetch API (AJAX)

Kini buat antarmuka pengguna interaktif (index.php) yang menyertakan CSRF Token pada meta tag dan mengirimkan header X-CSRF-TOKEN via JavaScript Fetch API:

<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
    <title>Aplikasi AI Chat PHP & Fetch API</title>
    <style>
        body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #f8fafc; margin: 0; padding: 20px; }
        .chat-container { max-width: 650px; margin: 20px auto; background: #1e293b; border-radius: 12px; padding: 20px; box-shadow: 0 10px 25px rgba(0,0,0,0.5); }
        .chat-box { height: 380px; overflow-y: auto; border: 1px solid #334155; padding: 15px; border-radius: 8px; margin-bottom: 15px; display: flex; flex-direction: column; gap: 12px; }
        .msg { padding: 12px 16px; border-radius: 10px; max-width: 80%; line-height: 1.5; font-size: 14px; white-space: pre-wrap; }
        .user-msg { background: #0284c7; color: #ffffff; align-self: flex-end; border-bottom-right-radius: 2px; }
        .ai-msg { background: #334155; color: #f1f5f9; align-self: flex-start; border-bottom-left-radius: 2px; }
        .error-msg { background: #7f1d1d; color: #fecaca; align-self: center; text-align: center; border: 1px solid #991b1b; }
        .input-group { display: flex; gap: 10px; }
        textarea { flex: 1; padding: 12px; border-radius: 8px; border: 1px solid #475569; background: #0f172a; color: #fff; font-size: 14px; resize: none; outline: none; }
        textarea:focus { border-color: #0284c7; }
        button { padding: 0 24px; background: #0284c7; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 14px; transition: background 0.2s; }
        button:hover { background: #0369a1; }
        button:disabled { background: #475569; cursor: not-allowed; }
    </style>
</head>
<body>

<div class="chat-container">
    <h2 style="margin-top: 0; color: #38bdf8;">🤖 AI Chat Sederhana PHP</h2>
    
    <div id="chatBox" class="chat-box">
        <div class="msg ai-msg">Halo! Saya asisten AI PHP Anda. Silakan ketik pertanyaan di bawah.</div>
    </div>

    <div class="input-group">
        <textarea id="promptInput" rows="2" placeholder="Ketik pertanyaan Anda..."></textarea>
        <button id="sendBtn" onclick="sendMessage()">Kirim</button>
    </div>
</div>

<script>
async function sendMessage() {
    const input     = document.getElementById('promptInput');
    const sendBtn   = document.getElementById('sendBtn');
    const chatBox   = document.getElementById('chatBox');
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
    const prompt    = input.value.trim();

    if (!prompt) return;

    // 1. Tampilkan pesan User di layar
    appendMessage(prompt, 'user-msg');
    input.value = '';

    // 2. Set status loading
    sendBtn.disabled = true;
    sendBtn.innerText = 'Memproses...';

    try {
        // 3. Kirim request AJAX dengan Header CSRF Token
        const response = await fetch('api/chat.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': csrfToken
            },
            body: JSON.stringify({ prompt: prompt })
        });

        const data = await response.json();

        if (data.success) {
            appendMessage(data.answer, 'ai-msg');
        } else {
            appendMessage('⚠️ Error: ' + data.error, 'error-msg');
        }
    } catch (err) {
        appendMessage('⚠️ Gagal terhubung ke server backend PHP.', 'error-msg');
    } finally {
        // 4. Kembalikan state tombol
        sendBtn.disabled = false;
        sendBtn.innerText = 'Kirim';
        chatBox.scrollTop = chatBox.scrollHeight;
    }
}

function appendMessage(text, className) {
    const chatBox = document.getElementById('chatBox');
    const msgDiv  = document.createElement('div');
    msgDiv.className = 'msg ' + className;
    msgDiv.innerText = text;
    chatBox.appendChild(msgDiv);
    chatBox.scrollTop = chatBox.scrollHeight;
}
</script>

</body>
</html>

6. Implementasi Streaming Output Kata-demi-Kata yang Aman (PHP SSE & cURL WriteFunction)

Untuk memberikan pengalaman percakapan yang cepat dan responsif layaknya ChatGPT, Anda dapat mengaktifkan opsi streaming pada OpenAI API. Teks balasan AI akan terdorong langsung ke browser karakter demi karakter secara *real-time* tanpa harus menunggu seluruh respon selesai di-generate.

Keamanan Endpoint Streaming (Proteksi Billing API Key)

Jangan pernah membuka endpoint Event Stream SSE melalui HTTP GET publik tanpa otentikasi. Gunakan metode HTTP POST serta validasi sesi dan CSRF token (header X-CSRF-TOKEN) sebelum menyajikan stream data agar endpoint tidak dieksploitasi oleh pihak luar yang mencoba menyedot saldo OpenAI API Anda.

Berikut adalah implementasi endpoint backend streaming api/stream.php yang aman dengan metode HTTP POST, validasi token, dan pembersihan buffer PHP (ob_flush() dan flush()):

<?php
// api/stream.php
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // Matikan output buffering Nginx/Apache

require_once __DIR__ . '/../vendor/autoload.php';

session_start();

// 1. Validasi Sesi & CSRF Token untuk mencegah eksploitasi billing API Key
$headers   = getallheaders();
$csrfToken = $headers['X-CSRF-TOKEN'] ?? $headers['x-csrf-token'] ?? '';

if (empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $csrfToken)) {
    http_response_code(403);
    echo "data: " . json_encode(['error' => 'Access Denied: Invalid atau Missing CSRF Token.']) . "\n\n";
    exit;
}

// 2. Tangkap payload JSON dari HTTP POST body
$rawInput  = file_get_contents('php://input');
$inputData = json_decode($rawInput, true) ?? [];
$prompt    = trim(htmlspecialchars($inputData['prompt'] ?? '', ENT_QUOTES, 'UTF-8'));

if (empty($prompt)) {
    echo "data: " . json_encode(['error' => 'Prompt tidak boleh kosong']) . "\n\n";
    exit;
}

// 3. Matikan output buffer bawaan PHP agar data langsung terdorong ke browser
if (ob_get_level()) {
    ob_end_clean();
}

$config = require __DIR__ . '/../config/config.php';

$payload = [
    'model'    => $config['openai_model'],
    'messages' => [
        ['role' => 'system', 'content' => 'Kamu adalah asisten AI PHP yang responsif.'],
        ['role' => 'user',   'content' => $prompt]
    ],
    'stream'   => true // Aktifkan mode streaming kata-demi-kata
];

$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $config['openai_api_key'],
    ],
    // Callback WriteFunction untuk menangkap & mendorong chunk data SSE secara langsung
    CURLOPT_WRITEFUNCTION  => function ($ch, $data) {
        echo $data;
        if (ob_get_level() > 0) {
            ob_flush();
        }
        flush(); // Paksa PHP mendorong buffer langsung ke client
        return strlen($data);
    }
]);

curl_exec($ch);
curl_close($ch);

Pada sisi frontend (JavaScript), kirim request POST asinkron beserta header X-CSRF-TOKEN dan baca stream balasan menggunakan ReadableStream reader:

async function sendStreamMessage(prompt) {
    const chatBox   = document.getElementById('chatBox');
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
    const aiMsgDiv  = document.createElement('div');
    aiMsgDiv.className = 'msg ai-msg';
    chatBox.appendChild(aiMsgDiv);

    // Request HTTP POST asinkron ke api/stream.php dengan CSRF Token Header
    const response = await fetch('api/stream.php', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': csrfToken
        },
        body: JSON.stringify({ prompt: prompt })
    });

    const reader   = response.body.getReader();
    const decoder  = new TextDecoder('utf-8');

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value, { stream: true });
        const lines = chunk.split('\n');

        for (const line of lines) {
            if (line.startsWith('data: ') && line !== 'data: [DONE]') {
                try {
                    const json = JSON.parse(line.replace('data: ', ''));
                    const content = json.choices[0]?.delta?.content || '';
                    aiMsgDiv.innerText += content; // Efek ketik kata-demi-kata
                    chatBox.scrollTop = chatBox.scrollHeight;
                } catch (e) {}
            }
        }
    }
}

Keuntungan Fitur Streaming Response

Dengan kombinasi CURLOPT_WRITEFUNCTION, ob_flush(), dan flush(), aplikasi AI Chat PHP Anda tidak lagi terasa lambat atau menggantung saat memproses prompt panjang, memberikan *User Experience (UX)* kelas enterprise layaknya platform AI modern.

Perbandingan: SSE (Streaming) vs REST AJAX Biasa

Berikut adalah perbandingan teknis antara metode Server-Sent Events (SSE) dan REST AJAX Request Biasa dalam menyajikan balasan AI pada aplikasi PHP:

Kriteria REST AJAX Biasa (POST JSON) Server-Sent Events (SSE Streaming)
Mekanisme Transmisi Menunggu seluruh respons selesai (blocking). Stream chunk kata-demi-kata secara real-time.
Latensi Respon Awal Tinggi (3-10 detik tergantung panjang teks). Sangat Rendah (< 500 ms untuk kata pertama).
User Experience (UX) Spinner loading statis. Interaktif (efek ketik mirip ChatGPT).
Efisiensi Bandwidth Satu kali HTTP connection tunggal. Persistent HTTP connection (Keep-Alive).
Metode Keamanan Token Standard Header (X-CSRF-TOKEN). HTTP POST / Session Cookie Token.

Optimasi Server: Kompresi Brotli & Gzip (< 50 KB Transfer Payload)

Untuk menekan transfer payload di bawah 50 KB pada server Nginx/Apache tanpa mengorbankan kelengkapan isi tutorial, aktifkan kompresi Brotli (brotli_static on; brotli_comp_level 6;) atau Gzip (gzip_comp_level 5;). Kompresi Brotli secara otomatis memadatkan respon HTML & JS hingga 82%, menghasilkan waktu muat halaman ultra-cepat (< 300ms).

7. Langkah Selanjutnya: Function Calling OpenAI & Vector Database (RAG) di PHP

Setelah berhasil membangun aplikasi AI Chat dasar, berikut adalah dua konsep tingkat lanjut untuk mengembangkan AI Agent PHP ke skala enterprise:

1. OpenAI Function Calling

Memungkinkan AI Agent PHP mengeksekusi fungsi backend kustom secara otomatis, seperti melakukan query database MySQL, membaca stok produk, atau memanggil API pihak ketiga saat pengguna mengajukan pertanyaan spesifik.

$tools = [['type' => 'function', 'function' => ['name' => 'get_user_orders']]];

2. Vector Database & RAG PHP

Mengintegrasikan pencarian semantik (Vector Embedding) menggunakan database seperti PostgreSQL pgvector, Qdrant, atau Pinecone. Teknik RAG (*Retrieval-Augmented Generation*) memungkinkan AI menjawab pertanyaan dari dokumen PDF/basis pengetahuan internal perusahaan Anda.

$embedding = $client->embeddings(['input' => $queryText]);

FAQ: Pertanyaan Sering Diajukan seputar OpenAI API & PHP

Berikut adalah jawaban atas pertanyaan populer seputar pengintegrasian OpenAI Chat API, keamanan file .env, dan penanganan error pada skrip PHP Anda (selaras dengan markup FAQPage Schema):

Mengapa penting menyimpan OpenAI API Key di file .env saat membuat AI Chat PHP?
Menyimpan API Key di file .env menggunakan package vlucas/phpdotenv mencegah key rahasia ter-commit secara tidak sengaja ke repositori publik seperti GitHub. Kebocoran API Key dapat menyebabkan penyalahgunaan kuota dan pembengkakan tagihan billing Anda.
Bagaimana cara menangani HTTP Error 401 (Unauthorized) pada OpenAI API di PHP?
Error 401 terjadi jika API Key tidak valid atau belum dikonfigurasi. Tangani error ini dengan memeriksa HTTP Response Code dari cURL, lalu berikan pesan error yang informatif kepada pengguna tanpa membocorkan key rahasia Anda.
Bagaimana cara mengatasi HTTP Error 429 (Too Many Requests / Quota Exceeded) di PHP?
Error 429 menunjukkan rate limit terlampaui atau saldo billing habis. Solusinya adalah menerapkan strategi retry otomatis dengan Exponential Backoff Retry di skrip PHP serta memastikan saldo billing aktif di dashboard OpenAI.
Mengapa sebaiknya menggunakan Fetch API (AJAX) dibandingkan Form Submit biasa untuk AI Chat?
Penggunaan JavaScript Fetch API memungkinkan pengiriman dan penerimaan pesan secara asinkron tanpa reload halaman (SPA feel), memberikan indikator loading (spinner) secara langsung, dan memudahkan integrasi fitur streaming response di masa mendatang.
Bagaimana cara membaca respons JSON dari OpenAI Chat Completions API di PHP?
Gunakan fungsi json_decode($response, true) untuk mengubah string JSON response cURL menjadi array asosiatif PHP. Isi balasan teks AI dapat diakses pada index $data['choices'][0]['message']['content'].

Poin Kunci Bab 5

Dengan mengamankan API Key via .env, menangani skenario error API secara tangguh di OpenAIClient, menerapkan proteksi CSRF & IP Rate Limiting di api/chat.php, serta mendukung Streaming Output Kata-demi-Kata (SSE) yang Aman, Anda telah berhasil membangun fondasi aplikasi AI Chat modern yang aman, responsif, dan siap produksi.

Rusmawan Abdullah Sani

Penulis & Instruktur: Rusmawan Abdullah Sani

DevOps Engineer & Sysadmin dengan spesialisasi arsitektur PHP 8.3+, CI/CD Pipeline, & Integrasi AI Agent Enterprise.

Profil LinkedIn