Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
|
@@ -7,6 +7,10 @@ from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
|
| 7 |
import re
|
| 8 |
import os
|
| 9 |
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
GOOGLE_API_KEY = "AIzaSyASwqVh3ELFVKH-W3WuHtmjg3XgtwjJQKg"
|
| 12 |
SEARCH_ENGINE_ID = "f34f8a4816771488b"
|
|
@@ -15,6 +19,11 @@ MODEL_PATH = "./vietnamese_fake_news_model"
|
|
| 15 |
|
| 16 |
genai.configure(api_key=GEMINI_API_KEY)
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
print("Loading the DistilBERT model we trained...")
|
| 19 |
try:
|
| 20 |
if os.path.exists(MODEL_PATH):
|
|
@@ -44,6 +53,166 @@ except Exception as e:
|
|
| 44 |
tokenizer = None
|
| 45 |
model = None
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
CREDIBLE_SOURCES = {
|
| 48 |
'vnexpress.net': 0.95,
|
| 49 |
'tuoitre.vn': 0.95,
|
|
@@ -219,7 +388,7 @@ def google_search(news_text):
|
|
| 219 |
result = service.cse().list(
|
| 220 |
q=search_query,
|
| 221 |
cx=SEARCH_ENGINE_ID,
|
| 222 |
-
num=10
|
| 223 |
).execute()
|
| 224 |
|
| 225 |
print(f"API Response keys: {list(result.keys())}")
|
|
@@ -351,6 +520,13 @@ def analyze_source_support(news_text, search_results):
|
|
| 351 |
def analyze_with_gemini(news_text, search_results, distilbert_prediction, distilbert_confidence):
|
| 352 |
"""Use Gemini AI to analyze the news and compare with our model results"""
|
| 353 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
# Try to use the latest Gemini model available
|
| 355 |
try:
|
| 356 |
model = genai.GenerativeModel('gemini-2.0-flash-exp')
|
|
@@ -363,11 +539,11 @@ def analyze_with_gemini(news_text, search_results, distilbert_prediction, distil
|
|
| 363 |
except:
|
| 364 |
model = genai.GenerativeModel('gemini-1.5-flash')
|
| 365 |
|
| 366 |
-
# Format the search results for Gemini
|
| 367 |
search_summary = ""
|
| 368 |
if search_results:
|
| 369 |
search_summary = "Kết quả tìm kiếm Google:\n"
|
| 370 |
-
for i, result in enumerate(search_results[:
|
| 371 |
search_summary += f"{i}. {result['title']}\n {result['snippet']}\n Nguồn: {result['link']}\n\n"
|
| 372 |
else:
|
| 373 |
search_summary = "Không tìm thấy kết quả tìm kiếm Google cho tin tức này. Điều này có thể do API bị giới hạn hoặc tin tức quá mới/chưa được đăng tải."
|
|
@@ -381,35 +557,37 @@ Bạn là một chuyên gia phân tích tin tức chuyên nghiệp. Hãy phân t
|
|
| 381 |
|
| 382 |
{search_summary}
|
| 383 |
|
|
|
|
|
|
|
| 384 |
Hãy thực hiện phân tích toàn diện theo các tiêu chí sau:
|
| 385 |
|
| 386 |
-
1.
|
| 387 |
-
2.
|
| 388 |
-
3.
|
| 389 |
-
4.
|
| 390 |
-
5.
|
| 391 |
|
| 392 |
Trả lời theo định dạng sau (chỉ bằng tiếng Việt, viết chi tiết và chuyên nghiệp):
|
| 393 |
|
| 394 |
-
|
| 395 |
|
| 396 |
-
|
| 397 |
|
| 398 |
-
|
| 399 |
-
-
|
| 400 |
-
-
|
| 401 |
-
-
|
| 402 |
-
-
|
| 403 |
-
-
|
| 404 |
|
| 405 |
-
|
| 406 |
|
| 407 |
-
|
| 408 |
- [Hướng dẫn cụ thể để kiểm chứng thông tin]
|
| 409 |
- [Các nguồn tin đáng tin cậy để tham khảo]
|
| 410 |
- [Cách phân biệt tin thật và tin giả]
|
| 411 |
|
| 412 |
-
QUAN TRỌNG: Trong phần "ĐỘ TIN CẬY", hãy cung cấp tỷ lệ phần trăm chính xác dựa trên phân tích của bạn. Ví dụ: "95
|
| 413 |
|
| 414 |
Viết chi tiết, chuyên nghiệp và hữu ích cho người đọc.
|
| 415 |
"""
|
|
@@ -420,12 +598,12 @@ Viết chi tiết, chuyên nghiệp và hữu ích cho người đọc.
|
|
| 420 |
if search_results:
|
| 421 |
print(f"DEBUG - First search result title: {search_results[0].get('title', 'No title')}")
|
| 422 |
|
| 423 |
-
# Use settings optimized for
|
| 424 |
generation_config = genai.types.GenerationConfig(
|
| 425 |
-
temperature=0.3, #
|
| 426 |
-
top_p=0.
|
| 427 |
-
top_k=
|
| 428 |
-
max_output_tokens=
|
| 429 |
)
|
| 430 |
response = model.generate_content(prompt, generation_config=generation_config)
|
| 431 |
print("Gemini API response received successfully")
|
|
@@ -473,25 +651,25 @@ Viết chi tiết, chuyên nghiệp và hữu ích cho người đọc.
|
|
| 473 |
if len(news_text) < 100:
|
| 474 |
warning_signs.append("Tin tức quá ngắn, thiếu thông tin chi tiết")
|
| 475 |
|
| 476 |
-
fallback_analysis = f"""
|
| 477 |
|
| 478 |
-
|
| 479 |
|
| 480 |
-
|
| 481 |
-
-
|
| 482 |
-
-
|
| 483 |
-
-
|
| 484 |
-
-
|
| 485 |
-
-
|
| 486 |
|
| 487 |
-
|
| 488 |
{chr(10).join([f"- {sign}" for sign in warning_signs]) if warning_signs else "- Không phát hiện dấu hiệu cảnh báo rõ ràng"}
|
| 489 |
|
| 490 |
-
|
| 491 |
-
-
|
| 492 |
-
-
|
| 493 |
-
-
|
| 494 |
-
-
|
| 495 |
return fallback_analysis
|
| 496 |
|
| 497 |
# For other errors, see what models are available
|
|
@@ -513,8 +691,8 @@ def extract_gemini_percentage(gemini_analysis):
|
|
| 513 |
# Look for the confidence percentage pattern
|
| 514 |
import re
|
| 515 |
|
| 516 |
-
# Pattern to match "X
|
| 517 |
-
percentage_pattern = r'độ tin cậy.*?(\d+)
|
| 518 |
match = re.search(percentage_pattern, gemini_lower)
|
| 519 |
|
| 520 |
if match:
|
|
@@ -647,10 +825,10 @@ def calculate_combined_confidence(distilbert_prediction, distilbert_confidence,
|
|
| 647 |
else:
|
| 648 |
# Fallback to conclusion analysis
|
| 649 |
conclusion_score = 0.5 # Default neutral
|
| 650 |
-
if "
|
| 651 |
conclusion_score = 0.1 # Very low for FAKE
|
| 652 |
print("Gemini Conclusion: FAKE")
|
| 653 |
-
elif "
|
| 654 |
conclusion_score = 0.9 # Very high for REAL
|
| 655 |
print("Gemini Conclusion: REAL")
|
| 656 |
elif "giả" in gemini_lower and "kết luận" in gemini_lower:
|
|
@@ -814,6 +992,26 @@ def analyze_news(news_text):
|
|
| 814 |
# Step 6: Format the final results
|
| 815 |
real_confidence = combined_confidence
|
| 816 |
fake_confidence = 1 - combined_confidence
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 817 |
|
| 818 |
# Build the detailed report with better formatting
|
| 819 |
# Use combined_confidence to determine the final classification (not just DistilBERT)
|
|
@@ -953,6 +1151,11 @@ def create_interface():
|
|
| 953 |
<div style="background: #f8f9fa; padding: 15px; border-radius: 10px; border-left: 4px solid #17a2b8; margin: 20px 0;">
|
| 954 |
<p style="margin: 0; color: #495057;"><strong>💡 Lưu ý:</strong> Kết quả có thể thay đổi nhẹ giữa các lần phân tích do tính chất AI của Gemini, nhưng độ chính xác tổng thể vẫn được đảm bảo.</p>
|
| 955 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 956 |
</div>
|
| 957 |
""")
|
| 958 |
|
|
|
|
| 7 |
import re
|
| 8 |
import os
|
| 9 |
import numpy as np
|
| 10 |
+
import json
|
| 11 |
+
import sqlite3
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
import hashlib
|
| 14 |
|
| 15 |
GOOGLE_API_KEY = "AIzaSyASwqVh3ELFVKH-W3WuHtmjg3XgtwjJQKg"
|
| 16 |
SEARCH_ENGINE_ID = "f34f8a4816771488b"
|
|
|
|
| 19 |
|
| 20 |
genai.configure(api_key=GEMINI_API_KEY)
|
| 21 |
|
| 22 |
+
# Knowledge Base Configuration
|
| 23 |
+
KNOWLEDGE_BASE_DB = "knowledge_base.db"
|
| 24 |
+
CONFIDENCE_THRESHOLD = 0.95 # 95% threshold for auto-updating knowledge base
|
| 25 |
+
ENABLE_KNOWLEDGE_BASE_SEARCH = False # Set to True to enable knowledge base search (slower)
|
| 26 |
+
|
| 27 |
print("Loading the DistilBERT model we trained...")
|
| 28 |
try:
|
| 29 |
if os.path.exists(MODEL_PATH):
|
|
|
|
| 53 |
tokenizer = None
|
| 54 |
model = None
|
| 55 |
|
| 56 |
+
# --- KNOWLEDGE BASE MANAGEMENT ---
|
| 57 |
+
def init_knowledge_base():
|
| 58 |
+
"""Initialize the SQLite knowledge base"""
|
| 59 |
+
conn = sqlite3.connect(KNOWLEDGE_BASE_DB)
|
| 60 |
+
cursor = conn.cursor()
|
| 61 |
+
|
| 62 |
+
cursor.execute('''
|
| 63 |
+
CREATE TABLE IF NOT EXISTS knowledge_entries (
|
| 64 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 65 |
+
content_hash TEXT UNIQUE,
|
| 66 |
+
news_text TEXT,
|
| 67 |
+
prediction TEXT,
|
| 68 |
+
confidence REAL,
|
| 69 |
+
search_results TEXT,
|
| 70 |
+
gemini_analysis TEXT,
|
| 71 |
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 72 |
+
last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 73 |
+
access_count INTEGER DEFAULT 1
|
| 74 |
+
)
|
| 75 |
+
''')
|
| 76 |
+
|
| 77 |
+
conn.commit()
|
| 78 |
+
conn.close()
|
| 79 |
+
print("Knowledge base initialized successfully!")
|
| 80 |
+
|
| 81 |
+
def add_to_knowledge_base(news_text, prediction, confidence, search_results, gemini_analysis):
|
| 82 |
+
"""Add high-confidence result to knowledge base"""
|
| 83 |
+
try:
|
| 84 |
+
# Create content hash for deduplication
|
| 85 |
+
content_hash = hashlib.md5(news_text.encode('utf-8')).hexdigest()
|
| 86 |
+
|
| 87 |
+
conn = sqlite3.connect(KNOWLEDGE_BASE_DB)
|
| 88 |
+
cursor = conn.cursor()
|
| 89 |
+
|
| 90 |
+
# Check if entry already exists
|
| 91 |
+
cursor.execute('SELECT id FROM knowledge_entries WHERE content_hash = ?', (content_hash,))
|
| 92 |
+
if cursor.fetchone():
|
| 93 |
+
print(f"Entry already exists in knowledge base (hash: {content_hash[:8]}...)")
|
| 94 |
+
conn.close()
|
| 95 |
+
return False
|
| 96 |
+
|
| 97 |
+
# Insert new entry
|
| 98 |
+
cursor.execute('''
|
| 99 |
+
INSERT INTO knowledge_entries
|
| 100 |
+
(content_hash, news_text, prediction, confidence, search_results, gemini_analysis)
|
| 101 |
+
VALUES (?, ?, ?, ?, ?, ?)
|
| 102 |
+
''', (
|
| 103 |
+
content_hash,
|
| 104 |
+
news_text,
|
| 105 |
+
prediction,
|
| 106 |
+
confidence,
|
| 107 |
+
json.dumps(search_results, ensure_ascii=False),
|
| 108 |
+
gemini_analysis
|
| 109 |
+
))
|
| 110 |
+
|
| 111 |
+
conn.commit()
|
| 112 |
+
conn.close()
|
| 113 |
+
|
| 114 |
+
print(f"✅ Added high-confidence result to knowledge base (confidence: {confidence:.1%})")
|
| 115 |
+
print(f" Hash: {content_hash[:8]}...")
|
| 116 |
+
print(f" Prediction: {prediction}")
|
| 117 |
+
return True
|
| 118 |
+
|
| 119 |
+
except Exception as e:
|
| 120 |
+
print(f"Error adding to knowledge base: {e}")
|
| 121 |
+
return False
|
| 122 |
+
|
| 123 |
+
def search_knowledge_base(query_text, limit=5):
|
| 124 |
+
"""Search the knowledge base for similar entries"""
|
| 125 |
+
try:
|
| 126 |
+
conn = sqlite3.connect(KNOWLEDGE_BASE_DB)
|
| 127 |
+
cursor = conn.cursor()
|
| 128 |
+
|
| 129 |
+
# Simple text similarity search (you can enhance this with embeddings later)
|
| 130 |
+
cursor.execute('''
|
| 131 |
+
SELECT news_text, prediction, confidence, search_results, gemini_analysis,
|
| 132 |
+
created_at, access_count
|
| 133 |
+
FROM knowledge_entries
|
| 134 |
+
WHERE news_text LIKE ? OR gemini_analysis LIKE ?
|
| 135 |
+
ORDER BY confidence DESC, access_count DESC
|
| 136 |
+
LIMIT ?
|
| 137 |
+
''', (f'%{query_text[:50]}%', f'%{query_text[:50]}%', limit))
|
| 138 |
+
|
| 139 |
+
results = cursor.fetchall()
|
| 140 |
+
|
| 141 |
+
# Update access count and last_accessed
|
| 142 |
+
for result in results:
|
| 143 |
+
cursor.execute('''
|
| 144 |
+
UPDATE knowledge_entries
|
| 145 |
+
SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP
|
| 146 |
+
WHERE news_text = ?
|
| 147 |
+
''', (result[0],))
|
| 148 |
+
|
| 149 |
+
conn.commit()
|
| 150 |
+
conn.close()
|
| 151 |
+
|
| 152 |
+
if results:
|
| 153 |
+
print(f"📚 Found {len(results)} similar entries in knowledge base")
|
| 154 |
+
return results
|
| 155 |
+
else:
|
| 156 |
+
return []
|
| 157 |
+
|
| 158 |
+
except Exception as e:
|
| 159 |
+
print(f"Error searching knowledge base: {e}")
|
| 160 |
+
return []
|
| 161 |
+
|
| 162 |
+
def format_knowledge_for_rag(knowledge_results):
|
| 163 |
+
"""Format knowledge base results for RAG augmentation"""
|
| 164 |
+
if not knowledge_results:
|
| 165 |
+
return ""
|
| 166 |
+
|
| 167 |
+
knowledge_summary = "\n=== KIẾN THỨC TƯƠNG TỰ TỪ CƠ SỞ DỮ LIỆU ===\n"
|
| 168 |
+
|
| 169 |
+
for i, (news_text, prediction, confidence, search_results, gemini_analysis, created_at, access_count) in enumerate(knowledge_results, 1):
|
| 170 |
+
knowledge_summary += f"\n{i}. Tin tức tương tự (Độ tin cậy: {confidence:.1%}, Lần truy cập: {access_count}):\n"
|
| 171 |
+
knowledge_summary += f" Nội dung: {news_text[:200]}...\n"
|
| 172 |
+
knowledge_summary += f" Kết luận: {prediction}\n"
|
| 173 |
+
knowledge_summary += f" Thời gian: {created_at}\n"
|
| 174 |
+
|
| 175 |
+
knowledge_summary += "\n==========================================\n"
|
| 176 |
+
return knowledge_summary
|
| 177 |
+
|
| 178 |
+
def get_knowledge_base_stats():
|
| 179 |
+
"""Get statistics about the knowledge base"""
|
| 180 |
+
try:
|
| 181 |
+
conn = sqlite3.connect(KNOWLEDGE_BASE_DB)
|
| 182 |
+
cursor = conn.cursor()
|
| 183 |
+
|
| 184 |
+
# Get total entries
|
| 185 |
+
cursor.execute('SELECT COUNT(*) FROM knowledge_entries')
|
| 186 |
+
total_entries = cursor.fetchone()[0]
|
| 187 |
+
|
| 188 |
+
# Get entries by prediction
|
| 189 |
+
cursor.execute('SELECT prediction, COUNT(*) FROM knowledge_entries GROUP BY prediction')
|
| 190 |
+
prediction_counts = dict(cursor.fetchall())
|
| 191 |
+
|
| 192 |
+
# Get average confidence
|
| 193 |
+
cursor.execute('SELECT AVG(confidence) FROM knowledge_entries')
|
| 194 |
+
avg_confidence = cursor.fetchone()[0] or 0
|
| 195 |
+
|
| 196 |
+
# Get most accessed entries
|
| 197 |
+
cursor.execute('SELECT news_text, access_count FROM knowledge_entries ORDER BY access_count DESC LIMIT 3')
|
| 198 |
+
top_accessed = cursor.fetchall()
|
| 199 |
+
|
| 200 |
+
conn.close()
|
| 201 |
+
|
| 202 |
+
return {
|
| 203 |
+
'total_entries': total_entries,
|
| 204 |
+
'prediction_counts': prediction_counts,
|
| 205 |
+
'avg_confidence': avg_confidence,
|
| 206 |
+
'top_accessed': top_accessed
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
except Exception as e:
|
| 210 |
+
print(f"Error getting knowledge base stats: {e}")
|
| 211 |
+
return None
|
| 212 |
+
|
| 213 |
+
# Initialize knowledge base on startup
|
| 214 |
+
init_knowledge_base()
|
| 215 |
+
|
| 216 |
CREDIBLE_SOURCES = {
|
| 217 |
'vnexpress.net': 0.95,
|
| 218 |
'tuoitre.vn': 0.95,
|
|
|
|
| 388 |
result = service.cse().list(
|
| 389 |
q=search_query,
|
| 390 |
cx=SEARCH_ENGINE_ID,
|
| 391 |
+
num=5 # Reduced from 10 to 5 for faster processing
|
| 392 |
).execute()
|
| 393 |
|
| 394 |
print(f"API Response keys: {list(result.keys())}")
|
|
|
|
| 520 |
def analyze_with_gemini(news_text, search_results, distilbert_prediction, distilbert_confidence):
|
| 521 |
"""Use Gemini AI to analyze the news and compare with our model results"""
|
| 522 |
try:
|
| 523 |
+
# Knowledge base search (optional for faster performance)
|
| 524 |
+
if ENABLE_KNOWLEDGE_BASE_SEARCH:
|
| 525 |
+
print("🔍 Searching knowledge base for similar entries...")
|
| 526 |
+
knowledge_results = search_knowledge_base(news_text, limit=3)
|
| 527 |
+
knowledge_context = format_knowledge_for_rag(knowledge_results)
|
| 528 |
+
else:
|
| 529 |
+
knowledge_context = ""
|
| 530 |
# Try to use the latest Gemini model available
|
| 531 |
try:
|
| 532 |
model = genai.GenerativeModel('gemini-2.0-flash-exp')
|
|
|
|
| 539 |
except:
|
| 540 |
model = genai.GenerativeModel('gemini-1.5-flash')
|
| 541 |
|
| 542 |
+
# Format the search results for Gemini (limit to top 3 for speed)
|
| 543 |
search_summary = ""
|
| 544 |
if search_results:
|
| 545 |
search_summary = "Kết quả tìm kiếm Google:\n"
|
| 546 |
+
for i, result in enumerate(search_results[:3], 1): # Reduced from 5 to 3
|
| 547 |
search_summary += f"{i}. {result['title']}\n {result['snippet']}\n Nguồn: {result['link']}\n\n"
|
| 548 |
else:
|
| 549 |
search_summary = "Không tìm thấy kết quả tìm kiếm Google cho tin tức này. Điều này có thể do API bị giới hạn hoặc tin tức quá mới/chưa được đăng tải."
|
|
|
|
| 557 |
|
| 558 |
{search_summary}
|
| 559 |
|
| 560 |
+
{knowledge_context}
|
| 561 |
+
|
| 562 |
Hãy thực hiện phân tích toàn diện theo các tiêu chí sau:
|
| 563 |
|
| 564 |
+
1. Phân tích nội dung: Kiểm tra tính logic, mâu thuẫn, ngôn ngữ cảm xúc thái quá
|
| 565 |
+
2. Phân tích nguồn tin: Đánh giá uy tín và độ tin cậy của nguồn
|
| 566 |
+
3. Phân tích ngữ cảnh: So sánh với thông tin có sẵn và kiến thức thực tế
|
| 567 |
+
4. Phân tích ngôn ngữ: Tìm dấu hiệu của tin giả như từ ngữ gây sốc, cảm xúc
|
| 568 |
+
5. Phân tích thời gian: Kiểm tra tính hợp lý về mặt thời gian
|
| 569 |
|
| 570 |
Trả lời theo định dạng sau (chỉ bằng tiếng Việt, viết chi tiết và chuyên nghiệp):
|
| 571 |
|
| 572 |
+
1. KẾT LUẬN: [THẬT/GIẢ/KHÔNG XÁC ĐỊNH]
|
| 573 |
|
| 574 |
+
2. ĐỘ TIN CẬY: [THẬT: X% / GIẢ: Y%] (Trong đó X% là độ tin cậy tin THẬT, Y% là độ tin cậy tin GIẢ, X+Y=100%)
|
| 575 |
|
| 576 |
+
3. PHÂN TÍCH CHI TIẾT:
|
| 577 |
+
- Nội dung: [Phân tích chi tiết về nội dung tin tức]
|
| 578 |
+
- Nguồn tin: [Đánh giá về nguồn và độ tin cậy]
|
| 579 |
+
- Ngữ cảnh: [So sánh với thông tin có sẵn]
|
| 580 |
+
- Ngôn ngữ: [Phân tích cách sử dụng từ ngữ]
|
| 581 |
+
- Thời gian: [Kiểm tra tính hợp lý về mặt thời gian]
|
| 582 |
|
| 583 |
+
4. CÁC DẤU HIỆU CẢNH BÁO: [Liệt kê các dấu hiệu đáng ngờ nếu có]
|
| 584 |
|
| 585 |
+
5. KHUYẾN NGHỊ CHO NGƯỜI ĐỌC:
|
| 586 |
- [Hướng dẫn cụ thể để kiểm chứng thông tin]
|
| 587 |
- [Các nguồn tin đáng tin cậy để tham khảo]
|
| 588 |
- [Cách phân biệt tin thật và tin giả]
|
| 589 |
|
| 590 |
+
QUAN TRỌNG: Trong phần "ĐỘ TIN CẬY", hãy cung cấp tỷ lệ phần trăm chính xác dựa trên phân tích của bạn. Ví dụ: "THẬT: 95% / GIẢ: 5%" nghĩa là 95% tin tức này là THẬT, 5% là GIẢ.
|
| 591 |
|
| 592 |
Viết chi tiết, chuyên nghiệp và hữu ích cho người đọc.
|
| 593 |
"""
|
|
|
|
| 598 |
if search_results:
|
| 599 |
print(f"DEBUG - First search result title: {search_results[0].get('title', 'No title')}")
|
| 600 |
|
| 601 |
+
# Use settings optimized for faster processing
|
| 602 |
generation_config = genai.types.GenerationConfig(
|
| 603 |
+
temperature=0.3, # Lower for more consistent results
|
| 604 |
+
top_p=0.8, # Reduced for faster processing
|
| 605 |
+
top_k=20, # Reduced for faster processing
|
| 606 |
+
max_output_tokens=1000 # Reduced for faster responses
|
| 607 |
)
|
| 608 |
response = model.generate_content(prompt, generation_config=generation_config)
|
| 609 |
print("Gemini API response received successfully")
|
|
|
|
| 651 |
if len(news_text) < 100:
|
| 652 |
warning_signs.append("Tin tức quá ngắn, thiếu thông tin chi tiết")
|
| 653 |
|
| 654 |
+
fallback_analysis = f"""1. KẾT LUẬN: {conclusion}
|
| 655 |
|
| 656 |
+
2. ĐỘ TIN CẬY: {'THẬT: 5% / GIẢ: 95%' if conclusion == 'GIẢ' else 'THẬT: 95% / GIẢ: 5%' if conclusion == 'THẬT' else 'THẬT: 50% / GIẢ: 50%'}
|
| 657 |
|
| 658 |
+
3. PHÂN TÍCH CHI TIẾT:
|
| 659 |
+
- Nội dung: {'Tin tức có vẻ hợp lý' if distilbert_prediction == 'REAL' else 'Tin tức có nhiều dấu hiệu đáng ngờ' if distilbert_prediction == 'FAKE' else 'Nội dung không rõ ràng'}
|
| 660 |
+
- Nguồn tin: Google Search kh��ng khả dụng (hết quota) - không thể kiểm tra nguồn
|
| 661 |
+
- Ngữ cảnh: Phân tích từ khóa: {confidence_boost}
|
| 662 |
+
- Ngôn ngữ: {'Ngôn ngữ trung tính' if fake_score == real_score else 'Có dấu hiệu cảm xúc thái quá' if fake_score > real_score else 'Ngôn ngữ khách quan'}
|
| 663 |
+
- Thời gian: Không thể xác minh do thiếu thông tin bổ sung
|
| 664 |
|
| 665 |
+
4. CÁC DẤU HIỆU CẢNH BÁO:
|
| 666 |
{chr(10).join([f"- {sign}" for sign in warning_signs]) if warning_signs else "- Không phát hiện dấu hiệu cảnh báo rõ ràng"}
|
| 667 |
|
| 668 |
+
5. KHUYẾN NGHỊ CHO NGƯỜI ĐỌC:
|
| 669 |
+
- Kiểm tra nguồn: Tìm kiếm thông tin tương tự trên các trang báo uy tín như VnExpress, Tuổi Trẻ, Thanh Niên
|
| 670 |
+
- Xác minh thời gian: Kiểm tra xem tin tức có được đăng tải đồng thời trên nhiều nguồn không
|
| 671 |
+
- Đánh giá ngôn ngữ: Tránh chia sẻ tin tức có ngôn ngữ cảm xúc thái quá hoặc tạo cảm giác cấp bách
|
| 672 |
+
- Lưu ý: Do hệ thống API tạm thời không khả dụng, kết quả phân tích có thể không hoàn toàn chính xác"""
|
| 673 |
return fallback_analysis
|
| 674 |
|
| 675 |
# For other errors, see what models are available
|
|
|
|
| 691 |
# Look for the confidence percentage pattern
|
| 692 |
import re
|
| 693 |
|
| 694 |
+
# Pattern to match "THẬT: X% / GIẢ: Y%" format
|
| 695 |
+
percentage_pattern = r'độ tin cậy.*?thật.*?(\d+)%.*?giả.*?(\d+)%'
|
| 696 |
match = re.search(percentage_pattern, gemini_lower)
|
| 697 |
|
| 698 |
if match:
|
|
|
|
| 825 |
else:
|
| 826 |
# Fallback to conclusion analysis
|
| 827 |
conclusion_score = 0.5 # Default neutral
|
| 828 |
+
if "kết luận: giả" in gemini_lower or "kết luận: fake" in gemini_lower:
|
| 829 |
conclusion_score = 0.1 # Very low for FAKE
|
| 830 |
print("Gemini Conclusion: FAKE")
|
| 831 |
+
elif "kết luận: thật" in gemini_lower or "kết luận: real" in gemini_lower:
|
| 832 |
conclusion_score = 0.9 # Very high for REAL
|
| 833 |
print("Gemini Conclusion: REAL")
|
| 834 |
elif "giả" in gemini_lower and "kết luận" in gemini_lower:
|
|
|
|
| 992 |
# Step 6: Format the final results
|
| 993 |
real_confidence = combined_confidence
|
| 994 |
fake_confidence = 1 - combined_confidence
|
| 995 |
+
|
| 996 |
+
# Step 7: Check if result should be added to knowledge base
|
| 997 |
+
max_confidence = max(real_confidence, fake_confidence)
|
| 998 |
+
if max_confidence > CONFIDENCE_THRESHOLD:
|
| 999 |
+
print(f"🚀 High confidence result detected ({max_confidence:.1%}) - adding to knowledge base...")
|
| 1000 |
+
final_prediction = "REAL" if real_confidence > fake_confidence else "FAKE"
|
| 1001 |
+
|
| 1002 |
+
# Add to knowledge base
|
| 1003 |
+
success = add_to_knowledge_base(
|
| 1004 |
+
news_text=news_text,
|
| 1005 |
+
prediction=final_prediction,
|
| 1006 |
+
confidence=max_confidence,
|
| 1007 |
+
search_results=search_results,
|
| 1008 |
+
gemini_analysis=gemini_analysis
|
| 1009 |
+
)
|
| 1010 |
+
|
| 1011 |
+
if success:
|
| 1012 |
+
print("✅ Successfully added to knowledge base for future RAG retrieval!")
|
| 1013 |
+
else:
|
| 1014 |
+
print("⚠️ Failed to add to knowledge base (duplicate or error)")
|
| 1015 |
|
| 1016 |
# Build the detailed report with better formatting
|
| 1017 |
# Use combined_confidence to determine the final classification (not just DistilBERT)
|
|
|
|
| 1151 |
<div style="background: #f8f9fa; padding: 15px; border-radius: 10px; border-left: 4px solid #17a2b8; margin: 20px 0;">
|
| 1152 |
<p style="margin: 0; color: #495057;"><strong>💡 Lưu ý:</strong> Kết quả có thể thay đổi nhẹ giữa các lần phân tích do tính chất AI của Gemini, nhưng độ chính xác tổng thể vẫn được đảm bảo.</p>
|
| 1153 |
</div>
|
| 1154 |
+
|
| 1155 |
+
<div style="background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); padding: 15px; border-radius: 10px; margin: 20px 0;">
|
| 1156 |
+
<h4 style="margin: 0 0 10px 0; color: #333;">🧠 Hệ thống RAG với Cơ sở Tri thức Tự động</h4>
|
| 1157 |
+
<p style="margin: 0; color: #555; font-size: 14px;">Khi độ tin cậy > 95%, hệ thống sẽ tự động lưu kết quả vào cơ sở tri thức để sử dụng cho các phân tích tương lai.</p>
|
| 1158 |
+
</div>
|
| 1159 |
</div>
|
| 1160 |
""")
|
| 1161 |
|