هندسة السياق للوكلاء الذكية: دليلك العملي لبناء أنظمة ذكاء اصطناعي موثوقة

دليل عملي لهندسة السياق (Context Engineering) للوكلاء الذكية في 2026. تعلّم الاستراتيجيات الأربع — الكتابة والاختيار والضغط والعزل — مع أمثلة Python وأنظمة ذاكرة متقدمة وتقنيات تحسين KV-Cache.

مقدمة: لماذا أصبحت هندسة السياق المهارة الأهم لمهندسي الذكاء الاصطناعي؟

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

وصفها Andrej Karpathy بطريقة عبقرية: "النموذج اللغوي الكبير هو المعالج (CPU)، ونافذة السياق هي الذاكرة العشوائية (RAM)، وأنت نظام التشغيل المسؤول عن تحميل المعلومات الصحيحة تماماً لكل مهمة." هذه الاستعارة — رغم بساطتها — تلخّص جوهر ما نسمّيه هندسة السياق (Context Engineering).

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

اختبارات Chroma Research على 18 نموذجاً لغوياً أكدت هذا: الدقة تنخفض مع زيادة طول المدخلات عبر جميع النماذج — حتى في المهام البسيطة.

في هذا الدليل، سنتعمق في هندسة السياق من الألف إلى الياء — من الفرق الجوهري بينها وبين هندسة الأوامر، مروراً بالاستراتيجيات الأربع الأساسية وأنظمة الذاكرة، وصولاً إلى أنماط التنفيذ العملية بأمثلة Python حقيقية. وسنستفيد من أحدث الدروس من Anthropic وLangChain وManus في بناء وكلاء ذكية إنتاجية.

هندسة السياق مقابل هندسة الأوامر: التحول الجوهري

ما الفرق الحقيقي؟

لنوضّح الأمر ببساطة:

  • هندسة الأوامر (Prompt Engineering): تركّز على كيف تسأل النموذج. صياغة التعليمات، اختيار الأمثلة، تحديد النبرة — كل هذا ضمن تفاعل واحد.
  • هندسة السياق (Context Engineering): تركّز على ما يعرفه النموذج عندما تسأله. تصميم النظام الكامل الذي يحدد المعلومات والأدوات والذاكرة التي تصل للنموذج في كل لحظة.

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

الجدول التالي يوضّح الفرق بشكل أعمق:

البُعدهندسة الأوامرهندسة السياق
النطاقتفاعل واحد (مدخل ← مخرج)النظام بأكمله (ذاكرة، أدوات، سجل، سياسات)
العقليةصياغة تعليمات واضحةتصميم بنية فكرية كاملة للنموذج
قابلية التكرارتحتاج تعديلات يدوية مستمرةمصمّمة للاتساق وإعادة الاستخدام
قابلية التوسعتنهار مع التعقيدمبنية للتوسع من الأساس

النقطة المهمة هنا: هندسة الأوامر وهندسة السياق متكاملتان وليستا متعارضتان. ما زلت تحتاج أوامر جيدة — لكنك الآن تعتمد أيضاً على الاسترجاع والذاكرة والسياسات حتى يستدلّ النموذج على الحقائق الصحيحة.

لماذا أصبحت هندسة السياق ضرورية في 2026؟

ثلاثة عوامل أساسية دفعت هذا التحول:

  1. تعقيد التطبيقات: انتقلنا من روبوتات محادثة بسيطة إلى وكلاء ذكية متعددة الخطوات تستخدم أدوات وتتخذ قرارات. هذه الأنظمة تحتاج إدارة سياق ديناميكية — أوامر ثابتة لن تكفي بعد الآن.
  2. اكتشاف حدود نوافذ السياق: حتى مع نوافذ بحجم مليون رمز، الأبحاث أثبتت أن حشو السياق يُضعف الأداء فعلياً. الأداء الأمثل يبدأ بالتدهور عند حوالي 3,000 رمز (والمنطقة المثالية هي 150-300 كلمة للتعليمات فقط).
  3. ظهور أنماط إنتاجية مثبتة: شركات مثل Anthropic وManus وGoogle نشرت دروساً عملية من بناء وكلاء إنتاجية حقيقية، مما حوّل هندسة السياق من مفهوم نظري إلى تخصص هندسي ناضج.

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

فريق LangChain — مع باحثين وممارسين آخرين — توصّلوا إلى أربع استراتيجيات أساسية تشكّل أعمدة هندسة السياق: الكتابة، الاختيار، الضغط، والعزل. دعونا نغوص في كل واحدة.

1. الكتابة (Write): حفظ السياق خارج النافذة

الفكرة بسيطة: بدلاً من محاولة الاحتفاظ بكل شيء في نافذة السياق (أي "الذاكرة العشوائية")، يكتب الوكيل الحقائق المهمة والقرارات والنتائج الوسيطة في مكان خارجي — ملفات، قواعد بيانات، أو ذاكرة مستمرة — ليسترجعها لاحقاً عند الحاجة.

فكّر فيها كدفتر ملاحظات يرافق الوكيل أينما ذهب.

from datetime import datetime

class AgentScratchpad:
    """دفتر ملاحظات للوكيل الذكي — يحفظ السياق خارج نافذة النموذج"""

    def __init__(self):
        self.notes = []
        self.key_facts = {}

    def write_note(self, category: str, content: str):
        """كتابة ملاحظة مصنّفة مع طابع زمني"""
        self.notes.append({
            "timestamp": datetime.utcnow().isoformat(),
            "category": category,
            "content": content
        })

    def save_fact(self, key: str, value: str):
        """حفظ حقيقة مهمة يمكن استرجاعها لاحقاً"""
        self.key_facts[key] = {
            "value": value,
            "saved_at": datetime.utcnow().isoformat()
        }

    def get_relevant_notes(self, category: str, limit: int = 5) -> list:
        """استرجاع أحدث الملاحظات في فئة معينة"""
        filtered = [n for n in self.notes if n["category"] == category]
        return filtered[-limit:]

# الاستخدام العملي
scratchpad = AgentScratchpad()
scratchpad.write_note("research", "المستخدم يبحث عن حلول لتحسين أداء API")
scratchpad.save_fact("user_tech_stack", "Python + FastAPI + PostgreSQL")
scratchpad.save_fact("user_goal", "تقليل زمن الاستجابة من 2 ثانية إلى أقل من 200ms")

هذا النمط بسيط لكنه فعّال بشكل مدهش. Anthropic تسمّيه "تدوين الملاحظات المنظّم" (Structured Note-Taking) وتوصي به بشكل خاص للمهام التي تتضمن مراحل واضحة — كالتطوير التكراري حيث يحتاج الوكيل لتتبّع ما أنجزه وما بقي.

2. الاختيار (Select): جلب السياق المناسب في الوقت المناسب

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

لكن الاختيار يتجاوز RAG التقليدي بمراحل. يشمل أيضاً:

  • اختيار الأدوات الديناميكي: تقديم فقط الأدوات ذات الصلة بالمهمة الحالية بدلاً من إغراق النموذج بقائمة كاملة.
  • اختيار الذكريات: جلب فقط الذكريات المرتبطة بالمهمة الحالية — وليس كل شيء حفظه الوكيل.
  • اختيار التعليمات: مراحل مختلفة من المحادثة تحتاج تعليمات مختلفة تماماً.
from typing import Optional

class DynamicContextSelector:
    """اختيار السياق الديناميكي — يقدم للنموذج فقط ما يحتاجه"""

    def __init__(self, tools: dict, knowledge_base, memory_store):
        self.tools = tools
        self.knowledge_base = knowledge_base
        self.memory_store = memory_store

    def select_tools(self, user_query: str, max_tools: int = 5) -> list:
        """اختيار الأدوات ذات الصلة فقط — تجنب إغراق النموذج"""
        query_lower = user_query.lower()

        # تصنيف الاستعلام لتحديد فئة الأدوات المناسبة
        tool_categories = {
            "search": ["بحث", "ابحث", "جد", "find", "search"],
            "calculate": ["احسب", "كم", "calculate", "math"],
            "database": ["قاعدة بيانات", "استعلام", "query", "database"],
            "file": ["ملف", "اقرأ", "اكتب", "file", "read", "write"],
        }

        relevant_categories = []
        for category, keywords in tool_categories.items():
            if any(kw in query_lower for kw in keywords):
                relevant_categories.append(category)

        # إذا لم نجد تطابقاً، نقدّم الأدوات الأكثر استخداماً
        if not relevant_categories:
            relevant_categories = ["search", "calculate"]

        selected = []
        for cat in relevant_categories:
            if cat in self.tools:
                selected.extend(self.tools[cat][:max_tools])

        return selected[:max_tools]

    def build_context(
        self,
        user_query: str,
        conversation_history: list,
        max_tokens: int = 3000
    ) -> dict:
        """بناء السياق الكامل للنموذج — منظّم ومحسوب"""
        context = {
            "system_instructions": self._get_stage_instructions(
                conversation_history
            ),
            "relevant_tools": self.select_tools(user_query),
            "retrieved_knowledge": self.knowledge_base.search(
                user_query, top_k=3
            ),
            "user_memories": self.memory_store.recall(
                user_query, limit=5
            ),
            "recent_history": conversation_history[-6:],
        }
        return context

    def _get_stage_instructions(self, history: list) -> str:
        """تعليمات مختلفة حسب مرحلة المحادثة"""
        turn_count = len(history)
        if turn_count == 0:
            return "أنت مساعد ذكي. ابدأ بفهم هدف المستخدم الرئيسي."
        elif turn_count < 4:
            return "استمر في جمع المتطلبات وتوضيح التفاصيل."
        else:
            return "قدّم حلولاً عملية ومحددة بناءً على ما تعلمته."

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

3. الضغط (Compress): تكثيف السياق بدون فقدان المعنى

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

Anthropic تسمّي تقنيتها الأساسية "الانضغاط" (Compaction): عندما تقترب المحادثة من حد نافذة السياق، تُلخَّص محتوياتها وتُعاد تهيئة نافذة جديدة بالملخص. النتيجة؟ يستمر الوكيل بأداء شبه مثالي دون أن يشعر المستخدم بأي انقطاع.

class ContextCompressor:
    """ضغط السياق — تلخيص ذكي للمحادثات الطويلة"""

    MAX_HISTORY_ITEMS = 20
    SUMMARY_THRESHOLD = 15

    def __init__(self, llm_client):
        self.llm_client = llm_client

    def compress_history(self, conversation_history: list) -> list:
        """ضغط تاريخ المحادثة مع الحفاظ على السياق الجوهري"""
        if len(conversation_history) <= self.SUMMARY_THRESHOLD:
            return conversation_history

        # الاحتفاظ بأول رسالتين (تهيئة الجلسة) وآخر 5 (الأحدث)
        initialization = conversation_history[:2]
        recent = conversation_history[-5:]
        middle = conversation_history[2:-5]

        # تلخيص الجزء الأوسط
        summary = self._summarize_messages(middle)

        return initialization + [summary] + recent

    def _summarize_messages(self, messages: list) -> dict:
        """تلخيص مجموعة رسائل إلى رسالة واحدة مضغوطة"""
        messages_text = "\n".join(
            [f"{m['role']}: {m['content']}" for m in messages]
        )

        summary_prompt = f"""لخّص المحادثة التالية في فقرة واحدة مركّزة.
احتفظ بـ: القرارات المتخذة، الحقائق المكتشفة، تفضيلات المستخدم.
احذف: التكرارات، المحادثات الجانبية، التفاصيل غير المهمة.

المحادثة:
{messages_text}"""

        summary_text = self.llm_client.generate(summary_prompt)

        return {
            "role": "system",
            "content": f"[ملخص {len(messages)} رسالة سابقة]: {summary_text}",
            "metadata": {"type": "compressed", "original_count": len(messages)}
        }

    def hierarchical_compress(self, history: list) -> list:
        """ضغط هرمي — الأحدث بالتفصيل والأقدم ملخص"""
        if len(history) <= 10:
            return history

        layers = []

        # الطبقة 1: آخر 5 رسائل كاملة (تفصيل كامل)
        layers.extend(history[-5:])

        # الطبقة 2: الـ10 السابقة ملخصة جزئياً
        if len(history) > 15:
            mid_section = history[-15:-5]
            mid_summary = self._summarize_messages(mid_section)
            layers.insert(0, mid_summary)

        # الطبقة 3: كل ما قبل ذلك ملخص بإيجاز شديد
        if len(history) > 15:
            old_section = history[:-15]
            old_summary = self._summarize_messages(old_section)
            old_summary["content"] = "[ملخص موجز]: " + old_summary["content"]
            layers.insert(0, old_summary)

        return layers

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

4. العزل (Isolate): فصل السياقات لتركيز أفضل

وهنا ندخل في الجزء الممتع. العزل يعني تقسيم المهمة إلى أجزاء معزولة — باستخدام وكلاء متعددين أو أدوات محاصَرة — بحيث يبقى كل سياق مركّزاً ضمن حجم يمكن إدارته.

هذا هو الرابط المباشر بين هندسة السياق وأنظمة الوكلاء المتعددة. بدلاً من حشو كل شيء في سياق واحد (وتحويله لفوضى)، تُوزَّع المهمة على وكلاء متخصصين — كل واحد بنافذة سياق مستقلة ومركّزة.

import asyncio
from dataclasses import dataclass

@dataclass
class AgentResult:
    agent_name: str
    output: str
    confidence: float

class IsolatedAgentOrchestrator:
    """تنسيق وكلاء معزولين — كل وكيل بسياق مستقل"""

    def __init__(self, llm_client):
        self.llm_client = llm_client

    async def research_with_isolation(self, query: str) -> dict:
        """بحث معزول — كل جانب يُعالج بسياق منفصل"""

        # تشغيل ثلاثة وكلاء بالتوازي بسياقات معزولة
        tasks = [
            self._run_isolated_agent(
                "باحث_تقني",
                f"ابحث عن الجوانب التقنية فقط لهذا الموضوع: {query}",
                "أنت باحث تقني متخصص. ركّز على التفاصيل التقنية فقط."
            ),
            self._run_isolated_agent(
                "محلل_سوق",
                f"حلل جوانب السوق والمنافسة لهذا الموضوع: {query}",
                "أنت محلل سوق متخصص. ركّز على الاتجاهات والفرص التجارية."
            ),
            self._run_isolated_agent(
                "مراجع_مخاطر",
                f"حدد المخاطر والتحديات المحتملة: {query}",
                "أنت خبير إدارة مخاطر. ركّز على نقاط الضعف والتحديات."
            ),
        ]

        results = await asyncio.gather(*tasks)

        # دمج النتائج في سياق نظيف ومركّز
        return self._merge_results(results)

    async def _run_isolated_agent(
        self, name: str, task: str, system_prompt: str
    ) -> AgentResult:
        """تشغيل وكيل واحد بسياق معزول تماماً"""
        response = await self.llm_client.generate_async(
            system=system_prompt,
            messages=[{"role": "user", "content": task}],
            max_tokens=1000
        )
        return AgentResult(
            agent_name=name,
            output=response.text,
            confidence=response.confidence
        )

    def _merge_results(self, results: list[AgentResult]) -> dict:
        """دمج نتائج الوكلاء المعزولين في تقرير موحّد"""
        return {
            "combined_analysis": {
                r.agent_name: {
                    "findings": r.output,
                    "confidence": r.confidence
                }
                for r in results
            },
            "agent_count": len(results)
        }

لماذا العزل مهم لهذه الدرجة؟ فريق Manus أشار إلى ملاحظة ذكية: نماذج اللغة "مقلّدة ممتازة" — تميل لتكرار أنماط السلوك الموجودة في السياق. يعني إذا امتلأ السياق بأزواج متشابهة من الأفعال والملاحظات، سيتبع النموذج نفس النمط حتى لو لم يعد مناسباً. العزل يكسر هذه الحلقة ويمنح كل وكيل فرصة للتفكير بحرية.

أنظمة الذاكرة: العمود الفقري لهندسة السياق

الذاكرة قصيرة المدى مقابل الذاكرة طويلة المدى

تماماً كالبشر، تطبيقات الذكاء الاصطناعي تعتمد على نوعين من الذاكرة:

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

وهنا رقم يستحق التوقف عنده: الأبحاث تُظهر أن أنظمة الذاكرة المصممة بعناية تُحسّن دقة المحادثات متعددة الأدوار بنسبة 30-50%. هذا فارق ضخم لا يمكن تجاهله.

نموذج عملي: طبقات الذاكرة الثلاث

لبناء وكلاء ذكية فعّالة حقاً، يوصي الخبراء بتقسيم الذاكرة إلى ثلاث طبقات — وكل طبقة لها دور مختلف تماماً:

from typing import Optional
import json

class SemanticMemory:
    """الذاكرة الدلالية (المكتبة) — المعرفة العامة والوثائق"""

    def __init__(self, vector_store):
        self.vector_store = vector_store

    def store_knowledge(self, content: str, metadata: dict):
        """تخزين معرفة في قاعدة البيانات المتجهية"""
        self.vector_store.add(content, metadata=metadata)

    def retrieve(self, query: str, top_k: int = 3) -> list:
        """استرجاع المعرفة الأكثر صلة"""
        return self.vector_store.similarity_search(query, k=top_k)


class EpisodicMemory:
    """الذاكرة العرضية (اليوميات) — تفاعلات المستخدم وتفضيلاته"""

    def __init__(self, store):
        self.store = store

    def record_interaction(self, user_id: str, event: dict):
        """تسجيل تفاعل أو تفضيل للمستخدم"""
        key = f"user:{user_id}:episodes"
        self.store.append(key, {
            **event,
            "recorded_at": datetime.utcnow().isoformat()
        })

    def recall_preferences(self, user_id: str, topic: str) -> list:
        """تذكّر تفضيلات المستخدم المتعلقة بموضوع معين"""
        key = f"user:{user_id}:episodes"
        all_episodes = self.store.get_all(key)
        return [
            ep for ep in all_episodes
            if topic.lower() in ep.get("content", "").lower()
        ]


class WorkingMemory:
    """الذاكرة العاملة (السبورة) — المهمة الحالية فقط"""

    def __init__(self):
        self.current_task = None
        self.intermediate_results = []
        self.active_tools = []

    def set_task(self, task_description: str):
        self.current_task = task_description
        self.intermediate_results = []

    def add_result(self, step: str, result: str):
        self.intermediate_results.append({
            "step": step, "result": result
        })

    def get_summary(self) -> str:
        """ملخص الذاكرة العاملة لحقنه في السياق"""
        if not self.current_task:
            return ""
        steps = "\n".join(
            [f"- {r['step']}: {r['result'][:100]}"
             for r in self.intermediate_results]
        )
        return f"المهمة الحالية: {self.current_task}\nالتقدم:\n{steps}"


class MemoryManager:
    """مدير الذاكرة — ينسّق بين الطبقات الثلاث"""

    def __init__(self, semantic, episodic, working):
        self.semantic = semantic
        self.episodic = episodic
        self.working = working

    def build_memory_context(
        self, user_id: str, query: str
    ) -> str:
        """بناء سياق الذاكرة الكامل من جميع الطبقات"""
        parts = []

        # 1. المعرفة ذات الصلة من الذاكرة الدلالية
        knowledge = self.semantic.retrieve(query, top_k=3)
        if knowledge:
            parts.append("## معرفة ذات صلة")
            for doc in knowledge:
                parts.append(f"- {doc.content[:200]}")

        # 2. تفضيلات المستخدم من الذاكرة العرضية
        prefs = self.episodic.recall_preferences(user_id, query)
        if prefs:
            parts.append("## تفضيلات المستخدم")
            for p in prefs[-3:]:
                parts.append(f"- {p['content']}")

        # 3. حالة المهمة الحالية من الذاكرة العاملة
        working_summary = self.working.get_summary()
        if working_summary:
            parts.append(f"## المهمة الحالية\n{working_summary}")

        return "\n\n".join(parts)

لنفهم الفكرة بمثال واقعي: تخيّل أن المستخدم أخبر وكيل حجز السفر "أنا أكره الرحلات الصباحية المبكرة". هذا التفضيل يُخزَّن في الذاكرة العرضية. في كل مرة يُستدعى فيها وكيل البحث عن الرحلات لاحقاً، يُسترجع هذا التفضيل ويُحقن في السياق تلقائياً — دون أن يحتاج المستخدم لتكرار نفسه مرة أخرى. بصراحة، هذا هو الفرق بين وكيل ذكي حقيقي ومجرد واجهة محادثة.

تحسين الأداء: ذاكرة KV-Cache ونافذة السياق

لماذا تهم ذاكرة KV-Cache؟

شارك فريق Manus درساً مهماً من تجربتهم: معدل إصابة KV-Cache هو المقياس الأهم على الإطلاق لوكيل ذكي في بيئة الإنتاج. لماذا؟ لأنه يؤثر مباشرة على التكلفة وزمن الاستجابة — وهما العاملان اللذان يحددان قابلية التوسع.

الفكرة ببساطة: عندما يُعالج النموذج اللغوي طلباً، يبني ذاكرة مؤقتة (KV-Cache) للرموز التي عالجها. إذا كان الطلب التالي يبدأ بنفس البادئة، يمكن إعادة استخدام هذه الذاكرة بدلاً من إعادة حسابها من الصفر — وهذا يوفّر وقتاً وتكلفة بشكل ملحوظ.

نمط البادئة الثابتة والذيل المتغير

فريق Google ADK اعتمد نمطاً ذكياً لتعظيم إصابات KV-Cache، والفكرة بسيطة: قسّم نافذة السياق إلى منطقتين:

  • بادئة ثابتة (Stable Prefix): تعليمات النظام، هوية الوكيل، الملخصات طويلة المدى — تبقى ثابتة ولا تتغير بين الطلبات.
  • ذيل متغير (Variable Suffix): آخر رسالة من المستخدم، مخرجات الأدوات الجديدة، التحديثات الآنية — يتغير مع كل طلب.
class KVCacheOptimizedContext:
    """بنية سياق محسّنة لتعظيم إصابات KV-Cache"""

    def __init__(self, system_prompt: str, agent_identity: str):
        # البادئة الثابتة — لا تتغير أبداً بين الطلبات
        self.stable_prefix = {
            "system": system_prompt,
            "identity": agent_identity,
            "tools_schema": None,  # يُعيَّن مرة واحدة
            "long_term_summary": ""
        }
        # الذيل المتغير — يتغير مع كل طلب
        self.variable_suffix = []

    def set_tools(self, tools_schema: list):
        """تعيين مخطط الأدوات (مرة واحدة)"""
        self.stable_prefix["tools_schema"] = tools_schema

    def update_summary(self, summary: str):
        """تحديث الملخص طويل المدى"""
        self.stable_prefix["long_term_summary"] = summary

    def build_messages(self, new_user_message: str) -> list:
        """بناء قائمة الرسائل مع الحفاظ على ثبات البادئة"""
        messages = []

        # البادئة الثابتة أولاً (تستفيد من KV-Cache)
        system_content = (
            f"{self.stable_prefix['system']}\n\n"
            f"هويتك: {self.stable_prefix['identity']}\n\n"
        )
        if self.stable_prefix["long_term_summary"]:
            system_content += (
                f"ملخص السياق: "
                f"{self.stable_prefix['long_term_summary']}"
            )

        messages.append({"role": "system", "content": system_content})

        # الذيل المتغير — الرسائل الأخيرة فقط
        messages.extend(self.variable_suffix[-10:])
        messages.append({"role": "user", "content": new_user_message})

        return messages

    def append_exchange(self, user_msg: str, assistant_msg: str):
        """إضافة تبادل جديد للذيل المتغير"""
        self.variable_suffix.append(
            {"role": "user", "content": user_msg}
        )
        self.variable_suffix.append(
            {"role": "assistant", "content": assistant_msg}
        )

المبدأ الأساسي واضح: رتّب خط أنابيب السياق بحيث تبقى الأجزاء الثابتة في بداية النافذة، وادفع المحتوى المتغير نحو النهاية. بهذه الطريقة، معظم الرموز في البادئة ستكون مخزّنة مسبقاً في KV-Cache وتوفّر عليك حسابات مكلفة.

الأخطاء الشائعة في هندسة السياق وكيفية تجنبها

بعد مراجعة تجارب عشرات الفِرق في بناء وكلاء إنتاجية، هذه أكثر الأخطاء شيوعاً — وبعضها وقعت فيه شخصياً قبل أن أتعلم:

1. فخ "حشو السياق" (Context Dumping)

يعني أن تضع كميات ضخمة من البيانات مباشرة في سجل المحادثة — ملف CSV بحجم 5MB، أو استجابة JSON ضخمة من API، أو نص كامل لمستند PDF. يبدو الأمر منطقياً ("أعطِ النموذج كل شيء وخلّيه يقرر") لكنه يخلق "ضريبة دائمة" على الجلسة حيث يُسحب هذا الحمل مع كل دورة تفاعل.

الحل: لخّص البيانات الكبيرة قبل حقنها، أو استخدم الاسترجاع عند الطلب بدلاً من التحميل المسبق.

2. تسميم السياق (Context Poisoning)

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

الحل: أضف آليات تحقق لمخرجات الأدوات قبل حقنها في السياق، واستخدم فترات "نسيان منظّم" لتنظيف السياق دورياً.

3. تشتت السياق (Context Distraction)

حتى لو كانت المعلومات صحيحة، إغراق السياق بمعلومات غير ذات صلة يُشتت النموذج. مثال طريف: اكتشف باحثون أن وكيلاً ذكياً يلعب لعبة Pokémon بدأ يُكرر نفس الأفعال من سجله بدلاً من ابتكار استراتيجيات جديدة — لأن السياق المتنامي أصبح عبئاً لا مساعدة!

الحل: اقتطع سجل المحادثة بانتظام، لخّص الأجزاء القديمة، وقدّم فقط المعلومات المرتبطة مباشرة بالمهمة الحالية.

4. تجاهل "الحيز المفقود في المنتصف" (Lost in the Middle)

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

الحل: ضع المعلومات الأهم في البداية (تعليمات النظام) أو النهاية (الاستعلام الأخير). استخدم بنية هرمية واضحة مع عناوين تساعد النموذج على التنقل.

5. تعليمات "فكّر خطوة بخطوة" مع نماذج الاستدلال

هذه قد تفاجئك: عبارة "Think step by step" — التي اعتدنا عليها جميعاً — يمكن أن تضر بأداء نماذج الاستدلال الحديثة مثل Claude Opus وGPT-5. السبب؟ هذه النماذج تعالج الاستدلال داخلياً بالفعل، وتعليمات سلسلة التفكير الصريحة أصبحت زائدة أو حتى مُربكة لها. وثائق OpenAI نفسها تحذّر من ذلك الآن.

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

أدوات وأُطر عمل هندسة السياق في 2026

النظام البيئي لأدوات هندسة السياق نضج بشكل ملحوظ. إليك أبرز الأُطر وما تتفوق فيه:

الإطارالقدرة الأساسيةالأفضل لـ
LangGraphتحكم دقيق بالسياق لكل خطوة، ذاكرة قصيرة وطويلة المدىالوكلاء المعقدين متعددي الخطوات
LlamaIndexكتل ذاكرة جاهزة (متجهية، استخلاص حقائق، ثابتة)أنظمة RAG المتقدمة
LangMemتجريدات متقدمة لإدارة الذاكرة مع LangGraphالذاكرة طويلة المدى عبر الجلسات
LLMLinguaضغط الأوامر والسياق آلياًتقليل استهلاك الرموز وتسريع الاستجابة
Weaviate / Elysiaبحث هجين واسترجاع ديناميكيالبحث الدلالي وتنسيق الوكلاء

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

تجميع كل شيء: بناء وكيل ذكي بهندسة سياق متكاملة

والآن، لنجمع كل ما ناقشناه في مثال عملي واحد — وكيل مساعد تقني يستخدم الاستراتيجيات الأربع معاً:

class ContextEngineeredAgent:
    """وكيل ذكي بهندسة سياق متكاملة"""

    def __init__(self, llm_client, vector_store, memory_store):
        self.llm = llm_client

        # الاستراتيجية 1 — الكتابة: دفتر ملاحظات مستمر
        self.scratchpad = AgentScratchpad()

        # الاستراتيجية 2 — الاختيار: سياق ديناميكي
        self.selector = DynamicContextSelector(
            tools=self._load_tools(),
            knowledge_base=vector_store,
            memory_store=memory_store
        )

        # الاستراتيجية 3 — الضغط: إدارة المحادثات الطويلة
        self.compressor = ContextCompressor(llm_client)

        # الاستراتيجية 4 — العزل: تنسيق وكلاء متخصصين
        self.orchestrator = IsolatedAgentOrchestrator(llm_client)

        # تحسين الأداء — بنية KV-Cache
        self.kv_context = KVCacheOptimizedContext(
            system_prompt="أنت مساعد تقني خبير ومفيد.",
            agent_identity="مساعد تقني متخصص في Python وبنية الأنظمة"
        )

        self.conversation_history = []

    async def handle_message(
        self, user_id: str, message: str
    ) -> str:
        """معالجة رسالة المستخدم بهندسة سياق كاملة"""

        # 1. ضغط السجل إذا أصبح طويلاً
        self.conversation_history = (
            self.compressor.compress_history(
                self.conversation_history
            )
        )

        # 2. اختيار السياق المناسب ديناميكياً
        context = self.selector.build_context(
            user_query=message,
            conversation_history=self.conversation_history
        )

        # 3. بناء الرسائل مع تحسين KV-Cache
        messages = self.kv_context.build_messages(message)

        # 4. إضافة السياق المسترجع
        context_msg = (
            f"[سياق مسترجع]:\n"
            f"{json.dumps(context['retrieved_knowledge'], ensure_ascii=False)}"
        )
        messages.insert(-1, {
            "role": "system", "content": context_msg
        })

        # 5. توليد الاستجابة
        response = await self.llm.generate_async(
            messages=messages,
            tools=context["relevant_tools"]
        )

        # 6. كتابة الملاحظات للمرجعية المستقبلية
        self.scratchpad.write_note(
            "interaction",
            f"سؤال: {message[:100]} | إجابة: {response.text[:100]}"
        )

        # 7. تحديث السجل
        self.kv_context.append_exchange(message, response.text)
        self.conversation_history.append(
            {"role": "user", "content": message}
        )
        self.conversation_history.append(
            {"role": "assistant", "content": response.text}
        )

        return response.text

    def _load_tools(self) -> dict:
        """تحميل الأدوات المتاحة مصنّفة حسب الفئة"""
        return {
            "search": [
                {"name": "web_search", "description": "البحث في الويب"},
                {"name": "docs_search", "description": "البحث في التوثيق"}
            ],
            "calculate": [
                {"name": "calculator", "description": "العمليات الحسابية"}
            ],
            "database": [
                {"name": "sql_query", "description": "استعلام قاعدة البيانات"}
            ]
        }

هذا المثال يوضّح كيف تتكامل الاستراتيجيات الأربع في وكيل واحد متماسك. كل طلب يمر عبر خط أنابيب مُحسَّن: ضغط السجل القديم، اختيار السياق المناسب، بناء الرسائل بطريقة صديقة لـ KV-Cache، ثم تدوين الملاحظات للمستقبل. هذه هي هندسة السياق في أبهى صورها.

الأسئلة الشائعة

ما الفرق بين هندسة السياق وهندسة الأوامر (Prompt Engineering)؟

هندسة الأوامر تركّز على صياغة التعليمات لتفاعل واحد — كيف تسأل النموذج. هندسة السياق أوسع بكثير: تصمّم النظام الكامل الذي يحدد ما يعرفه النموذج عند كل تفاعل — من الذاكرة والأدوات المتاحة إلى المعرفة المسترجعة وسجل المحادثة. كلاهما مهم ومتكامل، لكن هندسة السياق هي ما يصنع الفارق في الأنظمة الإنتاجية الحقيقية.

هل أحتاج هندسة السياق لتطبيق بسيط يستخدم ChatGPT API؟

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

كيف أقيس جودة هندسة السياق في نظامي؟

هناك أربعة مقاييس عملية يجب مراقبتها: (1) معدل إصابة KV-Cache — كلما ارتفع، قلّت التكلفة وزمن الاستجابة. (2) نسبة الإشارة إلى الضوضاء — كم من المعلومات المحقونة يستخدمها النموذج فعلياً في إجابته. (3) دقة الاسترجاع — هل المعلومات المسترجعة ذات صلة حقيقية بالسؤال. (4) استهلاك الرموز لكل طلب — يجب أن يبقى ضمن حدود معقولة ولا يتضخم مع مرور الوقت.

ما أفضل طريقة للتعامل مع المحادثات الطويلة جداً؟

التلخيص الهرمي هو أفضل ما جرّبته: احتفظ بالرسائل الأخيرة كاملة (آخر 5-10 رسائل)، لخّص الرسائل المتوسطة في فقرة واحدة، واختصر الأقدم في جملة أو جملتين. تقنية الانضغاط (Compaction) من Anthropic تُنفّذ هذا تلقائياً وتُعيد تهيئة النافذة بملخص عالي الدقة عند الاقتراب من الحد الأقصى — وهي تعمل بشكل ممتاز في الممارسة العملية.

هل تعمل هندسة السياق مع جميع نماذج اللغة الكبيرة أم فقط مع نماذج محددة؟

المبادئ الأربعة (الكتابة والاختيار والضغط والعزل) عالمية وتعمل مع أي نموذج — سواء كان Claude Opus أو GPT-5 أو Gemini أو النماذج مفتوحة المصدر مثل Llama. الفرق يكمن في التفاصيل التقنية: حجم نافذة السياق يختلف، ودعم KV-Cache يتفاوت، وبعض النماذج تتعامل مع السياق الطويل أفضل من غيرها. لكن الاستراتيجيات نفسها تنطبق على الجميع.

عن الكاتب Editorial Team

Our team of expert writers and editors.