HungBB commited on
Commit
7fcfdd0
·
verified ·
1 Parent(s): be4a894

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +148 -0
  2. requirements.txt +8 -0
app.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import pickle
4
+ import gzip
5
+ import json
6
+ import re
7
+ import numpy as np
8
+ import faiss
9
+ from sentence_transformers import SentenceTransformer
10
+ from rank_bm25 import BM25Okapi
11
+ import google.generativeai as genai
12
+ from flask import Flask, request, jsonify
13
+ from flask_cors import CORS
14
+ from huggingface_hub import hf_hub_download # Thư viện để tải file từ Hub
15
+
16
+ print("--- KHỞI ĐỘNG MÁY CHỦ CHATBOT ---")
17
+
18
+ # --- 1. THIẾT LẬP VÀ TẢI TÀI NGUYÊN TỪ HUGGING FACE HUB ---
19
+ try:
20
+ print("Đang tải các tài nguyên cần thiết từ Hugging Face Hub...")
21
+
22
+ # !!! THAY THẾ BẰNG USERNAME VÀ TÊN DATASET CỦA BẠN !!!
23
+ HF_REPO_ID = "TEN_USERNAME_HF/egov-bot-data"
24
+
25
+ # Tự động tải các file từ "Kho Dữ liệu" về môi trường của Space
26
+ RAW_PATH = hf_hub_download(repo_id=HF_REPO_ID, filename="toan_bo_du_lieu_final.json")
27
+ FAISS_PATH = hf_hub_download(repo_id=HF_REPO_ID, filename="index.faiss")
28
+ METAS_PATH = hf_hub_download(repo_id=HF_REPO_ID, filename="metas.pkl.gz")
29
+ BM25_PATH = hf_hub_download(repo_id=HF_REPO_ID, filename="bm25.pkl.gz")
30
+ print("✅ Tải file dữ liệu thành công!")
31
+
32
+ # Lấy API Key từ Secret của Hugging Face
33
+ API_KEY = os.environ.get("GOOGLE_API_KEY")
34
+ genai.configure(api_key=API_KEY)
35
+
36
+ # Tải các mô hình và dữ liệu
37
+ generation_model = genai.GenerativeModel('gemini-2.5-flash')
38
+ embedding_model = SentenceTransformer("AITeamVN/Vietnamese_Embedding")
39
+
40
+ faiss_index = faiss.read_index(FAISS_PATH)
41
+ with gzip.open(METAS_PATH, "rb") as f:
42
+ metadatas = pickle.load(f)
43
+ with gzip.open(BM25_PATH, "rb") as f:
44
+ bm25 = pickle.load(f)
45
+ with open(RAW_PATH, "r", encoding="utf-8") as f:
46
+ raw_data = json.load(f)
47
+ procedure_map = {item['nguon']: item for item in raw_data}
48
+
49
+ print(f"✅ Tải tài nguyên thành công! Sẵn có {faiss_index.ntotal} chunks kiến thức.")
50
+
51
+ except Exception as e:
52
+ print(f"❌ LỖI KHI TẢI TÀI NGUYÊN: {e}")
53
+
54
+ # --- 2. CÁC HÀM XỬ LÝ CỦA BỘ NÃO (LOGIC TỪ COLAB CỦA BẠN) ---
55
+ # (Toàn bộ các hàm classify_followup, minmax_scale, retrieve, get_full_procedure_text của bạn được giữ nguyên ở đây)
56
+ def classify_followup(text: str):
57
+ text = text.lower().strip()
58
+ score = 0
59
+ strong_followup_keywords = [r"\b(nó|cái (này|đó|ấy)|thủ tục (này|đó|ấy))\b", r"\b(vừa (nói|hỏi)|trước đó|ở trên|phía trên)\b", r"\b(tiếp theo|tiếp|còn nữa|ngoài ra)\b", r"\b(thế (thì|à)|vậy (thì|à)|như vậy)\b"]
60
+ detail_questions = [r"\b(mất bao lâu|thời gian|bao nhiêu tiền|chi phí|phí)\b", r"\b(ở đâu|tại đâu|chỗ nào|địa chỉ)\b", r"\b(cần (gì|những gì)|yêu cầu|điều kiện)\b"]
61
+ specific_services = [r"\b(làm|cấp|gia hạn|đổi|đăng ký)\s+(căn cước|cmnd|cccd)\b", r"\b(làm|cấp|gia hạn|đổi)\s+hộ chiếu\b", r"\b(đăng ký)\s+(kết hôn|sinh|tử|hộ khẩu)\b"]
62
+ if any(re.search(p, text) for p in strong_followup_keywords): score -= 3
63
+ if any(re.search(p, text) for p in detail_questions): score -= 2
64
+ if any(re.search(p, text) for p in specific_services): score += 3
65
+ if len(text.split()) <= 4: score -=1
66
+ return 0 if score < 0 else 1
67
+
68
+ def minmax_scale(arr):
69
+ arr = np.array(arr, dtype="float32")
70
+ if len(arr) == 0 or np.max(arr) == np.min(arr): return np.zeros_like(arr)
71
+ return (arr - np.min(arr)) / (np.max(arr) - np.min(arr))
72
+
73
+ def retrieve(query: str, top_k=3):
74
+ qv = embedding_model.encode([query], normalize_embeddings=True).astype("float32")
75
+ D, I = faiss_index.search(qv, top_k * 5)
76
+ vec_scores = (1 - D[0]).tolist()
77
+ vec_idx = I[0].tolist()
78
+ tokenized_query = query.split()
79
+ bm25_scores_all = bm25.get_scores(tokenized_query)
80
+ bm25_top_idx = np.argsort(-bm25_scores_all)[:top_k * 5].tolist()
81
+ union_idx = list(dict.fromkeys(vec_idx + bm25_top_idx))
82
+ vec_map = {i: s for i, s in zip(vec_idx, vec_scores)}
83
+ vec_list = [vec_map.get(i, 0.0) for i in union_idx]
84
+ bm25_list = [bm25_scores_all[i] for i in union_idx]
85
+ vec_scaled = minmax_scale(vec_list)
86
+ bm25_scaled = minmax_scale(bm25_list)
87
+ fused = 0.7 * vec_scaled + 0.3 * bm25_scaled
88
+ order = np.argsort(-fused)
89
+ return [union_idx[i] for i in order[:top_k]]
90
+
91
+ def get_full_procedure_text(parent_id):
92
+ procedure = procedure_map.get(parent_id)
93
+ if not procedure: return "Không tìm thấy thủ tục."
94
+ parts = []
95
+ field_map = {"ten_thu_tuc": "Tên thủ tục", "cach_thuc_thuc_hien": "Cách thức thực hiện", "thanh_phan_ho_so": "Thành phần hồ sơ", "trinh_tu_thuc_hien": "Trình tự thực hiện", "co_quan_thuc_hien": "Cơ quan thực hiện", "yeu_cau_dieu_kien": "Yêu cầu, điều kiện", "thu_tuc_lien_quan": "Thủ tục liên quan", "nguon": "Nguồn"}
96
+ for k, v in procedure.items():
97
+ if v and k in field_map:
98
+ parts.append(f"{field_map[k]}:\n{str(v).strip()}")
99
+ return "\n\n".join(parts)
100
+
101
+ # --- 3. KHỞI TẠO MÁY CHỦ FLASK VÀ API ---
102
+ app = Flask(__name__)
103
+ CORS(app)
104
+
105
+ chat_histories = {}
106
+
107
+ @app.route('/chat', methods=['POST'])
108
+ def chat():
109
+ data = request.json
110
+ user_query = data.get('question')
111
+ session_id = data.get('session_id', 'default')
112
+
113
+ if not user_query:
114
+ return jsonify({"error": "Không có câu hỏi nào được cung cấp"}), 400
115
+
116
+ if session_id not in chat_histories:
117
+ chat_histories[session_id] = []
118
+
119
+ current_history = chat_histories[session_id]
120
+
121
+ context = ""
122
+ if classify_followup(user_query) == 0 and current_history:
123
+ context = current_history[-1].get('context', '')
124
+ print(f"[{session_id}] Dùng lại ngữ cảnh cũ cho câu hỏi followup.")
125
+ else:
126
+ retrieved_indices = retrieve(user_query)
127
+ if retrieved_indices:
128
+ parent_id = metadatas[retrieved_indices[0]]["parent_id"]
129
+ context = get_full_procedure_text(parent_id)
130
+ print(f"[{session_id}] Đã tìm được ngữ cảnh mới.")
131
+
132
+ history_str = "\n".join([f"{item['role']}: {item['content']}" for item in current_history])
133
+ prompt = f"""Bạn là trợ lý eGov-Bot... (Nội dung prompt của bạn ở đây)
134
+ Lịch sử trò chuyện: {history_str}
135
+ DỮ LIỆU: --- {context} ---
136
+ CÂU HỎI: {user_query}
137
+ """
138
+
139
+ response = generation_model.generate_content(prompt)
140
+ final_answer = response.text
141
+
142
+ current_history.append({'role': 'user', 'content': user_query})
143
+ current_history.append({'role': 'model', 'content': final_answer, 'context': context})
144
+
145
+ return jsonify({"answer": final_answer})
146
+
147
+ if __name__ == '__main__':
148
+ app.run(host='0.0.0.0', port=7860)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ Flask
2
+ flask-cors
3
+ numpy==1.26.4
4
+ sentence-transformers==2.2.2
5
+ faiss-cpu==1.7.4
6
+ rank_bm25==0.2.2
7
+ google-generativeai
8
+ huggingface-hub