Update app.py
Browse files
app.py
CHANGED
@@ -1,54 +1,196 @@
|
|
1 |
import torch
|
2 |
-
from transformers import AutoTokenizer, AutoModel
|
3 |
import numpy as np
|
4 |
from sklearn.metrics.pairwise import cosine_similarity
|
5 |
import gradio as gr
|
|
|
6 |
|
7 |
-
# 📌
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
11 |
|
12 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
faq_data = {
|
14 |
-
"پایتخت ایران کجاست؟": "تهران",
|
15 |
-
"زبان رسمی ایران چیست؟": "فارسی",
|
16 |
-
"واحد پول ایران چیست؟": "ریال",
|
17 |
-
"چه زمانی انتخاب واحد شروع میشود؟": "معمولاً پایان شهریور یا
|
18 |
-
"چه معدلی برای گرفتن 24 واحد لازم است؟": "حداقل معدل 17
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
}
|
20 |
|
21 |
-
|
22 |
-
|
|
|
|
|
23 |
|
24 |
# 📄 تولید embedding
|
25 |
def get_embedding(text):
|
26 |
-
inputs =
|
27 |
with torch.no_grad():
|
28 |
-
outputs =
|
29 |
-
|
30 |
return emb
|
31 |
|
|
|
32 |
faq_embeddings = [get_embedding(q) for q in questions]
|
33 |
|
34 |
-
# 📄
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
def answer_question(user_question):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
user_emb = get_embedding(user_question)
|
37 |
sims = [cosine_similarity([user_emb], [emb])[0][0] for emb in faq_embeddings]
|
38 |
best_idx = int(np.argmax(sims))
|
39 |
best_score = sims[best_idx]
|
40 |
-
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
return answers[best_idx]
|
43 |
else:
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
# 📄 رابط Gradio
|
47 |
-
with gr.Blocks() as demo:
|
48 |
-
gr.Markdown("## 🤖 دستیار فارسی (
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import torch
|
2 |
+
from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM
|
3 |
import numpy as np
|
4 |
from sklearn.metrics.pairwise import cosine_similarity
|
5 |
import gradio as gr
|
6 |
+
import re
|
7 |
|
8 |
+
# 📌 مدلهای مختلف
|
9 |
+
# مدل BERT برای semantic search
|
10 |
+
bert_model_name = "HooshvareLab/bert-fa-base-uncased"
|
11 |
+
bert_tokenizer = AutoTokenizer.from_pretrained(bert_model_name)
|
12 |
+
bert_model = AutoModel.from_pretrained(bert_model_name)
|
13 |
|
14 |
+
# مدل generative برای تولید پاسخ (میتوانید از مدلهای دیگر استفاده کنید)
|
15 |
+
# اگر مدل generative فارسی در دسترس نداشته باشید، از همین BERT استفاده میکنیم
|
16 |
+
try:
|
17 |
+
# مثال: استفاده از مدل generative فارسی
|
18 |
+
gen_model_name = "HooshvareLab/bert-fa-base-uncased" # جایگزین کنید
|
19 |
+
gen_tokenizer = AutoTokenizer.from_pretrained(gen_model_name)
|
20 |
+
gen_model = AutoModel.from_pretrained(gen_model_name)
|
21 |
+
has_generative = False # فعلاً False چون BERT generative نیست
|
22 |
+
except:
|
23 |
+
has_generative = False
|
24 |
+
|
25 |
+
# 📄 دیتاست گستردهتر
|
26 |
faq_data = {
|
27 |
+
"پایتخت ایران کجاست؟": "تهران پایتخت ایران است.",
|
28 |
+
"زبان رسمی ایران چیست؟": "فارسی زبان رسمی ایران است.",
|
29 |
+
"واحد پول ایران چیست؟": "ریال واحد پول ایران است.",
|
30 |
+
"چه زمانی انتخاب واحد شروع میشود؟": "معمولاً انتخاب واحد در پایان شهریور یا بهمن ماه شروع میشود.",
|
31 |
+
"چه معدلی برای گرفتن 24 واحد لازم است؟": "برای گرفتن 24 واحد حداقل معدل 17 لازم است.",
|
32 |
+
"ساعت کاری ادارات چیست؟": "ساعت کاری ادارات معمولاً از 8 صبح تا 4 عصر است.",
|
33 |
+
"چگونه میتوانم درخواست پاسپورت بدهم؟": "برای درخواست پاسپورت باید به اداره گذرنامه مراجعه کنید.",
|
34 |
+
"فصلهای سال در ایران چیست؟": "فصلهای سال شامل بهار، تابستان، پاییز و زمستان است.",
|
35 |
+
}
|
36 |
+
|
37 |
+
# اضافه کردن دانش عمومی
|
38 |
+
general_knowledge = {
|
39 |
+
"چگونه": "این سوال درباره نحوه انجام کاری است. برای پاسخ دقیقتر، لطفاً سوال خود را کاملتر بپرسید.",
|
40 |
+
"چرا": "این سوال درباره دلیل چیزی است. برای پاسخ بهتر، موضوع مشخصی را بیان کنید.",
|
41 |
+
"چیست": "این سوال تعریف چیزی را میخواهد. لطفاً موضوع مورد نظر را دقیقتر بیان کنید.",
|
42 |
+
"کجا": "این سوال درباره مکان است. برای پاسخ دقیقتر، موضوع خاصی را مشخص کنید.",
|
43 |
+
"کی": "این سوال درباره زمان است. لطفاً موضوع مورد نظر را دقیقتر بیان کنید.",
|
44 |
}
|
45 |
|
46 |
+
# ترکیب دیتاستها
|
47 |
+
all_data = {**faq_data, **general_knowledge}
|
48 |
+
questions = list(all_data.keys())
|
49 |
+
answers = list(all_data.values())
|
50 |
|
51 |
# 📄 تولید embedding
|
52 |
def get_embedding(text):
|
53 |
+
inputs = bert_tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=128)
|
54 |
with torch.no_grad():
|
55 |
+
outputs = bert_model(**inputs)
|
56 |
+
emb = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy()
|
57 |
return emb
|
58 |
|
59 |
+
# محاسبه embedding برای همه سوالات
|
60 |
faq_embeddings = [get_embedding(q) for q in questions]
|
61 |
|
62 |
+
# 📄 تشخیص نوع سوال
|
63 |
+
def detect_question_type(question):
|
64 |
+
question_lower = question.lower()
|
65 |
+
|
66 |
+
# الگوهای سوال
|
67 |
+
patterns = {
|
68 |
+
'definition': ['چیست', 'چی هست', 'تعریف', 'معنی'],
|
69 |
+
'how': ['چگونه', 'چطور', 'چه طور'],
|
70 |
+
'why': ['چرا', 'به چه دلیل'],
|
71 |
+
'when': ['چه زمانی', 'کی', 'چه وقت'],
|
72 |
+
'where': ['کجا', 'در کجا', 'کدام مکان'],
|
73 |
+
'who': ['کی', 'چه کسی', 'کدام فرد'],
|
74 |
+
'greeting': ['سلام', 'درود', 'صبح بخیر', 'ظهر بخیر', 'عصر بخیر', 'شب بخیر'],
|
75 |
+
'thanks': ['ممنون', 'متشکرم', 'سپاس', 'تشکر'],
|
76 |
+
'yes_no': ['آیا', 'مگر', 'آیا که']
|
77 |
+
}
|
78 |
+
|
79 |
+
for q_type, words in patterns.items():
|
80 |
+
for word in words:
|
81 |
+
if word in question_lower:
|
82 |
+
return q_type
|
83 |
+
|
84 |
+
return 'general'
|
85 |
+
|
86 |
+
# 📄 تولید پاسخ برای سوالات عمومی
|
87 |
+
def generate_general_answer(question, question_type):
|
88 |
+
if question_type == 'greeting':
|
89 |
+
return "سلام! چطور میتونم کمکتون کنم؟"
|
90 |
+
|
91 |
+
elif question_type == 'thanks':
|
92 |
+
return "خواهش میکنم! اگر سوال دیگری دارید، بپرسید."
|
93 |
+
|
94 |
+
elif question_type == 'definition':
|
95 |
+
return "برای تعریف دقیقتر این موضوع، لطفاً سوال خود را کاملتر بپرسید تا بتوانم پاسخ مناسبی ارائه دهم."
|
96 |
+
|
97 |
+
elif question_type == 'how':
|
98 |
+
return "برای راهنمایی دقیقتر درباره نحوه انجام این کار، لطفاً جزئیات بیشتری از سوال خود ارائه دهید."
|
99 |
+
|
100 |
+
elif question_type == 'why':
|
101 |
+
return "برای توضیح دلایل، لطفاً موضوع مورد نظر را دقیقتر مشخص کنید تا بتوانم پاسخ مناسبی ارائه دهم."
|
102 |
+
|
103 |
+
elif question_type == 'when':
|
104 |
+
return "برای اطلاع از زمان دقیق، لطفاً موضوع خاصی را مشخص کنید تا بتوانم راهنمایی کنم."
|
105 |
+
|
106 |
+
elif question_type == 'where':
|
107 |
+
return "برای اطلاع از مکان، لطفاً موضوع مورد نظر را دقیقتر بیان کنید."
|
108 |
+
|
109 |
+
elif question_type == 'yes_no':
|
110 |
+
return "برای پاسخ به این سوال، لطفاً موضوع را کاملتر مطرح کنید."
|
111 |
+
|
112 |
+
else:
|
113 |
+
# پاسخ عمومی هوشمند
|
114 |
+
return "سوال جالبی پرسیدهاید. متأسفانه در حال حاضر اطلاعات کاملی در این زمینه ندارم، اما اگر سوال خود را دقیقتر مطرح کنید، شاید بتوانم کمک بیشتری کنم."
|
115 |
+
|
116 |
+
# 📄 تابع اصلی پاسخ
|
117 |
def answer_question(user_question):
|
118 |
+
# حذف فاصلههای اضافی
|
119 |
+
user_question = user_question.strip()
|
120 |
+
|
121 |
+
if not user_question:
|
122 |
+
return "لطفاً سوال خود را بنویسید."
|
123 |
+
|
124 |
+
# تشخیص نوع سوال
|
125 |
+
question_type = detect_question_type(user_question)
|
126 |
+
|
127 |
+
# جستجوی semantic در دیتاست
|
128 |
user_emb = get_embedding(user_question)
|
129 |
sims = [cosine_similarity([user_emb], [emb])[0][0] for emb in faq_embeddings]
|
130 |
best_idx = int(np.argmax(sims))
|
131 |
best_score = sims[best_idx]
|
132 |
+
|
133 |
+
# تنظیم threshold بر اساس نوع سوال
|
134 |
+
if question_type in ['greeting', 'thanks']:
|
135 |
+
threshold = 0.3
|
136 |
+
else:
|
137 |
+
threshold = 0.65
|
138 |
+
|
139 |
+
if best_score > threshold:
|
140 |
return answers[best_idx]
|
141 |
else:
|
142 |
+
# تولید پاسخ برای سوالات خارج از دیتاست
|
143 |
+
return generate_general_answer(user_question, question_type)
|
144 |
+
|
145 |
+
# 📄 تابع اضافه کردن دانش جدید
|
146 |
+
def add_knowledge(question, answer):
|
147 |
+
if question and answer:
|
148 |
+
global questions, answers, faq_embeddings, all_data
|
149 |
+
|
150 |
+
# اضافه کردن به دیتاست
|
151 |
+
all_data[question] = answer
|
152 |
+
questions.append(question)
|
153 |
+
answers.append(answer)
|
154 |
+
|
155 |
+
# محاسبه embedding جدید
|
156 |
+
new_emb = get_embedding(question)
|
157 |
+
faq_embeddings.append(new_emb)
|
158 |
+
|
159 |
+
return f"دانش جدید اضافه شد: {question} -> {answer}"
|
160 |
+
else:
|
161 |
+
return "لطفاً هم سوال و هم پاسخ را وارد کنید."
|
162 |
|
163 |
# 📄 رابط Gradio
|
164 |
+
with gr.Blocks(title="🤖 دستیار فارسی هوشمند") as demo:
|
165 |
+
gr.Markdown("## 🤖 دستیار فارسی هوشمند (پاسخ به سوالات داخل و خارج دیتاست)")
|
166 |
+
|
167 |
+
with gr.Tab("💬 پرسش و پاسخ"):
|
168 |
+
inp = gr.Textbox(label="سؤال خود را بنویسید", placeholder="مثال: سلام، پایتخت ایران کجاست؟")
|
169 |
+
out = gr.Textbox(label="��اسخ", lines=3)
|
170 |
+
btn = gr.Button("پاسخ بده", variant="primary")
|
171 |
+
btn.click(fn=answer_question, inputs=inp, outputs=out)
|
172 |
+
|
173 |
+
with gr.Tab("📚 افزودن دانش"):
|
174 |
+
gr.Markdown("### افزودن سوال و پاسخ جدید به دیتاست")
|
175 |
+
new_q = gr.Textbox(label="سوال جدید")
|
176 |
+
new_a = gr.Textbox(label="پاسخ جدید", lines=2)
|
177 |
+
add_btn = gr.Button("اضافه کن", variant="secondary")
|
178 |
+
add_result = gr.Textbox(label="نتیجه")
|
179 |
+
add_btn.click(fn=add_knowledge, inputs=[new_q, new_a], outputs=add_result)
|
180 |
+
|
181 |
+
with gr.Tab("ℹ️ راهنما"):
|
182 |
+
gr.Markdown("""
|
183 |
+
### نحوه استفاده:
|
184 |
+
1. **سوالات معمولی**: مثل "پایتخت ایران کجاست؟"
|
185 |
+
2. **احوالپرسی**: مثل "سلام" یا "صبح بخیر"
|
186 |
+
3. **تشکر**: مثل "ممنون" یا "متشکرم"
|
187 |
+
4. **سوالات عمومی**: حتی اگر در دیتاست نباشد، پاسخ مناسب میدهد
|
188 |
+
|
189 |
+
### ویژگیها:
|
190 |
+
- پاسخ به سوالات داخل دیتاست با دقت بالا
|
191 |
+
- پاسخ هوشمند به سوالات خارج دیتاست
|
192 |
+
- تشخیص نوع سوال (تعریف، چگونه، چرا، کجا، کی)
|
193 |
+
- قابلیت افزودن دانش جدید
|
194 |
+
""")
|
195 |
+
|
196 |
+
demo.launch()
|