|
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_model_name = "HooshvareLab/bert-fa-base-uncased" |
|
bert_tokenizer = AutoTokenizer.from_pretrained(bert_model_name) |
|
bert_model = AutoModel.from_pretrained(bert_model_name) |
|
|
|
|
|
|
|
try: |
|
|
|
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 |
|
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()) |
|
|
|
|
|
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 |
|
|
|
|
|
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) |
|
|
|
|
|
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] |
|
|
|
|
|
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) |
|
|
|
|
|
new_emb = get_embedding(question) |
|
faq_embeddings.append(new_emb) |
|
|
|
return f"دانش جدید اضافه شد: {question} -> {answer}" |
|
else: |
|
return "لطفاً هم سوال و هم پاسخ را وارد کنید." |
|
|
|
|
|
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() |