Implementasi tools database MySQL untuk AI Agent PHP: searchUser, insertData, getProduct, dan searchArticle dengan prepared statements aman dan validasi input ketat.
Daftar Isi
- TL;DR — Ringkasan Tool Database MySQL AI Agent PHP
- Implementasi Class DatabaseTool untuk AI Agent PHP
- Penjelasan Parameter dan Spesifikasi Operasi DatabaseTool
- Kode Sumber Lengkap Class DatabaseTool di PHP
- Integrasi Function Calling DatabaseTool ke AI Agent Loop
- FAQ: Pertanyaan Seputar Tool Database MySQL AI Agent PHP
- 1. Bagaimana cara mencegah SQL Injection pada Tool AI Agent?
- 2. Mengapa AI Agent tidak boleh menulis query SQL mentah secara bebas?
- 3. Mengapa klausul LIMIT wajib ditambahkan pada query tool database?
- 4. Bagaimana cara menyembunyikan kolom sensitif seperti password dari model AI?
Terakhir diperbarui:
TL;DR — Ringkasan Tool Database MySQL AI Agent PHP
- Prepared Statements: Wajib menggunakan parameter binding PDO untuk mengeliminasi risiko SQL Injection saat model AI mengekstrak input dinamis.
- Whitelist Operasi: Batasi akses AI hanya pada daftar method eksplisit (seperti
searchUser,getProduct,insertData). - Klausul LIMIT Wajib: Pasang batasan jumlah baris hasil (misal:
LIMIT 10) guna mencegah memory exhaustion dan token blowout. - Sanitasi Kolom Sensitif: Jangan pernah menyertakan kolom
password,hash, atautokenke dalam output JSON yang dikirimkan ke model AI.
Implementasi Class DatabaseTool untuk AI Agent PHP
Dalam materi belajar ai-agent-php tingkat lanjut, memberikan akses database langsung ke Large Language Model (LLM) memerlukan standar arsitektur keamanan yang sangat ketat. Alih-alih mengizinkan model AI menyusun query SQL mentah secara bebas, kita membangun class DatabaseTool dengan pendekatan Whitelist Action & Parameterized Queries menggunakan PDO PHP murni:

Penjelasan Parameter dan Spesifikasi Operasi DatabaseTool
Berikut adalah tabel rincian parameter fungsi yang diekspos oleh DatabaseTool untuk memandu model AI dalam memilih operasi data yang tepat:
| Nama Operasi | Parameter Kunci | Validasi & Batasan Keamanan | Output Data |
|---|---|---|---|
| searchUser | keyword (string) | Minimal 2 karakter, LIKE binding (? , ?), LIMIT 10 | id, name, email, role (password disembunyikan) |
| getProduct | id (integer) | Casting (int), wajib id > 0, WHERE is_active = 1 | id, name, sku, price, stock, category |
| searchArticle | keyword (string), category (string) | Prepared statement dinamis, filter published, LIMIT 5 | id, title, slug, category, excerpt |
| insertData | table (string), data (object) | Strict Whitelist table (feedback, contact_messages) | success: true, id: lastInsertId() |
Database Safety Takeaway: Pemisahan operasi via whitelist mencegah injeksi DROP TABLE atau manipulasi hak akses oleh instruksi berbahaya (*Prompt Injection*).
Kode Sumber Lengkap Class DatabaseTool di PHP
Berikut adalah implementasi class DatabaseTool berbasis ToolInterface:
<?php
namespace App\Tools;
use PDO;
class DatabaseTool implements ToolInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function getDefinition(): array
{
return [
'type' => 'function',
'function' => [
'name' => 'database_query',
'description' => 'Mengakses basis data relasional untuk mencari pengguna, produk, artikel, atau menyimpan pesan masukan. Gunakan operasi: searchUser, getProduct, searchArticle, insertData.',
'parameters' => [
'type' => 'object',
'properties' => [
'operation' => [
'type' => 'string',
'enum' => ['searchUser', 'getProduct', 'searchArticle', 'insertData'],
'description' => 'Pilihan operasi basis data yang diizinkan.',
],
'params' => [
'type' => 'object',
'description' => 'Parameter objek terstruktur untuk operasi yang dipilih.',
],
],
'required' => ['operation'],
],
],
];
}
public function execute(array $args): mixed
{
$operation = $args['operation'] ?? '';
$params = $args['params'] ?? [];
return match ($operation) {
'searchUser' => $this->searchUser($params),
'getProduct' => $this->getProduct($params),
'searchArticle' => $this->searchArticle($params),
'insertData' => $this->insertData($params),
default => ['status' => 'error', 'message' => "Operasi '$operation' tidak diizinkan."],
};
}
private function searchUser(array $params): array
{
$keyword = trim($params['keyword'] ?? '');
if (empty($keyword) || strlen($keyword) < 2) {
return ['status' => 'error', 'message' => 'Kata kunci pencarian user minimal 2 karakter.'];
}
$stmt = $this->pdo->prepare(
'SELECT id, name, email, role, created_at
FROM users
WHERE name LIKE ? OR email LIKE ?
ORDER BY name ASC
LIMIT 10'
);
$like = '%' . $keyword . '%';
$stmt->execute([$like, $like]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return ['status' => 'success', 'count' => count($rows), 'data' => $rows];
}
private function getProduct(array $params): array
{
$productId = (int) ($params['id'] ?? 0);
if ($productId <= 0) {
return ['status' => 'error', 'message' => 'ID produk wajib berupa bilangan bulat positif.'];
}
$stmt = $this->pdo->prepare(
'SELECT id, name, sku, price, stock, category, description
FROM products
WHERE id = ? AND is_active = 1
LIMIT 1'
);
$stmt->execute([$productId]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
return $product
? ['status' => 'success', 'data' => $product]
: ['status' => 'error', 'message' => "Produk ID $productId tidak ditemukan."];
}
private function searchArticle(array $params): array
{
$keyword = trim($params['keyword'] ?? '');
$category = trim($params['category'] ?? '');
$sql = 'SELECT id, title, slug, category, excerpt, created_at FROM articles WHERE status = "published"';
$values = [];
if (!empty($keyword)) {
$sql .= ' AND (title LIKE ? OR content LIKE ?)';
$values[] = '%' . $keyword . '%';
$values[] = '%' . $keyword . '%';
}
if (!empty($category)) {
$sql .= ' AND category = ?';
$values[] = $category;
}
$sql .= ' ORDER BY created_at DESC LIMIT 5';
$stmt = $this->pdo->prepare($sql);
$stmt->execute($values);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return ['status' => 'success', 'count' => count($rows), 'data' => $rows];
}
private function insertData(array $params): array
{
$table = $params['table'] ?? '';
$data = $params['data'] ?? [];
$allowed = ['feedback', 'contact_messages'];
if (!in_array($table, $allowed, true)) {
return ['status' => 'error', 'message' => "Tabel '$table' tidak diizinkan untuk operasi insert."];
}
if (empty($data) || !is_array($data)) {
return ['status' => 'error', 'message' => 'Data insert tidak boleh kosong.'];
}
$columns = implode(', ', array_keys($data));
$placeholders = implode(', ', array_fill(0, count($data), '?'));
$stmt = $this->pdo->prepare("INSERT INTO $table ($columns) VALUES ($placeholders)");
$stmt->execute(array_values($data));
return ['status' => 'success', 'inserted_id' => (int) $this->pdo->lastInsertId()];
}
}| Dimensi Keamanan & Stabilitas | Direct Raw SQL Execution (Berbahaya) | Whitelist Function Calling (Standar Enterprise) |
|---|---|---|
| Ketahanan Prompt Injection | Sangat Rentan (AI bisa dimanipulasi mengeksekusi DROP / UPDATE) | Kebal 100% (Model hanya mengirim parameter, bukan sintaks query) |
| Proteksi SQL Injection | Rawan (Jika interpolasi string SQL tidak diescape sempurna) | Sempurna (100% diproteksi via PDO Prepared Statements) |
| Kebocoran Data Sensitif | Tinggi (SELECT * dapat membocorkan password_hash & token) | Nol (Kolom output difilter secara eksplisit di level method PHP) |
| Kontrol Resource & Token | Tidak Terkontrol (Query tanpa LIMIT memicu memory crash) | Terkontrol (Setiap query dibatasi LIMIT 5–10 baris teratas) |
| Audit Logging & Tracing | Sulit (Log SQL mentah tidak memiliki konteks intensi AI) | Sangat Mudah (Tiap pemanggilan fungsi tercatat di structured log) |
Security Architecture Takeaway: Whitelist Function Calling membatasi ruang serang (attack surface) database secara absolut di lingkungan produksi.
Integrasi Function Calling DatabaseTool ke AI Agent Loop
Berikut adalah contoh skrip implementasi menghubungkan koneksi database PDO ke objek Agent:
<?php
// run-agent-database.php
require_once __DIR__ . '/vendor/autoload.php';
use App\OpenAIClient;
use App\Agent\Agent;
use App\Tools\DatabaseTool;
// Inisialisasi koneksi PDO aman
$pdo = new PDO('mysql:host=127.0.0.1;dbname=infokoding;charset=utf8mb4', 'db_user', 'secret_pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$client = new OpenAIClient(getenv('OPENAI_API_KEY'));
$agent = new Agent($client, maxIterations: 5);
// Registrasikan DatabaseTool
$agent->registerTool(new DatabaseTool($pdo));
// Prompt pencarian multi-kriteria
$prompt = 'Carikan pengguna dengan nama "Rusmawan" dan tampilkan peran (role) akunnya.';
$response = $agent->run($prompt);
echo "Jawaban Agent:\n" . $response . "\n";Ketika AI Agent mengeksekusi tugas mutasi multi-langkah (misalnya: membuat pesanan baru sekaligus memotong stok barang), kegagalan di tengah jalan dapat merusak konsistensi basis data (*dirty reads / partial writes*). Gunakan blok PDO Transaction (Atomic Rollback) dan konfigurasi ATTR_TIMEOUT ketat:
<?php
namespace App\Tools;
use PDO;
class TransactionalDatabaseTool implements ToolInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* Eksekusi mutasi multi-langkah dengan jaminan ACID Transaction.
*/
public function executeOrderTransaction(int $userId, int $productId, int $qty): array
{
try {
// 1. Mulai transaksi database (Atomic)
$this->pdo->beginTransaction();
// 2. Periksa stok dengan lock baris (SELECT ... FOR UPDATE)
$stmt = $this->pdo->prepare("SELECT stock, price FROM products WHERE id = ? FOR UPDATE");
$stmt->execute([$productId]);
$product = $stmt->fetch();
if (!$product || $product['stock'] < $qty) {
throw new \RuntimeException("Stok produk tidak mencukupi atau produk tidak ditemukan.");
}
// 3. Potong stok produk
$updateStmt = $this->pdo->prepare("UPDATE products SET stock = stock - ? WHERE id = ?");
$updateStmt->execute([$qty, $productId]);
// 4. Catat riwayat pesanan
$orderStmt = $this->pdo->prepare("INSERT INTO orders (user_id, product_id, quantity, created_at) VALUES (?, ?, ?, NOW())");
$orderStmt->execute([$userId, $productId, $qty]);
$orderId = (int) $this->pdo->lastInsertId();
// 5. Commit perubahan data permanen jika seluruh tahap berhasil
$this->pdo->commit();
return [
'status' => 'success',
'message' => 'Transaksi pesanan berhasil diproses secara atomik.',
'order_id' => $orderId,
];
} catch (\Throwable $e) {
// 6. Rollback instan jika terjadi exception / deadlock / timeout
if ($this->pdo->inTransaction()) {
$this->pdo->rollBack();
}
return [
'status' => 'error',
'message' => 'Transaksi dibatalkan (Rollback): ' . $e->getMessage(),
];
}
}
}Best Practice Connection Timeout & Deadlock Guard:
- PDO::ATTR_TIMEOUT: Pasang timeout 5 detik pada konstruktor PDO agar worker PHP tidak hang saat database mengalami query lock tinggi.
- inTransaction Check: Selalu periksa
$this->pdo->inTransaction()sebelum memanggilrollBack()guna mencegah fatal error No active transaction.
Transaction Takeaway: Penerapan beginTransaction() dan rollBack() menjaga integritas data 100% konsisten meskipun model AI mengalami interupsi jaringan.
FAQ: Pertanyaan Seputar Tool Database MySQL AI Agent PHP
1. Bagaimana cara mencegah SQL Injection pada Tool AI Agent?
Ringkasan Jawaban:Gunakan selalu PDO Prepared Statements dengan parameter binding (? atau named parameter) dan jangan pernah menggabungkan string input dari AI secara langsung ke dalam query SQL.Dengan prepared statements, database driver memperlakukan input pengguna murni sebagai nilai data (bukan bagian sintaks perintah SQL yang dapat dieksekusi).
2. Mengapa AI Agent tidak boleh menulis query SQL mentah secara bebas?
Ringkasan Jawaban:Membiarkan AI menghasilkan SQL mentah membuka celah Prompt Injection berbahaya di mana penyerang dapat memanipulasi model untuk mengeksekusi DROP TABLE atau membaca data sensitif.Pendekatan *Whitelist Function* (seperti searchUser atau getProduct) memastikan model hanya dapat mengakses endpoint database yang telah Anda beri izin khusus.
3. Mengapa klausul LIMIT wajib ditambahkan pada query tool database?
Ringkasan Jawaban:Klausul LIMIT mencegah terjadinya dump ribuan baris data yang dapat menghabiskan memori RAM server (*memory exhaustion*) dan memicu lonjakan biaya token (*token blowout*).Membatasi output query antara 5 hingga 10 baris teratas menjaga efisiensi context window LLM dan mempercepat waktu inferensi.
4. Bagaimana cara menyembunyikan kolom sensitif seperti password dari model AI?
Ringkasan Jawaban:Cantumkan nama kolom secara eksplisit pada klausulSELECT id, name, emailalih-alih menggunakanSELECT *, dan bersihkan array data sebelum dikonversi ke JSON.
Ini menjamin data rahasia seperti hash kata sandi, session token, atau nomor rekening tidak pernah bocor ke log percakapan LLM pihak ketiga.
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.