125 lines
7.0 KiB
Python
125 lines
7.0 KiB
Python
|
|
import json
|
|||
|
|
import re
|
|||
|
|
from openai import OpenAI
|
|||
|
|
from config import API_KEY, BASE_URL, MODEL_NAME
|
|||
|
|
|
|||
|
|
|
|||
|
|
class APIHandler:
|
|||
|
|
def __init__(self):
|
|||
|
|
# ایجاد کلاینت با استفاده از کتابخانه رسمی OpenAI
|
|||
|
|
self.client = OpenAI(
|
|||
|
|
base_url=BASE_URL,
|
|||
|
|
api_key=API_KEY
|
|||
|
|
)
|
|||
|
|
self.model_name = MODEL_NAME
|
|||
|
|
|
|||
|
|
def _call_api(self, prompt, system_prompt="شما یک دستیار هوش مصنوعی دقیق در تحلیل متن هستید.", temperature=0.3):
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
response = self.client.chat.completions.create(
|
|||
|
|
model=self.model_name,
|
|||
|
|
messages=[
|
|||
|
|
{"role": "system", "content": system_prompt},
|
|||
|
|
{"role": "user", "content": prompt}
|
|||
|
|
],
|
|||
|
|
temperature=temperature,
|
|||
|
|
timeout=60 # جلوگیری از گیر کردن برنامه در صورت کندی سرور
|
|||
|
|
)
|
|||
|
|
# استخراج محتوای متنی از ریسپانس استاندارد openai
|
|||
|
|
return response.choices[0].message.content.strip()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"API failed {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_semantic_unit(self, segment_sentences):
|
|||
|
|
"""تابع ۱: استخراج واحد معنایی (برای جایگزینی در نودهای درخت)"""
|
|||
|
|
text_to_summarize = " ".join(segment_sentences)
|
|||
|
|
|
|||
|
|
prompt = f"""تو یک دستیار هوش مصنوعی متخصص در تحلیل متن و زبانشناسی پردازشی هستی.
|
|||
|
|
وظیفه تو این است که متن ورودی را بخوانی و تنها «هسته اصلی واحد معنایی» آن را سنتز و تولید کنی.
|
|||
|
|
|
|||
|
|
تعریف واحد معنایی مورد نظر: یک تکجملهٔ خلاصه، دقیق و جامع که یک رویداد کامل را توصیف کند. این جمله باید حتماً دارای ساختار منطقی (شامل عامل/فاعل، مفعول و فعلِ نشاندهنده نتیجه) باشد.
|
|||
|
|
|
|||
|
|
قوانین اکید خروجی (Strict Rules):
|
|||
|
|
1. به هیچوجه از کلمات ربط اضافی، پیشدرآمد (مثل "جمله استخراج شده:"، "خلاصه متن:" یا "بله، حتما") و توضیحات استفاده نکن.
|
|||
|
|
|
|||
|
|
مثالها (Few-Shot Examples):
|
|||
|
|
متن ورودی: "دیروز در پی بارش شدید باران، رودخانه طغیان کرد. این اتفاق باعث شد تا تمام خانههای روستای مجاور زیر آب بروند و مردم مجبور به تخلیه سریع منطقه شوند."
|
|||
|
|
خروجی: طغیان رودخانه بر اثر بارش باران منجر به آبگرفتگی خانهها و تخلیه روستا شد.
|
|||
|
|
|
|||
|
|
متن ورودی: "تیم توسعهدهنده ماهها روی بهینهسازی الگوریتم کار کرد. در نهایت موفق شدند زمان پردازش دادهها را به نصف کاهش دهند که این موضوع رضایت مدیران را در پی داشت."
|
|||
|
|
خروجی: تلاش تیم توسعهدهنده برای بهینهسازی الگوریتم باعث کاهش نصف زمان پردازش و جلب رضایت مدیران شد.
|
|||
|
|
|
|||
|
|
|
|||
|
|
متن ورودی اصلی:
|
|||
|
|
{text_to_summarize}
|
|||
|
|
"""
|
|||
|
|
response = self._call_api(prompt)
|
|||
|
|
return response if response else "خطا در استخراج واحد معنایی"
|
|||
|
|
|
|||
|
|
def extract_propositions(self, sentences):
|
|||
|
|
"""تابع ۲: استخراج تکتک گزارهها از جملات یک سگمنت (برای شروع ساخت گراف)"""
|
|||
|
|
text = " ".join(sentences)
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
متن زیر را بخوان و تمام گزارههای معنایی (اقدامات، واقعیتها یا مفاهیم مستقل) موجود در آن را استخراج کن.
|
|||
|
|
قانون اکید: خروجی را فقط به صورت یک آرایه JSON معتبر از رشتهها برگردان و هیچ متن اضافهای ننویس.
|
|||
|
|
مثال خروجی: ["گزاره اول", "گزاره دوم"]
|
|||
|
|
|
|||
|
|
متن:
|
|||
|
|
{text}
|
|||
|
|
"""
|
|||
|
|
response = self._call_api(prompt, temperature=0.2)
|
|||
|
|
if not response:
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
clean_json = re.sub(r'```json|```', '', response).strip()
|
|||
|
|
propositions = json.loads(clean_json)
|
|||
|
|
if isinstance(propositions, list):
|
|||
|
|
return propositions
|
|||
|
|
return []
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"خطا در پارس کردن گزارهها (JSON Error): {e}")
|
|||
|
|
print(f"خروجی خام مدل این بود: {response}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def check_relation(self, prop1, prop2):
|
|||
|
|
"""تابع ۳: بررسی رابطه غایی (هدف-وسیله) یا علت و معلولی بین دو گزاره"""
|
|||
|
|
prompt = f"""
|
|||
|
|
دو گزاره زیر را بررسی کن و ببین آیا رابطه "علت و معلولی" یا "غایی (هدف و وسیله)" بین آنها برقرار است یا خیر.
|
|||
|
|
اگر هیچ ارتباطی از این نوع وجود ندارد، دقیقاً و فقط بنویس: "ارتباط وجود ندارد"
|
|||
|
|
اگر ارتباط وجود دارد، در یک گزاره کوتاه و دقیق این رابطه را توصیف کن (مثلاً: گزاره اول وسیلهای برای رسیدن به هدف گزاره دوم است).
|
|||
|
|
|
|||
|
|
گزاره ۱: {prop1}
|
|||
|
|
گزاره ۲: {prop2}
|
|||
|
|
"""
|
|||
|
|
response = self._call_api(prompt, temperature=0.3)
|
|||
|
|
return response if response else "ارتباط وجود ندارد"
|
|||
|
|
|
|||
|
|
def score_relation(self, relation_desc):
|
|||
|
|
"""تابع ۴: امتیازدهی به رابطه از 1 تا 10"""
|
|||
|
|
if not relation_desc or relation_desc.strip() == "ارتباط وجود ندارد" or relation_desc.strip() == "":
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
با توجه به توصیف رابطه زیر، یک نمره از 1 تا 10 به میزان قوت و منطقی بودن این رابطه علت و معلولی/غایی بده.
|
|||
|
|
قانون اکید: فـــقـــط یک عدد صحیح برگردان. هیچ کاراکتر یا کلمهای غیر از عدد ننویس.
|
|||
|
|
|
|||
|
|
توصیف رابطه:
|
|||
|
|
{relation_desc}
|
|||
|
|
"""
|
|||
|
|
response = self._call_api(prompt, temperature=0.1)
|
|||
|
|
|
|||
|
|
if not response:
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
numbers = re.findall(r'\d+', response)
|
|||
|
|
if numbers:
|
|||
|
|
score = int(numbers[0])
|
|||
|
|
return max(1, min(10, score))
|
|||
|
|
return 0
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"failed to extract scores {e}")
|
|||
|
|
return 0
|