Agen menulis «selesai», tetapi fitur tidak berfungsi: mengapa itu terjadi dan cara memeriksa pekerjaan agen AI
Agen menulis «selesai» karena ia menilai pekerjaannya sendiri dan hanya melihat apa yang sudah diperiksanya, biasanya tes unit yang tidak melihat sambungan antarmodul, migrasi, konfigurasi, dan skenario end-to-end. Solusinya ada pada struktur, bukan pada prompt: Definition of Done di AGENTS.md, E2E wajib, skrip verifikasi yang sendirian berhak memberi status passing, dan penilai independen dengan konteks bersih.
Mengapa ini terjadi
Situasi ini sudah dikenal semua orang yang bekerja dengan agen: agen menulis «selesai», tetapi fitur tidak berfungsi. Ia tidak berbohong dengan sengaja. Ia melaporkan apa yang sempat diperiksanya dan yakin itu sudah cukup. Jarak antara pernyataan dan kenyataan adalah sifat dari skema «pelaksana menilai pekerjaannya sendiri», bukan cacat satu model tertentu.
Ada tiga penyebab. Pertama, model cenderung terlalu percaya diri: tingkat keyakinan jawaban lebih tinggi daripada ketepatannya. Kedua, agen menilai pekerjaannya dalam konteks yang sama tempat ia mengerjakannya: ia sudah meyakinkan dirinya bahwa solusinya benar, dan permintaan «periksa lebih objektif» tidak mengubah konteks itu. Ketiga, yang paling praktis, agen hanya punya akses ke pemeriksaan parsial: tes unit cepat hijau, sedangkan skenario end-to-end tidak pernah dijalankan oleh siapa pun.
Kronik sebuah «selesai» yang keliru
Agen diberi tugas reset kata sandi. Ia mengubah skema basis data, menambahkan endpoint, menyambungkan templat email, menjalankan tes unit — semuanya hijau. Di chat muncul kalimat yakin: «Selesai».
Kenyataannya terjadi hal berikut:
- migrasi gagal di tengah dan meninggalkan skema dalam keadaan tidak konsisten;
- templat email ada, tetapi layanan surel (SMTP) belum dikonfigurasi di lingkungan — email tidak terkirim;
- skenario «pengguna klik tautan → memasukkan kata sandi baru → masuk» tidak pernah dijalankan.
Tes unit tidak mungkin menangkap satu pun masalah itu: basis data di-mock, pengiriman email di-mock, dan tidak ada eksekusi menyeluruh sama sekali.
Mengapa tes hijau tidak membuktikan bahwa fitur berfungsi
Tes unit memeriksa satu modul secara terisolasi: dependensi diganti mock, dunia luar dimatikan. Ini memberi kecepatan dan ketepatan, tetapi juga kebutaan sistematis. Kebutaan itu bukan bug pada tesnya, melainkan bagian dari desainnya. Unit yang hijau hanya membuktikan bahwa setiap bagian berfungsi sendiri-sendiri, bukan bahwa bagian-bagian itu bekerja bersama.
Ada empat kelas cacat yang secara prinsip tidak bisa dilihat tes unit:
| Kelas cacat | Contoh | Mengapa unit tidak menangkapnya |
|---|---|---|
| Ketidakcocokan antarmuka | Satu komponen memberi jalur relatif, komponen lain mengharapkan jalur absolut | Setiap komponen diuji dengan mock-nya sendiri yang «menerima» format apa pun |
| Perambatan status | Skema basis data berubah, tetapi cache ORM masih menyimpan versi lama | Di unit tidak ada basis data nyata maupun cache |
| Siklus hidup sumber daya | Deskriptor file dan koneksi tidak dilepas, memori bocor pada file besar | Tes berjalan dengan data kecil dan selesai sebelum sumber daya habis |
| Lingkungan dan konfigurasi | Variabel lingkungan tidak ada, SMTP belum diatur, migrasi belum diterapkan | Tes tidak menjalankan aplikasi dengan konfigurasi sungguhan |
Tingkat pemeriksaan saling bersarang: semua yang ditangkap tes unit juga ditangkap tes integrasi, dan semua yang ditangkap tes integrasi juga ditangkap run end-to-end (E2E). Setiap tingkat berikutnya melihat lebih banyak. Karena itu, hak untuk menyatakan «selesai» hanya sebaiknya dimiliki oleh tingkat paling atas.
Mengukur masalahnya: verification gap
Sebelum memperbaiki, hitung dulu. Ambil 10 tugas terakhir yang ditutup agen. Untuk masing-masing, jawab dua pertanyaan: apakah agen menyatakan «selesai», dan apakah pemeriksaan independen mengonfirmasinya (Anda sendiri menjalankan skenarionya, atau tes E2E hijau). Proporsi ketidaksesuaian adalah verification gap Anda.
Angka ini berguna karena dua alasan. Ia menunjukkan skala masalah tanpa perdebatan «ya, kelihatannya baik-baik saja». Dan ia memberi garis dasar: setelah setiap perubahan proses, hitung ulang di kumpulan tugas yang serupa dan lihat apakah ada perbaikan.
Cara memeriksa dengan benar: tiga lapis validasi
Analogi yang berguna adalah bengkel mobil. Mobil melewati tiga pos, dan tidak ada pos yang boleh dilewati.
- Diagnostik (detik): sintaks, tipe, linter. Menangkap salah ketik dan kesalahan kasar.
- Bangku uji (menit): tes unit dan integrasi, aplikasi berhasil dijalankan. Mesinnya hidup, tetapi mobil masih diam di tempat.
- Uji jalan: skenario end-to-end seperti yang dijalani pengguna — lewat API, browser, atau CLI, dengan basis data dan konfigurasi sungguhan.
Aturan urutannya: jangan pergi ke uji jalan selama diagnostik masih merah, dan jangan merilis mobil tanpa melewati uji jalan. Tes unit yang hijau adalah pos kedua yang sudah dilewati, bukan pos ketiga.
Semuanya bisa dikumpulkan dalam satu pintu masuk:
# Makefile
check:
ruff check .
mypy app
pytest tests/unit tests/integration
e2e:
docker compose up -d db mailhog
alembic upgrade head
pytest tests/e2e
verify: check e2e
Di sini target E2E menyalakan basis data sungguhan dan server surel uji, menerapkan migrasi, dan menjalankan skenario dari awal sampai akhir. Migrasi yang gagal atau surel yang belum dikonfigurasi akan langsung terlihat, bukan di sisi pelanggan.
Definition of Done di AGENTS.md
Agen tidak boleh memutuskan sendiri arti «selesai». Kriterianya ditulis di file instruksi dan dirumuskan secara dapat diverifikasi:
## Definition of Done
Fitur dianggap selesai hanya jika SEMUA poin berikut terpenuhi:
1. make check selesai dengan kode 0
2. make e2e selesai dengan kode 0, skenario fitur ada di tests/e2e
3. Migrasi diterapkan pada basis data kosong dan dapat di-rollback
4. Variabel lingkungan baru dijelaskan di .env.example
5. PROGRESS.md mencatat apa yang dikerjakan dan cara memverifikasinya
BUKAN tanda bahwa fitur selesai:
- «kodenya sudah ditulis dan kelihatannya benar»
- «tes unit lulus»
- «di komputer saya sudah berjalan»
Daftar eksplisit tentang apa yang tidak dianggap selesai bekerja lebih baik daripada yang dikira: agen berhenti tidak lagi di hasil hijau pertama.
Siapa yang berhak memberi status «passing»
Langkah berikutnya adalah mengambil status selesai sepenuhnya dari tangan agen. Fitur disimpan dalam daftar (misalnya feature_list.json) dengan kolom «perilaku», «perintah verifikasi», dan «status». Status menjadi passing hanya oleh skrip: ia menjalankan make check umum, lalu perintah verifikasi khusus fitur itu, dan jika berhasil mencatat status bersama hash commit sebagai bukti. Jika gagal, fitur tetap aktif — tidak ada kata «hampir berhasil».
{
"id": "F04",
"behavior": "Pengguna menerima email dengan tautan dan mengganti kata sandi",
"verification": "pytest tests/e2e/test_password_reset.py",
"state": "active"
}
Ada jebakan di sini: skrip hanya memeriksa apa yang tertulis di kolom verification. Jika pemeriksaan arsitektur termasuk dalam Definition of Done tetapi tidak ada di perintah fitur, celahnya akan tetap tak terlihat. Karena itu make check umum harus mencakup semua pemeriksaan wajib, bukan hanya tes.
Penilai independen
Laporan diri pelaksana tidak pernah menjadi sumber kebenaran yang independen. Solusinya bersifat struktural: pisahkan peran.
- perencana memecah persyaratan menjadi fitur dengan kriteria penerimaan dan tidak menulis kode;
- generator mengimplementasikan satu fitur dan menjalankan tes, tetapi tidak memutuskan apakah fitur itu selesai;
- penilai mengeluarkan vonis berdasarkan daftar kriteria.
Penilai tidak boleh melihat penalaran generator, karena ia akan mewarisi keyakinannya. Ia hanya menerima persyaratan, diff, dan hasil menjalankan pemeriksaan. Di Claude Code ini mudah dilakukan dengan subagen, karena ia punya konteks bersih sendiri. Lebih baik lagi jika penilainya adalah model lain, karena model lain punya titik buta yang berbeda. Penilaian bergaya «kelihatannya baik» dilarang: setiap temuan harus merujuk ke file, baris, atau log.
Pesan kesalahan yang bisa diperbaiki agen sendiri
Pemeriksaan hanya berguna jika keluarannya menuntun ke perbaikan. «Test failed» atau AssertionError polos tidak berarti apa-apa bagi agen. Pesan yang baik menyebutkan apa yang rusak, mengapa aturan itu ada, dan cara memperbaikinya:
ERROR arch-check: app/services/refund.py mengimpor app/db/session.py
WHY: service tidak mengakses basis data secara langsung, hanya lewat repository
FIX: gunakan RefundRepository dari app/repositories/refund.py
Format seperti ini mengubah pemeriksaan dari larangan menjadi petunjuk: agen memperbaiki pelanggaran tanpa campur tangan Anda.
Pemeriksaan manual: di mana masih diperlukan
Otomatisasi tidak meniadakan manusia, tetapi mempersempit perannya. Pemeriksaan manual dibutuhkan di tempat kriterianya sulit diformalkan: tampilan antarmuka, teks, kenyamanan skenario. Lakukan dengan daftar periksa dari kriteria penerimaan, bukan dengan sekadar «mengklik-klik». Jika Anda menulis komentar yang sama kepada agen untuk kedua kalinya, itu tanda untuk mengubahnya menjadi pemeriksaan otomatis: tes, aturan linter, atau guard grep di make check.
Bagaimana E2E mengubah perilaku agen bahkan sebelum dijalankan
Nilai tes end-to-end bukan hanya pada bug yang ditangkap. Jika agen tahu sejak awal bahwa pekerjaannya akan diperiksa skenario E2E, ia menulis kode secara berbeda: memikirkan kontrak dengan komponen tetangga, bukan hanya fungsinya sendiri, menangani jalur kesalahan, dan tidak melanggar batas arsitektur karena pelanggarannya akan muncul saat dijalankan. Pemeriksaan bekerja sebagai batasan di pintu masuk, bukan hanya filter di pintu keluar.
Ada syaratnya: E2E bermakna jika sistemnya punya batas yang jelas. Pada arsitektur spageti, tes end-to-end hanya membuktikan bahwa spageti itu berjalan dan tidak menunjukkan di mana rancangan dilanggar. Karena itu lapisan dan aturan dependensi ditetapkan dulu, baru skenario end-to-end dibangun. Agen dengan senang hati menyalin pola yang sudah ada di repositori, termasuk pola yang buruk, sehingga batasan sebaiknya diberlakukan sejak hari pertama.
Kesalahan umum
- Meminta agen «periksa lebih teliti». Konteksnya tetap sama, dan penilaiannya tetap melebih-lebihkan.
- Menganggap cakupan 100% sebagai jaminan. Cakupan unit tidak mencakup sambungan antarmodul.
- Melakukan refaktor sebelum fitur terkonfirmasi. Urutannya ketat: kebenaran → performa → gaya.
- Mock di E2E pada bagian yang paling sering rusak. Basis data, migrasi, dan konfigurasi harus dijalankan sungguhan di tes end-to-end; layanan pembayaran pihak ketiga yang berbayar boleh diganti dengan server uji.
Kesimpulan
Ketika agen menulis «selesai», tetapi fitur tidak berfungsi, masalahnya hampir selalu ada pada proses pemeriksaan, bukan pada model. Hitung verification gap, tetapkan Definition of Done di AGENTS.md, jadikan E2E wajib, serahkan status selesai kepada skrip, dan tambahkan penilai independen. Skema ini dibahas secara mendalam dan praktis di kursus harness engineering.
Pertanyaan umum
Bisakah agen diminta menjalankan tes E2E sendiri?
Bisa, jika perintahnya tertulis di AGENTS.md sebagai bagian wajib dari Definition of Done dan alat yang diperlukan tersedia baginya. Lebih andal lagi jika perintah itu juga dijalankan oleh hook atau di CI, sehingga tidak bergantung pada ingatan agen.
Tes E2E lambat, apakah perlu dijalankan setiap kali ada perubahan?
Tidak. Lint, tipe, dan tes unit selalu dijalankan; E2E dijalankan saat perubahan menyentuh lebih dari satu lapisan dan sebelum fitur dinyatakan selesai.
Bagaimana cara memeriksa frontend yang dibuat agen?
Dengan tes end-to-end lewat browser (misalnya Playwright) yang mengklik antarmuka sungguhan, sedangkan tampilan visualnya diperiksa secara manual dengan daftar periksa atau tangkapan layar yang dinilai oleh penilai.
Apakah cukup dengan code review oleh manusia, tanpa penilai?
Review manusia tetap berguna, tetapi mahal dan tidak bisa mengikuti jam kerja otonom agen. Penilai dengan rubrik menyaring masalah yang jelas sebelum sampai ke manusia, dan manusia memutuskan hal-hal yang diperdebatkan.
Topik ini dibahas mendalam dengan praktik di kursus “Harness Engineering: инженерия надёжности AI-агентов”.
- Агент написал «готово», а фича не работает: verification gap и Definition of Done
- Все тесты зелёные, а фича сломана: E2E-тесты и архитектурные правила для AI-агента