File size: 3,402 Bytes
c90b40e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from sentence_transformers import CrossEncoder, SentenceTransformer
from transformers import AutoTokenizer, AutoModelForCausalLM
import faiss
import numpy as np
from typing import List, Dict

class ArabicRAGSystem:
    def __init__(self):
        # Initialize models
        self.embedding_model = SentenceTransformer("aubmindlab/bert-base-arabertv2")
        self.cross_encoder = CrossEncoder("Arabic-Misc/roberta-base-arabic-camelbert-da-msa")
        self.tokenizer = AutoTokenizer.from_pretrained("inception-mbzuai/jais-13b-chat")
        self.llm = AutoModelForCausalLM.from_pretrained("inception-mbzuai/jais-13b-chat")
        self.index = faiss.IndexFlatL2(768)

    def _create_index(self, documents: List[Dict]):
        texts = [doc["text"] for doc in documents]
        embeddings = self.embedding_model.encode(texts)
        self.index.add(np.array(embeddings))

    def generate_answer(self, question: str, documents: List[Dict], 
                      top_k: int = 5, temperature: float = 0.7) -> tuple:
        # Indexing phase
        self._create_index(documents)
        
        # Two-stage retrieval
        query_embedding = self.embedding_model.encode([question])
        distances, indices = self.index.search(query_embedding, top_k*2)
        
        # Re-ranking with cross-encoder
        pairs = [[question, documents[idx]["text"]] for idx in indices[0]]
        scores = self.cross_encoder.predict(pairs)
        ranked_indices = np.argsort(scores)[::-1][:top_k]
        
        # Prepare context
        context = "\n\n".join([
            f"المصدر: {documents[idx]['source']}\n"
            f"الصفحة: {documents[idx]['page']}\n"
            f"النص: {documents[idx]['text']}"
            for idx in [indices[0][i] for i in ranked_indices]
        ])
        
        # Generate answer
        prompt = f"""
        أنت خبير في التحليل الديني. قم بالإجابة على السؤال التالي بناءً على السياق المقدم فقط:
        
        السياق:
        {context}
        
        السؤال: 
        {question}
        
        التعليمات:
        - أجب باللغة العربية الفصحى
        - استخدم علامات التنسيق المناسبة
        - أشر إلى المصادر باستخدام التنسيق [المصدر: اسم الملف، الصفحة: رقم]
        - إذا لم توجد إجابة واضحة، قل "لا تتوفر معلومات كافية"
        
        الإجابة:
        """.strip()
        
        inputs = self.tokenizer(prompt, return_tensors="pt")
        outputs = self.llm.generate(
            inputs.input_ids,
            max_new_tokens=512,
            temperature=temperature,
            do_sample=True,
            pad_token_id=self.tokenizer.eos_token_id
        )
        
        answer = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
        answer = answer.split("الإجابة:")[-1].strip()
        
        # Prepare sources
        sources = []
        for idx in [indices[0][i] for i in ranked_indices]:
            sources.append({
                "text": documents[idx]["text"],
                "source": documents[idx]["source"],
                "page": documents[idx]["page"],
                "score": float(scores[idx])
            })
        
        return answer, sources