first commit
This commit is contained in:
+393
@@ -0,0 +1,393 @@
|
||||
# مستند سیستم 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)
|
||||
Reference in New Issue
Block a user