File size: 10,015 Bytes
bb769c0
4b84e82
bb769c0
 
 
4b84e82
bb769c0
4b84e82
 
 
 
 
bb769c0
4b84e82
 
 
 
 
 
 
 
 
 
 
 
bb769c0
4b84e82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb769c0
 
4b84e82
 
 
 
bb769c0
 
 
4b84e82
bb769c0
4b84e82
 
bb769c0
 
4b84e82
bb769c0
 
4b84e82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb769c0
4b84e82
 
 
 
 
 
 
 
 
 
bb769c0
 
 
 
4b84e82
 
 
 
 
 
 
 
bb769c0
 
4b84e82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb769c0
 
4b84e82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import torch
from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import gradio as gr
import re

# 📌 مدل‌های مختلف
# مدل BERT برای semantic search
bert_model_name = "HooshvareLab/bert-fa-base-uncased"
bert_tokenizer = AutoTokenizer.from_pretrained(bert_model_name)
bert_model = AutoModel.from_pretrained(bert_model_name)

# مدل generative برای تولید پاسخ (می‌توانید از مدل‌های دیگر استفاده کنید)
# اگر مدل generative فارسی در دسترس نداشته باشید، از همین BERT استفاده می‌کنیم
try:
    # مثال: استفاده از مدل generative فارسی
    gen_model_name = "HooshvareLab/bert-fa-base-uncased"  # جایگزین کنید
    gen_tokenizer = AutoTokenizer.from_pretrained(gen_model_name)
    gen_model = AutoModel.from_pretrained(gen_model_name)
    has_generative = False  # فعلاً False چون BERT generative نیست
except:
    has_generative = False

# 📄 دیتاست گسترده‌تر
faq_data = {
    "پایتخت ایران کجاست؟": "تهران پایتخت ایران است.",
    "زبان رسمی ایران چیست؟": "فارسی زبان رسمی ایران است.",
    "واحد پول ایران چیست؟": "ریال واحد پول ایران است.",
    "چه زمانی انتخاب واحد شروع می‌شود؟": "معمولاً انتخاب واحد در پایان شهریور یا بهمن ماه شروع می‌شود.",
    "چه معدلی برای گرفتن 24 واحد لازم است؟": "برای گرفتن 24 واحد حداقل معدل 17 لازم است.",
    "ساعت کاری ادارات چیست؟": "ساعت کاری ادارات معمولاً از 8 صبح تا 4 عصر است.",
    "چگونه می‌توانم درخواست پاسپورت بدهم؟": "برای درخواست پاسپورت باید به اداره گذرنامه مراجعه کنید.",
    "فصل‌های سال در ایران چیست؟": "فصل‌های سال شامل بهار، تابستان، پاییز و زمستان است.",
}

# اضافه کردن دانش عمومی
general_knowledge = {
    "چگونه": "این سوال درباره نحوه انجام کاری است. برای پاسخ دقیق‌تر، لطفاً سوال خود را کامل‌تر بپرسید.",
    "چرا": "این سوال درباره دلیل چیزی است. برای پاسخ بهتر، موضوع مشخصی را بیان کنید.",
    "چیست": "این سوال تعریف چیزی را می‌خواهد. لطفاً موضوع مورد نظر را دقیق‌تر بیان کنید.",
    "کجا": "این سوال درباره مکان است. برای پاسخ دقیق‌تر، موضوع خاصی را مشخص کنید.",
    "کی": "این سوال درباره زمان است. لطفاً موضوع مورد نظر را دقیق‌تر بیان کنید.",
}

# ترکیب دیتاست‌ها
all_data = {**faq_data, **general_knowledge}
questions = list(all_data.keys())
answers = list(all_data.values())

# 📄 تولید embedding
def get_embedding(text):
    inputs = bert_tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=128)
    with torch.no_grad():
        outputs = bert_model(**inputs)
        emb = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy()
    return emb

# محاسبه embedding برای همه سوالات
faq_embeddings = [get_embedding(q) for q in questions]

# 📄 تشخیص نوع سوال
def detect_question_type(question):
    question_lower = question.lower()
    
    # الگوهای سوال
    patterns = {
        'definition': ['چیست', 'چی هست', 'تعریف', 'معنی'],
        'how': ['چگونه', 'چطور', 'چه طور'],
        'why': ['چرا', 'به چه دلیل'],
        'when': ['چه زمانی', 'کی', 'چه وقت'],
        'where': ['کجا', 'در کجا', 'کدام مکان'],
        'who': ['کی', 'چه کسی', 'کدام فرد'],
        'greeting': ['سلام', 'درود', 'صبح بخیر', 'ظهر بخیر', 'عصر بخیر', 'شب بخیر'],
        'thanks': ['ممنون', 'متشکرم', 'سپاس', 'تشکر'],
        'yes_no': ['آیا', 'مگر', 'آیا که']
    }
    
    for q_type, words in patterns.items():
        for word in words:
            if word in question_lower:
                return q_type
    
    return 'general'

# 📄 تولید پاسخ برای سوالات عمومی
def generate_general_answer(question, question_type):
    if question_type == 'greeting':
        return "سلام! چطور می‌تونم کمکتون کنم؟"
    
    elif question_type == 'thanks':
        return "خواهش می‌کنم! اگر سوال دیگری دارید، بپرسید."
    
    elif question_type == 'definition':
        return "برای تعریف دقیق‌تر این موضوع، لطفاً سوال خود را کامل‌تر بپرسید تا بتوانم پاسخ مناسبی ارائه دهم."
    
    elif question_type == 'how':
        return "برای راهنمایی دقیق‌تر درباره نحوه انجام این کار، لطفاً جزئیات بیشتری از سوال خود ارائه دهید."
    
    elif question_type == 'why':
        return "برای توضیح دلایل، لطفاً موضوع مورد نظر را دقیق‌تر مشخص کنید تا بتوانم پاسخ مناسبی ارائه دهم."
    
    elif question_type == 'when':
        return "برای اطلاع از زمان دقیق، لطفاً موضوع خاصی را مشخص کنید تا بتوانم راهنمایی کنم."
    
    elif question_type == 'where':
        return "برای اطلاع از مکان، لطفاً موضوع مورد نظر را دقیق‌تر بیان کنید."
    
    elif question_type == 'yes_no':
        return "برای پاسخ به این سوال، لطفاً موضوع را کامل‌تر مطرح کنید."
    
    else:
        # پاسخ عمومی هوشمند
        return "سوال جالبی پرسیده‌اید. متأسفانه در حال حاضر اطلاعات کاملی در این زمینه ندارم، اما اگر سوال خود را دقیق‌تر مطرح کنید، شاید بتوانم کمک بیشتری کنم."

# 📄 تابع اصلی پاسخ
def answer_question(user_question):
    # حذف فاصله‌های اضافی
    user_question = user_question.strip()
    
    if not user_question:
        return "لطفاً سوال خود را بنویسید."
    
    # تشخیص نوع سوال
    question_type = detect_question_type(user_question)
    
    # جستجوی semantic در دیتاست
    user_emb = get_embedding(user_question)
    sims = [cosine_similarity([user_emb], [emb])[0][0] for emb in faq_embeddings]
    best_idx = int(np.argmax(sims))
    best_score = sims[best_idx]
    
    # تنظیم threshold بر اساس نوع سوال
    if question_type in ['greeting', 'thanks']:
        threshold = 0.3
    else:
        threshold = 0.65
    
    if best_score > threshold:
        return answers[best_idx]
    else:
        # تولید پاسخ برای سوالات خارج از دیتاست
        return generate_general_answer(user_question, question_type)

# 📄 تابع اضافه کردن دانش جدید
def add_knowledge(question, answer):
    if question and answer:
        global questions, answers, faq_embeddings, all_data
        
        # اضافه کردن به دیتاست
        all_data[question] = answer
        questions.append(question)
        answers.append(answer)
        
        # محاسبه embedding جدید
        new_emb = get_embedding(question)
        faq_embeddings.append(new_emb)
        
        return f"دانش جدید اضافه شد: {question} -> {answer}"
    else:
        return "لطفاً هم سوال و هم پاسخ را وارد کنید."

# 📄 رابط Gradio
with gr.Blocks(title="🤖 دستیار فارسی هوشمند") as demo:
    gr.Markdown("## 🤖 دستیار فارسی هوشمند (پاسخ به سوالات داخل و خارج دیتاست)")
    
    with gr.Tab("💬 پرسش و پاسخ"):
        inp = gr.Textbox(label="سؤال خود را بنویسید", placeholder="مثال: سلام، پایتخت ایران کجاست؟")
        out = gr.Textbox(label="پاسخ", lines=3)
        btn = gr.Button("پاسخ بده", variant="primary")
        btn.click(fn=answer_question, inputs=inp, outputs=out)
    
    with gr.Tab("📚 افزودن دانش"):
        gr.Markdown("### افزودن سوال و پاسخ جدید به دیتاست")
        new_q = gr.Textbox(label="سوال جدید")
        new_a = gr.Textbox(label="پاسخ جدید", lines=2)
        add_btn = gr.Button("اضافه کن", variant="secondary")
        add_result = gr.Textbox(label="نتیجه")
        add_btn.click(fn=add_knowledge, inputs=[new_q, new_a], outputs=add_result)
    
    with gr.Tab("ℹ️ راهنما"):
        gr.Markdown("""
        ### نحوه استفاده:
        1. **سوالات معمولی**: مثل "پایتخت ایران کجاست؟"
        2. **احوال‌پرسی**: مثل "سلام" یا "صبح بخیر"
        3. **تشکر**: مثل "ممنون" یا "متشکرم"
        4. **سوالات عمومی**: حتی اگر در دیتاست نباشد، پاسخ مناسب می‌دهد
        
        ### ویژگی‌ها:
        - پاسخ به سوالات داخل دیتاست با دقت بالا
        - پاسخ هوشمند به سوالات خارج دیتاست
        - تشخیص نوع سوال (تعریف، چگونه، چرا، کجا، کی)
        - قابلیت افزودن دانش جدید
        """)

demo.launch()