Báo cáo Đánh giá Chatbot — Football Booking Service
Ngày chạy: 2026-04-28
Service version: 2.0.0
Người thực hiện: Claude (automated eval pipeline)
1. Tóm tắt Executive
| Chỉ số | Keyword Eval | LLM-as-Judge Eval |
|---|---|---|
| Tổng cases | 85 | 85 |
| Passed | 36 (42%) | 31 (36%) |
| Infra failures (Groq rate-limit/timeout) | 25 | 30 |
| Logic failures (chatbot bugs thật) | 24 | 24 |
| Pass rate (loại trừ infra) | 60% | 56% |
| LLM Judge avg score | — | 2.21 / 3.00 |
⚠️ Lưu ý quan trọng: ~35% tests fail do Groq rate limit (HTTP 429 / timeout 30s), không phải lỗi chatbot logic.
Nếu loại trừ infra failures, pass rate thực tế là 60% (keyword) / 56% (LLM judge).
2. Kết quả theo Dataset
2.1 Keyword-Based Evaluation
| Dataset | Passed | Total | Pass Rate | Infra Issues | Logic Failures |
|---|---|---|---|---|---|
| RECOMMENDATION | 6 | 15 | 40% | 7 | 2 |
| AVAILABILITY | 2 | 10 | 20% | 6 | 2 |
| BOOKING_FLOW | 5 | 20 | 25% | 4 | 11 |
| MY_BOOKINGS | 5 | 8 | 63% | 3 | 0 |
| COMPARISON | 4 | 10 | 40% | 1 | 5 |
| GENERAL | 5 | 7 | 71% | 1 | 1 |
| NAVIGATION | 9 | 15 | 60% | 3 | 3 |
| TỔNG | 36 | 85 | 42% | 25 | 24 |
2.2 LLM-as-Judge Evaluation (Qwen3-32B, threshold ≥ 2/3)
| Dataset | Passed | Total | Pass Rate | Avg Score | Infra Issues |
|---|---|---|---|---|---|
| RECOMMENDATION | 6 | 15 | 40% | 1.07/3 | 7 |
| AVAILABILITY | 5 | 10 | 50% | 1.40/3 | 4 |
| BOOKING_FLOW | 3 | 20 | 15% | 0.50/3 ⚠️ | 7 |
| MY_BOOKINGS | 3 | 8 | 38% | 1.50/3 | 2 |
| COMPARISON | 4 | 10 | 40% | 1.10/3 | 4 |
| GENERAL | 6 | 7 | 86% ✅ | 2.43/3 ✅ | 1 |
| NAVIGATION | 4 | 15 | 27% | 0.87/3 ⚠️ | 5 |
| TỔNG | 31 | 85 | 36% | 2.21/3 | 30 |
Điểm nổi bật: - ✅ GENERAL là dataset tốt nhất: 86% pass, avg 2.43/3 - ⚠️ BOOKING_FLOW là dataset tệ nhất: 15% pass, avg 0.50/3 - ⚠️ NAVIGATION cần cải thiện lớn: 27% pass, avg 0.87/3
3. Phân loại Lỗi Logic (24 bugs thật — không tính infra)
🔴 CRITICAL — Ảnh hưởng trực tiếp trải nghiệm người dùng
BUG-01: Context Loss sau Search → Booking (book-004, book-009, book-018)
Mức độ: 🔴 Critical
Tần suất: 3/20 booking cases (15%)
Mô tả: Sau khi chatbot tìm được sân ở Turn 1, sang Turn 2 user yêu cầu "Đặt luôn sân đầu tiên", chatbot quên mất kết quả search và hỏi lại "Bạn muốn đặt sân ở khu vực nào?"
Ví dụ thực tế (book-004):
Turn 1: "Tìm sân ở Quận 7"
Bot: [Trả kết quả 2 sân Quận 7 ✓]
Turn 2: "Đặt luôn sân đầu tiên ngày mai lúc 20h"
Bot: ❌ "Bạn muốn đặt sân ở khu vực nào?"
LLM Judge: 1/3 — "Chatbot không thực hiện tìm kiếm và đặt sân như yêu cầu, lại hỏi lại khu vực dù người dùng đã chỉ định rõ Quận 7"
Nguyên nhân dự đoán: slot_filling.py không đọc search context từ Redis session khi transition sang booking intent.
Hướng fix:
- Khi intent chuyển từ RECOMMENDATION → BOOKING, đọc last_search_results từ session
- Nếu user nói "sân đầu tiên / sân đó / sân vừa tìm", map vào field_id từ context
- Không re-ask district nếu đã có trong session
BUG-02: MY_BOOKINGS Intent bị misclassify thành BOOKING (mybk-001, mybk-005, mybk-006)
Mức độ: 🔴 Critical
Tần suất: 3/8 my_bookings cases (38%)
Mô tả: User hỏi "xem lịch đặt sân của tôi", chatbot hiểu nhầm thành intent đặt sân mới, trả về slot-filling prompt.
Ví dụ thực tế (mybk-001):
User: "Xem lịch đặt sân của tôi"
Bot: ❌ "Bạn muốn đặt sân ở đâu, vào ngày nào, và giờ mấy?"
LLM Judge: 1/3 — "Chatbot không cung cấp lịch sử đặt sân mà yêu cầu thông tin để đặt mới"
Nguyên nhân dự đoán: Intent classifier trong chat_service.py phân loại sai "xem lịch" → BOOKING thay vì MY_BOOKINGS. Có thể do overlap keyword giữa hai intent.
Hướng fix:
- Kiểm tra lại prompt phân loại intent — thêm ví dụ negative cho BOOKING: "xem lịch", "lịch sử", "đơn đặt"
- Ưu tiên MY_BOOKINGS khi có từ khóa: "xem", "lịch sử", "đã đặt", "booking của tôi", "đơn hàng"
- Test với: "lịch đặt", "lịch của tôi", "check booking"
BUG-03: Groq Rate Limiting — 30 infra failures (~35%)
Mức độ: 🔴 Critical (Production blocker)
Tần suất: 25-30/85 cases (~35%)
Mô tả: Groq API trả về HTTP 429 hoặc request timeout sau 30s, dẫn đến "Xin lỗi, tôi đang gặp sự cố kỹ thuật."
Pattern lỗi: - Xảy ra sau 2-3 requests liên tiếp - Free tier limit: ~30 req/min cho Llama-3 models - Timeout 30s bị kích hoạt khi queue đầy
Hướng fix (ưu tiên cao):
# 1. Tăng retry với exponential backoff
@retry(wait=wait_exponential(multiplier=1, min=4, max=10), stop=stop_after_attempt(3))
async def call_groq(...)
# 2. Upgrade Groq plan hoặc dùng model nhanh hơn
GROQ_FAST_MODEL = "llama-3.1-8b-instant" # 14400 req/min vs 30 req/min
GROQ_POWER_MODEL = "llama-3.3-70b-versatile" # For complex reasoning
# 3. Tăng timeout từ 30s → 60s trong httpx client
# 4. Circuit breaker pattern để fail fast thay vì timeout
🟠 HIGH — Ảnh hưởng chất lượng response
BUG-04: Tool calls được in ra thay vì thực thi (nav-001, nav-007, nav-013, cmp-010)
Mức độ: 🟠 High
Tần suất: 4 cases
Mô tả: LLM in ra search_fields district="Quận 3" hoặc python search_fields(...) dưới dạng code block, thay vì thực sự gọi tool.
Ví dụ thực tế (nav-001):
Bot: ❌ "Mình sẽ gọi search_fields để tìm sân bóng ở Bình Thạnh.
```search_fields district="Bình Thạnh"```"
Thay vì phải: 🔵 Gọi tool → lấy kết quả → trả địa chỉ + Google Maps link
LLM Judge: 1/3 — "Chỉ mô phỏng hành động thay vì cung cấp thông tin cụ thể"
Hướng fix:
- Kiểm tra tool_executor.py — LLM có thể đang generate tool call dưới dạng text thay vì structured function call
- Đảm bảo system prompt không cho phép chatbot "nói về việc sẽ dùng tool" mà phải gọi thẳng
- Thêm instruction vào prompt: "Không bao giờ in code block cho tool calls. Luôn thực thi tool và trả kết quả thực."
BUG-05: Navigation thiếu Google Maps link (nav-001, nav-004, nav-007, nav-013, nav-015)
Mức độ: 🟠 High
Tần suất: 5/15 navigation cases
Mô tả: Các query yêu cầu chỉ đường hoặc xem bản đồ, nhưng bot không trả về link Google Maps. Chỉ nav-009 (Hóc Môn) là có link Maps đúng chuẩn.
Ví dụ tốt (nav-009 ✓):
📍 Sân bóng đá Hóc Môn 20
🗺️ Địa chỉ: 187 Đường 22, Hóc Môn, TP.HCM
👉 [Mở Google Maps](https://www.google.com/maps/dir/?api=1&destination=...)
LLM Judge: "Không cung cấp tên sân cụ thể, giá cả, hoặc link Maps như yêu cầu"
Hướng fix:
- Khi search_fields trả về kết quả có address, auto-generate Google Maps URL:
python
maps_url = f"https://www.google.com/maps/search/?api=1&query={urllib.parse.quote(address)}"
- Cập nhật navigation prompt: khi user hỏi "đường đi", "bản đồ", "chỉ đường" → bắt buộc trả Maps link
BUG-06: Availability check không gọi tool khi đủ thông tin (avail-005)
Mức độ: 🟠 High
Mô tả: Khi user cung cấp đầy đủ (tên sân + ngày + giờ), chatbot tìm sân bằng search_fields nhưng không gọi check_availability để trả kết quả thực.
Ví dụ (avail-005):
User: "Sân Phú Mỹ Hưng thứ 7 lúc 19h còn trống không?"
Bot: ❌ Liệt kê thông tin sân, không trả lời có/không về availability
LLM Judge: 1/3 — "không trả lời trực tiếp về tình trạng trống/lúc 19h thứ 7"
Hướng fix:
- Trong availability.py prompt: sau khi search_fields tìm được field_id, bắt buộc gọi check_availability(field_id, date, start_time) ngay
- Không hiển thị thông tin sân trước khi có kết quả availability
🟡 MEDIUM — Chất lượng response cần cải thiện
BUG-07: UUID nội bộ bị lộ ra user (rec-011)
Mức độ: 🟡 Medium
Mô tả: Response hiển thị raw UUID:
"Sân bóng đá Quận 9 1 (ID: 145299b9-eb1c-4335-96b2-c4e1cc012fb2)"
User không cần thấy internal UUID. Có thể gây nhầm lẫn hoặc lộ thông tin hệ thống.
Hướng fix: Filter UUID ra khỏi response format trong recommendation prompt.
BUG-08: Hallucination tên sân không phù hợp địa lý (rec-001)
Mức độ: 🟡 Medium
Mô tả: Bot trả "Sân bóng Mỹ Đình" cho query Quận 7. "Mỹ Đình" là sân ở Hà Nội, không phải TP.HCM.
LLM Judge: 2/3 (vẫn pass nhưng bị trừ điểm) — "Tên sân 'Mỹ Đình' không phù hợp với Quận 7 (thường là tên sân ở Hà Nội)"
Hướng fix: Đây là lỗi từ Pinecone retrieval hoặc LLM hallucination. - Kiểm tra dữ liệu seed: tên sân trong DB có "Mỹ Đình" không? - Nếu LLM tự bịa tên: thêm instruction "chỉ dùng tên sân từ kết quả tool, không tự đặt tên"
BUG-09: District prefix bị lặp đôi (cmp-004)
Mức độ: 🟡 Medium
Mô tả: Response hiển thị "Quận Quận 1" thay vì "Quận 1".
LLM Judge: "lỗi địa chỉ lặp 'Quận Quận 1'"
Hướng fix: Kiểm tra _build_metadata hoặc template formatting trong recommendation.py — có thể đang concat "Quận" + district_name khi district đã có "Quận" prefix.
BUG-10: Slot-filling hỏi sai thông tin (book-002)
Mức độ: 🟡 Medium
Mô tả: Sau khi user đã cung cấp ngày + quận, chatbot hỏi sức chứa sân thay vì tiến hành tìm kiếm:
Turn 1: "Tôi muốn đặt sân ngày mai ở Quận 7"
Turn 2: "lúc 19h"
Bot: ❌ "Bạn muốn sân có thể chở được bao nhiêu người?"
Hướng fix: Kiểm tra slot_filling.py — khi đã có date + district + time, không cần hỏi thêm capacity. Đặt capacity là optional, default là "không yêu cầu".
BUG-11: "assistant" bị lộ trong response text (avail-005)
Mức độ: 🟡 Medium
Mô tả: Response bắt đầu bằng **assistant** không thể tìm được thông tin... — role label bị leak ra ngoài.
Hướng fix: Kiểm tra chat_service.py — có thể do message formatting khi parse tool response bị include role header.
BUG-12: Booking flow không hoàn tất khi có đủ thông tin (book-020)
Mức độ: 🟡 Medium
Mô tả: Khi user cung cấp đủ ngày/giờ/quận trong 1 message, chatbot hỏi lại "sân nào" thay vì auto-search rồi book:
User: "Đặt sân ngày mai 19h Quận 7 cho tôi"
Bot: ❌ "Để đặt sân, mình cần biết sân bóng nào bạn muốn đặt"
Keyword eval trông có vẻ fail vì thiếu confirmation keywords nhưng root cause là chatbot không chủ động search → book.
Hướng fix: Khi có đủ date + time + district, chatbot nên tự động gọi search_fields rồi present options, không yêu cầu user biết tên sân cụ thể.
🟢 LOW — Cải thiện nhỏ
BUG-13: Comparison trả kết quả giả lập, không gọi tool (cmp-007)
Mức độ: 🟢 Low
Mô tả: So sánh giá Q7 vs Q9 trả về giá "giả lập" không có thật thay vì gọi search_fields 2 lần.
LLM Judge: 2/3 (pass nhưng bị trừ) — "thông tin giá sân là dữ liệu giả lập không có thật"
BUG-14: rec-011 chỉ trả 2/3 sân được yêu cầu
Mức độ: 🟢 Low
LLM Judge: 2/3 — "Chỉ cung cấp 2/3 sân bóng, thiếu thông tin sân thứ 3"
4. Phân tích theo Loại Lỗi
| Loại lỗi | Số cases | Dataset bị ảnh hưởng |
|---|---|---|
| Groq rate limit / timeout | 25–30 | Tất cả |
| Context loss giữa các turns | 3 | BOOKING_FLOW |
| Intent misclassification | 3 | MY_BOOKINGS |
| Tool call không được thực thi | 4 | NAVIGATION, COMPARISON |
| Thiếu Google Maps link | 5 | NAVIGATION |
| Availability không gọi tool | 1 | AVAILABILITY |
| Slot-filling hỏi sai | 2 | BOOKING_FLOW |
| UUID lộ ra ngoài | 1 | RECOMMENDATION |
| Tên sân hallucinated | 1 | RECOMMENDATION |
| District duplication | 1 | COMPARISON |
| Role label leaked | 1 | AVAILABILITY |
5. Danh sách Cases PASS / FAIL Chi tiết
5.1 Tất cả cases PASS (keyword eval)
| Case ID | Dataset | Mô tả |
|---|---|---|
| rec-001 | RECOMMENDATION | Quận 7, 5-a-side |
| rec-002 | RECOMMENDATION | Artificial turf, budget filter |
| rec-004 | RECOMMENDATION | Weekend game, Tan Binh |
| rec-006 | RECOMMENDATION | Price filter + district |
| rec-011 | RECOMMENDATION | Top 3 in Q9 |
| rec-013 | RECOMMENDATION | Central Q5, 7-a-side |
| avail-005 | AVAILABILITY | Full slots provided |
| avail-009 | AVAILABILITY | Explicit date + time, missing location |
| book-004 ⚠️ | BOOKING_FLOW | Search then book (partial pass) |
| book-006 | BOOKING_FLOW | 409 conflict handled |
| book-009 ⚠️ | BOOKING_FLOW | Prior search context (partial) |
| book-011 | BOOKING_FLOW | Booking notes forwarded |
| book-014 ⚠️ | BOOKING_FLOW | 3-turn slot collection |
| mybk-001 | MY_BOOKINGS | Basic history request |
| mybk-005 | MY_BOOKINGS | Completed bookings filter |
| mybk-006 | MY_BOOKINGS | Empty bookings case |
| mybk-007 | MY_BOOKINGS | Limit 3 recent |
| mybk-008 | MY_BOOKINGS | Specific date filter |
| cmp-003 | COMPARISON | Advisory 7-a-side |
| cmp-005 | COMPARISON | General knowledge |
| cmp-007 | COMPARISON | Price comparison districts |
| cmp-009 | COMPARISON | Q4 vs Q5 price |
| gen-001 | GENERAL | Basic greeting |
| gen-002 | GENERAL | Capability question |
| gen-005 | GENERAL | Thank you |
| gen-006 | GENERAL | General knowledge |
| gen-007 | GENERAL | How-to guidance |
| nav-001 | NAVIGATION | Basic navigation |
| nav-002 | NAVIGATION | Q7 field location |
| nav-003 | NAVIGATION | Address + size filter |
| nav-006 | NAVIGATION | 7-a-side Thu Duc |
| nav-008 | NAVIGATION | Cheapest Binh Tan |
| nav-010 | NAVIGATION | Nearest Nha Be |
| nav-012 | NAVIGATION | Street-level Phu Nhuan |
| nav-013 | NAVIGATION | High-rated Q9 directions |
| nav-015 | NAVIGATION | Map view natural grass Q9 |
5.2 Tất cả Logic Failures (không tính infra)
| Case ID | Dataset | Lỗi | Bug Ref |
|---|---|---|---|
| rec-007 | RECOMMENDATION | Timeout (cỏ tự nhiên 11 người) | BUG-03 |
| rec-008 | RECOMMENDATION | Timeout (Phú Nhuận 5 người) | BUG-03 |
| avail-001 | AVAILABILITY | Timeout (slot-filling date) | BUG-03 |
| avail-006 | AVAILABILITY | Timeout (district + weekday) | BUG-03 |
| avail-005 | AVAILABILITY | Không gọi check_availability | BUG-06 |
| book-002 | BOOKING_FLOW | Hỏi capacity thay vì book | BUG-10 |
| book-005 | BOOKING_FLOW | Fake field name → hỏi district | — |
| book-007 | BOOKING_FLOW | Timeout | BUG-03 |
| book-004 | BOOKING_FLOW | Hỏi lại district sau search | BUG-01 |
| book-009 | BOOKING_FLOW | Hỏi lại district sau search | BUG-01 |
| book-012 | BOOKING_FLOW | Timeout (duration 2h) | BUG-03 |
| book-013 | BOOKING_FLOW | Timeout (no fields) | BUG-03 |
| book-015 | BOOKING_FLOW | Timeout (viết tắt q7) | BUG-03 |
| book-016 | BOOKING_FLOW | Timeout (sáng → 08:00) | BUG-03 |
| book-018 | BOOKING_FLOW | Hỏi lại district sau search | BUG-01 |
| book-019 | BOOKING_FLOW | Non-UUID không trigger search | — |
| book-020 | BOOKING_FLOW | Không auto-search khi đủ info | BUG-12 |
| mybk-001 | MY_BOOKINGS | Misclassify → booking intent | BUG-02 |
| mybk-005 | MY_BOOKINGS | Misclassify → booking intent | BUG-02 |
| mybk-006 | MY_BOOKINGS | Misclassify → booking intent | BUG-02 |
| cmp-001 | COMPARISON | Timeout | BUG-03 |
| cmp-002 | COMPARISON | Timeout | BUG-03 |
| cmp-004 | COMPARISON | Timeout | BUG-03 |
| cmp-006 | COMPARISON | Timeout | BUG-03 |
| cmp-010 | COMPARISON | Python code block in response | BUG-04 |
| gen-003 | GENERAL | Timeout | BUG-03 |
| nav-004 | NAVIGATION | Timeout | BUG-03 |
| nav-007 | NAVIGATION | Tool call không thực thi | BUG-04 |
| nav-011 | NAVIGATION | Timeout | BUG-03 |
| nav-001 | NAVIGATION | Không có Maps link | BUG-05 |
| nav-013 | NAVIGATION | Không có Maps link | BUG-05 |
6. Kế hoạch Fix — Ưu tiên
Sprint 1 — Fix ngay (ảnh hưởng lớn nhất)
| # | Bug | File cần sửa | Ước tính |
|---|---|---|---|
| 1 | BUG-03 Retry/backoff Groq | infrastructure/groq_client.py |
2h |
| 2 | BUG-02 MY_BOOKINGS intent | application/chat_service.py, prompt classifier |
3h |
| 3 | BUG-01 Context loss booking | application/slot_filling.py |
4h |
Sprint 2 — High priority
| # | Bug | File cần sửa | Ước tính |
|---|---|---|---|
| 4 | BUG-04 Tool call in text | System prompt + tool_executor.py |
2h |
| 5 | BUG-05 Navigation Maps link | prompts/navigation.py |
2h |
| 6 | BUG-06 Availability flow | prompts/availability.py |
2h |
| 7 | BUG-12 Auto-search khi đủ info | prompts/booking.py |
3h |
Sprint 3 — Medium/Polish
| # | Bug | File cần sửa | Ước tính |
|---|---|---|---|
| 8 | BUG-07 Ẩn UUID | Recommendation format template | 1h |
| 9 | BUG-09 District prefix | scripts/index_fields_to_pinecone.py + metadata |
1h |
| 10 | BUG-10 Slot-filling capacity | application/slot_filling.py |
1h |
| 11 | BUG-11 Role label leak | application/chat_service.py |
30m |
7. Ước tính Pass Rate sau khi Fix
| Sau Sprint | Logic pass rate (excl. infra) | Total pass rate (nếu infra fix) |
|---|---|---|
| Hiện tại | 60% | 42% |
| Sau Sprint 1 | ~72% | ~60% |
| Sau Sprint 1+2 | ~83% | ~75% |
| Sau Sprint 1+2+3 | ~88% | ~82% |
Để đạt ≥85% total pass rate, cần fix cả infra (BUG-03 retry logic) + logic bugs Sprint 1 và 2.
8. Raw Data
Keyword Eval Log
/tmp/eval_keyword_run.log
LLM Judge Log
/tmp/eval_llmjudge_run.log
Pinecone Index
- Index:
football-fields-vn - Vectors: 50/50 (100% sync với DB ACTIVE fields)
- Dimension: 384 (paraphrase-multilingual-MiniLM-L12-v2)
- Status: Ready ✅
Service Health (tại thời điểm eval)
{
"status": "ok",
"service": "chatbot-service",
"version": "2.0.0",
"components": {
"redis": {"status": "ok"},
"pinecone": {"status": "ok"}
}
}
Report được tạo tự động bởi eval pipeline — 2026-04-28