Technical Documentation

# Ultra Description Pipeline

Hệ thống sinh mô tả sản phẩm bằng AI cho Canifa — từ Sheets đến Chatbot. Tài liệu này giải thích tại sao pipeline chia làm 2 giai đoạn AI riêng biệt, cách mỗi endpoint hoạt động, và cách tích hợp toàn bộ luồng.

End-to-end pipeline — 5 bước tuần tự

### Ecom chuẩn bị sản phẩm mới trên Google Sheets

Team Ecom/Thiết kế tạo danh sách SP mới: Mã SP, Link ảnh, Tên SP, Chất liệu, Form, Tính năng. Tại bước này chưa có mô tả marketing nào — đây là dữ liệu thô từ nhà sản xuất.

#### Đây là bước nguồn — ground truth duy nhất

Chất liệu, chứng chỉ, thành phần — chỉ có ở đây, trước khi lên web. AI không được phép bịa bất kỳ thông số nào. Dữ liệu từ bước này là mandatory input cho cả 2 lần AI generate.

### AI lần 1 Sinh mô tả marketing cho web — `POST /generate-web-desc`

AI Vision nhìn ảnh SP → AI Writer đọc toàn bộ data từ Sheets → sinh mô tả marketing cho khách hàng đọc trên `canifa.com`. Output ghi ngược về cột "AI Description" trong Sheets.

Vì sao cần bước này? Magento yêu cầu có mô tả trước khi import SP. Không có mô tả → SP không thể lên web. Bước này giải phóng team Ecom khỏi việc viết tay hàng chục mô tả/ngày.

### Ecom review và import lên Magento

Team Ecom copy mô tả từ Sheets vào Magento (hoặc qua n8n tự động). SP live trên `canifa.com`. Khách hàng đọc được mô tả marketing hoàn chỉnh.

### Magento auto-sync → StarRocks DB

SP được ETL vào bảng `magento_product_dimension_with_text_embedding`. Từ đây các hệ thống nội bộ (chatbot AI, dashboard) có thể truy vấn theo `magento_ref_code`.

Đây là prerequisite bắt buộc cho AI lần 2 API `/generate` tra StarRocks để lấy ảnh, tên SP, chất liệu, giới tính, dòng SP. Nếu SP chưa sync → API báo lỗi "không tìm thấy sản phẩm".

### AI lần 2 Sinh Ultra Description cho AI chatbot — `POST /generate`

AI tra DB lấy đầy đủ context → Vision nhìn ảnh → Writer sinh 28 trường structured JSON. Output lưu vào PostgreSQL. AI chatbot đọc data này để tư vấn khách hàng chính xác, đa chiều.

Endpoint này đã có sẵn và production-ready Hỗ trợ batch generate, retry logic, round-robin API key, self-critique. Chỉ cần SP đã có trong StarRocks.
So sánh 2 lần AI generate

###
AI lần 1: Mô tả cho web Mới

Đầu ra phục vụ | Khách hàng trên web
Input yêu cầu | Ảnh + data từ Sheets
Output format | Text marketing (4 trường)
Cần StarRocks DB? | ❌ Không
Lưu kết quả vào | Google Sheets (qua n8n)
Trigger | SP mới, chưa lên web

###
AI lần 2: Ultra Desc cho AI Đã có

Đầu ra phục vụ | AI chatbot tư vấn
Input yêu cầu | Mã SP (tra DB tự động)
Output format | 28 trường JSON structured
Cần StarRocks DB? | ✅ Bắt buộc
Lưu kết quả vào | PostgreSQL
Trigger | SP đã live, đã sync DB

Business logic — câu hỏi trung tâm

## Tại sao không dùng 1 AI chạy hết?

Câu trả lời nằm ở vòng đời của sản phẩm và ai là người đọc output. Hai lần AI phục vụ hai audience hoàn toàn khác nhau, ở hai thời điểm khác nhau trong vòng đời SP.

Lý do 1: Timing constraint — thứ tự bắt buộc

Hai giai đoạn AI không thể chạy đồng thời vì phụ thuộc vào nhau theo chuỗi tuần tự bắt buộc trong hệ thống.

AI lần 1 xảy ra TRƯỚC khi SP tồn tại trong bất kỳ DB nào

SP mới chỉ nằm trong Sheets. Không có StarRocks record, không có Magento ID, không có vector embedding. AI lần 2 (`/generate`) tra StarRocks để lấy data — nhưng record đó chưa tồn tại. Nếu gọi lần 2 lúc này → 404 không tìm thấy SP.

AI lần 2 cần SP đã "chín" trong hệ thống

Để sinh 28 trường chính xác (giá, category, related products cho cross-sell...), AI lần 2 cần đọc từ StarRocks với đầy đủ metadata. Dữ liệu này chỉ có sau khi SP đã live trên Magento và đã sync DB — thường cách AI lần 1 vài giờ đến vài ngày.

Lý do 2: Output format khác nhau hoàn toàn

Khách hàng đọc bằng mắt. AI chatbot đọc bằng code. Hai audience này cần hai format đầu ra căn bản khác nhau.

AI lần 1 → Text tự nhiên

Khách cần văn xuôi hấp dẫn, có cảm xúc, storytelling. "Form regular fit vừa vặn, tôn dáng mà không gò bó..."

Đặt vào Magento description field → render trực tiếp cho khách đọc trên web.

AI lần 2 → JSON structured

Chatbot cần data có cấu trúc để tư vấn theo ngữ cảnh: "phối với quần gì?", "dịp nào mặc?", "size gì với chiều cao 1m70?"

28 trường riêng biệt → chatbot query trực tiếp từng trường phù hợp với câu hỏi của khách.

Lý do 3: Scope và depth khác nhau

AI lần 1 viết mô tả cho 1 SP cụ thể. AI lần 2 cần hiểu SP trong bối cảnh toàn bộ catalog.

Trường `cross_sell` trong Ultra Desc gợi ý "Quần Chinos Slim, Giày Loafer Da, Thắt Lưng Da Reversible" — chatbot cần biết Canifa đang bán những sản phẩm đó thật sự. AI lần 2 đọc từ StarRocks catalog nên có thể gợi ý đúng dòng SP thực tế đang có hàng. AI lần 1 ở giai đoạn Sheets không có thông tin này.

### Tóm lại — Design decision đúng

Tách thành 2 pipeline độc lập là intentional architecture, không phải over-engineering. Mỗi pipeline có trigger riêng, input source riêng, output destination riêng, và phục vụ một người dùng hoàn toàn khác nhau. Gộp lại thành 1 sẽ tạo ra hard dependency khiến SP không thể lên web cho đến khi toàn bộ AI pipeline (kể cả Ultra Desc phức tạp hơn) chạy xong.

Endpoint mới

## Sinh mô tả marketing cho web

Dành cho team Ecom gọi khi SP mới, chưa cần có trong DB. AI Vision nhìn ảnh + AI Writer đọc data Sheets → sinh mô tả khách hàng đọc trên web.

2 pha AI
POST /api/product-desc/generate-web-desc
Request body — input fields

Field | Kiểu | Bắt buộc? | Mô tả & Cột Sheets
--- | --- | --- | ---
`ma_san_pham` | string | Bắt buộc | Mã SP nội bộ. VD: `5TS25S021` — cột Mã sản phẩm
`ten_san_pham` | string | Bắt buộc | Tên đầy đủ — cột Tên sản phẩm
`image_url` | string | Bắt buộc | URL ảnh public (Vision AI download trực tiếp) — cột Link ảnh
`chat_lieu` | string | Nên có | Chất liệu chính xác. AI dùng verbatim, không đoán lại — cột Đặc tính chất liệu
`form` | string | Optional | Slim / Regular / Oversize — cột Form
`hoan_canh` | string | Optional | Dịp mặc mong muốn — cột Hoàn cảnh
`tinh_nang` | string | Optional | Co giãn, kháng khuẩn, thấm hút... — cột Tính năng
`featuring` | string | Optional | Điểm nhấn thiết kế đặc biệt — cột Featuring
`chung_chi` | string | Optional | OEKO-TEX, BCI Cotton... — cột Chứng chỉ
`huong_dan_su_dung` | string | Optional | Giặt ủi, bảo quản — cột Hướng dẫn sử dụng
`mo_ta_chung` | string | Optional | Mô tả thô ban đầu. AI enrich thêm — cột Mô tả chung
`gioi_tinh` | string | Optional | Nam / Nữ / Unisex / Bé trai / Bé gái

Response — output fields
{ "status": "success", "ma_san_pham": "5TS25S021", "ten_san_pham": "Áo Polo Nam Cotton USA Cổ Bẻ Tay Ngắn", // ── 4 trường output chính ──────────────────────────── "mo_ta_web": "Form regular fit vừa vặn, tôn dáng mà không gò bó. Thiết kế cổ bẻ tối giản lịch sự, phù hợp đi làm lẫn dạo phố cuối tuần. Cotton USA 100% mềm mại, thấm hút tốt, giữ form sau nhiều lần giặt...", "mo_ta_ngan": "Polo nam cotton USA, form regular, cổ bẻ thanh lịch", "dac_diem_noi_bat": [ "Cotton USA 100% mềm mại, thấm hút tốt", "Form Regular Fit tôn dáng, không gò bó", "Cổ bẻ lịch sự, hợp office lẫn casual" ], "huong_dan_bao_quan": "Giặt máy ≤30°C, lộn trái, không dùng tẩy, phơi bóng râm", // ── Debug / monitoring ─────────────────────────────── "elapsed_s": 8.3, "vision_data": { /* raw Vision output — bỏ đi nếu không cần debug */ } }
Lưu ý quan trọng về chất liệu Nếu có `chat_lieu` → AI dùng verbatim, không thêm không bớt. Nếu không có → AI ghi "Theo thông tin nhà sản xuất", không được đoán. Đây là hard rule để tránh thông tin sai lệch cho khách hàng.
Ví dụ cURL
curl -X POST http://localhost:5000/api/product-desc/generate-web-desc \ -H "Content-Type: application/json" \ -d '{ "ma_san_pham": "5TS25S021", "ten_san_pham": "Áo Polo Nam Cotton USA Cổ Bẻ Tay Ngắn", "image_url": "https://media.canifa.com/abc.jpg", "chat_lieu": "Cotton USA 100%", "form": "Regular Fit", "hoan_canh": "Đi làm, Dạo phố", "tinh_nang": "Co giãn nhẹ, thấm hút mồ hôi" }'
Endpoint hiện có — Production ready

## Ultra Description cho AI chatbot

Sinh 28 trường structured JSON từ mã SP. SP bắt buộc phải có trong StarRocks DB. Pipeline 3 pha: Vision → Writer → Self-critique.

POST /api/product-desc/generate
Request body — chỉ cần mã SP
// Gửi 1 trong 2 trường sau: { "magento_ref_code": "5TS25S021-SR079" } // hoặc { "internal_ref_code": "5TS25S021" }
Tại sao chỉ cần mã SP? Hệ thống tự tra StarRocks: tên SP, tất cả URL ảnh, chất liệu, giới tính, dòng SP, category, giá. AI lần 2 có context đầy đủ hơn nhiều so với AI lần 1 nhờ dữ liệu đã được enriched qua Magento pipeline.
AI pipeline bên trong — 3 pha
Pha 1 · Vision
Llama 4 Scout nhìn ảnh → extract 9 trường thô: màu sắc, form, đặc trưng thiết kế...
Pha 2 · Writer
GPT-OSS 120B kết hợp Vision output + DB data → sinh đủ 28 trường JSON với giọng Stylist Canifa
Pha 3 · Critique
Self-review: kiểm tra chất liệu không bịa, FAQ có thực tế, phối đồ có logic. Tự sửa nếu cần.
Các API phụ trợ đã có sẵn

Method | Endpoint | Mô tả
--- | --- | ---
`GET` | `/overview` | Stats: tổng SP, đã generate, đã duyệt, missing
`GET` | `/list` | Danh sách SP kèm trạng thái mô tả (phân trang)
`GET` | `/saved/{code}` | Lấy mô tả đã lưu theo mã SP
`POST` | `/generate` | Generate Ultra Desc cho 1 SP
`POST` | `/approve` | Duyệt / hủy duyệt mô tả
`POST` | `/batch-generate` | Generate hàng loạt (background task)
`POST` | `/batch-generate-all` | Generate toàn bộ SP còn thiếu (max 500)
`POST` | `/saved/{code}/update` | Edit nội dung JSON thủ công
`POST` | `/saved/{code}/rewrite-field` | AI viết lại 1 trường cụ thể
`POST` | `/sync-material` | Patch chất liệu từ StarRocks vào existing records
`DELETE` | `/saved/{code}` | Xóa mô tả đã lưu

28 trường — 6 nhóm logic

AI chatbot đọc toàn bộ 28 trường này để trả lời câu hỏi theo từng ngữ cảnh: styling, size, chất liệu, dịp mặc. Mỗi nhóm phục vụ một intent khác nhau.

Nhóm 1 — Thông tin cơ bản (5 trường)
ten_san_pham
Tên sản phẩm ngắn gọn, chuẩn
Áo Polo Nam Cổ Bẻ Tay Ngắn
tagline
1 câu slogan hấp dẫn, gây nhớ
Lịch lãm không cần cố gắng
mo_ta_chinh
3 câu: form dáng + thiết kế + tác dụng lên cơ thể
Form regular fit vừa vặn, tôn dáng mà không gò bó...
phong_cach
1-3 từ khóa phong cách
Smart Casual, Minimalist
tags
5 tags phân loại, cách dấu phẩy
polo, nam, công sở, smart casual, tay ngắn
Nhóm 2 — Chất liệu & bảo quản (3 trường)
chat_lieu
Chất liệu vải — dùng đúng từ DB hoặc Sheets, KHÔNG đoán
Cotton USA 100%
tinh_nang_vai
Tính năng nổi bật của vải
Co giãn 4 chiều, kháng khuẩn, thấm hút tốt
huong_dan_bao_quan
Hướng dẫn giặt/bảo quản cụ thể
Giặt máy ≤30°C, không dùng tẩy, phơi bóng râm
Nhóm 3 — Đối tượng mục tiêu (4 trường)
do_tuoi
Khoảng tuổi phù hợp
25-40
gioi_tinh
Nam / Nữ / Unisex
Nam
loi_song
Mô tả lối sống khách hàng mục tiêu
Người đàn ông hiện đại, coi trọng phong cách nhưng không cầu kỳ
tinh_cach
Tính cách phù hợp
Tự tin, chỉn chu, thực dụng
Nhóm 4 — Dịp mặc & styling (6 trường)
dip_mac
4+ dịp mặc cụ thể, ngăn bởi ·
Đi làm · Cafe cuối tuần · Gặp đối tác
phoi_do
3 combo phối đồ, ngăn bởi |
Chinos Beige + Loafer → Thanh lịch | Jean Slim + Sneaker → Năng động
nguyen_tac_phoi_do
Lý do và quy tắc phối đồ, tại sao combo đó hoạt động
Mix cùng độ fit — quần slim hoặc straight để cân bằng
tranh_phoi_cung
Những item KHÔNG NÊN phối — negative constraints
Tránh quần jogger nỉ quá thể thao, lệch phong cách
layer
Gợi ý layering theo mùa
Mặc đơn mùa hè. Layer cùng Bomber mùa thu
mua
Mùa phù hợp nhất
Xuân Hè
Nhóm 5 — FAQ (6 trường)
faq_1_q / faq_1_a
FAQ về form dáng, hack dáng
Áo này có hack dáng không? → Phom vai suông che nhẹ bắp tay...
faq_2_q / faq_2_a
FAQ về size, fit
1m70 65kg mặc size gì? → Nên chọn size M...
faq_3_q / faq_3_a
FAQ về mix & match
Phối với quần gì đẹp nhất? → Combo: Chinos + Loafer...
Nhóm 6 — Hỗ trợ bán hàng (4 trường)
hook_quang_cao
1 câu hook gây tò mò, dùng cho ads
Chiếc polo mà anh em mặc đi làm rồi đi cafe luôn
cross_sell
2-3 SP nên mua kèm — từ catalog thực tế
Quần Chinos Slim, Giày Loafer Da, Thắt Lưng Da
ly_do_mua
3 lý do nên mua, ngăn bởi |
Form hack dáng | Mặc đi làm hay đi chơi đều ổn | Cổ bẻ lịch sự
luu_y_size
Lưu ý chọn size cụ thể cho SP này
Nếu nặng trên 75kg nên lên 1 size
Mapping cột Google Sheets → API field

Cột trong Sheets | → API field | Gửi API? | Ghi chú
--- | --- | --- | ---
Mã sản phẩm | `ma_san_pham` | Bắt buộc | Unique ID
Tên sản phẩm | `ten_san_pham` | Bắt buộc | Tên đầy đủ
Link ảnh | `image_url` | Bắt buộc | URL public
Đặc tính chất liệu | `chat_lieu` | Nên có | AI dùng verbatim
Form | `form` | Optional |
Hoàn cảnh | `hoan_canh` | Optional |
Featuring | `featuring` | Optional | Điểm nhấn thiết kế
Tính năng | `tinh_nang` | Optional |
Chứng chỉ | `chung_chi` | Optional | OEKO-TEX, BCI...
Thành phần | `thanh_phan` | Optional |
Hướng dẫn sử dụng | `huong_dan_su_dung` | Optional |
Mô tả chung | `mo_ta_chung` | Optional | AI enrich thêm
Ngày check | — | Không gửi | Quản lý nội bộ
Ecom check / Thiết kế check | — | Không gửi | Workflow nội bộ
Số ký tự | — | Không gửi | Tự tính từ output
AI Description | — | Output ghi về | n8n ghi kết quả vào đây

n8n workflow — tự động hóa Sheets ↔ API
n8n

### Trigger: Schedule hoặc Webhook

n8n chạy theo lịch (VD: mỗi 30 phút) hoặc trigger khi Sheets được cập nhật. Đọc tất cả dòng chưa có giá trị trong cột "AI Description".

API

### Loop: gọi `/generate-web-desc` cho từng SP

Với mỗi dòng: map cột Sheets → request body → POST → nhận response JSON. Recommend batch size 5-10 SP để tránh timeout.

POST /api/product-desc/generate-web-desc Body: { ma_san_pham, ten_san_pham, image_url, chat_lieu, form, ... } → Response: { mo_ta_web, mo_ta_ngan, dac_diem_noi_bat, huong_dan_bao_quan }
✓

### Ghi kết quả ngược về Sheets

n8n update cột "AI Description" với `mo_ta_web`. Ecom review → approve → copy vào Magento. Cột "Số ký tự" có thể tự tính bằng `=LEN()`.

Kết quả cuối cùng của toàn bộ pipeline Ecom chỉ cần điền ảnh + info cơ bản → AI tự sinh mô tả web → lên Magento → sync DB → AI chatbot có đầy đủ 28 trường để tư vấn khách hàng chuyên nghiệp.