heraspec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +57 -0
- package/bin/heraspec.js +3803 -0
- package/bin/heraspec.js.map +7 -0
- package/dist/core/templates/skills/CHANGELOG.md +117 -0
- package/dist/core/templates/skills/README-template.md +58 -0
- package/dist/core/templates/skills/README.md +36 -0
- package/dist/core/templates/skills/content-optimization-skill.md +104 -0
- package/dist/core/templates/skills/data/charts.csv +26 -0
- package/dist/core/templates/skills/data/colors.csv +97 -0
- package/dist/core/templates/skills/data/landing.csv +31 -0
- package/dist/core/templates/skills/data/pages-proposed.csv +22 -0
- package/dist/core/templates/skills/data/pages.csv +10 -0
- package/dist/core/templates/skills/data/products.csv +97 -0
- package/dist/core/templates/skills/data/prompts.csv +24 -0
- package/dist/core/templates/skills/data/stacks/flutter.csv +53 -0
- package/dist/core/templates/skills/data/stacks/html-tailwind.csv +56 -0
- package/dist/core/templates/skills/data/stacks/nextjs.csv +53 -0
- package/dist/core/templates/skills/data/stacks/react-native.csv +52 -0
- package/dist/core/templates/skills/data/stacks/react.csv +54 -0
- package/dist/core/templates/skills/data/stacks/svelte.csv +54 -0
- package/dist/core/templates/skills/data/stacks/swiftui.csv +51 -0
- package/dist/core/templates/skills/data/stacks/vue.csv +50 -0
- package/dist/core/templates/skills/data/styles.csv +59 -0
- package/dist/core/templates/skills/data/typography.csv +58 -0
- package/dist/core/templates/skills/data/ux-guidelines.csv +100 -0
- package/dist/core/templates/skills/documents-skill.md +114 -0
- package/dist/core/templates/skills/e2e-test-skill.md +119 -0
- package/dist/core/templates/skills/integration-test-skill.md +118 -0
- package/dist/core/templates/skills/module-codebase-skill.md +110 -0
- package/dist/core/templates/skills/scripts/CODE_EXPLANATION.md +394 -0
- package/dist/core/templates/skills/scripts/SEARCH_ALGORITHMS_COMPARISON.md +421 -0
- package/dist/core/templates/skills/scripts/SEARCH_MODES_GUIDE.md +238 -0
- package/dist/core/templates/skills/scripts/core.py +385 -0
- package/dist/core/templates/skills/scripts/search.py +73 -0
- package/dist/core/templates/skills/suggestion-skill.md +118 -0
- package/dist/core/templates/skills/templates/accessibility-checklist.md +40 -0
- package/dist/core/templates/skills/templates/example-prompt-full-theme.md +333 -0
- package/dist/core/templates/skills/templates/page-types-guide.md +338 -0
- package/dist/core/templates/skills/templates/pages-proposed-summary.md +273 -0
- package/dist/core/templates/skills/templates/pre-delivery-checklist.md +42 -0
- package/dist/core/templates/skills/templates/prompt-template-full-theme.md +313 -0
- package/dist/core/templates/skills/templates/responsive-design.md +40 -0
- package/dist/core/templates/skills/ui-ux-skill.md +584 -0
- package/dist/core/templates/skills/unit-test-skill.md +111 -0
- package/dist/index.js +1736 -0
- package/package.json +71 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# Giải Thích Cách Hoạt Động Của UI/UX Builder Search Engine
|
|
2
|
+
|
|
3
|
+
## Tổng Quan
|
|
4
|
+
|
|
5
|
+
UI/UX Builder là một search engine sử dụng thuật toán **BM25** để tìm kiếm thông tin thiết kế từ các database CSV. Hệ thống gồm 2 file chính:
|
|
6
|
+
|
|
7
|
+
1. **`core.py`** - Core engine với BM25 algorithm và các hàm search
|
|
8
|
+
2. **`search.py`** - CLI interface để sử dụng từ command line
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 📁 Cấu Trúc File
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
scripts/
|
|
16
|
+
├── core.py # Core engine (BM25, search functions)
|
|
17
|
+
└── search.py # CLI interface
|
|
18
|
+
|
|
19
|
+
data/
|
|
20
|
+
├── styles.csv # 57 UI styles
|
|
21
|
+
├── colors.csv # 95 color palettes
|
|
22
|
+
├── typography.csv # 56 font pairings
|
|
23
|
+
├── pages.csv # 9+ page types
|
|
24
|
+
├── products.csv # Product recommendations
|
|
25
|
+
├── landing.csv # Landing page patterns
|
|
26
|
+
├── charts.csv # Chart types
|
|
27
|
+
├── ux-guidelines.csv # 98 UX guidelines
|
|
28
|
+
└── stacks/ # 8 tech stack guidelines
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🔍 Cách Hoạt Động Chi Tiết
|
|
34
|
+
|
|
35
|
+
### 1. File `core.py` - Core Engine
|
|
36
|
+
|
|
37
|
+
#### A. Configuration (Dòng 13-82)
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
CSV_CONFIG = {
|
|
41
|
+
"style": {
|
|
42
|
+
"file": "styles.csv",
|
|
43
|
+
"search_cols": ["Style Category", "Keywords", ...], # Cột để search
|
|
44
|
+
"output_cols": ["Style Category", "Type", ...] # Cột để trả về
|
|
45
|
+
},
|
|
46
|
+
...
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Chức năng:**
|
|
51
|
+
- Định nghĩa các domain (style, color, typography, pages, etc.)
|
|
52
|
+
- Mỗi domain có:
|
|
53
|
+
- `file`: Tên file CSV
|
|
54
|
+
- `search_cols`: Các cột dùng để tìm kiếm
|
|
55
|
+
- `output_cols`: Các cột trả về trong kết quả
|
|
56
|
+
|
|
57
|
+
**Ví dụ:** Khi search "minimalism" trong domain "style":
|
|
58
|
+
- Tìm trong các cột: "Style Category", "Keywords", "Best For", "Type"
|
|
59
|
+
- Trả về: "Style Category", "Type", "Keywords", "Primary Colors", etc.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
#### B. BM25 Class (Dòng 86-145)
|
|
64
|
+
|
|
65
|
+
**BM25** là thuật toán ranking phổ biến trong information retrieval, tốt hơn TF-IDF.
|
|
66
|
+
|
|
67
|
+
**Các phương thức:**
|
|
68
|
+
|
|
69
|
+
1. **`__init__(k1=1.5, b=0.75)`** (Dòng 89-97)
|
|
70
|
+
- `k1`: Điều chỉnh tần suất từ (term frequency)
|
|
71
|
+
- `b`: Điều chỉnh độ dài document
|
|
72
|
+
- Khởi tạo các biến: corpus, doc_lengths, idf, doc_freqs
|
|
73
|
+
|
|
74
|
+
2. **`tokenize(text)`** (Dòng 99-102)
|
|
75
|
+
```python
|
|
76
|
+
# Input: "Minimalism, Glassmorphism & Dark Mode"
|
|
77
|
+
# Output: ["minimalism", "glassmorphism", "dark", "mode"]
|
|
78
|
+
```
|
|
79
|
+
- Chuyển text thành lowercase
|
|
80
|
+
- Loại bỏ punctuation
|
|
81
|
+
- Chỉ giữ từ có > 2 ký tự
|
|
82
|
+
|
|
83
|
+
3. **`fit(documents)`** (Dòng 104-121)
|
|
84
|
+
- **Bước 1:** Tokenize tất cả documents
|
|
85
|
+
- **Bước 2:** Tính độ dài mỗi document và độ dài trung bình
|
|
86
|
+
- **Bước 3:** Tính document frequency (số documents chứa từ)
|
|
87
|
+
- **Bước 4:** Tính IDF (Inverse Document Frequency):
|
|
88
|
+
```python
|
|
89
|
+
idf[word] = log((N - freq + 0.5) / (freq + 0.5) + 1)
|
|
90
|
+
```
|
|
91
|
+
- Từ hiếm → IDF cao
|
|
92
|
+
- Từ phổ biến → IDF thấp
|
|
93
|
+
|
|
94
|
+
4. **`score(query)`** (Dòng 123-145)
|
|
95
|
+
- **Input:** Query string (ví dụ: "minimal dark mode")
|
|
96
|
+
- **Process:**
|
|
97
|
+
1. Tokenize query
|
|
98
|
+
2. Với mỗi document:
|
|
99
|
+
- Tính term frequency (TF) cho mỗi từ trong query
|
|
100
|
+
- Áp dụng công thức BM25:
|
|
101
|
+
```
|
|
102
|
+
score = Σ IDF(word) × (TF × (k1 + 1)) / (TF + k1 × (1 - b + b × doc_len/avgdl))
|
|
103
|
+
```
|
|
104
|
+
3. Trả về danh sách (index, score) sắp xếp giảm dần
|
|
105
|
+
|
|
106
|
+
**Ví dụ tính toán:**
|
|
107
|
+
```
|
|
108
|
+
Query: "minimal dark"
|
|
109
|
+
Document 1: "Minimalism Dark Mode UI" → score: 2.5
|
|
110
|
+
Document 2: "Glassmorphism Light UI" → score: 0.3
|
|
111
|
+
→ Document 1 được xếp hạng cao hơn
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
#### C. Search Functions (Dòng 148-242)
|
|
117
|
+
|
|
118
|
+
1. **`_load_csv(filepath)`** (Dòng 149-152)
|
|
119
|
+
- Đọc CSV file và trả về list of dictionaries
|
|
120
|
+
- Mỗi row là một dict với keys là column names
|
|
121
|
+
|
|
122
|
+
2. **`_search_csv(...)`** (Dòng 155-177)
|
|
123
|
+
- **Input:**
|
|
124
|
+
- `filepath`: Đường dẫn file CSV
|
|
125
|
+
- `search_cols`: Cột để search
|
|
126
|
+
- `output_cols`: Cột để trả về
|
|
127
|
+
- `query`: Từ khóa tìm kiếm
|
|
128
|
+
- `max_results`: Số kết quả tối đa (mặc định 3)
|
|
129
|
+
|
|
130
|
+
- **Process:**
|
|
131
|
+
1. Load CSV data
|
|
132
|
+
2. Tạo documents từ search columns:
|
|
133
|
+
```python
|
|
134
|
+
# Ví dụ: Nếu search_cols = ["Keywords", "Best For"]
|
|
135
|
+
# Document = "minimalism dark mode" + " " + "modern apps"
|
|
136
|
+
```
|
|
137
|
+
3. Khởi tạo BM25 và fit documents
|
|
138
|
+
4. Score query và lấy top results
|
|
139
|
+
5. Trả về list of dicts với output_cols
|
|
140
|
+
|
|
141
|
+
3. **`detect_domain(query)`** (Dòng 180-198)
|
|
142
|
+
- **Chức năng:** Tự động phát hiện domain phù hợp từ query
|
|
143
|
+
- **Cách hoạt động:**
|
|
144
|
+
```python
|
|
145
|
+
domain_keywords = {
|
|
146
|
+
"color": ["color", "palette", "hex", ...],
|
|
147
|
+
"pages": ["page", "home", "about", ...],
|
|
148
|
+
...
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
- Đếm số từ khóa match trong query
|
|
152
|
+
- Trả về domain có điểm cao nhất
|
|
153
|
+
- **Ví dụ:**
|
|
154
|
+
- Query: "home page design" → domain: "pages"
|
|
155
|
+
- Query: "blue color palette" → domain: "color"
|
|
156
|
+
- Query: "minimalism style" → domain: "style"
|
|
157
|
+
|
|
158
|
+
4. **`search(query, domain=None, max_results=3)`** (Dòng 201-220)
|
|
159
|
+
- **Main search function**
|
|
160
|
+
- **Process:**
|
|
161
|
+
1. Nếu không có domain → tự động detect
|
|
162
|
+
2. Lấy config từ CSV_CONFIG
|
|
163
|
+
3. Tạo filepath: `data/styles.csv`
|
|
164
|
+
4. Gọi `_search_csv()` để tìm kiếm
|
|
165
|
+
5. Trả về dict:
|
|
166
|
+
```python
|
|
167
|
+
{
|
|
168
|
+
"domain": "style",
|
|
169
|
+
"query": "minimalism",
|
|
170
|
+
"file": "styles.csv",
|
|
171
|
+
"count": 3,
|
|
172
|
+
"results": [...]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
5. **`search_stack(query, stack, max_results=3)`** (Dòng 223-242)
|
|
177
|
+
- Tương tự `search()` nhưng tìm trong thư mục `stacks/`
|
|
178
|
+
- Hỗ trợ 8 stacks: html-tailwind, react, nextjs, vue, svelte, swiftui, react-native, flutter
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### 2. File `search.py` - CLI Interface
|
|
183
|
+
|
|
184
|
+
#### A. Import và Setup (Dòng 11-12)
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- Import các hàm và config từ `core.py`
|
|
191
|
+
|
|
192
|
+
#### B. `format_output(result)` (Dòng 15-38)
|
|
193
|
+
|
|
194
|
+
**Chức năng:** Format kết quả thành markdown để AI dễ đọc
|
|
195
|
+
|
|
196
|
+
**Process:**
|
|
197
|
+
1. Kiểm tra có error không
|
|
198
|
+
2. Tạo header với domain/stack và query
|
|
199
|
+
3. Với mỗi result:
|
|
200
|
+
- Tạo section "Result 1", "Result 2", ...
|
|
201
|
+
- Format key-value pairs
|
|
202
|
+
- Giới hạn value length 300 ký tự
|
|
203
|
+
|
|
204
|
+
**Output format:**
|
|
205
|
+
```markdown
|
|
206
|
+
## UI/UX Builder Search Results
|
|
207
|
+
**Domain:** style | **Query:** minimalism
|
|
208
|
+
**Source:** styles.csv | **Found:** 3 results
|
|
209
|
+
|
|
210
|
+
### Result 1
|
|
211
|
+
- **Style Category:** Minimalism
|
|
212
|
+
- **Type:** General
|
|
213
|
+
- **Keywords:** minimal, clean, simple
|
|
214
|
+
...
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### C. Main CLI (Dòng 41-61)
|
|
218
|
+
|
|
219
|
+
**Argument Parser:**
|
|
220
|
+
```python
|
|
221
|
+
python search.py "minimalism" --domain style --max-results 5
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Arguments:**
|
|
225
|
+
- `query` (required): Từ khóa tìm kiếm
|
|
226
|
+
- `--domain` / `-d`: Chỉ định domain (style, color, pages, etc.)
|
|
227
|
+
- `--stack` / `-s`: Tìm trong stack guidelines
|
|
228
|
+
- `--max-results` / `-n`: Số kết quả tối đa (default: 3)
|
|
229
|
+
- `--json`: Output dạng JSON thay vì markdown
|
|
230
|
+
|
|
231
|
+
**Logic:**
|
|
232
|
+
1. Parse arguments
|
|
233
|
+
2. Nếu có `--stack` → gọi `search_stack()`
|
|
234
|
+
3. Nếu không → gọi `search()`
|
|
235
|
+
4. Nếu có `--json` → output JSON
|
|
236
|
+
5. Nếu không → format markdown và print
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 🔄 Flow Hoàn Chỉnh
|
|
241
|
+
|
|
242
|
+
### Ví dụ: Search "minimal dark mode"
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
python3 scripts/search.py "minimal dark mode" --domain style
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Step 1:** `search.py` parse arguments
|
|
249
|
+
- `query = "minimal dark mode"`
|
|
250
|
+
- `domain = "style"`
|
|
251
|
+
|
|
252
|
+
**Step 2:** Gọi `search("minimal dark mode", "style", 3)`
|
|
253
|
+
|
|
254
|
+
**Step 3:** `core.py` xử lý:
|
|
255
|
+
1. Lấy config: `CSV_CONFIG["style"]`
|
|
256
|
+
2. Filepath: `data/styles.csv`
|
|
257
|
+
3. Load CSV → list of dicts
|
|
258
|
+
4. Tạo documents từ search_cols:
|
|
259
|
+
```
|
|
260
|
+
Doc 1: "Minimalism General minimal clean simple Modern apps"
|
|
261
|
+
Doc 2: "Dark Mode General dark night oled Modern apps"
|
|
262
|
+
...
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Step 4:** BM25 processing:
|
|
266
|
+
1. Tokenize query: `["minimal", "dark", "mode"]`
|
|
267
|
+
2. Tokenize documents
|
|
268
|
+
3. Fit BM25: tính IDF cho tất cả từ
|
|
269
|
+
4. Score mỗi document:
|
|
270
|
+
- Document có "minimal", "dark", "mode" → score cao
|
|
271
|
+
- Document chỉ có 1-2 từ → score thấp
|
|
272
|
+
|
|
273
|
+
**Step 5:** Lấy top 3 results với score > 0
|
|
274
|
+
|
|
275
|
+
**Step 6:** Format output:
|
|
276
|
+
```markdown
|
|
277
|
+
## UI/UX Builder Search Results
|
|
278
|
+
**Domain:** style | **Query:** minimal dark mode
|
|
279
|
+
**Source:** styles.csv | **Found:** 3 results
|
|
280
|
+
|
|
281
|
+
### Result 1
|
|
282
|
+
- **Style Category:** Minimalism
|
|
283
|
+
- **Type:** General
|
|
284
|
+
...
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Step 7:** Print kết quả
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 🎯 Điểm Mạnh Của BM25
|
|
292
|
+
|
|
293
|
+
1. **Tốt hơn TF-IDF:**
|
|
294
|
+
- Xử lý tốt hơn với documents có độ dài khác nhau
|
|
295
|
+
- Công thức BM25 có saturation (từ xuất hiện nhiều lần không tăng điểm vô hạn)
|
|
296
|
+
|
|
297
|
+
2. **Không cần training:**
|
|
298
|
+
- Không cần machine learning model
|
|
299
|
+
- Chỉ cần tính toán thống kê
|
|
300
|
+
|
|
301
|
+
3. **Nhanh:**
|
|
302
|
+
- O(n) với n là số documents
|
|
303
|
+
- Phù hợp với dataset nhỏ-trung bình (< 10,000 records)
|
|
304
|
+
|
|
305
|
+
4. **Dễ hiểu:**
|
|
306
|
+
- Logic rõ ràng, dễ debug
|
|
307
|
+
- Có thể giải thích tại sao document được xếp hạng cao
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 📊 So Sánh Với Các Phương Pháp Khác
|
|
312
|
+
|
|
313
|
+
| Phương Pháp | Ưu Điểm | Nhược Điểm |
|
|
314
|
+
|------------|---------|------------|
|
|
315
|
+
| **BM25** (hiện tại) | Nhanh, không cần training, kết quả tốt | Không học được semantic meaning |
|
|
316
|
+
| **TF-IDF** | Đơn giản | Kém hơn BM25 với documents dài |
|
|
317
|
+
| **Vector Search (embeddings)** | Hiểu semantic, tìm được synonyms | Cần model, chậm hơn, phức tạp |
|
|
318
|
+
| **Keyword Match** | Rất nhanh | Không có ranking, kết quả kém |
|
|
319
|
+
|
|
320
|
+
**Tại sao chọn BM25:**
|
|
321
|
+
- Dataset nhỏ (hàng trăm records) → BM25 đủ tốt
|
|
322
|
+
- Không cần hiểu semantic (từ khóa rõ ràng)
|
|
323
|
+
- Cần tốc độ và đơn giản
|
|
324
|
+
- Kết quả đủ tốt cho use case này
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 🔧 Cách Mở Rộng
|
|
329
|
+
|
|
330
|
+
### Thêm Domain Mới:
|
|
331
|
+
|
|
332
|
+
1. Thêm vào `CSV_CONFIG`:
|
|
333
|
+
```python
|
|
334
|
+
"new_domain": {
|
|
335
|
+
"file": "new_domain.csv",
|
|
336
|
+
"search_cols": ["Column1", "Column2"],
|
|
337
|
+
"output_cols": ["Column1", "Column2", "Column3"]
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
2. Thêm keywords vào `detect_domain()`:
|
|
342
|
+
```python
|
|
343
|
+
"new_domain": ["keyword1", "keyword2", ...]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
3. Tạo file CSV trong `data/`
|
|
347
|
+
|
|
348
|
+
### Thêm Stack Mới:
|
|
349
|
+
|
|
350
|
+
1. Thêm vào `STACK_CONFIG`:
|
|
351
|
+
```python
|
|
352
|
+
"new_stack": {"file": "stacks/new_stack.csv"}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
2. Tạo file CSV trong `data/stacks/`
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 🐛 Debug Tips
|
|
360
|
+
|
|
361
|
+
1. **Không có kết quả:**
|
|
362
|
+
- Kiểm tra file CSV có tồn tại không
|
|
363
|
+
- Kiểm tra search_cols có đúng tên cột không
|
|
364
|
+
- Thử query đơn giản hơn
|
|
365
|
+
|
|
366
|
+
2. **Kết quả không liên quan:**
|
|
367
|
+
- Kiểm tra domain có đúng không
|
|
368
|
+
- Thử chỉ định domain thay vì auto-detect
|
|
369
|
+
- Kiểm tra keywords trong CSV có match không
|
|
370
|
+
|
|
371
|
+
3. **Performance chậm:**
|
|
372
|
+
- Dataset quá lớn → cân nhắc vector search
|
|
373
|
+
- Hoặc cache BM25 index
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 📝 Tóm Tắt
|
|
378
|
+
|
|
379
|
+
**UI/UX Builder Search Engine hoạt động như sau:**
|
|
380
|
+
|
|
381
|
+
1. **Input:** Query string + domain (optional)
|
|
382
|
+
2. **Process:**
|
|
383
|
+
- Auto-detect domain nếu không chỉ định
|
|
384
|
+
- Load CSV file tương ứng
|
|
385
|
+
- Tạo documents từ search columns
|
|
386
|
+
- Áp dụng BM25 algorithm để rank
|
|
387
|
+
- Lấy top N results
|
|
388
|
+
3. **Output:** Formatted markdown hoặc JSON với kết quả tìm kiếm
|
|
389
|
+
|
|
390
|
+
**Ưu điểm:**
|
|
391
|
+
- ✅ Nhanh và hiệu quả
|
|
392
|
+
- ✅ Không cần dependencies phức tạp
|
|
393
|
+
- ✅ Dễ mở rộng và maintain
|
|
394
|
+
- ✅ Kết quả tốt cho dataset nhỏ-trung bình
|