394 lines
15 KiB
Markdown
394 lines
15 KiB
Markdown
# مستند سیستم RAG — پایگاه دانش CropLogic
|
|
|
|
## فهرست
|
|
|
|
1. [معرفی کلی](#معرفی-کلی)
|
|
2. [معماری و ساختار](#معماری-و-ساختار)
|
|
3. [منابع داده](#منابع-داده)
|
|
4. [پایپلاین Embedding](#پایپلاین-embedding)
|
|
5. [نحوه اجرا](#نحوه-اجرا)
|
|
6. [فلوی پیام کاربر](#فلوی-پیام-کاربر)
|
|
7. [API Endpoint](#api-endpoint)
|
|
8. [تنظیمات](#تنظیمات)
|
|
9. [ایزولهسازی کاربران](#ایزولهسازی-کاربران)
|
|
10. [سرویسهای توصیه](#سرویسهای-توصیه)
|
|
|
|
---
|
|
|
|
## معرفی کلی
|
|
|
|
سیستم RAG در CropLogic یک چت هوشمند کشاورزی است که:
|
|
|
|
- **دانش پایه کشاورزی** را embed و ذخیره میکند
|
|
- **دادههای خاک و هواشناسی هر کاربر** را از DB میخواند و embed میکند
|
|
- وقتی کاربر سوال میپرسد، **اطلاعات مرتبط** را بازیابی و به **LLM** ارسال میکند
|
|
|
|
**Vector Store:** Qdrant
|
|
**API Provider:** GapGPT (با fallback به Avalai) — Adapter Pattern
|
|
|
|
### پایگاههای دانش مجزا
|
|
|
|
سیستم از **سه پایگاه دانش** مجزا استفاده میکند:
|
|
|
|
| KB | توضیح | فایل Tone |
|
|
|----|-------|-----------|
|
|
| `chat` | چت عمومی و پاسخ به سوالات متنوع | `config/tones/chat_tone.txt` |
|
|
| `irrigation` | توصیههای آبیاری (فرمت JSON) | `config/tones/irrigation_tone.txt` |
|
|
| `fertilization` | توصیههای کودهی (فرمت JSON) | `config/tones/fertilization_tone.txt` |
|
|
|
|
تشخیص هوشمند KB از روی کلمات کلیدی سوال (آبیاری، آب، کود، NPK).
|
|
|
|
---
|
|
|
|
## معماری و ساختار
|
|
|
|
```
|
|
rag/
|
|
├── config.py # بارگذاری تنظیمات از rag_config.yaml
|
|
├── api_provider.py # Adapter Pattern برای GapGPT/Avalai
|
|
├── client.py # ساخت کلاینت Qdrant
|
|
├── chunker.py # تکهتکه کردن متن
|
|
├── embedding.py # تعبیهسازی متن
|
|
├── vector_store.py # ذخیره و جستجو در Qdrant (با فیلتر kb_name)
|
|
├── user_data.py # خواندن دادههای خاک/سنسور/هواشناسی از DB
|
|
├── ingest.py # پایپلاین: خواندن → چانک → embed → ذخیره
|
|
├── retrieve.py # بازیابی: embed کوئری → جستجو
|
|
├── chat.py # ساخت context و چت استریمی با LLM
|
|
├── views.py # API endpoint
|
|
├── urls.py # مسیریابی
|
|
├── tasks.py # تسک Celery
|
|
├── services/ # سرویسهای توصیه (بدون API)
|
|
│ ├── irrigation.py # توصیه آبیاری
|
|
│ └── fertilization.py # توصیه کودهی
|
|
└── management/commands/
|
|
└── rag_ingest.py
|
|
```
|
|
|
|
فایلهای تنظیمات:
|
|
|
|
```
|
|
config/
|
|
├── rag_config.yaml
|
|
├── tones/
|
|
│ ├── chat_tone.txt
|
|
│ ├── irrigation_tone.txt
|
|
│ └── fertilization_tone.txt
|
|
└── knowledge_base/
|
|
├── chat/
|
|
├── irrigation/
|
|
└── fertilization/
|
|
```
|
|
|
|
---
|
|
|
|
## منابع داده
|
|
|
|
سیستم از **چهار منبع** داده تغذیه میشود:
|
|
|
|
### 1. لحنهای مجزا — `config/tones/`
|
|
|
|
هر KB یک فایل لحن مخصوص دارد که سبک خروجی LLM را تعریف میکند.
|
|
|
|
ذخیره با: `sensor_uuid = __global__`, `kb_name = chat|irrigation|fertilization`
|
|
|
|
### 2. پایگاههای دانش — `config/knowledge_base/`
|
|
|
|
- `chat/`: دانش عمومی کشاورزی
|
|
- `irrigation/`: دانش تخصصی آبیاری (ET0، بارش، رطوبت)
|
|
- `fertilization/`: دانش تخصصی کودهی (NPK، pH، نوع خاک)
|
|
|
|
ذخیره با: `sensor_uuid = __global__`, `kb_name = chat|irrigation|fertilization`
|
|
|
|
### 3. دادههای خاک کاربر — از DB
|
|
|
|
برای هر سنسور:
|
|
- `SensorData`: رطوبت، دما، pH، EC، NPK
|
|
- `SoilLocation`: مختصات جغرافیایی
|
|
- `SoilDepthData`: دادههای خاک در سه عمق
|
|
|
|
تابع `build_user_soil_text()` این دادهها را به متن فارسی تبدیل میکند.
|
|
|
|
ذخیره با: `sensor_uuid = {uuid واقعی}`, `kb_name = __all__`
|
|
|
|
### 4. دادههای هواشناسی کاربر — از DB
|
|
|
|
- `WeatherForecast`: پیشبینی ۷ روز آینده (دما، بارش، رطوبت، باد، ET0)
|
|
|
|
تابع `build_user_weather_text()` این دادهها را به متن فارسی تبدیل میکند.
|
|
|
|
ذخیره با: `sensor_uuid = {uuid واقعی}`, `kb_name = __all__`
|
|
|
|
---
|
|
|
|
## پایپلاین Embedding
|
|
|
|
```
|
|
منابع → load_sources() → chunk_text() → embed_texts() → Qdrant
|
|
```
|
|
|
|
1. **بارگذاری منابع** (`ingest.py:load_sources`):
|
|
- لحنها از `config/tones/`
|
|
- KBها از `config/knowledge_base/`
|
|
- دادههای کاربران از DB (`user_data.py`)
|
|
|
|
2. **چانک کردن** (`chunker.py`):
|
|
- حداکثر ۵۰۰ توکن هر چانک
|
|
- ۵۰ توکن همپوشانی
|
|
|
|
3. **Embedding** (`embedding.py`):
|
|
- استفاده از `api_provider.get_embedding_client()`
|
|
- مدل: `text-embedding-3-small`
|
|
- بچسایز: ۳۲
|
|
|
|
4. **ذخیره در Qdrant** (`vector_store.py`):
|
|
- هر point: `{id, vector[1536], payload{text, source, sensor_uuid, kb_name, chunk_index}}`
|
|
|
|
---
|
|
|
|
## نحوه اجرا
|
|
|
|
### دستی
|
|
|
|
```bash
|
|
python manage.py rag_ingest --recreate
|
|
```
|
|
|
|
### دورهای (Celery Beat)
|
|
|
|
تسک `rag_ingest_task` هر ۶ ساعت اجرا میشود و دادههای جدید را embed میکند.
|
|
|
|
---
|
|
|
|
## فلوی پیام کاربر
|
|
|
|
```
|
|
POST /api/rag/chat/ {message, sensor_uuid}
|
|
↓
|
|
1. تشخیص KB از روی کلمات کلیدی (_detect_kb_intent)
|
|
↓
|
|
2. بارگذاری دادههای فعلی کاربر از DB:
|
|
- build_user_soil_text(sensor_uuid)
|
|
- build_user_weather_text(sensor_uuid)
|
|
↓
|
|
3. Embed کردن سوال (embed_single)
|
|
↓
|
|
4. جستجو در Qdrant با فیلتر:
|
|
- sensor_uuid = {uuid کاربر} OR __global__
|
|
- kb_name = {detected_kb} OR __all__
|
|
↓
|
|
5. ساخت context:
|
|
[دادههای فعلی خاک] + [پیشبینی هواشناسی] + [متنهای مرجع از RAG]
|
|
↓
|
|
6. ارسال به LLM (GapGPT):
|
|
system_prompt = tone + دستورالعمل + context
|
|
↓
|
|
7. StreamingHttpResponse → کاربر
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoint
|
|
|
|
### POST `/api/rag/chat/`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"message": "وضعیت خاک من چطوره؟",
|
|
"sensor_uuid": "550e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
**Response:** Stream متنی (text/plain)
|
|
|
|
---
|
|
|
|
## تنظیمات
|
|
|
|
### `config/rag_config.yaml`
|
|
|
|
```yaml
|
|
embedding:
|
|
provider: "gapgpt" # gapgpt یا avalai
|
|
model: "text-embedding-3-small"
|
|
base_url: "https://api.gapgpt.app/v1"
|
|
api_key_env: "GAPGPT_API_KEY"
|
|
avalai_base_url: "https://api.avalai.ir/v1"
|
|
avalai_api_key_env: "AVALAI_API_KEY"
|
|
|
|
qdrant:
|
|
host: "localhost"
|
|
port: 6333
|
|
collection_name: "croplogic_kb"
|
|
vector_size: 1536
|
|
|
|
chunking:
|
|
max_chunk_tokens: 500
|
|
overlap_tokens: 50
|
|
|
|
llm:
|
|
model: "gpt-4o"
|
|
base_url: "https://api.gapgpt.app/v1"
|
|
api_key_env: "GAPGPT_API_KEY"
|
|
avalai_base_url: "https://api.avalai.ir/v1"
|
|
avalai_api_key_env: "AVALAI_API_KEY"
|
|
|
|
knowledge_bases:
|
|
chat:
|
|
path: "config/knowledge_base/chat"
|
|
tone_file: "config/tones/chat_tone.txt"
|
|
irrigation:
|
|
path: "config/knowledge_base/irrigation"
|
|
tone_file: "config/tones/irrigation_tone.txt"
|
|
fertilization:
|
|
path: "config/knowledge_base/fertilization"
|
|
tone_file: "config/tones/fertilization_tone.txt"
|
|
```
|
|
|
|
### متغیرهای محیطی
|
|
|
|
| متغیر | توضیح |
|
|
|-------|-------|
|
|
| `GAPGPT_API_KEY` | کلید API برای GapGPT |
|
|
| `AVALAI_API_KEY` | کلید API برای Avalai (fallback) |
|
|
| `QDRANT_HOST` | آدرس Qdrant |
|
|
| `QDRANT_PORT` | پورت Qdrant |
|
|
|
|
---
|
|
|
|
## ایزولهسازی کاربران
|
|
|
|
- هر چانک یک فیلد `sensor_uuid` در metadata دارد
|
|
- دادههای عمومی: `sensor_uuid = __global__`
|
|
- دادههای کاربر: `sensor_uuid = {uuid واقعی}`
|
|
- هنگام جستجو، فیلتر `should` اعمال میشود:
|
|
- `sensor_uuid = {uuid کاربر}` OR `__global__`
|
|
- `kb_name = {detected_kb}` OR `__all__`
|
|
- نتیجه: هر کاربر فقط دادههای خودش + دانش عمومی را میبیند
|
|
|
|
---
|
|
|
|
## سرویسهای توصیه
|
|
|
|
سرویسهای آبیاری و کودهی **بدون API** هستند و از RAG استفاده میکنند.
|
|
|
|
### توصیه آبیاری
|
|
|
|
```python
|
|
from rag.services import get_irrigation_recommendation
|
|
|
|
result = get_irrigation_recommendation(
|
|
sensor_uuid="550e8400-...",
|
|
query="توصیه آبیاری برای مزرعه من چیست؟" # اختیاری
|
|
)
|
|
```
|
|
|
|
**خروجی:**
|
|
```python
|
|
{
|
|
"irrigation_needed": True,
|
|
"amount_mm": 25.0,
|
|
"reason": "رطوبت خاک پایین و بارش پیشبینی نشده",
|
|
"next_check_date": "2026-03-20",
|
|
"raw_response": "..."
|
|
}
|
|
```
|
|
|
|
### توصیه کودهی
|
|
|
|
```python
|
|
from rag.services import get_fertilization_recommendation
|
|
|
|
result = get_fertilization_recommendation(
|
|
sensor_uuid="550e8400-...",
|
|
query="توصیه کودهی برای مزرعه من چیست؟" # اختیاری
|
|
)
|
|
```
|
|
|
|
**خروجی:**
|
|
```python
|
|
{
|
|
"fertilizer_needed": True,
|
|
"fertilizer_type": "NPK 20-10-10",
|
|
"amount_kg_per_hectare": 150.0,
|
|
"reason": "سطح ازت پایین",
|
|
"npk_status": {
|
|
"nitrogen": "low",
|
|
"phosphorus": "normal",
|
|
"potassium": "normal"
|
|
},
|
|
"raw_response": "..."
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## نمودار معماری
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ منابع داده │
|
|
│ │
|
|
│ ┌──────────┐ ┌──────────────┐ ┌───────────────────┐ │
|
|
│ │ tones/ │ │ knowledge_ │ │ Django DB │ │
|
|
│ │ 3 files │ │ base/ │ │ SensorData │ │
|
|
│ │ │ │ chat/irrig/ │ │ SoilLocation │ │
|
|
│ │ │ │ fertiliz/ │ │ SoilDepthData │ │
|
|
│ │ │ │ │ │ WeatherForecast │ │
|
|
│ └────┬─────┘ └──────┬───────┘ └────────┬──────────┘ │
|
|
│ │ │ │ │
|
|
│ └───────────┬────┘ │ │
|
|
│ __global__ sensor_uuid │
|
|
│ kb_name=chat/ kb_name=__all__ │
|
|
│ irrigation/ │
|
|
│ fertilization │
|
|
└───────────────┬────────────────────────┬────────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ ingest pipeline │
|
|
│ │
|
|
│ load_sources() → chunk_text() → embed_texts() │
|
|
│ (با Adapter Pattern: GapGPT/Avalai) │
|
|
│ │
|
|
│ کامند: python manage.py rag_ingest --recreate │
|
|
│ تسک: rag_ingest_task.delay(recreate=True) │
|
|
└────────────────────────┬────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Qdrant │
|
|
│ collection: croplogic_kb │
|
|
│ │
|
|
│ هر point = {id, vector[1536], payload{text, │
|
|
│ source, sensor_uuid, kb_name, │
|
|
│ chunk_index}} │
|
|
└────────────────────────┬────────────────────────────────┘
|
|
│
|
|
(هنگام سوال کاربر)
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ فلوی پاسخ به کاربر │
|
|
│ │
|
|
│ 1. POST /api/rag/chat/ {message, sensor_uuid} │
|
|
│ 2. تشخیص KB از کلمات کلیدی (_detect_kb_intent) │
|
|
│ 3. build_user_soil_text() + build_user_weather_text() │
|
|
│ 4. embed_single(message) → query vector │
|
|
│ 5. Qdrant search با فیلتر sensor_uuid + kb_name │
|
|
│ 6. system_prompt = tone + دستورالعمل + context │
|
|
│ 7. GapGPT LLM (gpt-4o) → streaming response │
|
|
│ 8. StreamingHttpResponse → کاربر │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
**تغییرات اخیر:**
|
|
|
|
- ✅ Adapter Pattern برای سوئیچ بین GapGPT و Avalai
|
|
- ✅ سه پایگاه دانش مجزا (chat/irrigation/fertilization)
|
|
- ✅ دادههای هواشناسی embed میشوند
|
|
- ✅ فیلتر `kb_name` در جستجوی Qdrant
|
|
- ✅ سرویسهای توصیه آبیاری و کودهی (بدون API)
|