# مستند سیستم 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)
