تنفيذ RAG الأساسي: دليل تعليمي خطوة بخطوة

يشرح هذا الدليل التعليمي المناسب للمبتدئين كيفية عمل تنفيذ RAG الأساسي، ويستعرض أفضل الممارسات، ويرشدك خطوة بخطوة إلى بناء نظام RAG يعمل فعليًا عبر سطر الأوامر باستخدام Python وChromaDB ونماذج التضمين واللغة من Perplexity.

Basic RAG implementation process diagram: the interface sends input to the orchestrator, which retrieves data from a vector database, builds the input context for the LLM, and returns the output to the interface.

يُعدّ التوليد المعزَّز بالاسترجاع (RAG) مكوّنًا أساسيًا في البنية التحتية الحديثة القائمة على نماذج اللغة الكبيرة (LLM). فمن خلال تمكين نماذج الذكاء الاصطناعي من الوصول إلى بيانات خارجية، يساعد على معالجة مشكلات مثل الهلوسة ونقص بيانات التدريب. كما يتيح للشركات الاستفادة من قواعد المعرفة الداخلية التي تحتوي على معلومات حساسة أو مملوكة لها.

قد يكون تنفيذ RAG معقدًا للغاية، لكن بناء نظام RAG أساسي أسهل مما قد تتوقع. وبالنسبة إلى المبتدئين، يمكن لإنشاء بنية بسيطة أن يوضّح كيفية عمل RAG على أرض الواقع، وأن يوفّر نقطة انطلاق لمزيد من التجريب.

في هذه المقالة، سنرسم مخططًا لتنفيذ RAG أساسي، ونستعرض أفضل الممارسات، ثم نبني نظامًا يعمل فعليًا باستخدام Python وChromaDB ونموذجين من Perplexity لإنشاء التضمينات وتوليد الإجابات.

عملية تنفيذ RAG الأساسي: نظرة عامة

يتألف نظام RAG الأساسي من ثلاثة مكونات رئيسية: منسِّق، ومصدر معلومات، ونموذج لغة كبير (LLM). وفي سير العمل المعتاد، يُدخل المستخدم موجّهًا، فيعدّله المنسِّق للبحث في أي مصادر معلومات متصلة. ثم تُضاف المعلومات المسترجعة إلى سياق الإدخال الأصلي وتُرسَل إلى نموذج اللغة الكبير لتوليد الرد.

ستحتاج إلى مراعاة الخطوات التالية عند بناء تنفيذ RAG الخاص بك:

  1. الفهرسة والتضمين: في هذه المرحلة، تُحوَّل مستنداتك إلى مصدر معلومات يمكن للمنسِّق البحث فيه. ومن أكثر الطرق شيوعًا لتحقيق ذلك بناء فهرس متجهات قابل للبحث. سنستخدم نموذجًا مخصصًا من Perplexity لتحويل أجزاء مستنداتك إلى تضمينات، ثم نخزّن هذه التضمينات في ChromaDB، وهي قاعدة بيانات متجهات شائعة ومفتوحة المصدر.
  2. التنسيق: هذا هو التطبيق الذي يقع بين مصدر المعلومات الخارجي (قاعدة بيانات متجهات في حالتنا) ونموذج اللغة الكبير. سننشئ برنامجًا نصيًا بسيطًا بلغة Python ليؤدي دور المنسِّق.
  3. الاسترجاع والتوليد: عندما يطرح المستخدم سؤالًا عبر واجهة الدردشة، يتلقّاه المنسِّق، ويُجري بحثًا متجهيًا، ثم يمرّر الإدخال الموسَّع إلى نموذج اللغة الكبير. وسنستخدم سطر الأوامر للتواصل مع المنسِّق المكتوب بلغة Python بدلًا من بناء واجهة أمامية مخصصة.

أفضل الممارسات: ما الذي يجعل تنفيذ RAG فعّالًا؟

توجد أنواع متعددة من بنى RAG، وتتطلب كل حالة استخدام نهجًا مختلفًا. ومع ذلك، تنطبق مجموعة من أفضل الممارسات في جميع الحالات، وتضمن أنظمة دقيقة وفعّالة من حيث التكلفة وقابلة للتوسّع وسهلة الصيانة.

تنطبق أفضل الممارسات التالية على جميع أنواع تنفيذ RAG:

  • احرص على وجود مسار نظيف لاستيعاب البيانات: ينبغي أن تكون بيانات المصدر نظيفة قدر الإمكان قبل الفهرسة. كما ينبغي استخدام نموذج تضمين يناسب المحتوى الخاص بك.
  • استخدم البحث الهجين: تختلف أفضل طرق الاسترجاع باختلاف حالة الاستخدام، سواء أكانت بحثًا في قاعدة بيانات متجهات، أم بحثًا على الويب عبر API، أم تنقّلًا في رسم بياني معرفي، أم بحثًا بالكلمات المفتاحية، أم غير ذلك من الطرق. ويُعد اختيار المزيج المناسب أمرًا مهمًا لتقليل التكاليف.
  • قيّم نظامك: بعد التنفيذ، ينبغي تقييم النظام وفق ثلاثة مقاييس أساسية: الدقة، والملاءمة، وزمن الاستجابة. تشير الدقة إلى صحة المعلومات، والملاءمة إلى مدى انطباق المعلومات المسترجعة على استفسارات المستخدمين، وزمن الاستجابة إلى السرعة التي تُسترجَع بها المعلومات.
  • تعامل مع المعلومات بأمان: إذا كان نظام RAG الخاص بك يعتمد على معلومات داخلية خاصة، فتأكد من تخزينها بأمان ومن إدارة الوصول إلى واجهة النظام. ولا ينبغي مشاركة مفاتيح API أبدًا.

تنفيذ RAG الأساسي: دليل تعليمي من 7 خطوات

في هذا الدليل التعليمي، سنبني تطبيق سطر أوامر يجيب عن الأسئلة المتعلقة بمستند واحد، وسنستخدم Python وChromaDB وPerplexity API.

لا يهدف هذا الدليل إلى بناء نظام RAG جاهز لبيئة الإنتاج، بل هو دليل تعليمي مناسب للمبتدئين، صُمّم لتوضيح كيفية عمل بنية RAG البسيطة من الناحية العملية.

الخطوة 1: تثبيت البرامج

تأكد من تثبيت ما يلي على جهاز الكمبيوتر، ومن أن لديك حسابًا في Perplexity API:

  • Python: هذه هي لغة البرمجة التي سنستخدمها لبناء المنسِّق الذي سيتولى تنسيق تدفق البيانات بين نموذج التضمين وChromaDB وPerplexity.
  • Visual Studio: سنستخدم Visual Studio محررًا للتعليمات البرمجية. بعد تثبيته، افتح لوحة Extensions ونزّل ملحق Python.
  • حساب Perplexity API: سيوفّر Perplexity كلًّا من نموذج التضمين (الذي يحوّل بياناتك إلى تضمينات متجهية) ونموذج اللغة الكبير الذي سيعالج الموجّه الموسَّع. لا تحتاج إلى حساب مدفوع لتنفيذ ذلك، لكن ستُخصم تكلفة بسيطة من رصيدك.

الخطوة 2: إنشاء مجلد للمشروع

ابدأ بإنشاء مجلد باسم rag-project، ثم افتحه في VS Code بتحديد File > Open Folder. بعد ذلك، افتح الوحدة الطرفية المدمجة عبر Terminal > New Terminal.

أدخل ما يلي لإنشاء بيئة Python افتراضية:

bash
py -3.12 -m venv .venv

تحتاج الآن إلى تثبيت أربع مكتبات Python: perplexityai وchromadb وpython-dotenv وnumpy. ستتيح لك حزمة Perplexity SDK الاتصال بنماذج Perplexity عبر API. أما ChromaDB فتبني قاعدة بيانات المتجهات وستعمل محليًا. وتحمّل Python-dotenv مفاتيح API بأمان من ملف محلي، بينما تلزم NumPy أثناء عملية الاستيعاب.

ثبّتها باستخدام الأمر التالي:

bash
.\.venv\Scripts\python.exe -m pip install perplexityai chromadb python-dotenv numpy

الخطوة 3: إعداد الملفات

أضف الملفات التالية إلى rag-project (جذر المشروع) بتحديد New File… في الشريط الجانبي Explorer:

  • index.py: هذا هو التطبيق الذي سيفهرس مستندك. ويمكنك إعادة تشغيله لإضافة مزيد من النصوص.
  • rag.py: هذا هو تطبيق Python الذي سيؤدي دور المنسِّق.
  • document.txt: سيحتوي هذا الملف على الوثائق التي ستحوّلها إلى قاعدة بيانات متجهات.
  • .env: في هذا الملف ستحتفظ بمفتاح Perplexity API الخاص بك.

أدخل ما يلي في .env:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

استبدل your_api_key_here بمفتاحك الفعلي. وللحصول عليه، انتقل إلى لوحة المعلومات الرئيسية في Perplexity API Console وحدّد Create an API key. واحرص على إبقاء مفتاح API هذا سريًا.

سنستخدم نموذجين: نموذج pplx-embed-v1-0.6b من Perplexity لتضمين الوثائق قبل رفعها إلى ChromaDB، ونموذج GPT-5.6-Luna (الذي نصل إليه عبر Perplexity API) لمعالجة المعلومات المسترجعة.

أضف في document.txt النص الذي سيسترجعه مسار RAG الخاص بك. وإذا لم يكن لديك مستند جاهز، فاستخدم ديوان وليام بليك Songs of Innocence and of Experience (أغاني البراءة والتجربة) من Project Gutenberg لهذا الدليل التعليمي، فهو نص قصير وسهل الفهم ويقع ضمن الملكية العامة.

الخطوة 4: كتابة برنامج الفهرسة

انسخ التعليمات البرمجية أدناه والصقها في index.py. ويقدّم كل عنوان فرعي شرحًا موجزًا لما يفعله الجزء التالي من التعليمات البرمجية. هذا هو البرنامج الذي سيحوّل المادة المصدرية إلى تضمينات متجهية ويخزّنها في قاعدة بيانات ChromaDB باستخدام نموذج التضمين pplx-embed-v1-0.6b من Perplexity ومكتبة NumPy.

إليك ما يفعله البرنامج:

  1. يفتح الملف document.txt.
  2. يقسّم النص إلى أجزاء متداخلة.
  3. يرسل هذه الأجزاء إلى نموذج التضمين pplx-embed-v1-0.6b من Perplexity.
  4. يفك ترميز المتجهات المُعادة ويُجري تطبيعها باستخدام NumPy، وهي مكتبة Python تضم دوال حسابية.
  5. يخزّن المتجهات والأجزاء الأصلية في قاعدة بيانات ChromaDB (سترى قاعدة البيانات تظهر في مجلد مشروعك).

أُعدّ البرنامج بحيث لا يعيد فهرسة مستندك في كل مرة تشغّله فيها. فإذا أضفت معلومات إلى document.txt، فلن يضمّن سوى النص الجديد.

python
from pathlib import Path from collections import defaultdict, deque from hashlib import sha256 import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and ChromaDB load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_or_create_collection( name="documents", metadata={"hnsw:space": "cosine"} ) # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Read the document and split it into chunks text = Path("document.txt").read_text(encoding="utf-8") words = text.split() chunk_size = 180 overlap = 30 step = chunk_size - overlap chunks = [" ".join(words[i:i + chunk_size]) for i in range(0, len(words), step)] if not chunks: raise ValueError("document.txt is empty.") # 3. Retrieve the chunks already stored in ChromaDB existing = collection.get(include=["documents"]) stored_chunks = defaultdict(deque) existing_ids = set(existing["ids"]) for chunk_id, document in zip(existing["ids"], existing["documents"]): stored_chunks[document].append(chunk_id) # 4. Identify unchanged and new chunks new_chunks = [] new_ids = [] kept_ids = set() unchanged = 0 for chunk in chunks: if stored_chunks[chunk]: chunk_id = stored_chunks[chunk].popleft() kept_ids.add(chunk_id) unchanged += 1 else: chunk_hash = sha256(chunk.encode("utf-8")).hexdigest() chunk_id = f"chunk-{chunk_hash}" suffix = 1 while chunk_id in existing_ids or chunk_id in kept_ids or chunk_id in new_ids: chunk_id = f"chunk-{chunk_hash}-{suffix}" suffix += 1 new_chunks.append(chunk) new_ids.append(chunk_id) kept_ids.add(chunk_id) # 5. Generate embeddings in batches of up to 100 chunks batch_size = 100 for i in range(0, len(new_chunks), batch_size): batch_chunks = new_chunks[i:i + batch_size] batch_ids = new_ids[i:i + batch_size] embeddings = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=batch_chunks, encoding_format="base64_int8" ) vectors = [decode_embedding(item.embedding) for item in embeddings.data] # 6. Store each batch in ChromaDB collection.upsert( ids=batch_ids, documents=batch_chunks, embeddings=vectors ) print(f"Indexed batch {(i // batch_size) + 1}") # 7. Remove outdated chunks old_ids = [chunk_id for chunk_id in existing["ids"] if chunk_id not in kept_ids] if old_ids: collection.delete(ids=old_ids) # 8. Display indexing results print("Indexing complete!") print("Unchanged chunks:", unchanged) print("New or modified chunks:", len(new_chunks)) print("Removed chunks:", len(old_ids)) print("Total stored chunks:", collection.count())

الخطوة 5: كتابة المنسِّق لنظام RAG

انسخ التعليمات البرمجية أدناه والصقها في rag.py. إنه تنفيذ RAG كامل لكنه مبسّط، يستخدم Perplexity SDK وإمكانات قاعدة بيانات المتجهات المحلية في ChromaDB.

إليك ما سيحدث عند تشغيل البرنامج:

  1. يتصل بـ Perplexity وبقاعدة بيانات ChromaDB الجديدة.
  2. يتحقق من احتواء قاعدة البيانات على مستندات مفهرسة.
  3. يستقبل سؤالًا من المستخدم.
  4. يرسل السؤال إلى نموذج التضمين من Perplexity.
  5. يفك ترميز التضمين الناتج ويُجري تطبيعه باستخدام NumPy.
  6. يبحث في ChromaDB عن أكثر ثلاثة أجزاء من المستند تشابهًا.
  7. ينشئ موجّهًا يتضمن الأجزاء المسترجعة وسؤال المستخدم.
  8. يرسل الموجّه إلى GPT-5.6 Luna عبر Agent API من Perplexity.
  9. يعرض الإجابة وينتظر سؤالًا آخر.
python
import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and the existing ChromaDB database load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_collection("documents") # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Check that the database contains indexed documents count = collection.count() if count == 0: raise ValueError("The database is empty. Run index.py first.") print("Connected to existing ChromaDB database.") print("Stored chunks:", count) # 3. Accept questions from the user while True: question = input("\nAsk a question (or type exit): ") if question.lower().strip() == "exit": break if not question.strip(): continue # 4. Convert the question into a Perplexity embedding question_response = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=[question], encoding_format="base64_int8" ) question_vector = decode_embedding(question_response.data[0].embedding) # 5. Retrieve the most similar document chunks from ChromaDB results = collection.query( query_embeddings=[question_vector], n_results=min(3, count) ) context = "\n\n".join(results["documents"][0]) # 6. Construct a prompt and send it to the fixed LLM response = ai.responses.create( model="openai/gpt-5.6-luna", instructions=( "Answer using only the supplied document excerpts. " "Do not use external information. " "If the answer is not present, say you don't know." ), input=f"Document excerpts:\n{context}\n\nQuestion: {question}" ) # 7. Display the answer print("\nAnswer:\n", response.output_text)

الخطوة 6: تشغيل التطبيقات

في الوحدة الطرفية PowerShell ضمن VS Code، أدخل ما يلي لتضمين المحتوى الموجود في document.txt:

bash
.\.venv\Scripts\python.exe index.py

إذا تمت فهرسة مستندك بنجاح، فمن المفترض أن ترى مخرجات مشابهة لما يلي:

plaintext
Indexing complete! Unchanged chunks: 0 New or modified chunks: 58 Removed chunks: 0 Total stored chunks: 58

حان الآن وقت تشغيل تطبيق المنسِّق الرئيسي. أدخل هذا الأمر:

bash
.\.venv\Scripts\python.exe rag.py

إذا عمل التطبيق بنجاح، فسترى ما يلي:

plaintext
Connected to existing ChromaDB database. Stored chunks: 58 Ask a question (or type exit):

يمكنك الآن طرح أسئلة تستند إلى قاعدة بيانات المتجهات التي أنشأتها، مثل:

plaintext
Ask a question (or type exit): What animals does William Blake talk about? Answer: The excerpts mention sheep, lambs, birds, beasts, wolves, tigers, and a lion.

الخطوة 7: فهم ما بنيته

يحتوي النظام الذي بنيته على المسارين الأساسيين الموجودين في كل بنية RAG: مسار الفهرسة، أو «الاستيعاب»، ومسار الاسترجاع والتوليد.

أصبح تنفيذ RAG الأساسي الخاص بك يؤدي الآن جميع ما يلي:

  • تخزين المتجهات: تُحفظ التضمينات المستندة إلى ملف المعرفة الخاص بك في قاعدة بيانات ChromaDB ضمن بيئتك المحلية.
  • الفهرسة التزايدية: لا يُعاد تضمين أجزاء المستند التي لم تتغير، ويمكنك تحديث المستند متى شئت.
  • الاسترجاع الدلالي: تسترجع ChromaDB الأجزاء استنادًا إلى التشابه المتجهي (بطلب من المنسِّق).
  • توليد إجابات مستندة إلى المصادر: يتلقى نموذج اللغة الكبير المقاطع المسترجعة بوصفها سياقًا ويدمجها في الإجابة.
  • فصل الفهرسة عن الاستعلام: يمكنك طرح الأسئلة دون إعادة معالجة النص الكامل الموجود في ملف المعرفة.
  • الاتصال بالنموذج عبر API: تُولَّد جميع الإجابات باستخدام GPT-5.6 Luna عبر Perplexity بدلًا من نموذج يعمل على جهازك.

الخطوات التالية: كيف تطوّر هذا التنفيذ أكثر

يهدف هذا الدليل التعليمي إلى بناء تنفيذ أساسي ومباشر، ويغطي بعض مكونات بنية RAG الأساسية الموجودة في الأنظمة الحديثة. وإذا أردت التعمق أكثر، فهناك طرق متعددة للبناء عليه.

إليك بعض الأفكار لتوسيع نطاق التنفيذ الخاص بك:

  • جرّب LangChain: يُعد LangChain أحد أشهر أُطر التنسيق مفتوحة المصدر، ويحتوي على مكونات جاهزة لبناء تطبيقات قادرة على تشغيل مسارات RAG معقدة.
  • وسّع وظائف البحث: اجمع بين البحث المتجهي الكثيف والبحث المعجمي (بالكلمات المفتاحية) مثل BM25. كما أن إضافة البحث على الويب عبر Perplexity Search API ستتيح لك إسناد الإجابات إلى بيانات حديثة.
  • أضف نموذجًا لإعادة الترتيب: ادمج نموذجًا لإعادة الترتيب يرتّب المقاطع المسترجعة وفق مدى صلتها بالموجّه الأولي، ويختار أكثرها صلة لتضمينها في سياق الإدخال المقدَّم إلى نموذج اللغة الكبير.
  • ابنِ نظام RAG تكيفيًا: يستخدم RAG التكيفي إما نموذج لغة كبيرًا أو مصنِّفًا (مكوّنًا داخل المنسِّق) لتحديد طريقة الاسترجاع المناسبة في نظام RAG معياري. ويُعد بناء نظام كهذا طريقة جيدة لاستكشاف تنفيذات RAG أكثر تطورًا.

على الرغم من أن كل هذا قد يبدو معقدًا، فضع في اعتبارك أن منصات الذكاء الاصطناعي الشائعة مثل Perplexity يمكنها تقديم مساعدة متقدمة في البرمجة. فهي ممتازة للمشاريع الشخصية والتجريب، ويمكن إبقاء تكاليفها منخفضة نسبيًا.

ملاحظة: يستخدم هذا الدليل التعليمي GPT-5.6 Luna. وقد يتغير توفر النماذج، لذا قد تحتاج إلى تحديث استدعاء API.