AI Engineering Academy · درس

حفظ سجل المحادثة في Redis وPostgreSQL

خزّنوا سجل المحادثة خارجيًا باستخدام RedisChatMessageHistory وPostgresChatMessageHistory كي تستمر الذاكرة بعد إعادة تشغيل التطبيق وتتوسع عبر مثيلات متعددة.

الدرس 4 من 413 خطوة

حفظ سجل المحادثة في Redis وPostgreSQL درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

لماذا نحفظ سجل المحادثة خارجيًا؟

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

واجهة سجل رسائل المحادثة في LangChain

يعرّف LangChain واجهة BaseChatMessageHistory التي تتضمن طريقتين: add_message(message) وmessages (خاصية). ويمكن لأي جهة تخزين خلفية تطبّق هذه الواجهة أن تُستخدم مباشرةً كذاكرة لأي سلسلة من LangChain. وتتبع تطبيقات Redis وPostgreSQL هذه الواجهة.

from langchain_community.chat_message_histories import RedisChatMessageHistory

# Each unique session_id gets its own history namespace
history = RedisChatMessageHistory(
    session_id='user:alice:session:42',
    url='redis://localhost:6379'
)

# Add messages
history.add_user_message('What is RAG?')
history.add_ai_message('RAG stands for Retrieval-Augmented Generation...')

# Retrieve stored messages
for msg in history.messages:
    print(msg.type, ':', msg.content[:50])

ربط سجل Redis بسلسلة

غلّف سلسلتك باستخدام RunnableWithMessageHistory، ووفّر دالة مصنع تُرجع كائن السجل المناسب لكل جلسة. يحمّل LangChain السجل تلقائيًا قبل كل استدعاء، ويحفظ الرسائل الجديدة بعد كل استجابة.

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

llm = ChatOpenAI(model='gpt-4o-mini')
prompt = ChatPromptTemplate.from_messages([
    ('system', 'You are a helpful assistant.'),
    MessagesPlaceholder(variable_name='history'),
    ('human', '{input}')
])

chain = prompt | llm

chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: RedisChatMessageHistory(
        session_id=session_id,
        url='redis://localhost:6379'
    ),
    input_messages_key='input',
    history_messages_key='history'
)

استدعاء السلسلة باستخدام معرّف جلسة

عند استدعاء سلسلة مغلّفة بسجل الرسائل، مرّر معرّف الجلسة في قاموس config ضمن configurable. يوجّه LangChain كل استدعاء تلقائيًا إلى مخزن السجل الصحيح، ولذلك يمتلك Alice وBob سجلَّي محادثة منفصلين تمامًا.

# First turn for session alice-001
response = chain_with_history.invoke(
    {'input': 'My name is Alice.'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)

# Second turn — the chain remembers Alice's name from Redis
response = chain_with_history.invoke(
    {'input': 'What is my name?'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)  # Should say 'Your name is Alice.'

تصميم مفاتيح Redis لسجل المحادثة

يخزّن Redis رسائل كل جلسة في قائمة تحت مفتاح مشتق من معرّف الجلسة. استخدم مخططًا منظمًا للمفاتيح مثل chat:<app>:<user_id>:<session_id> لتجنّب تعارض المفاتيح بين التطبيقات، ولتمكين إجراء مسح فعّال حسب المستخدم. عيّن مدة صلاحية TTL لمفاتيح الجلسات القديمة لمنع نمو التخزين بلا حدود.

import redis

r = redis.Redis.from_url('redis://localhost:6379')

# List all chat sessions for user alice
keys = r.keys('message_store:alice-*')
print('Active sessions:', len(keys))

# Set a 30-day TTL on a session to auto-expire old history
for key in keys:
    r.expire(key, 60 * 60 * 24 * 30)

تصميم جدول سجل المحادثة في PostgreSQL

للاستمرارية المعتمدة على SQL، أنشئ جدولًا يتضمن أعمدة لمعرّف الجلسة، ودور الرسالة، والمحتوى، والطابع الزمني. يُعد PostgreSQL مناسبًا عندما تحتاج إلى الاستعلام عن سجل المحادثة أو تدقيقه أو ربطه ببيانات حسابات المستخدمين. وتوفّر حزمة langchain_postgres تطبيقًا جاهزًا.

-- SQL table for storing chat messages
CREATE TABLE chat_messages (
    id          SERIAL PRIMARY KEY,
    session_id  TEXT NOT NULL,
    role        TEXT NOT NULL,   -- 'human' or 'ai'
    content     TEXT NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_chat_session ON chat_messages (session_id, created_at);

‏PostgresChatMessageHistory في LangChain

تعمل الفئة PostgresChatMessageHistory من langchain_postgres مثل إصدار Redis تمامًا، لكنها تخزّن الرسائل في قاعدة بيانات PostgreSQL الخاصة بك. مرّر سلسلة اتصال ومعرّف جلسة، وستتولى تلقائيًا إنشاء الجدول وتسلسل الرسائل.

from langchain_postgres import PostgresChatMessageHistory
import psycopg

CONNECTION_STRING = 'postgresql://user:pass@localhost:5432/mydb'

# Create history object for a session
history = PostgresChatMessageHistory(
    table_name='chat_messages',
    session_id='alice-001',
    connection=psycopg.connect(CONNECTION_STRING)
)

history.add_user_message('Tell me about embeddings.')
history.add_ai_message('Embeddings are dense vector representations...')

print(f'Stored {len(history.messages)} messages')

تحميل الجلسات السابقة للمستخدمين العائدين

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

def get_or_create_history(user_id: str, session_id: str):
    history = RedisChatMessageHistory(
        session_id=f'{user_id}:{session_id}',
        url='redis://localhost:6379'
    )
    if history.messages:
        print(f'Resuming session with {len(history.messages)} messages.')
    else:
        print('Starting a new session.')
    return history

تقليص السجل في قاعدة البيانات

قد تتراكم في الجلسات طويلة الأمد آلاف الرسائل. طبّق استراتيجية للتقليص تحذف الرسائل الأقدم من حد زمني معيّن، أو تحتفظ فقط بآخر N رسالة في قاعدة البيانات. يحافظ ذلك على سرعة الاسترجاع ويمنع تجاوز نوافذ السياق عند تحميل السجل.

# Keep only the last 20 messages per session in PostgreSQL
CLEAN_SQL = '''
    DELETE FROM chat_messages
    WHERE session_id = %s
    AND id NOT IN (
        SELECT id FROM chat_messages
        WHERE session_id = %s
        ORDER BY created_at DESC
        LIMIT 20
    )
'''

def trim_session(conn, session_id: str, keep_last: int = 20):
    with conn.cursor() as cur:
        cur.execute(CLEAN_SQL, (session_id, session_id))
    conn.commit()

الاختيار بين Redis وPostgreSQL

كلا مخزني البيانات جاهز للاستخدام في بيئات الإنتاج، لكن لكل منهما نقاط قوة مختلفة. يوفّر Redis عمليات قراءة بزمن أقل من ميلي ثانية، ما يجعله مثاليًا للمحادثات الفورية التي تكون فيها مدة الاستجابة أهم عامل. ويوفّر PostgreSQL إمكانية الاستعلام الكامل باستخدام SQL، ومعاملات ACID، وتكاملًا سهلًا مع قاعدة بيانات المستخدمين الحالية لأغراض التحليلات والتدقيق. تستخدم كثير من أنظمة الإنتاج Redis كذاكرة تخزين مؤقت أساسية وPostgreSQL للأرشفة الدائمة.

الأمان: عزل السجلات في التطبيقات متعددة المستأجرين

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

  • مفتاح جيد: chat:app1:user:42:session:abc
  • مفتاح سيئ: session:abc (يمكن تخمينه ولا يحدد نطاق المستخدم)

تحقق سريع

اختبر مدى فهمك لحفظ سجل المحادثة خارجيًا.

مراجعة الدرس

في هذا الدرس تعلمت أن: RedisChatMessageHistory يخزّن سجل المحادثة في Redis باستخدام مفاتيح مخصصة لكل جلسة، وأن PostgresChatMessageHistory يتيح تخزينًا دائمًا يمكن الاستعلام عنه باستخدام SQL، وأن RunnableWithMessageHistory يربط أي مخزن بسلسلتك عبر مصنع يستخدم session_id. بعد ذلك سنتعمق في إطار عمل ReAct لبناء الوكلاء القادرين على الاستدلال والتنفيذ.

البدء مجانًا

تعلم Python مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
30
الدروس
120

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

هل درس «حفظ سجل المحادثة في Redis وPostgreSQL» مجاني؟

نعم — نص درس «حفظ سجل المحادثة في Redis وPostgreSQL» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

ماذا ستتعلم في «حفظ سجل المحادثة في Redis وPostgreSQL»؟

خزّنوا سجل المحادثة خارجيًا باستخدام RedisChatMessageHistory وPostgresChatMessageHistory كي تستمر الذاكرة بعد إعادة تشغيل التطبيق وتتوسع عبر مثيلات متعددة. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟

لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «حفظ سجل المحادثة في Redis وPostgreSQL»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟

نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. لماذا تحتاج LLMs عديمة الحالة إلى ذاكرة خارجية
  2. ذاكرة المخزن وذاكرة النافذة
  3. ذاكرة التلخيص والاقتطاع المراعي لعدد Tokens
  4. حفظ سجل المحادثة في Redis وPostgreSQL
← العودة إلى AI Engineering Academy