LangChain Blogتاريخ نشر المصدر

هندسة السياق تبدأ بالاسترجاع

تخيّل أنك تجري امتحانًا مفتوح الكتاب: من الناحية الفنية لديك كل الإجابات، لكن هذا بشكل ما يجعل الأمر أصعب في العثور على الإجابة الصحيحة.

النقاط الرئيسية

  • تعمل الوكلاء (Agents) بنافذة سياق محدودة، ويفشل استرجاع المعلومات بثلاث طرق: البحث غير الفعّال، وتلقي قدر كبير من السياق دفعة واحدة، والسياق غير ذي الصلة أو القديم.
  • يحافظ هيكل Deep Agents على صِغَر حجم عمليات البحث افتراضيًا. أدوات نظام الملفات المدمجة تبحث قبل أن تقرأ: glob تطابق أسماء الملفات، وgrep تجد الأسطر المطابقة، وread_file تقرأ صفحة في كل مرة. نتائج الأدوات الكبيرة جدًا يتم تفريغها: تُحفظ النتيجة الكاملة في ملفات، لكن الوكيل يحصل فقط على المسار ومعاينة.
  • بالنسبة لأدوات البحث المخصصة (مثل RAG)، استخدم قاعدة "استرجِع، فرِّغ، فوِّض": احفظ النتائج في ملفات، وأعد المسارات، ودع الوكلاء الفرعيين يقرؤونها. الوكيل الرئيسي لا يرى سوى ما هو ذو صلة.

تخيّل أنك تجري امتحانًا مفتوح الكتاب: من الناحية الفنية لديك كل الإجابات، لكن هذا بشكل ما يجعل الأمر أصعب في العثور على الإجابة الصحيحة.

  • من أين تبدأ البحث أصلًا؟
  • متى تعرف أن مصادرك صحيحة؟
  • كيف تحسّن وقتك حتى لا تبذل كل طاقتك في عمليات بحث زائدة عن الحاجة؟

العثور على المعلومات الصحيحة بكفاءة ليس بهذه البساطة.

سلّم الوكيل سؤالًا وسيجد نفسه في مأزق مشابه: لديه نافذة سياق محدودة، لذا مثلنا نحن البشر، عليه أن يسترجع المعلومات لإتمام مهام معينة (بدلاً من أن يكون كل شيء محفوظًا في ذاكرته). عند البحث، يواجه الوكلاء صعوبات في:

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

لحسن الحظ، هناك عدة تقنيات يمكن للوكيل استخدامها لاسترجاع المعلومات الصحيحة. ومن واجب هيكل الوكيل (agent harness) تطبيق هذه التقنيات استراتيجيًا لتزويد النموذج بالسياق الصحيح في الوقت المناسب للمهمة المعطاة.

تتناول هذه المدونة تحديات استرجاع الوكلاء (العثور على المعلومات الضرورية فقط للمهمة المطروحة وسحبها)، والتقنيات المتاحة لنا لمواجهتها، وكيفية مراعاة ذلك عند تصميم هيكل وكيل (باستخدام هيكل Deep Agents من LangChain كمثال).

لمزيد من المعلومات حول كيفية إدارة السياق بمجرد أن تكون في نافذة السياق، تفضل بقراءة هذه التدوينة حول هندسة السياق.

مثالنا: وكيل تحليل المنافسة

لنرَ كيف يمكن تطبيق ذلك في حالة استخدام واقعية: وكيل تحليل المنافسة. كل يوم اثنين، يبحث الوكيل في مئات المنافسين ويكتب تقريرًا وفق قالب الفريق. ويحتاج إلى:

  • العثور على ملاحظات الأسبوع الماضي حول الشركة A
  • البحث عن رقم إيرادات محدد في تصدير ضخم لبيانات السوق
  • البحث في أرشيف الأخبار عن أي جديد

كل واحدة من هذه المهام هي نوع مختلف من الاسترجاع، وبالتالي تواجه مشكلات مختلفة. لقد بنينا وكيلنا باستخدام هيكل Deep Agents لذلك هناك بعض التقنيات التي يمكننا تطبيقها جاهزة لتقليل هذه المشكلات.

1. البحث غير الفعّال

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

لذلك، أفضل عملية بحث هي تلك التي لا يضطر الوكيل إلى تكرارها، وهذا يعني استخدام الأداة المناسبة لكل مهمة.

ابدأ بأدوات نظام الملفات المدمجة

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

النهج الأفضل هو فحص الأسماء و الكلمات المفتاحية أولًا (على نحو ما ستفحص به فهرس كتاب بدلًا من قراءة كل صفحة أثناء امتحان مفتوح الكتاب). أداة شائعتان لنظام الملفات تفعلان ذلك تمامًا:

  • glob يجد الملفات حسب نمط الاسم. على سبيل المثال، **/company-a*.md يجد ملاحظات الأسبوع الماضي حول الشركة A.
  • grep يبحث داخل الملفات دون تحميلها في نافذة السياق. إنه يطابق النص الحرفي ويمكنه إرجاع الملفات المطابقة، أو الأسطر المطابقة، أو الأعداد.

كل وكيل عميق يأتي مع هذه الأدوات مدمجة فيه, إلى جانب ls, read_file, write_file و edit_file. تعمل نفس الأدوات بغض النظر عن مكان وجود الملفات (حالة الوكيل، القرص المحلي، مخزن دائم أو بيئة معزولة)، لأنها جميعًا تمر عبر الخلفية.

أضف أداة استرجاع عندما لا تكون الكلمات المفتاحية كافية

Grep رائع عندما تعرف الكلمات التي تبحث عنها، لكنه يطابق فقط النص الدقيق الذي تعطيه إياه. هذه مشكلة للمهمة الثالثة في مثالنا: البحث في أرشيف إخباري عن أي شيء جديد. إذا بحث وكيلنا باستخدام grep عن "الشركة أ تمويل"، فسيفوّت مقالًا بعنوان "الشركة أ تغلق جولة Series B بقيمة $50M"، على الرغم من أن هذا بالضبط ما يبحث عنه.

للبحث بالمعنى بدلًا من الكلمات الدقيقة (البحث الدلالي)، تضيف أداة استرجاع مدعومة بـ مخزن متجهات. أداة الاسترجاع هي أي أداة يمكن للوكيل استدعاؤها لجلب المعلومات: تأخذ استعلامًا، وتبحث عنه في مصدر بيانات ما، وتعيد ما تجده (أقرب التطابقات).

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

  • قاعدة بيانات SQL، للاستعلامات المنظمة (مثل أرقام المبيعات أو سجلات العملاء)
  • واجهة برمجة تطبيقات بحث API (للحصول على معلومات حديثة من الويب)
  • خادم MCP (للأدوات التي بنىها آخرون مسبقًا، مثل البحث في التوثيق)

للاطلاع الأعمق على كيفية عمل البحث الدلالي، راجع دليل LangChain.

ترك القرار للوكيل بشأن متى يبحث

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

وكلاء البحث يحتاجون إلى الاسترجاع الوكيل (بدلًا من RAG بـ 2 خطوة): الوكيل يقرر متى يبحث، وماذا يبحث عنه، وهل يحتاج إلى المزيد. مع Deep Agents، يعمل هذا بالفعل بهذه الطريقة افتراضيًا: يعمل الوكيل في حلقة ويمكنه استدعاء أي أداة بعدد المرات التي يحتاجها.

حوّل خبرتك في البحث إلى مهارة

لمساعدة الوكيل على البحث بشكل جيد، يمكنك تغليف خبرتك في البحث كـ مهارة: مجموعة من التعليمات لمهمة محددة (مثل أي فهرس يُستخدم، وكيفية صياغة الاستعلامات وكيفية تنسيق الاستشهادات).

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

الإفصاح التدريجي (متحرك)

2. سياق زائد دفعة واحدة

ومع ذلك، على الرغم من تحسين عملية البحث، فحتى يمين قد يُعيد البحث كمًّا كبيرًا من النتائج. يحتاج وكيلنا إلى رقم واحد فقط من تصدير بيانات السوق (إيرادات الشركة B الفصلية)، لكن تحميل جميع الصفوف البالغ عددها 40,000 للعثور عليها سيملأ نافذة السياق الخاصة به. تواجه أداة الاسترجاع لدينا المشكلة نفسها: إذا كانت تُعيد كل مقال إخباري مباشرة إلى النموذج، فإن أربع نتائج لكل بحث عبر خمس شركات تتراكم بسرعة.

قراءة الملفات الكبيرة صفحة واحدة في كل مرة

تم بناء أدوات نظام الملفات لتُرجع أقل قدر ممكن. read_file يقرأ ملفاً صفحة واحدة في كل مرة (100 سطر افتراضياً)، ويمكن للوكيل تحديد offset (السطر الذي يبدأ منه) وlimit (عدد الأسطر المراد قراءتها) للانتقال إلى الجزء الذي يحتاجه. فمثلاً، للعثور على إيرادات الشركة B، يبحث الوكيل عنها باستخدام grep، ثم يقرأ فقط الأسطر القليلة المحيطة بالتطابق.

دع الأداة تُفرغ النتائج كبيرة الحجم

أحيانًا تعيد الأداة أكثر من المتوقع على أي حال. عندما تكون نتيجة الأداة كبيرة جدًا (أكثر من حوالي 20,000 رمز)، يحفظ النظام المُشغِّل النتيجة في ملف ضمن /large_tool_results/ ويعطي الوكيل المسار مع معاينة قصيرة (أول بضعة أسطر وآخرها). يمكن للوكيل عندئذٍ قراءة الأجزاء التي يحتاجها فقط عبر read_fileخاصة بالإزاحة (offset) والحد (limit).

اجعل أداة الاسترجاع لديك تُعيد مسارات الملفات

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

إليك أداة استرجاع مبسطة. تقوم بتشغيل البحث، وتحفظ كل نتيجة كملف في backendالخاص بالوكيل، وتعيد فقط قائمة مسارات الملفات:

backend = StateBackend()

@tool
def search_news(query: str) -> str:
    """Search the news archive and save each result to a file."""
    docs = vector_store.similarity_search(query, k=4)
    files = [(f"/retrieved/result_{i}.md", doc.page_content.encode()) for i, doc in enumerate(docs)]
    backend.upload_files(files)
    return "Saved results to: " + ", ".join(path for path, _ in files)

الآن يرى الوكيل الرئيسي أربعة مسارات قصيرة بدلاً من أربع مقالات كاملة.

3. سياق غير ذي صلة أو قديم

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

دع الوكلاء الفرعيين يرشحوا النتائج

ومع ذلك، لا يزال على الوكيل الرئيسي أن يقرأ تلك الملفات لمعرفة أي منها مهم (وكل مقال غير ذي صلة يفتحه يدخل فعلاً في سياقه). هنا يأتي دور جزء "التفويض" في نمط "استرجع، فوض، فوّض": وكيل فرعي يقرأ كل ملف تم استرجاعه ويعيد فقط الحقائق الرئيسية.

  1. عرّف الوكيل الفرعي: يخبر الوصفُ الوكيلَ الرئيسي متى يستخدمه (ملف واحد لكل استدعاء)، ويخبر موجه النظام الوكيل الفرعي بما يفعله مع ذلك الملف.
chunk_analyst = {
		"name": "chunk-analyst",
		"description": "Analyze one retrieved file. Pass the question and a single path under /retrieved/.",
		"system_prompt": "Use read_file to read the assigned file. Return the key facts and the source URL.",
}
  1. أعطِ الوكيل الرئيسي أداة الاسترجاع والوكيل الفرعي. مرر نفس الـ backend الذي استخدمته في search_news، حتى يستطيع الوكيل الفرعي قراءة الملفات التي حفظتها الأداة.
agent = create_deep_agent(
		model=model,
		tools=[search_news],
		backend=backend,  # same instance, so read_file can see /retrieved/
		subagents=[chunk_analyst],
)

الآن أصبح التدفق كالتالي:

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

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

السحب من مصادر محدثة

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

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

احترس من النص المسترجع

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

استراتيجيات الاسترجاع التي يجب وضعها في الاعتبار

إليك ما يجب الانتباه له (بغض النظر عن الإطار الذي تبني عليه):

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

جرّبه

uv add deepagents

تريد التعلم أكثر من مدرب مباشر؟ سأغطي هذا الموضوع في 9 نوفمبر: سجّل هنا!

‍

الاسترجاع هو نصف القصة فقط: كيف تجد المعلومات طريقها إلى الوكيل. كيف ندير نافذة سياق الوكيل كلما بدأت المعلومات في التراكم مع مرور الوقت؟

لمعرفة المزيد، تحقق من:

المصدر الأصلي

LangChain Blog

ملاحظات المحتوى

النشر الأصلي والحقوق تعود إلى المصدر.

ترجمة آلية · يُرجى الرجوع إلى الأصل