برمجة ذاكرة الملخصات (Summary Memory) للتعامل مع المحادثات الطويلة جداً بأقل تكلفة


ماذا سنبني اليوم؟ سنتعلم كيفية بناء ذاكرة تلخيصية (Summary Memory) باستخدام LangChain وOpenAI للتعامل بفعالية مع المحادثات الطويلة جداً، مما يقلل تكلفة استدعاء نماذج اللغة الكبيرة (LLMs) ويحسن إدارة سياق المحادثة.

الخطوة 1: إعداد البيئة وتجهيز النموذج

في هذه الخطوة، سنقوم بتثبيت المكتبات الضرورية وتهيئة نموذج اللغة الكبير (LLM) الذي سنستخدمه لتوليد الملخصات والإجابات. سنعتمد على OpenAI في هذا الدرس.

# تثبيت المكتبات اللازمة
# pip install langchain openai python-dotenv

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryBufferMemory
from langchain.prompts import PromptTemplate

# تحميل متغيرات البيئة من ملف .env (مثل مفتاح API الخاص بـ OpenAI)
load_dotenv()

# تهيئة نموذج اللغة الكبير (LLM)
# نستخدم gpt-3.5-turbo لفعاليته من حيث التكلفة والأداء
llm = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")

print("تم إعداد LLM بنجاح.")

الخطوة 2: بناء ذاكرة الملخصات (Summary Memory)

هنا سنقوم بإنشاء ذاكرة الملخصات الخاصة بنا. سنستخدم ConversationSummaryBufferMemory من LangChain، وهي ذاكرة تقوم بتلخيص المحادثة عندما يتجاوز عدد معين من التوكنات، مع الاحتفاظ بآخر الرسائل "كما هي" لضمان السياق القريب.

# تهيئة ذاكرة الملخصات
# llm: النموذج الذي سيستخدم لتوليد الملخصات
# max_token_limit: الحد الأقصى لعدد التوكنات التي سيتم الاحتفاظ بها في الذاكرة.
# عند تجاوز هذا الحد، سيتم تلخيص الجزء الأقدم من المحادثة.
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100) # يمكن تعديل الحد حسب الحاجة

print(f"تم تهيئة ذاكرة الملخصات بحد أقصى للتوكنات: {memory.max_token_limit}")

الخطوة 3: التفاعل مع الذاكرة وتجربة المحادثة

سنقوم الآن ببناء سلسلة محادثة (Conversation Chain) ونجري محادثة طويلة بما يكفي لتفعيل آلية التلخيص في الذاكرة. سنلاحظ كيف تتغير الذاكرة مع تقدم المحادثة.

# بناء سلسلة المحادثة
# llm: نموذج اللغة الكبير الذي سيجيب على الأسئلة
# memory: الذاكرة التي ستحتفظ بسجل المحادثة
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)

print("بدء المحادثة...")

# التفاعل الأول
print("\n--- التفاعل 1 ---")
response = conversation.predict(input="مرحباً! أنا مهتم بتعلم البرمجة. ما هي أفضل لغة أبدأ بها؟")
print(f"المساعد: {response}")
print(f"حالة الذاكرة بعد التفاعل 1:\n{memory.load_memory_variables({})}")

# التفاعل الثاني
print("\n--- التفاعل 2 ---")
response = conversation.predict(input="أريد بناء تطبيقات ويب. هل Python مناسبة لذلك؟ وما هي الأطر (Frameworks) الشائعة؟")
print(f"المساعد: {response}")
print(f"حالة الذاكرة بعد التفاعل 2:\n{memory.load_memory_variables({})}")

# التفاعل الثالث (محاولة تجاوز حد التوكنات لتفعيل التلخيص)
print("\n--- التفاعل 3 (توقع التلخيص) ---")
response = conversation.predict(input="ممتاز! لقد سمعت عن Django وFlask. ما الفرق الرئيسي بينهما؟ وهل هناك أي نصائح إضافية للمبتدئين في تطوير الويب باستخدام Python؟ أريد تفصيلاً معمقاً.")
print(f"المساعد: {response}")
print(f"حالة الذاكرة بعد التفاعل 3:\n{memory.load_memory_variables({})}")

# التفاعل الرابع (المزيد من المحادثة لتأكيد عمل التلخيص)
print("\n--- التفاعل 4 ---")
response = conversation.predict(input="شكراً جزيلاً على هذه المعلومات القيمة! ما هي الخطوة التالية التي تنصحني بها بعد تعلم الأساسيات وأحد الأطر؟")
print(f"المساعد: {response}")
print(f"حالة الذاكرة بعد التفاعل 4:\n{memory.load_memory_variables({})}")
ملاحظة تقنية: ConversationSummaryBufferMemory تعمل بذكاء. فهي لا تلخص المحادثة بالكامل في كل مرة، بل تحاول الاحتفاظ بالرسائل الأخيرة كما هي حتى يمتلئ الـ buffer، ثم تقوم بتلخيص الجزء الأقدم لتوفير المساحة، مع الحفاظ على أهم المعلومات في الملخص. هذا يوازن بين الحفاظ على السياق القريب وتقليل حجم الذاكرة.

الكود النهائي الكامل

هذا هو السكربت الكامل الذي يجمع جميع الخطوات المذكورة أعلاه. تأكد من وجود ملف .env في نفس مجلد السكربت يحتوي على OPENAI_API_KEY=your_api_key_here.

# تثبيت المكتبات اللازمة (قم بتشغيل هذا الأمر مرة واحدة)
# pip install langchain openai python-dotenv

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryBufferMemory
from langchain.prompts import PromptTemplate

# تحميل متغيرات البيئة من ملف .env
load_dotenv()

# تهيئة نموذج اللغة الكبير (LLM)
llm = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")

# تهيئة ذاكرة الملخصات بحد أقصى للتوكنات
# يمكن تعديل max_token_limit لتناسب طول المحادثات وتكلفة الاستخدام
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)

# بناء سلسلة المحادثة
conversation = ConversationChain(llm=llm, memory=memory, verbose=True)

print("بدء المحادثة مع ذاكرة الملخصات...")

# سلسلة من التفاعلات لاختبار الذاكرة والتلخيص
interactions = [
    "مرحباً! أنا مهتم بتعلم البرمجة. ما هي أفضل لغة أبدأ بها؟",
    "أريد بناء تطبيقات ويب. هل Python مناسبة لذلك؟ وما هي الأطر (Frameworks) الشائعة؟",
    "ممتاز! لقد سمعت عن Django وFlask. ما الفرق الرئيسي بينهما؟ وهل هناك أي نصائح إضافية للمبتدئين في تطوير الويب باستخدام Python؟ أريد تفصيلاً معمقاً.",
    "شكراً جزيلاً على هذه المعلومات القيمة! ما هي الخطوة التالية التي تنصحني بها بعد تعلم الأساسيات وأحد الأطر؟",
    "فهمت. وهل هناك أي موارد مجانية أو دورات تدريبية عالية الجودة تنصح بها للمبتدئين في تطوير الويب باستخدام بايثون؟"
]

for i, user_input in enumerate(interactions):
    print(f"\n--- التفاعل {i+1} ---")
    print(f"أنت: {user_input}")
    response = conversation.predict(input=user_input)
    print(f"المساعد: {response}")
    print(f"حالة الذاكرة بعد التفاعل {i+1}:\n{memory.load_memory_variables({})}")
    # طباعة ملخص الذاكرة لتوضيح التغييرات
    if "history" in memory.load_memory_variables({}):
        print(f"ملخص الذاكرة الحالي:\n{memory.load_memory_variables({})['history']}")

print("\nانتهت المحادثة.")

النتيجة المتوقعة

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

مثال على جزء من مخرجات الذاكرة بعد التلخيص:

{
    'history': 'The human is interested in learning programming and asks about the best language to start with. The AI recommends Python for beginners due to its versatility and ease of learning. The human then asks if Python is suitable for web development and about common frameworks. The AI confirms Python\'s suitability and mentions Django and Flask. The human asks for details on the difference between Django and Flask and tips for beginners in Python web development. The AI provides a detailed explanation of Django as a full-stack framework and Flask as a micro-framework, along with advice like starting small, practicing regularly, and understanding core concepts. The human expresses gratitude and asks for the next steps after learning basics and a framework.',
    'buffer': 'Human: فهمت. وهل هناك أي موارد مجانية أو دورات تدريبية عالية الجودة تنصح بها للمبتدئين في تطوير الويب باستخدام بايثون؟'
}

لاحظ كيف أن history يحتوي على ملخص طويل، بينما buffer يحتوي على آخر رسالة أو رسالتين فقط، مما يقلل بشكل كبير من عدد التوكنات المرسلة في كل طلب جديد.