# Roadmap Not a generic TODO list — this captures the **intended architecture progression** and *why* each phase is sequenced that way. ## Phase 1 — Telegram AI Foundation ✅ (current) TypeScript + grammY, Docker dev mode with hot reload, webhook + long-polling, `/start` `/help`, normalized message contract, LLM via LiteLLM, conversation persistence in Postgres, one controlled n8n path, tests + docs. **Why this order**: prove the transport (Telegram webhook) and the AI loop end-to-end before investing in durable context, RAG, or a second channel. ## Phase 2 — Durable Context + Business Tools **Progress (2026-08-30):** - ✅ **Product catalog awareness** — `CatalogRetriever` (`src/ai/retrieval/catalog.ts`) replaces `NoopRetriever`: SKU-token + name/category keyword lookup over the generated `src/data/catalog.ts` (259 entries, built by `gen_catalog.py` from `catalog_sku.csv`; no prices, no storage paths). Hits are injected into the prompt as "Product facts" with product-page links (`https://www.digikedai.com/products//`); free items (SKU prefix FREE — FREECXM04 = FREE + original paid SKU CXM04, the trial twin) are flagged and routed to `/free/`. Keyword search chosen over pgvector at this scale (eval criteria below). - ✅ **Passive human-handoff** in `system.ts` — admin contact (Telegram @MrFullStackDev only, no WhatsApp/phone) is offered only when the customer asks for a human or the bot cannot solve the issue; repeat questions are re-answered with one ask-line (deterministic `isRepeatQuery` on top of memory last_queries); URLs must stand alone on their own line; reply language mirrors the customer (language_code mapped to friendly names). - ✅ **Purchase flow: multi-way state machine + inline buttons** (第1–3批, 2026-08-31) — deterministic `pendingPurchase` state machine (mirrors the free-trial `pendingTrials` pattern) replaces the single admin deep link. Purchase intent is intercepted in `message:text` via `PURCHASE_INTENT_RE` (zh/en/ms, SKU-aware; FREE-prefixed trial SKUs are never intercepted) → a two-row inline keyboard: ① 网店下单 (online store / marketplace — neutral words, preferred, row on top) → neutral guidance + 联系 admin / 返回 buttons; ② 找 admin 购买 → admin deep link issued DIRECTLY (fallback, fast). 「返回」re-shows the menu; every callback branch ends with `answerCallbackQuery()` (Telegram retry idempotency). Deep-link prefill is a free-text sentence in the customer's language built by `buildAdminPurchaseLink({sku?, paymentPreference?, username?, lang?})` — whatever was collected (SKU / payment preference / labelled username), nothing forced; no SKU → generic product message. The URL is `encodeURIComponent`'d exactly ONCE in code; the prompt carries two static pre-encoded samples (with/without username) and the model only swaps alphanumeric SKU/username — this killed the double-nesting bug (第1批 f27bbb4). **中性词纪律**: no Shopee/Lazada/Add-On/TnG/bank-in anywhere in bot copy until the marketplace is live (test-asserted). Full plan: `docs/PURCHASE_FLOW_REDESIGN.md`. - ✅ **Free-trial flow button-ified** (第3批, 2026-08-31) — `startSku` / `confirmReuse` replies now carry inline buttons (✅ 确认并开通 / ✏️ 提供新 username / ♻️ 加到现有账号) dispatched into the `pendingTrials` state machine; the legacy text path (同意 / 新账号 xxx / 用户名:…) still works unchanged. Remaining items: - Enrich user profile & conversation memory (`src/ai/memory`). - Explicit tool schemas (product lookup, order lookup, status, free-account) with validation before n8n execution. - Admin/user authorization + rate limiting; allowlist enforced in production. - Logging, correlation IDs, observability. - Formalize Channel/Core contracts so Shopee/Lazada adapters drop in without touching the AI core. **Why now**: only once Phase 1 shows what the customers actually ask. ## Phase 3 — RAG + Multi-Channel - Enable `pgvector` in the existing `pgvector/pgvector:pg17` Postgres (already provisioned) when keyword lookup becomes insufficient. - Ingestion pipeline for docs/FAQs/products/policies. - Retrieval evaluation + source attribution/citation. - Shopee adapter (merchant/chat API), then Lazada adapter. - Channel-specific personas/prompts while keeping shared product/customer context. - Consider a separate vector DB **only** if pgvector becomes a real bottleneck. **Why pgvector before Qdrant**: vectors live alongside ordinary Postgres data; exact + ANN search; keeps the architecture smaller until scale demands separation. ## Criteria for adding new infrastructure Before introducing any new service, explain operational cost and confirm the existing stack (Postgres, n8n, Traefik, LiteLLM) can't already meet the need. Non-goals to protect: no Qdrant/vector DB without a concrete requirement, no multi-agent swarm, no model fine-tuning, no Dify unless it earns its place. ## Risks / open items - **LiteLLM shared key**: bot currently uses the mem0 LiteLLM master key. Phase 2 should mint a dedicated virtual key (rate-limit + revoke isolation). See `docs/DECISIONS.md`. - **Webhook auth**: `TELEGRAM_WEBHOOK_SECRET` guards the URL path; keep it strong in production. - **Free-account workflow**: the exact n8n workflow path/schema is still a placeholder — confirm with the actual workflow before wiring it to the LLM as a tool.