add new source

This commit is contained in:
mdorstkar 2026-04-30 19:16:50 +03:30
parent beb29cc01e
commit a11eb348bf
17 changed files with 3989906 additions and 41 deletions

View File

@ -5,9 +5,9 @@ import os
def create_faiss_index_from_json(): def create_faiss_index_from_json():
# مسیر فایل‌ها # مسیر فایل‌ها
json_file_path = './nahj_data/nahj_vectors_multilingual-e5-small.json' json_file_path = './nahj_data/nahj_vectors_jina-embeddings-v5-text-small.json'
faiss_index_path = './data-faiss/faiss_index_multilingual-e5-small.index' faiss_index_path = './data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index'
metadata_file_path = './data-faiss/faiss_index_nahj_metadata_multilingual-e5-small.json' metadata_file_path = './data-faiss/faiss_index_nahj_metadata_jina-embeddings-v5-text-small.json'
# --- 1. بارگذاری داده‌ها از JSON --- # --- 1. بارگذاری داده‌ها از JSON ---
with open(json_file_path, 'r', encoding='utf-8') as f: with open(json_file_path, 'r', encoding='utf-8') as f:

View File

@ -28,10 +28,29 @@ import os
os.environ['HF_HUB_OFFLINE'] = '1' os.environ['HF_HUB_OFFLINE'] = '1'
# models_list = [
# "jinaai/jina-embeddings-v5-text-small",
# "BAAI/bge-m3",
# "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
# "intfloat/multilingual-e5-small"
# ]
option_list = [
{"model_name":"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2","model_faiss":"./data-faiss/faiss_index_nahj.index","samples_numbers":[10,100,150,200,300],"normal_embedder":True},
{"model_name":"intfloat/multilingual-e5-small","model_faiss":"./data-faiss/faiss_index_multilingual-e5-small.index","samples_numbers":[10,100,150,200,300],"normal_embedder":True},
{"model_name":"jinaai/jina-embeddings-v5-text-small","model_faiss":"./data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index","samples_numbers":[10,100,150,200,300],"normal_embedder":False},
{"model_name":"BAAI/bge-m3","model_faiss":"./data-faiss/faiss_index_nahj_bge_m3.index","samples_numbers":[10,100,150,200,300],"normal_embedder":True}
]
normal_embedder = True
MODEL_PATH = "intfloat/multilingual-e5-small" MODEL_PATH = "intfloat/multilingual-e5-small"
# sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 # sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
# BAAI/bge-m3 # BAAI/bge-m3
# intfloat/multilingual-e5-small # intfloat/multilingual-e5-small
# jinaai/jina-embeddings-v5-text-small
# RERANKER_MODEL = "BAAI/bge-reranker-v2-m3" # RERANKER_MODEL = "BAAI/bge-reranker-v2-m3"
@ -39,10 +58,11 @@ FAISS_INDEX_PATH = "./data-faiss/faiss_index_multilingual-e5-small.index"
# ./data-faiss/faiss_index_nahj.index # ./data-faiss/faiss_index_nahj.index
# ./data-faiss/faiss_index_nahj_bge_m3.index # ./data-faiss/faiss_index_nahj_bge_m3.index
# ./data-faiss/faiss_index_multilingual-e5-small.index # ./data-faiss/faiss_index_multilingual-e5-small.index
# ./data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index
FAISS_METADATA_PATH = "./data-faiss/faiss_index_nahj_metadata.json" FAISS_METADATA_PATH = "./data-faiss/faiss_index_nahj_metadata.json"
normal_embedder = True
conn = sqlite3.connect('./db/nahj.db') conn = sqlite3.connect('./db/nahj.db')
cursor = conn.cursor() cursor = conn.cursor()
@ -109,7 +129,15 @@ class HybridRetrieverReranker:
# --- Dense Embedder --- # --- Dense Embedder ---
print("Loading SentenceTransformer model ...") print("Loading SentenceTransformer model ...")
self.embedder = SentenceTransformer(model_path, device=self.device)
if normal_embedder == False:
self.embedder = SentenceTransformer(model_path\
,trust_remote_code=True \
,model_kwargs={'default_task': 'retrieval'}\
,device=self.device)
else:
self.embedder = SentenceTransformer(model_path, device=self.device)
# embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2", device= self.device) # embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2", device= self.device)
# embedder.save(MODEL_PATH) # embedder.save(MODEL_PATH)
@ -267,18 +295,14 @@ class HybridRetrieverReranker:
for i in cand_idx for i in cand_idx
] ]
ids, part_ids, context_ids, large_titles, sentences, titles, urls, arabic_texts, Interpretation_links, types, faiss_index = load_faiss_index(FAISS_INDEX_PATH, FAISS_METADATA_PATH)
pipe = HybridRetrieverReranker(ids, part_ids,context_ids,large_titles,sentences , titles, urls, arabic_texts, Interpretation_links, types, faiss_index, dense_alpha=0.6 , model_path=MODEL_PATH)
def single_query(query: str , samples_number):
def single_query(query: str):
query = cleaning(query) query = cleaning(query)
# تنظیم final_k در اینجا مشخص میکنه پاسخ های مدل چندتا باشه # تنظیم final_k در اینجا مشخص میکنه پاسخ های مدل چندتا باشه
retrived_sections = pipe.search(query, sentences, topk_dense=100, topk_sparse=100, pre_rerank_k=100, final_k=10) retrived_sections = pipe.search(query, sentences, topk_dense=100, topk_sparse=100, pre_rerank_k=100, final_k=samples_number)
retrived_sections_list = [] retrived_sections_list = []
final_similars_text = '' final_similars_text = ''
@ -323,9 +347,9 @@ def get_passages_by_paragraphs(retrived_sections_list):
return final_passages return final_passages
# متد اصلی جست و جو # متد اصلی جست و جو
def bale_search(query): def bale_search(query,samples_number=10):
start = datetime.datetime.now() start = datetime.datetime.now()
id_list, result_passages, retrived_sections_list = single_query(query) id_list, result_passages, retrived_sections_list = single_query(query,samples_number)
related_paragraphs = get_passages_by_paragraphs(retrived_sections_list) related_paragraphs = get_passages_by_paragraphs(retrived_sections_list)
end_retrive = datetime.datetime.now() end_retrive = datetime.datetime.now()
@ -342,36 +366,71 @@ def bale_search(query):
return id_list.split("\n") return id_list.split("\n")
final_answers_and_questions = [] final_result = ''
true_n = 0
sen_n = 0
for s in answers_and_questions :
# حلقه اصلی برای دریافت سوال و جواب ها از اکسل
# ارسال آنها به مدل و صحت سنجی پاسخ آن
sen_n+=1
print(sen_n)
find = False
answer = f"خطبه {str(int(s[0]))}"
question = s[1]
bot_answers = bale_search(question)
titles_list = [] for option in option_list:
for _id in bot_answers:
if _id == '' :
continue
cursor.execute("SELECT * FROM speeches WHERE id = ?", (_id,))
result = cursor.fetchall()
answer_title = result[0][3]
if answer_title == answer :
find = True
if find == True : if option['normal_embedder'] == False:
true_n += 1 normal_embedder = False
s.append(find) else :
s[0] = int(s[0]) normal_embedder = True
final_answers_and_questions.append(s)
# پرینت کردن اینکه از تعداد سوالات پرسیده شده به چندتای اونها جواب صحیح داده شده ids, part_ids, context_ids, large_titles, sentences, titles, urls, arabic_texts, Interpretation_links, types, faiss_index = load_faiss_index(option['model_faiss'], FAISS_METADATA_PATH)
print(f"findes speechs number: {true_n}/{len(answers_and_questions)}")
pipe = HybridRetrieverReranker(ids, part_ids,context_ids,large_titles,sentences , titles, urls, arabic_texts, Interpretation_links, types, faiss_index, dense_alpha=0.6 , model_path=option['model_name'])
final_result+="\n--------------------------------------------------------\n"
final_result+=f"model : {option['model_name']}\n"
for num in option['samples_numbers'] :
final_answers_and_questions = []
true_n = 0
sen_n = 0
for s in answers_and_questions :
# حلقه اصلی برای دریافت سوال و جواب ها از اکسل
# ارسال آنها به مدل و صحت سنجی پاسخ آن
sen_n+=1
print(sen_n)
find = False
answer = f"خطبه {str(int(s[0]))}"
question = s[1]
bot_answers = bale_search(question ,samples_number = num)
titles_list = []
for _id in bot_answers:
if _id == '' :
continue
cursor.execute("SELECT * FROM speeches WHERE id = ?", (_id,))
result = cursor.fetchall()
answer_title = result[0][3]
if answer_title == answer :
find = True
if find == True :
true_n += 1
s.append(find)
s[0] = int(s[0])
final_answers_and_questions.append(s)
final_result+="_____________________\n"
final_result+=f"findes speechs number: {true_n}/{len(answers_and_questions)}\n"
final_result+=f"samples number : {num}\n"
final_result+="--------------------------------------------------------\n"
# پرینت کردن اینکه از تعداد سوالات پرسیده شده به چندتای اونها جواب صحیح داده شده
print(final_result)
# print("--------------------------------------------------------")
# print(f"model : {option['model_name']}")
# print(f"findes speechs number: {true_n}/{len(answers_and_questions)}")
# print(f"samples number : {num}")
# print("--------------------------------------------------------")

118
convert_parts_to_context.py Normal file
View File

@ -0,0 +1,118 @@
# بسم الله
import json
"""
این سورس خروجی سورس convert_sentence_to_part رو به عنوان ورودی دریافت میکنه
و پارت های نهج البلاغه رو به صورت یک (حکمت یا خطبه یا نامه) درمیاره که هرکدوم یک key شامل پاراگراف ها دارن
"""
nahj_parts_file = "./nahj_data/all_nahj_parts.json"
# --- 1. بارگذاری داده‌ها از JSON ---
with open(nahj_parts_file, 'r', encoding='utf-8') as f:
all_parts = json.load(f)
id_ = all_parts[0]['context_id']
paragraph_id = all_parts[0]['part_id']
number = all_parts[0]['number']
paragraph_order = all_parts[0]['part_order']
url = all_parts[0]['url']
interpretation_link = all_parts[0]['interpretation_link']
title = all_parts[0]['title']
large_title = all_parts[0]['large_title']
text = all_parts[0]['part_text']
arabic_text = all_parts[0]['arabic_text']
type_ = all_parts[0]['type']
first_try = True
paragraph_list = []
final_list = []
for part in all_parts:
if first_try == True:
first_try = False
paragraph_list.append({
"paragraph_id":paragraph_id,
"number": number,
"paragraph_order":paragraph_order,
"large_title":large_title,
"text":text,
"arabic_text":arabic_text
})
continue
if part['number'] == 480:
pass
if part['number'] == number and \
part['type'] == type_ :
paragraph_list.append({
"paragraph_id":part['part_id'],
"number": part['number'],
"paragraph_order":part['part_order'],
"large_title":part['large_title'],
"text":part['part_text'],
"arabic_text":part['arabic_text']
})
else:
final_list.append({
"id":id_,
"url":url,
"interpretation_link":interpretation_link,
"title":title,
"large_title":large_title,
"type":type_,
"paragraphs":paragraph_list
})
paragraph_list = [{
"paragraph_id":part['part_id'],
"number": part['number'],
"paragraph_order":part['part_order'],
"large_title":part['large_title'],
"text":part['part_text'],
"arabic_text":part['arabic_text']
}]
id_ = part['context_id']
paragraph_id = part['part_id']
number = part['number']
paragraph_order = part['part_order']
url = part['url']
interpretation_link = part['interpretation_link']
title = part['title']
large_title = part['large_title']
text = part['part_text']
arabic_text = part['arabic_text']
type_ = part['type']
final_list.append({
"id":id_,
"url":url,
"interpretation_link":interpretation_link,
"title":title,
"large_title":large_title,
"type":type_,
"paragraphs":paragraph_list
})
output_file_path = "./nahj_data/all_nahj_CONTEXT.json"
with open(output_file_path, 'w', encoding='utf-8') as f:
json.dump(final_list, f, ensure_ascii=False, indent=2)

105
convert_sentence_to_part.py Normal file
View File

@ -0,0 +1,105 @@
# بسم الله
import json
'''
این سورس دیکشنری جیسونی نهج البلاغه که شامل سنتنس ها (جدا شده بر اساس نقطه) هستش رو
به پارت ها تبدیل میکنه (پاراگراف)
فایل جیسونی که به عنوان ورودی میگیره ، خروجی unify_embedder دایرکتوری nahj هستش
'''
nahj_sentence_file = "./nahj_data/nahj_vector_bge_m3.json"
# --- 1. بارگذاری داده‌ها از JSON ---
with open(nahj_sentence_file, 'r', encoding='utf-8') as f:
all_sentences = json.load(f)
sentence_number = all_sentences['sentence-1']['number']
part_order = all_sentences['sentence-1']['part_orders']
sentence_text = all_sentences['sentence-1']['sentence']
context_id = all_sentences['sentence-1']['context_id']
part_id = all_sentences['sentence-1']['part_id']
sentence_url = all_sentences['sentence-1']['url']
interpretation_link = all_sentences['sentence-1']['Interpretation_link']
sentence_id = all_sentences['sentence-1']['id']
sentence_title = all_sentences['sentence-1']['title']
sentence_large_title = all_sentences['sentence-1']['large_title']
sentence_arabic_text = all_sentences['sentence-1']['arabic_text']
sentence_type = all_sentences['sentence-1']['type']
first_try = True
all_parts = []
for x,sentence in all_sentences.items() :
if first_try == True :
first_try = False
continue
if x=="sentence-3777":
pass
if sentence['part_orders'] == part_order and \
sentence['number'] == sentence_number and \
sentence["type"] == sentence_type:
sentence_text = sentence_text.strip()
if sentence_text[-1] == ".":
sentence_text = sentence_text + " " + sentence['sentence']
else :
sentence_text = sentence_text + ". " + sentence['sentence']
else :
all_parts.append({
"id" : sentence_id,
"context_id" : context_id,
"part_id" : part_id,
"number" : sentence_number,
"part_order" : part_order,
"url" : sentence_url,
"interpretation_link" : interpretation_link,
"title" : sentence_title,
"large_title" : sentence_large_title,
"part_text" : sentence_text,
"arabic_text" : sentence_arabic_text,
"type" : sentence_type
})
sentence_number = sentence['number']
part_order = sentence['part_orders']
sentence_text = sentence['sentence']
context_id = sentence['context_id']
part_id = sentence['part_id']
sentence_url = sentence['url']
interpretation_link = sentence['Interpretation_link']
sentence_id = sentence['id']
sentence_title = sentence['title']
sentence_large_title = sentence['large_title']
sentence_arabic_text = sentence['arabic_text']
sentence_type = sentence['type']
all_parts.append({
"id" : sentence_id,
"context_id" : context_id,
"part_id" : part_id,
"number" : sentence_number,
"part_order" : part_order,
"url" : sentence_url,
"interpretation_link" : interpretation_link,
"title" : sentence_title,
"large_title" : sentence_large_title,
"part_text" : sentence_text,
"arabic_text" : sentence_arabic_text,
"type" : sentence_type
})
output_file_path = "./nahj_data/all_nahj_parts.json"
with open(output_file_path, 'w', encoding='utf-8') as f:
json.dump(all_parts, f, ensure_ascii=False, indent=2)

File diff suppressed because one or more lines are too long

20022
nahj_data/all_nahj_parts.json Normal file

File diff suppressed because one or more lines are too long

View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

3821
nahj_data/nahj-metadata.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

319
nahj_get_metadata_v2.py Normal file
View File

@ -0,0 +1,319 @@
import json
import ast
from typing import Dict, Any
import time
import datetime
from openai import OpenAI
from langchain_openai import ChatOpenAI
today = f'{datetime.datetime.now().year}{datetime.datetime.now().month}{datetime.datetime.now().day}'
'''
این سورس ، حکمت ها و نامه ها و خطبه های نهج البلاغه
به همراه پاراگراف هایشان از فایل جیسون به همراه پرامپت به llm مورد نظر میفرسته
و پاسخ که یک دیکشنری پایتونی ( شامل مفاهیم کلیدی، شخصیت ها، عنوان ، آیدی ، قوانین و ... ) هست
رو به صورت جیسون ذخیره میکنه
'''
SYSTEM_PROMPT = """
تو یک استخراجگر ساختاریافته اطلاعات برای متون فارسی هستی.
وظیفه تو تحلیل هر پاراگراف و تولید خروجی دقیق بر اساس تعاریف زیر است.
فقط و فقط بر اساس متن ورودی عمل کن و هیچ دانش، تفسیر یا مفهوم خارجی اضافه نکن.
ساختار ورودی
هر ورودی شامل موارد زیر است:
id : شناسه متن
paragraphs : لیستی از پاراگرافها که هرکدام شامل:
paragraph_id
text
قوانین بنیادین سختگیرانه:
خروجی باید فقط یک دیکشنری معتبر پایتون باشد.
هیچ توضیح، مقدمه یا متن اضافی تولید نکن.
تمام مقادیر باید به زبان فارسی باشند.
اگر دادهای در متن وجود نداشت، مقدار آن را [] یا None قرار بده.
تعاریف عملیاتی استخراج:
1. title
یک رشته بین 4 تا 7 کلمه
فقط با واژگان موجود در متن ساخته شود
جهتگیری، تنش یا دوراهی اصلی متن را نشان دهد
2. central_concepts
شامل مفاهیم اصلی، محوری و بسیار مهم هر پاراگراف باشد
تعداد آن محدود و فقط شامل مفاهیم با اهمیت بالا باشد
هر مفهوم دقیقاً دو کلمهای باشد
اسامی خاص و اشخاص به هیچ وجه به عنوان کلیدواژه انتخاب نشوند
کلیدواژه ها مستقیماً از متن استخراج شود
فقط در قالب:
مضاف و مضافالیه
صفت و موصوف
بدون استفاده از حروف عطف(هرگز هرگز هرگز کلمات کلیدواژه با حرف «و» به هم عطف نشوند(کاملا سختگیرانه))
paragraph_effect
برای هر مفهوم مرکزی، احساس متن نسبت به آن باید به صورت طیفی عددی مشخص شود:
یک عدد اعشاری یا صحیح در بازه -1 تا +1
+1 بیشترین میزان تقویت
-1 بیشترین میزان تضعیف
هرچه عدد به +1 نزدیکتر باشد، مفهوم بیشتر تقویت شده است
هرچه عدد به -1 نزدیکتر باشد، مفهوم بیشتر تضعیف شده است
مقادیر بین این دو (مثلاً 0.2 ، -0.4 ، 0.75) مجاز و نشاندهنده شدت نسبی هستند
3. persons
فقط شخصیتهای کاملاً حقیقی (افراد واقعی)
نام باید صریحاً و دقیقاً در متن آمده باشد
اگر هیچ شخصیت حقیقی وجود نداشت، مقدار آن [] باشد
شخصیتهای فرضی، نمادین یا کلی وارد نشوند
4. rules
فقط قواعد بسیار مهم و محوری متن استخراج شوند
تعداد قواعد محدود باشد
هر قاعده یک جمله کوتاه، مستقل و انتزاعی باشد
انواع قواعد:
قاعده توصیفی
بیان رابطه بین دو مفهوم (موضوع + محمول)
قاعده هنجاری
بیانگر الزام، بایستگی یا ضرورت
معمولاً شامل واژگانی مانند:
باید، لازم است، ضروری است، نیازمند است، واجب است، حیاتی است و مانند آن
5. paragraph_type
برای هر پاراگراف یکی از موارد زیر را انتخاب کن (مرتبطترین گزینه):
خطبه: طلیعه سخن (بسم الله و الحمدلله، خوش آمدگویی، تبریک، تسلیت و ...)
اشاره یا مقدمه: (اشاره و توضیحی در رابطه با محتوای بحث و مناسبت آن)
تیتر: اگر این پاراگراف یک تیتر یا زیرتیتر یا سوتیتر باشد
شعر: محتوای پاراگراف یک مصرع یا بیت شعری است
آیه: اگر پاراگراف یک آیه از قرآن باشد
حدیث: اگر پاراگراف یک روایت یا حدیث از معصومین باشد
ارجاع: پاراگراف ارجاع به منابع و پاورقی بخشی از متن است
بدنه: اگر از انواع بالا نباشد
ساختار دقیق خروجی مورد انتظار باید لیستی از دیکشنری ها باشد که بازای هر پاراگراف تولید شده باشد و به صورت زیر باشد:
{
"paragraph_id": str,
"title": str,
"central_concepts": [
{
"concept": str,
"paragraph_effect": float
}
],
"paragraph_type": str,
"persons": [str],
"rules": [
{
"rule": str,
"type": "توصیفی" | "هنجاری"
}
]
}
"""
USER_PROMPT = '''
متن زیر را بر اساس دستورالعملهای سیستمی تحلیل کن و خروجی را در قالب دیکشنری پایتون ارائه بده:
## ساختار جیسون برای تحلیل:
'''
def get_key():
key = 'aa-Fu5oeQv8jx8NCWV39WenJ7Yy1mbcFJ4P20CLQURkql2Eleta' # nahj key
return key
def get_client():
url = "https://api.avalapis.ir/v1" #"https://api.avalai.ir/v1"
client = OpenAI(
api_key=get_key(),
base_url=url,
)
return client
def llm_request(text, model="gemini-2.5-flash-lite"):
# print(f'using model: {model}')
try:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"{USER_PROMPT}\n{text}"}]
response = client.chat.completions.create(
messages=messages,
model=model,
)
answer = response.choices[0].message.content
# messages.append({"role": "assistant", "content": answer})
except Exception as error:
with open(llm_error_path, mode='a+', encoding='utf-8') as file:
error_message = f'\n\ntext: {str(text)}\nerror:{error} \n-------------------------------\n'
file.write(error_message)
return 'Ooops ... Error!'
return answer
def text_to_dict(text: str) -> Dict[str, Any]:
text = text.replace('\n','')
text = text.lstrip('```json')
text = text.lstrip('json')
text = text.lstrip('```python')
text = text.rstrip('```')
text = text.strip()
try:
return json.loads(text)
except (json.JSONDecodeError, TypeError):
return ast.literal_eval(text)
client = get_client()
models = [ "gemini-2.5-flash-lite", "gpt-4o-mini","deepseek-reasoner"]
date = str((datetime.datetime.now())).replace(' ','-').replace(':','').replace('.','-')
def find_passed_data_ids(output_metadata_jsonl_path):
passed_data_ids = []
with open(output_metadata_jsonl_path, 'r', encoding='utf-8') as file:
passed_data = file.readlines()
for pd in passed_data:
passed_data_ids.append(str(json.loads(pd)['id']))
return passed_data_ids
if __name__ == "__main__":
input_data_path = './nahj_data/all_nahj_CONTEXT.json' # شامل تمامی (خطبه و نامه و حکمت ها) به همراه پاراگراف هایشان
llm_error_path = './nahj-answer/error-in-getting-metadata-Final.txt'
previous_peroid_errors_path = "./nahj_data/error-ids-Final.txt"
current_peroid_errors_path = "./nahj_data/error-ids3-Final.txt"
output_metadata_jsonl_path = './nahj_data/nahj-metadata-jsonline.json'
output_metadata_json_path = './nahj_data/nahj-metadata.json'
with open(input_data_path, 'r', encoding='utf-8') as file:
data = json.load(file)
passed_data_ids = []
passed_data_ids = find_passed_data_ids(output_metadata_jsonl_path)
failed_ids = []
with open(previous_peroid_errors_path, "r", encoding="utf-8") as f:
failed = f.read()
failed_ids = failed.splitlines()
start = (datetime.datetime.now())
print(f'start: {start}')
# len_pars = 0
# for item in data:
# len_pars += len(item['paragraphs'])
error_ids = []
test_enteries = []
all_paragraphs = 0
period = 1
end = False
while True:
print(f"******* PERIOD :: {period} *******")
for index ,entery in enumerate(data, 1):
if index > 10:
end = True
break
id = entery['id']
# خارج کردن داده هایی که قبلا کرول شده
if str(id) in passed_data_ids:
continue
# برای دور دوم به بعد که برخی از شناسه ها به خطر خورده ، شرط زیر از کامنت خارج شود
# if not str(id) in failed_ids:
# continue
# if not id == 27793
# continue
print(f'id: {id} - record: {index}/{len(data)} - period: {period}')
llm_answer_data = ''
new_entry = {}
new_paragraphs = []
new_entry['id'] = id
# new_entry['keywords'] = entery['keywords']
paragraphs = entery["paragraphs"]
for p in paragraphs:
large_title = p['large_title']
new_paragraphs.append({
'paragraph_id': p['paragraph_id'],
'text': p['text']
# 'text': f"بخشی از {large_title} : {p['text'] }"
})
new_entry['paragraphs'] = new_paragraphs
try:
result_data = llm_request(new_entry)#gpt-4o
llm_answer_data = text_to_dict(result_data)
except Exception as e:
print(f'error id: {id} - {e} >> llm result: {result_data}')
# error_ids.append(id)
with open(current_peroid_errors_path, "a", encoding="utf-8") as f:
f.write(f"{id}\n")
continue
entery['paragraph_metadata'] = llm_answer_data
test_enteries.append(entery)
with open(output_metadata_jsonl_path, 'a', encoding='utf-8') as f:
json.dump(entery, f, ensure_ascii=False)
f.write('\n')
time.sleep(1)
passed_data_ids = find_passed_data_ids(output_metadata_jsonl_path)
if len(data) == len(passed_data_ids):
print('ALL DATA PASSED OK!')
break
print(f'##### period result: passd {len(passed_data_ids)}/{len(data)} #####')
period+= 1
if end == True:
break
# with open(f'./leader_data/leader-metadata-bayanat-{id}.json', mode='w', encoding='utf-8') as file:
with open(output_metadata_json_path, mode='w', encoding='utf-8') as file:
result_message = json.dump(test_enteries, file, ensure_ascii=False, indent=2)
print('all done!')
print('---------------------------------------------')
print(f'full duration: {(datetime.datetime.now() - start).total_seconds()}')
print(f'all_paragraphs: {all_paragraphs}')
print('---------------------------------------------')

13
requirements.txt Normal file
View File

@ -0,0 +1,13 @@
cleantext==1.1.4
faiss==1.5.3
hazm==0.12.1
langchain_openai==1.2.1
numpy==2.4.4
openai==2.33.0
pandas==3.0.2
pydantic==2.13.3
Requests==2.33.1
scikit_learn==1.8.0
sentence_transformers==5.3.0
torch==2.11.0
transformers==5.6.1