Cara kami merangkai lapisan AI untuk opexmx, sebuah CMMS maintenance (maintenance Corrective + Preventive, manajemen aset, basis pengetahuan). Tujuannya: pipeline copilot + retrieval yang murah per permintaan, aman dari prompt injection, dan presisi saat retrieval β tanpa membayar model frontier di setiap giliran.
Semua berjalan di Cloudflare Workers AI (lewat AI Gateway yang kompatibel dengan OpenAI). Model id yang sama berlaku di cloud maupun on-prem.
TL;DR β pipeline-nya
flowchart TD
U([pesan pengguna]) --> R1["1. Guard regex β gratis, menangkap ~80β90% prompt injection"]
R1 -->|bersih / tidak pasti| G["2. Klasifier Granite β intent + safety + routing, JSON-only (hanya jika ragu)"]
G -->|allow| E["3. Qwen3-Embedding 0.6B β query ke vektor, 768-dim (Matryoshka dari 1024)"]
E --> V["4. Pencarian vektor β over-fetch top-K x 3, Vectorize / pgvector (recall)"]
V --> B["5. Reranker BGE base β cross-encoder men-skor ulang, ambil top-K (presisi)"]
B --> Q["6. Qwen3-30B-A3B (MoE) + agen tool-calling β satu panggilan generasi, konteks terbaik. Manager Mode ke Llama-3.3-70B"]
Model mahal (30B/70B) berjalan sekali, hanya setelah permintaan divalidasi dan konteks terbaik terkumpul. Sebagian besar belanja per permintaan adalah klasifier kecil + rerank murah, bukan generasi.
Mengapa setiap lapisan ada
1. Guard β regex dulu, lalu klasifier murah
Copilot yang menelan teks pengguna bebas butuh pertahanan prompt-injection. Kami jalankan dua lapis, termurah dulu:
- Lapisan 1 β regex (gratis). Daftar frasa:
ignore previous instructions,reveal your system prompt,DAN,jailbreak,developer message:, dll. Menangkap sebagian besar serangan di $0. Berbasis frasa, bukan kata kunci telanjang βapi key/tokensendiri terlalu umum di teks maintenance nyata, jadi diserahkan ke klasifier. - Lapisan 2 β klasifier LLM (hanya jika Lapisan 1 bersih dan pesan bukan obrolan
sepele). Model kecil mengembalikan JSON ketat:
Ini melakukan tiga pekerjaan dalam satu panggilan murah: gerbang safety, klasifikasi intent, dan petunjuk routing. Petunjuk ditampilkan ke UI; tool-calling agen tetap menjadi router sebenarnya (tanpa rewrite agen).{ "intent": "maintenance", "safe": true, "requires_rag": true, "requires_tools": false, "confidence": 0.9 }
Kebijakan kegagalan: fail-open. Jika klasifier error atau tak dikonfigurasi, pesan diizinkan β Lapisan 1 regex tetap memblokir kasus yang jelas. Chat tidak pernah rusak total saat guard down.
2. Embeddings β Qwen3-Embedding-0.6B di 768 dim
Kami mengindeks manual + dokumen knowledge-center (PDF, rich-text, keterangan gambar) jadi
chunk lalu meng-embed-nya. Kami memilih @cf/qwen/qwen3-embedding-0.6b demi kualitas
berbiaya rendah.
Jebakan dimensi (ini pernah menggigit kami): Qwen3-Embedding-0.6B secara native
1024-dim, tetapi vector store kami (indeks Cloudflare Vectorize + kolom pgvector) sudah
di-pin ke 768 dari model sebelumnya (bge-base-en-v1.5). Dimensi campuran ilegal dalam
satu indeks. Alih-alih migrasi seluruh store ke 1024 (indeks baru + kolom baru + reindex
penuh), kami memanfaatkan Matryoshka representation learning: Qwen3 mendukung truncation
output di rentang 32β1024 dim, jadi kami minta dimensions: 768 pada panggilan
/embeddings dan mendapat vektor 768-dim yang tetap kompatibel dengan store yang ada.
β οΈ Field
dimensionsmilik OpenAI harus diteruskan gateway ke model. Jika gateway Anda menghapusnya, Anda diam-diam mendapat 1024 dan insert gagal. Verifikasi dengan:curl -s $GW/embeddings -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -d '{"model":"@cf/qwen/qwen3-embedding-0.6b","input":["test"],"dimensions":768}' \ | jq '.data[0].embedding | length' # harapan 768Juga: kirim
dimensionshanya untuk model yang mendukung Matryoshka.bge-basefixed-768 dan 400 pada field ini.
3. Retrieval β pencarian vektor (recall)
Embed query, cari di indeks, over-fetch (mis. top-KΓ3) dengan fan-out filter (dokumen global + lingkup aset + lingkup tipe peralatan, digabung berdasarkan skor terbaik per chunk). Embedding recall-optimized β ia mengangkat lingkungan yang benar, bukan chunk yang persis tepat.
4. Reranker β BGE-Reranker-Base (presisi)
Cross-encoder bukan chatbot lain. Tugasnya satu: "dari chunk yang diretrieve, mana yang
benar-benar paling relevan?" Ia menerima pasangan (query, passage) dan mengembalikan
skor relevansi, lalu kami urutkan ulang dan ambil top-K.
Ini adalah tuas kualitas retrieval terbesar dengan biaya ~nol:
| chunk | embedding sim | skor reranker |
|---|---|---|
| Manual pompa | 0.84 | 0.99 |
| Troubleshooting pompa | 0.82 | 0.95 |
| Spesifikasi bearing | 0.81 | 0.91 |
| SOP konveyor (off-topic) | 0.80 | 0.20 |
Fail-open: reranker error apa pun β pertahankan urutan embedding-similarity. Retrieval tidak pernah putus.
5. Generasi β Qwen3-30B-A3B + tool-calling
30B Mixture-of-Experts (hanya ~3B aktif per token) memberi kualitas model besar pada
latensi/biaya model kecil, dengan function calling native untuk loop agen. Tools:
search_assets, get_asset_status, search_documents, search_problems,
create_ticket, PM/parts/metrics, dll. Model merutekan sendiri.
Manager Mode menaikkan hanya langkah sintesis ke Llama-3.3-70B (chatModelPro) saat
pengguna meminta tinjauan lintas-domain. Ronde tool tetap di model dasar murah; hanya
briefing akhir yang membayar model besar.
6. Autocomplete / fill-with-AI β jalur cepat terpisah
Autocomplete ghost-text inline dan "isi field ini" butuh latensi rendah, bukan reasoning.
Mereka berjalan di model kecil cepat (chatModelFast, Llama-3.1-8B-instruct-fp8-fast)
dan fallback ke model chat jika tak mengembalikan apa-apa.
β οΈ Gotcha: jika provider Anda gateway kompatibel-OpenAI, pastikan ia menghormati model id per-role yang berbeda. Bug umum: gateway men-collapse semua id
@cf/ke satu model chat terkonfigurasi, sehingga setelan "fast model" Anda diam-diam diabaikan dan setiap autocomplete membayar 30B. Teruskan id yang diminta saat gateway berbicara dalam namespace model yang sama; hanya collapse@cf/βnama-lokal pada endpoint lokal satu-model (Ollama/mlx).
β οΈ Gotcha #2: model reasoning kecil (mis. MoE mikro hibrid) adalah pilihan salah untuk tugas generasi seperti pengisian field. Dengan thinking dimatikan ia bisa memancarkan kosong. Pakai untuk klasifikasi; pakai model instruct biasa untuk autocomplete/fill.
Model & biaya (Cloudflare Workers AI, ~$0.011 / 1k Neurons)
| Peran | Model | Catatan |
|---|---|---|
| Chat / agen | @cf/qwen/qwen3-30b-a3b-fp8 | MoE murah, multibahasa, tool-calling |
| Pro / Manager | @cf/meta/llama-3.3-70b-β¦-fp8-fast | langkah sintesis saja |
| Autocomplete | @cf/meta/llama-3.1-8b-β¦-fp8-fast | jalur cepat terpisah |
| Embeddings | @cf/qwen/qwen3-embedding-0.6b | 768-dim (Matryoshka-truncated) |
| Reranker | @cf/baai/bge-reranker-base | cross-encoder, pasca-retrieval |
| Guard/klasifier | @cf/ibm-granite/granite-4.0-h-micro | intent + prompt-injection, JSON-only |
Biaya andalan: ~$0.045 / pengguna ringan / bulan, ~$0.27 / pengguna berat. Guard + reranker menambah belanja bisa diabaikan relatif terhadap satu generasi 30B per giliran.
Prinsip desain
- Termurah-dulu. Regex sebelum LLM; klasifier kecil sebelum 30B; embedding+rerank sebelum generasi. Bayar model besar sekali, terakhir.
- Fail-open di jalur safety/klasifier; fail-open di jalur rerank. Tak boleh ada yang memutus chat atau retrieval. Regex tetap memblokir injection yang jelas meski guard LLM down.
- Satu model murah, tiga pekerjaan. Guard mengembalikan safety + intent + petunjuk routing dalam satu panggilan JSON, bukan tiga model terpisah.
- Disiplin dimensi. Pilih satu dimensi embedding dan pin seluruh store ke sana. Saat ganti model, samakan dimensi atau pakai truncation Matryoshka β lalu reindex agar setiap vektor tinggal di ruang yang sama.
- Recall saat retrieval, presisi saat rerank, kualitas saat generasi. Jangan minta satu tahap mengerjakan tugas tahap lain.
Pelajaran yang kami bayar
- Verifikasi langsung, jangan percaya halaman docs. Sumber docs/search mengklaim
Qwen3-Embedding 768-dim "mungkin." Gateway live mengembalikan 1024 dan insert gagal.
Selalu curl endpoint dan cek
.data[0].embedding | length. - Gateway harus meneruskan
dimensions. Tanpa passthrough, truncation Matryoshka tak terlihat dan Anda diam-diam mendapat dim native. - Model id per-role harus dihormati. Gateway yang menulis ulang semua id
@cf/ke satu model chat diam-diam menggagalkan setelan model fast/pro Anda (dan eskalasi Manager-Mode). - Kecil β selalu murah. Model reasoning mikro pada prompt generasi kompleks bisa mengembalikan kosong dan menyia-nyiakan detik sebelum fallback. Cocokkan kelas model ke tugasnya.
Mereproduksi bentuk ini
Jika Anda membangun sesuatu yang serupa, bagian minimalnya:
- modul guard: daftar regex + panggilan JSON-klasifier (fail-open);
- langkah embed yang di-pin ke satu dimensi (truncate Matryoshka bila perlu);
- langkah retrieve yang over-fetch;
- langkah rerank (cross-encoder) yang men-skor ulang dan memangkas (fail-open);
- langkah generate pada satu model bagus, dengan jalur eskalasi model pro;
- jalur cepat (model instruct kecil) untuk autocomplete/fill, dengan fallback model chat.
Semuanya muat dalam anggaran permintaan di mana biaya dominan adalah satu panggilan generasi β persis seperti yang Anda inginkan.
Bicara dengan kami tentang men-deploy copilot RAG pada data maintenance Anda β