الخطوة 4 في RAG: إدخال المعرفة إلى قاعدة بيانات المتجهات
ستحمّل الآن مقالات أخبار Clark College ومستندات الدعم إلى pgvector.
الخطوة 1: تعرّف على البيانات النموذجية
يحتوي الملف advanced/rag/sample_data.py على نحو 30 مستندًا عن Clark College:
- مقالات إخبارية: معرض CTE، ومسابقة Geomatics، ومهرجان Sakura، وغيرها.
- خدمات الدعم: جلسات الدعم التعليمي، والمساعدات المالية، وPenguin Pantry، والإرشاد، وغيرها.
يبدو كل مستند هكذا:
{
"title": "Tutoring Services at Clark College",
"content": "Clark College offers free, friendly, and supportive tutoring...",
"source": "https://www.clark.edu/campus-life/student-support/",
"category": "support",
}
هذه مستندات نموذجية — ينبغي أن تستبدلها ببياناتك الخاصة.
الخطوة 2: قطّع المستندات
المستندات أطول من أن تُضمَّن كقطعة واحدة، لذا نقسّمها إلى مقاطع (chunks):
النص الأصلي: "Clark College offers free tutoring in business, English,
math, science, and general education classes. Online
eTutoring is also available..."
المقطع 1: "Clark College offers free tutoring in business, English, math,"
المقطع 2: "math, science, and general education classes. Online eTutoring"
المقطع 3: "Online eTutoring is also available to currently enrolled..."
كل مقطع نحو 300 كلمة، مع تداخل 50 كلمة حتى لا يضيع أي سياق عند الحدود.
الخطوة 3: أنشئ سكربت الإدخال
أنشئ الملف advanced/rag/knowledge_base.py:
from sentence_transformers import SentenceTransformer
from sample_data import DOCUMENTS
CHUNK_SIZE = 300
CHUNK_OVERLAP = 50
embedder = SentenceTransformer("all-MiniLM-L6-v2")
def chunk_text(text, chunk_size=CHUNK_SIZE, overlap=CHUNK_OVERLAP):
words = text.split()
chunks = []
start = 0
while start < len(words):
end = start + chunk_size
chunk = " ".join(words[start:end])
chunks.append(chunk)
start += chunk_size - overlap
return chunks
def get_all_chunks(documents=None):
if documents is None:
documents = DOCUMENTS
all_chunks = []
for doc in documents:
chunks = chunk_text(doc["content"])
for i, chunk in enumerate(chunks):
all_chunks.append({
"title": doc["title"],
"content": chunk,
"source": doc["source"],
"category": doc.get("category", "general"),
"chunk_index": i,
})
return all_chunks
def index_documents(db, DocumentModel, documents=None):
chunks = get_all_chunks(documents)
for chunk in chunks:
vec = embedder.encode(chunk["content"]).tolist()
doc = DocumentModel(
title=chunk["title"],
content=chunk["content"],
source=chunk["source"],
category=chunk["category"],
embedding=vec,
)
db.session.add(doc)
db.session.commit()
return len(chunks)
if __name__ == "__main__":
from app import create_app, db
from app.models import Document
app = create_app()
with app.app_context():
db.create_all()
count = index_documents(db, Document)
print(f"Indexed {count} chunks from {len(DOCUMENTS)} documents")
الخطوة 4: شغّل عملية الإدخال
تأكد من أن PostgreSQL وpgvector يعملان:
cd aiproject/advanced
docker compose -f docker-compose.rag.yml up -d db
ثم شغّل عملية الإدخال:
cd aiproject/backend
DATABASE_URL="postgresql://aiproject:aiproject@localhost:5432/aiproject" \
python -m advanced.rag.knowledge_base
الناتج المتوقع:
Indexed 87 chunks from 30 documents
الخطوة 5: تحقّق من البيانات
from app import create_app, db
from app.models import Document
app = create_app()
with app.app_context():
docs = Document.query.limit(3).all()
for d in docs:
print(f"[{d.category}] {d.title}: {d.content[:80]}...")
المتوقع:
[support] Tutoring Services at Clark College: Clark College offers free tutoring...
[news] CTE Showcase 2026: Clark College welcomed 20 high schools...
[support] Disability Access Center: The Disability Access Center assists...
كيف تضيف بياناتك الخاصة
الخيار 1: عدّل sample_data.py
أضف مدخلات جديدة إلى قائمة DOCUMENTS:
{
"title": "Your Document Title",
"content": "Full text of your document...",
"source": "https://example.com",
"category": "your-category",
}
ثم أعد تشغيل knowledge_base.py لفهرسة البيانات الجديدة.
الخيار 2: حمّل من ملف JSON
import json
with open("my_documents.json") as f:
documents = json.load(f)
ينبغي أن يكون ملف JSON قائمة من الكائنات ذات الحقول title وcontent وsource وcategory.
الخيار 3: كاشط ويب (اختياري)
استخدم requests وbeautifulsoup4 لكشط صفحات الويب. راجع advanced/rag/challenges.md للتفاصيل.
اكتب اختبارات للتضمينات
اختبر أن نموذج التضمين ينتج المخرجات الصحيحة:
أنشئ الملف advanced/rag/tests/test_rag_engine.py:
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from rag_engine import embed_text, build_rag_context
def test_embed_text_returns_384_dimensions():
vec = embed_text("Clark College offers free tutoring")
assert len(vec) == 384
assert all(isinstance(v, float) for v in vec)
def test_embed_text_consistent_output():
vec1 = embed_text("tutoring services")
vec2 = embed_text("tutoring services")
assert vec1 == vec2 # المدخل نفسه ← المتجه نفسه
def test_build_rag_context_formats_correctly():
results = [{"title": "Test", "content": "Hello world"}]
context = build_rag_context(results)
assert "[Test]" in context
assert "Hello world" in context
شغّلها:
cd aiproject
python -m pytest advanced/rag/tests/ -v
تتحقق هذه الاختبارات من: - أن أحجام التضمينات صحيحة (384 بُعدًا) - أن التضمينات حتمية (المدخل نفسه ← المتجه نفسه) - أن دالة بناء السياق تنسّق النتائج بشكل صحيح
اختبر عملك (يدويًا)
from sentence_transformers import SentenceTransformer
from app.models import Document
from app import create_app, db
model = SentenceTransformer("all-MiniLM-L6-v2")
app = create_app()
with app.app_context():
query = "Where can I get free tutoring?"
query_vec = model.encode(query).tolist()
results = (
db.session.query(Document)
.order_by(Document.embedding.cosine_distance(query_vec))
.limit(3)
.all()
)
for r in results:
sim = 1 - r.embedding.cosine_distance(query_vec)
print(f"[{sim:.2f}] {r.title}")
print(f" {r.content[:100]}...")
المتوقع: أن تحتل المستندات المتعلقة بالدعم التعليمي أعلى الترتيب.