گراف بازیافت بڑھا ہوا جنریشن ( GraphRAG ) رفتار پکڑ رہا ہے اور روایتی ویکٹر تلاش کی بازیافت کے طریقوں میں ایک طاقتور اضافہ بن رہا ہے۔ یہ نقطہ نظر گراف ڈیٹا بیس کی ساختی نوعیت کا فائدہ اٹھاتا ہے، جو ڈیٹا کو نوڈس اور تعلقات کے طور پر منظم کرتے ہیں، تاکہ بازیافت کی گئی معلومات کی گہرائی اور سیاق و سباق کو بڑھایا جا سکے۔
گرافس متنوع ڈیٹا کی اقسام میں پیچیدہ تعلقات اور صفات کو آسانی سے گرفت میں لے کر، متنوع اور باہم جڑی ہوئی معلومات کو منظم انداز میں پیش کرنے اور ذخیرہ کرنے میں بہترین ہیں۔ اس کے برعکس، ویکٹر ڈیٹا بیس اکثر ایسی ساختی معلومات کے ساتھ جدوجہد کرتے ہیں، کیونکہ ان کی طاقت اعلیٰ جہتی ویکٹرز کے ذریعے غیر ساختہ ڈیٹا کو سنبھالنے میں مضمر ہے۔ آپ کی RAG ایپلیکیشن میں، آپ دونوں جہانوں میں بہترین حاصل کرنے کے لیے غیر ساختہ متن کے ذریعے ویکٹر کی تلاش کے ساتھ سٹرکچرڈ گراف ڈیٹا کو یکجا کر سکتے ہیں۔ ہم اس بلاگ پوسٹ میں یہی ظاہر کریں گے۔
علم کا گراف بنانا عام طور پر سب سے مشکل مرحلہ ہوتا ہے۔ اس میں ڈیٹا اکٹھا کرنا اور اس کی تشکیل کرنا شامل ہے، جس کے لیے ڈومین اور گراف ماڈلنگ دونوں کی گہری سمجھ کی ضرورت ہوتی ہے۔
اس عمل کو آسان بنانے کے لیے، ہم LLMs کے ساتھ تجربہ کر رہے ہیں۔ زبان اور سیاق و سباق کی اپنی گہری سمجھ کے ساتھ، LLMs نالج گراف بنانے کے عمل کے اہم حصوں کو خودکار کر سکتے ہیں۔ ٹیکسٹ ڈیٹا کا تجزیہ کرکے، یہ ماڈل اداروں کی شناخت کر سکتے ہیں، ان کے تعلقات کو سمجھ سکتے ہیں، اور تجویز کر سکتے ہیں کہ گراف ڈھانچے میں ان کی بہترین نمائندگی کیسے کی جا سکتی ہے۔
ان تجربات کے نتیجے میں، ہم نے گراف کی تعمیر کے ماڈیول کا پہلا ورژن LangChain میں شامل کیا ہے، جسے ہم اس بلاگ پوسٹ میں ظاہر کریں گے۔
کوڈ GitHub پر دستیاب ہے۔
آپ کو Neo4j مثال قائم کرنے کی ضرورت ہے۔ اس بلاگ پوسٹ میں مثالوں کے ساتھ عمل کریں۔ سب سے آسان طریقہ Neo4j Aura پر ایک مفت مثال شروع کرنا ہے، جو Neo4j ڈیٹا بیس کے کلاؤڈ انسٹینس پیش کرتا ہے۔ متبادل طور پر، آپ Neo4j ڈیسک ٹاپ ایپلیکیشن ڈاؤن لوڈ کرکے اور مقامی ڈیٹا بیس کی مثال بنا کر Neo4j ڈیٹابیس کی مقامی مثال بھی ترتیب دے سکتے ہیں۔
os.environ["OPENAI_API_KEY"] = "sk-" os.environ["NEO4J_URI"] = "bolt://localhost:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "password" graph = Neo4jGraph()
اس کے علاوہ، آپ کو ایک OpenAI کلید فراہم کرنا ضروری ہے، کیونکہ ہم اس بلاگ پوسٹ میں ان کے ماڈل استعمال کریں گے۔
اس مظاہرے کے لیے، ہم الزبتھ اول کا ویکیپیڈیا صفحہ استعمال کریں گے۔ ہم بغیر کسی رکاوٹ کے ویکیپیڈیا سے دستاویزات لانے اور تقسیم کرنے کے لیے LangChain لوڈرز کا استعمال کر سکتے ہیں۔
# Read the wikipedia article raw_documents = WikipediaLoader(query="Elizabeth I").load() # Define chunking strategy text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=24) documents = text_splitter.split_documents(raw_documents[:3])
بازیافت شدہ دستاویزات کی بنیاد پر گراف بنانے کا وقت آگیا ہے۔ اس مقصد کے لیے، ہم نے ایک LLMGraphTransformermodule نافذ کیا ہے جو گراف ڈیٹا بیس میں علمی گراف کی تعمیر اور ذخیرہ کرنے کو نمایاں طور پر آسان بناتا ہے۔
llm=ChatOpenAI(temperature=0, model_name="gpt-4-0125-preview") llm_transformer = LLMGraphTransformer(llm=llm) # Extract graph data graph_documents = llm_transformer.convert_to_graph_documents(documents) # Store to neo4j graph.add_graph_documents( graph_documents, baseEntityLabel=True, include_source=True )
آپ اس بات کی وضاحت کر سکتے ہیں کہ آپ کس LLM کو نالج گراف جنریشن چین استعمال کرنا چاہتے ہیں۔ فی الحال، ہم OpenAI اور Mistral سے صرف فنکشن کالنگ ماڈلز کو سپورٹ کرتے ہیں۔ تاہم، ہم مستقبل میں LLM انتخاب کو بڑھانے کا ارادہ رکھتے ہیں۔ اس مثال میں، ہم جدید ترین GPT-4 استعمال کر رہے ہیں۔ نوٹ کریں کہ تیار کردہ گراف کا معیار نمایاں طور پر اس ماڈل پر منحصر ہے جسے آپ استعمال کر رہے ہیں۔ نظریہ میں، آپ ہمیشہ سب سے زیادہ قابل استعمال کرنا چاہتے ہیں۔ LLM گراف ٹرانسفارمرز گراف دستاویزات واپس کرتے ہیں، جنہیں add_graph_documents طریقہ کے ذریعے Neo4j میں درآمد کیا جا سکتا ہے۔ baseEntityLabel پیرامیٹر ایک اضافی تفویض کرتا ہے۔
آپ Neo4j براؤزر میں تیار کردہ گراف کا معائنہ کر سکتے ہیں۔
نوٹ کریں کہ یہ تصویر تیار کردہ گراف کے صرف ایک حصے کی نمائندگی کرتی ہے۔
گراف جنریشن کے بعد، ہم ایک ہائبرڈ بازیافت کا طریقہ استعمال کریں گے جو RAG ایپلی کیشنز کے لیے گراف کی بازیافت کے ساتھ ویکٹر اور کلیدی الفاظ کے اشاریہ جات کو یکجا کرتا ہے۔
خاکہ بازیافت کے عمل کی وضاحت کرتا ہے جس کا آغاز ایک صارف کے سوال کے ساتھ ہوتا ہے، جسے پھر RAG بازیافت کرنے والے کو بھیج دیا جاتا ہے۔ یہ ریٹریور غیر ساختہ ٹیکسٹ ڈیٹا کے ذریعے تلاش کرنے کے لیے کلیدی الفاظ اور ویکٹر کی تلاش کا استعمال کرتا ہے اور اسے معلومات کے گراف سے جمع کردہ معلومات کے ساتھ جوڑتا ہے۔ چونکہ Neo4j میں کلیدی الفاظ اور ویکٹر انڈیکس دونوں شامل ہیں، اس لیے آپ ایک ہی ڈیٹا بیس سسٹم کے ساتھ بازیافت کے تینوں اختیارات کو نافذ کر سکتے ہیں۔ ان ذرائع سے جمع کردہ ڈیٹا کو حتمی جواب تیار کرنے اور فراہم کرنے کے لیے LLM میں فیڈ کیا جاتا ہے۔
آپ دستاویزات میں مطلوبہ الفاظ اور ویکٹر کی بازیافت دونوں کو شامل کرنے کے لیے Neo4jVector.from_existing_graph طریقہ استعمال کر سکتے ہیں۔ یہ طریقہ مطلوبہ الفاظ اور ویکٹر کی تلاش کے اشاریہ جات کو ہائبرڈ تلاش کے نقطہ نظر کے لیے ترتیب دیتا ہے، دستاویز کے لیبل والے نوڈس کو نشانہ بناتا ہے۔ مزید برآں، یہ ٹیکسٹ ایمبیڈنگ اقدار کا حساب لگاتا ہے اگر وہ غائب ہیں۔
vector_index = Neo4jVector.from_existing_graph( OpenAIEmbeddings(), search_type="hybrid", node_label="Document", text_node_properties=["text"], embedding_node_property="embedding" )
اس کے بعد ویکٹر انڈیکس کو مماثلت_تلاش کے طریقہ سے بلایا جا سکتا ہے۔
دوسری طرف، گراف کی بازیافت کو ترتیب دینا زیادہ شامل ہے لیکن زیادہ آزادی فراہم کرتا ہے۔ یہ مثال متعلقہ نوڈس کی شناخت کرنے اور ان کے براہ راست پڑوس کو واپس کرنے کے لیے ایک مکمل ٹیکسٹ انڈیکس کا استعمال کرے گی۔
گراف کی بازیافت ان پٹ میں متعلقہ اداروں کی شناخت سے شروع ہوتی ہے۔ سادگی کے لیے، ہم LLM کو لوگوں، تنظیموں اور مقامات کی شناخت کرنے کی ہدایت کرتے ہیں۔ اسے حاصل کرنے کے لیے، ہم اسے حاصل کرنے کے لیے نئے شامل کیے گئے with_structured_output طریقہ کے ساتھ LCEL استعمال کریں گے۔
# Extract entities from text class Entities(BaseModel): """Identifying information about entities.""" names: List[str] = Field( ..., description="All the person, organization, or business entities that " "appear in the text", ) prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are extracting organization and person entities from the text.", ), ( "human", "Use the given format to extract information from the following " "input: {question}", ), ] ) entity_chain = prompt | llm.with_structured_output(Entities)
آئیے اس کی جانچ کریں:
entity_chain.invoke({"question": "Where was Amelia Earhart born?"}).names # ['Amelia Earhart']
بہت اچھا، اب جب کہ ہم سوال میں موجود ہستیوں کا پتہ لگا سکتے ہیں، آئیے علمی گراف پر نقشہ بنانے کے لیے ایک مکمل متن کا انڈیکس استعمال کریں۔ سب سے پہلے، ہمیں ایک مکمل ٹیکسٹ انڈیکس اور ایک فنکشن کی وضاحت کرنے کی ضرورت ہے جو مکمل متن کے سوالات پیدا کرے گا جو تھوڑا سا غلط ہجے کی اجازت دیتا ہے، جس کی ہم یہاں زیادہ تفصیل میں نہیں جائیں گے۔
graph.query( "CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]") def generate_full_text_query(input: str) -> str: """ Generate a full-text search query for a given input string. This function constructs a query string suitable for a full-text search. It processes the input string by splitting it into words and appending a similarity threshold (~2 changed characters) to each word, then combines them using the AND operator. Useful for mapping entities from user questions to database values, and allows for some misspelings. """ full_text_query = "" words = [el for el in remove_lucene_chars(input).split() if el] for word in words[:-1]: full_text_query += f" {word}~2 AND" full_text_query += f" {words[-1]}~2" return full_text_query.strip()
آئیے اب یہ سب ایک ساتھ رکھیں۔
# Fulltext index query def structured_retriever(question: str) -> str: """ Collects the neighborhood of entities mentioned in the question """ result = "" entities = entity_chain.invoke({"question": question}) for entity in entities.names: response = graph.query( """CALL db.index.fulltext.queryNodes('entity', $query, {limit:2}) YIELD node,score CALL { MATCH (node)-[r:!MENTIONS]->(neighbor) RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS output UNION MATCH (node)<-[r:!MENTIONS]-(neighbor) RETURN neighbor.id + ' - ' + type(r) + ' -> ' + node.id AS output } RETURN output LIMIT 50 """, {"query": generate_full_text_query(entity)}, ) result += "\n".join([el['output'] for el in response]) return result
اسٹرکچرڈ_ریٹریور فنکشن صارف کے سوال میں موجود اداروں کا پتہ لگانے سے شروع ہوتا ہے۔ اگلا، یہ پتہ چلنے والے اداروں پر اعادہ کرتا ہے اور متعلقہ نوڈس کے پڑوس کو بازیافت کرنے کے لیے سائفر ٹیمپلیٹ کا استعمال کرتا ہے۔ آئیے اس کی جانچ کریں!
print(structured_retriever("Who is Elizabeth I?")) # Elizabeth I - BORN_ON -> 7 September 1533 # Elizabeth I - DIED_ON -> 24 March 1603 # Elizabeth I - TITLE_HELD_FROM -> Queen Of England And Ireland # Elizabeth I - TITLE_HELD_UNTIL -> 17 November 1558 # Elizabeth I - MEMBER_OF -> House Of Tudor # Elizabeth I - CHILD_OF -> Henry Viii # and more...
جیسا کہ شروع میں ذکر کیا گیا ہے، ہم ایل ایل ایم کو دیے گئے حتمی سیاق و سباق کو تخلیق کرنے کے لیے غیر ساختہ اور گراف بازیافت کو یکجا کریں گے۔
def retriever(question: str): print(f"Search query: {question}") structured_data = structured_retriever(question) unstructured_data = [el.page_content for el in vector_index.similarity_search(question)] final_data = f"""Structured data: {structured_data} Unstructured data: {"#Document ". join(unstructured_data)} """ return final_data
جیسا کہ ہم Python کے ساتھ کام کر رہے ہیں، ہم صرف f-string کا استعمال کرتے ہوئے آؤٹ پٹ کو جوڑ سکتے ہیں۔
ہم نے RAG کے بازیافت جزو کو کامیابی کے ساتھ نافذ کیا ہے۔ اگلا، ہم ایک پرامپٹ متعارف کراتے ہیں جو RAG چین کے نفاذ کو مکمل کرتے ہوئے، ردعمل پیدا کرنے کے لیے مربوط ہائبرڈ ریٹریور کے فراہم کردہ سیاق و سباق کا فائدہ اٹھاتا ہے۔
template = """Answer the question based only on the following context: {context} Question: {question} """ prompt = ChatPromptTemplate.from_template(template) chain = ( RunnableParallel( { "context": _search_query | retriever, "question": RunnablePassthrough(), } ) | prompt | llm | StrOutputParser() )
آخر میں، ہم آگے بڑھ سکتے ہیں اور اپنے ہائبرڈ RAG کے نفاذ کی جانچ کر سکتے ہیں۔
chain.invoke({"question": "Which house did Elizabeth I belong to?"}) # Search query: Which house did Elizabeth I belong to? # 'Elizabeth I belonged to the House of Tudor.'
میں نے ایک سوال کو دوبارہ لکھنے کی خصوصیت بھی شامل کی ہے، جس سے RAG چین کو بات چیت کی ترتیبات کے مطابق ڈھالنے کے قابل بنایا گیا ہے جو فالو اپ سوالات کی اجازت دیتی ہیں۔ یہ دیکھتے ہوئے کہ ہم ویکٹر اور مطلوبہ الفاظ کی تلاش کے طریقے استعمال کرتے ہیں، ہمیں اپنے تلاش کے عمل کو بہتر بنانے کے لیے فالو اپ سوالات کو دوبارہ لکھنا چاہیے۔
chain.invoke( { "question": "When was she born?", "chat_history": [("Which house did Elizabeth I belong to?", "House Of Tudor")], } ) # Search query: When was Elizabeth I born? # 'Elizabeth I was born on 7 September 1533.'
آپ دیکھ سکتے ہیں کہ وہ کب پیدا ہوئی؟ پہلی بار دوبارہ لکھا گیا الزبتھ I کب پیدا ہوئی؟ . پھر سے لکھی گئی استفسار کو متعلقہ سیاق و سباق کی بازیافت اور سوال کا جواب دینے کے لیے استعمال کیا گیا۔
LLMGraphTransformer کے متعارف ہونے کے ساتھ، نالج گرافس تیار کرنے کا عمل اب ہموار اور زیادہ قابل رسائی ہونا چاہیے، جس سے ہر اس شخص کے لیے آسان ہو جائے گا جو اپنی RAG ایپلی کیشنز کو اس گہرائی اور سیاق و سباق کے ساتھ بڑھانا چاہتے ہیں جو علمی گراف فراہم کرتے ہیں۔ یہ صرف ایک آغاز ہے کیونکہ ہمارے پاس بہت ساری بہتری کی منصوبہ بندی ہے۔
اگر آپ کے پاس LLMs کے ساتھ ہمارے تخلیق کرنے والے گراف کے بارے میں بصیرت، تجاویز یا سوالات ہیں، تو براہ کرم رابطہ کرنے میں ہچکچاہٹ محسوس نہ کریں۔
کوڈ پر دستیاب ہے۔