برمجة ذاكرة التخزين المؤقت (Buffer Memory) للاحتفاظ بسياق المحادثة قصيرة المدى
ماذا سنتعلم اليوم؟ في هذا الدرس، سنستكشف كيفية برمجة ذاكرة التخزين المؤقت (Buffer Memory) باستخدام LangChain و Python للحفاظ على سياق المحادثة في تطبيقات الذكاء الاصطناعية التخاطبية. سنبني نظامًا بسيطًا يتذكر المحادثات السابقة لتقديم استجابات متماسكة.
الخطوة 1: تهيئة البيئة والمكتبات الأساسية
نبدأ باستيراد المكتبات الضرورية من LangChain وتهيئة نموذج اللغة الكبير (LLM) الذي سنستخدمه. في هذا المثال، سنستخدم OpenAI GPT.
ملاحظة تقنية: تأكد من تثبيت مكتباتlangchainوopenaiباستخدامpip install langchain langchain-openai. يجب عليك أيضًا تعيين مفتاح API الخاص بـ OpenAI كمتغير بيئة أو تمريره مباشرة.
import os
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain_core.prompts import PromptTemplate
# تعيين مفتاح OpenAI API (استبدل 'YOUR_OPENAI_API_KEY' بمفتاحك الحقيقي أو استخدم متغير بيئة)
# يفضل استخدام os.environ.get("OPENAI_API_KEY") لبيئة الإنتاج
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# تهيئة نموذج اللغة الكبير (LLM)
# يمكن استخدام نماذج أخرى مثل 'gpt-3.5-turbo' أو 'gpt-4'
llm = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
الخطوة 2: إعداد ذاكرة التخزين المؤقت (Buffer Memory)
تعتبر ConversationBufferMemory أحد أبسط أنواع الذاكرة في LangChain. إنها تخزن جميع رسائل المحادثة (المدخلات والمخرجات) في قائمة، مما يسمح للنموذج بالوصول إلى تاريخ المحادثة بالكامل.
# تهيئة ذاكرة التخزين المؤقت
# 'memory_key' هو المفتاح الذي ستستخدمه السلسلة للوصول إلى الذاكرة
# 'return_messages=True' سيجعل الذاكرة تعيد تاريخ المحادثة كقائمة من كائنات الرسائل، وهو مفيد لـ ChatModels
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
الخطوة 3: بناء سلسلة المحادثة (Conversation Chain)
سنستخدم ConversationChain من LangChain، والتي تم تصميمها خصيصًا لإنشاء روبوتات محادثة بسيطة. تتولى هذه السلسلة دمج الـ LLM والذاكرة والقالب لإنشاء موجه (prompt) كامل للنموذج.
ملاحظة تقنية:ConversationChainتتطلب قالبًا (prompt template) يجب أن يحتوي على متغيرhistoryلسجل المحادثة ومتغيرinputلمدخلات المستخدم الحالية. في مثالنا، استخدمناchat_historyكـmemory_keyلذا يجب أن يتطابق اسم المتغير في القالب.
# تعريف قالب الموجه (Prompt Template)
# يجب أن يحتوي القالب على 'chat_history' (اسم الـ memory_key) و 'input'
template = """أنت مساعد ودود ومفيد.
تاريخ المحادثة:
{chat_history}
الإدخال الجديد: {input}
الرد:"""
# إنشاء كائن PromptTemplate
prompt = PromptTemplate(input_variables=["chat_history", "input"], template=template)
# بناء سلسلة المحادثة باستخدام LLM والذاكرة والقالب
conversation = ConversationChain(
llm=llm,
memory=memory,
prompt=prompt, # تمرير القالب المخصص
verbose=True # لطباعة الموجهات والاستجابات الوسيطة (مفيدة للتصحيح)
)
الخطوة 4: التفاعل مع السلسلة والحفاظ على السياق
الآن، حان الوقت لاختبار سلسلة المحادثة الخاصة بنا. سنرسل عدة استفسارات ونلاحظ كيف يتذكر النموذج السياق من التفاعلات السابقة.
# التفاعل الأول
print("المستخدم: مرحباً! ما هو اسمك؟")
response1 = conversation.predict(input="مرحباً! ما هو اسمك؟")
print(f"الروبوت: {response1}\n")
# التفاعل الثاني - يجب أن يتذكر الروبوت أنه تم سؤاله عن اسمه
print("المستخدم: هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟")
response2 = conversation.predict(input="هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟")
print(f"الروبوت: {response2}\n")
# التفاعل الثالث - يجب أن يتذكر الروبوت موضوع بايثون
print("المستخدم: ما هو الفرق بين القوائم والمصفوفات في بايثون؟")
response3 = conversation.predict(input="ما هو الفرق بين القوائم والمصفوفات في بايثون؟")
print(f"الروبوت: {response3}\n")
الكود النهائي الكامل
هذا هو السكربت كاملاً، جاهز للنسخ والتشغيل:
import os
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain_core.prompts import PromptTemplate
# تعيين مفتاح OpenAI API (استبدل 'YOUR_OPENAI_API_KEY' بمفتاحك الحقيقي أو استخدم متغير بيئة)
# يفضل استخدام os.environ.get("OPENAI_API_KEY") لبيئة الإنتاج
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# تهيئة نموذج اللغة الكبير (LLM)
llm = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# تهيئة ذاكرة التخزين المؤقت
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# تعريف قالب الموجه (Prompt Template)
template = """أنت مساعد ودود ومفيد.
تاريخ المحادثة:
{chat_history}
الإدخال الجديد: {input}
الرد:"""
# إنشاء كائن PromptTemplate
prompt = PromptTemplate(input_variables=["chat_history", "input"], template=template)
# بناء سلسلة المحادثة باستخدام LLM والذاكرة والقالب
conversation = ConversationChain(
llm=llm,
memory=memory,
prompt=prompt,
verbose=True
)
# التفاعل الأول
print("المستخدم: مرحباً! ما هو اسمك؟")
response1 = conversation.predict(input="مرحباً! ما هو اسمك؟")
print(f"الروبوت: {response1}\n")
# التفاعل الثاني - يجب أن يتذكر الروبوت أنه تم سؤله عن اسمه
print("المستخدم: هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟")
response2 = conversation.predict(input="هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟")
print(f"الروبوت: {response2}\n")
# التفاعل الثالث - يجب أن يتذكر الروبوت موضوع بايثون
print("المستخدم: ما هو الفرق بين القوائم والمصفوفات في بايثون؟")
response3 = conversation.predict(input="ما هو الفرق بين القوائم والمصفوفات في بايثون؟")
print(f"الروبوت: {response3}\n")
النتيجة المتوقعة
عند تشغيل السكربت، سترى مخرجات مفصلة (بسبب verbose=True) لكل تفاعل، بما في ذلك الموجه الكامل الذي يتم إرساله إلى نموذج اللغة الكبير، والذي سيحتوي على سجل المحادثة السابق. ستكون استجابات الروبوت متماسكة وتأخذ في الاعتبار السياق الذي تم بناؤه عبر التفاعلات، كما هو موضح في الأمثلة التالية:
المستخدم: مرحباً! ما هو اسمك؟ > Entering new ConversationChain chain... Prompt after formatting: أنت مساعد ودود ومفيد. تاريخ المحادثة: الإدخال الجديد: مرحباً! ما هو اسمك؟ الرد: > Finished chain. الروبوت: مرحباً! أنا مساعد ذكاء اصطناعي، ليس لدي اسم. كيف يمكنني مساعدتك اليوم؟ المستخدم: هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟ > Entering new ConversationChain chain... Prompt after formatting: أنت مساعد ودود ومفيد. تاريخ المحادثة: Human: مرحباً! ما هو اسمك؟ AI: مرحباً! أنا مساعد ذكاء اصطناعي، ليس لدي اسم. كيف يمكنني مساعدتك اليوم؟ الإدخال الجديد: هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟ الرد: > Finished chain. الروبوت: بالتأكيد! يسعدني مساعدتك في برمجة بايثون. ما هو سؤالك الأول؟ المستخدم: ما هو الفرق بين القوائم والمصفوفات في بايثون؟ > Entering new ConversationChain chain... Prompt after formatting: أنت مساعد ودود ومفيد. تاريخ المحادثة: Human: مرحباً! ما هو اسمك؟ AI: مرحباً! أنا مساعد ذكاء اصطناعي، ليس لدي اسم. كيف يمكنني مساعدتك اليوم؟ Human: هذا لطيف. هل يمكنك مساعدتي في برمجة بايثون؟ AI: بالتأكيد! يسعدني مساعدتك في برمجة بايثون. ما هو سؤالك الأول؟ الإدخال الجديد: ما هو الفرق بين القوائم والمصفوفات في بايثون؟ الرد: > Finished chain. الروبوت: في بايثون، القوائم (Lists) هي بنية بيانات مدمجة يمكن أن تحتوي على عناصر من أنواع بيانات مختلفة ويمكن تغيير حجمها. المصفوفات (Arrays) ليست نوعًا مدمجًا ولكنها متوفرة عبر مكتبات مثل NumPy، وتحتوي عادةً على عناصر من نفس نوع البيانات ولها حجم ثابت بعد الإنشاء.