Files

528 lines
15 KiB
Markdown
Raw Permalink Normal View History

2026-05-11 03:27:21 +03:30
# توضیح خیلی ساده منطق RAG در پروژه
این فایل قرار است خیلی ساده بگوید RAG در این پروژه چطور کار می‌کند.
## اول: RAG یعنی چه؟
RAG یعنی:
1. سوال کاربر را می‌گیریم
2. متن‌های مرتبط را از حافظه دانشی پیدا می‌کنیم
3. آن متن‌ها را کنار سوال می‌گذاریم
4. بعد از مدل زبانی می‌خواهیم جواب بدهد
یعنی مدل فقط از حافظه خودش جواب نمی‌دهد؛
قبل از جواب دادن، اطلاعات مرتبط پروژه را هم می‌بیند.
---
## نقش فایل `rag/apps.py`
فایل `rag/apps.py` فقط اپ Django مربوط به RAG را ثبت می‌کند.
کار اصلی‌اش این است:
- اسم اپ را مشخص می‌کند: `rag`
- نام نمایشی اپ را مشخص می‌کند
پس:
- `rag/apps.py` منطق اصلی RAG را پیاده‌سازی نمی‌کند
- فقط می‌گوید این اپ در پروژه وجود دارد
منطق اصلی RAG بیشتر در این فایل‌هاست:
- `rag/views.py`
- `rag/chat.py`
- `rag/retrieve.py`
- `rag/ingest.py`
- `rag/embedding.py`
- `rag/vector_store.py`
- `rag/user_data.py`
- `rag/config.py`
---
## تصویر خیلی ساده از کل جریان
RAG در این پروژه دو بخش اصلی دارد:
### 1) آماده‌سازی دانش
در این بخش سیستم اطلاعات را جمع می‌کند و داخل دیتابیس برداری ذخیره می‌کند.
مراحل:
1. فایل‌های دانش را می‌خواند
2. متن‌ها را خرد می‌کند
3. هر تکه را تبدیل به embedding می‌کند
4. embeddingها را داخل Qdrant ذخیره می‌کند
این کار بیشتر در `rag/ingest.py` انجام می‌شود.
### 2) جواب دادن به سوال کاربر
در این بخش وقتی کاربر سوال می‌پرسد:
1. سوال embedding می‌شود
2. متن‌های نزدیک و مرتبط پیدا می‌شوند
3. داده‌های کاربر هم اضافه می‌شود
4. همه این‌ها به مدل زبانی داده می‌شود
5. مدل جواب را به صورت stream برمی‌گرداند
این کار بیشتر در `rag/chat.py` و `rag/retrieve.py` انجام می‌شود.
---
## بخش اول: سیستم چطور دانش را آماده می‌کند؟
### فایل اصلی: `rag/ingest.py`
این فایل کارش این است که اطلاعات را وارد سیستم RAG کند.
### از کجا اطلاعات می‌آید؟
سیستم این منابع را می‌خواند:
- فایل‌های پایگاه دانش
- فایل لحن یا tone
- داده‌های خاک هر کاربر
- داده‌های هواشناسی هر کاربر
### پایگاه دانش یعنی چه؟
پایگاه دانش یعنی متن‌هایی که پروژه از قبل دارد.
مثلا:
- اطلاعات عمومی چت
- اطلاعات آبیاری
- اطلاعات کودهی
در تنظیمات، برای هر بخش یک knowledge base تعریف شده است.
---
## مرحله 1: خواندن منابع
تابع `load_sources()` در `rag/ingest.py` منابع را جمع می‌کند.
خروجی این تابع تقریبا این شکلی است:
- شناسه منبع
- متن منبع
- شناسه سنسور یا کاربر
- نام پایگاه دانش
نکته مهم:
- داده‌های عمومی با `__global__` ذخیره می‌شوند
- داده‌های شخصی هر کاربر با `sensor_uuid` خودش ذخیره می‌شوند
- داده‌های کاربری معمولا با `__all__` در `kb_name` علامت می‌خورند
این کار باعث می‌شود بعدا سیستم بداند هر متن برای چه کسی یا چه بخشی بوده است.
---
## مرحله 2: خرد کردن متن
### فایل: `rag/chunker.py`
متن‌های طولانی مستقیم وارد جستجو نمی‌شوند.
اول آن‌ها را به تکه‌های کوچک‌تر تبدیل می‌کنیم.
چرا؟
چون:
- جستجو دقیق‌تر می‌شود
- embedding بهتر می‌شود
- مدل فقط بخش‌های لازم را می‌بیند
مثلا یک فایل بلند به چند chunk تبدیل می‌شود.
---
## مرحله 3: ساخت embedding
### فایل: `rag/embedding.py`
هر chunk متنی به یک لیست عددی تبدیل می‌شود.
به این لیست عددی می‌گوییم embedding.
خیلی ساده:
- متن شبیه به هم -> embedding شبیه به هم
- متن متفاوت -> embedding متفاوت
پس بعدا اگر کاربر سوالی شبیه یک متن بپرسد،
سیستم می‌تواند آن متن را پیدا کند.
---
## مرحله 4: ذخیره در Qdrant
### فایل: `rag/vector_store.py`
بعد از ساخت embedding، داده‌ها داخل Qdrant ذخیره می‌شوند.
Qdrant در این پروژه نقش حافظه برداری را دارد.
برای هر chunk این چیزها ذخیره می‌شود:
- خود متن
- embedding
- منبع متن
- شماره chunk
- `sensor_uuid`
- `kb_name`
این metadata خیلی مهم است؛
چون کمک می‌کند بعدا فقط داده‌های مرتبط برگردند.
---
## دستور ورود اطلاعات
### فایل: `rag/management/commands/rag_ingest.py`
این فایل یک command جنگو دارد که ingestion را اجرا می‌کند.
یعنی اگر این دستور اجرا شود:
```bash
python manage.py rag_ingest
```
سیستم:
- منابع را می‌خواند
- chunk می‌کند
- embedding می‌سازد
- داخل Qdrant ذخیره می‌کند
---
## بخش دوم: وقتی کاربر سوال می‌پرسد چه می‌شود؟
### ورودی API
### فایل: `rag/views.py`
در این فایل endpoint چت وجود دارد.
کارش این است که از کاربر این اطلاعات را بگیرد:
- `service_id`
- `query`
- `user_id` یا `sensor_uuid`
بعد چند بررسی انجام می‌شود:
- آیا سوال خالی نیست؟
- آیا `service_id` معتبر است؟
- اگر سرویس نیاز به داده کاربر دارد، آیا `user_id` داده شده؟
اگر همه چیز درست باشد،
در نهایت `chat_rag_stream()` صدا زده می‌شود.
---
## `service_id` چرا مهم است؟
چون سیستم چند نوع سرویس دارد.
مثلا:
- سرویس چت عمومی
- سرویس آبیاری
- سرویس کودهی
هر سرویس می‌تواند این‌ها را مشخص کند:
- از کدام knowledge base استفاده شود
- از چه مدل زبانی استفاده شود
- آیا داده‌های شخصی کاربر لازم است یا نه
- چه tone یا system prompt استفاده شود
این تنظیمات در `rag/config.py` و فایل `config/rag_config.yaml` مدیریت می‌شوند.
---
## ساخت context
### فایل اصلی: `rag/chat.py`
مهم‌ترین بخش پاسخ‌گویی همین‌جاست.
تابع مهم: `build_rag_context()`
این تابع یک context برای مدل می‌سازد.
این context از چند بخش ساخته می‌شود:
1. داده فعلی خاک کاربر
2. داده هواشناسی کاربر
3. متن‌های مرتبط پیدا شده از RAG
یعنی مدل فقط سوال را نمی‌بیند؛
بلکه این اطلاعات کمکی را هم می‌بیند.
---
## داده خاک و هواشناسی کاربر از کجا می‌آید؟
### فایل: `rag/user_data.py`
این فایل اطلاعات کاربر را از دیتابیس پروژه می‌سازد.
دو تابع مهم:
- `build_user_soil_text(sensor_uuid)`
- `build_user_weather_text(sensor_uuid)`
کار این توابع:
- داده‌های مدل‌های پروژه را می‌خوانند
- آن‌ها را به متن ساده تبدیل می‌کنند
چرا به متن؟
چون سیستم RAG در نهایت با متن کار می‌کند.
پس حتی داده‌های دیتابیس هم به متن تبدیل می‌شوند تا:
- embed شوند
- یا مستقیم داخل context قرار بگیرند
---
## پیدا کردن متن‌های مرتبط
### فایل: `rag/retrieve.py`
در اینجا تابع `search_with_query()` کار اصلی بازیابی را انجام می‌دهد.
مراحلش ساده است:
1. سوال کاربر embedding می‌شود
2. یک جستجوی شباهت در Qdrant انجام می‌شود
3. فقط متن‌های مجاز برگردانده می‌شوند
---
## چرا گفتیم "متن‌های مجاز"؟
چون این پروژه داده کاربر دارد و نباید اطلاعات یک کاربر به کاربر دیگر برسد.
برای همین موقع جستجو فیلتر گذاشته می‌شود.
فیلترها معمولا این‌ها هستند:
- `sensor_uuid`
- `kb_name`
یعنی سیستم فقط این‌ها را برمی‌گرداند:
- داده‌های عمومی (`__global__`)
- داده‌های همان کاربر
- داده‌های همان knowledge base
پس این بخش برای امنیت و جداسازی اطلاعات خیلی مهم است.
---
## جستجو در Qdrant چطور انجام می‌شود؟
### فایل: `rag/vector_store.py`
تابع `search()` در این فایل:
- query vector را می‌گیرد
- فیلترها را می‌سازد
- از Qdrant نتیجه می‌گیرد
بعد نتیجه‌ها را به شکل ساده برمی‌گرداند:
- `id`
- `score`
- `text`
- `metadata`
`score` یعنی میزان شباهت.
هرچه بیشتر باشد، یعنی متن به سوال نزدیک‌تر است.
---
## بعد از بازیابی چه می‌شود؟
### دوباره در `rag/chat.py`
بعد از این که متن‌های مرتبط پیدا شدند:
- متن‌های مرجع جمع می‌شوند
- داده کاربر هم کنار آن‌ها قرار می‌گیرد
- tone و system prompt هم اضافه می‌شود
در آخر یک پیام system ساخته می‌شود که به مدل می‌گوید:
- از داده‌های خاک استفاده کن
- از متن‌های مرجع استفاده کن
- با زبان کاربر جواب بده
---
## تولید جواب نهایی
### تابع: `chat_rag_stream()`
این تابع:
1. تنظیمات سرویس را می‌خواند
2. context را می‌سازد
3. پیام system و user را آماده می‌کند
4. به مدل زبانی درخواست می‌فرستد
5. جواب را به صورت stream برمی‌گرداند
پس جواب نهایی فقط از خود مدل نیست؛
بلکه از ترکیب این‌ها ساخته می‌شود:
- سوال کاربر
- داده‌های فعلی کاربر
- متن‌های مرجع RAG
- لحن و دستور سیستم
---
## tone چیست؟
tone یعنی لحن پاسخ.
مثلا سیستم می‌تواند مشخص کند:
- رسمی جواب بده
- ساده جواب بده
- تخصصی جواب بده
فایل‌های tone از روی knowledge base یا service خوانده می‌شوند.
پس tone روی سبک جواب اثر دارد،
نه روی اصل جستجو.
---
## نقش `rag/config.py`
این فایل تنظیمات را بارگذاری می‌کند.
مثلا:
- مدل embedding چیست
- Qdrant کجاست
- اندازه vector چقدر است
- chunking چگونه باشد
- سرویس‌ها چه هستند
- هر سرویس از کدام knowledge base استفاده کند
یعنی این فایل مغز تنظیمات سیستم است.
---
## خلاصه خیلی ساده کل مسیر
اگر بخواهیم خیلی خلاصه بگوییم:
### مرحله آماده‌سازی
1. فایل‌ها و داده‌های کاربر خوانده می‌شوند
2. متن‌ها chunk می‌شوند
3. embedding ساخته می‌شود
4. داخل Qdrant ذخیره می‌شوند
### مرحله پاسخ‌گویی
1. کاربر سوال می‌پرسد
2. سوال embedding می‌شود
3. متن‌های مشابه پیدا می‌شوند
4. داده خاک و هواشناسی کاربر هم اضافه می‌شود
5. همه این‌ها به LLM داده می‌شود
6. LLM جواب نهایی را می‌سازد
---
## فرق این پروژه با یک چت ساده
اگر چت ساده بود:
- مدل فقط با دانسته‌های خودش جواب می‌داد
ولی اینجا:
- مدل به داده‌های واقعی پروژه دسترسی دارد
- داده‌های همان کاربر را می‌بیند
- از متن‌های مرجع واقعی استفاده می‌کند
پس جواب‌ها:
- دقیق‌تر می‌شوند
- شخصی‌تر می‌شوند
- به داده‌های واقعی نزدیک‌تر می‌شوند
---
## فایل‌ها را خیلی ساده به خاطر بسپار
- `rag/apps.py` -> فقط ثبت اپ
- `rag/views.py` -> گرفتن درخواست کاربر
- `rag/chat.py` -> ساخت context و گرفتن جواب از مدل
- `rag/retrieve.py` -> جستجوی متن مرتبط
- `rag/ingest.py` -> وارد کردن دانش به سیستم
- `rag/embedding.py` -> تبدیل متن به embedding
- `rag/vector_store.py` -> ذخیره و جستجو در Qdrant
- `rag/user_data.py` -> ساخت متن از داده‌های کاربر
- `rag/config.py` -> تنظیمات کل RAG
---
## یک مثال خیلی ساده
فرض کن کاربر بپرسد:
`آیا خاک من برای آبیاری مناسب است؟`
سیستم این کارها را می‌کند:
1. سوال را می‌گیرد
2. می‌فهمد باید از سرویس یا دانش آبیاری استفاده کند
3. داده خاک همان کاربر را از دیتابیس می‌گیرد
4. داده هواشناسی را هم می‌گیرد
5. متن‌های مرتبط آبیاری را از Qdrant پیدا می‌کند
6. همه را به مدل می‌دهد
7. مدل جواب می‌دهد
پس جواب نهایی فقط یک حدس عمومی نیست؛
بلکه بر اساس:
- اطلاعات خاک
- اطلاعات هوا
- متن‌های مرجع آبیاری
ساخته می‌شود.
---
## نتیجه نهایی
منطق RAG این پروژه به زبان خیلی ساده این است:
- اول دانش را آماده می‌کند
- بعد موقع سوال، دانش مرتبط را پیدا می‌کند
- داده‌های واقعی کاربر را هم اضافه می‌کند
- و در آخر از مدل می‌خواهد با این اطلاعات جواب بدهد
و یادت باشد:
- `rag/apps.py` فقط فایل ثبت اپ است
- منطق واقعی RAG در فایل‌های `chat`, `retrieve`, `ingest`, `vector_store`, `user_data` و `views` قرار دارد