Spaces:
Sleeping
Sleeping
Download app.py from phucnguyen27/AIHANHCHINH: direct link, hf CLI and curl.
- Browser
- Download file 32.3 kB
-
https://huggingface.co/spaces/phucnguyen27/AIHANHCHINH/resolve/main/app.py
- Command line
-
hf download hf://spaces/phucnguyen27/AIHANHCHINH/app.py
-
curl -L -o app.py https://huggingface.co/spaces/phucnguyen27/AIHANHCHINH/resolve/main/app.py
32.3 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| 🏛️ AI HÀNH CHÍNH CÔNG VIP PRO | |
| Version 2.0 - Deploy on Hugging Face Spaces | |
| """ | |
| import os | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| # Tạo thư mục | |
| os.makedirs('data', exist_ok=True) | |
| # ============================================================ | |
| # IMPORTS | |
| # ============================================================ | |
| import torch | |
| import gc | |
| import json | |
| import numpy as np | |
| import pandas as pd | |
| from datetime import datetime | |
| import gradio as gr | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline, BitsAndBytesConfig | |
| from langchain_huggingface.embeddings import HuggingFaceEmbeddings | |
| from langchain_core.documents import Document | |
| from langchain_pinecone import PineconeVectorStore | |
| from pinecone import Pinecone | |
| from rank_bm25 import BM25Okapi | |
| from sklearn.ensemble import RandomForestClassifier, VotingClassifier | |
| from sklearn.linear_model import LogisticRegression | |
| from sklearn.feature_extraction.text import TfidfVectorizer | |
| # ============================================================ | |
| # DỮ LIỆU DỊCH VỤ CÔNG (40+ THỦ TỤC) | |
| # ============================================================ | |
| DICHVUCONG_DATA = { | |
| # NHÂN SỤ | |
| "KHAI_SINH": {"code":"KHAI_SINH","name":"Đăng ký khai sinh","docs":"Giấy khai sinh, CCCD cha mẹ, Sổ hộ khẩu","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. Chuẩn bị: Giấy khai sinh, CCCD cha mẹ, Sổ hộ khẩu. 2. Đến UBND xã/phường nơi cư trú. 3. Nộp hồ sơ. 4. Nhận kết quả sau 1-3 ngày.","category":"NHAN_SU"}, | |
| "KHAI_SINH_QUA_HAN": {"code":"KHAI_SINH_QUA_HAN","name":"Khai sinh quá hạn","docs":"Giấy khai sinh, CCCD, Sổ hộ khẩu, Đơn giải trình","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ố. 3. Đến UBND nộp hồ sơ.","category":"NHAN_SU"}, | |
| "KHAI_TU": {"code":"KHAI_TU","name":"Đăng ký khai tử","docs":"Giấy báo tử, CCCD người chết, Sổ hộ khẩu","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ử. 2. Chuẩn bị hồ sơ. 3. Đến UBND nơi người chết cư trú.","category":"NHAN_SU"}, | |
| "KET_HON": {"code":"KET_HON","name":"Đăng ký kết hôn","docs":"CCCD cả 2 bên, Sổ hộ khẩu, Giấy xác nhận tình trạng hôn nhân","coquan":"UBND xã/phường","time":"1-3 ngày","lephi":"Miễn phí","note":"Trong 30 ngày có giấy xác nhận","steps":"1. Xin giấy xác nhận tình trạng hôn nhân. 2. Cả 2 đến UBND. 3. Đăng ký kết hôn.","category":"NHAN_SU"}, | |
| "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, CCCD, Sổ hộ khẩu","coquan":"Tòa án","time":"2-4 tháng","lephi":"300.000đ","note":"Cả 2 bên đồng ý","steps":"1. Soạn đơn xin ly hôn. 2. Nộp tại Tòa án. 3. Tòa án thụ lý và ra quyết định.","category":"NHAN_SU"}, | |
| "KET_HON_NGOAI": {"code":"KET_HON_NGOAI","name":"Kết hôn người nước ngoài","docs":"CCCD, Hộ chiếu, Giấy xác nhận, Giấy hợp pháp hóa lãnh sự","coquan":"UBND quận/huyện","time":"5-7 ngày","lephi":"Miễn phí","note":"Cần hợp pháp hóa lãnh sự","steps":"1. Hợp pháp hóa giấy tờ. 2. Xin xác nhận. 3. Đăng ký tại UBND quận.","category":"NHAN_SU"}, | |
| # CCCD - HỘ KHẨU | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| # BẰNG LÁI | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| "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"}, | |
| # HỘ CHIẾU | |
| "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"}, | |
| "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"}, | |
| # LÝ LỊCH | |
| "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"}, | |
| # NHÀ ĐẤT | |
| "SO_DO": {"code":"SO_DO","name":"Cấp sổ đỏ (Giấy chứng nhận quyền sử dụng đất)","docs":"Đơn xin cấp, Giấy tờ về quyền sử dụng đất","coquan":"Văn phòng đăng ký đất đai","time":"10-30 ngày","lephi":"80.000đ-500.000đ","note":"Cần giấy tờ hợp pháp","steps":"1. Làm đơn xin cấp sổ đỏ. 2. Chuẩn bị giấy tờ chứng minh quyền sử dụng. 3. Nộp tại VP Đăng ký đất đai. 4. Đóng phí và nhận sổ.","category":"NHADAT"}, | |
| "SANG_TEN": {"code":"SANG_TEN","name":"Sang tên sổ đỏ","docs":"Sổ đỏ, CCCD, Hợp đồng chuyển nhượng","coquan":"Văn phòng đăng ký đất đai","time":"10-20 ngày","lephi":"0.5% giá trị","note":"Cần công chứng hợp đồng","steps":"1. Công chứng hợp đồng chuyển nhượng. 2. Cả 2 bên đến VP Đăng ký đất đai. 3. Làm thủ tục sang tên. 4. Đóng lệ phí trước bạ.","category":"NHADAT"}, | |
| # GIAO THÔNG XE | |
| "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"}, | |
| "CHUYEN_NHUONG_XE": {"code":"CHUYEN_NHUONG_XE","name":"Chuyển nhượng 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"}, | |
| # XÂY DỰNG | |
| "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"}, | |
| # KINH DOANH | |
| "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"}, | |
| # Y TẾ | |
| "BHYT_CAP": {"code":"BHYT_CAP","name":"Cấp thẻ BHYT","docs":"CCCD, Sổ hộ khẩu","coquan":"Bảo hiểm xã hội","time":"3-5 ngày","lephi":"Miễn phí","note":"Trẻ em dưới 6 tuổi miễn phí","steps":"1. Chuẩn bị CCCD, sổ hộ khẩu. 2. Đến BHXH nơi thường trú. 3. Làm thủ tục cấp thẻ BHYT.","category":"YTE"}, | |
| "BHYT_DOI": {"code":"BHYT_DOI","name":"Đổi thẻ BHYT","docs":"Thẻ BHYT cũ, CCCD","coquan":"Bảo hiểm xã hội","time":"1-3 ngày","lephi":"Miễn phí","note":"Đổi khi thẻ hư hỏng hoặc sai thông tin","steps":"1. Mang thẻ BHYT cũ và CCCD. 2. Đến BHXH nơi đã cấp thẻ. 3. Làm thủ tục đổi thẻ.","category":"YTE"}, | |
| # GIÁO DỤC | |
| "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"}, | |
| # KHÁC | |
| "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"}, | |
| "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"}, | |
| } | |
| # ============================================================ | |
| # SITUATIONS CLASSIFICATION | |
| # ============================================================ | |
| SITUATIONS = { | |
| 'khai_sinh_moi': ['khai sinh moi', 'sinh con', 'be moi sinh', 'dang ky khai sinh', 'lam khai sinh', 'khai be'], | |
| 'khai_sinh_qua_han': ['khai sinh qua han', 'qua han khai sinh', 'muon khai sinh', 'qua han 60 ngay'], | |
| 'ket_hon': ['ket hon', 'dang ky ket hon', 'dang ky cuoi', 'lay vo chong', 'hon nhan', 'lam cuoi'], | |
| 'ly_hon': ['ly hon', 'ly di', 'don ly hon', 'ly dinh', 'dong y ly hon'], | |
| 'khai_tu': ['khai tu', 'bao tu', 'nguoi chet', 'dang ky khai tu', 'giay bao tu'], | |
| 'ket_hon_ngoai': ['ket hon nguoi nuoc ngoai', 'lay nguoi nuoc ngoai', 'vo chong nuoc ngoai'], | |
| 'cccd_cap': ['cap cccd', 'lam cccd', 'cccd lan dau', 'can cuoc cong dan', 'cmnd', 'lam can cuoc'], | |
| 'cccd_do': ['doi cccd', 'doi can cuoc', 'lam lai cccd', 'cccd het han', 'doi cmnd'], | |
| 'cccd_cap_lai': ['cap lai cccd', 'cccd bi mat', 'lam lai can cuoc', 'mat cccd'], | |
| 'ho_khau_tach': ['tach ho khau', 'tach khau', 'tach ra khoi ho', 'tach ho', 'tach so'], | |
| 'ho_khau_nhap': ['nhap ho khau', 'nhap khau', 'vao ho khau', 'nhap vao ho', 'nhap ho'], | |
| 'tam_tru': ['tam tru', 'dang ky tam tru', 'xin tam tru', 'lam tam tru', 'o thue'], | |
| 'tru_so': ['tru so', 'tru qua han', 'tru qua 90 ngay', 'dang ky tru quan'], | |
| 'tam_vang': ['tam vang', 'dang ky tam vang', 'bao tam vang', 'di tam vang'], | |
| 'bang_lai_a1': ['bang lai a1', 'bang lai xe may', 'lai xe may', 'cap gplx a1', 'lam bang a1', 'bang xe may'], | |
| 'bang_lai_a2': ['bang lai a2', 'bang lai xe cong nong', 'lai xe cong nong'], | |
| 'bang_lai_b1': ['bang lai b1', 'bang lai oto duoi 9 cho', 'lai b1'], | |
| 'bang_lai_b2': ['bang lai b2', 'bang lai oto', 'lai xe o to', 'bang lai dong so'], | |
| 'bang_lai_do': ['doi bang lai', 'lam lai bang lai', 'bang lai mat', 'gia han bang lai'], | |
| 'bang_lai_cap_lai': ['cap lai bang lai', 'bang lai bi mat', 'mat bang lai'], | |
| 'thi_sat_hach': ['thi bang lai', 'thi sat hach', 'thi lai xe', 'dang ky thi bang lai'], | |
| 'ho_chieu_cap': ['lam ho chieu', 'cap ho chieu', 'passport', 'lam passport', 'xin ho chieu'], | |
| 'ho_chieu_gia_han': ['gia han ho chieu', 'doi ho chieu', 'ho chieu het han'], | |
| 'ly_lich_tu_phap': ['ly lich tu phap', 'an tien an', 'so tu phap', 'xet nghiem tien an', 'xin lltp'], | |
| 'so_do': ['so do', 'giay chu quyen', 'sodo', 'cap so do nha', 'lam so do', 'so do', 'giay so'], | |
| 'sang_ten': ['sang ten', 'chuyen nhuong', 'chuyen ten', 'mua ban nha', 'sang ten nha'], | |
| 'kinh_doanh': ['kinh doanh', 'mo cua hang', 'ban hang', 'giay phep kinh doanh', 'mo kinh doanh'], | |
| 'dang_ky_xe': ['dang ky xe', 'dang ki xe o to', 'nghia vu xe', 'dang ky xe moi', 'dang ki xe'], | |
| 'chuyen_nhuong_xe': ['chuyen nhuong xe', 'ban xe', 'mua xe', 'chuyen ten xe'], | |
| 'xay_dung': ['xay dung', 'xay nha', 'phep xay dung', 'cong nhat', 'xin phep xay'], | |
| 'bhyt_cap': ['cap the bhyt', 'lam bao hiem y te', 'dang ky bhyt', 'lam the bao hiem'], | |
| 'bhyt_do': ['doi the bhyt', 'gia han bao hiem y te', 'bhyt het han', 'doi the bao hiem'], | |
| 'hoc_bong': ['dang ky hoc bong', 'xin hoc bong', 'xet hoc bong', 'nop don hoc bong'], | |
| 'xin_viec': ['dang ky tim viec lam', 'tim viec', 'xin viec', 'tim kiem viec lam'], | |
| 'tro_cap': ['dang ky tro cap xa hoi', 'tro cap', 'ho tro', 'xin tro cap'], | |
| 'hoi': ['hoi', 'hoi ve', 'tu van', 'giup doi', 'can hoi', 'tim hieu', 'xin tu van'], | |
| } | |
| # ============================================================ | |
| # LOAD DATASET & BUILD DOCUMENTS | |
| # ============================================================ | |
| try: | |
| from datasets import load_dataset | |
| ds1 = load_dataset('hirine/dataset-thu-tuc-hanh-chinh-5733-samples', split='train') | |
| df1 = pd.DataFrame(ds1) | |
| except Exception as e: | |
| df1 = pd.DataFrame({ | |
| 'id': range(100), | |
| 'title': ['Khai sinh', 'Kết hôn', 'Làm CCCD', 'Tách hộ khẩu', 'Đổi bằng lái', | |
| 'Khai tử', 'Hộ chiếu', 'Lý lịch tư pháp', 'Sổ đỏ', 'Sang tên'] * 10, | |
| 'text': ['Thủ tục hành chính cần giấy tờ...' * 5] * 100 | |
| }) | |
| try: | |
| ds2_stream = load_dataset('phamson02/large-vi-legal-queries', split='train', streaming=True) | |
| data2 = [] | |
| for i, item in enumerate(ds2_stream): | |
| data2.append(item) | |
| if i >= 999: | |
| break | |
| df2 = pd.DataFrame(data2) | |
| except Exception as e: | |
| df2 = pd.DataFrame({ | |
| 'passage_id': range(500), | |
| 'title': ['Hỏi về CCCD', 'Hỏi về hộ khẩu', 'Hỏi về bằng lái', 'Hỏi về khai sinh'] * 125, | |
| 'context': ['Câu hỏi về thủ tục hành chính...' * 10] * 500 | |
| }) | |
| # Build documents | |
| all_docs = [] | |
| for i, row in df1.iterrows(): | |
| truncated_text = row['text'][:10000] | |
| text = f"THỦ TỤC: {row['title']}\nNỘI DUNG: {truncated_text}" | |
| all_docs.append(Document(page_content=text, metadata={"source": "ds1", "id": i})) | |
| ds2_limit = min(2000, len(df2)) | |
| for i, row in df2.head(ds2_limit).iterrows(): | |
| truncated_context = row.get('context', 'Nội dung')[:10000] | |
| text = f"CÂU HỎI: {row.get('title', 'Hỏi')}\nTRẢ LỜI: {truncated_context}" | |
| all_docs.append(Document(page_content=text, metadata={"source": "ds2", "id": i})) | |
| for code, proc in DICHVUCONG_DATA.items(): | |
| text = f"""MÃ: {proc['code']} | |
| THỦ TỤC: {proc['name']} | |
| HỒ SƠ: {proc['docs']} | |
| CƠ QUAN: {proc['coquan']} | |
| THỜI GIAN: {proc['time']} | |
| LỆ PHÍ: {proc['lephi']} | |
| LƯU Ý: {proc['note']} | |
| QUY TRÌNH: {proc['steps']}""" | |
| all_docs.append(Document(page_content=text, metadata={"source": "dvc", "code": code})) | |
| # ============================================================ | |
| # EMBEDDINGS & CLASSIFIER | |
| # ============================================================ | |
| device = 'cuda' if torch.cuda.is_available() else 'cpu' | |
| embeddings = HuggingFaceEmbeddings( | |
| model_name='keepitreal/vietnamese-sbert', | |
| model_kwargs={'device': device}, | |
| encode_kwargs={'normalize_embeddings': True} | |
| ) | |
| # Train Classifier | |
| train_text = [] | |
| train_label = [] | |
| for lbl, exs in SITUATIONS.items(): | |
| for ex in exs: | |
| train_text.append(ex) | |
| train_label.append(lbl) | |
| vec = TfidfVectorizer(ngram_range=(1, 2), min_df=1) | |
| X = vec.fit_transform(train_text) | |
| clf = VotingClassifier( | |
| estimators=[ | |
| ('rf', RandomForestClassifier(n_estimators=50, random_state=42)), | |
| ('lr', LogisticRegression(max_iter=1000, random_state=42)) | |
| ], | |
| voting='soft' | |
| ) | |
| clf.fit(X, train_label) | |
| # BM25 | |
| corpus = [doc.page_content for doc in all_docs] | |
| tokenized_corpus = [doc.split() for doc in corpus] | |
| bm25 = BM25Okapi(tokenized_corpus) | |
| # ============================================================ | |
| # PINECONE SETUP | |
| # ============================================================ | |
| PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY', '') | |
| vector_db = None | |
| if PINECONE_API_KEY: | |
| try: | |
| pc = Pinecone(api_key=PINECONE_API_KEY) | |
| index_name = 'ai-hanh-chinh-rag' | |
| dimension = 768 | |
| if not pc.has_index(index_name): | |
| pc.create_index( | |
| name=index_name, | |
| dimension=dimension, | |
| metric='cosine', | |
| spec={'serverless': {'cloud': 'aws', 'region': 'us-east-1'}} | |
| ) | |
| vector_db = PineconeVectorStore( | |
| index_name=index_name, | |
| embedding=embeddings, | |
| pinecone_api_key=PINECONE_API_KEY | |
| ) | |
| index = pc.Index(index_name) | |
| stats = index.describe_index_stats() | |
| vector_count = stats.get('total_vector_count', 0) | |
| if vector_count == 0: | |
| ids = [f"doc_{doc.metadata.get('source', 'unk')}_{i}" for i, doc in enumerate(all_docs)] | |
| vector_db.add_documents(documents=all_docs, ids=ids) | |
| except Exception as e: | |
| vector_db = None | |
| # ============================================================ | |
| # SEARCH FUNCTIONS | |
| # ============================================================ | |
| def hybrid_search(query, k_final=5): | |
| if vector_db: | |
| dense_results = vector_db.similarity_search_with_score(query, k=10) | |
| else: | |
| dense_results = [] | |
| tokenized_query = query.split() | |
| bm25_scores = bm25.get_scores(tokenized_query) | |
| top_indices = np.argsort(bm25_scores)[::-1][:10] | |
| combined = {} | |
| for doc, score in dense_results: | |
| doc_id = doc.page_content[:100] | |
| combined[doc_id] = {'doc': doc, 'score': 1 - score} | |
| for idx in top_indices: | |
| if idx < len(all_docs): | |
| doc_id = all_docs[idx].page_content[:100] | |
| if doc_id not in combined: | |
| combined[doc_id] = {'doc': all_docs[idx], 'score': 0.5} | |
| sorted_docs = sorted(combined.values(), key=lambda x: x['score'], reverse=True)[:k_final] | |
| return [item['doc'] for item in sorted_docs] | |
| # ============================================================ | |
| # LLM SETUP | |
| # ============================================================ | |
| MODEL_ID = 'Qwen/Qwen2.5-3B-Instruct' | |
| bnb_config = BitsAndBytesConfig( | |
| load_in_4bit=True, | |
| bnb_4bit_compute_dtype=torch.float16, | |
| bnb_4bit_use_double_quant=True | |
| ) | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, | |
| device_map='auto', | |
| quantization_config=bnb_config, | |
| trust_remote_code=True | |
| ) | |
| llm = pipeline( | |
| 'text-generation', | |
| model=model, | |
| tokenizer=tokenizer, | |
| max_new_tokens=512, | |
| temperature=0.1, | |
| repetition_penalty=1.1 | |
| ) | |
| # ============================================================ | |
| # VOICE AI SETUP (DISABLED FOR PYTHON 3.13 COMPATIBILITY) | |
| # ============================================================ | |
| # whisper_device = 'cuda' if torch.cuda.is_available() else 'cpu' | |
| # whisper_model = whisper.load_model('base', device=whisper_device) | |
| def speech_to_text(audio_path): | |
| return "" | |
| async def text_to_speech(text, voice='vi-VN-HoaiMyNeural'): | |
| return None | |
| # ============================================================ | |
| # AI PROCESS FUNCTION | |
| # ============================================================ | |
| def ai_process(query): | |
| try: | |
| torch.cuda.empty_cache() | |
| gc.collect() | |
| situation = clf.predict(vec.transform([query]))[0] | |
| docs = hybrid_search(query, k_final=5) | |
| proc_info = "" | |
| for proc_code, proc in DICHVUCONG_DATA.items(): | |
| if proc['code'] == situation.replace('_', '').upper() or proc['code'] in situation.upper(): | |
| proc_info = f""" | |
| **📋 THỦ TỤC:** {proc['name']} | |
| **📝 HỒ SƠ:** {proc['docs']} | |
| **🏢 NƠI LÀM:** {proc['coquan']} | |
| **⏰ THỜI GIAN:** {proc['time']} | |
| **💰 LỆ PHÍ:** {proc['lephi']} | |
| **⚠️ LƯU Ý:** {proc['note']} | |
| **📝 QUY TRÌNH:** {proc['steps']} | |
| """ | |
| break | |
| context_parts = [] | |
| for i, doc in enumerate(docs, 1): | |
| text = doc.page_content.strip() | |
| if len(text) > 500: | |
| text = text[:500] + "..." | |
| context_parts.append(f"[Tài liệu {i}] {text}") | |
| context = '\n\n'.join(context_parts) | |
| prompt = f"""Bạn là cán bộ hướng dẫn thủ tục hành chính công tại UBND. Hãy hỗ trợ người dân một cách chi tiết và thân thiện. | |
| TÌNH HUỐNG: {situation.upper().replace('_', ' ')} | |
| {proc_info} | |
| TÀI LIỆU THAM KHẢO: | |
| {context} | |
| CÂU HỎI: {query} | |
| HƯỚNG DẪN: | |
| 1. Chào hỏi thân thiện | |
| 2. Xác định thủ tục đúng | |
| 3. Liệt kê hồ sơ cần chuẩn bị (rõ ràng, từng món) | |
| 4. Nơi làm thủ tục | |
| 5. Thời gian và lệ phí | |
| 6. Lưu ý quan trọng | |
| QUY ĐỊNH: | |
| - Chỉ trả lời dựa trên tài liệu đã có | |
| - Không bịa đặt thông tin | |
| - Dùng ngôn ngữ dễ hiểu, lịch sự | |
| - Nếu không có thông tin, hướng dẫn đến UBND | |
| Bây giờ hãy trả lời:""" | |
| messages = [ | |
| {"role": "system", "content": "Bạn là cán bộ hành chính công tận tâm. Hỗ trợ người dân chi tiết, chính xác dựa trên tài liệu có."}, | |
| {"role": "user", "content": prompt} | |
| ] | |
| text_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| outputs = llm( | |
| text_prompt, | |
| max_new_tokens=512, | |
| temperature=0.1, | |
| repetition_penalty=1.1, | |
| do_sample=False | |
| ) | |
| response_text = outputs[0]["generated_text"] | |
| if "<|im_start|>assistant\n" in response_text: | |
| response_text = response_text.split("<|im_start|>assistant\n")[-1] | |
| if "<|im_end|>" in response_text: | |
| response_text = response_text.split("<|im_end|")[0] | |
| response = response_text.strip() | |
| clean_lines = [] | |
| for line in response.split('\n'): | |
| if any('\u4e00' <= char <= '\u9fff' for char in line): | |
| continue | |
| if "Infrastructure" in line or "Last Updated" in line: | |
| continue | |
| clean_lines.append(line) | |
| final_response = '\n'.join(clean_lines).strip() | |
| if len(final_response) < 20: | |
| final_response = "Dạ xin lỗi, hệ thống chưa cập nhật đủ thông tin về trường hợp này. Bác vui lòng mang giấy tờ cá nhân ra trực tiếp UBND Phường/Xã để được hỗ trợ ạ!" | |
| torch.cuda.empty_cache() | |
| gc.collect() | |
| return { | |
| "situation": situation, | |
| "response": final_response, | |
| "docs_count": len(docs) | |
| } | |
| except Exception as e: | |
| return { | |
| "situation": "error", | |
| "response": f"Dạ xin lỗi, hệ thống đang gặp sự cố. Vui lòng thử lại hoặc đến trực tiếp UBND để được hỗ trợ ạ!", | |
| "docs_count": 0 | |
| } | |
| # ============================================================ | |
| # GRADIO INTERFACE | |
| # ============================================================ | |
| def process_chat(text, history): | |
| try: | |
| if history is None: | |
| history = [] | |
| query = text.strip() if text else "" | |
| if len(query) < 3: | |
| return history, '' | |
| result = ai_process(query) | |
| response = f"""**🏛️ AI HÀNH CHÍNH CÔNG VIP PRO** | |
| **📋 Tình huống:** `{result['situation']}` | |
| {result['response']} | |
| --- | |
| _📄 Tài liệu tham khảo: {result['docs_count']}_""" | |
| history.append((query, response)) | |
| return history, '' | |
| except Exception as e: | |
| return history, str(e) | |
| def clear_chat(): | |
| return [], '' | |
| pro_css = """ | |
| .gradio-container { | |
| font-family: 'Segoe UI', sans-serif; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| } | |
| .chatbot { | |
| border-radius: 20px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.1); | |
| } | |
| .pro-badge { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 5px 15px; | |
| border-radius: 20px; | |
| font-weight: bold; | |
| } | |
| """ | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue='purple', secondary_hue='indigo'), | |
| css=pro_css) as demo: | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; margin-bottom: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);"> | |
| <h1 style="color: white; margin: 0; font-size: 2.8em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">🏛️ AI HÀNH CHÍNH CÔNG VIP PRO</h1> | |
| <p style="color: white; margin: 15px 0 0 0; font-size: 1.2em;">Version 2.0 - Tư vấn thủ tục hành chính bằng AI</p> | |
| <div style="margin-top: 15px;"> | |
| <span class="pro-badge">Hybrid RAG</span> | |
| <span class="pro-badge">50+ Situations</span> | |
| <span class="pro-badge">Qwen 2.5 3B</span> | |
| </div> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| chat = gr.Chatbot(height=500, label='💬 Lịch sử chat', | |
| show_copy_button=True) | |
| with gr.Column(scale=1): | |
| msg_in = gr.Textbox(label='✏️ Nhập câu hỏi', lines=4, | |
| placeholder='Ví dụ: Làm bằng lái xe máy cần giấy tờ gì?') | |
| with gr.Row(): | |
| btn_send = gr.Button('🚀 GỬI', variant='primary', size='lg') | |
| btn_clear = gr.Button('🔄 LÀM MỚI', size='lg') | |
| gr.Examples( | |
| examples=[ | |
| 'Làm bằng lái xe máy cần giấy tờ gì?', | |
| 'Khai sinh quá hạn phải làm sao?', | |
| 'Đăng ký xe ô tô mới ở đâu?', | |
| 'Tách hộ khẩu cần những gì?', | |
| 'Làm hộ chiếu bao nhiêu tiền?' | |
| ], | |
| inputs=[msg_in] | |
| ) | |
| btn_send.click(process_chat, [msg_in, chat], | |
| [chat, msg_in]) | |
| msg_in.submit(process_chat, [msg_in, chat], | |
| [chat, msg_in]) | |
| btn_clear.click(clear_chat, [], [chat, msg_in]) | |
| # ============================================================ | |
| # LAUNCH APP | |
| # ============================================================ | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |