AI di form artinya AI bekerja di dalam form yang sudah ada, bukan di jendela chat terpisah: dia mengusulkan isi field, menulis draf teks, dan memberi peringatan, lalu manusia yang memutuskan. Bayangin pegawai magang di Toko Buku yang mengisi formulir buku baru pakai pensil. Cepat, tapi kadang salah baca. Bu Admin memeriksa, menghapus yang salah, lalu menandatangani pakai pulpen. AI itu pensilnya; tombol Simpan tetap pulpen Bu Admin.
🧪 Tebakan cepat: server sudah memakai Structured Outputs dengan
strict: true, jadi JSON dari model pasti cocok dengan schema. Masih perlu memeriksa isinya di server, atau sudah aman? Tebak dulu, jawabannya ada di bagian "Validasi di server".
Empat cara AI membantu form
| Cara | Contoh di form "Tambah buku" |
|---|---|
| Auto-complete cerdas | Bu Admin mengetik "Ronggeng Dukuh Paruk", field penulis mengusulkan "Ahmad Tohari" |
| Sugesti konten | Tombol "Tulis sinopsis" membuat draf sinopsis di textarea |
| Ekstraksi | Email supplier ditempel, AI mengisi judul, penulis, harga, stok |
| Validasi semantik | AI memperingatkan kalau sinopsisnya menyebut Belitung padahal bukunya tentang Dukuh Paruk |
Jadi AI membuat mengisi form lebih enak lewat auto-complete cerdas, validasi semantik (mendeteksi isi yang gak masuk akal), dan sugesti konten. Yang bukan caranya: mengisi semua field lalu menyimpan sendiri tanpa input manusia, atau mengganti form dengan chat. Auto-complete dan validasi semantik dipanggil dengan debounce: tunggu Bu Admin berhenti mengetik sekitar setengah detik, baru kirim request, supaya gak ada request di setiap huruf.
Ekstrak field dari teks bebas
Ronggeng Dukuh Paruk datang dengan email supplier:
Halo Bu, kiriman minggu ini: Ronggeng Dukuh Paruk karya Ahmad Tohari, 3 eksemplar.
Harga jual Rp95.000. Ceritanya tentang Srintil, penari ronggeng di dusun kecil,
sampai geger politik 1965. Kalau ada yang kurang, hubungi saya di 0812-0000-0000
atau [email protected]. Salam, Darto
Server meminta model mengubahnya jadi JSON dengan Structured Outputs (text.format dari pelajaran Prompt Engineering):
const SCHEMA = {
type: "object",
properties: {
judul: { type: "string" },
penulis: { type: "string" },
harga: { type: ["integer", "null"] }, // rupiah, null kalau gak disebut
stok: { type: ["integer", "null"] },
ringkasan: { type: "string" },
},
required: ["judul", "penulis", "harga", "stok", "ringkasan"],
additionalProperties: false,
};
const r = await client.responses.create({
model: "gpt-6-luna",
reasoning: { effort: "none" },
store: false, // jangan simpan respons ini di OpenAI
instructions: "Ambil data buku dari <email>. Kalau sebuah nilai gak disebut, isi null. Jangan menebak.",
input: `<email>${bersihkan(hapusDataPribadi(email))}</email>`,
text: { format: { type: "json_schema", name: "buku_baru", strict: true, schema: SCHEMA } },
});
const saran = JSON.parse(r.output_text);
["integer", "null"]membolehkan model menjawab null, jadi dia gak dipaksa mengarang harga yang gak disebut.- Instruksi "jangan menebak" penting: panduan Structured Outputs OpenAI menulis model akan selalu berusaha mengikuti schema, dan itu bisa memancing halusinasi kalau teksnya gak cocok dengan schema.
- Contoh jawaban (di Lab ini stand-in yang ditulis tangan, bukan keluaran model):
{"judul":"Ronggeng Dukuh Paruk","penulis":"Ahmad Tohari","harga":95000,"stok":3,"ringkasan":"Srintil, penari ronggeng di dusun kecil, sampai geger politik 1965."}
Validasi di server
Schema menjamin bentuk: harga pasti integer atau null. Tapi panduan yang sama menulis Structured Outputs masih bisa berisi kesalahan. Kalau model membaca "Rp95.000" sebagai 95, schema tetap senang, karena 95 juga integer. Jadi ya, isinya tetap diperiksa di server. Itu jawaban tebakannya.
function validasi(d) {
const masalah = [];
if (!d.judul.trim() || d.judul.length > 200) masalah.push("judul kosong atau lebih dari 200 karakter");
if (d.harga !== null && (d.harga < 1000 || d.harga > 10000000)) masalah.push(`harga ${d.harga} di luar Rp1.000 sampai Rp10.000.000`);
if (d.stok !== null && (d.stok < 0 || d.stok > 10000)) masalah.push(`stok ${d.stok} gak masuk akal`);
return masalah;
}
| Jawaban model | validasi |
Yang terjadi |
|---|---|---|
harga: 95000, stok: 3 |
[] |
field terisi sebagai saran |
harga: 95, stok: 3 |
["harga 95 di luar Rp1.000 sampai Rp10.000.000"] |
field harga ditandai merah, Bu Admin membetulkan |
Validasi yang sama dijalankan lagi saat POST /books, karena Bu Admin juga bisa salah ketik. Request yang gagal validasi dijawab 422 dengan daftar masalahnya.
Manusia tetap memegang kendali
AI mengusulkan, gak pernah menyimpan. Endpoint ekstraksi cuma mengembalikan { saran, masalah }; gak ada INSERT di sana. Di form:
- Field yang diisi AI ditandai "saran AI" sampai Bu Admin menyentuh atau menerimanya.
- Tombol Simpan cuma jalan kalau Bu Admin yang menekannya. Gak ada auto-submit setelah ekstraksi selesai.
- Database mencatat siapa yang menyimpan (
created_by), bukan "AI".
Dengan begitu, salah baca model berhenti di layar Bu Admin, bukan di katalog pelanggan.
Tulis sinopsis: streaming ke textarea dan tombol Batal
Menulis sinopsis butuh beberapa detik. Supaya Bu Admin gak menatap layar kosong, teksnya di-stream ke textarea sepotong demi sepotong (pelajaran Streaming), dengan tombol Batal:
let kontrol = null;
async function tulisSinopsis(textarea, buku) {
const sebelumnya = textarea.value; // untuk dikembalikan kalau Batal
kontrol = new AbortController();
textarea.readOnly = true;
textarea.value = "";
try {
const res = await fetch("/api/admin/sinopsis", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ judul: buku.judul, penulis: buku.penulis, ringkasan: buku.ringkasan }),
signal: kontrol.signal,
});
await bacaSSE(res.body, (ev) => { textarea.value += ev.teks; }); // server mengirim data: {"teks":"..."}
} catch (err) {
if (err.name !== "AbortError") throw err;
textarea.value = sebelumnya; // Batal: teks Bu Admin kembali utuh
} finally {
textarea.readOnly = false; // setelah selesai, Bu Admin bebas mengedit
}
}
tombolBatal.onclick = () => kontrol?.abort();
readOnlyselama streaming mencegah ketikan Bu Admin tercampur dengan teks yang datang.- Batal mengembalikan teks sebelumnya, bukan meninggalkan setengah kalimat.
- Yang dikirim cuma judul, penulis, dan ringkasan; bukan seluruh email.
Privasi: apa yang dikirim ke provider
Semua yang ada di input keluar dari server Toko Buku ke OpenAI. Email supplier tadi berisi nomor HP dan alamat email Pak Darto, yang gak dibutuhkan untuk mengisi data buku. Hapus sebelum dikirim:
function hapusDataPribadi(teks) {
return teks
.replace(/(\+62|0)8\d{1,2}[-\s]?\d{3,4}[-\s]?\d{3,4}/g, "[nomor disembunyikan]")
.replace(/[\w.+-]+@[\w-]+(\.[\w-]+)+/g, "[email disembunyikan]");
}
Menurut halaman "Your data" OpenAI (dicek Oktober 2026): data API gak dipakai melatih model kecuali kamu memilih ikut; log pemantauan penyalahgunaan bisa disimpan sampai 30 hari; dan Responses API menyimpan respons minimal 30 hari kecuali store: false. Kirim seperlunya, dan tulis di kebijakan privasi toko bahwa teks tertentu diproses provider AI.
⚠️ Jebakan yang sering ketemu
- Auto-submit. Ekstraksi yang langsung menyimpan membuat harga 95 masuk katalog. AI cuma mengisi saran; Simpan ditekan manusia.
- Percaya schema sama dengan benar.
strict: truemenjamin tipe, bukan nilai. Validasi rentang dan panjang di server, di endpoint ekstraksi dan diPOST /books. - Mengirim semua ke provider. Nomor HP, email, dan data pelanggan gak dibutuhkan untuk menulis sinopsis. Hapus dulu, dan pakai
store: false. - Streaming tanpa Batal. Bu Admin terjebak menunggu, atau textarea-nya tertimpa. Sediakan Batal yang mengembalikan teks lama.
TL;DR: AI di form mengusulkan, manusia menyimpan. Empat caranya: auto-complete cerdas (dengan debounce), sugesti konten (tombol "Tulis sinopsis"), ekstraksi field dari teks bebas, dan validasi semantik. Ekstraksi memakai Structured Outputs (text.format, json_schema, strict: true, field opsional ["integer", "null"], instruksi "jangan menebak"), tapi schema cuma menjamin bentuk: harga 95 tetap lolos schema, jadi server memvalidasi rentang dan panjang lalu menjawab 422 kalau ada masalah. Endpoint ekstraksi gak menyimpan apa-apa; Simpan ditekan Bu Admin. Draf panjang di-stream ke textarea yang readOnly, dengan Batal (AbortController) yang mengembalikan teks lama. Sebelum mengirim, hapus data pribadi (nomor HP, email) dan pakai store: false; data API OpenAI gak dipakai melatih model secara default, tapi log bisa disimpan sampai 30 hari.