--- title: AI Platform Lead Scoring v2 description: Hành vi nhà cung cấp Lead Scoring và ranh giới với DotB-Omni trong bản rebuild. status: review_ready --- # Lead Scoring v2 Đặc tả này thay thế hành vi Pilot trong `old_repo/`. API còn phải được DotB-Omni kiểm tra bằng vài fixture chung trước khi coi là hợp đồng tích hợp đã phát hành. Các quyết định dưới đây đã được chốt trong kế hoạch rebuild; các giới hạn hạ tầng/AIHub chưa xác minh được ghi riêng ở cuối. ## Sở hữu - DotB-Omni tạo snapshot, định nghĩa rules/points và rule/AI weights, score bands, HITL/review/override, quyền người dùng và hành động CRM. Omni gửi tenant key và V-Key từ backend của mình, không từ browser. - AI Platform xác thực tenant, thực thi rules của Omni, gọi nhà cung cấp AI đã chọn khi cần, tính rule_score/ai_score/final_score, lưu run và gửi thông báo kết thúc. Lead Scoring mặc định dùng AIHub; quản trị nội bộ có thể kích hoạt một AI profile có phiên bản chọn AIHub hoặc OpenAI-compatible cho toàn service. Omni không chọn provider trong từng request. Admin Web nội bộ chỉ quản lý AI profile và xem/chạy fixtures; không quản lý customer rules/review. - DotB EMS là nguồn sự thật CRM. Omni nhận webhook terminal rồi GET chi tiết run trước khi ghi EMS. Webhook không chứa snapshot, V-Key hay AI evidence. ## Admission và idempotency `POST /v1/lead-scoring/runs` nhận `Authorization: Bearer `, `Idempotency-Key: ` và `X-Tenant-VKey` tùy chọn. Body chứa `request_id` cùng UUID, `subject_ref`, snapshot, app-defined scoring configuration (`version`, rules/points, `rule_weight`, `ai_weight`) và AI profile. Tenant được suy ra từ key xác thực, không từ body. `GET /v1/lead-scoring/runs/{run_id}` chỉ trả dữ liệu của tenant đó. - Fingerprint SHA-256 của canonical **body Omni**; không bao gồm tenant key, V-Key hoặc AI profile do server chọn. Lookup `(tenant, request_id)` trước khi chọn profile. Cùng key/body trả run cũ mà không schedule AI; cùng key/body khác trả 409. PostgreSQL unique constraint giải quyết concurrent requests; retry admission đã commit nhưng mất response dùng cùng UUID/body. - Run mới đóng băng AI profile đang active (gồm provider và model), INSERT + COMMIT trước khi schedule và trả 202; `Location` trỏ đến GET. Nếu không schedule được, run thành failed và response phản ánh trạng thái thực, không để pending giả. Chấm lại hoặc thử profile mới dùng UUID mới. Không reuse UUID quá 90 ngày: run và fingerprint bị xóa, không có tombstone. - Validate chặt loại rule/operator/value, tổng weights bằng 1, score/points và dữ liệu snapshot; đầu vào sai trả 422. Snapshot tối đa 10 conversations, 50 messages, 20.000 ký tự nội dung và 90 ngày evidence; giữ thứ tự deterministic. Kích thước toàn body có hard cap. Không hard-code rule giáo dục hay category/action/review state. ## Kết quả và lỗi - `ai_weight=0`: `rules_only`, `degraded=false`, `ai_score=null`, không gọi AIHub và không cần V-Key. - `ai_weight>0` nhưng thiếu V-Key khi profile chọn AIHub, hoặc nhà cung cấp đã chọn lỗi: `rules_only_fallback`, `degraded=true`, `ai_score/ai_confidence=null`, `final_score=rule_score`, `error_code` đã chuẩn hóa. Không tự AI retry. - AI thành công: `hybrid`, `degraded=false`, `final_score=clamp(round(rule_score*rule_weight + ai_score*ai_weight), 0, 100)`; trả breakdown, reasoning/evidence/confidence được giới hạn. Cùng raw signals có thể ảnh hưởng cả rules lẫn AI; không gửi app rules/points vào AI prompt để hạn chế chấm hai lần. - Bug/code/DB failure thành `failed`, không giả là AI fallback. AI profile gồm provider, versioned model, prompts và timeout/token limits, không sở hữu app rules hoặc weights. Profile cũ mặc định AIHub; đổi provider cần phiên bản mới, ghi audit actor/version/provider cùng transaction, không ghi secret. Medium two-pass là mặc định; Light/Heavy chỉ bổ sung khi có nhu cầu và được xác minh với nhà cung cấp. ## Key, session và delivery - Platform có một active tenant key/tenant dùng chung cho LS và feature sau này: provisioner secret server-side cấp qua `POST /v1/platform/tenant-keys`, raw trả một lần, DB chỉ có verifier/digest; cấp trùng 409. Nếu response mất sau commit, admin CLI rotate cũ/thay mới atomically; không tự rotate khi POST bị replay. - `X-Tenant-VKey` chỉ giữ trong RAM của current scoring task và forward `Authorization: Bearer` tới AIHub khi profile chọn AIHub. OpenAI-compatible dùng URL được lưu theo phiên bản profile; API key tùy chọn vẫn cấu hình ở server và chỉ được gửi tới URL đã cấu hình cho key đó, không tự gửi sang URL mới nhập ở Web. HTTP chỉ cho loopback khi development, ngoài development yêu cầu HTTPS. Chưa xác nhận quản lý key cho các URL production khác nhau. Không persist/log/trace/error/browser bất kỳ raw secret nào. V-Key không phải platform authentication. Admin session độc lập, password hash, CSRF cho mutation, cookie bảo vệ, logout/expiry và audit thay đổi config/key không kèm secrets. - Một backend process + asyncio tasks. Startup trước readiness terminalize pending/running cũ thành `failed/worker_interrupted`; task exception cũng terminalize. V-Key không durable nên không resume AI sau crash; caller muốn làm lại dùng UUID mới. - Terminal transition và PostgreSQL outbox event commit cùng transaction. Dispatcher gửi HMAC-signed webhook tới một Omni URL cố định với event ID/timestamp, bounded retry và dead-letter. Omni xác minh chữ ký/chống replay/dedupe, GET chi tiết và áp dụng writeback theo run mới nhất của `(tenant, subject)`; delivery replay không chấm AI lại. Không broker, dynamic subscription, polling scheduler tại Omni. ## Privacy và nghiệm thu - Scheduled retention xóa run, snapshot, fingerprint, evidence và outbox liên quan sau 90 ngày; test xóa thật. Không lưu thông tin nhạy cảm trong payload webhook/log. Chính sách xóa tenant/offboarding và vòng đời backup phải chốt trước production; xóa DB không có nghĩa xóa ngay backup. - Fixture chung tối thiểu: admission/replay và 409, rules-only/hybrid/fallback, failed run, webhook HMAC và Omni GET/writeback. Generated OpenAPI là nguồn schema; không lặp lại bộ schema cho từng projection không có consumer. - Cần xác minh bằng sandbox AIHub về endpoint, strict structured output, model, timeout, quota và lỗi; Omni phải xác nhận fixture, signature/replay và writeback. Hosting, TLS, secret injection, backup/restore và alert destination chốt theo nơi deploy. Chưa khẳng định production-ready khi các điều đó chưa được kiểm chứng.