مستند سیستم RAG — پایگاه دانش CropLogic
فهرست
- معرفی کلی
- معماری و ساختار
- منابع داده
- پایپلاین Embedding
- نحوه اجرا
- فلوی پیام کاربر
- API Endpoint
- تنظیمات
- ایزولهسازی کاربران
- سرویسهای توصیه
معرفی کلی
سیستم 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، NPKSoilLocation: مختصات جغرافیایی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
-
بارگذاری منابع (
ingest.py:load_sources):- لحنها از
config/tones/ - KBها از
config/knowledge_base/ - دادههای کاربران از DB (
user_data.py)
- لحنها از
-
چانک کردن (
chunker.py):- حداکثر ۵۰۰ توکن هر چانک
- ۵۰ توکن همپوشانی
-
Embedding (
embedding.py):- استفاده از
api_provider.get_embedding_client() - مدل:
text-embedding-3-small - بچسایز: ۳۲
- استفاده از
-
ذخیره در Qdrant (
vector_store.py):- هر point:
{id, vector[1536], payload{text, source, sensor_uuid, kb_name, chunk_index}}
- هر point:
نحوه اجرا
دستی
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:
{
"message": "وضعیت خاک من چطوره؟",
"sensor_uuid": "550e8400-e29b-41d4-a716-446655440000"
}
Response: Stream متنی (text/plain)
تنظیمات
config/rag_config.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 استفاده میکنند.
توصیه آبیاری
from rag.services import get_irrigation_recommendation
result = get_irrigation_recommendation(
sensor_uuid="550e8400-...",
query="توصیه آبیاری برای مزرعه من چیست؟" # اختیاری
)
خروجی:
{
"irrigation_needed": True,
"amount_mm": 25.0,
"reason": "رطوبت خاک پایین و بارش پیشبینی نشده",
"next_check_date": "2026-03-20",
"raw_response": "..."
}
توصیه کودهی
from rag.services import get_fertilization_recommendation
result = get_fertilization_recommendation(
sensor_uuid="550e8400-...",
query="توصیه کودهی برای مزرعه من چیست؟" # اختیاری
)
خروجی:
{
"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)