دليل تقني

RAG مش كافي: شو يحتاج نظام الذكاء الاصطناعي الموثوق فوقه

وين RAG ينكسر بالإنتاج وشو لازم تبني فوقه. جودة الـ chunks، طبقات التنسيق، البحث الهجين، حدود الهلوسة، إدارة التكاليف، ومتى تتخطى RAG كلياً.

18 فبراير 202618 دقيقة للقراءةفريق هندسة أورنتس

فخ الـ RAG Demo

كل RAG demo بيشتغل. ارفع شوية PDFs، قسّمهم chunks، حوّلهم embeddings، اسأل سؤال، واحصل على جواب. الـ demo دايماً مبهر. أصحاب القرار متحمسين. الفريق يقدّر إنه يوصل للإنتاج بأسبوعين.

بعد ست شهور، النظام لسا غير موثوق. المستخدمين بيحصلوا على أجوبة غلط من chunks قديمة. الموديل بيستشهد بثقة بوثائق تقول عكس اللي بيدّعيه الجواب. التكاليف 10 أضعاف التقدير الأولي. وما حدا قادر يفهم ليش نفس السؤال بيعطي أجوبة مختلفة حسب الوقت.

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

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

وين RAG بينكسر

نوع الفشلشو بيصيرقديش شائع
جودة الـ Chunkحدود التقسيم الغلط بتقطع السياق، والجواب مبني على معلومات ناقصةشائع جداً
بيانات قديمةالـ index مش محدّث، والجواب مبني على وثيقة قديمةشائع
فشل الاسترجاعالوثيقة المطلوبة موجودة بس الـ embedding similarity ما بيطلّعهاشائع
هلوسة رغم الاسترجاعالموديل بيتجاهل السياق المسترجع وبيولّد من بيانات التدريبشائع
طفحان context windowchunks كتير بتنسترجع، الموديل بيفقد التركيزمتوسط
خلط بين الوثائقchunks من وثائق مختلفة بتنخلط، الموديل بيدمج حقائق متناقضةمتوسط
انفجار التكاليفتكاليف الـ embedding + الاسترجاع + التوليد بتتضاعف مع حجم الاستعلاماتتدريجي
طفرات بالتأخيرالبحث المتجهي + إعادة الترتيب + التوليد بياخد وقت طويل للاستخدام التفاعليمتوسط

جودة الـ Chunk هي كل شي

أكتر مشكلة بتنقلل قيمتها. إذا الـ chunks تبعك قاطعين فقرة بنص فكرة، السياق المسترجع ناقص. إذا الـ chunks كبار كتير، المحتوى الغير مرتبط بيخفف المعلومات المفيدة. إذا الـ chunks ما بتحافظ على بنية الوثيقة (عناوين، جداول، قوائم)، الموديل بيخسر السياق التنظيمي.

// سيء: chunks بحجم ثابت بتقطع السياق
function naiveChunk(text: string, size: number): string[] {
    const chunks = [];
    for (let i = 0; i < text.length; i += size) {
        chunks.push(text.slice(i, i + size));
    }
    return chunks;
    // المشكلة: بتقطع الجمل والفقرات والجداول بنصهم
}

// أحسن: تقسيم دلالي مع تداخل
function semanticChunk(text: string, options: ChunkOptions): Chunk[] {
    const sections = splitByHeadings(text);      // احترم بنية الوثيقة
    const paragraphs = sections.flatMap(s =>
        splitByParagraphs(s, { maxSize: options.maxChunkSize })
    );

    return paragraphs.map((p, i) => ({
        content: p.text,
        metadata: {
            section: p.sectionTitle,
            pageNumber: p.pageNumber,
            documentId: p.documentId,
            position: i,
        },
        // تداخل: أضف آخر جملتين من الـ chunk السابق
        prefix: i > 0 ? getLastSentences(paragraphs[i - 1].text, 2) : '',
    }));
}

التداخل مهم. بدونه، سؤال يمتد على chunk-ين بيحصل على سياق جزئي من كل واحد وجواب كامل من ولا واحد. مع تداخل 2-3 جمل، الموديل عنده سياق كافي يربط حدود الـ chunks.

الـ metadata تبع الـ chunk مهمة بنفس المستوى. كل chunk لازم يحمل معه ID الوثيقة المصدر، عنوان القسم، رقم الصفحة، والموقع. بدون metadata، ما فيك تقول للمستخدم من وين إجا الجواب. بدون نسبة للمصدر، الجواب ما بينتحقق منه.

جودة الاسترجاع مقابل كمية الاسترجاع

استرجاع chunks أكتر ما يعني أجوبة أحسن. عملياً، لقينا إنه 3-5 chunks عالية الجودة دايماً بيتفوقوا على 10-15 chunk متوسطة.

عدد الـ Chunks المسترجعةجودة الجوابالتأخيرالتكلفة
1-2خطر فقدان السياقسريعمنخفضة
3-5أفضل توازن (موصى به)متوسطمتوسطة
5-10عوائد متناقصة، شوية تشويشأبطأأعلى
10+تخفيف السياق، الموديل بيتلخبطبطيءعالية

الحل: استرجع بنطاق واسع، بعدين أعد الترتيب بقوة.

async function retrieveAndRerank(query: string, options: RetrievalOptions) {
    // الخطوة 1: استرجاع واسع (جيب 20 مرشح)
    const candidates = await vectorStore.search(query, { limit: 20 });

    // الخطوة 2: أعد الترتيب بـ cross-encoder (سجّل كل مرشح ضد الاستعلام)
    const reranked = await reranker.rank(query, candidates, {
        model: 'cross-encoder/ms-marco-MiniLM-L-12-v2',
    });

    // الخطوة 3: خذ أعلى 5 بعد إعادة الترتيب
    const topChunks = reranked.slice(0, 5);

    // الخطوة 4: فلتر حسب حد أدنى لدرجة الملاءمة
    return topChunks.filter(c => c.score > options.minRelevanceScore);
}

الـ reranker هو موديل cross-encoder بيسجّل كل مرشح ضد الاستعلام بدقة أعلى بكتير من cosine similarity على الـ embeddings. أبطأ (بيشغّل inference لكل مرشح)، بس تحسين الجودة كبير. تشغيله على 20 مرشح لاختيار 5 بيضيف 100-200ms تأخير، وهالشي مقبول لأغلب حالات الاستخدام.

طبقة التنسيق اللي RAG يحتاجها

RAG الخام هو: حوّل الاستعلام embedding، ابحث بالـ vectors، حشي السياق بالـ prompt، ولّد. نظام إنتاج يحتاج طبقة تنسيق بين الاسترجاع والتوليد.

استعلام المستخدم
    │
    ▼
┌──────────────────┐
│  تحليل الاستعلام  │  تصنيف النية، استخراج الكيانات، كشف اللغة
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  التوجيه          │  أي index؟ أي استراتيجية استرجاع؟ cache hit؟
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  الاسترجاع       │  بحث متجهي + بحث نصي (هجين)
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  إعادة الترتيب    │  تسجيل بـ cross-encoder، فلترة الـ chunks المنخفضة
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  تجميع السياق    │  ترتيب الـ chunks، إضافة metadata، احترام ميزانية التوكنات
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  التوليد          │  استدعاء LLM مع السياق المجمّع + system prompt
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  تحقق المخرجات   │  فحص الهلوسة، تحقق الاستشهادات، مسح PII
└────────┬─────────┘
         │
         ▼
    الاستجابة

تحليل الاستعلام

مش كل استعلام يحتاج RAG. بعض الاستعلامات محادثة ("مرحبا"، "شكراً"). بعضها عن النظام نفسه ("كيف أستخدم هالأداة؟"). بعضها غامض ويحتاج توضيح. محلل الاستعلام بيصنّف النية قبل ما يفعّل الاسترجاع.

async function analyzeQuery(query: string): Promise<QueryAnalysis> {
    // تصنيف سريع (ممكن يكون موديل صغير أو قائم على قواعد)
    const intent = await classifyIntent(query);

    if (intent === 'greeting' || intent === 'meta') {
        return { needsRetrieval: false, intent, response: getStaticResponse(intent) };
    }

    if (intent === 'ambiguous') {
        return { needsRetrieval: false, intent, clarificationNeeded: true };
    }

    return {
        needsRetrieval: true,
        intent,
        extractedEntities: await extractEntities(query),
        detectedLanguage: await detectLanguage(query),
    };
}

التوجيه

استعلامات مختلفة ممكن تحتاج indices مختلفة، استراتيجيات استرجاع مختلفة، أو موديلات مختلفة.

نوع الاستعلامالـ Indexالاستراتيجيةالموديل
سؤال عن منتجindex المنتجاتهجين (نصي + متجهي)موديل سريع (GPT-4o-mini)
سؤال قانوني/امتثالindex السياساتمتجهي فقط (دقيق)موديل دقيق (GPT-4o)
دعم تقنيindex قاعدة المعرفةهجين + إعادة ترتيبموديل سريع
استعلام متعدد اللغاتindex متعدد اللغاتمتجهي مع فلتر لغةموديل متعدد اللغات

تجميع السياق

بعد الاسترجاع وإعادة الترتيب، الـ chunks لازم تتجمّع بـ prompt يحترم ميزانية التوكنات تبع الموديل.

function assembleContext(chunks: RankedChunk[], tokenBudget: number): string {
    let context = '';
    let tokensUsed = 0;

    for (const chunk of chunks) {
        const chunkTokens = estimateTokens(chunk.content);
        if (tokensUsed + chunkTokens > tokenBudget) break;

        context += `\n\n---\nSource: ${chunk.metadata.documentTitle} (${chunk.metadata.section})\n`;
        context += chunk.content;
        tokensUsed += chunkTokens;
    }

    return context;
}

ميزانية التوكنات لازم تحسب الـ system prompt، استعلام المستخدم، السياق المجمّع، وطول الاستجابة المتوقع. غلطة شائعة هي تعبئة كامل الـ context window بـ chunks مسترجعة، وما يضل مجال لاستجابة جيدة.

البحث الهجين: نصي + متجهي

البحث المتجهي لحاله بيفوّت الاستعلامات المبنية على كلمات مفتاحية. مستخدم يبحث عن "error code E-4021" بيحصل على نتائج سيئة من embedding similarity لأنه رموز الأخطاء ما عندها معنى دلالي. البحث النصي لحاله بيفوّت الاستعلامات الدلالية. مستخدم يبحث عن "كيف أصلح مشاكل تسجيل الدخول" ما رح يلاقي وثيقة عنوانها "دليل استكشاف أخطاء المصادقة."

البحث الهجين بيجمع الاتنين:

async function hybridSearch(query: string, options: SearchOptions) {
    // تنفيذ متوازي
    const [vectorResults, textResults] = await Promise.all([
        vectorStore.search(query, { limit: options.vectorLimit }),
        textIndex.search(query, { limit: options.textLimit }),
    ]);

    // Reciprocal Rank Fusion (RRF) لدمج النتائج
    const merged = reciprocalRankFusion(vectorResults, textResults, {
        vectorWeight: 0.6,
        textWeight: 0.4,
    });

    return merged.slice(0, options.totalLimit);
}

function reciprocalRankFusion(
    vectorResults: SearchResult[],
    textResults: SearchResult[],
    weights: { vectorWeight: number; textWeight: number },
): SearchResult[] {
    const scores = new Map<string, number>();
    const k = 60; // ثابت RRF

    vectorResults.forEach((result, rank) => {
        const score = (scores.get(result.id) || 0) + weights.vectorWeight / (k + rank + 1);
        scores.set(result.id, score);
    });

    textResults.forEach((result, rank) => {
        const score = (scores.get(result.id) || 0) + weights.textWeight / (k + rank + 1);
        scores.set(result.id, score);
    });

    return Array.from(scores.entries())
        .sort(([, a], [, b]) => b - a)
        .map(([id, score]) => ({ id, score }));
}

نسبة الأوزان (vector 0.6, text 0.4) هي نقطة بداية. عدّلها حسب توزيع استعلاماتك. إذا أغلب الاستعلامات مبنية على كلمات مفتاحية (SKUs منتجات، رموز أخطاء)، زد وزن النص. إذا أغلب الاستعلامات لغة طبيعية، زد وزن الـ vector.

لأكتر عن بنية البحث بسياق التجارة الإلكترونية، شوف دليل منصات التجارة الإلكترونية.

حدود الهلوسة

RAG بيقلل الهلوسة مقارنة بالتوليد الصافي من LLM. بس ما بيلغيها. الموديل لسا ممكن:

  • يتجاهل السياق المسترجع ويولّد من بيانات التدريب
  • يخلط معلومات من عدة chunks بشكل غلط
  • يخترع استشهادات مش موجودة بالسياق المسترجع
  • يستنتج أبعد مما السياق بيدعم

استراتيجيات التخفيف

1. System prompts مقيّدة:

You are a support assistant. Answer ONLY based on the provided context.
If the context does not contain enough information to answer, say
"I don't have enough information to answer that question."
Do NOT use information from your training data.
Every claim must reference a specific source from the context.

2. تحقق الاستشهادات:

async function verifyCitations(response: string, chunks: RankedChunk[]): VerificationResult {
    const citations = extractCitations(response);
    const verified = [];
    const unverified = [];

    for (const citation of citations) {
        const found = chunks.some(chunk =>
            chunk.content.includes(citation.claimedText) ||
            fuzzyMatch(chunk.content, citation.claimedText, 0.85)
        );
        (found ? verified : unverified).push(citation);
    }

    return {
        allVerified: unverified.length === 0,
        verified,
        unverified,
        confidenceScore: verified.length / (verified.length + unverified.length),
    };
}

3. تسجيل الثقة:

إذا استجابة الموديل ما بتتوافق كويس مع السياق المسترجع (تداخل منخفض، بدون اقتباسات مباشرة)، علّمها كثقة منخفضة. اعرض تحذير للمستخدم أو حوّلها لإنسان.

لأكتر عن أنماط فشل الذكاء الاصطناعي وكيف تتعامل معها، شوف دليل أنماط فشل الذكاء الاصطناعي.

التكلفة والتأخير

تكاليف RAG بتتضاعف مع حجم الاستعلامات على تلات أبعاد:

المكوّنمحرّك التكلفةالنطاق النموذجي
Embedding الاستعلاملكل استعلام (inference الموديل)$0.0001 لكل استعلام
البحث المتجهيلكل استعلام (حوسبة + I/O)$0.0005 لكل استعلام
إعادة الترتيبلكل استعلام * المرشحين (inference الموديل)$0.001 لكل استعلام
توليد LLMتوكنات الإدخال (سياق) + توكنات الإخراج$0.01-0.10 لكل استعلام
Embedding الوثائقمرة وحدة لكل وثيقة (عند الإدخال)$0.0001 لكل صفحة

توليد LLM بيسيطر على التكلفة. تقليل حجم السياق (chunks أقل، chunks أقصر) بيقلل مباشرة المكوّن الأغلى.

استراتيجيات التخزين المؤقت

// Semantic cache: خزّن الاستجابات لاستعلامات مشابهة
async function cachedQuery(query: string): Promise<string | null> {
    // حوّل الاستعلام لـ embedding
    const queryEmbedding = await embedder.embed(query);

    // ابحث بـ index الكاش عن استعلامات مشابهة
    const cached = await cacheIndex.search(queryEmbedding, {
        minSimilarity: 0.95,  // حد عالي لـ cache hits
        limit: 1,
    });

    if (cached.length > 0) {
        return cached[0].response;  // Cache hit
    }

    return null;  // Cache miss، تابع مع pipeline RAG الكامل
}

الـ semantic caching بيشتغل لأنه مستخدمين كتير بيسألوا أسئلة مشابهة بصياغات مختلفة شوي. "كيف أعيد تعيين كلمة المرور؟" و"تعليمات إعادة تعيين كلمة المرور" هم نصوص مختلفة بس دلالياً متطابقين. حد تشابه 0.95 بيضمن إنه بس الاستعلامات شبه المتطابقة بتحصل على استجابات مخزنة.

ميزانية التأخير

للاستخدام التفاعلي (chatbot، مساعد دعم)، الـ pipeline الكامل لازم يخلص بأقل من 3 ثواني:

المرحلةالميزانيةالتحسين
تحليل الاستعلام50msقائم على قواعد أو موديل صغير
فحص الكاش30msindex متجهي بالذاكرة
البحث المتجهي100mscluster بحث مخصص
البحث النصي100msمتوازي مع البحث المتجهي
إعادة الترتيب200mscross-encoder صغير، حدد المرشحين
تجميع السياق10msبالذاكرة
توليد LLM1,500msStreaming، موديل سريع
تحقق المخرجات100msقائم على قواعد + موديل صغير
الإجمالي~2,100ms

عمل streaming لاستجابة LLM للمستخدم أثناء التوليد بيخلي التأخير المحسوس أقل بكتير. المستخدم بيشوف أول التوكنات بـ 300-500ms حتى لو الاستجابة الكاملة بتاخد 1,500ms.

متى تتخطى RAG كلياً

RAG مش دايماً النمط الصح. أحياناً مقاربات أبسط بتشتغل أحسن:

السيناريومقاربة أحسنليش
FAQ ثابت (أقل من 50 سؤال)مطابقة كلمات مفتاحية + استجابة قالبأسرع، أرخص، حتمي
استعلامات بيانات هيكليةاستعلام SQL/API + قالبLLM بيضيف تأخير وخطر هلوسة
بيانات لحظية (أسعار أسهم، مخزون)استدعاء API مباشرالـ Embeddings قديمة بطبيعتها
تصنيف بسيطمصنّف fine-tunedأرخص، أسرع، أكتر موثوقية
تلخيص وثيقةاستدعاء LLM مباشر (بدون retrieval)الوثيقة الكاملة هي السياق

RAG منطقي لما عندك قاعدة معرفة كبيرة (مئات لآلاف الوثائق)، استعلامات لغة طبيعية ما بتنطابق بالكلمات المفتاحية، وحاجة لأجوبة مركّبة من عدة مصادر. إذا حالة الاستخدام تبعك ما بتوافق هالملف، مقاربة أبسط رح تكون أكتر موثوقية وأرخص.

لكيف نتعامل مع قرارات البنية هاي بخدمات الذكاء الاصطناعي تبعنا، وللأنماط الأوسع بتصميم سير عمل الذكاء الاصطناعي، هالصفحات بتعطي سياق أكتر.

الأخطاء الشائعة

  1. تقسيم بحجم ثابت. الـ chunks اللي بتقطع الفقرات والجداول وبلوكات الكود بنصهم بتنتج استرجاع سيء. استخدم تقسيم دلالي يحترم بنية الوثيقة.

  2. بدون تداخل بالـ chunks. بدون تداخل، الاستعلامات اللي بتمتد على حدود الـ chunks بتحصل على سياق جزئي من كل واحد وجواب كامل من ولا واحد.

  3. بدون إعادة ترتيب. الـ embedding similarity هو فلتر تقريبي. cross-encoder reranker بيحسّن جودة أعلى 5 نتائج بشكل كبير.

  4. تعبئة كامل الـ context window. خلّي مجال للـ system prompt، استعلام المستخدم، والاستجابة المتوقعة. prompt محشي بـ 15 chunk ما بيخلي مجال لجواب جيد.

  5. بدون بحث هجين. البحث المتجهي لحاله بيفشل مع استعلامات الكلمات المفتاحية (رموز أخطاء، SKUs منتجات). البحث النصي لحاله بيفشل مع الاستعلامات الدلالية. استخدم الاتنين.

  6. بدون semantic caching. أسئلة مشابهة من مستخدمين مختلفين بتفعّل pipeline RAG الكامل كل مرة. semantic cache بحد تشابه 0.95 بيقلل التكاليف بشكل كبير.

  7. الوثوق بمخرجات RAG بدون تحقق. RAG بيقلل الهلوسة. ما بيلغيها. تحقق من الاستشهادات ضد السياق المسترجع. علّم الادعاءات الغير متحقق منها.

  8. بدون مراقبة. لازم تتابع جودة الاسترجاع (هل الـ chunks الصح انسترجعت؟)، جودة الجواب (هل المستخدم لقى الجواب مفيد؟)، التأخير، التكلفة لكل استعلام، ونسبة cache hit.

النقاط الأساسية

  • RAG هو نمط retrieval، مش حل. نظام موثوق يحتاج تحليل استعلام، توجيه، بحث هجين، إعادة ترتيب، تجميع سياق، توليد، وتحقق مخرجات فوق الاسترجاع.

  • جودة الـ Chunk بتحدد جودة الجواب. التقسيم الدلالي مع التداخل، الـ metadata، والحفاظ على بنية الوثيقة هو الأساس. كل شي تاني مبني فوق chunks جيدة.

  • استرجع بنطاق واسع، أعد الترتيب بقوة. جيب 20 مرشح بـ embedding similarity. سجّلهم بـ cross-encoder. خذ أعلى 5. فلتر حسب حد أدنى للملاءمة.

  • البحث الهجين بيغطي اللي الـ vectors بيفوّتوه. الكلمات المفتاحية، رموز الأخطاء، IDs المنتجات، والمطابقات الدقيقة بتحتاج بحث نصي. الاستعلامات الدلالية بتحتاج بحث متجهي. استخدم الاتنين مع reciprocal rank fusion.

  • توليد LLM بيسيطر على التكلفة. تقليل حجم السياق (chunks أقل وأحسن) هو أكتر تحسين فعّال للتكلفة.

  • أحياناً RAG هو النمط الغلط. FAQs الثابتة، استعلامات البيانات الهيكلية، البيانات اللحظية، والتصنيف البسيط كلها عندها حلول أبسط وأكتر موثوقية.

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

المواضيع المغطاة

RAG إنتاجقيود RAGموثوقية الذكاء الاصطناعيمشاكل retrieval augmented generationبنية RAGبحث هجينتنسيق RAGهلوسة RAG

جاهز لبناء أنظمة ذكاء اصطناعي جاهزة للإنتاج؟

فريقنا متخصص في بناء أنظمة ذكاء اصطناعي جاهزة للإنتاج. خلينا نحكي كيف نقدر نساعد.

ابدأ محادثة