Pakai Model Non-Thinking untuk Coding Kompleks: Bisa, Asal Tahu Batasnya

Hari ini saya coba jawab pertanyaan yang sering muncul di kalangan yang baru mulai pakai AI coding agent: “Kalau pakai model non-thinking, apakah tetap bisa dipakai untuk coding yang kompleks?” Singkatnya: bisa, tapi dengan syarat — dan ada jenis pekerjaan yang memang lebih aman diserahkan ke model thinking. Catatan ini saya tulis khusus dari sudut pandang coding (Hermes Agent dan OpenCode), tidak membahas hal lain di luar itu.

Apa Bedanya Model Thinking vs Non-Thinking?

Dalam konteks coding, “thinking” (atau extended thinking / reasoning) artinya model diberi ruang untuk berpikir panjang sebelum menjawab — ia menuliskan rantai penalaran internal dulu, baru menghasilkan kode atau keputusan. Ini memakai token tambahan (biasanya tidak terlihat di output akhir), sehingga lebih lambat dan lebih mahal.

Model non-thinking menjawab langsung: input diproses, output langsung keluar. Lebih cepat, lebih murah, tapi untuk masalah yang butuh penalaran bertingkat, hasilnya bisa meleset lebih cepat.

Penting: yang wajib dimiliki untuk coding adalah kemampuan tool calling yang baik (membaca file, menjalankan perintah, melihat error, mengedit). Mode thinking itu nilai tambah, bukan prasyarat.

Kapan Non-Thinking Masih Andal

Untuk jenis pekerjaan berikut, model non-thinking yang bagus biasanya sudah cukup:

  • Boilerplate dan scaffolding — kerangka proyek, file konfigurasi, stub fungsi.
  • CRUD sederhana — endpoint API, form, operasi database standar.
  • Task satu file dengan spesifikasi eksplisit — “ubah fungsi ini supaya menerima parameter X”.
  • Refactor kecil dan mekanis — rename, pindah fungsi, ubah format.
  • Perbaikan bug yang error-nya jelas dan lokal.

Ciri umumnya: tugasnya jelas, ruang lingkupnya sempit, dan ada cara verifikasi cepat (test, compile, atau output yang bisa dicek).

Di Mana Non-Thinking Sering Gagal

Yang berikut ini justru menjadi titik lemah model non-thinking:

  • Debugging bertingkat — error menimpa error, root cause tersembunyi di balik gejala.
  • Refactor lintas file — banyak dependensi silang yang harus diikuti konsistensinya.
  • Keputusan arsitektur — memilih desain dengan trade-off yang saling bertabrakan.
  • Memahami codebase besar yang belum familiar — butuh penalaran panjang untuk membangun model mental.
  • Task ambigu — spesifikasi tidak lengkap, perlu inferensi dan pertimbangan.

Pada kasus seperti ini, model non-thinking berisiko gagal diam-diam: hasilnya terlihat jalan, tapi logikanya salah di tempat yang tidak langsung kelihatan.

Bukti dari Benchmark

Di benchmark agentic coding seperti SWE-bench Verified (500 isu GitHub nyata dari repo populer seperti Django, Flask, scikit-learn yang harus diselesaikan oleh model), posisi papan atas hampir selalu diisi model reasoning/thinking. Menurut leaderboard yang dipantau benchlm.ai per Agustus 2026, model teratas (Claude Opus 5) mencapai sekitar 96% resolusi.

Pola umumnya: versi thinking dari sebuah model hampir selalu mengungguli versi non-thinking-nya pada task multi-step, dan gap-nya mengecil drastis pada task sederhana. Jadi benchmark ini menegaskan — bukan soal “bisa atau tidak”, tapi soal di titik mana non-thinking mulai jebol.

Perbandingan Ringkas

AspekNon-ThinkingThinking
BiayaMurahMahal (token reasoning)
KecepatanCepatLambat
Konsumsi tokenHematBoros
Task sederhana & jelasSangat cocokBisa, tapi boros
Debugging & refactor besarRisiko gagal diam-diamAndal

Pola Praktis: Campur, Jangan Pilih Satu

Cara paling efektif bukan memilih salah satu, tapi memakai keduanya sesuai tugas — di Hermes dan OpenCode, ganti model per sesi/per task itu normal.

Hermes Agent

Ganti model kapan saja via hermes model, atau langsung set default:

hermes model
hermes config set model.default nama-model-non-thinking

Di dalam sesi, perintah /model untuk ganti model, dan /reasoning untuk mengatur level reasoning — kalau modelnya memang mendukung mode thinking. Model non-thinking tetap jalan normal tanpa setting tambahan.

OpenCode

Pilih model via /models. Kalau model default-nya thinking (misalnya Claude lewat provider Anthropic) dan ingin mematikannya, set lewat opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "anthropic": {
      "models": {
        "claude-sonnet-4-5": {
          "options": {
            "thinking": { "type": "disabled" }
          }
        }
      }
    }
  }
}

Untuk provider OpenAI-compatible (misalnya GPT), mode reasoning diatur lewat reasoningEffort — nilai rendah berarti “hampir non-thinking”.

Kesimpulan

Model non-thinking bisa dipakai untuk coding kompleks, asal tugas dipecah kecil dan ada loop verifikasi: tulis sedikit → jalankan → lihat error → perbaiki. Untuk boilerplate, CRUD, dan task single-file, non-thinking bahkan pilihan paling efisien.

Tapi untuk debugging bertingkat, refactor lintas file, dan keputusan desain, model thinking masih pilihan yang jauh lebih aman — terutama kalau pekerjaan berjalan tanpa pengawasan. Prinsipnya: pakai model sesuai tingkat kesulitan tugas, bukan satu model untuk segalanya.

Sumber

Belajar Laravel #2: Routing & Controller Pertama — Dari URL ke View

Hari ini saya lanjut belajar Laravel ke episode kedua: routing dan controller pertama. Setelah minggu lalu berhasil membuat project dan menjalankan php artisan serve, sekarang saya penasaran — dari mana sebenarnya halaman awal itu muncul? Ternyata jawabannya ada di satu file kecil bernama routes/web.php. File inilah yang menjadi pintu masuk hampir semua request ke aplikasi web kita.

routes/web.php: pintu masuk aplikasi

Buka folder project lalu buka routes/web.php. Isinya singkat, hanya satu route default:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Bisa dibaca seperti ini: “kalau ada request GET ke alamat /, jalankan fungsi ini, lalu kembalikan view welcome“. Di sinilah saya sadar perbedaan mendasar Laravel dengan PHP biasa: alih-alih membuat file index.php per halaman, semua alamat dipetakan lewat satu file route. Ini disebut front controller — semua request masuk lewat public/index.php, lalu Laravel yang memutuskan ke mana request itu diteruskan berdasarkan route yang cocok.

Route closure vs controller

Di contoh di atas, logika ditulis langsung di dalam route sebagai closure (fungsi anonim). Untuk halaman sekecil welcome, cara ini cukup. Tapi kalau semua logika ditumpuk di routes/web.php, file itu cepat sekali jadi penuh sesak. Solusi Laravel: pindahkan logika ke controller.

Buat controller lewat Artisan:

php artisan make:controller HalamanController

Perintah itu membuat file app/Http/Controllers/HalamanController.php. Isinya kelas kosong. Lalu saya tambahkan method sendiri:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HalamanController extends Controller
{
    public function index()
    {
        return view('welcome');
    }
}

Dan route-nya berubah menjadi:

Route::get('/', [HalamanController::class, 'index']);

Pola [NamaController::class, 'namaMethod'] adalah cara Laravel modern menunjuk controller di route. Hasilnya sama dengan versi closure, tapi logika sudah pindah ke tempat yang lebih rapi dan bisa diuji. Prinsipnya: route itu peta, controller itu pekerja.

View pertama

Method index() tadi mengembalikan view('welcome'). View adalah file template yang disimpan di resources/views dengan ekstensi .blade.php — jadi view('welcome') mencari resources/views/welcome.blade.php. Saya coba buat halaman sendiri, misalnya halaman tentang:

Route::get('/tentang', [HalamanController::class, 'tentang']);

Lalu di controller:

public function tentang()
{
    return view('tentang');
}

Dan buat file resources/views/tentang.blade.php berisi HTML sederhana. Setelah itu php artisan serve dan buka http://127.0.0.1:8000/tentang — halaman muncul. Kesannya ajaib padahal sederhana: route → controller → view. Tiga lapis yang akan terus kita pakai di semua episode berikutnya.

Kesalahan yang umum

  • Lupa menjalankan php artisan serve, lalu bingung kenapa halaman 404. Route tidak akan jalan tanpa server.
  • Nama view salah ketik: view('welcome') tapi file bernama welcome.blade.php — pastikan nama persis.
  • Menaruh route di bawah route lain yang polanya sama; di Laravel, route yang cocok pertama yang dipakai. Urutan route bisa penting.
  • Menulis logika bisnis menumpuk di route closure sampai file web.php ratusan baris — kebiasaan yang sebaiknya dihindari sejak awal dengan memakai controller.

Route Parameter: Menangkap Bagian Dinamis dari URL

Hari ini saya melanjutkan eksperimen dengan routing dan menemukan fitur yang langsung terasa penting: parameter route. Sering kali sebuah URL membutuhkan bagian dinamis, misalnya /mahasiswa/1, /mahasiswa/2, dan seterusnya untuk halaman detail. Tidak mungkin saya menulis satu route untuk setiap ID yang mungkin ada. Di Laravel, bagian dinamis itu cukup ditulis dengan kurung kurawal:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Menampilkan detail mahasiswa dengan ID: ' . $id;
});

Saat ada request ke /mahasiswa/7, Laravel menangkap potongan 7 dari URL lalu memasukkannya ke variabel $id di closure. Urutan parameternya mengikuti urutan segmen di URL, jadi kalau ada dua parameter seperti {tahun}/{bulan}, keduanya diterima sebagai dua argumen berurutan.

Parameter juga bisa dibuat opsional dengan tanda tanya, lengkap dengan nilai default:

Route::get('/sapa/{nama?}', function ($nama = 'Tamu') {
    return 'Halo, ' . $nama . '!';
});

Dengan {nama?}, URL /sapa tanpa segmen tambahan pun tetap valid dan menghasilkan “Halo, Tamu!”. Yang menarik bagi saya: satu definisi route kini melayani banyak variasi alamat sekaligus — halaman profil tanpa slug maupun dengan slug cukup ditangani satu baris.

Kebiasaan yang saya ambil dari sini: setiap kali tergoda menulis /mahasiswa/1, /mahasiswa/2, dan seterusnya secara manual, itu tanda URL-nya seharusnya punya parameter. Pola ini juga membuat tautan dari halaman daftar ke halaman detail menjadi alami, karena ID-nya tinggal dicetak di dalam loop di view.

Satu hal lagi yang membuat saya merasa lebih aman: constraint. Tanpa pengaman, /mahasiswa/abc tetap cocok dengan pola dan $id berisi teks “abc”. Laravel menyediakan cara ringkas untuk membatasinya:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Detail mahasiswa: ' . $id;
})->whereNumber('id');

whereNumber('id') memastikan parameter itu hanya boleh angka; permintaan /mahasiswa/abc langsung berakhir 404 tanpa perlu if manual di dalam closure. Ada juga whereAlpha() untuk huruf serta where('id', '[0-9]+') untuk pola kustom. Validasi dasar di level route — ini berarti bagi saya sebagai lapisan pertama sebelum logika controller.

Nama Route dan Redirect

Percobaan berikutnya: memberi nama pada route. Alih-alih menulis alamat secara harfiah di banyak tempat, saya menandai route dengan nama lalu memanggilnya lewat nama tersebut:

Route::get('/mahasiswa/{id}', function ($id) {
    return 'Detail mahasiswa: ' . $id;
})->name('mahasiswa.show');

Route::get('/contoh-lompat', function () {
    return redirect()->route('mahasiswa.show', ['id' => 7]);
});

redirect()->route('mahasiswa.show', ['id' => 7]) meminta Laravel mengarahkan pengunjung ke URL lengkap milik route bernama itu, lengkap dengan parameternya. Nama route juga berguna untuk membangun tautan di dalam view:

<a href="{{ route('mahasiswa.show', ['id' => $mhs['id']]) }}">
    Lihat detail {{ $mhs['nama'] }}
</a>

Manfaatnya baru terasa setelah beberapa hari: ketika saya mengubah alamat dari /mahasiswa/{id} menjadi /siswa/{id}, semua tautan dan redirect tetap berfungsi karena dibangun dari nama, bukan dari tulisan alamat yang tersebar di banyak file. Ini menyelamatkan saya dari pencarian-replace yang rawan ada yang terlewat.

Grup Route dan Prefix

Terakhir, soal kerapian. Semakin banyak route, file web.php mulai sulit dibaca. Laravel menyediakan grup untuk mengelompokkan route yang berbagi kesamaan — prefix URL, middleware, bahkan domain. Contoh nyata: semua halaman panel admin biasanya berawalan /admin dan wajib login:

Route::prefix('admin')->middleware('auth')->group(function () {
    Route::get('/dashboard', function () {
        return view('admin.dashboard');
    })->name('admin.dashboard');

    Route::get('/pengaturan', function () {
        return view('admin.pengaturan');
    })->name('admin.pengaturan');
});

Kedua route di atas menjadi /admin/dashboard dan /admin/pengaturan. Middleware auth berlaku untuk seluruh grup: siapa pun yang belum masuk otomatis dialihkan ke halaman login sebelum sempat menyentuh closure atau controller. Kalau suatu saat prefix berubah menjadi /panel, saya cukup mengedit satu baris, buka menyusuri setiap route.

Bagi saya, grup route ibarat daftar isi: sekali dibuka, struktur aplikasi terbaca dari pengelompokannya — mana area publik, mana area admin, mana API. Tiga hal yang saya pelajari hari ini (parameter, nama route, grup) semuanya masih bermain di file yang sama, routes/web.php, tetapi membuat file itu jauh lebih bermakna daripada sekadar daftar alamat.

Kesimpulan

Hari ini saya paham alur dasar aplikasi Laravel: request masuk ke routes/web.php, dicocokkan dengan pola URL, lalu diteruskan ke closure atau controller, dan akhirnya controller mengembalikan view Blade. Konsep ini kecil tapi fondasi — semua fitur berikutnya (form, database, login) menumpang di alur yang sama. Minggu depan saya lanjut ke Blade: layout, @extends, dan perulangan @foreach.

Sumber

Mengganti Model di Tengah Sesi Coding: Dampaknya dan Solusi Failover (OpenCode, Hermes, 9Router)

Beberapa hari terakhir saya memakai beberapa tool coding berbasis AI sekaligus, dan muncul pertanyaan yang sering ditanyakan banyak orang: kalau di tengah sesi kode modelnya ganti-ganti — entah karena rate limit atau provider sedang turun — apakah berpengaruh ke hasil kerja? Apalagi sekarang ada 9Router yang bisa auto-fallback ke model lain. Catatan ini saya rangkum dari eksperimen dan riset saya soal OpenCode, Hermes Agent, dan 9Router.

Apa yang Sebenarnya Terjadi Saat Model Ganti di Tengah Sesi?

Ganti model di tengah sesi coding bukan sekadar soal “sambungannya pindah”. Ada tiga hal yang berubah:

  • Konteks percakapan bisa terpotong. Setiap model punya batas token window sendiri. Saat failover, riwayat prompt biasanya di-truncate agar muat di model baru. Model pengganti bisa jadi tidak paham penuh alur debug yang sedang kita jalani.
  • Gaya dan keputusan teknis bisa melenceng. Tiap model punya kebiasaan sendiri soal penamaan, struktur folder, sampai pilihan pola kode. Kalau berganti di tengah fitur yang sama, hasilnya bisa tidak konsisten.
  • Ada biaya dan latency tambahan. Koneksi ke provider baru perlu inisialisasi ulang, dan jika konteks dikirim ulang, token yang dipakai juga bertambah.

Bagaimana OpenCode Menangani Ini?

OpenCode (tool agentic coding open source yang awalnya dikembangkan oleh Andrej Karpathy) mendukung banyak provider sekaligus: OpenAI, Anthropic, Google, hingga model lokal. Beberapa hal yang saya perhatikan:

  • Bisa ganti model/provider secara dinamis tanpa restart sesi, dan konteks terbaru tetap dibawa.
  • Ada failover bawaan — jika satu provider rate limited, ia beralih ke model berikutnya sesuai urutan yang kita daftarkan.
  • Semua perubahan model tercatat di log sesi, sehingga riwayatnya bisa ditelusuri kembali.

Kekurangannya: tiap provider perlu API key sendiri, dan failover bisa menambah latency kalau tidak hati-hati mengatur urutan.

Bagaimana dengan Hermes Agent?

Hermes Agent (dari Nous Research) adalah agen yang lebih umum — bisa untuk coding, riset, sampai otomasi. Untuk urusan ganti model di tengah sesi, pengalamannya sedikit berbeda:

  • Provider bisa di-load/unload secara dinamis, tapi sinkronisasi konteks cenderung manual — perlu resume sesi atau menyuntikkan ringkasan konteks.
  • Failover otomatis tidak sepenuhnya built-in seperti OpenCode, tapi bisa dikonfigurasi lewat fallback_providers di config — ini yang saya pakai untuk beberapa cron blog saya.
  • Restore konteks dari disk butuh waktu, jadi ada latency tambahan saat pindah sesi.

Di Mana Posisi 9Router?

Di sinilah 9Router berperan. Daripada tiap tool mengurus failover-nya sendiri, kita bisa mengarahkan semuanya ke satu router yang mengatur urutan model:

opencode --model "9router://gpt-4o,claude-3.5-sonnet,gemini-1.5-flash"

Jika model pertama kena limit, 9Router otomatis meneruskan ke model berikutnya. Sisi tool (OpenCode/Hermes) tidak perlu tahu apa-apa — mereka tetap memakai endpoint yang sama. Ini menyederhanakan masalah jadi satu: atur urutan prioritas, sisanya otomatis.

Jadi, Apakah Ganti Model Berpengaruh?

AspekDampakCara Mitigasi
Konteks sesiBisa terpotong (truncate)Checkpoint / ringkasan konteks tiap beberapa langkah
Konsistensi kodeGaya bisa berubah antar modelBiarkan satu model menyelesaikan satu fitur utuh
KetersediaanTurun jika satu provider downFailover 9Router dengan urutan model cadangan
Latency & biayaNaik saat failoverUrutkan model dari yang paling sering dipakai

Perspektif Hermes Agent: Kenapa Task Tetap Nyambung?

Ada satu hal yang memperjelas gambaran ini: jawaban langsung dari tim Hermes Agent (Nous Research) saat saya tanyakan hal yang sama — “jika saat eksekusi task model/provider diganti 9Router, apakah task yang dijalankan model berbeda akan tetap nyambung?”. Jawabannya: ya, tetap nyambung — dengan satu syarat penting.

Konteks Dikelola Hermes, Bukan Provider

Konteks (prompt + tools + state) disimpan di sisi gateway/sesi Hermes. Saat 9Router mengganti provider/model, Hermes tetap mengirim konteks yang sama — lewat history.messages — ke provider baru. Provider hanya “menumpang” pada konteks yang sudah disiapkan Hermes.

Syaratnya: Provider OpenAI-Compatible

Provider OpenAI-compatible (kagiro, laguna, dsb — yang memang terhubung lewat 9Router) memakai format message yang sama. Jadi model yang berbeda sekalipun akan menerima konteks yang sama persis. Yang bikin putus hanyalah perubahan model yang sangat signifikan — misalnya dari OpenAI-style ke Claude-style — karena format outputnya berbeda.

Contoh Jalan atau Tidaknya

PerpindahanKonteks Tetap?Alasan
kagiro → laguna (9Router)✅ YaFormat message sama
laguna → deepseek (9Router)✅ YaFormat message sama
deepseek → llama.cpp (GGUF lokal)⚠️ TergantungKalau via endpoint OpenAI-compatible masih nyambung, tapi gaya model baru bisa beda
ke Claude (non-OpenAI)❌ PutusFormat beda, konteks harus di-reformat

Bottom Line

Selama semua provider yang dipakai adalah OpenAI-compatible endpoint (yang memang dilakukan via 9Router), ganti model/provider tidak akan memutus alur task — Hermes akan mengirim konteks yang sama ke provider baru. Logika tool (mengubah config, restart gateway, dsb) tetap jalan karena Hermes yang mengeksekusi, bukan provider. Yang nyata berubah hanyalah cost dan kecepatan response.

Bagaimana dengan OpenCode? Apakah Sama?

Pertanyaan lanjutannya: apakah prinsip yang sama berlaku di OpenCode? Berdasarkan dokumentasi resminya — ya, prinsipnya sama.

“Select a model to use it in the current session. Switching models updates that session without changing your config.” — Dokumentasi resmi OpenCode (opencode.ai/v2/docs/models)

Konteks Dipegang OpenCode, Bukan Provider

Seperti Hermes, konteks (transcript + state) disimpan di sisi OpenCode — bukan di provider. Ganti model lewat model picker (tab) atau per-run (--model) hanya mengganti model yang dipakai sesi itu; riwayat percakapan tetap utuh dan dikirim penuh ke model baru.

9Router Transparan untuk OpenCode

OpenCode melihat 9Router sebagai satu provider OpenAI-compatible biasa. Saat failover terjadi di balik layar, OpenCode tidak tahu modelnya berganti — ia tetap mengirim konteks sesi yang sama ke endpoint yang sama. Perilakunya persis seperti Hermes.

Perbandingan Hermes vs OpenCode

AspekHermes AgentOpenCode
Pemegang konteksGateway/sesi Hermes (history.messages)Session store OpenCode (transcript)
Switch model mid-sessionDinamis via configModel picker tab / --model, tanpa ubah config
Auto-failover native antar provider✅ Ada (fallback_providers)❌ Tidak ada — manual
Failover via 9RouterKonteks nyambung ✅Konteks nyambung ✅

Satu perbedaan penting: Hermes punya failover native antar provider, OpenCode tidak. Di OpenCode, 9Router justru perannya lebih besar — dialah satu-satunya mekanisme failover otomatisnya. Tanpa 9Router, ganti model di OpenCode harus manual.

Kesimpulan

Mengganti model di tengah sesi berpengaruh, terutama pada konteks dan konsistensi — tapi dampaknya bisa dikelola. Untuk masalah ketersediaan (rate limit, provider down), 9Router sudah menyelesaikan bagian terberatnya dengan failover otomatis. Untuk masalah konteks, kebiasaan kecil seperti menyimpan ringkasan pekerjaan setiap beberapa langkah sudah sangat membantu.

Pendekatan yang saya pakai sekarang: satu model untuk satu fitur utuh, dan 9Router sebagai jaring pengaman kalau model utama sedang bermasalah. Kombinasi yang cukup untuk sebagian besar pekerjaan harian.