מימוש 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. הוא מאפשר למודלי AI לגשת לנתונים חיצוניים, ובכך מסייע להתמודד עם בעיות כמו הזיות ונתוני אימון חלקיים. בנוסף, בזכותו חברות יכולות להשתמש במאגרי ידע פנימיים המכילים מידע רגיש או קנייני.

מימוש RAG עשוי להיות מורכב מאוד. עם זאת, בניית מערכת RAG בסיסית קלה באופן מפתיע. עבור מתחילים, יצירת ארכיטקטורה פשוטה יכולה להמחיש כיצד RAG פועל בעולם האמיתי ולספק קרש קפיצה להתנסות נוספת.

במאמר זה נשרטט מימוש RAG בסיסי, נסקור שיטות עבודה מומלצות, ולאחר מכן נבנה מערכת עובדת באמצעות Python, ChromaDB ושני מודלים של Perplexity להטמעה וליצירת תשובות.

תהליך מימוש RAG בסיסי: סקירה כללית

למערכת RAG בסיסית יש שלושה רכיבים עיקריים: מתזמר (orchestrator), מקור מידע ו-LLM. בתהליך עבודה טיפוסי, המשתמש מזין הנחיה, והמתזמר מתאים אותה כדי לחפש בכל מקורות המידע המחוברים. לאחר מכן, המידע שאוחזר מתווסף להקשר הקלט המקורי ונשלח ל-LLM כדי שניתן יהיה ליצור תשובה.

בעת בניית מימוש ה-RAG שלכם, תצטרכו להביא בחשבון את השלבים הבאים:

  1. יצירת אינדקס והטמעה: בשלב זה, המסמכים שלכם מומרים למקור מידע שהמתזמר יכול לחפש בו. אחת הדרכים הנפוצות ביותר לעשות זאת היא לבנות אינדקס וקטורי שניתן לחפש בו. נשתמש במודל ייעודי של Perplexity כדי להמיר את מקטעי המסמכים שלכם להטמעות. לאחר מכן נאחסן את ההטמעות ב-ChromaDB, מסד נתונים וקטורי פופולרי בקוד פתוח.
  2. תזמור: זהו היישום שנמצא בין מקור המידע החיצוני (במקרה שלנו, מסד נתונים וקטורי) לבין ה-LLM. ניצור סקריפט Python פשוט שישמש כמתזמר.
  3. אחזור ויצירה: כאשר משתמש שואל שאלה דרך ממשק הצ׳אט, המתזמר יקבל אותה, יריץ חיפוש וקטורי ויזין את הקלט המורחב ל-LLM. במקום לבנות ממשק משתמש ייעודי, נשתמש בשורת הפקודה כדי לתקשר עם המתזמר שלנו ב-Python.

שיטות עבודה מומלצות: מה הופך מימוש RAG לאפקטיבי?

קיימים סוגים רבים של ארכיטקטורות RAG, ותרחישי שימוש שונים מחייבים גישות שונות. עם זאת, מספר שיטות עבודה מומלצות חלות בכל המקרים ומבטיחות מערכות מדויקות, חסכוניות, ניתנות להרחבה וקלות לתחזוקה.

שיטות העבודה המומלצות הבאות חלות על כל סוגי מימושי ה-RAG:

  • הקפידו על צינור קליטה נקי: נתוני המקור שלכם צריכים להיות נקיים ככל האפשר לפני יצירת האינדקס. כמו כן, כדאי להשתמש במודל הטמעה שמתאים לתוכן שלכם.
  • השתמשו בחיפוש היברידי: שיטות האחזור הטובות ביותר, בין אם חיפוש במסד נתונים וקטורי, חיפוש באינטרנט מבוסס API, סריקה של גרף ידע, חיפוש לפי מילות מפתח או שיטות אחרות, משתנות בהתאם לתרחיש השימוש. בחירת השילוב הנכון חשובה לצמצום העלויות.
  • העריכו את המערכת שלכם: לאחר המימוש, כדאי להעריך את המערכת לפי שלושה מדדי ליבה: דיוק, רלוונטיות וזמן השהיה. דיוק מתייחס לנכונות העובדתית, רלוונטיות מתייחסת למידת ההתאמה של המידע שאוחזר לשאילתות המשתמשים, וזמן השהיה מתייחס למהירות שבה המידע מאוחזר.
  • טפלו במידע באופן מאובטח: אם מערכת ה-RAG שלכם נשענת על מידע פנימי פרטי, ודאו שהוא מאוחסן באופן מאובטח ושהגישה לממשק המערכת מנוהלת. לעולם אין לשתף מפתחות API.

מימוש RAG בסיסי: מדריך ב-7 שלבים

במדריך זה נבנה יישום שורת פקודה שעונה על שאלות לגבי מסמך יחיד. נשתמש ב-Python, ב-ChromaDB וב-API של Perplexity.

המטרה אינה לבנות מערכת RAG מוכנה לסביבת ייצור, אלא לספק מדריך ידידותי למתחילים שנועד להמחיש כיצד ארכיטקטורת RAG בסיסית עובדת בפועל.

שלב 1: התקנת תוכנה

ודאו שהרכיבים הבאים מותקנים במחשב שלכם ושיש לכם חשבון Perplexity API:

  • Python: זוהי שפת התכנות שבה נשתמש כדי לבנות את המתזמר, שיתאם את זרימת הנתונים בין מודל ההטמעה, ChromaDB ו-Perplexity.
  • Visual Studio: נשתמש ב-Visual Studio כעורך הקוד. לאחר ההתקנה, פתחו את החלונית Extensions והורידו את ההרחבה Python.
  • חשבון Perplexity API: Perplexity תספק גם את מודל ההטמעה (שהופך את הנתונים שלכם להטמעות וקטוריות) וגם את ה-LLM שיעבד את ההנחיה המורחבת. אין צורך בחשבון בתשלום כדי לעשות זאת, אם כי תהיה עלות קטנה בקרדיטים.

שלב 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. ה-SDK של Perplexity יאפשר לכם להתחבר למודלים של 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: כאן תשמרו את מפתח ה-API של Perplexity.

בקובץ .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 (שהגישה אליו נעשית דרך ה-API של Perplexity), שיעבד את המידע שאוחזר.

בקובץ document.txt, הוסיפו את הטקסט שצינור ה-RAG שלכם יאחזר. אם אין לכם מסמך זמין, השתמשו לצורך המדריך ב-Songs of Innocence and of Experience של ויליאם בלייק מ-Project Gutenberg. זהו טקסט קצר וקל להבנה שנמצא בנחלת הכלל.

שלב 4: כתיבת התוכנית ליצירת האינדקס

העתיקו והדביקו את הקוד שלהלן לקובץ index.py. כל כותרת משנה מספקת הסבר קצר על מה שעושה קטע הקוד שאחריה. זוהי התוכנית שתמיר את חומר המקור שלכם להטמעות וקטוריות ותאחסן אותן במסד נתונים של ChromaDB באמצעות מודל ההטמעה של Perplexity, pplx-embed-v1-0.6b, וספריית 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 שלם אך מינימלי, שמשתמש ב-SDK של Perplexity ובפונקציונליות מסד הנתונים הווקטורי המקומי של 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 מאחזר מקטעים על סמך דמיון וקטורי (ביוזמת המתזמר).
  • יצירת תשובות מבוססות מקורות: ה-LLM מקבל את הקטעים שאוחזרו כהקשר ומשלב אותם בתשובה.
  • יצירת אינדקס ושאילתות באופן עצמאי: תוכלו לשאול שאלות בלי לעבד מחדש את כל הטקסט שבקובץ הידע שלכם.
  • חיבור למודל באמצעות API: כל התשובות נוצרות באמצעות GPT-5.6 Luna דרך Perplexity, ולא באמצעות מודל שפועל במחשב שלכם.

השלבים הבאים: איך להרחיב את המימוש הזה

מטרת המדריך הזה היא לבנות מימוש בסיסי ופשוט. הוא מכסה חלק מרכיבי הליבה של ארכיטקטורת RAG שקיימים במערכות מודרניות. אם תרצו להתקדם, יש דרכים שונות להרחיב אותו.

הנה כמה רעיונות להרחבת היקף המימוש שלכם:

  • התנסו ב-LangChain: LangChain היא אחת ממסגרות התזמור הפופולריות ביותר בקוד פתוח. היא כוללת רכיבים מוכנים מראש לבניית יישומים שמסוגלים להריץ צינורות RAG מורכבים.
  • הרחיבו את יכולות החיפוש: שלבו חיפוש וקטורי צפוף עם חיפוש לקסיקלי (לפי מילות מפתח), כמו BM25. הוספת חיפוש באינטרנט באמצעות Perplexity Search API תאפשר לכם גם לבסס את התשובות על נתונים עדכניים.
  • הוסיפו מודל דירוג מחדש: שלבו מודל דירוג מחדש שמדרג את הקטעים שאוחזרו ביחס להנחיה הראשונית ובוחר את הרלוונטיים ביותר להכללה בהקשר הקלט שמסופק ל-LLM.
  • בנו מערכת RAG אדפטיבית: RAG אדפטיבי משתמש ב-LLM או במסווג (רכיב בתוך המתזמר) כדי לקבוע באיזו שיטת אחזור להשתמש במערכת RAG מודולרית. בניית מערכת כזו היא דרך טובה לחקור מימושי RAG מתוחכמים יותר.

אמנם כל זה עשוי להישמע מורכב, אבל זכרו שפלטפורמות AI נפוצות כמו Perplexity יכולות לספק סיוע מתקדם בכתיבת קוד. הן מצוינות לפרויקטים אישיים ולהתנסות, ואפשר לשמור על עלויות נמוכות יחסית.

הערה: מדריך זה משתמש ב-GPT-5.6 Luna. זמינות המודלים עשויה להשתנות, כך שייתכן שתצטרכו לעדכן את קריאת ה-API.