deddoggo commited on
Commit
8829c2d
·
1 Parent(s): 8860136
Files changed (2) hide show
  1. app.py +35 -16
  2. rag_pipeline.py +76 -24
app.py CHANGED
@@ -2,48 +2,66 @@
2
  import gradio as gr
3
  import time
4
 
 
5
  from rag_pipeline import initialize_components, generate_response
6
 
7
- # --- KHỞI TẠO CÁC THÀNH PHẦN (CHỈ CHẠY 1 LẦN) ---
8
  start_time = time.time()
9
  print("Bắt đầu khởi tạo ứng dụng Chatbot Luật Giao thông...")
10
- DATA_PATH = "data/luat_chi_tiet_output_openai_sdk_final_cleaned.json"
 
 
11
  COMPONENTS = initialize_components(DATA_PATH)
12
  end_time = time.time()
13
  print(f"✅ Ứng dụng đã sẵn sàng! Thời gian khởi tạo: {end_time - start_time:.2f} giây.")
14
  # ----------------------------------------------------
15
 
16
 
17
- def chat_interface(query, history):
18
- """
19
- Hàm xử lý logic cho giao diện chat của Gradio.
20
- """
21
- print(f"Nhận được câu hỏi từ người dùng: '{query}'")
22
- # Gọi hàm generate_response với query và các thành phần đã được khởi tạo
23
- response = generate_response(query, COMPONENTS)
24
- return response
25
-
26
  # --- GIAO DIỆN GRADIO ---
27
  with gr.Blocks(theme=gr.themes.Soft(), title="Chatbot Luật Giao thông Việt Nam") as demo:
28
  gr.Markdown(
29
  """
30
- # ⚖️ Chatbot Luật Giao thông Việt Nam
31
- Hỏi đáp về các quy định, mức phạt trong luật giao thông đường bộ dựa trên sở dữ liệu được cung cấp.
32
  *Lưu ý: Đây là một sản phẩm demo. Thông tin chỉ mang tính chất tham khảo.*
33
  """
34
  )
35
 
36
- chatbot = gr.Chatbot(label="Chatbot", height=500)
 
 
37
  msg = gr.Textbox(label="Nhập câu hỏi của bạn", placeholder="Ví dụ: Vượt đèn đỏ bị phạt bao nhiêu tiền?")
 
38
  clear = gr.ClearButton([msg, chatbot])
39
 
40
  def respond(message, chat_history):
41
- bot_message = chat_interface(message, COMPONENTS, chat_history)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  chat_history.append((message, bot_message))
 
 
43
  return "", chat_history
44
 
 
 
 
 
45
  msg.submit(respond, [msg, chatbot], [msg, chatbot])
46
 
 
47
  gr.Examples(
48
  examples=[
49
  "Phương tiện giao thông đường bộ gồm những loại nào?",
@@ -54,5 +72,6 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Chatbot Luật Giao thông Việt
54
  inputs=msg
55
  )
56
 
 
57
  if __name__ == "__main__":
58
- demo.launch()
 
2
  import gradio as gr
3
  import time
4
 
5
+ # Đảm bảo import đúng các hàm cần thiết từ rag_pipeline
6
  from rag_pipeline import initialize_components, generate_response
7
 
8
+ # --- KHỞI TẠO CÁC THÀNH PHẦN (CHỈ CHẠY 1 LẦN KHI ỨNG DỤNG START) ---
9
  start_time = time.time()
10
  print("Bắt đầu khởi tạo ứng dụng Chatbot Luật Giao thông...")
11
+ # Đường dẫn đến file dữ liệu của bạn
12
+ DATA_PATH = "data/luat_chi_tiet_output_openai_sdk_final_cleaned.json"
13
+ # Hàm này sẽ tải models, dữ liệu, và tạo index.
14
  COMPONENTS = initialize_components(DATA_PATH)
15
  end_time = time.time()
16
  print(f"✅ Ứng dụng đã sẵn sàng! Thời gian khởi tạo: {end_time - start_time:.2f} giây.")
17
  # ----------------------------------------------------
18
 
19
 
 
 
 
 
 
 
 
 
 
20
  # --- GIAO DIỆN GRADIO ---
21
  with gr.Blocks(theme=gr.themes.Soft(), title="Chatbot Luật Giao thông Việt Nam") as demo:
22
  gr.Markdown(
23
  """
24
+ # ⚖️ Chatbot Luật Giao thông Việt Nam (Multi-turn)
25
+ Hỏi đáp về các quy định, mức phạt trong luật giao thông đường bộ. Chatbot thể hiểu các câu hỏi nối tiếp.
26
  *Lưu ý: Đây là một sản phẩm demo. Thông tin chỉ mang tính chất tham khảo.*
27
  """
28
  )
29
 
30
+ # Sử dụng gr.Chatbot để quản lý và hiển thị lịch sử trò chuyện
31
+ chatbot = gr.Chatbot(label="Cuộc trò chuyện", height=500)
32
+ # Textbox để người dùng nhập câu hỏi
33
  msg = gr.Textbox(label="Nhập câu hỏi của bạn", placeholder="Ví dụ: Vượt đèn đỏ bị phạt bao nhiêu tiền?")
34
+ # Nút để xóa cuộc trò chuyện
35
  clear = gr.ClearButton([msg, chatbot])
36
 
37
  def respond(message, chat_history):
38
+ """
39
+ Hàm xử lý logic cho mỗi lượt chat.
40
+ 'message' là tin nhắn mới nhất của người dùng.
41
+ 'chat_history' là danh sách các cặp [tin nhắn cũ, trả lời cũ] do Gradio quản lý.
42
+ """
43
+ print(f"Nhận được câu hỏi: '{message}'")
44
+ print(f"Lịch sử trò chuyện hiện tại: {chat_history}")
45
+
46
+ # Gọi thẳng hàm generate_response với các tham số cần thiết:
47
+ # 1. message: câu hỏi mới
48
+ # 2. chat_history: lịch sử trò chuyện
49
+ # 3. COMPONENTS: dictionary chứa các model và dữ liệu đã được khởi tạo
50
+ bot_message = generate_response(message, chat_history, COMPONENTS)
51
+
52
+ # Cập nhật lịch sử để hiển thị trên giao diện
53
  chat_history.append((message, bot_message))
54
+
55
+ # Trả về chuỗi rỗng để xóa nội dung trong textbox và lịch sử đã được cập nhật
56
  return "", chat_history
57
 
58
+ # Thiết lập sự kiện: khi người dùng 'submit' (nhấn Enter) trong textbox 'msg',
59
+ # hàm 'respond' sẽ được gọi.
60
+ # - Inputs: nội dung từ 'msg' và 'chatbot' (lịch sử).
61
+ # - Outputs: cập nhật lại nội dung cho 'msg' (thành rỗng) và 'chatbot' (lịch sử mới).
62
  msg.submit(respond, [msg, chatbot], [msg, chatbot])
63
 
64
+ # Thêm một vài ví dụ để người dùng dễ bắt đầu
65
  gr.Examples(
66
  examples=[
67
  "Phương tiện giao thông đường bộ gồm những loại nào?",
 
72
  inputs=msg
73
  )
74
 
75
+ # Chạy ứng dụng
76
  if __name__ == "__main__":
77
+ demo.launch()
rag_pipeline.py CHANGED
@@ -79,41 +79,80 @@ def initialize_components(data_path):
79
  "bm25_model": bm25_model
80
  }
81
 
82
- def build_multiturn_prompt(chat_history, current_query, retrieved_context):
83
- # Lấy tối đa N lượt gần nhất (tránh token quá dài)
84
- max_turns = 5
85
- history_text = ""
86
- for i, (user_msg, bot_msg) in enumerate(chat_history[-max_turns:]):
87
- history_text += f"[Người dùng]: {user_msg}\n[Chatbot]: {bot_msg}\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- full_prompt = f"""Bạn một chatbot trợ luật giao thông Việt Nam. Dưới đây các đoạn hội thoại trước đó, trích dẫn luật phù hợp để trả lời câu hỏi tiếp theo.
 
 
90
 
91
- ### Lịch sử hội thoại:
92
- {history_text}
93
 
94
- ### Thông tin luật:
95
- {retrieved_context}
96
 
97
- ### Câu hỏi hiện tại:
98
- {current_query}
99
 
100
- ### Trả lời:"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- return full_prompt
103
 
104
- def generate_response(query, components, chat_history=None):
105
  """
106
- Tạo câu trả lời cho một query bằng cách sử dụng các thành phần đã được khởi tạo.
107
  """
108
- print("--- Bắt đầu quy trình RAG cho query mới ---")
109
 
110
  # Unpack các thành phần
111
  llm_model = components["llm_model"]
112
  tokenizer = components["tokenizer"]
 
 
 
113
 
114
- # 1. Truy xuất ngữ cảnh
115
  retrieved_results = search_relevant_laws(
116
- query_text=query,
117
  embedding_model=components["embedding_model"],
118
  faiss_index=components["faiss_index"],
119
  chunks_data=components["chunks_data"],
@@ -122,7 +161,7 @@ def generate_response(query, components, chat_history=None):
122
  initial_k_multiplier=18
123
  )
124
 
125
- # 2. Định dạng Context
126
  if not retrieved_results:
127
  context = "Không tìm thấy thông tin luật liên quan trong cơ sở dữ liệu."
128
  else:
@@ -134,10 +173,23 @@ def generate_response(query, components, chat_history=None):
134
  context_parts.append(f"{header}\n{text}")
135
  context = "\n\n---\n\n".join(context_parts)
136
 
137
- # 3. Xây dựng Prompt tạo câu trả lời
138
- prompt = build_multiturn_prompt(chat_history or [], query, context)
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- print("--- Bắt đầu tạo câu trả lời từ LLM ---")
141
  inputs = tokenizer(prompt, return_tensors="pt").to("cuda" if torch.cuda.is_available() else "cpu")
142
 
143
  generation_config = dict(
 
79
  "bm25_model": bm25_model
80
  }
81
 
82
+ def format_history(chat_history):
83
+ """Định dạng lịch sử trò chuyện thành một chuỗi dễ đọc cho LLM."""
84
+ if not chat_history:
85
+ return ""
86
+ formatted = []
87
+ for user_turn, bot_turn in chat_history:
88
+ formatted.append(f"Người dùng: {user_turn}")
89
+ formatted.append(f"Bot: {bot_turn}")
90
+ return "\n".join(formatted)
91
+
92
+ def create_standalone_question(question, chat_history, components):
93
+ """
94
+ Sử dụng LLM để tạo một câu hỏi độc lập dựa trên câu hỏi mới và lịch sử trò chuyện.
95
+ """
96
+ llm_model = components["llm_model"]
97
+ tokenizer = components["tokenizer"]
98
+
99
+ # Định dạng lịch sử trò chuyện
100
+ history_str = format_history(chat_history)
101
+
102
+ # Nếu không có lịch sử, câu hỏi đã là độc lập
103
+ if not history_str:
104
+ return question
105
 
106
+ prompt = f"""Dựa vào lịch sử trò chuyện dưới đây câu hỏi theo sau, hãy tạo ra một câu hỏi độc lập, đầy đủ ngữ nghĩa.
107
+ Câu hỏi mới này phải có thể hiểu được mà không cần đọc lại lịch sử.
108
+ Chỉ trả về câu hỏi đã được viết lại, không thêm bất kỳ lời giải thích nào.
109
 
110
+ ### Lịch sử trò chuyện:
111
+ {history_str}
112
 
113
+ ### Câu hỏi theo sau:
114
+ {question}
115
 
116
+ ### Câu hỏi độc lập:
117
+ """
118
 
119
+ inputs = tokenizer(prompt, return_tensors="pt").to("cuda" if torch.cuda.is_available() else "cpu")
120
+
121
+ # Generate với max_new_tokens nhỏ vì chỉ cần tạo lại câu hỏi
122
+ output_ids = llm_model.generate(
123
+ **inputs,
124
+ max_new_tokens=100,
125
+ temperature=0.1,
126
+ do_sample=True,
127
+ pad_token_id=tokenizer.eos_token_id
128
+ )
129
+
130
+ input_length = inputs.input_ids.shape[1]
131
+ generated_ids = output_ids[0][input_length:]
132
+ standalone_question = tokenizer.decode(generated_ids, skip_special_tokens=True).strip()
133
+
134
+ print(f"--- Câu hỏi gốc: '{question}'")
135
+ print(f"--- Câu hỏi độc lập được tạo: '{standalone_question}'")
136
+
137
+ return standalone_question
138
 
 
139
 
140
+ def generate_response(query, chat_history, components): # Thêm chat_history
141
  """
142
+ Tạo câu trả lời cho một query, xem xét đến lịch sử trò chuyện.
143
  """
144
+ print("--- Bắt đầu quy trình RAG (Multi-turn) cho query mới ---")
145
 
146
  # Unpack các thành phần
147
  llm_model = components["llm_model"]
148
  tokenizer = components["tokenizer"]
149
+
150
+ # 1. [MỚI] Tạo câu hỏi độc lập từ lịch sử
151
+ standalone_query = create_standalone_question(query, chat_history, components)
152
 
153
+ # 2. Truy xuất ngữ cảnh bằng câu hỏi độc lập
154
  retrieved_results = search_relevant_laws(
155
+ query_text=standalone_query, # Sử dụng câu hỏi đã được cô đọng
156
  embedding_model=components["embedding_model"],
157
  faiss_index=components["faiss_index"],
158
  chunks_data=components["chunks_data"],
 
161
  initial_k_multiplier=18
162
  )
163
 
164
+ # 3. Định dạng Context (giữ nguyên)
165
  if not retrieved_results:
166
  context = "Không tìm thấy thông tin luật liên quan trong cơ sở dữ liệu."
167
  else:
 
173
  context_parts.append(f"{header}\n{text}")
174
  context = "\n\n---\n\n".join(context_parts)
175
 
176
+ # 4. Xây dựng Prompt cuối cùng (có thêm lịch sử)
177
+ history_str = format_history(chat_history)
178
+ prompt = f"""Bạn là một trợ lý am hiểu luật giao thông Việt Nam. Dựa vào lịch sử trò chuy��n và các thông tin luật được cung cấp dưới đây, hãy trả lời câu hỏi của người dùng một cách tự nhiên và chính xác.
179
+
180
+ ### Lịch sử trò chuyện:
181
+ {history_str}
182
+
183
+ ### Thông tin luật liên quan (dùng để trả lời câu hỏi mới nhất):
184
+ {context}
185
+
186
+ ### Câu hỏi mới nhất của người dùng:
187
+ {query}
188
+
189
+ ### Trả lời của bạn:
190
+ """
191
 
192
+ print("--- Bắt đầu tạo câu trả lời cuối cùng từ LLM ---")
193
  inputs = tokenizer(prompt, return_tensors="pt").to("cuda" if torch.cuda.is_available() else "cpu")
194
 
195
  generation_config = dict(