GPT Review Round 2 — Điều 24 Luật Nhãn v1.1 (2026-03-15)
GPT Review Round 2 — Điều 24: Luật Nhãn v1.1
Ngày: 2026-03-15 Nguồn: Incomex Hội đồng AI (GPT) Phạm vi: Phản biện kiến trúc round 2, KHÔNG sửa luật gốc
Kết luận tổng quát
🟢 Đồng ý mạnh hơn round 1. v1.1 đã sửa đúng các điểm lớn: thêm chiều 6, conflict resolution, tree constraints, migration per field, permissions, và function chung.
Tuy vậy còn 4 điểm cần sửa/chốt và 4 điểm nên cân nhắc trước khi coi là đủ chắc để triển khai rộng.
1. 16 điểm đã sửa — có điểm nào chưa đúng/chưa đủ?
🟢 Đồng ý
- Thêm chiều 6: đúng
- Conflict resolution: đúng hướng
- 1 function chung: đúng hướng Assembly First hơn
- AI Agent read-only taxonomy: đúng
- Migration per field: đúng
depth<=2,root_no_parent,no_self_parent,replaced_by, compound index, JSONB CHECK: đều hợp lý
🔴 Cần sửa 1
no_self_parent không đủ để chống cycle. Nó chỉ chặn A→A, nhưng không chặn A→B→A.
Đề xuất:
- Giữ CHECK hiện tại
- Bổ sung trigger/validation chống cycle thật bằng recursive query khi INSERT/UPDATE parent_id
🔴 Cần sửa 2
replaced_by hiện mới là FK tới taxonomy(code), nhưng chưa chặn:
- label thay thế thuộc facet khác
- label deprecated trỏ về chính nó
- chuỗi deprecated vòng lặp
Đề xuất:
replaced_by != code- same-facet với label gốc
- nếu label bị deprecated mà còn được dùng → DOT báo migrate bắt buộc
2. Chiều 6 "Phục vụ ai?" — 6 giá trị đã đủ chưa? Cần phân cấp không?
🟢 Đồng ý
6 giá trị hiện tại đủ để bắt đầu.
🟡 Nên cân nhắc
Chiều này nên giữ phẳng ở v1.1. Chưa cần cha-con.
Lý do
- Audience là chiều vận hành, ít giá trị, cần dễ chọn
- Nếu phân cấp sớm sẽ tăng ambiguity: ví dụ Admin có thuộc System không?
- Giá trị hiện tại đủ bao phủ nội bộ + business đầu kỳ
Góc khuất nhỏ
AUD-USR và AUD-CUS có thể bị đội vận hành dùng lẫn nhau.
Đề xuất:
- Giữ phẳng
- Viết mô tả phân biệt cứng:
AUD-USR= user nội bộ/end-user của hệ thống nội bộAUD-CUS= khách hàng bên ngoài
3. Conflict resolution — còn edge case nào chưa cover?
🟡 Chưa cover hết
Logic hiện tại tốt hơn v1.0, nhưng còn 2 edge case.
🔴 Edge case A — nhiều rule cùng chiều single, cùng priority, cùng rule_type
Hiện luật nói: đồng priority → system_issues. Đúng. Nhưng cần chốt thêm: không insert nhãn tạm thời nào cho chiều đó. Nếu không sẽ có trạng thái nửa đúng nửa sai.
🔴 Edge case B — entity thuộc 2 collections?
Với thiết kế hiện tại, 1 record DB chỉ thuộc 1 table. Nên “entity thuộc 2 collections” không phải case DB-native, mà là case cùng business object xuất hiện qua nhiều representation.
Đề xuất:
- Không giải bài toán này ở conflict engine v1.1
- Coi đó là bài toán identity/canonical entity ở Luật Chống Trùng / mapping, không phải label rule
🟡 Edge case C — collection rule + keyword rule cùng chiều multiple
Hiện đang merge. Đồng ý. Nhưng nên thêm nguyên tắc: nếu keyword chỉ match label cha và collection đã gán label con cùng nhánh thì bỏ label cha để tránh dư thừa.
4. fn_auto_label_assignment() — mỗi table schema khác nhau, xử lý thế nào?
🔴 Cần sửa
Bản mô tả hiện tại dùng trực tiếp NEW.name, NEW.description. Điều này không an toàn vì không phải table nào cũng có đủ 2 field này.
Đề xuất tốt hơn
Function chung vẫn giữ, nhưng phải đọc metadata từ label_rules.condition hoặc meta_catalog để biết table đó dùng field nào làm nguồn text.
Ví dụ:
- ưu tiên field list từ config:
['name','title','description','purpose','content'] - build text nguồn bằng dynamic extraction an toàn
- table không có
descriptionthì chỉ dùngname/title
Nguyên tắc: function chung, nhưng input field mapping phải là data-driven, không hardcode NEW.description.
5. CHECK same-facet bằng TRIGGER — có cách constraint thuần không?
🟡 Gần như không đẹp bằng constraint thuần
Với self-reference và cần so sánh NEW.facet_id với parent.facet_id, PostgreSQL CHECK thường không phù hợp vì CHECK không nên query bảng khác để enforce quan hệ này.
Kết luận
Giữ trigger là chấp nhận được. Đây là một trong số ít chỗ trigger hợp lý.
🟡 Nên cân nhắc
Nếu muốn giảm rủi ro trigger logic, có thể dùng composite key kiểu:
(id, facet_id)unique trên taxonomy- FK
(parent_id, facet_id)references(id, facet_id)
Đây là cách “constraint-like” hơn trigger.
Tôi nghiêng về phương án này hơn trigger nếu đội triển khai chịu được độ phức tạp schema tăng nhẹ.
6. entity_labels.entity_code là text tự do — validate tồn tại thật được không?
🔴 Đây là lỗ hổng lớn nhất còn lại
Hiện entity_code là soft reference. Điều này dễ tạo:
- code typo
- code đã retire/xoá
- code không tồn tại nhưng label vẫn gán
Kết luận
Không có FK chuẩn vì entity nằm rải ở 17+ bảng. Chấp nhận soft reference là thực tế, nhưng phải bù bằng governance mạnh.
Đề xuất
- Giữ
entity_codetext - Bắt buộc
dot-label-validatequét existence - Tạo thêm v_all_entity_codes hoặc table index toàn cục từ
meta_catalog/registries để check existence nhanh - UNIQUE/validation ở DB có thể làm qua trigger: entity_code phải nằm trong
v_all_entity_codes
Nếu không muốn trigger DB lúc đầu, ít nhất phải có DOT validate + system_issues + CI/audit coverage.
7. Còn góc khuất nào khi scale 5000 entities + 1200 labels + 20000 entity_labels?
🟢 Về PostgreSQL
Khối lượng này còn nhỏ. PG xử lý tốt nếu index đúng.
🟡 Về bottleneck thực tế
Bottleneck lớn hơn sẽ nằm ở Directus Admin và UX, không phải PG.
Điểm cần chú ý
- Directus junction UX với 20K rows sẽ nặng nếu render raw M2M khắp nơi
- Tree View taxonomy 1200 labels vẫn chịu được, nhưng cần sort/filter rõ
- Coverage report và “Cùng nhóm” nên query qua SQL tối ưu, không lồng nhiều API calls
Đề xuất
- index thêm trên
entity_labels(entity_code, label_code)đã có unique rồi, tốt - thêm index phụ
taxonomy(status, facet_id, depth)nếu Directus lọc nhiều - query “Cùng nhóm” nên limit + rank, không trả full union vô hạn
8. "Cùng nhóm" — union hay intersection?
🔴 Cần chốt luật
Nếu 1 entity có 3 domain labels, union sẽ cho tập rộng nhưng dễ nhiễu. Intersection cho tập rất chính xác nhưng dễ rỗng.
Đề xuất
Mặc định dùng UNION có xếp hạng (scored union), KHÔNG dùng intersection thuần.
Cách làm
- lấy union của entities cùng ít nhất 1 label domain
- tính điểm theo số nhãn domain trùng:
- trùng 3 nhãn = điểm cao
- trùng 2 nhãn = trung bình
- trùng 1 nhãn = thấp
- sort giảm dần, chỉ hiện top N
Lý do
- Intersection thuần sẽ quá chặt, nhiều lúc ra 0
- Union thuần sẽ quá loãng
- Scored union là cân bằng tốt nhất
Phán quyết theo mức độ
🔴 Cần sửa
- Chống cycle thật, không chỉ
no_self_parent fn_auto_label_assignment()phải data-driven theo field mapping, không gọi cứngNEW.description- Chốt rõ
entity_labels.entity_codevalidation strategy - Chốt “Cùng nhóm” = scored union, không để mơ hồ
🟡 Nên cân nhắc
- Giữ audience phẳng, không phân cấp ở v1.1
- Dùng composite FK
(parent_id, facet_id)thay triggersame-facetnếu muốn schema mạnh hơn - Thêm ràng buộc cho
replaced_by - Loại bỏ label cha dư thừa khi đã có label con cùng nhánh
🟢 Đồng ý
- Chiều 6 là bổ sung đúng
- Conflict resolution tổng thể đã đi đúng hướng
- 1 function chung tốt hơn N logic rời rạc
- Permissions read-only cho AI là rất đúng
- Không dùng
ltreelà hợp lý với 3 tầng cố định
Kết luận cuối
🟢 v1.1 đã đủ tốt để tiếp tục tiến gần triển khai. 🔴 Nhưng chưa nên coi là khóa kiến trúc cuối cùng nếu chưa xử lý 4 điểm: cycle thật, field mapping trong function chung, validation entity_code, và luật "Cùng nhóm".