Update app.py
Browse files
app.py
CHANGED
|
@@ -387,42 +387,6 @@ llm = ChatOpenAI(
|
|
| 387 |
max_tokens=1024
|
| 388 |
)
|
| 389 |
|
| 390 |
-
# ---------- پردازش فایلها با کش و موازی ----------
|
| 391 |
-
folder_path = '46'
|
| 392 |
-
normalizer = Normalizer()
|
| 393 |
-
sentence_tokenizer = SentenceTokenizer()
|
| 394 |
-
|
| 395 |
-
@st.cache_data(show_spinner="در حال پردازش اسناد... لطفاً صبور باشید.")
|
| 396 |
-
def load_and_process_documents(path):
|
| 397 |
-
def process_docx(filename):
|
| 398 |
-
try:
|
| 399 |
-
full_path = os.path.join(path, filename)
|
| 400 |
-
doc = docx.Document(full_path)
|
| 401 |
-
text = "\n".join([para.text for para in doc.paragraphs]) # استخراج متن
|
| 402 |
-
normalized = normalizer.normalize(text)
|
| 403 |
-
sentences = normalized
|
| 404 |
-
return sentences
|
| 405 |
-
except Exception as e:
|
| 406 |
-
print(f"Error processing {filename}: {e}")
|
| 407 |
-
return []
|
| 408 |
-
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 409 |
-
results = executor.map(process_docx, [f for f in os.listdir(path) if f.endswith(".docx")])
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
return list(results)
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
all_sentences = load_and_process_documents(folder_path)
|
| 417 |
-
|
| 418 |
-
def clean_text(text):
|
| 419 |
-
cleaned_text = re.sub(r'[^آ-ی۰-۹0-9،.؟!؛+\-* ]+', '', text)
|
| 420 |
-
|
| 421 |
-
return cleaned_text
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
# all_sentences = load_and_process_documents(folder_path)
|
| 425 |
-
# st.markdown(all_sentences[2])
|
| 426 |
|
| 427 |
# ---------- ورودی جستجو ----------
|
| 428 |
st.markdown("""
|
|
@@ -539,112 +503,76 @@ st.markdown("""
|
|
| 539 |
|
| 540 |
|
| 541 |
|
| 542 |
-
import
|
| 543 |
-
|
| 544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
|
| 546 |
-
def extract_keywords(query):
|
| 547 |
-
tokenizer = WordTokenizer()
|
| 548 |
-
words = tokenizer.tokenize(query)
|
| 549 |
-
return [word for word in words if word not in string.punctuation]
|
| 550 |
|
| 551 |
def clean_text(text):
|
| 552 |
-
return
|
| 553 |
|
| 554 |
-
def compute_similarity(sentence, query, threshold):
|
| 555 |
-
similarity = fuzz.partial_ratio(query, sentence)
|
| 556 |
-
if similarity >= threshold:
|
| 557 |
-
return sentence
|
| 558 |
-
return None
|
| 559 |
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
if query:
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
st.markdown(matched_text)
|
| 576 |
-
|
| 577 |
-
prompt = f"""
|
| 578 |
-
تعدادی پاسخ برای سوال زیر تولید شده است. لطفاً ابتدا این پاسخها را بررسی کن، سپس با در نظر گرفتن محتوای سوال و لحن آن، یک پاسخ نهایی حرفهای، دقیق و روان از داخل پاسخها ارائه کن که هم به سوال پاسخ دهد و هم از نظر نگارشی و ساختاری در سطح بالایی باشد. پاسخ نهایی باید حداکثر 2048 کاراکتر و حداقل 512 باشد، خلاصه و واضح نوشته شود و فقط به زبان فارسی باشد. از تکرار اضافی پرهیز کن و فقط از پاسخهای زیر استفاده کن. در صورت نیاز، محتوای چند پاسخ را با هم ترکیب کن.
|
| 579 |
-
سوال:
|
| 580 |
-
{query}
|
| 581 |
-
پاسخها:
|
| 582 |
-
{matched_text}
|
| 583 |
-
پاسخ نهایی حرفهای بازنویسیشده:
|
| 584 |
-
"""
|
| 585 |
-
|
| 586 |
-
response = llm([
|
| 587 |
-
SystemMessage(content="You are a helpful assistant."),
|
| 588 |
-
HumanMessage(content=prompt)
|
| 589 |
-
])
|
| 590 |
-
rewritten = clean_text(response.content.strip())
|
| 591 |
-
|
| 592 |
-
review_prompt = f"""
|
| 593 |
-
لطفاً بررسی کن که آیا پاسخ زیر به سوال دادهشده مرتبط، دقیق و معتبر است یا خیر. اگر پاسخ قابل قبول و دقیق است بنویس 'تأیید شد'. اگر متوسط است بنویس 'کمی خوب'. اگر بیربط یا اشتباه است بنویس 'نیاز به اصلاح دارد'.
|
| 594 |
-
سوال:
|
| 595 |
-
{query}
|
| 596 |
-
پاسخ:
|
| 597 |
-
{rewritten}
|
| 598 |
-
"""
|
| 599 |
-
|
| 600 |
-
review_response = llm([
|
| 601 |
-
SystemMessage(content="You are a helpful assistant."),
|
| 602 |
-
HumanMessage(content=review_prompt)
|
| 603 |
-
])
|
| 604 |
-
review_result = review_response.content.strip()
|
| 605 |
-
|
| 606 |
-
if "تأیید شد" in review_result:
|
| 607 |
-
st.markdown(f'<div class="chat-message">{rewritten}</div>', unsafe_allow_html=True)
|
| 608 |
-
|
| 609 |
-
elif "کمی خوب" in review_result:
|
| 610 |
-
final_prompt = f"""
|
| 611 |
-
لطفاً برای سوال زیر پاسخی حرفهای، دقیق و روان تولید کن که مرتبط و معتبر باشد. از زبانی جز فارسی استفاده نکن. از محتوای زیر استفاده کن و یک پاسخ نهایی خوب بنویس:
|
| 612 |
-
سوال:
|
| 613 |
-
{query}
|
| 614 |
-
پاسخ اولیه:
|
| 615 |
-
{rewritten}
|
| 616 |
-
پاسخ نهایی:
|
| 617 |
-
"""
|
| 618 |
-
new_response = llm([
|
| 619 |
-
SystemMessage(content="You are a helpful assistant."),
|
| 620 |
-
HumanMessage(content=final_prompt)
|
| 621 |
-
])
|
| 622 |
-
final_answer = clean_text(new_response.content.strip())
|
| 623 |
-
st.markdown(f'<div class="chat-message">{final_answer}</div>', unsafe_allow_html=True)
|
| 624 |
-
|
| 625 |
-
else:
|
| 626 |
-
fallback_prompt = f"""
|
| 627 |
-
لطفاً برای سوال زیر پاسخی حرفهای، دقیق و روان تولید کن که مرتبط و معتبر باشد. اگر اطلاعات کافی وجود ندارد، صادقانه بگو. فقط به زبان فارسی پاسخ بده:
|
| 628 |
-
سوال:
|
| 629 |
-
{query}
|
| 630 |
-
"""
|
| 631 |
-
fallback_response = llm([
|
| 632 |
-
SystemMessage(content="You are a helpful assistant."),
|
| 633 |
-
HumanMessage(content=fallback_prompt)
|
| 634 |
-
])
|
| 635 |
-
final_fallback = clean_text(fallback_response.content.strip())
|
| 636 |
-
st.markdown(f'<div class="chat-message">{final_fallback}</div>', unsafe_allow_html=True)
|
| 637 |
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
rewritten = clean_text(response.content.strip())
|
| 649 |
-
st.markdown(f'<div class="chat-message">{rewritten}</div>', unsafe_allow_html=True)
|
| 650 |
-
think.empty()
|
|
|
|
| 387 |
max_tokens=1024
|
| 388 |
)
|
| 389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
|
| 391 |
# ---------- ورودی جستجو ----------
|
| 392 |
st.markdown("""
|
|
|
|
| 503 |
|
| 504 |
|
| 505 |
|
| 506 |
+
import os
|
| 507 |
+
import re
|
| 508 |
+
import docx
|
| 509 |
+
import streamlit as st
|
| 510 |
+
import concurrent.futures
|
| 511 |
+
from hazm import Normalizer
|
| 512 |
+
from rapidfuzz import fuzz
|
| 513 |
+
from langchain.schema import SystemMessage, HumanMessage
|
| 514 |
+
|
| 515 |
+
folder_path = '46'
|
| 516 |
+
normalizer = Normalizer()
|
| 517 |
+
|
| 518 |
+
@st.cache_data(show_spinner="در حال پردازش اسناد... لطفاً صبور باشید.")
|
| 519 |
+
def load_and_process_documents(path):
|
| 520 |
+
def process_docx(filename):
|
| 521 |
+
try:
|
| 522 |
+
full_path = os.path.join(path, filename)
|
| 523 |
+
doc = docx.Document(full_path)
|
| 524 |
+
text = "\n".join([para.text for para in doc.paragraphs])
|
| 525 |
+
normalized = normalizer.normalize(text)
|
| 526 |
+
return filename, normalized
|
| 527 |
+
except Exception as e:
|
| 528 |
+
print(f"Error processing {filename}: {e}")
|
| 529 |
+
return filename, ""
|
| 530 |
+
|
| 531 |
+
filenames = [f for f in os.listdir(path) if f.endswith(".docx")]
|
| 532 |
+
doc_texts = {}
|
| 533 |
+
|
| 534 |
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 535 |
+
for filename, content in executor.map(process_docx, filenames):
|
| 536 |
+
doc_texts[filename] = content
|
| 537 |
+
|
| 538 |
+
return doc_texts
|
| 539 |
+
|
| 540 |
+
doc_texts = load_and_process_documents(folder_path)
|
| 541 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
|
| 543 |
def clean_text(text):
|
| 544 |
+
return re.sub(r'[^آ-ی۰-۹0-9،.؟!؛+\-* ]+', '', text)
|
| 545 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
|
| 547 |
+
def find_closest_filename(query, filenames):
|
| 548 |
+
scores = [(f, fuzz.partial_ratio(query, f)) for f in filenames]
|
| 549 |
+
scores.sort(key=lambda x: x[1], reverse=True)
|
| 550 |
+
return scores[0][0] if scores else None
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
# فرض بر این است که متغیر query توسط کاربر مشخص شده است
|
| 554 |
if query:
|
| 555 |
+
closest_file = find_closest_filename(query, list(doc_texts.keys()))
|
| 556 |
+
|
| 557 |
+
if closest_file:
|
| 558 |
+
matched_text = doc_texts[closest_file]
|
| 559 |
+
|
| 560 |
+
prompt = f"""
|
| 561 |
+
لطفاً با توجه به سؤال زیر و محتوای سند موجود، یک پاسخ نهایی حرفهای، دقیق و روان تولید کن. فقط از متن سند استفاده کن. اگر اطلاعات کافی در متن وجود ندارد، صادقانه اعلام کن.
|
| 562 |
+
سوال:
|
| 563 |
+
{query}
|
| 564 |
+
محتوای سند:
|
| 565 |
+
{matched_text}
|
| 566 |
+
پاسخ نهایی:
|
| 567 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
|
| 569 |
+
response = llm([
|
| 570 |
+
SystemMessage(content="You are a helpful assistant."),
|
| 571 |
+
HumanMessage(content=prompt)
|
| 572 |
+
])
|
| 573 |
+
rewritten = clean_text(response.content.strip())
|
| 574 |
+
|
| 575 |
+
st.markdown(f'<div class="chat-message">{rewritten}</div>', unsafe_allow_html=True)
|
| 576 |
+
|
| 577 |
+
else:
|
| 578 |
+
st.warning("هیچ سند مرتبطی پیدا نشد.")
|
|
|
|
|
|
|
|
|