· 10 menit baca

Bagaimana kami membuat LLM murah andal di DynamoDB: validator tool + eval

Asisten AI DynoTable berjalan pada kredensial Anda sendiri, yang berarti Anda memilih modelnya — dan banyak orang memilih yang murah. Jadi kami tidak menyetel asisten terhadap model frontier. Batas bawah kami adalah Amazon Nova Lite, sebuah model yang biayanya sekadar pembulatan per query dan gagal dengan cara yang tidak dialami model frontier.

Inilah inti dari seluruh postingan ini: pada satu skenario — sebuah join yang membutuhkan tabel composite-key sebagai basisnya — Nova Lite memperoleh skor 0%. Kami tidak berganti model, tidak menambahkan contoh few-shot, tidak melakukan fine-tune. Kami menulis ulang satu pesan error validator untuk mengatakan persis apa yang harus dilakukan sebagai gantinya. Skornya melonjak menjadi 100%, membalik join tersebut tepat pada langkah berikutnya.

Model tak pernah menjadi hambatannya. Pesannya yang menjadi hambatan.

Ini adalah kisah engineering di balik pelajaran itu: validator yang mengelilingi setiap panggilan tool, hal-hal yang sengaja tidak kami validasi, dan eval harness yang menjadi penengah setiap perubahan. Jika Anda membangun sebuah di atas model hemat — atas DynamoDB atau apa pun — sebagian besarnya bisa dipindahkan.

Mengapa model murah gagal secara spesifik di DynamoDB

DynamoDB adalah target yang tidak bersahabat bagi model kecil karena permukaan query-nya tampak seperti SQL padahal bukan. PartiQL menerima sintaks SELECT tetapi tidak mendukung JOIN, tidak GROUP BY, tidak DISTINCT, tidak LIKE — dan model yang belajar SQL dari internet memancarkan semuanya dengan percaya diri.

Kegagalan yang benar-benar kami amati, masing-masing dari run nyata yang tercatat:

  • Argumen limit yang hilang. rutin menghilangkan batas baris opsional pada sebuah tool query. Backend dengan senang hati mengembalikan segalanya, sebuah hasil tool 200 KB mendarat di percakapan, dan giliran model berikutnya mati pada langit-langit konteks — sebuah ValidationException di tengah loop, disalahkan pada komponen yang salah.
  • Konstruk yang tidak didukung. JOIN, GROUP BY, agregat dalam PartiQL — statement yang tak akan pernah dijalankan DynamoDB, dipancarkan karena akan benar di Postgres.
  • Penukaran identifier. Nova Lite, diberi SELECT * FROM customers dan instruksi "tambahkan WHERE untuk Jerman", terkadang mengembalikan SELECT * FROM orders WHERE … — sebuah tabel yang tak pernah disebutkan pengguna. Statement itu terurai dengan sempurna. Tak ada yang sifatnya sintaktis menangkapnya.
  • Kesalahan perutean tool. Ditanya tentang sebuah file yang dilampirkan, Nova Lite memanggil tool daftar tab-terbuka untuk mencari file di tab — ia jarang menggunakan mekanisme pencarian-tool sama sekali, sehingga apa pun yang tidak langsung terlihat dianggap tidak ada.

Tak satu pun dari ini bersifat hipotetis. Masing-masing mendorong sebuah pagar pengaman spesifik di bawah.

Validasi di batas tool, dan jadikan error sebuah pelajaran

Satu keputusan arsitektur yang paling penting: validasi berada di dalam langkah eksekusi tool, bukan di skema input. Penolakan skema adalah jalan buntu — loop melihat sebuah type error dan tidak belajar apa pun. Sebuah tool yang berjalan, memeriksa, dan mengembalikan error terstruktur memberi model sebuah hasil tool normal yang bisa ia tanggapi pada langkah berikutnya.

Dan error itu ditulis untuk model, bukan untuk log manusia. Error tool ekspor kami berbunyi seperti ini, kata demi kata:

startExport requires exactly one of {tabId} or {sql}.
FIX: call listOpenTabs and pass {tabId}, or pass {sql} with a single SELECT.
startExport {sql} must be a read-only single SELECT.
FIX: remove any INSERT/UPDATE/DELETE/DDL and pass one SELECT statement.

Kegagalan validasi query membawa prefiks validation:validation: parse-error: …, validation: partiql-unsupported: JOIN is not supported by DynamoDB PartiQL. — yang oleh system prompt ditetapkan sebagai sinyal untuk memperbaiki statement alih-alih mengulang input yang sama. Sebuah pesan penolakan adalah sinyal pengajaran. Sebelum kami mengadopsi konvensi FIX:, kegagalan yang sama berakhir dengan model meminta maaf dan menyuruh pengguna mengeklik tombol ekspor sendiri. Sesudahnya, ia mengoreksi diri dan menyelesaikan tugas.

Korolarinya butuh waktu lebih lama untuk dipelajari: di mana contoh konkret berada adalah sebuah sumbu desain. Jika sebuah kesalahan ditangkap oleh validator saat runtime, biarkan system prompt tetap generik dan biarkan pesan error dinamis membawa tabel spesifik dan perbaikannya — prompt tetap kecil dan pelajaran tiba tepat ketika dibutuhkan. Hanya kesalahan yang tak bisa ditangkap validator mana pun (seperti merutekan sebuah pertanyaan agregat ke tool yang tepat sejak awal) yang layak mendapat contoh konkret di dalam prompt itu sendiri.

DynamoDBTool boundaryModelDynamoDBTool boundaryModelrunPartiQL("SELECT … JOIN …")validation: partiql-unsupported:JOIN is not supported by DynamoDB PartiQL.runWorkbenchSql("SELECT … JOIN …")streamed pages (capped)rows10 rows / 5 KB + "Showing 10 of 4,200…"

Uraikan hal-hal yang akan Anda tolak

Menolak JOIN dengan syntax error at offset 27 yang generik tidak mengajarkan apa pun. Jadi parser PartiQL tulisan tangan kami melakukan sesuatu yang agak nyeleneh: ia sengaja mengurai konstruk yang tidak didukung DynamoDB menjadi pohon sintaks yang valid — hanya agar sebuah walker bisa memancarkan diagnostik yang spesifik dan dapat diajarkan. Semuanya kata demi kata, semuanya menghadap model:

  • JOIN is not supported by DynamoDB PartiQL.
  • GROUP BY is not supported by DynamoDB PartiQL.
  • TOP N is not supported. Use the API limit parameter.
  • IN list has 120 items. DynamoDB PartiQL caps IN at 50 values (PK column) or 100 values (non-key column).
  • IS NOT NULL is not supported in DynamoDB PartiQL. Use attribute_exists.
  • lower(path) is not a DynamoDB PartiQL function. Use … instead. — dengan penggantinya diselesaikan per fungsi.

Setiap pesan menyebutkan konstruk dan jalan keluarnya. Dipadukan dengan aturan prompt "jika PartiQL menolak sebuah konstruk yang tidak didukung, segera beralih ke SQL workbench", sebuah model murah pulih dari insting Postgres-nya dalam satu langkah alih-alih terombang-ambing.

Ketahui apa yang TIDAK boleh divalidasi

Lapisan schema-aware dari validator SQL kami menyelesaikan referensi tabel dan kolom terhadap deskripsi tabel nyata yang sudah dimiliki aplikasi. Pada awalnya ia juga menandai referensi ke atribut yang tidak dideklarasikan DynamoDB — yang terdengar benar dan justru salah. attributeDefinitions DynamoDB hanya mendaftar atribut key, sehingga pemeriksaan itu menolak WHERE <non-key> = … — bentuk query sah yang paling umum yang ada. Pemeriksaan itu kini menjadi sebuah peringatan yang tak pernah menghalangi.

Setiap validator adalah sebuah taruhan bahwa bentuk yang ditolak lebih mungkin salah daripada benar. Ketika model data tak bisa mendukung pemeriksaan itu, jangan bertaruh.

Pembatasan hasil sebagai perilaku, bukan sekadar keamanan

Perbaikan untuk ledakan limit yang hilang bukanlah "tambahkan limit ke prompt" (model murah tetap menghilangkannya). Hasil tool query dibatasi ketat — 10 baris atau 5 KB, mana yang lebih dulu, selalu mempertahankan setidaknya baris pertama — dan pemberitahuan pemotongannya sendiri bersifat preskriptif:

Showing 10 of 4,200. To filter: re-run with WHERE.
To view in UI: emit proposeOpenTable.
To aggregate: use runWorkbenchSql GROUP BY.

Batas tersebut sengaja jauh di bawah apa yang bisa ditampung konteks. Tujuannya adalah perilaku: memaksa model menyerahkan kepada pengguna sebuah tampilan tabel nyata atau sebuah agregat yang eksak, tak pernah mengenumerasi baris ke dalam chat. Petunjuk pemulihan menyebutkan tool yang tepat, sehingga batas itu mengajarkan perutean pada saat model membutuhkannya. Amplop yang sama dicerminkan byte demi byte dalam eval harness, dan sebuah skenario regresi menyematkan truncated: true terhadap sebuah tabel 10.000-item sehingga batas itu tak akan pernah menghilang secara diam-diam.

Alih-alih mengenumerasi baris ke dalam chat, asisten memancarkan sebuah chip yang Anda klik untuk membuka hasilnya sebagai sebuah tab DynoTable nyata.
Alih-alih mengenumerasi baris ke dalam chat, asisten memancarkan sebuah chip yang Anda klik untuk membuka hasilnya sebagai sebuah tab DynoTable nyata.

Eval adalah penengahnya

Tak satu pun dari yang di atas dirancang berdasarkan intuisi dan dipertahankan atas keyakinan. Setiap klausa prompt, pesan validator, dan batas dijaga oleh sebuah suite eval yang menjalankan loop agen nyata terhadap DynamoDB Local yang telah di-seed — tool nyata, eksekusi query nyata, pembangun prompt dan validator produksi yang diimpor langsung alih-alih diimplementasikan ulang — dinilai oleh pemeriksaan biner atas jejak tool, state akhir database, dan teksnya.

Apa yang ditangkap eval yang terlewatkan oleh intuisi:

  • Sebuah penggeneralisasian prompt yang "lebih bersih" memundurkan sebuah skenario dari 100% menjadi 0%. Ia terbaca lebih baik bagi kami. Model batas bawah tidak setuju. Hanya eval yang menyadarinya.
  • Saran prompt untuk model frontier secara aktif salah untuk model murah. Panduan yang dipublikasikan mengatakan untuk melunakkan bahasa CRITICAL/MUST karena model besar terlalu terpicu olehnya. Model murah kurang patuh dan membutuhkan versi yang tegas. Kami kini menjaga lewat eval setiap saran yang dipinjam alih-alih menerapkannya atas keyakinan.
  • Asersi tool yang ketat menghukum perilaku yang benar. Sebuah scorer yang mewajibkan model memanggil tool ask untuk klarifikasi lulus 19% di seluruh tangga model — hampir setiap "kegagalan" adalah model yang telah dengan benar meminta klarifikasi dalam teks. Scorer itu kini menerima keduanya. Nilailah niat yang menghadap pengguna, bukan mekanismenya.
  • Tangga model layak dipersempit. Kami mulai luas — Mistral, AI21, GLM, Qwen, Nova Pro, dan lainnya — lalu memangkasnya menjadi dua anak tangga (Nova Lite, Claude Haiku) karena alasan yang hanya muncul dari run nyata: satu keluarga model biayanya ~10× per panggilan karena Bedrock tidak menawarkan prompt caching untuknya; Nova Pro berhalusinasi nama atribut yang digabungkan seperti customers.customerName pada join terbuka. Dan satu keluarga harus di-blocklist sepenuhnya — API terpadu Bedrock dengan senang hati menerima definisi tool untuk model yang adapter-nya kemudian memancarkan pemanggilan fungsi sebagai teks biasa, sesuatu yang tak akan diberitahukan metadata API kepada Anda. Kami menemukannya dengan sebuah smoke probe berbayar, bukan sebuah spec.

Dua disiplin menjaga suite tetap jujur. Tabel contoh tak pernah sama dengan tabel tes: contoh prompt menggunakan nama netral yang tidak ada dalam data seed, dan eval berjalan pada tabel yang berbeda — sehingga sebuah kelulusan membuktikan model menggeneralisasi pola, bukan bahwa ia menghafal contoh kami. Dan mode watch $0 menolak fixture yang direkam: keluaran model yang diputar ulang akan meluluskan (hijau) asersi yang bergantung pada aktivitas tool nyata, sehingga tier gratis hanya memvalidasi pengkabelan, dan apa pun yang mengklaim sebuah skor harus menghabiskan token nyata pada model nyata.

Daftar periksa, jika Anda sedang membangun salah satu dari ini

  • Pilih model batas bawah Anda terlebih dahulu dan selalu eval terhadapnya; model frontier menyembunyikan bug Anda.
  • Tempatkan validasi di langkah eksekusi tool; kembalikan error yang bisa ditindaklanjuti loop, tak pernah jalan buntu skema.
  • Tulis setiap penolakan sebagai what's wrong + FIX: exact next action, dan sisipkan tabel pengguna yang sebenarnya ke dalamnya.
  • Uraikan apa yang hendak Anda tolak, agar diagnostiknya bisa spesifik.
  • Audit setiap validator terhadap model data Anda — hapus yang menolak bentuk yang sah.
  • Batasi hasil tool di bawah apa yang muat, dan buat pemberitahuan pemotongan merutekan model ke tool berikutnya yang tepat.
  • Jalankan eval sebagai loop nyata terhadap backend (lokal) nyata, menggunakan ulang jalur kode produksi; jaga data contoh tetap terpisah dari data tes; jadikan eval — bukan selera — sebagai penengah setiap perubahan prompt dan pesan.

Apakah semua ini penting jika Anda menggunakan model frontier?

Lebih jarang, tetapi ya. Validator menahan beban di setiap tier — sebuah model frontier tetap tak bisa menjalankan JOIN di DynamoDB, dan error preskriptif hanya dikonsumsi lebih cepat. Pada run kami sendiri, perilaku pemancaran chip dari sebuah model kelas frontier pernah turun ke 0% di bawah sebuah frasa prompt yang bisa ditangani model yang lebih kecil — eval harness menangkap itu juga. Rekayasa keandalan bukanlah pajak khusus model kecil; model kecil hanya membuat tagihannya tiba lebih cepat.

Di mana ini berjalan

Semua ini dikirim di dalam asisten DynoTable dan katalog tool bergerbang-nya: query bahasa alami yang schema-aware pada kredensial Bedrock Anda sendiri, agregat seluruh tabel yang eksak, dan penulisan yang hanya pernah masuk ke sebuah staging area yang dapat ditinjau. Agen eksternal mendapat toolkit tervalidasi yang sama melalui server MCP — bagaimana kami membangun itu dengan aman adalah kisahnya sendiri, dari OAuth hingga isolasi kredensial.

Dan ketika Anda menginginkan determinisme alih-alih generasi — query yang akan Anda commit ke kode — lewati model sepenuhnya: Expression Builder merakit request yang eksak secara manual.

Bekerja dengan DynamoDB tanpa Console

DynoTable adalah klien desktop yang cepat untuk DynamoDB — jelajahi tabel, jalankan query gaya SQL, dan edit Item secara lokal.