engine-bot memakai fat controller + domain services pattern. Controller (mainController.js) menangani buffer, dedup, context, orchestration, response, dan scheduling. Domain logic di service/ dan tools/ menangani query, formatting, dan external API.
Struktur direktori
app.js # Express + Socket.IO + MongoDB + Redis health
routes/chatbotRoutes.js # Semua endpoint /api
controllers/ # 5 file — mainController, aiAgent, product, address, webhook
middleware/ # authMiddleware
config/ # env.js, db.js (gitignored)
models/ # 24 Mongoose schema
service/ # 15 domain service — query, formatting, external API
subagents/ # 3 specialist agent — orders, payment, booking
tools/ # 2 domain tools — order, booking
prompt/ # Prompt Markdown — runtime configuration
scheduler/ # FollowUpMessages, workerService, worker
utils/ # util.js (vision/transcription), redisHealth.jsPola dominan — fat controller
controllers/mainController.js adalah entry point percakapan. Controller menangani:
1. verifyToken (middleware) — auth
2. functionRouteBot — validate store_id, customer_id, agent_id
3. dedup (in-memory map) — 10 detik, key customer_id
4. userBuffer (message batching) — based on time_reply
5. context loader — customer, agent, store, history, active order
6. intent detector — service/intent.js
7. main orchestrator — prompt/main-agent.md + OpenAI
8. specialist agent — subagents/*.js + tools
9. response formatter — JSON, image extraction
10. follow-up scheduler — scheduler/workerService.js (optional)Domain service — service/
Service menangani query MongoDB, formatting, dan external API:
| File | Fungsi |
|---|---|
service/chatbot.js | Orchestration helper |
service/conversationsEmbed.js | Conversation embedding |
service/createVdb.js | Vector DB creation (FAISS) |
service/ragVdb.js | RAG retrieval |
service/intent.js | Intent detection |
service/product.js | Product query + formatting |
service/order.js | Order lifecycle + checkout |
service/carts.js | Cart query |
service/ongkir.js | RajaOngkir shipping cost + tracking |
service/address.js | Address parse + format |
service/prompt.js | Prompt loading + context injection |
service/costOpenAi.js | Token cost tracking |
service/devices.js | Device query |
service/getCartsId.js | Cart ID helper |
Specialist agent — subagents/
| File | Agent | Model | Fungsi |
|---|---|---|---|
subagents-orders.js | Customer service, admin, logistics | gpt-4o-mini / gpt-5.4 | FAQ, cart, checkout, shipping |
subagents-payment.js | Payment | gpt-5.4 | Metode bayar, konfirmasi, instant payment |
subagents-booking.js | Booking | gpt-4o-mini | Reservasi event — stub |
subagents/payment/ | Payment tools | — | Detail tool payment |
Domain tools — tools/
| File | Fungsi |
|---|---|
tools/order/tools.js | Cart, checkout, address, shipping, payment, complaint |
tools/booking/tools.js | Booking — mengembalikan pesan belum diimplementasikan |
Prompt — runtime configuration
Prompt Markdown di prompt/ adalah runtime configuration, bukan dokumentasi yang bisa dihapus:
| Prompt | Fungsi |
|---|---|
prompt/main-agent.md | Routing dan final JSON response — main orchestrator |
prompt/order/customer-service.md | Product, FAQ, complaint |
prompt/order/admin.md | Cart dan checkout |
prompt/order/logistics-agent.md | Address, ongkir, courier, tracking |
prompt/order/payment.md | Payment method, confirmation, status |
prompt/booking/booking.md | Booking flow — backend stub |
Agent-specific prompt digabung dengan dynamic context melalui promptWithContext.
Response shape
Tidak ada standardisasi — shape berbeda per controller:
// Chatbot reply normal (POST /api/chatbot-api)
{
"statusCode": 201,
"idUser": "customer_id",
"idAgent": "agent_id",
"idStore": "store_id",
"data": {
"answer": ["Balasan chatbot"],
"images": [],
"history": []
}
}
// Welcome (POST /api/generate_welcome)
{
"status": 201,
"message": "success",
"data": { "welcome_message": "...", "cost_request": "$0.000000" }
}
// Auth smoke test (GET /api/test)
{ "message": "berhasil" }
// Notify (POST /api/notify-new-message)
"Notification send to client"ES modules
package.json memakai "type": "module". Semua import memakai import/export, bukan require. Path import wajib sertakan .js extension.
Logging
Log ad-hoc via console.log/console.error — tidak ada structured logger. Prefix [...] digunakan untuk konteks:
| Prefix | Makna |
|---|---|
[Guard] | Dedup request |
[FastPath] | Direct shipping cost path |
[MAIN_ORCHESTRATOR] | Main agent invocation |
[ADMIN_AGENT] | Cart/order agent |
[SUBAGENT_PAYMENT] | Payment agent |
[INSTANT_PAYMENT] | Checkout creation |
[FollowUp] | Scheduling |
[WORKER] | Job processing |
[WEBHOOK_PAYMENT] | Payment callback |
Mongoose schema — inkonsistensi
| Masalah | Lokasi | Dampak |
|---|---|---|
require vs required | models/orders.js banyak field | Typo — validasi tidak aktif |
payment_method duplikat | models/orders.js:203 dan :255 | Mongoose ambil yang terakhir |
Orders vs orders | Nama model capital | Mongoose auto-pluralize — verifikasi collection name |
Menulis service baru
// service/myFeature.js
import MyModel from '../models/myModel.js';
export async function doSomething(storeId, customerId) {
return await MyModel.find({ store_id: storeId, customer_id: customerId });
}Daftarkan di controller atau tool yang relevan. Selalu sertakan store_id di query.
Anti-pola yang sering muncul
- Query tanpa
store_id— bocor antar-merchant. Lihat Multi-tenant. - Kredensial hardcoded di
config/env.js— lihat Environment. x-forwarded-fordipercaya untuk IP allowlist — lihat Auth & JWT.- Socket.IO tanpa auth/tenant room — lihat Tech Debt.
- Prompt Markdown sebagai kontrak — perubahan prompt mengubah perilaku agent tanpa code change.
Langkah berikutnya
- Service lain yang dipanggil? Baca External Services.
- Known issues? Baca Tech Debt.
- Cara kerja agent? Baca Agent Architecture.