مقدمه
در مقاله قبلی، چتبات خود را با طبقهبندی قصد و مسیریابی شرطی گسترش دادیم. اکنون چتبات ما میتواند تشخیص دهد که آیا سوال کاربر مربوط به §L0§Hiwa AI§/L0§ است یا خیر، قبل از اینکه تصمیم بگیرد آیا باید درخواست را ادامه دهد یا خیر.
گراف به این شکل است:

با این حال، ربات چت ما هنوز یک محدودیت مهم دارد.
میتواند تشخیص دهد که آیا سوال مرتبط با Hiwa AI است یا خیر، اما مدل زبانی خود لزوماً اطلاعاتی درباره وبسایت فعلی شرکت ندارد. به عنوان مثال، کاربر ممکن است بپرسد:
Hiwa AI چه خدماتی ارائه میدهد؟
مدل را نمیتوان انتظار داشت که صرفاً به این دلیل که اطلاعات در وبسایت شرکت وجود دارد، پاسخ را بداند.
میتوانستیم تمام محتوای وبسایت را درون سیستم پرامپت قرار دهیم، اما این روش مقیاسپذیر نیست. یک وبسایت میتواند شامل صدها یا هزاران صفحه باشد و ارسال تمام این اطلاعات به مدل برای هر سوال، بسیار ناکارآمد خواهد بود. در عوض، به روشی نیاز داریم که تنها اطلاعات مرتبط با سوال کاربر را بازیابی کند.
این جایی است که تولید تقویتشده با بازیابی (RAG) وارد عمل میشود.
RAG چیست؟
RAG مخفف عبارت Retrieval-Augmented Generation است.
ایده اصلی ساده است:
- اطلاعات را در یک پایگاه دانش قابل جستجو ذخیره کنید.
- هنگامی که کاربر سوالی میپرسد، پایگاه دانش را جستجو کنید.
- اطلاعات مرتبطترین را بازیابی کنید.
- این اطلاعات را به مدل زبانی بدهید.
- اجازه دهید مدل با استفاده از زمینه بازیابیشده پاسخ را تولید کند.
به جای این:

ما یک مرحله بازیابی معرفی میکنیم:

مدل زبانی همچنان مسئول تولید پاسخ است، اما اطلاعاتی که برای تولید آن پاسخ استفاده میشود از پایگاه دانش خودمان تأمین میشود. این ویژگی بهویژه هنگام کار با مدلهای محلی بسیار مفید است، زیرا نیازی به آموزش مدل روی محتوای وبسایت نداریم.
مرحله ۱: جمعآوری محتوای وبسایت
قبل از اینکه بتوانیم وبسایت را جستجو کنیم، نیاز داریم محتوای آن را استخراج کنیم. یک وبسایت شامل انواع مختلفی از اطلاعات است:
- متن
- منوهای ناوبری
- سربرگها و پابرگها
- تصاویر
- لینکها
- عناصر HTML
- فراداده
برای چتبات ما، محتوای متنی معنادار از اهمیت اصلی برخوردار است. یک خزنده ساده ممکن است یک صفحه وب مانند این را بازیابی کند:
import requests
from bs4 import BeautifulSoup
def extract_text(url: str) -> str:
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
for element in soup(["script", "style", "nav", "footer"]):
element.decompose()
return soup.get_text(" ", strip=True)این تابع صفحه را دانلود میکند، عناصری که برای پایگاه دانش ما مفید نیستند را حذف میکند و متن باقیمانده را برمیگرداند. با این حال، برای یک وبسایت واقعی، معمولاً به یک خزنده پیچیدهتر نیاز داریم.
ممکن است نیاز داشته باشیم:
- صفحات متعدد را کشف کنید.
- پیوندها را دنبال کنید
- خواندن نقشه سایت
- از خزیدن چندباره بر روی یک صفحه جلوگیری کنید.
- مدیریت محتوای تولید شده به صورت پویا.
- URL مرتبط با هر سند را ذخیره کنید.
ایده مهم این است که RAG قبل از خود چتبات شروع میشود. ابتدا باید دانشی را بسازیم که چتبات در نهایت آن را جستجو کند.
مرحله ۲: تقسیم محتوا به بخشهای کوچکتر
یک صفحه وب میتواند حاوی مقدار زیادی متن باشد. معمولاً نمیخواهیم کل صفحه وب را به عنوان یک سند عظیم ذخیره کنیم. در عوض، محتوا را به بخشهای کوچکتری به نام «تکهها» تقسیم میکنیم. به عنوان مثال، تصور کنید یک صفحه شامل موارد زیر است:
- هایوا هوش مصنوعی چندین سرویس مبتنی بر هوش مصنوعی ارائه میدهد.
- پلتفرم به کسبوکارها امکان میدهد تا گردشکارهای مختلف را خودکار کنند.
- کاربران همچنین میتوانند قابلیتهای هوش مصنوعی را در برنامههای موجود خود ادغام کنند.
- هایوا هوش مصنوعی ابزارهایی برای پردازش اسناد و دستیارهای هوشمند ارائه میدهد.
به جای اینکه کل صفحه را به عنوان یک سند واحد در نظر بگیریم، میتوانیم آن را به بخشهای کوچکتر تقسیم کنیم:
- چانک ۱: هایوا هوش مصنوعی چندین سرویس مبتنی بر هوش مصنوعی ارائه میدهد.
- این پلتفرم به کسبوکارها امکان میدهد تا گردشکارهای مختلف را خودکار کنند.
- کاربران همچنین میتوانند قابلیتهای هوش مصنوعی را در برنامههای موجود خود ادغام کنند.
- Hiwa AI ابزارهایی برای پردازش اسناد و دستیارهای هوشمند فراهم میکند.
این بازیابی را بسیار دقیقتر میکند. اگر کاربر بپرسد:
کسبوکارها چه چیزی را میتوانند خودکار کنند؟
ما میخواهیم بخش مربوط به خودکارسازی گردش کار را بازیابی کنیم، نه کل صفحه وبسایت.
مرحله ۳: ایجاد امبدینگها
اکنون ما قطعاتی از متن داریم، اما هنوز به روشی برای جستجوی آنها بر اساس معنیشان نیاز داریم. اینجاست که امبدینگها مفید هستند. یک مدل امبدینگ متن را به لیستی از اعداد به نام وکتور تبدیل میکند. به عنوان مثال:
"Hiwa AI چه خدماتی ارائه میدهد؟"
ممکن است به چیزی از نظر مفهومی مشابه تبدیل شود:
[0.12, -0.43, 0.87, 0.21, ...]
بردار واقعی بسیار بزرگتر از این مثال است. نکته مهم این است که بخشهای متنی با معنای مشابه، تمایل به داشتن بردارهای مشابه دارند. به عنوان مثال:
"Hiwa AI چه خدماتی ارائه میدهد؟"
و
Hiwa AI خدمات مبتنی بر هوش مصنوعی را برای کسبوکارها ارائه میدهد.
جملات مختلف هستند، اما معنای مشابهی دارند. بنابراین، باید امبدینگهای آنها در فضای برداری نسبتاً نزدیک به هم باشند. میتوانیم یک امبدینگ با استفاده از یک مدل امبدینگ تولید کنیم:
from openai import OpenAI
client = OpenAI( api_key="YOUR_API_KEY" )
def embed_text(text: str) -> list[float]:
response = client.embeddings.create( model="BAAI/bge-m3", input=text, )
return response.data[0].embedding مدل جاسازی دقیق برای درک معماری اهمیت ندارد. نکته مهم این است که باید از همان مدل جاسازی هم برای ذخیره اسناد و هم برای جستجوی آنها استفاده شود.
مرحله ۴: ذخیرهسازی تکهها
هر بخش باید همراه با جاسازی (embedding) مربوطه ذخیره شود. یک رکورد ساده در پایگاه داده ممکن است به این شکل باشد:
| شناسه | متن | جاسازی | آدرس اینترنتی |
|---|---|---|---|
| ۴۲ | Hiwa AI ابزارهای مبتنی بر هوش مصنوعی را برای کسبوکارها فراهم میکند. | [0.12, -0.43, 0.87, ...] | §L0§خدمات§/L0§ |
این متن مهم است زیرا این همان چیزی است که در نهایت به مدل زبانی ارائه خواهیم داد. تعبیهسازی مهم است زیرا از آن برای یافتن متن مرتبط استفاده میکنیم. URL برای ردیابی منبع اطلاعات مفید است و میتواند بعداً برای ارائه استناد به کاربر مورد استفاده قرار گیرد.
مرحله ۵: جستجو در پایگاه دانش
اکنون همه چیز لازم برای انجام بازیابی را داریم. فرض کنید کاربر میپرسد:
Hiwa AI چه خدماتی ارائه میدهد؟
ابتدا، یک امبدینگ برای سوال ایجاد میکنیم.
query_embedding = embed_text( "What services does Hiwa AI provide?")اکنون یک بردار داریم که نشاندهنده سوال کاربر است. در مرحله بعد، این بردار را با بردارهای ذخیرهشده در پایگاه داده مقایسه میکنیم. از نظر مفهومی، این فرآیند به این شکل است:
پرس و جو جاسازی شده │ ▼ مقایسه با جاسازیهای ذخیره شده │ ▼ یافتن مشابهترین قطعات │ ▼ بازگرداندن متن مرتبط
محاسبه شباهت میتواند از معیارهای مختلف فاصله استفاده کند. یکی از معیارهای متداول، شباهت کسینوسی است. نتیجه ممکن است به این شکل باشد:
[
{"text": "Hiwa AI provides AI-powered tools for businesses.",
"score": 0.91 },
{"text": "The platform provides intelligent document processing.",
"score": 0.87 },
{"text": "Businesses can integrate AI assistants into their workflows.",
"score": 0.82 } ] قطعات با بالاترین امتیاز، آنهایی هستند که مرتبطترین به سوال کاربر در نظر گرفته میشوند.
مرحله ۶: ارائه اطلاعات بازیابیشده به مدل زبانی بزرگ (LLM)
در این مرحله، اطلاعاتی که مدل نیاز دارد را در اختیار داریم. میتوانیم قطعات بازیابی شده را با سوال کاربر ترکیب کنیم. به عنوان مثال:
context = "\n\n".join( chunk["text"] for chunk in retrieved_chunks ) سپس یک پرامپت میسازیم:
prompt = f""" You are an assistant for Hiwa AI. Answer the user's question using the provided context. Context: {context} Question: {user_question} """اکنون میتوانیم این پرامپت را به مدل زبانی خود ارسال کنیم:
response = llm.invoke(prompt) print(response.content) نکته مهم این است که مدل نیازی به آشنایی قبلی با وبسایت ندارد. ما اطلاعات مرتبط را در زمان استنتاج ارائه میدهیم. این ایده بنیادی پشت مفهوم تولید تقویتشده با بازیابی است.
سرهمبندی خط بازیابی
اکنون میتوانیم مراحل فردی را در یک تابع ساده بازیابی ترکیب کنیم:
def retrieve_documents( query: str, top_k: int = 3,):
query_embedding = embed_text(query)
results = search_similar_chunks(query_embedding=query_embedding, top_k=top_k,)
return resultsسپس چتبات میتواند از آن به این صورت استفاده کند:
user_question = "What services does Hiwa AI provide?"
documents = retrieve_documents(user_question)
context = "\n\n".join( document["text"] for document in documents )
prompt = f""" You are an assistant for Hiwa AI. Use the following context to answer the question. Context: {context} Question: {user_question} """
response = llm.invoke(prompt)
print(response.content) گردش کار کلی اکنون به این صورت است:

اتصال این به LangGraph ما
در این مرحله، دو بخش مجزا داریم. از مقاله قبلی، سیستم طبقهبندی نیات و مسیریابی شرطی را داریم. حالا یک خط لوله بازیابی اطلاعات نیز داریم. گام بعدی ترکیب این دو ایده است. به جای ارسال مستقیم سوال مرتبط به چتبات، میتوانیم آن را ابتدا از طریق یک گره بازیابی مسیریابی کنیم:

اکنون چتبات هر سه مؤلفه مهم را دارد:
- میتواند هدف کاربر را درک کند.
- میتواند اطلاعات مرتبط را بازیابی کند.
- میتواند با استفاده از آن اطلاعات پاسخی تولید کند.
در مقاله بعدی، خط بازیابی را با گردش کار LangGraph ترکیب کرده و ربات چت کامل RAG را میسازیم.
نتیجهگیری
یک مدل زبانی به تنهایی همیشه برای ساخت یک برنامه کاربردی کافی نیست. زمانی که نیاز داریم یک چتبات به سوالات درباره یک منبع اطلاعاتی خاص و در حال تغییر پاسخ دهد، به روشی نیاز داریم که آن دانش را به مدل ارائه دهیم.
RAG این مشکل را با جداسازی بازیابی دانش از تولید پاسخ حل میکند. این فرآیند ساده است:

نکته مهم این است که نیازی نیست مدل زبانی خود را هر بار که وبسایت تغییر میکند، دوباره آموزش دهیم. میتوانیم به سادگی پایگاه دانش را بهروزرسانی کنیم. این ویژگی، RAG را به یک روش عملی برای ساخت برنامههای هوش مصنوعی تبدیل میکند که نیاز به کار با اطلاعات خصوصی، حوزهای یا متغیر دارند.

