إنشاء وكيل توليد معزز بالاسترجاع تصحيحي باستخدام IBM Granite وTavily

مؤلف

Jobit Varughese

Technical Content Writer

IBM

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

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

رسم تخطيطي للوثائق ذات الصلة

في هذا البرنامج التعليمي، ستتعرف على كيفية إنشاء نظام توليد معزز بالاسترجاع تصحيحي (cRAG) فائق الإمكانات باستخدام نماذج ®IBM® Granite على ®Watsonx وLangChain. يمكن أيضًا استخدام أطر عمل مماثلة مثل LlamaIndex أو LangGraph لإنشاء تدفقات توليد معزز بالاسترجاع معقدة ذات عقد مميزة. يمكن لتقنيات مثل الضبط الدقيق أن تعزز أداء النموذج اللغوي الكبير المحدد لنظام التوليد المعزز بالاسترجاع الخاص بمجال معين. وتعد النماذج اللغوية الكبيرة مثل تلك التابع لشركة OpenAI (على سبيل المثال، نماذج GPT مثل ChatGPT) خيارات شائعة أيضًا لمثل هؤلاء الوكلاء، على الرغم من أن هذا البرنامج التعليمي يركز على IBM Granite.

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

  • استرجاع المعلومات من مستند PDF الخاص بك.

  • إذا لم تكن المستندات الداخلية كافية لتوليد الإجابة، يمكن للوكيل استخدام البحث الخارجي على الويب (Tavily) كحل بديل.

  • يصفي الوكيل النتائج الخارجية غير ذات الصلة بذكاء بحيث تكون الإجابات مخصصة للسياسات الخاصة.

  • سيقدم الوكيل إجابات واضحة ومحدودة بمعلومات جزئية عند توفرها أو رفضًا واضحًا في حال عدم وجود سياق.

حالة الاستخدام: بناء وكيل استعلام جدير بالثقة لبوليصة تأمين

هذا البرنامج التعليمي هو عرض توضيحي لإنشاء وكيل استعلام لبوليصة تأمين مصمم لتحليل وثائق البوليصة (كتيب PDF) والإجابة على استفسارات المستخدمين بدقة. نستخدم نماذج IBM Granite وLangChain لبناء الوكيل بخطوات استرجاع وتحقق قوية تضمن إجابات عالية الجودة ومقيدة بالمصدر.

لنفهم كيف تنطبق المبادئ الأساسية للتوليد المعزز بالاسترجاع الموثوق في حالة الاستخدام الخاصة بنا.

تطبيق المبادئ الأساسية

قاعدة المعرفة الداخلية (PDF): المصدر الأساسي للمعلومات للوكيل هو ملف PDF لبوليصة التأمين التي قدمتها. يحول هذا المستند إلى مخزن متجهات قابل للبحث.

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

تقييم السياق: ستقيم أداة تقييم الاسترجاع القائم على النموذج اللغوي الكبير (التي تعمل كأداة تصنيف) مدى صلة العناصر المسترجعة من ملف PDF الداخلي الخاص بك مع ضمان تضمين العناصر المسترجعة عالية الجودة فقط.

إعادة صياغة الاستعلام: بالنسبة إلى عمليات البحث على الويب، يمكن للوكيل إعادة صياغة استعلام المستخدم لتحسين فرص العثور على المعلومات الخارجية ذات الصلة.

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

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

المتطلبات الأساسية

تحتاج إلى حساب ®IBM Cloud لإنشاء مشروع ®watsonx.ai . تأكد من أن لديك إمكانية الوصول إلى مفتاح واجهة برمجة تطبيقات watsonx ومعرّف المشروع. وستحتاج أيضًا إلى مفتاح واجهة برمجة التطبيقات لمحرك Tavily AI من أجل إمكانيات البحث على الويب.

الخطوات

الخطوة 1. إعداد البيئة.

رغم توفُّر عدة أدوات للاختيار منها، يُرشدك هذا البرنامج التعليمي خلال خطوات إعداد حساب IBM لاستخدام Jupyter Notebook.

  1. سجِّل الدخول إلى watsonx.ai باستخدام حساب IBM Cloud الخاص بك.
  2. أنشئ مشروع watsonx.ai.يمكنك الحصول على معرِّف المشروع من داخل مشروعك. انقر على علامة التبويب الإدارة (Manage)، ثم انسخ معرِّف المشروع من قسم التفاصيل (Details) في صفحة عام (General). ستحتاج إلى هذا المعرِّف في هذا البرنامج التعليمي.
  3. أنشئ Jupyter Notebook.

تفتح هذه الخطوة بيئة دفتر ملاحظات حيث يمكنك نسخ التعليمات البرمجية من هذا البرنامج التعليمي. أو يمكنك تنزيل هذا الدفتر إلى نظامك المحلي وتحميله إلى مشروع watsonx.ai كأصل. لمشاهدة المزيد من برامج Granite التعليمية، تفصل بزيارة مجتمعIBM Granite. هذا البرنامج التعليمي متاح أيضًا على Github.

الخطوة 2. إعداد خدمة وقت تشغيل watsonx.ai ومفتاح واجهة برمجة التطبيقات.

  1. أنشئ مثيل خدمة watsonx.ai Runtime (اختَر خطة Lite، وهي مثيل مجاني).
  2. أنشئ مفتاح واجهة برمجة تطبيقات (API).
  3. اربط مثيل خدمة watsonx.ai Runtime بالمشروع الذي أنشأته في watsonx.ai.

الخطوة 3. تثبيت الحزم

للعمل باستخدام إطار عمل LangChain ودمج IBM WatsonxLLM، نحتاج إلى تثبيت بعض المكتبات الأساسية. لنبدأ بتثبيت الحزم المطلوبة. تتضمن هذه المجموعة langchain لإطار عمل التوليد المعزز بالاسترجاع، وlangchain-ibm لدمج watsonx، و, faiss-cpu لتخزين المتجهات بكفاءة، وPyPDF2 لمعالجة ملفات PDF، ومحولات الجملة للحصول على تضمين وطلبات لاستدعاءات واجهة برمجة التطبيقات على الويب. هذه المكتبات ضرورية لتطبيق حلول التعلم الآلي ومعالجة اللغة الطبيعية.

# Install Libraries
!pip install langchain langchain-ibm faiss-cpu PyPDF2 sentence-transformers requests

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

الخطوة 4. استيراد المكتبات المطلوبة.

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

# Import required libraries

import os
import io
import getpass
from PyPDF2 import PdfReader
from langchain_ibm import WatsonxLLM
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
import requests
from botocore.client import Config
import ibm_boto3
from langchain.prompts import PromptTemplate
from langchain.tools import BaseTool

# Watsonx
WML_URL = "https://us-south.ml.cloud.ibm.com"
WML_API_KEY = getpass.getpass(" Enter Watsonx API Key: ")
PROJECT_ID = input(" Enter Watsonx Project ID: ")

# Tavily
TAVILY_API_KEY = getpass.getpass(" Enter Tavily API Key: ")

print(" Credentials loaded.")

يساعد os على العمل مع نظام التشغيل.

يتيح io العمل مع تدفقات البيانات.

يستخدم getpass طريقة آمنة لالتقاط المعلومات الحساسة مثل مفاتيح واجهة برمجة التطبيقات ولا يعرض المدخلات على الشاشة.

يسمح PyPDF2.PdfReader باستخراج المحتوى من ملفات PDF.

يسمح langchain_ibm.WatsonxLLM لنا باستخدام النموذج اللغوي الكبير IBM Watsonx Granite بسهولة ضمن إطار عمل LangChain.

يأخذ langchain.embeddings.HuggingFaceEmbeddings نموذج HuggingFace ويولد التضمينات النصية المهمة للبحث الدلالي.

langchain.vectorstores.FAISS هي مكتبة لتخزين المتجهات والبحث عن التشابهات بكفاءة، تتيح لنا إنشاء فهرس متجهات والاستعلام عنه.

يساعد langchain.text_splitter.RecursiveCharacterTextSplitter على تقسيم مكونات النص الكبيرة إلى أجزاء أصغر حجمًا لازمة لمعالجة المستندات التي لا تتسع في الذاكرة.

يمثل langchain.schema.Document وحدة نصية عشوائية تحمل بيانات وصفية مرتبطة بها، ما يجعلها عنصرًا أساسيًا في langchain.

تُستخدم requests لإجراء طلبات HTTP خارجية إلى واجهات برمجة التطبيقات.

botocore.client.Config هي فئة تكوين تُستخدم لتعريف إعدادات التكوين لعميل AWS/IBM Cloud Object Storage.

ibm_boto3 هو مجموعة تطوير برمجيات IBM Cloud Object Storage للغة Python الذي يساعد على التفاعل مع cloud object storage.

يوفر langchain.prompts.PromptTemplate طريقة لإنشاء موجهات قابلة لإعادة الاستخدام ومنظمة للنماذج اللغوية.

langchain.tools.BaseTool هي الفئة الأساسية التي يمكنك من خلالها إنشاء أدوات مخصصة يمكن توفيرها لوكلاء LangChain لاستخدامها.

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

الخطوة 5. تحميل ملف PDF من IBM Cloud Object Storage ومعالجته

في هذه الخطوة، سنحمل ملف PDF الخاص ببوليصة التأمين من IBM Cloud Object Storage. تقرأ التعليمات البرمجية ملف PDF ومحتوى النص، وتقسم النص إلى أجزاء أصغر يمكن إدارتها. تُحول هذه الأجزاء إلى تضمينات رقمية وتُخزن في مخزن متجهات FAISS الذي يجهزنا للبحث عن التشابه الدلالي لاحقًا في السياق المحلي لتحسين نتائج الاسترجاع.

import os, types
import pandas as pd
from botocore.client import Config
import ibm_boto3

def __iter__(self): return 0

cos_client = ibm_boto3.client(service_name='s3',
ibm_api_key_id='YOUR_IBM_API_KEY',
ibm_auth_endpoint="https://iam.cloud.ibm.com/identity/token",
config=Config(signature_version='oauth'),
endpoint_url='https://s3.direct.us-south.cloud-object-storage.appdomain.cloud')

bucket = 'YOUR_BUCKET_NAME'
object_key = 'YOUR_OBJECT_KEY'

streaming_body_2 = cos_client.get_object(Bucket=bucket, Key=object_key)['Body']
pdf_bytes = io.BytesIO(streaming_body_2.read())

reader = PdfReader(pdf_bytes)
text = ""
for page in reader.pages:
extracted = page.extract_text()
if extracted:
text += extracted

print(f" Extracted {len(text)} characters from PDF.")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(text)
print(f" Split into {len(chunks)} chunks.")

embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
vectorstore = FAISS.from_texts(chunks, embeddings)

print(f" Created FAISS index.")

يمكن ibm_boto3.client العميل من التفاعل مع IBM Cloud Object Storage.

Bucket هو اسم cloud object storage الذي يحتوي على ملف PDF.

object_key هو اسم ملف PDF في حاوية cloud object storage.

يسترجع cos_client.get_object(...).read() محتوى ملف PDF في cloud object storage كبايتات.

يحول io.BytesIO البايتات الأولية لملف PDF إلى دفق ثنائي في الذاكرة بتنسيق يمكن استخدامه من قِبل PdfReader.

ينشئ PdfReader كائنًا يمكنه تحليل النص واستخراجه من ملف PDF.

يستخرجpage.extract_text() نص صفحة واحدة من ملف PDF.

يكوّن RecursiveCharacterTextSplitter لتقسيم النص المستخرج إلى أجزاء مكونة من 500 حرف مع تداخل 50 حرفًا، وبالتالي الحفاظ على كل التفاصيل في سياقها.

يقسم splitter.split_text(text) كل صفحات نص ملف PDF إلى أجزاء أصغر.

يحمل HuggingFaceEmbeddings نموذج محول الجملة الذي دُرب سابقًا لتحويل أجزاء النص إلى تمثيلات متجهة كثيفة.

ينشئ FAISS.from_texts(chunks, embeddings) فهرس FAISS في الذاكرة يتيح البحث عن أجزاء النص حسب أوجه التشابه الدلالية بينها.

تتولى هذه الخطوة الاستيعاب الكامل لمستند PDF من السحابة إلى نص جاهز للنموذج اللغوي الكبير وفهرسة مريحة للاسترجاع في الوقت الفعلي.

الخطوة 6. تهيئة النموذج اللغوي الكبير والأدوات

في هذه الخطوة، سنكوّن النموذج اللغوي الكبير IBM Granite لتوجيه استنتاجات الوكيل الخاص بك ودمجه مع وظيفة البحث على الويب Tavily. تُعد معلمات النموذج اللغوي الكبير لتقديم استجابات واقعية ومستقرة.

llm = WatsonxLLM(
model_id="ibm/granite-3-2b-instruct",
url=WML_URL,
apikey=WML_API_KEY,
project_id=PROJECT_ID,
params={
"max_new_tokens": 300, # ~2-3 paragraphs, good for corrective RAG
"temperature": 0.2, # low temperature = more factual, stable answers
}
)

print(" Watsonx Granite LLM ready.")
class TavilySearch(BaseTool):
name: str = "tavily_search"
description: str = "Search the web using Tavily for extra info."

def _run(self, query: str):
response = requests.post(
"https://api.tavily.com/search",
json={"api_key": TAVILY_API_KEY, "query": query}
)
response.raise_for_status()
return response.json()['results'][0]['content']


tavily_tool = TavilySearch()

ينشئ WatsonxLLM مثيلاً لغلاف النموذج اللغوي الكبير لمنصة IBM watsonx، ما يسمح بالتفاعل مع نماذج Granite.

model_id="ibm/granite-3-2b-instruct" هو نموذج IBM Granite (نموذج تعليمات يحتوي على 2.7 مليار معلمة) مصمم لمهام الذكاء الاصطناعي التوليدي القائمة على التعليمات.

يحدد class TavilySearch(BaseTool) أداة LangChain مخصصة لإجراء عمليات بحث على الويب باستخدام واجهة برمجة تطبيقات Tavily.

ينشئ tavily_tool = TavilySearch() مثيلاً قابلاً للتنفيذ لأداة البحث Tavily المخصصة.

عندما نهيئ watsonxLLM، تُمرر قيم url وapikey وproject_id  من بيانات الاعتماد التي أعددناها سابقًا للمصادقة والاتصال بالخدمة. تحدد معلماته، مثل  "max_new_tokens": 300، طول الاستجابة، بينما يتحكم  "temperature": 0.2  في إبداع المخرجات، مفضلاً النتائج الأكثر حتمية.

يتضمن تعريف فئة TavilySearch وصفًا لوظيفتها. يوجد منطقها في طريقة def _run(self, query: str) . في هذه الطريقة، نُرسل طلب HTTP POST إلى نقطة نهاية واجهة برمجة تطبيقات Tavily، بما في ذلك TAVILY_API_KEY واستعلام البحث في حمولة JSON. ثم نتحقق من وجود أي أخطاء HTTP باستخدام  response.raise_for_status() ونحلل استجابة JSON للوصول إلى مقتطف المحتوى من أول نتيجة بحث.

تُعد هذه الخطوة النموذج اللغوي لتوليد النص وتشمل أداة بحث خارجية على الويب كوسيلة لتعزيز معرفة النموذج اللغوي.

الخطوة 7. تحديد قوالب الموجهات ووظائف المساعدة

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

# Define Prompt Templates and Helper Functions

# Prompt for scoring the relevance of retrieved chunks
scoring_prompt_template = PromptTemplate.from_template(
"""
You are an evaluator. Score the relevance of the context chunk to the given insurance question.

Question: "{query}"

Context:
\"\"\"
{chunk}
\"\"\"

Respond only in this format:
Score: <0-5>
Reason: <one line reason>
"""
)

# Prompt for rewriting the user's query for better web search results
rewrite_prompt_template = PromptTemplate.from_template(
"""
You are a helpful assistant. Improve the following question to be clearer for an insurance information search.
Focus on making the query more specific if possible.

Original Question: "{query}"

Rewrite it to be clearer:
"""
)

# NEW: Prompt for verifying if Tavily context is from a relevant source (private policy vs. public program)
CONTEXT_SOURCE_VERIFICATION_PROMPT = PromptTemplate.from_template(
"""
You are an expert at identifying if a piece of text is from a general, public, or unrelated source
versus a specific, private, or relevant policy document.

Read the following context and determine if it appears to discuss general information,
public health programs (like Medi-Cal, Medicaid, Medicare, NHS, government-funded programs, state-funded),
or information that is clearly *not* specific to a private insurance policy like the one
the user might be asking about (assuming the user is asking about their own private policy).

If the context explicitly mentions or heavily implies public health programs, or is too general
to be useful for a specific private policy question, respond with "NO".
Otherwise (if it seems like it *could* be from a private policy context, a general insurance guide,
or does not explicitly mention public programs), respond with "YES".

Context:
\"\"\"
Response:
"""
)


# Function to score chunks using the LLM
def score_chunks(chunks, query):
scored = []
for chunk in chunks:
prompt = scoring_prompt_template.format(query=query, chunk=chunk)
response = llm(prompt).strip()

try:
# Extract score using more robust parsing
score_line = [line for line in response.splitlines() if "Score:" in line]
if score_line:
score = int(score_line[0].replace("Score:", "").strip())
else:
score = 0 # Default to 0 if score line not found
except Exception as e:
print(f" Could not parse score for chunk: {e}. Response: {response[:50]}...")
score = 0 # Default to 0 on error

scored.append((chunk, score))
return scored

# Function to retrieve documents from FAISS vector store
def retrieve_from_vectorstore(query):
# Retrieve top 8 similar documents from your PDF content
docs = vectorstore.similarity_search(query, k=8)
return [doc.page_content for doc in docs]

print(" Prompt templates and helper functions defined.")

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

PromptTemplate.from_template هي وظيفة مساعدة من LangChain لإنشاء قالب قابل لإعادة الاستخدام لإنشاء موجهات.

تحدد scoring_prompt_template موجه يوجه النموذج اللغوي الكبير للعمل كأداة تقييم وتعيين درجة صلة (0–5) لجزء سياق معين بناءً على سؤال.

تحدد rewrite_prompt_template موجه يوجه النموذج اللغوي الكبير لتحسين السؤال الأصلي للمستخدم أو توضيحه من أجل البحث.

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

تحدد def score_chunks(chunks, query) وظيفة تأخذ قائمة بأجزاء النص واستعلامًا ثم تستخدم النموذج اللغوي الكبير لتقييم مدى صلة كل جزء.

تحدد def retrieve_from_vectorstore(query) وظيفة لاسترداد المستندات الأكثر تشابهًا من مخزن متجهات FAISS.

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

تنفذ الدالة retrieve_from_vectorstore عملية vectorstore.similarity_search للعثور على 8 أجزاء من المستندات الأكثر صلة بالاستعلام واسترداد page_content من كائنات مستند LangChain المستردة.

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

الخطوة 8. تنفيذ منطق التوليد المعزز للاسترجاع التصحيحي

الاسترجاع الأولي هو الوظيفة التي تمسح مخزن المتجهات لملف PDF.

تأخذ عملية تقييم السياق أجزاء ملف PDF التي استُرجعت لتقييم السياق وفقًا لمدى الصلة.

يحدث الرجوع إلى tavily إذا لم يكن هناك سياق ذو صلة كافٍ من ملف PDF، ثم يستعلم Tavily (بحث على الويب).

التحقق من المصدر هو خطوة مدعومة بنموذج لغوي كبير تتحقق ما إذا كانت نتائج Tavily ذات صلة ببوليصة خاصة قبل استخدامها. تمنع هذه الوظيفة الإجابات المضللة من برامج الصحة العامة.

يحدث إعادة كتابة الاستعلام والبحث الثاني في Tavily إذا لم يكن هناك أي سياق جيد، حيث يعيد كتابة الاستعلام ويحاول البحث في Tavily مرة أخرى.

يُصدر القرار النهائي عندما يكون هناك أي سياق ذي صلة، فيُرسل إلى النموذج اللغوي الكبير مع موجه (صارم) لإنشاء الإجابة. إذا لم يكن هناك سياق ذي صلة بعد كل المحاولات الممكنة، فإنه يرسل رفضًا مهذبًا.

# Implement the Corrective RAG Logic

MIN_CONTEXT_LENGTH = 100 # Adjust this based on how much minimal context you expect for a partial answer
SIMILARITY_THRESHOLD = 3 # Only scores >= 3 used for vector store chunks

def corrective_rag(query: str, policy_context_keywords: list = None):
"""
Executes the Corrective RAG process to answer insurance queries.

Args:
query (str): The user's question.
policy_context_keywords (list, optional): Keywords related to the specific policy
(e.g., ["Super Star Health", "Care Health Insurance"]).
Used to make external searches more specific. Defaults to None.
Returns:
str: The final answer generated by the LLM or a predefined refusal.
"""
retrieved_context_pieces = [] # To store all relevant pieces found throughout the process

# Initial vector search & Scoring (from your PDF)
chunks_from_vectorstore = retrieve_from_vectorstore(query)
scored_chunks_vector = score_chunks(chunks_from_vectorstore, query)
good_chunks_vector = [chunk for chunk, score in scored_chunks_vector if score >= SIMILARITY_THRESHOLD]
retrieved_context_pieces.extend(good_chunks_vector)

current_context = "\n\n".join(retrieved_context_pieces)
print(f" Context length after initial vector scoring: {len(current_context)}")

# Prepare specific query for Tavily by optionally adding policy keywords
tavily_search_query = query
if policy_context_keywords:
tavily_search_query = f"{query} {' '.join(policy_context_keywords)}"

# Fallback: Tavily direct search (only if current context is too short from vector store)
if len(current_context) < MIN_CONTEXT_LENGTH:
print(f" Context too short from internal docs, trying Tavily direct with query: '{tavily_search_query}'...")
tavily_context_direct = tavily_tool._run(tavily_search_query)

if tavily_context_direct:
# --- NEW STEP: Verify Tavily Context Source ---
# Ask the LLM if the Tavily result seems to be from a private policy context or a public program
verification_prompt = CONTEXT_SOURCE_VERIFICATION_PROMPT.format(context=tavily_context_direct)
is_relevant_source = llm(verification_prompt).strip().upper()

if is_relevant_source == "YES":
retrieved_context_pieces.append(tavily_context_direct)
current_context = "\n\n".join(retrieved_context_pieces) # Re-combine all good context
print(f" Context length after Tavily direct (verified and added): {len(current_context)}")
else:
print(f" Tavily direct context source rejected (e.g., public program): {tavily_context_direct[:100]}...")
# Context is NOT added, so it remains short and triggers the next fallback or final refusal

# Fallback: Rewrite query + Tavily (only if context is still too short after direct Tavily)
if len(current_context) < MIN_CONTEXT_LENGTH:
print(" Context still too short, rewriting query and trying Tavily...")
rewrite_prompt = rewrite_prompt_template.format(query=query)
improved_query = llm(rewrite_prompt).strip()

# Add policy keywords to the rewritten query too
if policy_context_keywords:
improved_query = f"{improved_query} {' '.join(policy_context_keywords)}"

print(f" Rewritten query: '{improved_query}'")
tavily_context_rewritten = tavily_tool._run(improved_query)

if tavily_context_rewritten:
# --- NEW STEP: Verify Rewritten Tavily Context Source ---
verification_prompt = CONTEXT_SOURCE_VERIFICATION_PROMPT.format(context=tavily_context_rewritten)
is_relevant_source = llm(verification_prompt).strip().upper()

if is_relevant_source == "YES":
retrieved_context_pieces.append(tavily_context_rewritten)
current_context = "\n\n".join(retrieved_context_pieces) # Re-combine all good context
print(f" Context length after rewritten Tavily (verified and added): {len(current_context)}")
else:
print(f" Tavily rewritten context source rejected (e.g., public program): {tavily_context_rewritten[:100]}...")

# --- Final Decision Point ---
# Now, `current_context` holds ALL the "good" and "verified" context we managed to gather.
# The decision to call the LLM for an answer or give a hard refusal is based on `current_context`'s length.

# Final check for absolutely no good context
# This triggers only if *no* relevant internal or external context was found or verified.
if len(current_context.strip()) == 0:
print(" No good context found after all attempts. Returning absolute fallback.")
return (
"Based on the information provided, there is no clear mention of this specific detail "
"in the policy documents available."
)

# If we have *any* context (even if short), pass it to the LLM to process
# The LLM will then decide how to phrase the answer based on its prompt instructions
# (exact, partial, or full refusal if context is irrelevant or insufficient based on its own reasoning).
final_prompt = (
f"You are a careful insurance expert.\n"
f"Use ONLY the following context to answer the user's question. If the context is too short "
f"or does not contain the answer, you must indicate that.\n"
f"Context:\n```\n{current_context}\n```\n\n" # Pass the gathered context
f"User's Question: {query}\n\n" # Pass the original query for the LLM's reference
f"NEVER add new details that are not in the context word-for-word.\n"
f"If the context clearly says the answer, give it exactly as written in the context, but in prose.\n"
f"If the context does not mention the topic at all, or the answer is not in the context, say:\n"
f"\"I'm sorry, but this information is not available in the provided policy details.\"\n"
f"If the context partially mentions the topic but does not directly answer the specific question (e.g., mentions 'dental' but not 'wisdom tooth removal'), reply like this:\n"
f"\"Based on the information provided, here’s what is known: [quote relevant details from the context related to the broad topic.] "
f"There is no clear mention of the specific detail asked about.\"\n"
f"Do NOT assume. Do NOT make up extra information.\n"
f"Do NOT generate extra questions or conversational filler.\n"
f"Final Answer:"
)

return llm(final_prompt)

print(" Corrective RAG logic implemented.")

تسمح لك التمريرة الأولى لمعلمة policy_context_keywords بإضافة مصطلحات محددة من بوليصتك (على سبيل المثال، اسمها، شركة التأمين) للمساعدة على تضييق نطاق البحث عبر Tavily.

تحدد MIN_CONTEXT_LENGTH الحد الأدنى المقبول لطول السياق المسترجع.

تحدد SIMILARITY_THRESHOLD الحد الأدنى لدرجة الصلة التي يجب أن يتمتع بها الجزء ليعتبر "جيدًا".

تحدد def corrective_rag(...) الوظيفة الرئيسية التي تنسق سير عمل التوليد المعزز بالاسترجاع التصحيحي بأكمله.

تبدأ وظيفة corrective_rag بإنشاءg retrieved_context_pieces لجمع السياق ذي الصلة. تجلب أولاً chunks_from_vectorstore من مخزن متجهات PDF بناءً على الاستعلام، ثم تقيم scored_chunks_vector مدى صلتها باستخدام النموذج اللغوي. يُحتفظ فقط بـ good_chunks_vector التي تفي بـ SIMILARITY_THRESHOLD . ثم تُجمع current_context من هذه القطع.

إذا كان current_context أقل من w MIN_CONTEXT_LENGTH، يحاول النظام إجراء بحث على الويب. يبني tavily_search_query، مع إمكانية دمج  policy_context_keywords. يُجرى بحث مباشر (tavily_context_direct). والأهم من ذلك، يُنشئ verification_prompt ويُرسل إلى النموذج اللغوي الكبير لتحديد ما إذا كانت نتيجة البحث على الويب (is_relevant_source) من بوليصة خاصة أو من برنامج عام. إذا كانت الإجابة نعم، يُضاف السياق.

إذا ظل السياق غير كافٍ، يستعد النظام لإعادة كتابة الاستعلام. يستخدم rewrite_prompt للحصول على improved_query من النموذج اللغوي الكبير، ثم يجري بحثًا ثانيًا على الويب (tavily_context_rewritten). يخضع هذا السياق الجديد أيضًا لعملية التحقق من المصدر نفسها.

وأخيرًا، if len(current_context.strip()) == 0 هو عملية التحقق الأخيرة. إذا لم يُعثر على سياق ذي صلة بعد كل المحاولات، تُرسل رسالة رفض محددة سابقًا. خلاف ذلك، يُنشئ final_prompt يضم كل السياقات المتحقق منها، ويُرسل إلى النموذج اللغوي لتوليد إجابته النهائية.

تتعامل وظيفة corrective_rag بأكملها مع مراحل وظائف الاسترجاع والتقييم والتحقق للتوليد المعزز بالاسترجاع التصحيحي بالتفصيل. وهي تسمح بالتحديث المستمر لقاعدة المعرفة وتدفق المعرفة وتوفر ميزة الحصول على إجابات دقيقة ومراعية للسياق.

الخطوة 9. اختبار النظام

أخيرًا، نفذ وظيفة corrective_rag باستخدام استعلام نموذجي. من الضروري توفير policy_context_keywords الخاصة بوثيقة PDF لديك. ستساعد هذه الكلمات الرئيسية على جعل بحث الويب Tavily أكثر صلة ببوليصتك الفعلية، ما يمنع المعلومات العامة أو معلومات برامج الصحة العامة من تشويه سياقك.

راقب بيانات الطباعة الخاصة بطول السياق ونتائج التحقق لفهم تدفق المعلومات.

query = "How does the policy cover for In-Patient Hospitalisation?"
result = corrective_rag(query)

print("\n FINAL ANSWER:\n")
print(result)

تحدد policy_specific_keywords = ["Super Star Health", "Care Health Insurance"] قائمة الكلمات الرئيسية ذات الصلة ببوليصة التأمين المحملة، ما يساعد على تضييق نطاق نتائج البحث على الويب.

تحدد query = "..."  السؤال المحدد الذي قد يطرحه المستخدم.

ستدعي result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) الدالة corrective_rag الرئيسية وتمرر استعلام المستخدم والكلمات الرئيسية الخاصة بالبوليصة لبدء عملية التوليد المعزز بالاسترجاع بأكملها.

يعرض print("\n FINAL ANSWER (...)") عنوانًا واضحًا قبل طباعة الإجابة التي أُنشئت.

يُخرج print(result) الإجابة النهائية التي أعادها نظام corrective_rag.

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

الامتيازات الرئيسية

نسق نظام التوليد المعزز بالاسترجاع التصحيحي قاعدة معرفة PDF داخلية بشكل كامل مع خدمة خارجية (Tavily) لاسترداد معلومات شاملة للطلبات المعقدة.

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

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

باستخدام التوليد المقيد، أُنشأت إجابة موثوقة ودقيقة من حيث السياق بشكل عام، ورفض النظام الإجابة بأدب إذا لم تكن هناك معلومات مؤكدة كافية.

أظهر هذا المثال كيف يمكن استخدام LangChain والنماذج اللغوية الكبيرة IBM Granite على watsonx لتطوير تطبيقات فائقة الإمكانات وجديرة بالثقة قائمة على الذكاء الاصطناعي في مجالات حساسة مثل طرح أسئلة حول بواليص التأمين.

حلول ذات صلة
وكلاء الذكاء الاصطناعي للأعمال

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

    استكشف watsonx Orchestrate
    حلول وكلاء الذكاء الاصطناعي من IBM

    يمكنك بناء مستقبل عملك باستخدام حلول الذكاء الاصطناعي الجديرة بالثقة.

    استكشف حلول وكلاء الذكاء الاصطناعي
    خدمات الذكاء الاصطناعي لدى IBM Consulting

    تساعد خدمات IBM Consulting AI في إعادة تصور طريقة عمل الشركات باستخدام حلول الذكاء الاصطناعي من أجل النهوض بأعمالها.

    استكشف خدمات الذكاء الاصطناعي
    اتخِذ الخطوة التالية

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

    استكشف watsonx Orchestrate استكشف watsonx.ai