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