entidi2608 commited on
Commit
eb4d8a0
·
1 Parent(s): fb63684

upgrade: qa chain

Browse files
Files changed (2) hide show
  1. prompt_templete.py +393 -186
  2. rag_components.py +66 -42
prompt_templete.py CHANGED
@@ -33,45 +33,7 @@
33
 
34
  # Prompt to condense question for legal chain
35
 
36
- CONDENSE_QUESTION_PROMPT = """"
37
- Bạn là một chuyên gia tối ưu hóa truy vấn tìm kiếm cho hệ thống pháp luật.
38
- Nhiệm vụ của bạn là kết hợp lịch sử trò chuyện (nếu có liên quan) và một câu hỏi mới để tạo ra một **câu hỏi độc lập, hoàn chỉnh duy nhất**. Câu hỏi này phải rõ ràng và sẵn sàng để được sử dụng để truy vấn một cơ sở dữ liệu vector.
39
 
40
- **QUY TẮC BẮT BUỘC:**
41
- - **GIỮ NGUYÊN:** Giữ lại tất cả các thuật ngữ pháp lý, số hiệu văn bản, điều khoản, ngày tháng, năm cụ thể.
42
- - **KHÔNG THÊM THẮT:** Nếu câu hỏi gốc mang tính tổng quát, câu hỏi viết lại phải giữ nguyên sự tổng quát đó, không được tự ý thêm các giả định không có trong câu hỏi.
43
- - **BỎ QUA NẾU KHÔNG LIÊN QUAN:** Nếu câu hỏi mới là một chủ đề hoàn toàn khác với lịch sử trò chuyện, hãy bỏ qua lịch sử và chỉ tập trung vào câu hỏi mới.
44
- - **HOÀN CHỈNH:** Câu hỏi cuối cùng phải là một câu hỏi hoàn chỉnh, có đầy đủ chủ ngữ, vị ngữ.
45
-
46
- ---
47
- **VÍ DỤ:**
48
-
49
- **Ví dụ 1: Câu hỏi nối tiếp thay đổi chủ thể**
50
- - Lịch sử hội thoại: `[("Hỏi: Mức phạt khi vượt đèn đỏ với xe máy là bao nhiêu?", "Trả lời: ...")]`
51
- - Câu hỏi mới của người dùng: `còn ô tô thì sao`
52
- - Câu hỏi độc lập: `Mức xử phạt hành chính đối với người điều khiển xe ô tô có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?`
53
-
54
- **Ví dụ 2: Câu hỏi mới không liên quan đến lịch sử**
55
- - Lịch sử hội thoại: `[("Hỏi: Thủ tục ly hôn đơn phương gồm những gì?", "Trả lời: ...")]`
56
- - Câu hỏi mới của người dùng: `quy định về hợp đồng lao động`
57
- - Câu hỏi độc lập: `Quy định của pháp luật về hợp đồng lao động là gì?`
58
-
59
- **Ví dụ 3: Câu hỏi mới đã đủ rõ ràng**
60
- - Lịch sử hội thoại: `(trống)`
61
- - Câu hỏi mới của người dùng: `Người lao động bị nợ lương 2 tháng thì phải làm gì?`
62
- - Câu hỏi độc lập: `Người lao động bị nợ lương 2 tháng thì phải làm gì?`
63
- ---
64
-
65
- **BÂY GIỜ, HÃY THỰC HIỆN NHIỆM VỤ:**
66
-
67
- **Lịch sử hội thoại:**
68
- {chat_history}
69
-
70
- **Câu hỏi mới của người dùng:**
71
- {input}
72
-
73
- **Câu hỏi độc lập:**
74
- """
75
 
76
  # CONDENSE_QUESTION_PROMPT = """
77
  # Dựa trên lịch sử hội thoại sau và một câu hỏi mới của người dùng, hãy viết lại câu hỏi mới thành một câu hỏi **độc lập, đầy đủ ý nghĩa và ngắn gọn nhất có thể**.
@@ -190,43 +152,6 @@ Nhiệm vụ của bạn là trả lời câu hỏi của người dùng dựa H
190
 
191
  # Prompt for generic chain
192
 
193
- GENERAL_PROMPT = """
194
- Bạn là JuriBot, một trợ lý AI thân thiện và chuyên nghiệp, chuyên sâu về pháp luật Việt Nam.
195
- Nhiệm vụ của bạn là trả lời một cách lịch sự dựa trên câu hỏi đã được viết lại và phân loại của người dùng.
196
-
197
- **QUY TẮC PHẢN HỒI (DỰA TRÊN `classification`):**
198
-
199
- - Nếu `classification` là **`chit_chat`**:
200
- - Hãy phản hồi một cách tự nhiên và thân thiện.
201
- - Nếu là lời chào, hãy chào lại.
202
- - Nếu là lời cảm ơn, hãy đáp lại ("Rất vui được giúp bạn!").
203
- - Nếu là nhận xét hoặc hỏi về bản thân ("bạn là ai?"), hãy giới thiệu ngắn gọn vai trò của mình là một trợ lý pháp lý AI, nhấn mạnh chỉ cung cấp thông tin tham khảo và không thay thế luật sư.
204
-
205
- - Nếu `classification` là **`out_of_scope_legal`**:
206
- - Hãy lịch sự trả lời rằng chuyên môn của bạn chỉ giới hạn trong pháp luật Việt Nam và không thể cung cấp thông tin về luật của quốc gia khác.
207
-
208
- - Nếu `classification` là **`general_knowledge`**:
209
- - Hãy lịch sự giải thích rằng bạn là một trợ lý chuyên về pháp lý và không được đào tạo để trả lời các câu hỏi kiến thức chung.
210
-
211
- - Nếu `classification` là **`ambiguous_legal_topic`** (dành cho phiên bản nâng cao của prompt tiền xử lý):
212
- - Hãy yêu cầu người dùng làm rõ câu hỏi, có thể gợi ý một vài ví dụ để giúp họ.
213
-
214
- **HƯỚNG DẪN TÔNG GIỌNG:**
215
- - Luôn giữ thái độ chuyên nghiệp, hữu ích và khiêm tốn.
216
- - Kết thúc câu trả lời bằng một câu hỏi mở để khuyến khích người dùng tiếp tục hỏi về pháp luật Việt Nam (ví dụ: "Bạn có câu hỏi nào khác liên quan đến pháp luật Việt Nam không ạ?").
217
-
218
- ---
219
- **DỮ LIỆU ĐẦU VÀO:**
220
-
221
- **Phân loại:**
222
- {classification}
223
-
224
- **Câu hỏi c��a người dùng (đã được viết lại):**
225
- {rewritten_question}
226
-
227
- **Câu trả lời của bạn:**
228
- """
229
-
230
  # GENERAL_PROMPT = """
231
  # Bạn là JuriBot, một trợ lý AI chuyên sâu về pháp luật Việt Nam.
232
 
@@ -252,94 +177,6 @@ Nhiệm vụ của bạn là trả lời một cách lịch sự dựa trên câ
252
 
253
  # prompt_templete.py
254
 
255
- UNIFIED_PREPROCESSING_PROMPT=""""
256
- Bạn là một AI điều phối viên siêu thông minh, chuyên phân tích và tối ưu hóa các câu hỏi của người dùng cho một hệ thống chatbot **CHUYÊN VỀ PHÁP LUẬT VIỆT NAM**.
257
- Nhiệm vụ của bạn là nhận câu hỏi của người dùng và lịch sử trò chuyện, sau đó viết lại câu hỏi cho rõ ràng và phân loại nó một cách chính xác.
258
-
259
- **QUY TRÌNH BẮT BUỘC:**
260
-
261
- **Bước 1: CHUẨN HÓA CƠ BẢN**
262
- - **Thêm dấu tiếng Việt đầy đủ và chính xác** nếu câu hỏi bị thiếu dấu.
263
- - Sửa các lỗi chính tả và ngữ pháp thông thường.
264
-
265
- **Bước 2: VIẾT LẠI & HOÀN CHỈNH**
266
- - Dựa vào kết quả của Bước 1 và lịch sử trò chuyện, hãy giải quyết các đại từ và các câu hỏi nối tiếp.
267
- - Nếu đầu vào là một câu hỏi pháp lý, hãy thay thế thuật ngữ thông tục bằng thuật ngữ pháp lý chính thức và tạo ra một **câu hỏi tìm kiếm độc lập, hoàn chỉnh**.
268
- - Nếu đầu vào không phải là câu hỏi (ví dụ: chào hỏi, cảm ơn, nhận xét), chỉ cần chuẩn hóa nó thành một câu hoàn chỉnh và lịch sự.
269
-
270
- **Bước 3: PHÂN LOẠI**
271
- - Dựa trên nội dung đã được hoàn chỉnh ở Bước 2, phân loại nó vào MỘT trong các loại sau:
272
- - `legal_rag`: Nếu câu hỏi liên quan đến tra cứu quy định pháp lý của **Việt Nam**.
273
- - `out_of_scope_legal`: Nếu câu hỏi liên quan đến pháp luật của **quốc gia khác** hoặc các vấn đề pháp lý không thuộc phạm vi hệ thống.
274
- - `chit_chat`: Đối với chào hỏi, cảm ơn, nhận xét, hỏi đáp thông thường không phải là câu hỏi (ví dụ: "bạn là ai?", "bạn làm được gì?").
275
- - `general_knowledge`: Đối với các câu hỏi về kiến thức chung, không liên quan đến pháp luật (ví dụ: diện tích một tỉnh, thủ đô một nước).
276
-
277
- **Lịch sử trò chuyện (nếu có):**
278
- {chat_history}
279
-
280
- **Câu hỏi mới của người dùng:**
281
- {input}
282
-
283
- **OUTPUT (Chỉ trả về một đối tượng JSON duy nhất):**
284
- {{
285
- "classification": "...",
286
- "rewritten_question": "..."
287
- }}
288
-
289
- ---
290
- **VÍ DỤ CHI TIẾT:**
291
-
292
- **Ví dụ 1 (Pháp lý trong phạm vi):**
293
- - Câu hỏi mới: "xe may vuot den do bi phat bao nhieu tien"
294
- - Output:
295
- {{
296
- "classification": "legal_rag",
297
- "rewritten_question": "Mức xử phạt hành chính đối với người điều khiển xe mô tô, xe gắn máy có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?"
298
- }}
299
-
300
- **Ví dụ 2 (Pháp lý ngoài phạm vi):**
301
- - Câu hỏi mới: "điều kiện kết hôn tại Mỹ"
302
- - Output:
303
- {{
304
- "classification": "out_of_scope_legal",
305
- "rewritten_question": "Điều kiện kết hôn tại Mỹ được quy định như thế nào?"
306
- }}
307
-
308
- **Ví dụ 3 (Kiến thức chung):**
309
- - Câu hỏi mới: "tuyen quang co dien tich bao nhieu"
310
- - Output:
311
- {{
312
- "classification": "general_knowledge",
313
- "rewritten_question": "Tỉnh Tuyên Quang có diện tích bao nhiêu?"
314
- }}
315
-
316
- **Ví dụ 4 (Trò chuyện/Nhận xét):**
317
- - Câu hỏi mới: "bro trả lời oke phết"
318
- - Output:
319
- {{
320
- "classification": "chit_chat",
321
- "rewritten_question": "Cảm ơn bạn đã nhận xét."
322
- }}
323
-
324
- **Ví dụ 5 (Chào hỏi):**
325
- - Câu hỏi mới: "chao ban"
326
- - Output:
327
- {{
328
- "classification": "chit_chat",
329
- "rewritten_question": "Chào bạn."
330
- }}
331
-
332
- **Ví dụ 6 (Lịch sử & Sai chính tả):**
333
- - Lịch sử: [("Hỏi: Điều kiện kết hôn là gì?", "Trả lời: ...")]
334
- - Câu hỏi mới: "the thu tuc ly hon don phuong thì sao"
335
- - Output:
336
- {{
337
- "classification": "legal_rag",
338
- "rewritten_question": "Thủ tục ly hôn theo yêu cầu của một bên (ly hôn đơn phương) được quy định như thế nào?"
339
- }}
340
- ---
341
- """
342
-
343
 
344
  # UNIFIED_PREPROCESSING_PROMPT = """
345
  # Bạn là một AI điều phối viên siêu thông minh, chuyên phân tích và tối ưu hóa các câu hỏi của người dùng cho một hệ thống chatbot **CHUYÊN VỀ PHÁP LUẬT VIỆT NAM**.
@@ -413,43 +250,413 @@ Nhiệm vụ của bạn là nhận câu hỏi của người dùng và lịch s
413
 
414
 
415
 
 
416
 
 
 
417
 
418
- KEYWORD_EXTRACTION_PROMPT = """
419
- Bạn một chuyên gia phân tích truy vấn pháp lý. Nhiệm vụ của bạn là nhận một câu hỏi và rút ra một danh sách các **cụm từ khóa cốt lõi, ngắn gọn khả năng xuất hiện cao nhất** trong nội dung một điều luật cụ thể.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
- **HƯỚNG DẪN:**
422
- - Tập trung vào **hành vi vi phạm** và **đối tượng**.
423
- - Loại bỏ các từ hỏi như "bao nhiêu", "là gì", "thế nào".
424
- - Sử dụng các thuật ngữ pháp lý nếu có thể.
425
- - Chỉ trả về các cụm từ khóa, mỗi cụm từ trên một dòng, không đánh số.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
- **Ví dụ 1:**
428
- Câu hỏi: Mức xử phạt hành chính khi xe máy vượt đèn đỏ theo quy định hiện hành?
429
- OUTPUT:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  xử phạt xe máy
431
  không chấp hành hiệu lệnh đèn tín hiệu giao thông
432
  tước quyền sử dụng giấy phép lái xe
433
 
434
- ** dụ 2:**
435
- Câu hỏi: Thủ tục ly hôn đơn phương cần những giấy tờ gì?
436
- OUTPUT:
437
  thủ tục ly hôn đơn phương
438
  hồ sơ ly hôn
439
  giấy tờ cần thiết
440
  tòa án nhân dân
441
-
442
- **Ví dụ 3:**
443
- Câu hỏi: Người lao động bị nợ lương 2 tháng phải làm sao?
444
- OUTPUT:
445
- người lao động bị nợ lương
446
- người sử dụng lao động không trả lương
447
- khiếu nại tiền lương
448
- khởi kiện đòi lương
449
-
450
  ---
451
- **Câu hỏi gốc:**
452
  {question}
453
 
454
  **OUTPUT:**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  """
 
33
 
34
  # Prompt to condense question for legal chain
35
 
 
 
 
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  # CONDENSE_QUESTION_PROMPT = """
39
  # Dựa trên lịch sử hội thoại sau và một câu hỏi mới của người dùng, hãy viết lại câu hỏi mới thành một câu hỏi **độc lập, đầy đủ ý nghĩa và ngắn gọn nhất có thể**.
 
152
 
153
  # Prompt for generic chain
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  # GENERAL_PROMPT = """
156
  # Bạn là JuriBot, một trợ lý AI chuyên sâu về pháp luật Việt Nam.
157
 
 
177
 
178
  # prompt_templete.py
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  # UNIFIED_PREPROCESSING_PROMPT = """
182
  # Bạn là một AI điều phối viên siêu thông minh, chuyên phân tích và tối ưu hóa các câu hỏi của người dùng cho một hệ thống chatbot **CHUYÊN VỀ PHÁP LUẬT VIỆT NAM**.
 
250
 
251
 
252
 
253
+ #-----------------------------New Prompts Vietnamese--------------------------------
254
 
255
+ # KEYWORD_EXTRACTION_PROMPT = """
256
+ # Bạn là một chuyên gia phân tích truy vấn pháp lý. Nhiệm vụ của bạn là nhận một câu h��i và rút ra một danh sách các **cụm từ khóa cốt lõi, ngắn gọn và có khả năng xuất hiện cao nhất** trong nội dung một điều luật cụ thể.
257
 
258
+ # **HƯỚNG DẪN:**
259
+ # - Tập trung vào **hành vi vi phạm** và **đối tượng**.
260
+ # - Loại bỏ các từ hỏi như "bao nhiêu", "là gì", "thế nào".
261
+ # - Sử dụng các thuật ngữ pháp lý nếu có thể.
262
+ # - Chỉ trả về các cụm từ khóa, mỗi cụm từ trên một dòng, không có đánh số.
263
+
264
+ # **Ví dụ 1:**
265
+ # Câu hỏi: Mức xử phạt hành chính khi xe máy vượt đèn đỏ theo quy định hiện hành?
266
+ # OUTPUT:
267
+ # xử phạt xe máy
268
+ # không chấp hành hiệu lệnh đèn tín hiệu giao thông
269
+ # tước quyền sử dụng giấy phép lái xe
270
+
271
+ # **Ví dụ 2:**
272
+ # Câu hỏi: Thủ tục ly hôn đơn phương cần những giấy tờ gì?
273
+ # OUTPUT:
274
+ # thủ tục ly hôn đơn phương
275
+ # hồ sơ ly hôn
276
+ # giấy tờ cần thiết
277
+ # tòa án nhân dân
278
+
279
+ # **Ví dụ 3:**
280
+ # Câu hỏi: Người lao động bị nợ lương 2 tháng phải làm sao?
281
+ # OUTPUT:
282
+ # người lao động bị nợ lương
283
+ # người sử dụng lao động không trả lương
284
+ # khiếu nại tiền lương
285
+ # khởi kiện đòi lương
286
+
287
+ # ---
288
+ # **Câu hỏi gốc:**
289
+ # {question}
290
 
291
+ # **OUTPUT:**
292
+ # """
293
+
294
+ # CONDENSE_QUESTION_PROMPT = """"
295
+ # Bạn một chuyên gia tối ưu hóa truy vấn tìm kiếm cho hệ thống pháp luật.
296
+ # Nhiệm vụ của bạn là kết hợp lịch sử trò chuyện (nếu có liên quan) và một câu hỏi mới để tạo ra một **câu hỏi độc lập, hoàn chỉnh duy nhất**. Câu hỏi này phải rõ ràng và sẵn sàng để được sử dụng để truy vấn một cơ sở dữ liệu vector.
297
+
298
+ # **QUY TẮC BẮT BUỘC:**
299
+ # - **GIỮ NGUYÊN:** Giữ lại tất cả các thuật ngữ pháp lý, số hiệu văn bản, điều khoản, ngày tháng, năm cụ thể.
300
+ # - **KHÔNG THÊM THẮT:** Nếu câu hỏi gốc mang tính tổng quát, câu hỏi viết lại phải giữ nguyên sự tổng quát đó, không được tự ý thêm các giả định không có trong câu hỏi.
301
+ # - **BỎ QUA NẾU KHÔNG LIÊN QUAN:** Nếu câu hỏi mới là một chủ đề hoàn toàn khác với lịch sử trò chuyện, hãy bỏ qua lịch sử và chỉ tập trung vào câu hỏi mới.
302
+ # - **HOÀN CHỈNH:** Câu hỏi cuối cùng phải là một câu hỏi hoàn chỉnh, có đầy đủ chủ ngữ, vị ngữ.
303
+
304
+ # ---
305
+ # **VÍ DỤ:**
306
+
307
+ # **Ví dụ 1: Câu hỏi nối tiếp thay đổi chủ thể**
308
+ # - Lịch sử hội thoại: `[("Hỏi: Mức phạt khi vượt đèn đỏ với xe máy là bao nhiêu?", "Trả lời: ...")]`
309
+ # - Câu hỏi mới của người dùng: `còn ô tô thì sao`
310
+ # - Câu hỏi độc lập: `Mức xử phạt hành chính đối với người điều khiển xe ô tô có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?`
311
+
312
+ # **Ví dụ 2: Câu hỏi mới không liên quan đến lịch sử**
313
+ # - Lịch sử hội thoại: `[("Hỏi: Thủ tục ly hôn đơn phương gồm những gì?", "Trả lời: ...")]`
314
+ # - Câu hỏi mới của người dùng: `quy định về hợp đồng lao động`
315
+ # - Câu hỏi độc lập: `Quy định của pháp luật về hợp đồng lao động là gì?`
316
+
317
+ # **Ví dụ 3: Câu hỏi mới đã đủ rõ ràng**
318
+ # - Lịch sử hội thoại: `(trống)`
319
+ # - Câu hỏi mới của người dùng: `Người lao động bị nợ lương 2 tháng thì phải làm gì?`
320
+ # - Câu hỏi độc lập: `Người lao động bị nợ lương 2 tháng thì phải làm gì?`
321
+ # ---
322
+
323
+ # **BÂY GIỜ, HÃY THỰC HIỆN NHIỆM VỤ:**
324
+
325
+ # **Lịch sử hội thoại:**
326
+ # {chat_history}
327
+
328
+ # **Câu hỏi mới của người dùng:**
329
+ # {input}
330
+
331
+ # **Câu hỏi độc lập:**
332
+ # """
333
 
334
+
335
+ # UNIFIED_PREPROCESSING_PROMPT=""""
336
+ # Bạn là một AI điều phối viên siêu thông minh, chuyên phân tích và tối ưu hóa các câu hỏi của người dùng cho một hệ thống chatbot **CHUYÊN VỀ PHÁP LUẬT VIỆT NAM**.
337
+ # Nhiệm vụ của bạn là nhận câu hỏi của người dùng và lịch sử trò chuyện, sau đó viết lại câu hỏi cho rõ ràng và phân loại nó một cách chính xác.
338
+
339
+ # **QUY TRÌNH BẮT BUỘC:**
340
+
341
+ # **Bước 1: CHUẨN HÓA CƠ BẢN**
342
+ # - **Thêm dấu tiếng Việt đầy đủ và chính xác** nếu câu hỏi bị thiếu dấu.
343
+ # - Sửa các lỗi chính tả và ngữ pháp thông thường.
344
+
345
+ # **Bước 2: VIẾT LẠI & HOÀN CHỈNH**
346
+ # - Dựa vào kết quả của Bước 1 và lịch sử trò chuyện, hãy giải quyết các đại từ và các câu hỏi nối tiếp.
347
+ # - Nếu đầu vào là một câu hỏi pháp lý, hãy thay thế thuật ngữ thông tục bằng thuật ngữ pháp lý chính thức và tạo ra một **câu hỏi tìm kiếm độc lập, hoàn chỉnh**.
348
+ # - Nếu đầu vào không phải là câu hỏi (ví dụ: chào hỏi, cảm ơn, nhận xét), chỉ cần chuẩn hóa nó thành một câu hoàn chỉnh và lịch sự.
349
+
350
+ # **Bước 3: PHÂN LOẠI**
351
+ # - Dựa trên nội dung đã được hoàn chỉnh ở Bước 2, phân loại nó vào MỘT trong các loại sau:
352
+ # - `legal_rag`: Nếu câu hỏi liên quan đến tra cứu quy định pháp lý của **Việt Nam**.
353
+ # - `out_of_scope_legal`: Nếu câu hỏi liên quan đến pháp luật của **quốc gia khác** hoặc các vấn đề pháp lý không thuộc phạm vi hệ thống.
354
+ # - `chit_chat`: Đối với chào hỏi, cảm ơn, nhận xét, hỏi đáp thông thường không phải là câu hỏi (ví dụ: "bạn là ai?", "bạn làm được gì?").
355
+ # - `general_knowledge`: Đối với các câu hỏi về kiến thức chung, không liên quan đến pháp luật (ví dụ: diện tích một tỉnh, thủ đô một nước).
356
+
357
+ # **Lịch sử trò chuyện (nếu có):**
358
+ # {chat_history}
359
+
360
+ # **Câu hỏi mới của người dùng:**
361
+ # {input}
362
+
363
+ # **OUTPUT:**
364
+ # **QUAN TRỌNG: Chỉ được trả về một đối tượng JSON duy nhất, không có bất kỳ văn bản, giải thích, hay lời dẫn nào trước hoặc sau nó. Toàn bộ phản hồi của bạn phải là một JSON hợp lệ.**
365
+ # {{
366
+ # "classification": "...",
367
+ # "rewritten_question": "..."
368
+ # }}
369
+
370
+ # ---
371
+ # **VÍ DỤ CHI TIẾT:**
372
+
373
+ # **Ví dụ 1 (Pháp lý trong phạm vi):**
374
+ # - Câu hỏi mới: "xe may vuot den do bi phat bao nhieu tien"
375
+ # - Output:
376
+ # {{
377
+ # "classification": "legal_rag",
378
+ # "rewritten_question": "Mức xử phạt hành chính đối với người điều khiển xe mô tô, xe gắn máy có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?"
379
+ # }}
380
+
381
+ # **Ví dụ 2 (Pháp lý ngoài phạm vi):**
382
+ # - Câu hỏi mới: "điều kiện kết hôn tại Mỹ"
383
+ # - Output:
384
+ # {{
385
+ # "classification": "out_of_scope_legal",
386
+ # "rewritten_question": "Điều kiện kết hôn tại Mỹ được quy định như thế nào?"
387
+ # }}
388
+
389
+ # **Ví dụ 3 (Kiến thức chung):**
390
+ # - Câu hỏi mới: "tuyen quang co dien tich bao nhieu"
391
+ # - Output:
392
+ # {{
393
+ # "classification": "general_knowledge",
394
+ # "rewritten_question": "Tỉnh Tuyên Quang có diện tích bao nhiêu?"
395
+ # }}
396
+
397
+ # **Ví dụ 4 (Trò chuyện/Nhận xét):**
398
+ # - Câu hỏi mới: "bro trả lời oke phết"
399
+ # - Output:
400
+ # {{
401
+ # "classification": "chit_chat",
402
+ # "rewritten_question": "Cảm ơn bạn đã nhận xét."
403
+ # }}
404
+
405
+ # **Ví dụ 5 (Chào hỏi):**
406
+ # - Câu hỏi mới: "chao ban"
407
+ # - Output:
408
+ # {{
409
+ # "classification": "chit_chat",
410
+ # "rewritten_question": "Chào bạn."
411
+ # }}
412
+
413
+ # **Ví dụ 6 (Lịch sử & Sai chính tả):**
414
+ # - Lịch sử: [("Hỏi: Điều kiện kết hôn là gì?", "Trả lời: ...")]
415
+ # - Câu hỏi mới: "the thu tuc ly hon don phuong thì sao"
416
+ # - Output:
417
+ # {{
418
+ # "classification": "legal_rag",
419
+ # "rewritten_question": "Thủ tục ly hôn theo yêu cầu của một bên (ly hôn đơn phương) được quy định như thế nào?"
420
+ # }}
421
+ # ---
422
+ # """
423
+
424
+
425
+ # GENERAL_PROMPT = """
426
+ # Bạn là JuriBot, một trợ lý AI thân thiện và chuyên nghiệp, chuyên sâu về pháp luật Việt Nam.
427
+ # Nhiệm vụ của bạn là trả lời một cách lịch sự dựa trên câu hỏi đã được viết lại và phân loại của người dùng.
428
+
429
+ # **QUY TẮC PHẢN HỒI (DỰA TRÊN `classification`):**
430
+
431
+ # - Nếu `classification` là **`chit_chat`**:
432
+ # - Hãy phản hồi một cách tự nhiên và thân thiện.
433
+ # - Nếu là lời chào, hãy chào lại.
434
+ # - Nếu là lời cảm ơn, hãy đáp lại ("Rất vui được giúp bạn!").
435
+ # - Nếu là nhận xét hoặc hỏi về bản thân ("bạn là ai?"), hãy giới thiệu ngắn gọn vai trò của mình là một trợ lý pháp lý AI, nhấn mạnh chỉ cung cấp thông tin tham khảo và không thay thế luật sư.
436
+
437
+ # - Nếu `classification` là **`out_of_scope_legal`**:
438
+ # - Hãy lịch sự trả lời rằng chuyên môn của bạn chỉ giới hạn trong pháp luật Việt Nam và không thể cung cấp thông tin về luật của quốc gia khác.
439
+
440
+ # - Nếu `classification` là **`general_knowledge`**:
441
+ # - Hãy lịch sự giải thích rằng bạn là một trợ lý chuyên về pháp lý và không được đào tạo để trả lời các câu hỏi kiến thức chung.
442
+
443
+ # - Nếu `classification` là **`ambiguous_legal_topic`** (dành cho phiên bản nâng cao của prompt tiền xử lý):
444
+ # - Hãy yêu cầu người dùng làm rõ câu hỏi, có thể gợi ý một vài ví dụ để giúp họ.
445
+
446
+ # **HƯỚNG DẪN TÔNG GIỌNG:**
447
+ # - Luôn giữ thái độ chuyên nghiệp, hữu ích và khiêm tốn.
448
+ # - Kết thúc câu trả lời bằng một câu hỏi mở để khuyến khích người dùng tiếp tục hỏi về pháp luật Việt Nam (ví dụ: "Bạn có câu hỏi nào khác liên quan đến pháp luật Việt Nam không ạ?").
449
+
450
+ # ---
451
+ # **DỮ LIỆU ĐẦU VÀO:**
452
+
453
+ # **Phân loại:**
454
+ # {classification}
455
+
456
+ # **Câu hỏi của người dùng (đã được viết lại):**
457
+ # {rewritten_question}
458
+
459
+ # **Câu trả lời của bạn:**
460
+ # """
461
+ #------------------------------New Prompts Vietnamese--------------------------------
462
+
463
+
464
+ #-----------------------------New Prompts English--------------------------------
465
+
466
+ # ==============================================================================
467
+ # PROMPT 1: UNIFIED_PREPROCESSING_PROMPT
468
+ # Nhiệm vụ: "Bộ não" chính, phân tích và định tuyến câu hỏi.
469
+ # Tối ưu: Chỉ dẫn tiếng Anh ngắn gọn, giữ ví dụ tiếng Việt, quy tắc JSON nghiêm ngặt.
470
+ # ==============================================================================
471
+ UNIFIED_PREPROCESSING_PROMPT = """
472
+ You are a lean, efficient query pre-processing engine for a Vietnamese Law chatbot.
473
+ Your task is to analyze the user's input and chat history, then output a single, raw JSON object.
474
+
475
+ **INSTRUCTIONS:**
476
+ 1. **Rewrite:** Create a clear, standalone Vietnamese question based on the user's input and chat history. Use official legal terms for legal queries.
477
+ 2. **Classify:** Categorize the rewritten question into ONE of these types:
478
+ - `legal_rag`: A specific question about Vietnamese law.
479
+ - `out_of_scope_legal`: A question about non-Vietnamese law.
480
+ - `chit_chat`: Greetings, thanks, feedback, or questions about the bot.
481
+ - `general_knowledge`: A non-legal, general knowledge question.
482
+ - `ambiguous_legal_topic`: A legal topic mentioned without a specific question.
483
+
484
+ ---
485
+ **EXAMPLES:**
486
+
487
+ # Example 1: In-scope Legal
488
+ - User Input: "xe may vuot den do bi phat bao nhieu tien"
489
+ - Output:
490
+ {
491
+ "classification": "legal_rag",
492
+ "rewritten_question": "Mức xử phạt hành chính đối với người điều khiển xe mô tô, xe gắn máy có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?"
493
+ }
494
+
495
+ # Example 2: Out-of-scope Legal
496
+ - User Input: "điều kiện kết hôn tại Mỹ"
497
+ - Output:
498
+ {
499
+ "classification": "out_of_scope_legal",
500
+ "rewritten_question": "Điều kiện kết hôn tại Mỹ được quy định như thế nào?"
501
+ }
502
+
503
+ # Example 3: Chit-chat
504
+ - User Input: "bro trả lời oke phết"
505
+ - Output:
506
+ {
507
+ "classification": "chit_chat",
508
+ "rewritten_question": "Cảm ơn bạn đã nhận xét."
509
+ }
510
+
511
+ # Example 4: History-aware
512
+ - History: [("Hỏi: Điều kiện kết hôn là gì?", "Trả lời: ...")]
513
+ - User Input: "the thu tuc ly hon don phuong thì sao"
514
+ - Output:
515
+ {
516
+ "classification": "legal_rag",
517
+ "rewritten_question": "Thủ tục ly hôn theo yêu cầu của một bên (ly hôn đơn phương) được quy định như thế nào?"
518
+ }
519
+ ---
520
+
521
+ **Chat History:**
522
+ {chat_history}
523
+
524
+ **User Input:**
525
+ {input}
526
+
527
+ **OUTPUT:**
528
+ **CRITICAL: Your entire response MUST be a single, raw JSON object. Do not include any text, explanations, or markdown formatting before or after the JSON.**
529
+ """
530
+
531
+ # ==============================================================================
532
+ # PROMPT 2: QA_PROMPT_TEMPLATE
533
+ # Nhiệm vụ: Prompt RAG chính, tạo câu trả lời từ context.
534
+ # Tối ưu: Thêm quy tắc trả lời đa ngôn ngữ, giữ cấu trúc Chain-of-Thought.
535
+ # ==============================================================================
536
+ QA_PROMPT_TEMPLATE = """
537
+ You are JuriBot, a meticulous AI legal assistant.
538
+ **CRITICAL RULE: Always respond in the same language as the user's QUESTION (`{input}`).**
539
+
540
+ Your task is to answer the QUESTION based **strictly** on the provided CONTEXT.
541
+
542
+ **RULES:**
543
+ 1. **NO OUTSIDE KNOWLEDGE:** Your answer must be 100% derived from the CONTEXT.
544
+ 2. **BE HONEST:** If the CONTEXT is empty or irrelevant, state: "Dựa trên các tài liệu được cung cấp, tôi không tìm thấy thông tin để trả lời câu hỏi này."
545
+ 3. **PRIORITIZE NEWEST LAW:** If laws conflict, use the one with the most recent `nam_ban_hanh` (year) from metadata.
546
+ 4. **STATE FACTS, NOT ADVICE:** Present information only. Do not give advice or opinions.
547
+
548
+ ---
549
+ **CONTEXT:**
550
+ *(Note: Context is a list of documents with `page_content` and `metadata`)*
551
+ {context}
552
+ ---
553
+ **QUESTION:**
554
+ {input}
555
+ ---
556
+
557
+ **ANSWER GENERATION PROCESS (Internal thought process):**
558
+
559
+ **1. Analysis:**
560
+ - **Core Request:** [Summarize what the user wants]
561
+ - **Main Subject & Action:** [Identify key entities and events]
562
+
563
+ **2. Context Evaluation:**
564
+ - [List relevant documents from CONTEXT and why. If none, state that.]
565
+
566
+ **3. Response Plan:**
567
+ - [Outline the answer structure based on selected documents.]
568
+
569
+ **4. Final Answer (Use this exact format):**
570
+
571
+ **[BEGIN FINAL ANSWER]**
572
+
573
+ ### [A clear, concise title for the answer in the user's language]
574
+
575
+ [Present the answer here. Use bullet points for key information.]
576
+ - **Quy định (Regulation):** [Content of the regulation]
577
+ - **Mức phạt (Penalty):** [Details on the penalty, if any]
578
+ - **Biện pháp bổ sung (Additional Measures):** [Details on supplementary penalties]
579
+
580
+ ### Nguồn tham khảo (Source)
581
+
582
+ - **Văn bản (Document):** [`ten_van_ban`, `so_hieu` from metadata]
583
+ - **Điều khoản (Clause):** [`dieu_code`, `khoan_code` from metadata, if available]
584
+
585
+ *(Repeat Source block for each document used)*
586
+
587
+ **[END FINAL ANSWER]**
588
+ """
589
+
590
+
591
+ # ==============================================================================
592
+ # PROMPT 3: GENERAL_RESPONSE_PROMPT
593
+ # Nhiệm vụ: Tạo các câu trả lời không cần tra cứu (Non-RAG).
594
+ # Tối ưu: Thêm quy tắc trả lời đa ngôn ngữ, nhận cả `input` gốc.
595
+ # ==============================================================================
596
+ GENERAL_RESPONSE_PROMPT = """
597
+ You are JuriBot, a friendly and professional AI assistant for Vietnamese law.
598
+ **CRITICAL RULE: Always respond in the same language as the user's original question (`{input}`).**
599
+
600
+ Based on the provided classification, formulate a polite response.
601
+
602
+ - If `classification` is **`chit_chat`**: Respond naturally. If it's about you, introduce yourself as a Vietnamese legal AI assistant.
603
+ - If `classification` is **`out_of_scope_legal`**: Politely state that your expertise is limited to Vietnamese law.
604
+ - If `classification` is **`general_knowledge`**: Politely explain you are a specialized legal AI and cannot answer general knowledge questions.
605
+ - If `classification` is **`ambiguous_legal_topic`**: Ask for clarification and suggest example questions.
606
+
607
+ ---
608
+ **INPUT DATA:**
609
+ - **User's Original Question:** {input}
610
+ - **Classification:** {classification}
611
+ - **Rewritten Question:** {rewritten_question}
612
+
613
+ **YOUR RESPONSE (in user's language):**
614
+ """
615
+
616
+ # ==============================================================================
617
+ # Các prompt phụ (KEYWORD và CONDENSE) - Tùy chọn nếu bạn có dùng trong chain
618
+ # Tối ưu: Đã được dịch và rút gọn.
619
+ # ==============================================================================
620
+ KEYWORD_EXTRACTION_PROMPT = """
621
+ You are a legal query analyst. Extract core search keywords from the Vietnamese question.
622
+ Focus on the **violating act**, **subject**, and **consequences**. Remove question words.
623
+ Return only keywords, one per line.
624
+
625
+ **Question:** Mức xử phạt hành chính khi xe máy vượt đèn đỏ theo quy định hiện hành?
626
+ **OUTPUT:**
627
  xử phạt xe máy
628
  không chấp hành hiệu lệnh đèn tín hiệu giao thông
629
  tước quyền sử dụng giấy phép lái xe
630
 
631
+ **Question:** Thủ tục ly hôn đơn phương cần những giấy tờ gì?
632
+ **OUTPUT:**
 
633
  thủ tục ly hôn đơn phương
634
  hồ sơ ly hôn
635
  giấy tờ cần thiết
636
  tòa án nhân dân
 
 
 
 
 
 
 
 
 
637
  ---
638
+ **Question:**
639
  {question}
640
 
641
  **OUTPUT:**
642
+ """
643
+
644
+ CONDENSE_QUESTION_PROMPT = """
645
+ Rephrase the follow-up question into a single, standalone Vietnamese question, using the chat history for context. If the new question is unrelated, ignore the history.
646
+
647
+ **History:** `[("Hỏi: Mức phạt khi vượt đèn đỏ với xe máy là bao nhiêu?", "Trả lời: ...")]`
648
+ **Follow-up:** `còn ô tô thì sao`
649
+ **Standalone Question:** `Mức xử phạt hành chính đối với người điều khiển xe ô tô có hành vi không chấp hành hiệu lệnh của đèn tín hiệu giao thông là bao nhiêu?`
650
+
651
+ **History:** `[("Hỏi: Thủ tục ly hôn đơn phương gồm những gì?", "Trả lời: ...")]`
652
+ **Follow-up:** `quy định về hợp đồng lao động`
653
+ **Standalone Question:** `Quy định của pháp luật về hợp đồng lao động là gì?`
654
+ ---
655
+ **History:**
656
+ {chat_history}
657
+
658
+ **Follow-up Question:**
659
+ {input}
660
+
661
+ **Standalone Question:**
662
  """
rag_components.py CHANGED
@@ -14,6 +14,8 @@ import weaviate
14
  import weaviate.classes.config as wvc_config
15
  from weaviate.exceptions import WeaviateQueryException
16
  import time
 
 
17
  from operator import itemgetter
18
 
19
 
@@ -425,22 +427,36 @@ def get_google_llm(google_api_key):
425
 
426
 
427
  #new update
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  def _extract_final_answer(rag_output_with_thinking: str) -> str:
429
  """
430
  Hàm trợ giúp để trích xuất câu trả lời cuối cùng từ output của QA_PROMPT_TEMPLATE.
431
- Nó tìm các thẻ đánh dấu đặc biệt và chỉ trả về nội dung ở giữa.
432
  """
433
- start_tag = "[BẮT ĐẦU CÂU TRẢ LỜI CUỐI CÙNG]"
434
- end_tag = "[KẾT THÚC CÂU TRẢ LỜI CUỐI CÙNG]"
435
 
436
  start_index = rag_output_with_thinking.find(start_tag)
437
  end_index = rag_output_with_thinking.find(end_tag)
438
 
439
  if start_index != -1 and end_index != -1:
440
- # Lấy nội dung giữa 2 thẻ
441
  return rag_output_with_thinking[start_index + len(start_tag):end_index].strip()
442
 
443
- # Nếu không tìm thấy thẻ, trả về toàn bộ output để gỡ lỗi
444
  logger.warning("Không tìm thấy thẻ đánh dấu trả lời trong output của RAG. Trả về toàn bộ output.")
445
  return rag_output_with_thinking
446
 
@@ -451,63 +467,53 @@ def create_qa_chain(
451
  process_input_llm: any = None
452
  ):
453
  """
454
- PHIÊN BẢN CẢI TIẾN: Tạo ra một RAG chain hoàn chỉnh với kiến trúc dựa trên Router thông minh,
455
- sử dụng các prompt đã được tối ưu hóa.
456
  """
457
  if not all([llm, retriever]):
458
  logger.error("🔸 Thiếu LLM hoặc Retriever chính để tạo QA Chain.")
459
  return None
460
 
461
  try:
462
- logger.info("🔸 Bắt đầu tạo QA Chain với Router thông minh...")
463
 
464
  preprocessing_llm = process_input_llm or llm
465
 
466
- # ----- 1. KHAI BÁO CÁC PROMPT TEMPLATE MỚI -----
467
-
468
- # Prompt tiền xử lý hợp nhất (bộ não của hệ thống)
469
- # Dòng này đã được sửa để khớp với tên prompt của bạn
470
  unified_preprocessing_prompt = ChatPromptTemplate.from_template(
471
  prompt_templete.UNIFIED_PREPROCESSING_PROMPT
472
  )
473
-
474
- # Prompt tạo câu trả lời RAG (với Chain-of-Thought)
475
  qa_rag_prompt = ChatPromptTemplate.from_template(
476
  prompt_templete.QA_PROMPT_TEMPLATE
477
  )
478
-
479
- # Prompt tạo câu trả lời chung (cho các trường hợp không phải pháp lý)
480
  general_response_prompt = ChatPromptTemplate.from_template(
481
- prompt_templete.GENERAL_PROMPT
482
  )
483
 
484
- # ----- 2. ĐỊNH NGHĨA CÁC NHÁNH XỬ LÝ (CHAINS) -----
485
 
486
- # --- Nhánh A: LEGAL QUERY (Nhánh RAG chính) ---
487
  legal_rag_chain = (
488
  RunnablePassthrough.assign(
489
  context=itemgetter("rewritten_question") | retriever
490
  ).assign(
491
- # Chạy chuỗi con để chỉ lấy câu trả lời
492
  answer=(
493
- RunnablePassthrough.assign(input=itemgetter("rewritten_question"))
494
- | qa_rag_prompt
495
  | llm
496
  | StrOutputParser()
497
  | RunnableLambda(_extract_final_answer)
498
  )
499
  )
500
- # Chỉ chọn lọc 'answer' 'context' cho output cuối cùng của nhánh này
501
  | (lambda x: {"answer": x["answer"], "context": x["context"]})
502
- ).with_config({"run_name": "LegalRAGChainWithContext"})
503
-
504
 
505
- # --- Nhánh B: GENERAL RESPONSE (Nhánh phản hồi chung cho các loại còn lại) ---
506
  general_response_chain = (
507
  general_response_prompt
508
  | llm
509
  | StrOutputParser()
510
- # Bọc output lại thành dict để đồng bộ với nhánh legal
511
  | (lambda answer_str: {"answer": answer_str, "context": []})
512
  ).with_config({"run_name": "GeneralResponseChain"})
513
 
@@ -515,8 +521,6 @@ def create_qa_chain(
515
  def route(info: dict):
516
  classification = info.get("classification")
517
  logger.info(f"➡️ Định tuyến truy vấn với phân loại: '{classification}'")
518
-
519
- # Sử dụng 'legal_rag' vì đó là tên phân loại trong prompt của bạn
520
  if classification == "legal_rag":
521
  return legal_rag_chain
522
  else:
@@ -524,24 +528,44 @@ def create_qa_chain(
524
 
525
  # ----- 4. KẾT HỢP THÀNH FULL CHAIN -----
526
 
527
- # Bước 1: Tiền xử để lấy ra dict {"classification": "...", "rewritten_question": "..."}
528
- preprocessing_chain = unified_preprocessing_prompt | preprocessing_llm | JsonOutputParser()
529
-
530
-
531
- def chain_with_context(info_dict: dict):
532
- selected_chain = route(info_dict)
533
- return selected_chain.invoke(info_dict)
534
 
535
- # Giờ đây full_chain sẽ trả về một dict {"answer": ..., "context": ...}
536
- # không cần bước lambda cuối cùng
537
- full_chain = preprocessing_chain | RunnableLambda(chain_with_context)
 
 
 
 
538
 
539
- logger.info("✅ Tạo thành công QA Chain phiên bản TỐI ƯU NHẤT.")
540
- return full_chain
 
 
 
 
 
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
 
543
 
544
- logger.info("✅ Tạo thành công QA Chain với Router thông minh.")
545
  return full_chain
546
 
547
  except Exception as e:
 
14
  import weaviate.classes.config as wvc_config
15
  from weaviate.exceptions import WeaviateQueryException
16
  import time
17
+ import json
18
+ import re
19
  from operator import itemgetter
20
 
21
 
 
427
 
428
 
429
  #new update
430
+ def _extract_and_parse_json(text_with_json: str) -> dict:
431
+ """
432
+ Hàm trợ giúp để tìm và trích xuất khối JSON đầu tiên từ một chuỗi văn bản.
433
+ """
434
+ json_match = re.search(r'\{.*\}', text_with_json, re.DOTALL)
435
+ if json_match:
436
+ json_str = json_match.group(0)
437
+ try:
438
+ return json.loads(json_str)
439
+ except json.JSONDecodeError:
440
+ logger.error(f"Không thể phân tích chuỗi JSON được trích xuất: {json_str}")
441
+ raise
442
+ else:
443
+ logger.error(f"Không tìm thấy khối JSON nào trong output: {text_with_json}")
444
+ raise ValueError("Không tìm thấy đối tượng JSON trong output của LLM")
445
+
446
+
447
  def _extract_final_answer(rag_output_with_thinking: str) -> str:
448
  """
449
  Hàm trợ giúp để trích xuất câu trả lời cuối cùng từ output của QA_PROMPT_TEMPLATE.
 
450
  """
451
+ start_tag = "[BEGIN FINAL ANSWER]"
452
+ end_tag = "[END FINAL ANSWER]"
453
 
454
  start_index = rag_output_with_thinking.find(start_tag)
455
  end_index = rag_output_with_thinking.find(end_tag)
456
 
457
  if start_index != -1 and end_index != -1:
 
458
  return rag_output_with_thinking[start_index + len(start_tag):end_index].strip()
459
 
 
460
  logger.warning("Không tìm thấy thẻ đánh dấu trả lời trong output của RAG. Trả về toàn bộ output.")
461
  return rag_output_with_thinking
462
 
 
467
  process_input_llm: any = None
468
  ):
469
  """
470
+ PHIÊN BẢN CUỐI CÙNG: Tạo ra một RAG chain hoàn chỉnh, khả năng xử đa ngôn ngữ
471
+ bằng cách bảo toàn dữ liệu đầu vào gốc.
472
  """
473
  if not all([llm, retriever]):
474
  logger.error("🔸 Thiếu LLM hoặc Retriever chính để tạo QA Chain.")
475
  return None
476
 
477
  try:
478
+ logger.info("🔸 Bắt đầu tạo QA Chain phiên bản Tối Ưu Nhất...")
479
 
480
  preprocessing_llm = process_input_llm or llm
481
 
482
+ # ----- 1. KHAI BÁO PROMPTS -----
 
 
 
483
  unified_preprocessing_prompt = ChatPromptTemplate.from_template(
484
  prompt_templete.UNIFIED_PREPROCESSING_PROMPT
485
  )
 
 
486
  qa_rag_prompt = ChatPromptTemplate.from_template(
487
  prompt_templete.QA_PROMPT_TEMPLATE
488
  )
 
 
489
  general_response_prompt = ChatPromptTemplate.from_template(
490
+ prompt_templete.GENERAL_RESPONSE_PROMPT
491
  )
492
 
493
+ # ----- 2. ĐỊNH NGHĨA CÁC NHÁNH XỬ LÝ -----
494
 
495
+ # --- Nhánh A: LEGAL RAG (Nhận dict đầy đủ) ---
496
  legal_rag_chain = (
497
  RunnablePassthrough.assign(
498
  context=itemgetter("rewritten_question") | retriever
499
  ).assign(
500
+ # Lấy câu trả lời đã được dọn dẹp
501
  answer=(
502
+ qa_rag_prompt
 
503
  | llm
504
  | StrOutputParser()
505
  | RunnableLambda(_extract_final_answer)
506
  )
507
  )
508
+ # Chỉ trả về 2 key quan trọng nhất
509
  | (lambda x: {"answer": x["answer"], "context": x["context"]})
510
+ ).with_config({"run_name": "LegalRAGChain"})
 
511
 
512
+ # --- Nhánh B: GENERAL RESPONSE (Nhận dict đầy đủ) ---
513
  general_response_chain = (
514
  general_response_prompt
515
  | llm
516
  | StrOutputParser()
 
517
  | (lambda answer_str: {"answer": answer_str, "context": []})
518
  ).with_config({"run_name": "GeneralResponseChain"})
519
 
 
521
  def route(info: dict):
522
  classification = info.get("classification")
523
  logger.info(f"➡️ Định tuyến truy vấn với phân loại: '{classification}'")
 
 
524
  if classification == "legal_rag":
525
  return legal_rag_chain
526
  else:
 
528
 
529
  # ----- 4. KẾT HỢP THÀNH FULL CHAIN -----
530
 
531
+ # <--- THAY ĐỔI QUAN TRỌNG BẮT ĐẦU TỪ ĐÂY --->
 
 
 
 
 
 
532
 
533
+ # 4.1. Chuỗi con để thực hiện tiền xử trả về JSON
534
+ preprocessing_logic = (
535
+ unified_preprocessing_prompt
536
+ | preprocessing_llm
537
+ | StrOutputParser()
538
+ | RunnableLambda(_extract_and_parse_json)
539
+ )
540
 
541
+ # 4.2. Xây dựng chuỗi chính để BẢO TOÀN HỢP NHẤT dữ liệu
542
+ full_chain = (
543
+ # Bắt đầu với một Passthrough để giữ lại dữ liệu gốc (input, chat_history)
544
+ RunnablePassthrough.assign(
545
+ # Chạy chuỗi tiền xử lý và gán kết quả của nó vào một key mới là `processed`
546
+ processed=preprocessing_logic
547
+ )
548
 
549
+ | RunnableLambda(
550
+ # Hàm này sẽ "làm phẳng" dict trên thành một dict duy nhất
551
+ # để các nhánh sau có thể truy cập tất cả các key
552
+ lambda x: {
553
+ "input": x["input"],
554
+ "chat_history": x.get("chat_history", []),
555
+ "classification": x["processed"]["classification"],
556
+ "rewritten_question": x["processed"]["rewritten_question"]
557
+ }
558
+ )
559
+ # 4.3. Chạy bộ định tuyến với dict đã được làm phẳng
560
+ | RunnableLambda(
561
+ # `info_dict` bây giờ chứa tất cả các key cần thiết
562
+ lambda info_dict: route(info_dict).invoke(info_dict)
563
+ )
564
+ )
565
 
566
+ # <--- THAY ĐỔI QUAN TRỌNG KẾT THÚC TẠI ĐÂY --->
567
 
568
+ logger.info("✅ Tạo thành công QA Chain phiên bản TỐI ƯU NHẤT.")
569
  return full_chain
570
 
571
  except Exception as e: