Atsatoru commited on
Commit
d5e76ed
·
verified ·
1 Parent(s): e0f140b

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +1689 -0
app.py ADDED
@@ -0,0 +1,1689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ # PHẦN 1: CÀI ĐẶT THƯ VIỆN
6
+ # ============================================================
7
+
8
+ import os
9
+ import sys
10
+ import warnings
11
+ warnings.filterwarnings('ignore')
12
+
13
+ print('='*70)
14
+ print('🏛️ AI HÀNH CHÍNH CÔNG VIP - CÀI ĐẶT THƯ VIỆN')
15
+ print('='*70)
16
+ print()
17
+
18
+ # Tạo thư mục (đường dẫn tương đối cho Hugging Face Spaces)
19
+ DATA_DIR = os.path.dirname(os.path.abspath(__file__))
20
+ os.makedirs(os.path.join(DATA_DIR, 'data'), exist_ok=True)
21
+ os.makedirs(os.path.join(DATA_DIR, 'models'), exist_ok=True)
22
+ os.makedirs(os.path.join(DATA_DIR, 'audio_output'), exist_ok=True)
23
+
24
+ # Cài đặt thư viện - tự động cài đặt từ requirements.txt trên Hugging Face Spaces
25
+ print('📦 Thư viện sẽ được cài đặt tự động từ requirements.txt')
26
+ print('✅ Sẵn sàng!')
27
+ print()
28
+ ADMIN_PASSWORD = 'TOIDEPTRAI'
29
+
30
+ # Import libraries
31
+ import torch
32
+ import gc
33
+ import json
34
+ import numpy as np
35
+ import pandas as pd
36
+ from datetime import datetime
37
+
38
+ # Check GPU
39
+ print('🔍 Kiểm tra GPU:')
40
+ print(f' - CUDA Available: {torch.cuda.is_available()}')
41
+ if torch.cuda.is_available():
42
+ print(f' - GPU Name: {torch.cuda.get_device_name(0)}')
43
+ print(f' - GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB')
44
+ # Set memory efficient
45
+ torch.cuda.empty_cache()
46
+ else:
47
+ print(' - ⚠️ Không có GPU, sẽ chạy bằng CPU (chậm hơn)')
48
+ print()
49
+ print('='*70)
50
+
51
+ # ======================================================================
52
+ # ============================================================
53
+ # PHẦN 2: TẢI DATASET
54
+ # ============================================================
55
+
56
+ print('='*70)
57
+ print('📥 TẢI DATASET')
58
+ print('='*70)
59
+ print()
60
+
61
+ # Dataset: Thủ tục hành chính
62
+ try:
63
+ from datasets import load_dataset
64
+ print('🔄 Đang tải Dataset: Thủ tục hành chính...')
65
+ ds1 = load_dataset('hirine/dataset-thu-tuc-hanh-chinh-5733-samples', split='train')
66
+ df1 = pd.DataFrame(ds1)
67
+ print(f' ✅ Dataset: {len(df1):,} samples')
68
+ except Exception as e:
69
+ print(f' ⚠️ Lỗi tải Dataset: {e}')
70
+ print(' 🔄 Tạo dữ liệu mẫu...')
71
+ df1 = pd.DataFrame({
72
+ 'id': range(100),
73
+ 'title': ['Khai sinh', 'Kết hôn', 'Làm CCCD', 'Tách hộ khẩu', 'Đổi bằng lái',
74
+ 'Khai tử', 'Hộ chiếu', 'Lý lịch tư pháp', 'Sổ đỏ', 'Sang tên'] * 10,
75
+ 'text': ['Thủ tục hành chính cần giấy tờ...' * 5] * 100
76
+ })
77
+ print(f' ✅ Đã tạo dữ liệu mẫu: {len(df1)} records')
78
+
79
+ df1.to_csv(os.path.join(DATA_DIR, 'data/ds1.csv'), index=False)
80
+ print()
81
+
82
+ # Tổng kết
83
+ print(f'📊 Tổng samples: {len(df1):,}')
84
+ print()
85
+ print('='*70)
86
+
87
+ # ======================================================================
88
+ # ============================================================
89
+ # PHẦN 3: DATA DỊCH VỤ CÔNG (40+ THỦ TỤC)
90
+ # ============================================================
91
+
92
+ print('='*70)
93
+ print('📥 TẢI DATA DỊCH VỤ CÔNG')
94
+ print('='*70)
95
+ print()
96
+
97
+ # Dữ liệu Dịch vụ công đầy đủ
98
+ DICHVUCONG_DATA = {
99
+ # NHÂN SỰ - HỘ TỊCH - TƯ PHÁP
100
+ "KHAI_SINH": {"code":"KHAI_SINH","name":"Đăng ký khai sinh trực tiếp","docs":"Giấy chứng sinh, CCCD cha mẹ, Giấy chứng nhận kết hôn","coquan":"UBND xã/phường","time":"1-3 ngày","lephi":"Miễn phí","note":"Làm trong 60 ngày kể từ khi sinh","steps":"1. Đến UBND nơi cư trú. 2. Nộp hồ sơ. 3. Nhận kết quả.","category":"NHAN_SU"},
101
+ "KHAISINH_LIEN_THONG": {"code":"KS_LT","name":"Liên thông: Khai sinh - Thường trú - Cấp thẻ BHYT","docs":"Giấy chứng sinh điện tử, CCCD cha mẹ","coquan":"Cổng Dịch vụ công Quốc gia","time":"1-3 ngày làm việc","lephi":"Miễn phí","note":"Thực hiện online 100%, nhận 3 kết quả cùng lúc","steps":"1. Truy cập dichvucong.gov.vn. 2. Chọn nhóm dịch vụ Liên thông. 3. Nhập mã giấy chứng sinh. 4. Chờ phê duyệt.","category":"NHAN_SU"},
102
+ "KHAI_SINH_QUA_HAN": {"code":"KHAI_SINH_QUA_HAN","name":"Khai sinh quá hạn","docs":"Giấy chứng sinh, CCCD, Đơn giải trình lý do chậm trễ","coquan":"UBND xã/phường","time":"3-7 ngày","lephi":"Miễn phí","note":"Quá 60 ngày cần đơn giải trình","steps":"1. Viết đơn giải trình. 2. Xin xác nhận tổ dân phố (nếu cần). 3. Nộp hồ sơ tại UBND.","category":"NHAN_SU"},
103
+ "KHAI_TU": {"code":"KHAI_TU","name":"Đăng ký khai tử","docs":"Giấy báo tử, CCCD người chết","coquan":"UBND xã/phường","time":"1-3 ngày","lephi":"Miễn phí","note":"Làm trong 60 ngày kể từ khi chết","steps":"1. Lấy giấy báo tử tại Y tế/Công an. 2. Đến UBND nơi người chết cư trú.","category":"NHAN_SU"},
104
+ "KET_HON": {"code":"KET_HON","name":"Đăng ký kết hôn (Trong nước)","docs":"CCCD cả 2 bên, Giấy xác nhận tình trạng hôn nhân (nếu cư trú khác nơi đăng ký)","coquan":"UBND xã/phường","time":"1-3 ngày","lephi":"Miễn phí","note":"Cả 2 bên phải có mặt","steps":"1. Chuẩn bị giấy xác nhận độc thân. 2. Cả 2 cùng đến UBND ký vào sổ hộ tịch.","category":"NHAN_SU"},
105
+ "LY_HON": {"code":"LY_HON","name":"Ly hôn thuận tình","docs":"Đơn xin ly hôn, Giấy kết hôn gốc, CCCD, Khai sinh con (nếu có)","coquan":"Tòa án nhân dân quận/huyện","time":"2-4 tháng","lephi":"300.000đ","note":"Cả hai bên cùng đồng ý và ký tên","steps":"1. Nộp đơn tại Tòa án. 2. Hòa giải. 3. Tòa án ra quyết định công nhận.","category":"NHAN_SU"},
106
+ "KET_HON_NGOAI": {"code":"KET_HON_NGOAI","name":"Kết hôn có yếu tố nước ngoài","docs":"Hộ chiếu, Giấy xác nhận tình trạng hôn nhân hợp pháp hóa lãnh sự","coquan":"UBND quận/huyện","time":"5-15 ngày","lephi":"Dưới 1.000.000đ","note":"Cần phỏng vấn tại tư pháp","steps":"1. Hợp pháp hóa giấy tờ nước ngoài. 2. Nộp tại UBND quận. 3. Phỏng vấn và ký giấy.","category":"NHAN_SU"},
107
+ "SAO_KHAI_SINH": {"code":"SAO_KS","name":"Cấp bản sao trích lục khai sinh","docs":"CCCD người yêu cầu","coquan":"UBND nơi lưu trữ sổ gốc","time":"Ngay trong ngày","lephi":"8.000đ/bản","note":"Có thể xin bản sao điện tử trên DVC","steps":"1. Nộp tờ khai. 2. Đóng phí. 3. Nhận bản sao trích lục.","category":"NHAN_SU"},
108
+ "XAC_NHAN_DOC_THAN": {"code":"XN_DOC_THAN","name":"Xác nhận tình trạng hôn nhân (Độc thân)","docs":"CCCD, Quyết định ly hôn (nếu có)","coquan":"UBND xã/phường nơi thường trú","time":"1-3 ngày","lephi":"15.000đ","note":"Giấy có giá trị 6 tháng để kết hôn hoặc vay vốn","steps":"1. Nộp đơn tại UBND. 2. Cán bộ kiểm tra sổ hộ tịch. 3. Cấp giấy.","category":"NHAN_SU"},
109
+ "LY_LICH_TU_PHAP": {"code":"LLTP","name":"Cấp lý lịch tư pháp (Số 1 và Số 2)","docs":"CCCD gắn chip","coquan":"Sở Tư pháp hoặc qua app VNeID","time":"7-10 ngày","lephi":"200.000đ (miễn phí cho một số đối tượng)","note":"Hiện tại làm qua VNeID rất nhanh","steps":"1. Truy cập VNeID hoặc DVC. 2. Chọn cấp LLTP. 3. Thanh toán online. 4. Nhận kết quả bản điện tử/giấy.","category":"HANH_CHINH"},
110
+ "CHUNG_THUC_BAN_SAO": {"code":"CHUNG_THUC","name":"Công chứng/Chứng thực bản sao (Sao y bản chính)","docs":"Bản chính giấy tờ, Bản photo","coquan":"UBND xã/phường hoặc Phòng Công chứng","time":"Lấy ngay","lephi":"2.000đ/trang","note":"Trình bản chính để đối chiếu","steps":"1. Xuất trình bản chính. 2. Cán bộ ký đóng dấu vào bản photo.","category":"HANH_CHINH"},
111
+ # CCCD - HỘ KHẨU
112
+ "CCCD_CAP": {"code":"CCCD_CAP","name":"Cấp CCCD lần đầu","docs":"Giấy khai sinh, Sổ hộ khẩu","coquan":"Công an quận/huyện","time":"7-15 ngày","lephi":"Miễn phí","note":"Từ 14 tuổi","steps":"1. Chuẩn bị giấy khai sinh, sổ hộ khẩu. 2. Đến Công an quận/huyện. 3. Làm thủ tục cấp CCCD.","category":"CCCD_HK"},
113
+ "CCCD_DOI": {"code":"CCCD_DOI","name":"Đổi CCCD","docs":"CCCD cũ, Giấy khai sinh","coquan":"Công an quận/huyện","time":"7-15 ngày","lephi":"Miễn phí","note":"Đổi khi hư hỏng/sai thông tin","steps":"1. Mang CCCD cũ. 2. Đến Công an nơi thường trú. 3. Làm thủ tục đổi.","category":"CCCD_HK"},
114
+ "VNEID_DINH_DANH_2": {"code":"VNEID_L2","name":"Đăng ký tài khoản VNeID Mức độ 2","docs":"CCCD gắn chip, Số điện thoại chính chủ","coquan":"Công an xã/phường","time":"3-5 ngày duyệt","lephi":"Miễn phí","note":"Thay thế thẻ vật lý khi đi máy bay, hành chính","steps":"1. Đến trực tiếp Công an. 2. Quét chip CCCD. 3. Chụp ảnh chân dung. 4. Chờ Bộ Công an phê duyệt.","category":"CCCD_HK"},
115
+ "CCCD_CAP_LAI": {"code":"CCCD_CAP_LAI","name":"Cấp lại CCCD khi mất","docs":"Giấy khai sinh, Sổ hộ khẩu, Đơn xin cấp lại","coquan":"Công an quận/huyện","time":"7-15 ngày","lephi":"Miễn phí","note":"Báo mất và xin cấp lại","steps":"1. Làm đơn báo mất. 2. Đến Công an. 3. Xin cấp lại CCCD.","category":"CCCD_HK"},
116
+ "HO_KHAU_TACH": {"code":"HO_KHAU_TACH","name":"Tách sổ hộ khẩu","docs":"Sổ hộ khẩu, CCCD, Đơn xin tách, Giấy tờ nơi ở mới","coquan":"Công an quận/huyện","time":"3-7 ngày","lephi":"Miễn phí","note":"Cần đồng ý chủ hộ","steps":"1. Viết đơn xin tách hộ khẩu. 2. Xin chữ ký chủ hộ. 3. Đến Công án làm thủ tục.","category":"CCCD_HK"},
117
+ "HO_KHAU_NHAP": {"code":"HO_KHAU_NHAP","name":"Nhập sổ hộ khẩu","docs":"Sổ hộ khẩu, CCCD, Đơn xin nhập, Giấy tờ nơi ở","coquan":"Công an quận/huyện","time":"3-7 ngày","lephi":"Miễn phí","note":"Cần đồng ý chủ hộ m��i","steps":"1. Viết đơn xin nhập. 2. Xin đồng ý chủ hộ mới. 3. Đến Công an làm thủ tục.","category":"CCCD_HK"},
118
+ "TAM_TRU": {"code":"TAM_TRU","name":"Đăng ký tạm trú","docs":"CCCD, Đơn xin tạm trú, Giấy tờ nơi ở","coquan":"Công an xã/phường","time":"3-5 ngày","lephi":"Miễn phí","note":"Tối đa 2 năm","steps":"1. Lấy giấy xác nhận của chủ nhà. 2. Viết đơn xin tạm trú. 3. Đến Công an xã/phường.","category":"CCCD_HK"},
119
+ "TRU_SO": {"code":"TRU_SO","name":"Đăng ký trú quán","docs":"CCCD, Đơn xin trú quán, Xác nhận tổ dân phố","coquan":"Công an xã/phường","time":"3-5 ngày","lephi":"Miễn phí","note":"Cho người không có hộ khẩu","steps":"1. Xin xác nhận tổ dân phố. 2. Viết đơn xin trú quán. 3. Đến Công an.","category":"CCCD_HK"},
120
+ "TAM_VANG": {"code":"TAM_VANG","name":"Đăng ký tạm vắng","docs":"CCCD, Sổ hộ khẩu, Đơn xin","coquan":"Công an","time":"1-3 ngày","lephi":"Miễn phí","note":"Khi đi khỏi nơi thường trú trên 30 ngày","steps":"1. Làm đơn xin báo tạm vắng. 2. Nộp tại Công an nơi thường trú.","category":"CCCD_HK"},
121
+
122
+ # BẰNG LÁI
123
+ "BANG_LAI_A1": {"code":"BANG_LAI_A1","name":"Cấp đổi bằng lái A1","docs":"CCCD, Giấy khai sinh (nếu dưới 18), 1 ảnh 3x4","coquan":"Sở GTVT","time":"7-10 ngày","lephi":"Miễn phí","note":"Từ 18 tuổi, đổi bằng không cần thi lại","steps":"1. Chuẩn bị CCCD, ảnh. 2. Đến Sở GTVT hoặc trung tâm sát hạch. 3. Nộp hồ sơ và nhận bằng mới.","category":"GIAO_THONG"},
124
+ "BANG_LAI_A1_CAP_MOI": {"code":"BANG_LAI_A1_CAP_MOI","name":"Cấp mới bằng lái A1","docs":"CCCD, Giấy khai sinh, Học lý, Giấy khám sức khỏe","coquan":"Sở GTVT","time":"15-20 ngày","lephi":"110.000đ","note":"Phải thi sát hạch","steps":"1. Học lý thuyết và sa hình. 2. Đăng ký thi. 3. Thi sát hạch. 4. Nhận bằng khi đỗ.","category":"GIAO_THONG"},
125
+ "BANG_LAI_A2": {"code":"BANG_LAI_A2","name":"Cấp bằng lái A2","docs":"CCCD, Học lý, Giấy khám sức khỏe","coquan":"Sở GTVT","time":"15-20 ngày","lephi":"150.000đ","note":"Xe mô tô trên 175cc","steps":"1. Học lý thuyết và sa hình A2. 2. Đăng ký thi. 3. Thi và nhận bằng.","category":"GIAO_THONG"},
126
+ "BANG_LAI_B1": {"code":"BANG_LAI_B1","name":"Cấp bằng lái B1","docs":"CCCD, Học lý, Giấy khám sức khỏe","coquan":"Sở GTVT","time":"20-25 ngày","lephi":"350.000đ","note":"Ô tô dưới 9 chỗ","steps":"1. Học lý thuyết, sa hình, đường trường. 2. Đăng ký thi B1. 3. Thi và nhận bằng.","category":"GIAO_THONG"},
127
+ "BANG_LAI_B2": {"code":"BANG_LAI_B2","name":"Cấp bằng lái B2","docs":"CCCD, Học lý, Giấy khám sức khỏe","coquan":"Sở GTVT","time":"20-25 ngày","lephi":"400.000đ","note":"Ô tô kinh doanh","steps":"1. Học lý thuyết, sa hình, đường trường. 2. Đăng ký thi B2. 3. Thi và nhận bằng.","category":"GIAO_THONG"},
128
+ "BANG_LAI_DOI": {"code":"BANG_LAI_DOI","name":"Đổi bằng lái","docs":"GPLX cũ, CCCD, 1 ảnh 3x4","coquan":"Sở GTVT","time":"7-10 ngày","lephi":"135.000đ","note":"Đổi khi hết hạn","steps":"1. Chuẩn bị bằng cũ, CCCD, ảnh. 2. Đến Sở GTVT. 3. Đổi bằng mới.","category":"GIAO_THONG"},
129
+ "BANG_LAI_CAP_LAI": {"code":"BANG_LAI_CAP_LAI","name":"Cấp lại bằng lái","docs":"CCCD, 1 ảnh 3x4, Đơn xin cấp lại","coquan":"Sở GTVT","time":"7-10 ngày","lephi":"135.000đ","note":"Khi bị mất","steps":"1. Làm đơn xin cấp lại. 2. Đến Sở GTVT. 3. Nhận bằng mới.","category":"GIAO_THONG"},
130
+ "GPLX_ONLINE": {"code":"GPLX_ONL","name":"Đổi Giấy phép lái xe trực tuyến (Mức độ 4)","docs":"GPLX cũ, Giấy khám sức khỏe điện tử, Ảnh chân dung file số","coquan":"Cổng DVC Cục đường bộ","time":"5 ngày","lephi":"115.000đ","note":"Nhận bằng tại nhà qua bưu điện","steps":"1. Khám sức khỏe tại BV có kết nối dữ liệu. 2. Kê khai trên DVC. 3. Thanh toán tiền. 4. Bưu điện giao bằng.","category":"GIAO_THONG"},
131
+ "DANG_KY_XE": {"code":"DK_XE","name":"Đăng ký xe máy/ô tô mới","docs":"Hóa đơn GTGT, Tờ khai lệ phí trước bạ, Giấy xuất xưởng","coquan":"Công an quận/huyện","time":"1 ngày","lephi":"Tùy loại xe và khu vực","note":"Phải đóng thuế trước bạ trước khi đăng ký","steps":"1. Đóng thuế trước bạ. 2. Đến công an bấm biển số. 3. Nhận biển số ngay, nhận cavet sau 2 ngày.","category":"GIAO_THONG"},
132
+ "THU_HOI_BIEN_SO": {"code":"THU_HOI","name":"Thu hồi biển số, đăng ký xe (Khi bán xe)","docs":"Cavet xe, Biển số xe, Hợp đồng mua bán","coquan":"Cơ quan Công an nơi quản lý xe","time":"2 ngày","lephi":"Miễn phí","note":"Biển số sẽ được giữ lại làm biển định danh cho chủ cũ","steps":"1. Chủ xe nộp đăng ký và biển số. 2. Nhận giấy chứng nhận thu hồi.","category":"GIAO_THONG"},
133
+ "PHAT_NGUOI": {"code":"PHAT_NGUOI","name":"Nộp phạt vi phạm giao thông (Phạt nguội)","docs":"Mã số quyết định xử phạt","coquan":"Cổng Dịch vụ công Quốc gia","time":"Xử lý ngay","lephi":"Theo biên bản","note":"Có thể tra cứu bằng biển số xe","steps":"1. Tra cứu mã vi phạm. 2. Thanh toán qua ngân hàng/ví điện tử. 3. Hệ thống tự động xóa lỗi.","category":"GIAO_THONG"},
134
+ "DANG_KIEM": {"code":"DANG_KIEM","name":"Đăng kiểm xe cơ giới","docs":"Cavet xe, Đăng kiểm cũ, Bảo hiểm dân sự","coquan":"Trạm đăng kiểm","time":"1-2 giờ","lephi":"~300.000đ + Phí đường bộ","note":"Nên đặt lịch qua app TTDK","steps":"1. Đặt lịch. 2. Đưa xe đến kiểm định. 3. Dán tem mới.","category":"GIAO_THONG"},
135
+ # HỘ CHIẾU (2)
136
+ "HO_CHIEU_CAP": {"code":"HO_CHIEU_CAP","name":"Cấp hộ chiếu","docs":"CCCD, Sổ hộ khẩu, 4 ảnh 4x6","coquan":"Phòng xuất nhập cảnh","time":"7-15 ngày","lephi":"200.000đ","note":"Hộ chiếu gắn chip 10 năm","steps":"1. Chuẩn bị CCCD, hộ khẩu, ảnh. 2. Làm đơn xin cấp hộ chiếu. 3. Nộp tại Phòng XNC. 4. Nhận hộ chiếu.","category":"HANH_CHINH"},
137
+ "HO_CHIEU_GIA_HAN": {"code":"HO_CHIEU_GIA_HAN","name":"Gia hạn hộ chiếu","docs":"Hộ chiếu cũ, CCCD, 1 ảnh 4x6","coquan":"Phòng xuất nhập cảnh","time":"5-10 ngày","lephi":"200.000đ","note":"Trước 6 tháng hết hạn","steps":"1. Kiểm tra hạn hộ chiếu. 2. Chuẩn bị hồ sơ gia hạn. 3. Nộp tại Phòng XNC.","category":"HANH_CHINH"},
138
+
139
+ # LÝ LỊCH (1)
140
+ "LY_LICH_TU_PHAP": {"code":"LLTP","name":"Cấp lý lịch tư pháp","docs":"CCCD, Đơn xin cấp LLTP","coquan":"Sở Tư pháp","time":"3-5 ngày","lephi":"Miễn phí","note":"Kiểm tra án tích","steps":"1. Làm đơn xin cấp lý lịch tư pháp. 2. Nộp tại Sở Tư pháp. 3. Chờ 3-5 ngày và nhận kết quả.","category":"HANH_CHINH"},
141
+
142
+ # NHÀ ĐẤT (2)
143
+ "SO_DO": {"code":"SO_DO","name":"Cấp Sổ đỏ lần đầu","docs":"Giấy tờ chứng minh quyền sử dụng đất, Trích lục bản đồ","coquan":"Văn phòng đăng ký đất đai","time":"20-30 ngày","lephi":"Tiền sử dụng đất + Lệ phí cấp sổ","note":"Quy trình phức tạp, cần xác minh nguồn gốc đất","steps":"1. Nộp hồ sơ tại bộ phận 1 cửa. 2. Niêm yết công khai. 3. Thực hiện nghĩa vụ tài chính. 4. Nhận sổ.","category":"NHADAT"},
144
+ "SANG_TEN": {"code":"SANG_TEN","name":"Sang tên Sổ đỏ (Chuyển nhượng nhà đất)","docs":"Hợp đồng công chứng, Sổ đỏ gốc, CCCD hai bên","coquan":"Văn phòng đăng ký đất đai","time":"10-15 ngày","lephi":"Thuế TNCN 2%, Lệ phí trước bạ 0.5%","note":"Phải công chứng hợp đồng trước","steps":"1. Ký hợp đồng tại phòng công chứng. 2. Nộp hồ sơ đăng ký biến động. 3. Đóng thuế. 4. Nhận sổ đã sang tên.","category":"NHADAT"},
145
+ "QUY_HOACH_DAT": {"code":"QH_DAT","name":"Tra cứu thông tin quy hoạch đất đai","docs":"Phiếu yêu cầu, CCCD","coquan":"Phòng Tài nguyên và Môi trường","time":"2 ngày","lephi":"Theo quy định","note":"Để biết đất có dính dự án, lộ giới không","steps":"1. Gửi yêu cầu. 2. Cơ quan chức năng cung cấp văn bản trả lời.","category":"NHADAT"},
146
+ "CAP_PHEP_XAY_DUNG": {"code":"PHEP_XD","name":"Cấp phép xây dựng nhà ở riêng lẻ","docs":"Bản vẽ thiết kế, Sổ đỏ sao y, Đơn xin cấp phép","coquan":"UBND quận/huyện","time":"15 ngày","lephi":"~100.000đ","note":"Bắt buộc phải có trước khi khởi công","steps":"1. Thuê đơn vị thiết kế bản vẽ. 2. Nộp hồ sơ tại Quận. 3. Cán bộ kiểm tra thực địa. 4. Cấp phép.","category":"XAYDUNG"},
147
+
148
+ # GIAO THÔNG XE (2)
149
+ "DANG_KY_XE": {"code":"DANG_KY_XE","name":"Đăng ký xe ô tô","docs":"CCCD, Giấy chứng nhận quyền sở hữu xe, Giấy bảo hiểm","coquan":"Phòng đăng ký xe","time":"1-3 ngày","lephi":"1.500.000đ-3.000.000đ","note":"Trong 30 ngày kể từ khi mua","steps":"1. Chuẩn bị giấy tờ xe. 2. Mua bảo hiểm xe. 3. Đến Phòng đăng ký xe. 4. Nộp hồ sơ và nhận biển số.","category":"GIAO_THONG"},
150
+ "CHUYEN_NHUONG_XE": {"code":"CHUYEN_NHUONG_XE","name":"Sang tên chủ sở hữu xe","docs":"CCCD cả 2 bên, Sổ đăng ký xe, Hợp đồng chuyển nhượng","coquan":"Phòng đăng ký xe","time":"2-5 ngày","lephi":"500.000đ-1.000.000đ","note":"Cả 2 cùng đến làm","steps":"1. Làm hợp đồng chuyển nhượng có công chứng. 2. Cả 2 bên đến Phòng đăng ký xe. 3. Làm thủ tục chuyển nhượng.","category":"GIAO_THONG"},
151
+ "SANG_TEN_XE_CUNG_TINH": {"code":"ST_CT","name":"Sang tên xe trong cùng tỉnh/thành phố","docs":"Hợp đồng mua bán công chứng, Chứng nhận thu hồi biển số, CCCD chủ mới","coquan":"Công an Quận/Huyện","time":"2-5 ngày","lephi":"Thuế trước bạ (1% - 2%) + phí đổi cavet","note":"Chủ cũ phải làm thủ tục thu hồi biển số trước","steps":"1. Công chứng hợp đồng mua bán. 2. Chủ cũ nộp biển số/cavet. 3. Chủ mới đóng thuế trước bạ. 4. Chủ mới đăng ký tại Công an.","category":"GIAO_THONG"},
152
+ "SANG_TEN_XE_KHAC_TINH": {"code":"ST_KT","name":"Sang tên xe đi tỉnh khác (Di chuyển nguyên chủ)","docs":"Hồ sơ gốc của xe, Chứng nhận thu hồi, CCCD","coquan":"Công an nơi đến","time":"5-7 ngày","lephi":"Theo biểu phí khu vực đến","note":"Phải rút hồ sơ gốc (thu hồi) tại tỉnh cũ","steps":"1. Làm thủ tục thu hồi tại tỉnh cũ. 2. Cầm hồ sơ gốc đến Công an tỉnh mới. 3. Đóng thuế trước bạ tại nơi mới. 4. Bấm biển số mới.","category":"GIAO_THONG"},
153
+ "DOI_MAU_SON_XE": {"code":"MAU_SON","name":"Đổi màu sơn xe (Thay đổi màu sắc xe)","docs":"Cavet xe, CCCD, Đơn đề nghị thay đổi màu sơn","coquan":"Phòng/Đội CSGT","time":"1-2 ngày","lephi":"30.000đ - 50.000đ","note":"Phải làm thủ tục TRƯỚC khi sơn lại xe","steps":"1. Mang xe đến CSGT. 2. Cán bộ kiểm tra xe hiện tại. 3. Viết đơn xin đổi màu sơn. 4. Đi sơn xe. 5. Đến cấp lại cavet mới với màu sơn mới.","category":"GIAO_THONG"},
154
+ "CAP_LAI_BIEN_SO_MAT": {"code":"BS_MAT","name":"Cấp lại biển số xe bị mất/hỏng","docs":"CCCD, Cavet xe, Đơn trình báo mất biển số","coquan":"Phòng CSGT nơi đăng ký","time":"7 ngày làm việc","lephi":"150.000đ","note":"Biển số cấp lại vẫn là biển số định danh cũ của bạn","steps":"1. Nộp đơn báo mất tại Công an. 2. Kê khai trên DVC. 3. Chờ dập lại biển số mới theo số cũ.","category":"GIAO_THONG"},
155
+ "DANG_KY_XE_MAY_DIEN": {"code":"XE_DIEN","name":"Đăng ký biển số xe máy điện","docs":"Hóa đơn GTGT, Phiếu kiểm tra chất lượng xuất xưởng, CCCD","coquan":"Công an xã/phường","time":"1 ngày","lephi":"Tùy khu vực (từ 50.000đ)","note":"Xe máy điện bắt buộc phải có biển số mới được lưu thông","steps":"1. Đóng thuế trước bạ. 2. Khai báo trên cổng DVC Bộ Công an. 3. Mang xe đến công an xã bấm biển.","category":"GIAO_THONG"},
156
+
157
+ # XÂY DỰNG (1)
158
+ "CAP_PHEP_XAY_DUNG": {"code":"PHEP_XD","name":"Cấp phép xây dựng nhà ở","docs":"Đơn xin, Giấy chứng nhận quyền sử dụng đất, Bản vẽ quy hoạch","coquan":"Phòng Đô thị","time":"15-20 ngày","lephi":"50.000đ-200.000đ","note":"Cần quy hoạch","steps":"1. Lấy bản vẽ quy hoạch 1/500. 2. Thiết kế bản vẽ xây dựng. 3. Làm đơn xin cấp phép. 4. Nộp tại Phòng Đô thị.","category":"XAYDUNG"},
159
+ "THONG_BAO_KHOI_CONG": {"code": "KHOI_CONG", "name": "Thông báo khởi công xây dựng", "docs": "GPXD, Hợp đồng bảo hiểm, Thông tin nhà thầu", "coquan": "UBND Xã/Phường", "time": "3 ngày trước khi làm", "lephi": "Miễn phí", "note": "Bắt buộc phải báo trước khi đào móng", "steps": "1. Chuẩn bị GPXD. 2. Nộp đơn thông báo tại phường.", "category": "XAYDUNG"},
160
+ "GIA_HAN_GPXD": {"code": "GH_GPXD", "name": "Gia hạn Giấy phép xây dựng", "docs": "Bản gốc GPXD, Đơn xin gia hạn", "coquan": "Cơ quan cấp phép cũ", "time": "5 ngày", "lephi": "15.000đ", "note": "Phải làm trước khi hết hạn 12 tháng", "steps": "1. Nộp đơn khi gần hết hạn. 2. Đóng dấu gia hạn vào bản gốc.", "category": "XAYDUNG"},
161
+ "PHA_DO_CONG_TRINH": {"code": "PHA_DO", "name": "Cấp phép phá dỡ công trình", "docs": "Phương án phá dỡ, Đơn xin phá dỡ", "coquan": "UBND Quận/Huyện", "time": "7-10 ngày", "lephi": "Miễn phí", "note": "Đảm bảo an toàn cho các nhà liền kề", "steps": "1. Lập phương án che chắn. 2. Nộp thông báo phá dỡ.", "category": "XAYDUNG"},
162
+
163
+ # KINH DOANH (1)
164
+ "CAP_PHEP_KINH_DOANH": {"code":"PHEP_KD","name":"Cấp giấy phép kinh doanh","docs":"Đơn xin cấp phép kinh doanh, CCCD, Địa chỉ kinh doanh","coquan":"Phòng Đăng ký kinh doanh hoặc UBND","time":"3-5 ngày","lephi":"Miễn phí","note":"Kinh doanh cá thể miễn phí","steps":"1. Làm đơn xin cấp phép kinh doanh. 2. Chuẩn bị CCCD, địa chỉ kinh doanh. 3. Nộp tại Phòng ĐKKD hoặc UBND.","category":"KINHDOANH"},
165
+
166
+ # Y TẾ (2)
167
+ "BHYT_CAP_MOI": {"code": "BHYT_CAP", "name": "Đăng ký đóng, cấp thẻ BHYT đối với người chỉ tham gia BHYT", "docs": "CCCD, Tờ khai tham gia BHXH, BHYT (Mẫu TK1-TS)", "coquan": "Cơ quan Bảo hiểm xã hội / Đại lý thu", "time": "5 ngày làm việc", "lephi": "Theo mức đóng quy định", "note": "Đóng theo hộ gia đình sẽ được giảm trừ mức đóng từ người thứ 2", "steps": "1. Điền tờ khai Mẫu TK1-TS. 2. Đóng tiền cho Đại lý thu/Bưu điện. 3. Nhận thẻ BHYT mới.", "category": "YTE"},
168
+ "BHYT_BHXH_DOI": {"code": "BHYT_DOI", "name": "Cấp lại, đổi, điều chỉnh thông tin trên sổ BHXH, thẻ BHYT", "docs": "Tờ khai (Mẫu TK1-TS), Sổ BHXH/Thẻ BHYT cũ, Giấy tờ minh chứng thay đổi", "coquan": "Bảo hiểm xã hội", "time": "Trong ngày (nếu không đổi thông tin) / 3-5 ngày (có đổi thông tin)", "lephi": "Miễn phí", "note": "Có thể thực hiện thủ tục trực tuyến qua ứng dụng VssID hoặc Cổng DVC", "steps": "1. Kê khai thông tin cần điều chỉnh. 2. Nộp hồ sơ kèm minh chứng. 3. Nhận thẻ/sổ mới.", "category": "YTE"},
169
+ "BHYT_THANH_TOAN": {"code": "BHYT_TT", "name": "Thanh toán trực tiếp chi phí KCB BHYT / Cấp giấy chứng nhận không cùng chi trả", "docs": "Thẻ BHYT, CCCD, Hóa đơn viện phí gốc, Bản sao bệnh án/Giấy ra viện", "coquan": "Cơ quan BHXH cấp huyện/tỉnh", "time": "40 ngày", "lephi": "Miễn phí", "note": "Áp dụng lấy lại tiền khi khám trái tuyến hoặc cấp GCN miễn đồng chi trả (tham gia 5 năm liên tục)", "steps": "1. Tập hợp hóa đơn đỏ viện phí. 2. Nộp hồ sơ tại BHXH. 3. Nhận tiền thanh toán qua tài khoản.", "category": "YTE"},
170
+ "BH_THAI_SAN": {"code": "THAI_SAN", "name": "Giải quyết hưởng chế độ thai sản", "docs": "Giấy chứng sinh/khai sinh của con, Giấy ra viện (nếu có)", "coquan": "Cơ quan BHXH (qua Công ty)", "time": "6-10 ngày làm việc", "lephi": "Miễn phí", "note": "Cần đóng đủ 6 tháng BHXH trong vòng 12 tháng trước khi sinh", "steps": "1. Nộp giấy tờ sinh con cho Công ty. 2. Công ty lập danh sách nộp lên BHXH. 3. Nhận tiền thai sản.", "category": "YTE"},
171
+ "BH_OM_DAU": {"code": "OM_DAU", "name": "Giải quyết hưởng chế độ ốm đau", "docs": "Giấy ra viện (nội trú) hoặc Giấy chứng nhận nghỉ việc hưởng BHXH (ngoại trú)", "coquan": "Cơ quan BHXH (qua Công ty)", "time": "6 ngày làm việc", "lephi": "Miễn phí", "note": "Phải nộp hồ sơ cho công ty trong vòng 45 ngày từ lúc đi làm lại", "steps": "1. Xin giấy nghỉ ốm theo chuẩn Bộ Y tế. 2. Nộp cho kế toán. 3. BHXH chuyển tiền trợ cấp.", "category": "YTE"},
172
+ "BH_DSPHSK": {"code": "DSPHSK", "name": "Giải quyết hưởng trợ cấp nghỉ dưỡng sức phục hồi sức khỏe (DSPHSK)", "docs": "Danh sách đề nghị giải quyết hưởng chế độ (Mẫu 01B-HSB do công ty lập)", "coquan": "Cơ quan BHXH (qua Công ty)", "time": "6-10 ngày làm việc", "lephi": "Miễn phí", "note": "Dành cho NLĐ đã hết thời gian nghỉ ốm đau, thai sản mà sức khỏe còn yếu", "steps": "1. Công đoàn và Công ty xem xét số ngày nghỉ. 2. Công ty lập mẫu nộp BHXH. 3. Nhận tiền.", "category": "YTE"},
173
+ "BH_TNLD_LAN1": {"code": "TNLD_L1", "name": "Giải quyết hưởng chế độ Tai nạn lao động, Bệnh nghề nghiệp (TNLĐ, BNN) lần đầu", "docs": "Biên bản điều tra TNLĐ, Giấy ra viện, Biên bản giám định tỷ lệ suy giảm KNLĐ", "coquan": "Cơ quan BHXH", "time": "6 ngày làm việc", "lephi": "Miễn phí", "note": "Mức hưởng phụ thuộc vào tỷ lệ giám định suy giảm khả năng lao động", "steps": "1. Công ty lập biên bản tai nạn. 2. Đưa đi giám định y khoa. 3. Nộp hồ sơ nhận trợ cấp.", "category": "YTE"},
174
+ "BH_TNLD_TIEP": {"code": "TNLD_TIEP", "name": "Giải quyết hưởng chế độ TNLĐ, BNN đối với trường hợp tiếp tục bị TNLĐ/BNN", "docs": "Hồ sơ vụ tai nạn mới, Biên bản giám định tổng hợp tỷ lệ suy giảm KNLĐ", "coquan": "Cơ quan BHXH", "time": "6 ngày làm việc", "lephi": "Miễn phí", "note": "Áp dụng khi đang hưởng trợ cấp tai nạn cũ mà tiếp tục bị tai nạn mới", "steps": "1. Lập hồ sơ tai nạn mới. 2. Giám định tổng hợp phần trăm thương tật. 3. Nộp hồ sơ hưởng mức mới.", "category": "YTE"},
175
+ "BH_TNLD_TAIPHAT": {"code": "TNLD_TP", "name": "Giải quyết chế độ TNLĐ, BNN do thương tật, bệnh tật tái phát", "docs": "Hồ sơ y tế điều trị tái phát, Biên bản giám định lại thương tật", "coquan": "Cơ quan BHXH", "time": "6 ngày làm việc", "lephi": "Miễn phí", "note": "Dành cho người đã hưởng TNLĐ nay bị đau lại do vết thương cũ", "steps": "1. Đi viện điều trị đợt tái phát. 2. Xin giám định lại tỷ lệ thương tật. 3. Nộp xin điều chỉnh trợ cấp.", "category": "YTE"},
176
+
177
+ # GIÁO DỤC (1)
178
+ "HOC_BONG": {"code":"HOC_BONG","name":"Đăng ký xét học bổng","docs":"Đơn xin học bổng, Bảng tổng kết, Học bạ","coquan":"Trường học hoặc Phòng GD-ĐT","time":"7-10 ngày","lephi":"Mi��n phí","note":"Theo từng loại học bổng","steps":"1. Làm đơn xin xét học bổng. 2. Chuẩn bị hồ sơ (bảng tổng kết, học bạ). 3. Nộp tại trường hoặc Phòng GD-ĐT.","category":"GIAODUC"},
179
+ "CN_BANG_DH_NN": {"code": "CN_BANG_DH", "name": "Công nhận bằng Cử nhân, Thạc sĩ, Tiến sĩ do nước ngoài cấp", "docs": "Bản sao văn bằng + bảng điểm, Bản dịch công chứng, Hộ chiếu/Minh chứng thời gian học ở nước ngoài", "coquan": "Cục Quản lý chất lượng (Bộ GD&ĐT)", "time": "20 ngày làm việc", "lephi": "500.000đ/văn bằng", "note": "Thực hiện trực tuyến 100% trên Cổng DVC Bộ GD&ĐT", "steps": "1. Tạo hồ sơ online. 2. Nộp lệ phí trực tuyến. 3. Cục thẩm định hồ sơ. 4. Nhận Giấy công nhận bản điện tử/giấy.", "category": "GIAODUC"},
180
+ "CN_BANG_PT_NN": {"code": "CN_BANG_PT", "name": "Công nhận bằng tốt nghiệp THCS, THPT do nước ngoài cấp", "docs": "Bản sao văn bằng, Bảng điểm, Bản dịch công chứng, Giấy tờ tùy thân", "coquan": "Sở Giáo dục và Đào tạo địa phương", "time": "20 ngày làm việc", "lephi": "250.000đ/văn bằng", "note": "Dành cho học sinh học ở nước ngoài muốn về VN học tiếp hoặc xét tuyển", "steps": "1. Nộp hồ sơ trên cổng DVC của Sở. 2. Đóng phí. 3. Thẩm định. 4. Nhận Giấy công nhận.", "category": "GIAODUC"},
181
+ "DAC_CACH_THPT": {"code": "DAC_CACH", "name": "Xét đặc cách tốt nghiệp THPT", "docs": "Đơn đề nghị, Hồ sơ nhập viện/Giấy ra viện (nếu ốm đau), Biên bản xác nhận của Hội đồng thi", "coquan": "Sở Giáo dục và Đào tạo", "time": "Chậm nhất 07 ngày sau buổi thi cuối cùng", "lephi": "Miễn phí", "note": "Chỉ áp dụng khi thí sinh bị tai nạn, ốm đau đột xuất trước hoặc trong kỳ thi", "steps": "1. Nộp đơn và hồ sơ bệnh án cho Trưởng Điểm thi/Trường THPT. 2. Trường nộp lên Sở GD&ĐT. 3. Sở ra quyết định đặc cách.", "category": "GIAODUC"},
182
+ "PHUC_KHAO_THPT": {"code": "PHUC_KHAO", "name": "Phúc khảo bài thi tốt nghiệp THPT", "docs": "Đơn đề nghị phúc khảo bài thi (theo mẫu)", "coquan": "Nơi đăng ký dự thi (Trường THPT hoặc Phòng GD&ĐT)", "time": "15 ngày kể từ ngày hết hạn nhận đơn", "lephi": "Miễn phí (hoặc theo quy định của Sở)", "note": "Thí sinh phải nộp đơn trong vòng 10 ngày kể từ khi công bố điểm thi", "steps": "1. Viết đơn nộp tại trường cấp 3. 2. Trường chuyển dữ liệu lên Sở. 3. Tổ chức chấm lại. 4. Công bố điểm phúc khảo.", "category": "GIAODUC"},
183
+ "XET_TN_THAC_SI": {"code": "TN_THS", "name": "Xét tốt nghiệp và cấp bằng Thạc sĩ", "docs": "Đơn xin xét tốt nghiệp, Bảng điểm toàn khóa, Chứng chỉ ngoại ngữ, Quyết định bảo vệ luận văn", "coquan": "Phòng Đào tạo Sau đại học (Cơ sở GD Đại học)", "time": "Theo kế hoạch xét tốt nghiệp của trường", "lephi": "Lệ phí cấp bằng (tùy trường)", "note": "Học viên phải nộp lưu chiểu luận văn trước khi xét", "steps": "1. Nộp đơn xin xét tốt nghiệp. 2. Hội đồng trường họp xét duyệt. 3. Hiệu trưởng ký quyết định. 4. Cấp bằng và bảng điểm.", "category": "GIAODUC"},
184
+ "XET_CAP_TIEN_SI": {"code": "CAP_TS", "name": "Xét cấp bằng Tiến sĩ", "docs": "Hồ sơ bảo vệ luận án cấp viện/trường, Giấy biên nhận nộp lưu chiểu Thư viện Quốc gia", "coquan": "Cơ sở GD Đại học / Viện nghiên cứu", "time": "Theo đợt xét tốt nghiệp", "lephi": "Lệ phí cấp bằng (tùy trường)", "note": "Bắt buộc phải nộp bản cứng luận án vào Thư viện Quốc gia Việt Nam", "steps": "1. Hoàn thiện hồ sơ sau khi bảo vệ thành công. 2. Nộp lưu chiểu. 3. Hội đồng chức danh xét duyệt. 4. Cấp bằng.", "category": "GIAODUC"},
185
+ "TUYEN_SINH_10": {"code": "TS_10", "name": "Tuyển sinh vào lớp 10 trung học phổ thông", "docs": "Đơn dự tuyển, Học bạ THCS, Giấy CN tốt nghiệp THCS tạm thời, Giấy chứng nhận ưu tiên (nếu có)", "coquan": "Sở Giáo dục và Đào tạo / Trường THPT", "time": "Theo lịch tuyển sinh của tỉnh/thành phố", "lephi": "Lệ phí thi (khoảng 150.000đ - 300.000đ)", "note": "Học sinh lớp 9 thường được đăng ký nguyện vọng trực tuyến tại trường THCS đang học", "steps": "1. Đăng ký nguyện vọng trực tuyến. 2. Nhận thẻ dự thi. 3. Tham gia kỳ thi. 4. Xem điểm chuẩn và nộp hồ sơ nhập học.", "category": "GIAODUC"},
186
+ "BO_TUC_THCS": {"code": "BT_THCS", "name": "Tiếp nhận đối tượng học bổ túc trung học cơ sở", "docs": "Đơn xin học, Bản sao Giấy khai sinh, Học bạ (nếu đã học dở dang), CCCD", "coquan": "Trung tâm Giáo dục thường xuyên / Cơ sở GDTX cấp huyện", "time": "Theo kỳ tuyển sinh (thường vào tháng 8-9)", "lephi": "Miễn phí (hoặc theo quy định hỗ trợ của địa phương)", "note": "Dành cho người đã quá tuổi học THCS chính quy (từ 15 tuổi trở lên)", "steps": "1. Mua/Tải hồ sơ tại Trung tâm GDTX. 2. Điền thông tin và nộp lại kèm học bạ cũ. 3. Giám đốc Trung tâm ra quyết định tiếp nhận.", "category": "GIAODUC"},
187
+ "DK_THI_THPT": {"code": "THI_THPT", "name": "Đăng ký dự thi tốt nghiệp trung học phổ thông", "docs": "Phiếu đăng ký dự thi, CCCD, Học bạ THPT, Giấy chứng nhận ưu tiên (nếu có)", "coquan": "Trường THPT (với thí sinh lớp 12) hoặc Sở GD&ĐT (với thí sinh tự do)", "time": "Theo lịch của Bộ GD&ĐT (thường vào tháng 4-5)", "lephi": "Lệ phí xét tuyển (tùy số lượng nguyện vọng ĐH)", "note": "Thí sinh lớp 12 thực hiện đăng ký 100% bằng hình thức trực tuyến (online)", "steps": "1. Nhận tài khoản từ trường. 2. Đăng nhập hệ thống của Bộ GD&ĐT. 3. Điền thông tin, chọn môn thi, nguyện vọng. 4. In phiếu, ký xác nhận.", "category": "GIAODUC"},
188
+
189
+ # KHÁC (2)
190
+ "XIN_VIEC": {"code":"XIN_VIEC","name":"Đăng ký tìm việc làm","docs":"CCCD, Hồ sơ xin việc","coquan":"Trung tâm dịch vụ việc làm","time":"1-2 ngày","lephi":"Miễn phí","note":"","steps":"1. Chuẩn bị hồ sơ xin việc (CCCD, sơ yếu lý lịch). 2. Đến Trung tâm dịch vụ việc làm. 3. Đăng ký và được giới thiệu việc làm.","category":"KHAC"},
191
+ "TRO_CAP": {"code":"TRO_CAP","name":"Đăng ký trợ cấp xã hội","docs":"Đơn xin trợ cấp, CCCD, Sổ hộ khẩu","coquan":"UBND xã/phường","time":"7-10 ngày","lephi":"Miễn phí","note":"","steps":"1. Làm đơn xin trợ cấp xã hội. 2. Xin xác nhận hoàn cảnh khó khăn. 3. Nộp tại UBND xã/phường.","category":"KHAC"},
192
+ }
193
+
194
+
195
+ print(f'✅ Dataset Dịch vụ công: {len(DICHVUCONG_DATA)} thủ tục')
196
+ print()
197
+
198
+ # Lưu data
199
+ with open(os.path.join(DATA_DIR, 'data/dichvucong.json'), 'w', encoding='utf-8') as f:
200
+ json.dump(DICHVUCONG_DATA, f, ensure_ascii=False, indent=2)
201
+ print('✅ Đã lưu data/dichvucong.json')
202
+ print()
203
+ print('='*70)
204
+
205
+ # ======================================================================
206
+ # ============================================================
207
+ # PHẦN 4: BUILD DOCUMENTS & RAG SYSTEM
208
+ # ============================================================
209
+
210
+ print('='*70)
211
+ print('📦 XÂY DỰNG VECTOR DATABASE & RAG SYSTEM')
212
+ print('='*70)
213
+ print()
214
+
215
+ from langchain_huggingface.embeddings import HuggingFaceEmbeddings
216
+ from langchain_core.documents import Document
217
+ from rank_bm25 import BM25Okapi
218
+
219
+ # Build documents
220
+ print('🔄 Building documents...')
221
+ all_docs = []
222
+
223
+ # From Dataset
224
+ for i, row in df1.iterrows():
225
+ # Truncate text to avoid Pinecone metadata size limit
226
+ truncated_text = row['text'][:10000] # Limit to 10,000 characters
227
+ text = f"THỦ TỤC: {row['title']}\nNỘI DUNG: {truncated_text}"
228
+ all_docs.append(Document(page_content=text, metadata={"source": "ds1", "id": i}))
229
+ print(f' - Dataset: {len([d for d in all_docs if d.metadata["source"] == "ds1"])} docs')
230
+
231
+ # From Dịch vụ công
232
+ for code, proc in DICHVUCONG_DATA.items():
233
+ text = f"""MÃ: {proc['code']}
234
+ THỦ TỤC: {proc['name']}
235
+ HỒ SƠ: {proc['docs']}
236
+ CƠ QUAN: {proc['coquan']}
237
+ THỜI GIAN: {proc['time']}
238
+ LỆ PHÍ: {proc['lephi']}
239
+ LƯU Ý: {proc['note']}
240
+ QUY TRÌNH: {proc['steps']}"""
241
+ all_docs.append(Document(page_content=text, metadata={"source": "dvc", "code": code}))
242
+ print(f' - Dịch vụ công: {len([d for d in all_docs if d.metadata["source"] == "dvc"])} docs')
243
+
244
+ print(f'\n✅ Total documents: {len(all_docs):,}')
245
+ print()
246
+
247
+ # Initialize Embeddings
248
+ print('🔄 Loading embeddings model...')
249
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
250
+ embeddings = HuggingFaceEmbeddings(
251
+ model_name='keepitreal/vietnamese-sbert',
252
+ model_kwargs={'device': device},
253
+ encode_kwargs={'normalize_embeddings': True}
254
+ )
255
+ print('✅ Embeddings loaded!')
256
+ print()
257
+
258
+ # Configure Pinecone
259
+ print('🔄 Configuring Pinecone...')
260
+
261
+ # Import Pinecone libraries
262
+ from pinecone import Pinecone, PineconeApiException
263
+ from langchain_pinecone import PineconeVectorStore
264
+ import time
265
+
266
+ # Nhập PINECONE_API_KEY của bạn
267
+ # Lấy token tại: https://app.pinecone.io/keys
268
+ # Trên Hugging Face Spaces, set PINECONE_API_KEY trong Secrets
269
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY', '')
270
+ index_name = 'ai-hanh-chinh-rag'
271
+ dimension = 768 # keepitreal/vietnamese-sbert dimension
272
+
273
+ # Global variables
274
+ vector_db = None
275
+ pc = None
276
+ pinecone_available = False
277
+
278
+ # Hàm kết nối Pinecone với retry
279
+ def connect_pinecone(max_retries=3):
280
+ global pc, vector_db, pinecone_available
281
+
282
+ if not PINECONE_API_KEY:
283
+ print('⚠️ C���n thiết lập PINECONE_API_KEY trong Secrets của Hugging Face Space!')
284
+ print(' 1. Vào: https://app.pinecone.io/keys')
285
+ print(' 2. Tạo API Key')
286
+ print(' 3. Vào Settings → Repository secrets → Thêm: PINECONE_API_KEY')
287
+ return False
288
+
289
+ for attempt in range(max_retries):
290
+ try:
291
+ print(f' 🔑 Đang kết nối Pinecone (lần {attempt + 1}/{max_retries})...')
292
+
293
+ # Initialize Pinecone client
294
+ pc = Pinecone(api_key=PINECONE_API_KEY)
295
+
296
+ # Kiểm tra index đã tồn tại chưa
297
+ existing_indexes = [index['name'] for index in pc.list_indexes().get('indexes', [])]
298
+
299
+ if index_name not in existing_indexes:
300
+ print(f' 📝 Đang tạo index mới: {index_name}...')
301
+ pc.create_index(
302
+ name=index_name,
303
+ dimension=dimension,
304
+ metric='cosine',
305
+ spec={'serverless': {'cloud': 'aws', 'region': 'us-east-1'}}
306
+ )
307
+ # Chờ index sẵn sàng
308
+ print(' ⏳ Chờ index sẵn sàng...')
309
+ time.sleep(10)
310
+ else:
311
+ print(f' ✅ Index {index_name} đã tồn tại')
312
+
313
+ # Kết nối vector store
314
+ vector_db = PineconeVectorStore(
315
+ index_name=index_name,
316
+ embedding=embeddings,
317
+ pinecone_api_key=PINECONE_API_KEY
318
+ )
319
+
320
+ # Kiểm tra số vectors trong index
321
+ index = pc.Index(index_name)
322
+ stats = index.describe_index_stats()
323
+ vector_count = stats.get('total_vector_count', 0)
324
+ print(f' 📊 Index hiện có: {vector_count:,} vectors')
325
+
326
+ # Nếu index rỗng, thêm documents
327
+ if vector_count == 0:
328
+ print(f' 📤 Đang thêm {len(all_docs):,} documents vào Pinecone...')
329
+ ids = [f"doc_{doc.metadata.get('source', 'unk')}_{i}" for i, doc in enumerate(all_docs)]
330
+
331
+ # Thêm theo batch để tránh lỗi
332
+ batch_size = 100
333
+ for i in range(0, len(all_docs), batch_size):
334
+ batch_docs = all_docs[i:i+batch_size]
335
+ batch_ids = ids[i:i+batch_size]
336
+ vector_db.add_documents(documents=batch_docs, ids=batch_ids)
337
+ print(f' ✅ Đã thêm {min(i+batch_size, len(all_docs))}/{len(all_docs)} documents')
338
+
339
+ print(' ✅ Hoàn tất thêm documents vào Pinecone!')
340
+
341
+ pinecone_available = True
342
+ print('✅ Pinecone ready!')
343
+ return True
344
+
345
+ except PineconeApiException as e:
346
+ print(f' ⚠️ Pinecone API Error: {e}')
347
+ if attempt < max_retries - 1:
348
+ print(f' 🔄 Thử lại sau 2 giây...')
349
+ time.sleep(2)
350
+ else:
351
+ print(' ❌ Không thể kết nối Pinecone sau {max_retries} lần thử')
352
+ return False
353
+ except Exception as e:
354
+ print(f' ❌ Lỗi kết nối Pinecone: {e}')
355
+ if attempt < max_retries - 1:
356
+ print(f' 🔄 Thử lại sau 2 giây...')
357
+ time.sleep(2)
358
+ else:
359
+ print(' ❌ Không thể kết nối Pinecone sau {max_retries} lần thử')
360
+ return False
361
+
362
+ pinecone_available = False
363
+ return False
364
+
365
+ # Kết nối Pinecone
366
+ connect_pinecone()
367
+ print()
368
+
369
+ # Create BM25
370
+ print('🔄 Creating BM25 index...')
371
+ corpus = [doc.page_content for doc in all_docs]
372
+ tokenized_corpus = [doc.split() for doc in corpus]
373
+ bm25 = BM25Okapi(tokenized_corpus)
374
+ print('✅ BM25 ready!')
375
+ print()
376
+ print('='*70)
377
+
378
+ # ======================================================================
379
+ # ============================================================
380
+ # PHẦN 5: TRAIN CLASSIFIER PHÂN LOẠI TÌNH HUỐNG
381
+ # ============================================================
382
+
383
+ print('='*70)
384
+ print('🎯 TRAIN CLASSIFIER PHÂN LOẠI TÌNH HUỐNG')
385
+ print('='*70)
386
+ print()
387
+
388
+ from sklearn.feature_extraction.text import TfidfVectorizer
389
+ from sklearn.ensemble import RandomForestClassifier
390
+
391
+ # Danh sách tình huống từ DICHVUCONG_DATA
392
+ SITUATIONS = list(DICHVUCONG_DATA.keys())
393
+
394
+ # Tạo training data từ DICHVUCONG_DATA
395
+ X_train = []
396
+ y_train = []
397
+
398
+ for code, proc in DICHVUCONG_DATA.items():
399
+ # Tạo các câu hỏi mẫu cho mỗi thủ tục
400
+ questions = [
401
+ f"Làm {proc['name'].lower()} cần gì?",
402
+ f"{proc['name']} ở đâu?",
403
+ f"Thủ tục {proc['name']} như thế nào?",
404
+ proc['name'].lower(),
405
+ f"Cách làm {proc['name'].lower()}",
406
+ ]
407
+ for q in questions:
408
+ X_train.append(q)
409
+ y_train.append(code)
410
+
411
+ # Thêm các câu hỏi chung
412
+ general_questions = [
413
+ "cho tôi hỏi", "tôi muốn hỏi", "ai giúp tôi với",
414
+ "cần làm gì", "ở đâu", "bao lâu", "bao nhiêu ti��n"
415
+ ]
416
+ for q in general_questions:
417
+ X_train.append(q)
418
+ y_train.append('hoi')
419
+
420
+ print(f'📊 Training data: {len(X_train)} samples')
421
+ print(f' - Situations: {len(SITUATIONS)}')
422
+
423
+ # Train TF-IDF vectorizer
424
+ vec = TfidfVectorizer(max_features=1000)
425
+ X_train_vec = vec.fit_transform(X_train)
426
+
427
+ # Train classifier
428
+ clf = RandomForestClassifier(n_estimators=50, max_depth=10, random_state=42)
429
+ clf.fit(X_train_vec, y_train)
430
+
431
+ print('✅ Classifier trained successfully!')
432
+ print()
433
+ print('='*70)
434
+
435
+ # ======================================================================
436
+ # ============================================================
437
+ # PHẦN 5.1: HYBRID SEARCH FUNCTION
438
+ # ============================================================
439
+ def hybrid_search(query, k_final=5):
440
+ """Hybrid Search: Pinecone + BM25 với error handling"""
441
+
442
+ # Danh sách kết quả
443
+ dense_results = []
444
+ sparse_results = []
445
+
446
+ # 1. DENSE SEARCH từ Pinecone
447
+ if pinecone_available and vector_db is not None:
448
+ try:
449
+ # Tìm kiếm với score (cosine similarity)
450
+ results = vector_db.similarity_search_with_score(query, k=10)
451
+
452
+ # Pinecone trả về (doc, score) với score là distance (càng nhỏ càng tốt)
453
+ # Chuyển thành similarity (càng lớn càng tốt)
454
+ for doc, distance in results:
455
+ if distance < 0:
456
+ # Nếu score là similarity (đã chuẩn)
457
+ similarity = distance
458
+ else:
459
+ # Nếu score là distance, chuyển thành similarity
460
+ similarity = 1 - distance
461
+
462
+ dense_results.append({
463
+ 'doc': doc,
464
+ 'score': similarity,
465
+ 'source': 'pinecone'
466
+ })
467
+
468
+ except Exception as e:
469
+ print(f'⚠️ Pinecone search error: {e}, dùng fallback local docs...')
470
+ # Fallback: dùng local documents nếu Pinecone lỗi
471
+ dense_results = []
472
+
473
+ # 2. SPARSE SEARCH từ BM25 (local)
474
+ try:
475
+ tokenized_query = query.split()
476
+ bm25_scores = bm25.get_scores(tokenized_query)
477
+
478
+ # Lấy top 10 kết quả
479
+ top_indices = np.argsort(bm25_scores)[::-1][:10]
480
+
481
+ for idx in top_indices:
482
+ if idx < len(all_docs):
483
+ sparse_results.append({
484
+ 'doc': all_docs[idx],
485
+ 'score': float(bm25_scores[idx]),
486
+ 'source': 'bm25'
487
+ })
488
+ except Exception as e:
489
+ print(f'⚠️ BM25 search error: {e}')
490
+ sparse_results = []
491
+
492
+ # 3. KẾT HỢP (Merge) kết quả
493
+ combined = {}
494
+
495
+ # Thêm kết quả từ Pinecone (ưu tiên cao hơn)
496
+ for item in dense_results:
497
+ doc_id = item['doc'].page_content[:100]
498
+ combined[doc_id] = item
499
+
500
+ # Thêm kết quả từ BM25 (nếu chưa có)
501
+ for item in sparse_results:
502
+ doc_id = item['doc'].page_content[:100]
503
+ if doc_id not in combined:
504
+ combined[doc_id] = item
505
+ else:
506
+ # Nếu đã có từ Pinecone, cộng thêm điểm BM25
507
+ combined[doc_id]['score'] += item['score'] * 0.3
508
+
509
+ # 4. SẮP XẾP và trả về top k_final
510
+ sorted_results = sorted(combined.values(), key=lambda x: x['score'], reverse=True)[:k_final]
511
+
512
+ # Trả về list documents
513
+ return [item['doc'] for item in sorted_results]
514
+
515
+ # Hàm lấy documents trực tiếp từ Pinecone
516
+ def search_pinecone_only(query, k=5):
517
+ """Tìm kiếm chỉ dùng Pinecone (RAG thuần túy)"""
518
+ if not pinecone_available or vector_db is None:
519
+ print('⚠️ Pinecone không khả dụng, dùng local documents...')
520
+ return all_docs[:k]
521
+
522
+ try:
523
+ results = vector_db.similarity_search(query, k=k)
524
+ return results
525
+ except Exception as e:
526
+ print(f'⚠️ Lỗi Pinecone: {e}')
527
+ return all_docs[:k]
528
+
529
+ # Hàm lấy context từ RAG cho LLM
530
+ def get_rag_context(query, max_docs=5):
531
+ """Lấy context từ RAG system để đưa vào prompt LLM"""
532
+ docs = hybrid_search(query, k_final=max_docs)
533
+
534
+ context_parts = []
535
+ for i, doc in enumerate(docs, 1):
536
+ text = doc.page_content.strip()
537
+ if len(text) > 800:
538
+ text = text[:800] + "..."
539
+ context_parts.append(f"[Tài liệu {i}]: {text}")
540
+
541
+ return "\n\n".join(context_parts)
542
+
543
+ print('✅ Hybrid Search function ready!')
544
+ print(f' - Pinecone: {"✅ Connected" if pinecone_available else "❌ Not available"}')
545
+ print(f' - BM25: ✅ Ready')
546
+ print(f' - Total documents: {len(all_docs):,}')
547
+ print()
548
+ print('='*70)
549
+
550
+ # ======================================================================
551
+ # ============================================================
552
+ # PHẦN 6: LOAD LLM (QWEN 2.5 3B)
553
+ # ============================================================
554
+
555
+ print('='*70)
556
+ print('🤖 LOAD LLM (QWEN 2.5 3B INSTRUCT)')
557
+ print('='*70)
558
+ print()
559
+
560
+ from transformers import AutoModelForCausalLM, AutoTokenizer
561
+
562
+ MODEL_ID = 'Qwen/Qwen2.5-3B-Instruct'
563
+
564
+ print(f'Model: {MODEL_ID}')
565
+
566
+ # Detect device
567
+ HAS_GPU = torch.cuda.is_available()
568
+ device_str = "GPU" if HAS_GPU else "CPU"
569
+ print(f'🖥️ Device: {device_str}')
570
+
571
+ if HAS_GPU:
572
+ print('🔄 Loading model với 4-bit quantization (GPU mode)...')
573
+ from transformers import BitsAndBytesConfig
574
+
575
+ bnb_config = BitsAndBytesConfig(
576
+ load_in_4bit=True,
577
+ bnb_4bit_use_double_quant=True,
578
+ bnb_4bit_quant_type="nf4",
579
+ bnb_4bit_compute_dtype=torch.bfloat16
580
+ )
581
+
582
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
583
+
584
+ model = AutoModelForCausalLM.from_pretrained(
585
+ MODEL_ID,
586
+ quantization_config=bnb_config,
587
+ device_map="auto",
588
+ low_cpu_mem_usage=True,
589
+ trust_remote_code=True
590
+ )
591
+
592
+ quant_type = "4-bit NF4"
593
+ else:
594
+ print('🔄 Loading model với float16 (CPU mode - không dùng quantization)...')
595
+
596
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
597
+
598
+ model = AutoModelForCausalLM.from_pretrained(
599
+ MODEL_ID,
600
+ torch_dtype=torch.float16,
601
+ device_map="auto",
602
+ low_cpu_mem_usage=True,
603
+ trust_remote_code=True
604
+ )
605
+
606
+ quant_type = "Float16 (CPU)"
607
+
608
+ print('✅ LLM loaded successfully!')
609
+ print(f' - Device: {device_str}')
610
+ print(f' - Quantization: {quant_type}')
611
+ print()
612
+ print('='*70)
613
+
614
+ # ======================================================================
615
+ # ============================================================
616
+ # PHẦN 7: SETUP VOICE AI (WHISPER + EDGE-TTS) - LAZY LOAD
617
+ # ============================================================
618
+
619
+ print('='*70)
620
+ print('🎤 SETUP VOICE AI (LAZY MODE)')
621
+ print('='*70)
622
+ print()
623
+
624
+ import whisper
625
+ import edge_tts
626
+ import asyncio
627
+
628
+ # Flag để lazy load Whisper
629
+ whisper_model = None
630
+ whisper_device = 'cuda' if torch.cuda.is_available() else 'cpu'
631
+
632
+ def get_whisper_model():
633
+ """Lazy load Whisper chỉ khi cần"""
634
+ global whisper_model
635
+ if whisper_model is None:
636
+ print('🔄 Loading Whisper model (lazy load)...')
637
+ whisper_model = whisper.load_model('tiny', device=whisper_device) # Dùng 'tiny' thay 'base' để nhanh hơn
638
+ print('✅ Whisper ready!')
639
+ return whisper_model
640
+
641
+ def speech_to_text(audio_path):
642
+ """Convert speech to text"""
643
+ try:
644
+ if audio_path and os.path.exists(audio_path):
645
+ model = get_whisper_model()
646
+ result = model.transcribe(audio_path, language='vi', fp16=False)
647
+ return result["text"].strip()
648
+ except Exception as e:
649
+ print(f'Whisper error: {e}')
650
+ return ""
651
+
652
+ async def text_to_speech(text, voice='vi-VN-HoaiMyNeural'):
653
+ """Convert text to speech"""
654
+ try:
655
+ text = str(text)[:1500].replace('*','').replace('#','').replace('_','')
656
+ timestamp = datetime.now().strftime('%H%M%S')
657
+ output_path = os.path.join(DATA_DIR, f"audio_output/audio_{timestamp}.mp3")
658
+
659
+ communicate = edge_tts.Communicate(text, voice)
660
+ await communicate.save(output_path)
661
+ return output_path
662
+ except Exception as e:
663
+ print(f'TTS error: {e}')
664
+ return None
665
+
666
+ print('✅ Voice AI ready (Whisper sẽ load khi cần)!')
667
+ print()
668
+ print('='*70)
669
+
670
+
671
+
672
+
673
+ # ======================================================================
674
+ # ============================================================
675
+ # PHẦN 12.1: CÀI ĐẶT THƯ VIỆN NÂNG CAO
676
+ # ============================================================
677
+
678
+ print('='*70)
679
+ print('🚀 NÂNG CẤP AI PRO - IMPORT THƯ VIỆN')
680
+ print('='*70)
681
+ print()
682
+
683
+ # Thư viện bổ sung sẽ được cài đặt từ requirements.txt
684
+ # - sentencepiece>=0.1.99
685
+ # - protobuf>=3.20.0
686
+ print('✅ Sẵn sàng!')
687
+ print()
688
+
689
+ # Import bổ sung
690
+ from functools import lru_cache
691
+ from typing import List, Dict, Tuple
692
+ import re
693
+ import hashlib
694
+ import time
695
+
696
+ print('='*70)
697
+
698
+ # ======================================================================
699
+ # ============================================================
700
+ # PHẦN 12.2: PROMPT ENGINE NÂNG CAO (PERSONA-DRIVEN)
701
+ # ============================================================
702
+
703
+ print('='*70)
704
+ print('🎯 PROMPT ENGINE NÂNG CAO')
705
+ print('='*70)
706
+ print()
707
+
708
+ # Định nghĩa các persona cho AI
709
+ AI_PERSONAS = {
710
+ "thuong": {
711
+ "name": "Chị Thuong - Cán bộ tư vấn thân thiện",
712
+ "tone": "thân thiện, gần gũi, dùng từ ngữ bình dân",
713
+ "greeting": ["Chào bác/cháu ạ,", "Dạ vâng,", "Bác/cháu hỏi đúng người rồi ạ,"],
714
+ "closing": ["Có gì bác/chứ cứ hỏi thêm nhé!", "Chúc bác/cháu làm thủ tục thuận lợi ạ!"],
715
+ "style": "conversational"
716
+ },
717
+ "chuyen": {
718
+ "name": "Anh Chuyen - Chuyên viên hành chính công",
719
+ "tone": "chuyên nghiệp, ngắn gọn, súc tích",
720
+ "greeting": ["Dạ, về vấn đề", "Theo quy định,", "Về thủ tục"],
721
+ "closing": ["Trân trọng!", "Thông tin trên để tham khảo ạ."],
722
+ "style": "professional"
723
+ },
724
+ "chi_tiet": {
725
+ "name": "Cô Chi Tiet - Cán bộ hướng dẫn chi tiết",
726
+ "tone": "chi tiết, từng bước, cẩn thận",
727
+ "greeting": ["Dạ để em hướng dẫn chi tiết cho bác/cháu ạ,", "Em sẽ giải thích kỹ càng ạ,"],
728
+ "closing": ["Bác/cháu làm theo như trên là được ạ!", "Nếu chưa hiểu chỗ nào cứ hỏi thêm ạ!"],
729
+ "style": "detailed"
730
+ }
731
+ }
732
+
733
+ # Query Patterns để hiểu ý định người dùng
734
+ QUERY_PATTERNS = {
735
+ "can_gi": ["cần gì", "giấy tờ gì", "chuẩn bị gì", "hồ sơ gì", "documents"],
736
+ "o_day": ["ở đâu", "nơi nào", "địa chỉ", "đ到哪里", "đến đâu"],
737
+ "bao_lau": ["bao lâu", "mất bao lâu", "thời gian", "how long", "bao nhiêu ngày"],
738
+ "bao_nhieu": ["bao nhiêu", "bao tiền", "phí", "lệ phí", "cost", "giá"],
739
+ "the_quy_trinh": ["quy trình", "cách làm", "làm thế nào", "làm sao", "hướng dẫn", "how to"],
740
+ "nguoi_nuoc_ngoai": ["người nước ngoài", "ngoài", "không phải VN", "foreigner"],
741
+ "qua_han": ["qua hạn", "quá hạn", "muộn", "sau thời hạn", "overdue"],
742
+ "mat": ["mất", "thất lạc", "đánh rơi", "không thấy", "lost"],
743
+ "doi": ["đổi", "làm lại", "cập nhật", "thay đổi", "renew", "change"],
744
+ "moi": ["lần đầu", "mới", "chưa từng làm", "first time", "new"],
745
+ }
746
+
747
+ # Query Expansion - Mở rộng câu hỏi
748
+ def expand_query(query: str) -> List[str]:
749
+ """Mở rộng câu hỏi để tìm kiếm tốt hơn"""
750
+ expanded_queries = [query]
751
+ query_lower = query.lower()
752
+
753
+ # Thêm từ đồng nghĩa
754
+ for pattern, keywords in QUERY_PATTERNS.items():
755
+ for kw in keywords:
756
+ if kw in query_lower:
757
+ # Thêm các từ khóa liên quan
758
+ for related_kw in keywords:
759
+ if related_kw not in query_lower:
760
+ expanded_queries.append(query_lower.replace(kw, related_kw))
761
+
762
+ return list(set(expanded_queries))
763
+
764
+ # Detect intent from query
765
+ def detect_intent(query: str) -> Dict[str, any]:
766
+ """Phát hiện ý định người dùng từ câu hỏi"""
767
+ query_lower = query.lower()
768
+ intents = {
769
+ "need_docs": False,
770
+ "need_location": False,
771
+ "need_time": False,
772
+ "need_fee": False,
773
+ "need_process": False,
774
+ "is_foreign": False,
775
+ "is_overdue": False,
776
+ "is_lost": False,
777
+ "is_renewal": False,
778
+ "is_new": False
779
+ }
780
+
781
+ for pattern, keywords in QUERY_PATTERNS.items():
782
+ for kw in keywords:
783
+ if kw in query_lower:
784
+ intents[pattern] = True
785
+
786
+ return intents
787
+
788
+ # Dynamic Response Templates
789
+ RESPONSE_TEMPLATES = {
790
+ " khai_sinh": {
791
+ "short": "Dạ để khai sinh cho bé, bác/cháu cần mang: Giấy khai sinh, CCCD của cả cha mẹ, và sổ hộ khẩu. Ra UBND xã/phường nơi cư trú là được ạ!",
792
+ "detailed": """Dạ về khai sinh cho bé, em hướng dẫn chi tiết ạ:
793
+
794
+ 📋 **HỒ SƠ CẦN CHUẨN BỊ:**
795
+ 1. Giấy khai sinh (điền đầy đủ thông tin)
796
+ 2. CCCD/CMND của cả cha và mẹ
797
+ 3. Sổ hộ khẩu của cha mẹ
798
+
799
+ 🏢 **NƠI LÀM:** UBND xã/phường nơi cư trú
800
+
801
+ ⏰ **THỜI GIAN:** 1-3 ngày
802
+
803
+ 💰 **LỆ PHÍ:** Miễn phí
804
+
805
+ ⚠️ **LƯU Ý:** Làm trong 60 ngày kể từ khi sinh để tránh phạt nhé!"""
806
+ }
807
+ }
808
+
809
+ print('✅ Prompt Engine đã sẵn sàng!')
810
+ print(f' - Personas: {len(AI_PERSONAS)}')
811
+ print(f' - Query Patterns: {len(QUERY_PATTERNS)}')
812
+ print()
813
+ print('='*70)
814
+
815
+ # ======================================================================
816
+ # ============================================================
817
+ # PHẦN 12.3: CONTEXT MEMORY & CHAIN OF THOUGHT
818
+ # ============================================================
819
+
820
+ print('='*70)
821
+ print('🧠 CONTEXT MEMORY & CHAIN OF THOUGHT')
822
+ print('='*70)
823
+ print()
824
+
825
+ # Conversation Memory with Context Tracking
826
+ class ConversationMemory:
827
+ def __init__(self, max_history=10):
828
+ self.history = [] # Lưu lịch sử chat
829
+ self.context = {} # Lưu ngữ cảnh (tình huống, giấy tờ đã có, etc.)
830
+ self.max_history = max_history
831
+ self.persona = "thuong" # Persona mặc định
832
+
833
+ def add_message(self, role: str, content: str, metadata: dict = None):
834
+ """Thêm tin nhắn vào lịch sử"""
835
+ self.history.append({
836
+ "role": role,
837
+ "content": content,
838
+ "metadata": metadata or {},
839
+ "timestamp": time.time()
840
+ })
841
+
842
+ # Giữ lịch sử trong giới hạn
843
+ if len(self.history) > self.max_history * 2:
844
+ self.history = self.history[-self.max_history * 2:]
845
+
846
+ def update_context(self, key: str, value: any):
847
+ """Cập nhật ngữ cảnh"""
848
+ self.context[key] = value
849
+
850
+ def get_context_summary(self) -> str:
851
+ """Tóm tắt ngữ cảnh hiện tại"""
852
+ if not self.context:
853
+ return ""
854
+
855
+ summary_parts = []
856
+ if "situation" in self.context:
857
+ summary_parts.append(f"Vấn đề: {self.context['situation']}")
858
+ if "has_docs" in self.context:
859
+ summary_parts.append(f"Đã có giấy tờ: {self.context['has_docs']}")
860
+ if "location" in self.context:
861
+ summary_parts.append(f"Khu vực: {self.context['location']}")
862
+
863
+ return " | ".join(summary_parts) if summary_parts else ""
864
+
865
+ def get_relevant_history(self, current_query: str) -> str:
866
+ """Lấy lịch sử liên quan đến câu hỏi hiện tại"""
867
+ if not self.history:
868
+ return ""
869
+
870
+ # Lấy 3-4 tin nhắn gần nhất
871
+ recent = self.history[-6:]
872
+ relevant = []
873
+
874
+ for msg in recent:
875
+ if msg["role"] == "user":
876
+ relevant.append(f"Người dùng: {msg['content']}")
877
+ elif msg["role"] == "assistant":
878
+ # Chỉ lấy tóm tắt
879
+ content = msg['content'][:200]
880
+ relevant.append(f"AI: {content}...")
881
+
882
+ return "\n".join(relevant)
883
+
884
+ def set_persona(self, persona: str):
885
+ """Thay đổi persona"""
886
+ if persona in AI_PERSONAS:
887
+ self.persona = persona
888
+
889
+ def get_persona(self) -> dict:
890
+ """Lấy persona hiện tại"""
891
+ return AI_PERSONAS.get(self.persona, AI_PERSONAS["thuong"])
892
+
893
+ def detect_situation_from_history(self) -> str:
894
+ """Phát hiện tình huống từ lịch sử"""
895
+ for msg in reversed(self.history):
896
+ if msg.get("metadata", {}).get("situation"):
897
+ return msg["metadata"]["situation"]
898
+ return ""
899
+
900
+ def clear(self):
901
+ """Xóa lịch sử và ngữ cảnh"""
902
+ self.history = []
903
+ self.context = {}
904
+
905
+ # Global memory instance
906
+ conversation_memory = ConversationMemory()
907
+
908
+ # Chain of Thought Prompting
909
+ def build_chain_of_thought_prompt(query: str, situation: str, context: str,
910
+ retrieved_docs: list, intents: dict) -> str:
911
+ """Xây dựng prompt với Chain of Thought để suy luận từng bước"""
912
+
913
+ persona = conversation_memory.get_persona()
914
+
915
+ # Bước 1: Xác định vấn đề
916
+ step1 = f"""BƯỚC 1 - XÁC ĐỊNH VẤN ĐỀ:
917
+ Người dùng đang hỏi về: {situation.replace('_', ' ').upper()}
918
+ Câu hỏi cụ thể: "{query}"
919
+ """
920
+
921
+ # Bước 2: Phân tích ý định
922
+ intent_list = [k for k, v in intents.items() if v]
923
+ step2 = f"""BƯỚC 2 - PHÂN TÍCH Ý ĐỊNH:
924
+ Người dùng muốn biết: {', '.join(intent_list) if intent_list else 'thông tin chung'}
925
+ """
926
+
927
+ # Bước 3: Lấy thông tin từ tài liệu
928
+ step3 = """BƯỚC 3 - TRA CỨU THÔNG TIN:
929
+ Dựa trên tài liệu và quy định hiện hành:
930
+ """
931
+ for i, doc in enumerate(retrieved_docs[:3], 1):
932
+ doc_text = doc.page_content.strip()[:300]
933
+ step3 += f"\n[i] {doc_text}..."
934
+
935
+ # Bước 4: Tổng hợp câu trả lời
936
+ step4 = f"""BƯỚC 4 - TỔNG HỢP TRẢ LỜI:
937
+ Hãy trả lời theo phong cách: {persona['tone']}
938
+
939
+ Lưu ý:
940
+ - Dùng các mẫu câu: {', '.join(persona['greeting'][:2])}
941
+ - Kết thúc bằng: {', '.join(persona['closing'][:2])}
942
+ - Tránh lặp lại từ ngữ, thay đổi cách diễn đạt
943
+ - Nếu người dùng hỏi ngắn gọn, trả lời ngắn gọn
944
+ - Nếu người dùng hỏi chi tiết, trả lời đầy đủ
945
+ """
946
+
947
+ if context:
948
+ step4 += f"\n- Ngữ cảnh trước đó: {context}"
949
+
950
+ return step4
951
+
952
+ # Dynamic Temperature based on query complexity
953
+ def calculate_temperature(query: str, situation: str) -> float:
954
+ """Tính độ sáng tạo phù hợp dựa trên độ phức tạp câu hỏi"""
955
+
956
+ # Câu hỏi đơn giản, cần chính xác cao
957
+ simple_patterns = ["cần gì", "ở đâu", "bao lâu", "bao nhiêu"]
958
+ for pattern in simple_patterns:
959
+ if pattern in query.lower():
960
+ return 0.1 # Thấp, tập trung vào chính xác
961
+
962
+ # Câu hỏi phức tạp, cần linh hoạt
963
+ complex_patterns = ["nhưng", "tuy nhiên", "trong trường hợp", "vậy nếu", "làm sao khi"]
964
+ for pattern in complex_patterns:
965
+ if pattern in query.lower():
966
+ return 0.4 # Cao hơn, linh hoạt hơn
967
+
968
+ # Câu hỏi tình huống đặc biệt
969
+ if "qua_han" in situation or "mat" in situation or "doi" in situation:
970
+ return 0.3 # Trung bình
971
+
972
+ return 0.2 # Mặc định
973
+
974
+ print('✅ Context Memory & CoT đã sẵn sàng!')
975
+ print()
976
+ print('='*70)
977
+
978
+ # ======================================================================
979
+ # ============================================================
980
+ # PHẦN 12.4: AI PROCESS PRO - PHIÊN BẢN NÂNG CẤP
981
+ # ============================================================
982
+
983
+
984
+
985
+ # Response Diversity - Tránh lặp lại
986
+ import random
987
+
988
+ def get_diverse_greeting(persona_key: str) -> str:
989
+ """Lấy lời chào ngẫu nhiên để đa dạng hóa"""
990
+ persona = AI_PERSONAS.get(persona_key, AI_PERSONAS["thuong"])
991
+ return random.choice(persona["greeting"])
992
+
993
+ def get_diverse_closing(persona_key: str) -> str:
994
+ """Lấy lời kết ngẫu nhiên"""
995
+ persona = AI_PERSONAS.get(persona_key, AI_PERSONAS["thuong"])
996
+ return random.choice(persona["closing"])
997
+
998
+ # Advanced RAG với Re-ranking
999
+ def advanced_hybrid_search(query: str, k_final: int = 3) -> list:
1000
+ """Tìm kiếm nâng cao với re-ranking - Tối ưu tốc độ"""
1001
+
1002
+ # Query expansion - giới hạn để tăng tốc
1003
+ expanded_queries = expand_query(query)
1004
+
1005
+ # Tìm kiếm với các query mở rộng - giảm từ 3 xuống 2
1006
+ all_results = {}
1007
+ for expanded_q in expanded_queries[:2]: # Giảm từ 3 xuống 2 để nhanh hơn
1008
+ docs = hybrid_search(expanded_q, k_final=5) # Giảm từ 8 xuống 5
1009
+ for doc in docs:
1010
+ doc_id = doc.page_content[:50]
1011
+ if doc_id not in all_results:
1012
+ all_results[doc_id] = {"doc": doc, "score": 1.0}
1013
+ else:
1014
+ all_results[doc_id]["score"] += 0.5
1015
+
1016
+ # Re-ranking dựa trên relevance
1017
+ query_words = set(query.lower().split())
1018
+ for doc_id, item in all_results.items():
1019
+ doc_words = set(item["doc"].page_content.lower().split())
1020
+ overlap = len(query_words & doc_words)
1021
+ item["score"] += overlap * 0.1
1022
+
1023
+ # Sort và trả về
1024
+ sorted_results = sorted(all_results.values(), key=lambda x: x["score"], reverse=True)
1025
+ return [item["doc"] for item in sorted_results[:k_final]]
1026
+
1027
+ # Smart Response Builder
1028
+ def build_smart_response(query: str, situation: str, docs: list, intents: dict,
1029
+ proc_info: str = "") -> str:
1030
+ """Xây dựng câu trả lời thông minh, tự nhiên"""
1031
+
1032
+ persona = conversation_memory.get_persona()
1033
+ persona_key = conversation_memory.persona
1034
+
1035
+ # Xác định độ dài câu trả lời dựa trên câu hỏi
1036
+ query_length = len(query.split())
1037
+ if query_length <= 5 and any([intents["need_docs"], intents["need_location"]]):
1038
+ # Câu hỏi ngắn, trả lời ngắn gọn
1039
+ response_style = "short"
1040
+ else:
1041
+ response_style = "detailed"
1042
+
1043
+ # Lấy thông tin thủ tục
1044
+ proc_data = None
1045
+ for code, proc in DICHVUCONG_DATA.items():
1046
+ if situation.replace("_", "").upper() in code or code in situation.upper():
1047
+ proc_data = proc
1048
+ break
1049
+
1050
+ # Xây dựng câu trả lời
1051
+ parts = []
1052
+
1053
+ # Lời chào ngẫu nhiên
1054
+ greeting = get_diverse_greeting(persona_key)
1055
+ parts.append(greeting)
1056
+
1057
+ # Phần thân câu trả lời
1058
+ if response_style == "short" and proc_data:
1059
+ # Trả lời ngắn gọn
1060
+ if intents["need_docs"]:
1061
+ parts.append(f"\nĐể {proc_data['name'].lower()}, bác/cháu cần chuẩn bị:")
1062
+ docs_list = proc_data['docs'].split(',')
1063
+ for doc in docs_list:
1064
+ parts.append(f"- {doc.strip()}")
1065
+
1066
+ if intents["need_location"]:
1067
+ parts.append(f"\nRa làm ở: {proc_data['coquan']}")
1068
+
1069
+ if intents["need_time"]:
1070
+ parts.append(f"\nThời gian: {proc_data['time']}")
1071
+
1072
+ if intents["need_fee"]:
1073
+ parts.append(f"\nLệ phí: {proc_data['lephi']}")
1074
+
1075
+ if intents["need_process"]:
1076
+ parts.append(f"\nCách làm: {proc_data['steps'][:100]}...")
1077
+
1078
+ else:
1079
+ # Trả lời chi tiết
1080
+ if proc_data:
1081
+ parts.append(f"\nem sẽ hướng dẫn về **{proc_data['name']}** ạ:")
1082
+
1083
+ if intents["need_docs"] or not any(intents.values()):
1084
+ parts.append(f"\n📋 **Giấy tờ cần chuẩn bị:**")
1085
+ docs_list = proc_data['docs'].split(',')
1086
+ for i, doc in enumerate(docs_list, 1):
1087
+ parts.append(f"{i}. {doc.strip()}")
1088
+
1089
+ if intents["need_location"] or not any(intents.values()):
1090
+ parts.append(f"\n🏢 **Nơi làm thủ tục:** {proc_data['coquan']}")
1091
+
1092
+ if intents["need_time"] or not any(intents.values()):
1093
+ parts.append(f"\n⏰ **Thời gian:** {proc_data['time']}")
1094
+
1095
+ if intents["need_fee"] or not any(intents.values()):
1096
+ parts.append(f"\n💰 **Lệ phí:** {proc_data['lephi']}")
1097
+
1098
+ if proc_data.get('note'):
1099
+ parts.append(f"\n⚠️ **Lưu ý:** {proc_data['note']}")
1100
+
1101
+ if intents["need_process"] or not any(intents.values()):
1102
+ parts.append(f"\n📝 **Quy trình:**")
1103
+ steps = proc_data['steps'].split('.')
1104
+ for step in steps:
1105
+ step = step.strip()
1106
+ if step and len(step) > 3:
1107
+ parts.append(f"- {step}")
1108
+ else:
1109
+ # Không có dữ liệu thủ tục cụ thể
1110
+ parts.append(f"\nvề vấn đề bác/cháu hỏi, em xin được tư vấn ạ:")
1111
+
1112
+ # Dùng thông tin từ retrieved docs
1113
+ for i, doc in enumerate(docs[:2], 1):
1114
+ doc_text = doc.page_content.strip()[:200]
1115
+ parts.append(f"\nDựa trên tài liệu [{i}]: {doc_text}...")
1116
+
1117
+ # Lời kết ngẫu nhiên
1118
+ closing = get_diverse_closing(persona_key)
1119
+ parts.append(f"\n\n{closing}")
1120
+
1121
+ return "\n".join(parts)
1122
+
1123
+ # AI Process PRO - Hàm chính
1124
+ def ai_process_pro(query: str, use_memory: bool = True) -> dict:
1125
+ """
1126
+ AI Process PRO với tất cả các cải tiến:
1127
+ - Context Memory
1128
+ - Chain of Thought
1129
+ - Dynamic Temperature
1130
+ - Response Diversity
1131
+ - Advanced RAG
1132
+ """
1133
+ try:
1134
+ start_time = time.time()
1135
+
1136
+ # Clean memory
1137
+ torch.cuda.empty_cache()
1138
+ gc.collect()
1139
+
1140
+ # Bước 1: Phân tích câu hỏi
1141
+ intents = detect_intent(query)
1142
+
1143
+ # Bước 2: Phân loại tình huống
1144
+ try:
1145
+ situation = clf.predict(vec.transform([query]))[0]
1146
+ except:
1147
+ # Fallback: detect từ query patterns
1148
+ for pattern, keywords in QUERY_PATTERNS.items():
1149
+ for kw in keywords:
1150
+ if kw in query.lower():
1151
+ situation = pattern
1152
+ break
1153
+ else:
1154
+ continue
1155
+ break
1156
+ else:
1157
+ situation = 'hoi'
1158
+
1159
+ # Bước 3: Advanced RAG retrieval
1160
+ docs = advanced_hybrid_search(query, k_final=3) # Giảm từ 5 xuống 3
1161
+
1162
+ # Bước 4: Lấy thông tin thủ tục
1163
+ proc_info = ""
1164
+ proc_data = None
1165
+ for code, proc in DICHVUCONG_DATA.items():
1166
+ if situation.replace("_", "").upper() in code or code in situation.upper():
1167
+ proc_data = proc
1168
+ proc_info = f"""
1169
+ THỦ TỤC: {proc['name']}
1170
+ HỒ SƠ: {proc['docs']}
1171
+ CƠ QUAN: {proc['coquan']}
1172
+ THỜI GIAN: {proc['time']}
1173
+ LỆ PHÍ: {proc['lephi']}
1174
+ LƯU Ý: {proc['note']}
1175
+ QUY TRÌNH: {proc['steps']}
1176
+ """
1177
+ break
1178
+
1179
+ # Bước 5: Lấy ngữ cảnh từ memory
1180
+ context_summary = ""
1181
+ relevant_history = ""
1182
+ if use_memory:
1183
+ conversation_memory.update_context("situation", situation)
1184
+ context_summary = conversation_memory.get_context_summary()
1185
+ relevant_history = conversation_memory.get_relevant_history(query)
1186
+
1187
+ # Bước 6: Tính toán động temperature
1188
+ temperature = calculate_temperature(query, situation)
1189
+
1190
+ # Bước 7: Xây dựng prompt với Chain of Thought
1191
+ cot_prompt = build_chain_of_thought_prompt(
1192
+ query=query,
1193
+ situation=situation,
1194
+ context=context_summary,
1195
+ retrieved_docs=docs,
1196
+ intents=intents
1197
+ )
1198
+
1199
+ # Bước 8: Full prompt cho LLM
1200
+ full_prompt = f"""Bạn là cán bộ tư vấn thủ tục hành chính công tại UBND. Hãy hỗ trợ người dân.
1201
+
1202
+ {cot_prompt}
1203
+
1204
+ CÂU HỎI: {query}
1205
+
1206
+ {proc_info}
1207
+
1208
+ LỊCH SỬ ĐỌC CHUẨN KHI CÓ:
1209
+ {relevant_history}
1210
+
1211
+ HÃY TRẢ LỜI NGƯỜI DÂNG:"""
1212
+
1213
+ messages = [
1214
+ {"role": "system", "content": "Bạn là cán bộ UBND tận tâm, luôn hỗ trợ người dân nhiệt tình. Trả lời bằng tiếng Việt, ngôn ngữ tự nhiên, gần gũi như cán bộ thực tế đang tư vấn."},
1215
+ {"role": "user", "content": full_prompt}
1216
+ ]
1217
+
1218
+ text_prompt = tokenizer.apply_chat_template(
1219
+ messages,
1220
+ tokenize=False,
1221
+ add_generation_prompt=True
1222
+ )
1223
+
1224
+ # Bước 9: Generate response với dynamic temperature - dùng model.generate trực tiếp
1225
+ inputs = tokenizer(text_prompt, return_tensors="pt").to(model.device)
1226
+ input_length = inputs.input_ids.shape[1] # Lấy độ dài của prompt đầu vào
1227
+
1228
+ outputs = model.generate(
1229
+ **inputs,
1230
+ max_new_tokens=512, # Tăng lên một chút để tránh bị cắt chữ
1231
+ temperature=temperature,
1232
+ top_p=0.9,
1233
+ repetition_penalty=1.15,
1234
+ do_sample=temperature > 0.2
1235
+ )
1236
+
1237
+ # CHỈ giải mã những token mới (phần sau input_length)
1238
+ response_text = tokenizer.decode(outputs[0][input_length:], skip_special_tokens=True)
1239
+
1240
+ # Clean response
1241
+ if "<|im_start|>assistant\n" in response_text:
1242
+ response_text = response_text.split("<|im_start|>assistant\n")[-1]
1243
+ if "<|im_end|>" in response_text:
1244
+ response_text = response_text.split("<|im_end|")[0]
1245
+
1246
+ llm_response = response_text.strip()
1247
+
1248
+ # Bước 10: Post-process để đảm bảo tự nhiên
1249
+ # Loại bỏ tiếng Trung, ký tự lạ
1250
+ clean_lines = []
1251
+ for line in llm_response.split('\n'):
1252
+ # Filter Chinese characters
1253
+ if any('\u4e00' <= char <= '\u9fff' for char in line):
1254
+ continue
1255
+ # Filter unwanted phrases
1256
+ unwanted_keywords = ["Infrastructure", "Last Updated", "Step 1 -", "BƯỚC 1", "BƯỚC 2", "BƯỚC 3", "BƯỚC 4"]
1257
+ if any(phrase in line for phrase in unwanted_keywords):
1258
+ continue
1259
+ clean_lines.append(line)
1260
+
1261
+ final_response = '\n'.join(clean_lines).strip()
1262
+
1263
+ # Fallback nếu response quá ngắn
1264
+ if len(final_response) < 50:
1265
+ # Dùng smart response builder thay vì prompt mặc định
1266
+ final_response = build_smart_response(query, situation, docs, intents, proc_info)
1267
+ if "persons" in final_response.lower():
1268
+ final_response = final_response.replace("Persons", "hành chính về trật tự xã hội")
1269
+ final_response = final_response.replace("persons", "hành chính về trật tự xã hội")
1270
+
1271
+ # Fallback cuối cùng
1272
+ if len(final_response) < 30:
1273
+ final_response = "Dạ bác/cháu hỏi đúng người rồi ạ! Hiện tại em chưa có đầy đủ thông tin về trường hợp này. Bác/cháu vui lòng mang giấy tờ tùy thân ra trực tiếp UBND Phường/Xã để được hỗ trợ chi tiết hơn ạ!"
1274
+
1275
+ # Lưu vào memory
1276
+ if use_memory:
1277
+ conversation_memory.add_message("user", query, {"situation": situation})
1278
+ conversation_memory.add_message("assistant", final_response, {"situation": situation})
1279
+
1280
+ # Clean memory
1281
+ torch.cuda.empty_cache()
1282
+ gc.collect()
1283
+
1284
+ process_time = time.time() - start_time
1285
+
1286
+ return {
1287
+ "situation": situation,
1288
+ "response": final_response,
1289
+ "docs_count": len(docs),
1290
+ "process_time": round(process_time, 2),
1291
+ "intents": intents,
1292
+ "temperature": temperature
1293
+ }
1294
+
1295
+ except Exception as e:
1296
+ print(f'AI Process PRO Error: {e}')
1297
+ import traceback
1298
+ traceback.print_exc()
1299
+
1300
+ # Fallback với smart response
1301
+ try:
1302
+ fallback_response = build_smart_response(
1303
+ query,
1304
+ conversation_memory.detect_situation_from_history() or 'hoi',
1305
+ [],
1306
+ detect_intent(query),
1307
+ ""
1308
+ )
1309
+ return {
1310
+ "situation": "fallback",
1311
+ "response": fallback_response,
1312
+ "docs_count": 0,
1313
+ "process_time": 0.5
1314
+ }
1315
+ except:
1316
+ return {
1317
+ "situation": "error",
1318
+ "response": "Dạ xin lỗi, hệ thống đang bận. Bác/cháu vui lòng thử lại sau hoặc đến trực tiếp UBND để được hỗ trợ ạ!",
1319
+ "docs_count": 0,
1320
+ "process_time": 0
1321
+ }
1322
+
1323
+ print('✅ AI Process PRO đã sẵn sàng!')
1324
+ print()
1325
+ print('Cải tiến:')
1326
+ print(' - Context Memory: Nhớ lịch sử chat')
1327
+ print(' - Chain of Thought: Suy luận từng bước')
1328
+ print(' - Dynamic Temperature: Tự điều chỉnh độ sáng tạo')
1329
+ print(' - Response Diversity: Tránh lặp lại câu trả lời')
1330
+ print(' - Advanced RAG: Tìm kiếm chính xác hơn')
1331
+ print()
1332
+ print('='*70)
1333
+
1334
+ # ======================================================================
1335
+ # ============================================================
1336
+ # PHẦN 12.5: GRADIO INTERFACE
1337
+ # ============================================================
1338
+
1339
+ import gradio as gr
1340
+ print('='*70)
1341
+ print('🖥️ KHỞI TẠO GRADIO INTERFACE')
1342
+ print('='*70)
1343
+ print()
1344
+
1345
+ # Admin functions
1346
+ admin_procedures = list(DICHVUCONG_DATA.values())
1347
+
1348
+ def get_procedures_list():
1349
+ lines = ["| Mã | Tên thủ tục | Cơ quan |"]
1350
+ lines.append("|---|---|---|")
1351
+ for p in admin_procedures[:20]: # Limit to 20 for display
1352
+ lines.append(f"| {p['code']} | {p['name']} | {p['coquan']} |")
1353
+ return "\n".join(lines)
1354
+
1355
+ def admin_add(code, name, docs, coquan, time_val, lephi, note, steps):
1356
+ global admin_procedures
1357
+ new_proc = {
1358
+ 'code': code, 'name': name, 'docs': docs, 'coquan': coquan,
1359
+ 'time': time_val, 'lephi': lephi, 'note': note, 'steps': steps
1360
+ }
1361
+ admin_procedures.append(new_proc)
1362
+ # For simplicity, we are not updating DICHVUCONG_DATA or Pinecone here directly
1363
+ # In a real application, you would update the backend data source
1364
+ return f"✅ Đã thêm: {code} - {name}", get_procedures_list()
1365
+
1366
+ # Chat function với AI Process PRO
1367
+ def process_chat_pro(audio, text, history, persona="thuong"):
1368
+ try:
1369
+ if history is None:
1370
+ history = []
1371
+
1372
+ # Cập nhật persona
1373
+ conversation_memory.set_persona(persona)
1374
+
1375
+ # Xử lý input
1376
+ query = ''
1377
+ if audio and not os.path.isdir(audio):
1378
+ query = speech_to_text(audio)
1379
+ elif text and text.strip():
1380
+ query = text.strip()
1381
+
1382
+ if len(query) < 3:
1383
+ return history, None, ''
1384
+
1385
+ # Dùng AI Process PRO
1386
+ result = ai_process_pro(query, use_memory=True)
1387
+
1388
+ # Format response
1389
+ response = f"""
1390
+
1391
+ {result['response']}
1392
+
1393
+ ---
1394
+ _⏱️ Xử lý: {result['process_time']}s | 📄 Tài liệu: {result['docs_count']} | 🌡️ Temp: {result['temperature']}_"""
1395
+
1396
+ history.append({"role": "user", "content": query})
1397
+ history.append({"role": "assistant", "content": response})
1398
+
1399
+ # TTS
1400
+ audio_out = None
1401
+ try:
1402
+ loop = asyncio.get_event_loop()
1403
+ except:
1404
+ loop = asyncio.new_event_loop()
1405
+ asyncio.set_event_loop(loop)
1406
+
1407
+ try:
1408
+ audio_out = loop.run_until_complete(text_to_speech(result['response']))
1409
+ except:
1410
+ pass
1411
+
1412
+ return history, audio_out, ''
1413
+
1414
+ except Exception as e:
1415
+ import traceback
1416
+ traceback.print_exc()
1417
+ error_msg = f"Lỗi: {str(e)}"
1418
+ return history, None, error_msg
1419
+
1420
+ def clear_chat_pro():
1421
+ conversation_memory.clear()
1422
+ return [], None, ''
1423
+
1424
+ def change_persona(new_persona):
1425
+ conversation_memory.set_persona(new_persona)
1426
+ persona_names = {
1427
+ "thuong": "Chị Thuong - Thân thiện",
1428
+ "chuyen": "Anh Chuyen - Chuyên nghiệp",
1429
+ "chi_tiet": "Cô Chi Tiet - Chi tiết"
1430
+ }
1431
+ return f"✅ Đã chuyển sang: {persona_names.get(new_persona, new_persona)}"
1432
+
1433
+ # Build PRO interface
1434
+ custom_css = """
1435
+ /* Reset font & nền */
1436
+ .gradio-container {
1437
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
1438
+ background-color: #f8f9fa !important;
1439
+ }
1440
+
1441
+ /* Ẩn footer mặc định */
1442
+ footer {display: none !important;}
1443
+
1444
+ /* Thanh Topbar chuyên nghiệp (Gradient Xanh Navy) */
1445
+ .admin-topbar {
1446
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
1447
+ padding: 20px 30px;
1448
+ display: flex;
1449
+ align-items: center;
1450
+ margin: -20px -20px 20px -20px; /* Tràn viền */
1451
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
1452
+ color: white;
1453
+ }
1454
+
1455
+ .admin-icon {
1456
+ font-size: 45px;
1457
+ margin-right: 20px;
1458
+ background: rgba(255,255,255,0.1);
1459
+ padding: 10px;
1460
+ border-radius: 12px;
1461
+ }
1462
+
1463
+ .admin-brand h1 {
1464
+ color: #ffffff !important;
1465
+ font-size: 24px !important;
1466
+ font-weight: 600 !important;
1467
+ margin: 0 !important;
1468
+ letter-spacing: 1px;
1469
+ text-transform: uppercase;
1470
+ }
1471
+
1472
+ .admin-brand p {
1473
+ color: #e0e0e0 !important;
1474
+ font-size: 14px !important;
1475
+ margin: 5px 0 0 0 !important;
1476
+ font-weight: 300 !important;
1477
+ }
1478
+
1479
+ /* Tinh chỉnh Nút bấm */
1480
+ button.primary {
1481
+ background-color: #0056b3 !important;
1482
+ color: white !important;
1483
+ border-radius: 6px !important;
1484
+ font-weight: 500 !important;
1485
+ border: none !important;
1486
+ transition: all 0.3s ease;
1487
+ }
1488
+
1489
+ button.primary:hover {
1490
+ background-color: #004494 !important;
1491
+ box-shadow: 0 4px 8px rgba(0,86,179,0.3) !important;
1492
+ }
1493
+
1494
+ /* Tùy chỉnh Khung Chat */
1495
+ .main-chatbot {
1496
+ border: 1px solid #dee2e6 !important;
1497
+ border-radius: 8px !important;
1498
+ background-color: #ffffff !important;
1499
+ box-shadow: 0 2px 15px rgba(0,0,0,0.03) !important;
1500
+ }
1501
+
1502
+ /* Tiêu đề Box */
1503
+ .section-title {
1504
+ color: #1e3c72;
1505
+ font-weight: 600;
1506
+ margin-bottom: 10px;
1507
+ border-bottom: 2px solid #e9ecef;
1508
+ padding-bottom: 5px;
1509
+ }
1510
+ """
1511
+
1512
+ # Khởi tạo giao diện với Theme base màu xanh thanh lịch
1513
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate"), css=custom_css) as demo:
1514
+
1515
+ # 1. TOPBAR CHUYÊN NGHIỆP (Không dùng logo/tên thật)
1516
+ gr.HTML("""
1517
+ <div class="admin-topbar">
1518
+ <div class="admin-icon">🏛️</div>
1519
+ <div class="admin-brand">
1520
+ <h1>HỆ THỐNG TRỢ LÝ ẢO TƯ VẤN HÀNH CHÍNH</h1>
1521
+ <p>Giải đáp thông tin thủ tục - Nhanh chóng, Chính xác, Hỗ trợ 24/7</p>
1522
+ </div>
1523
+ </div>
1524
+ """)
1525
+
1526
+ with gr.Tabs():
1527
+ # Chat Tab
1528
+ with gr.Tab("💬 Trợ lý AI"):
1529
+ with gr.Row():
1530
+ with gr.Column(scale=7):
1531
+ chat = gr.Chatbot(height=550, label='Hệ thống tư vấn tự động', elem_classes="main-chatbot")
1532
+ audio_out = gr.Audio(label='🔊 Nghe phản hồi', autoplay=True)
1533
+
1534
+ with gr.Column(scale=3):
1535
+ gr.HTML("<div class='section-title'>Tra cứu thông tin</div>")
1536
+ msg_in = gr.Textbox(label='Nhập câu hỏi của bạn', lines=3,
1537
+ placeholder='VD: Điều kiện để cấp lại căn cước công dân là gì?')
1538
+ audio_in = gr.Audio(sources=['microphone'], type='filepath', label='🎤 Tìm kiếm bằng giọng nói')
1539
+
1540
+ with gr.Row():
1541
+ btn_send = gr.Button('GỬI CÂU HỎI', variant='primary')
1542
+ btn_clear = gr.Button('Làm mới')
1543
+
1544
+ # --- Ô HƯỚNG DẪN NHỎ THÊM VÀO ĐÂY ---
1545
+ gr.HTML("""
1546
+ <div style="
1547
+ margin-top: 15px;
1548
+ padding: 10px;
1549
+ border-radius: 6px;
1550
+ background-color: #f0f7ff;
1551
+ border: 1px dashed #2a5298;
1552
+ font-size: 13px;
1553
+ line-height: 1.5;
1554
+ color: #334155;
1555
+ ">
1556
+ <strong style="color: #1e3c72;">💡 Cách dùng nhanh:</strong>
1557
+ <ul style="margin: 5px 0 0 15px; padding: 0;">
1558
+ <li>Nhập câu hỏi hoặc dùng 🎤 <b>Micro</b>.</li>
1559
+ <li>Nhấn <b>Gửi</b> để nhận phản hồi từ AI.</li>
1560
+ <li>Hệ thống sẽ tự động 🔊 <b>Phát loa</b> câu trả lời.</li>
1561
+ </ul>
1562
+ </div>
1563
+ """)
1564
+
1565
+ gr.Markdown("---")
1566
+ gr.Markdown("**📌 Gợi ý tra cứu nhanh:**")
1567
+ gr.Examples(
1568
+ examples=[
1569
+ [None, 'Làm bằng lái xe máy cần giấy tờ gì?'],
1570
+ [None, 'Khai sinh quá hạn phải làm sao?'],
1571
+ [None, 'Đăng ký xe ô tô mới ở đâu?'],
1572
+ [None, 'Tách hộ khẩu cần những gì?'],
1573
+ [None, 'Làm hộ chiếu bao nhiêu tiền?'],
1574
+ ],
1575
+ inputs=[audio_in, msg_in]
1576
+ )
1577
+
1578
+ # (Các sự kiện click giữ nguyên)
1579
+ btn_send.click(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
1580
+ msg_in.submit(process_chat_pro, [audio_in, msg_in, chat], [chat, audio_out, msg_in])
1581
+ btn_clear.click(clear_chat_pro, [], [chat, audio_out, msg_in])
1582
+ with gr.Tab("⚙️ Admin Panel"):
1583
+ gr.Markdown("### 🔐 Xác thực quyền quản trị")
1584
+
1585
+ with gr.Row():
1586
+ input_pass = gr.Textbox(
1587
+ label="Nhập mật khẩu để mở khóa tính năng Admin",
1588
+ type="password",
1589
+ placeholder="nhap mat khau",
1590
+ scale=4
1591
+ )
1592
+ btn_login = gr.Button("Xác thực 🔑", scale=1)
1593
+
1594
+ # Group chứa nội dung Admin - Mặc định ẩn (visible=False)
1595
+ admin_content = gr.Group(visible=False)
1596
+
1597
+ with admin_content:
1598
+ gr.Markdown("---")
1599
+ gr.Markdown("### ➕ Thêm thủ tục mới (AI sẽ học ngay)")
1600
+ with gr.Row():
1601
+ with gr.Column():
1602
+ admin_code = gr.Textbox(label="Mã thủ tục")
1603
+ admin_name = gr.Textbox(label="Tên thủ tục")
1604
+ admin_docs = gr.Textbox(label="Hồ sơ cần chuẩn bị", lines=2)
1605
+ admin_coquan = gr.Textbox(label="Cơ quan tiếp nhận")
1606
+
1607
+ with gr.Column():
1608
+ admin_time = gr.Textbox(label="Thời gian")
1609
+ admin_lephi = gr.Textbox(label="Lệ phí")
1610
+ admin_note = gr.Textbox(label="Lưu ý", lines=2)
1611
+ admin_steps = gr.Textbox(label="Quy trình", lines=3)
1612
+
1613
+ btn_add = gr.Button("➕ THÊM VÀO HỆ THỐNG", variant='primary', size='lg')
1614
+ add_output = gr.Textbox(label="Kết quả xử lý")
1615
+ procedures_list = gr.Markdown(value=get_procedures_list())
1616
+
1617
+ # --- Logic xử lý ẩn hiện và mật khẩu ---
1618
+ def check_admin(password):
1619
+ if password == ADMIN_PASSWORD:
1620
+ # Nếu đúng: Hiện nội dung admin, ẩn ô nhập mật khẩu và hiện thông báo thành công
1621
+ gr.Info("Đăng nhập thành công! Quyền quản trị đã được mở.")
1622
+ return gr.update(visible=True), gr.update(visible=False)
1623
+ else:
1624
+ # Nếu sai: Giữ ẩn nội dung và hiện cảnh báo
1625
+ raise gr.Error("Mật khẩu không chính xác. Vui lòng thử lại!")
1626
+ return gr.update(visible=False), gr.update(visible=True)
1627
+
1628
+ # Sự kiện khi nhấn nút Xác thực
1629
+ btn_login.click(
1630
+ check_admin,
1631
+ inputs=[input_pass],
1632
+ outputs=[admin_content, input_pass]
1633
+ )
1634
+
1635
+ # Sự kiện khi nhấn nút THÊM (giữ nguyên logic cũ của bạn)
1636
+ btn_add.click(
1637
+ admin_add,
1638
+ [admin_code, admin_name, admin_docs, admin_coquan, admin_time, admin_lephi, admin_note, admin_steps],
1639
+ [add_output, procedures_list]
1640
+ )
1641
+
1642
+ # Memory Tab (mới)
1643
+ with gr.Tab("🧠 Memory Manager"):
1644
+ gr.Markdown("### 📊 Quản lý bộ nhớ hội thoại")
1645
+
1646
+ memory_info = gr.Textbox(label="Ngữ cảnh hiện tại", interactive=False, lines=3)
1647
+ memory_history = gr.Textbox(label="Lịch sử chat", interactive=False, lines=10)
1648
+
1649
+ with gr.Row():
1650
+ btn_refresh = gr.Button("🔄 Làm mới")
1651
+ btn_clear_mem = gr.Button("🗑️ Xóa memory", variant='stop')
1652
+
1653
+ def refresh_memory():
1654
+ ctx = conversation_memory.get_context_summary()
1655
+ hist = "\n".join([f"{m['role']}: {m['content'][:100]}..."
1656
+ for m in conversation_memory.history[-5:]])
1657
+ return ctx or "Không có ngữ cảnh", hist or "Không có lịch sử"
1658
+
1659
+ btn_refresh.click(refresh_memory, [], [memory_info, memory_history])
1660
+ btn_clear_mem.click(clear_chat_pro, [], [chat, audio_out, msg_in])
1661
+
1662
+
1663
+
1664
+
1665
+
1666
+ # ======================================================================
1667
+ # ============================================================
1668
+ # PHẦN 12.7: LAUNCH APP PRO
1669
+ # ============================================================
1670
+
1671
+ print('='*70)
1672
+ print('🚀 KHỞI ĐỘNG ỨNG DỤNG AI PRO')
1673
+ print('='*70)
1674
+ print()
1675
+
1676
+
1677
+
1678
+ print('='*70)
1679
+ print(' Đang tạo public link...')
1680
+ print(' Link sẽ xuất hiện bên dưới sau vài giây...')
1681
+ print('='*70)
1682
+ print()
1683
+
1684
+ # Launch với demo_pro thay vì demo
1685
+ demo.launch(
1686
+ share=True,
1687
+ server_name='0.0.0.0',
1688
+ show_error=True
1689
+ )