بناء تطبيقات RAG باستخدام قاعدة بيانات Azure ل PostgreSQL وPython

مكتمل

الآن بعد أن أصبحت قاعدة البيانات جاهزة مع التضمينات والفهارس المتجهة، حان الوقت لتحويل الاسترجاع إلى تطبيق عملي. الهدف بسيط: أخذ سؤال المستخدم، واسترجاع الأجزاء الأكثر صلة من PostgreSQL، وتوليد إجابة مبنية على تلك الأجزاء باستخدام Azure OpenAI عبر LangChain.

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

توليد Retrieval-Augmented باستخدام PostgreSQL و LangChain

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

  1. يطرح المستخدم سؤالا.
  2. تحسب قاعدة البيانات تضمين للسؤال وتستخدم فهرس المتجهات للعثور على أقرب التطابقات في قاعدة البيانات (الأجزاء العليا).
  3. يتم تمرير الأجزاء العليا إلى النموذج مع طلب يقول: "أجب فقط من هذا السياق. إذا لم تكن تعرف، قل ذلك."
  4. يعطي النموذج إجابة بلغة طبيعية بناء على السياق المقدم.

يوفر لانغتشين إطارا لبناء التطبيقات باستخدام نماذج اللغة. يبسط عملية الاتصال بمصادر البيانات المختلفة، وإدارة المحفزات، والتعامل مع الردود. من خلال دمج قاعدة بيانات Azure ل PostgreSQL مع Python و LangChain، يمكنك إنشاء تطبيق RAG قوي يسترجع المعلومات ذات الصلة ويولد ردودا دقيقة. بينما قد يتطور تطبيق RAG الخاص بك، فإن هذه المبادئ الأساسية توجه تطويره.

في هذا التطبيق، تستخدم غلاف LangChain Azure ChatOpenAI للتفاعل مع خدمة Azure OpenAI .

في أمثلة بايثون التالية، تستخدم هيكل الجداول التالي:

CREATE TABLE company_policies (
    id SERIAL PRIMARY KEY,
    title TEXT,
    policy_text TEXT,
    embedding VECTOR(1536)
);

في هذا الجدول، تفترض إنشاء مؤشر متجه على العمود embedding لتمكين عمليات بحث تشابه فعالة.

دعونا نراجع مقتطفات كود بايثون لكل خطوة. تتبع معظم تطبيقات RAG هيكلا مشابها.

الاتصال بقاعدة البيانات

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

import os, psycopg2
from contextlib import contextmanager

@contextmanager
def get_conn():
    conn = psycopg2.connect(
        host=os.getenv("PGHOST"),
        user=os.getenv("PGUSER"),
        password=os.getenv("PGPASSWORD"),
        dbname=os.getenv("PGDATABASE"),
        connect_timeout=10
    )
    try:
        yield conn
    finally:
        conn.close()

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

استرجاع القطع ذات الصلة

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

def retrieve_chunks(question, top_k=5):
    sql = """
    WITH q AS (
        SELECT azure_openai.create_embeddings(%s, %s)::vector AS qvec
    )
    SELECT id, title, policy_text
    FROM company_policies, q
    ORDER BY embedding <=> q.qvec
    LIMIT %s;
    """
    params = (os.getenv("OPENAI_EMBED_DEPLOYMENT"), question, top_k)
    with get_conn() as conn, conn.cursor() as cur:
        cur.execute(sql, params)
        rows = cur.fetchall()
    return [{"id": r[0], "title": r[1], "text": r[2]} for r in rows]

لذا تقوم هذه الدالة باسترجاع الأجزاء ذات الصلة من قاعدة البيانات بناء على سؤال المستخدم. ثم تستخدم هذه الأجزاء لتوليد إجابة واعية للسياق في خطوة لاحقة.

توليد إجابة باستخدام LangChain

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

from langchain_openai import AzureChatOpenAI

SYSTEM_PROMPT = """
You are a helpful assistant. Answer using ONLY the provided context.
If the answer is not in the context, say you don’t have enough information.
Cite policy titles in square brackets, e.g., [Vacation policy].
"""

def format_context(chunks):
    return "\n\n".join([f"[{c['title']}] {c['text']}" for c in chunks])

def generate_answer(question, chunks):
    llm = AzureChatOpenAI(
        azure_deployment=os.getenv("OPENAI_CHAT_DEPLOYMENT"),
        api_key=os.getenv("AZURE_OPENAI_API_KEY"),
        azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
        api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
        temperature=0
    )
    context = format_context(chunks)
    messages = [
        ("system", SYSTEM_PROMPT),
        ("human", f"Question: {question}\nContext:\n{context}")
    ]
    return llm.invoke(messages).content

إشعار

لاستخدام AzureChatOpenAI:

  • تمرير الرسائل كقائمة من الأدوار (الدور، المحتوى) الأتباع (أو كائنات الرسائل).
  • وفر إعدادات Azure عبر env vars وconstructor: AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, و azure_deployment, api_version.
  • احتفظ temperature=0 بالإجابات الواقعية. القيم الأكبر تزيد من الإبداع لكنها قد تقلل من الدقة.

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

اربطها معا

آخر شيء تحتاجه هو دالة بسيطة لتشغيل التدفق الكامل:

def answer_question(question):
    chunks = retrieve_chunks(question)
    if not chunks:
        return "I couldn’t find relevant content in the policy store."
    return generate_answer(question, chunks)

# Quick test
print(answer_question("How many vacation days do employees get?"))

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

النقاط الموجزة الأساسية

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

يجب أن يكون لديك الآن فهم قوي لكيفية بناء تطبيق التوليد المعزز (RAG) باستخدام قاعدة بيانات Azure ل PostgreSQLوPython. بينما قد يكون تطبيق RAG الخاص بك أكثر تعقيدا في سيناريو واقعي، إلا أن المبادئ الأساسية تبقى كما هي.