add new source
This commit is contained in:
parent
beb29cc01e
commit
a11eb348bf
|
|
@ -5,9 +5,9 @@ import os
|
|||
|
||||
def create_faiss_index_from_json():
|
||||
# مسیر فایلها
|
||||
json_file_path = './nahj_data/nahj_vectors_multilingual-e5-small.json'
|
||||
faiss_index_path = './data-faiss/faiss_index_multilingual-e5-small.index'
|
||||
metadata_file_path = './data-faiss/faiss_index_nahj_metadata_multilingual-e5-small.json'
|
||||
json_file_path = './nahj_data/nahj_vectors_jina-embeddings-v5-text-small.json'
|
||||
faiss_index_path = './data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index'
|
||||
metadata_file_path = './data-faiss/faiss_index_nahj_metadata_jina-embeddings-v5-text-small.json'
|
||||
|
||||
# --- 1. بارگذاری دادهها از JSON ---
|
||||
with open(json_file_path, 'r', encoding='utf-8') as f:
|
||||
|
|
|
|||
|
|
@ -28,10 +28,29 @@ import os
|
|||
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"
|
||||
# sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
# BAAI/bge-m3
|
||||
# intfloat/multilingual-e5-small
|
||||
# jinaai/jina-embeddings-v5-text-small
|
||||
|
||||
# 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_bge_m3.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"
|
||||
|
||||
|
||||
normal_embedder = True
|
||||
conn = sqlite3.connect('./db/nahj.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
|
@ -109,7 +129,15 @@ class HybridRetrieverReranker:
|
|||
|
||||
# --- Dense Embedder ---
|
||||
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.save(MODEL_PATH)
|
||||
|
||||
|
|
@ -267,18 +295,14 @@ class HybridRetrieverReranker:
|
|||
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):
|
||||
def single_query(query: str , samples_number):
|
||||
|
||||
query = cleaning(query)
|
||||
|
||||
# تنظیم 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 = []
|
||||
final_similars_text = ''
|
||||
|
|
@ -323,9 +347,9 @@ def get_passages_by_paragraphs(retrived_sections_list):
|
|||
return final_passages
|
||||
|
||||
# متد اصلی جست و جو
|
||||
def bale_search(query):
|
||||
def bale_search(query,samples_number=10):
|
||||
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)
|
||||
|
||||
end_retrive = datetime.datetime.now()
|
||||
|
|
@ -342,36 +366,71 @@ def bale_search(query):
|
|||
return id_list.split("\n")
|
||||
|
||||
|
||||
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)
|
||||
final_result = ''
|
||||
|
||||
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
|
||||
for option in option_list:
|
||||
|
||||
if find == True :
|
||||
true_n += 1
|
||||
s.append(find)
|
||||
s[0] = int(s[0])
|
||||
final_answers_and_questions.append(s)
|
||||
# پرینت کردن اینکه از تعداد سوالات پرسیده شده به چندتای اونها جواب صحیح داده شده
|
||||
print(f"findes speechs number: {true_n}/{len(answers_and_questions)}")
|
||||
if option['normal_embedder'] == False:
|
||||
normal_embedder = False
|
||||
else :
|
||||
normal_embedder = True
|
||||
|
||||
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)
|
||||
|
||||
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
118
convert_parts_to_context.py
Normal 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
105
convert_sentence_to_part.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
BIN
data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index
Normal file
BIN
data-faiss/faiss_index_nahj_jina-embeddings-v5-text-small.index
Normal file
Binary file not shown.
19432
nahj_data/all_nahj_CONTEXT.json
Normal file
19432
nahj_data/all_nahj_CONTEXT.json
Normal file
File diff suppressed because one or more lines are too long
20022
nahj_data/all_nahj_parts.json
Normal file
20022
nahj_data/all_nahj_parts.json
Normal file
File diff suppressed because one or more lines are too long
0
nahj_data/error-ids-Final.txt
Normal file
0
nahj_data/error-ids-Final.txt
Normal file
2927
nahj_data/nahj-metadata-copy(2).json
Normal file
2927
nahj_data/nahj-metadata-copy(2).json
Normal file
File diff suppressed because it is too large
Load Diff
3088
nahj_data/nahj-metadata-copy.json
Normal file
3088
nahj_data/nahj-metadata-copy.json
Normal file
File diff suppressed because it is too large
Load Diff
10
nahj_data/nahj-metadata-jsonline.json
Normal file
10
nahj_data/nahj-metadata-jsonline.json
Normal file
File diff suppressed because one or more lines are too long
4374
nahj_data/nahj-metadata-title(2).json
Normal file
4374
nahj_data/nahj-metadata-title(2).json
Normal file
File diff suppressed because it is too large
Load Diff
3821
nahj_data/nahj-metadata.json
Normal file
3821
nahj_data/nahj-metadata.json
Normal file
File diff suppressed because it is too large
Load Diff
3718
nahj_data/nahj-metadata_title(1).json
Normal file
3718
nahj_data/nahj-metadata_title(1).json
Normal file
File diff suppressed because it is too large
Load Diff
3931859
nahj_data/nahj_vectors_jina-embeddings-v5-text-small.json
Normal file
3931859
nahj_data/nahj_vectors_jina-embeddings-v5-text-small.json
Normal file
File diff suppressed because one or more lines are too long
319
nahj_get_metadata_v2.py
Normal file
319
nahj_get_metadata_v2.py
Normal 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
13
requirements.txt
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user