Files
Ai/docs/ai_api_cluster_data_policy.md
2026-05-13 16:45:54 +03:30

25 KiB

سیاست منبع داده و تجمیع کلاستر برای APIهای AI

این سند مشخص می‌کند هر API از کجا داده می‌گیرد، آیا الان خروجی آن بر اساس میانگین داده سنسور کلاسترها و میانگین داده ماهواره‌ای کلاسترهای location_data هست یا نه، و برای یکپارچه‌سازی چه policyای باید رعایت شود.

این سند بر اساس کد فعلی پروژه نوشته شده و مخصوص APIهای زیر است:

  • POST /api/rag/chat/
  • POST /api/soile/anomaly-detection/
  • POST /api/soile/health-summary/
  • POST /api/soile/moisture-heatmap/
  • POST /api/weather/farm-card/
  • POST /api/weather/water-need-prediction/
  • POST /api/pest-disease/detect/
  • POST /api/pest-disease/risk/
  • POST /api/irrigation/plan-from-text/
  • POST /api/irrigation/recommend/
  • POST /api/farm-data/parameters/
  • POST /api/fertilization/plan-from-text/
  • POST /api/fertilization/recommend/
  • POST /api/crop-simulation/current-farm-chart/
  • POST /api/crop-simulation/growth/
  • GET /api/crop-simulation/growth/{task_id}/status/
  • POST /api/crop-simulation/harvest-prediction/
  • GET /api/crop-simulation/yield-harvest-summary/
  • POST /api/crop-simulation/yield-prediction/
  • POST /api/economy/overview/
  • POST /api/farm-alerts/tracker/

1) قانون هدف کسب‌وکاری

قانون مطلوبی که باید برای محاسبه‌های عمومی AI رعایت شود این است:

  1. برای محاسبه‌های عمومی AI مثل RAG، crop_simulation، irrigation، fertilization، farm_alerts و سرویس‌های تحلیلی خاک، مبنای داده باید میانگین داده سنسورهای کلاسترها و میانگین داده ماهواره‌ای کلاسترهای location_data باشد.
  2. این تجمیع باید در سطح بلوک‌های بزرگ کشاورز انجام شود؛ یعنی اول هر block اصلی جداگانه محاسبه شود، بعد در صورت نیاز یک خلاصه کل مزرعه از روی blockهای اصلی ساخته شود.
  3. اگر کشاورز هنوز block تعریف نکرده باشد، حالت پیش‌فرض دامنه باید این باشد:
    • 1 بلوک بزرگ برای کل مزرعه
    • 1 بلوک کوچک داخل همان بلوک بزرگ
  4. هر API که فقط farm_uuid می‌گیرد و رفتار عمومی مزرعه را تحلیل می‌کند، باید از snapshot تجمیع‌شده‌ی مبتنی بر block/sub-block استفاده کند، نه از یک سنسور خام یا یک location خام.

2) وضعیت فعلی مدل داده

2.1) لایه مزرعه

رکورد canonical مزرعه در farm_data.SensorData نگه‌داری می‌شود:

  • شناسه اصلی: farm_uuid
  • اتصال مکانی: center_location -> location_data.SoilLocation
  • سنسورها: sensor_payload
  • آب‌وهوا: weather_forecast
  • گیاه: plant_assignments و plant_snapshots
  • روش آبیاری: irrigation_method

2.2) لایه مکانی

رکورد canonical مکانی در location_data.SoilLocation است:

  • farm_boundary
  • input_block_count
  • block_layout
  • block_subdivisions
  • remote_sensing_runs

2.3) لایه کلاستر و سنجش از دور

در location_data عملاً دو سطح تجمیع وجود دارد:

  • block = بلوک اصلی که کشاورز تعریف کرده
  • sub-block / cluster = بخش‌های کوچک‌تر داخل هر block

منطق فعلی تجمیع در location_data/satellite_snapshot.py پیاده شده است:

  • build_block_sensor_summary(...)
    • داده سنسورهای منتسب به sub-blockها را جمع می‌کند
    • ابتدا برای هر sub-block میانگین می‌سازد
    • سپس از روی sub-blockها میانگین block را می‌سازد
  • summarize_block_satellite_metrics(...)
    • داده‌های grid/cell ماهواره‌ای را برای cluster blockها خلاصه می‌کند
    • سپس میانگین block را می‌سازد
  • build_location_block_satellite_snapshots(...)
    • برای هر block اصلی یک snapshot می‌سازد
  • build_farmer_block_aggregated_snapshot(...)
    • از روی blockهای اصلی یک خلاصه کل مزرعه می‌سازد

پس زیرساخت فنی برای policy موردنظر تا حد زیادی در پروژه وجود دارد.


3) رفتار پیش‌فرض blockها

رفتار فعلی در کد

تابع build_block_layout() در location_data/models.py وقتی blockی داده نشود، این رفتار را دارد:

  • input_block_count = 1
  • فقط 1 بلوک اصلی با block-1 ساخته می‌شود
  • sub_blocks = []

یعنی الان بخش دوم rule شما کامل اعمال نشده، چون:

  • 1 بلوک بزرگ وجود دارد
  • ولی 1 بلوک کوچک داخل آن به‌صورت default ساخته نمی‌شود

رفتار مطلوب پیشنهادی

برای سازگاری با خواسته شما، policy پیشنهادی این است:

  • اگر هیچ block و هیچ subdivisionی تعریف نشده بود:
    • block_layout.blocks = [block-1]
    • داخل block-1.sub_blocks یک sub-block پیش‌فرض ساخته شود
  • این sub-block پیش‌فرض می‌تواند چیزی شبیه این داشته باشد:
{
  "sub_block_code": "block-1-sub-1",
  "cluster_label": 0,
  "source": "default",
  "boundary": {},
  "cluster_uuid": null
}

این policy باعث می‌شود تمام APIهای downstream همیشه حداقل یک سطح sub-block داشته باشند و منطق aggregation یکنواخت بماند.


4) منبع canonical پیشنهادی برای همه APIهای AI

برای APIهای عمومی AI، منبع canonical باید این ترتیب باشد:

4.1) سنسورها

منبع اصلی:

  • farm_data.SensorData.sensor_payload

اما نه به صورت خام. باید از مسیر زیر مصرف شود:

  • assign سنسورها به cluster/sub-block
  • میانگین‌گیری در سطح sub-block
  • میانگین‌گیری مجدد در سطح block اصلی
  • در صورت نیاز میانگین‌گیری در سطح کل مزرعه

4.2) ماهواره و remote sensing

منبع اصلی:

  • location_data.RemoteSensingRun
  • location_data.AnalysisGridObservation
  • location_data.RemoteSensingSubdivisionResult
  • location_data.RemoteSensingClusterBlock
  • location_data.RemoteSensingClusterAssignment

و باز هم باید از مسیر زیر مصرف شود:

  • میانگین متریک هر cluster/sub-block
  • میانگین متریک هر block اصلی
  • در صورت نیاز میانگین کل مزرعه

4.3) آب‌وهوا

منبع اصلی آب‌وهوا فعلاً cluster-based نیست و از اینجا می‌آید:

  • farm.weather_forecast
  • یا آخرین center_location.weather_forecasts

یعنی weather فعلاً location-center based است، نه cluster based.

4.4) source of truth نهایی برای APIهای عمومی

برای APIهایی که farm_uuid می‌گیرند، بهتر است source of truth نهایی یکی از این دو باشد:

  • get_farm_details(farm_uuid) بعد از ارتقا به policy جدید
  • یا یک service جدید مثل build_ai_farm_snapshot(farm_uuid)

این snapshot باید شامل این بخش‌ها باشد:

  • farm_level_aggregated_metrics
  • block_level_metrics
  • sub_block_level_metrics
  • weather
  • plants
  • irrigation_method
  • source_metadata

5) وضعیت فعلی هر API

در این بخش برای هر API مشخص شده:

  • آیا الان مبتنی بر میانگین سنسور کلاسترها و میانگین ماهواره‌ای کلاسترها هست یا نه
  • اگر نیست، الان داده را از کجا می‌گیرد
  • وضعیت انطباق آن با policy جدید

5.1) RAG

POST /api/rag/chat/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • متن خاک کاربر در rag/user_data.py -> build_user_soil_text()
  • از SensorData و center_location می‌خواند
  • از build_farmer_block_aggregated_snapshot(...) استفاده می‌کند
  • از build_location_block_satellite_snapshots(...) هم برای blockهای اصلی استفاده می‌کند

نتیجه:

  • برای summary کلی مزرعه، از aggregation مبتنی بر block استفاده می‌کند
  • برای satellite و sensor summary تا حدی با policy شما سازگار است
  • اما RAG chat هنوز یک contract رسمی و واحد برای cluster mean only ندارد و متن embed شده ممکن است ترکیبی از داده‌های سطح farm و block باشد

نیاز به اصلاح:

  • canonical snapshot باید صریحاً از block mean و sub-block mean ساخته شود
  • prompt/context باید روشن بگوید که اعداد از cluster averages آمده‌اند

POST /api/soile/anomaly-detection/

وضعیت فعلی: غیرمنطبق مستقیم، منطبق غیرمستقیم

منبع داده فعلی:

  • anomaly payload از request می‌آید
  • اطلاعات مزرعه از farm_data.services.get_farm_details()
  • سپس به rag/services/soil_anomaly.py داده می‌شود

نتیجه:

  • خود anomaly ممکن است از هر منبعی آمده باشد و API آن را enforce نمی‌کند
  • اما farm context از get_farm_details() می‌آید که فعلاً latest_satellite + sensor_payload را ترکیب می‌کند
  • get_farm_details() هنوز خلاصه soil را از build_location_satellite_snapshot(center_location) می‌گیرد، نه از build_farmer_block_aggregated_snapshot()

نیاز به اصلاح:

  • get_farm_details() باید خلاصه soil اصلی را از farmer-level aggregated snapshot بگیرد
  • anomaly input هم اگر داخلی تولید می‌شود، باید بر پایه cluster mean باشد

5.2) Soile

POST /api/soile/health-summary/

وضعیت فعلی: احتمالاً نیمه‌منطبق

منبع داده فعلی:

  • از farm_data.context.load_farm_context() و سرویس‌های soil استفاده می‌کند
  • load_farm_context() از build_location_block_satellite_snapshots(location) استفاده می‌کند
  • بخشی از context block-based است

اما:

  • اگر در summary نهایی از metrics خام سنسور یا latest weather/location استفاده شود، خروجی کاملاً cluster-mean-only نیست

نیاز به اصلاح:

  • health summary باید صریحاً از block_level و farm_level aggregated metrics استفاده کند
  • هیچ metric خام سنسور بدون عبور از منطق sub-block mean وارد پاسخ نشود

POST /api/soile/moisture-heatmap/

وضعیت فعلی: منطبق نیست

منبع داده فعلی:

  • heatmap ذاتاً spatial است و معمولاً از grid/cell یا location-level moisture data ساخته می‌شود
  • برای heatmap، میانگین‌گیری کامل روی clusterها کافی نیست چون heatmap نیاز به توزیع مکانی دارد

نتیجه:

  • این API نباید به خروجی تک‌عددی farm average تقلیل داده شود
  • بلکه باید داده خام grid/cell یا cluster-level map را نگه دارد

policy صحیح:

  • برای کارت summary یا average moisture بله، از cluster mean استفاده شود
  • اما برای heatmap خودِ خروجی باید cluster map یا grid map باشد، نه صرفاً میانگین کل مزرعه

5.3) Weather

POST /api/weather/farm-card/

وضعیت فعلی: غیرمنطبق

منبع داده فعلی:

  • از weather_forecast مرتبط با center_location
  • یا آخرین forecast همان location

نتیجه:

  • weather فعلاً بر اساس clusterهای location_data نیست
  • چون مدل weather cluster-based ندارد

نیاز به اصلاح:

  • اگر قرار است cluster-based شود، باید weather در سطح block/sub-block تعریف شود
  • در غیر این صورت، این API باید در سند به‌عنوان location-centered weather ثبت شود

POST /api/weather/water-need-prediction/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • در rag/services/water_need_prediction.py از get_farm_details(farm_uuid) استفاده می‌شود
  • همچنین از weather forecast مزرعه استفاده می‌کند

نتیجه:

  • بخش خاک/سنسور می‌تواند از snapshot مزرعه بیاید
  • اما چون get_farm_details() هنوز fully farmer-block-aggregated نیست، خروجی کامل منطبق نیست
  • بخش weather هم location-level است

نیاز به اصلاح:

  • خاک و ماهواره از farmer aggregated snapshot
  • weather تا زمان توسعه مدل cluster-weather، به‌صورت location-level باقی بماند ولی source آن شفاف اعلام شود

5.4) Pest & Disease

POST /api/pest-disease/detect/

وضعیت فعلی: غیرمنطبق مفهومی

منبع داده فعلی:

  • ورودی اصلی: تصویر
  • context کمکی: farm_uuid و داده مزرعه از get_farm_details()

نتیجه:

  • هسته این API image-based است، نه cluster average based
  • context مزرعه می‌تواند cluster-based شود، اما خروجی نهایی وابسته به تصویر است

policy صحیح:

  • context مزرعه برای کمک به تشخیص از snapshot تجمیعی بیاید
  • اما این API ذاتاً APIِ میانگین‌محور نیست

POST /api/pest-disease/risk/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • rag/services/pest_disease.py
  • اطلاعات مزرعه از get_farm_details()
  • داده RAG و پایگاه دانش تخصصی

نتیجه:

  • risk API باید از context مزرعه استفاده کند
  • اگر get_farm_details() اصلاح شود، این API هم خودبه‌خود نزدیک به policy مطلوب می‌شود

5.5) Irrigation

POST /api/irrigation/plan-from-text/

وضعیت فعلی: غیرمنطبق ذاتی

منبع داده فعلی:

  • ورودی اصلی: متن آزاد
  • مدل parser متنی

نتیجه:

  • این API parser است، نه تحلیل agronomic مبتنی بر sensor/satellite
  • بنابراین لازم نیست خروجی‌اش بر پایه cluster average باشد

policy صحیح:

  • فقط اگر farm context کمکی به parser اضافه شود، آن context باید از snapshot تجمیعی بیاید

POST /api/irrigation/recommend/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • از farm context و سرویس RAG irrigation استفاده می‌کند
  • بخشی از سنسور را ممکن است مستقیماً از sensor.sensor_payload بخواند

نتیجه:

  • اگر بعضی metricها مستقیم از payload خام خوانده شوند، با policy شما ناسازگار است

نیاز به اصلاح:

  • recommendation فقط باید از aggregated block/sub-block metrics تغذیه شود
  • fallback مستقیم به اولین سنسور یا payload خام باید حذف یا محدود شود

5.6) Farm Parameters

POST /api/farm-data/parameters/

وضعیت فعلی: خارج از دامنه این policy

منبع داده فعلی:

  • این API داده را ایجاد/ویرایش می‌کند
  • farm_boundary را می‌گیرد
  • center_location را resolve می‌کند
  • sensor_payload را ذخیره می‌کند
  • weather/location sync را trigger می‌کند

نتیجه:

  • این API تولیدکننده داده است، نه consumer تحلیلی
  • بنابراین قرار نیست خروجی analytic آن بر پایه cluster mean باشد

اما نقش مهم:

  • باید sensorها را طوری ذخیره کند که assignment به cluster/sub-block ممکن باشد
  • اگر cluster_uuid یا sub_block_code در payload نیاید، aggregation دقیق محدود می‌شود

5.7) Fertilization

POST /api/fertilization/plan-from-text/

وضعیت فعلی: غیرمنطبق ذاتی

منبع داده فعلی:

  • parser متن آزاد

نتیجه:

  • مانند irrigation text parser، این API ماهیتاً cluster-average consumer نیست

POST /api/fertilization/recommend/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • context مزرعه و RAG fertilization
  • معمولاً از get_farm_details() و contextهای وابسته استفاده می‌کند

نتیجه:

  • با اصلاح منبع canonical مزرعه، این API هم می‌تواند کاملاً منطبق شود

5.8) Crop Simulation

POST /api/crop-simulation/current-farm-chart/

POST /api/crop-simulation/growth/

POST /api/crop-simulation/harvest-prediction/

GET /api/crop-simulation/yield-harvest-summary/

POST /api/crop-simulation/yield-prediction/

وضعیت فعلی: غیرمنطبق تا نیمه‌منطبق

منبع داده فعلی:

  • سرویس‌های crop_simulation/services.py
  • بخشی از داده‌ها از farm context و بخشی از سنسور یا weather فعلی می‌آیند
  • در بعضی helperها متریک‌ها ممکن است مستقیماً از sensor_payload یا propertyهای سنسور resolve شوند

نتیجه:

  • با rule شما که گفته بودی برای crop_simulation باید از داده کل بلوک‌های بزرگ استفاده شود، وضعیت فعلی هنوز کامل نیست
  • چون منبع canonical شبیه‌سازی هنوز به‌صورت صریح farmer-block aggregation enforced نشده

نیاز به اصلاح:

  • ورودی تمام simulation endpointها باید از farm aggregated snapshot بیاید
  • یعنی متریک‌های soil moisture, temperature, ndvi, ndwi, slope, nitrogen, phosphorus, potassium از cluster mean -> block mean -> farm mean ساخته شوند
  • weather فعلاً جداگانه location-level می‌ماند مگر بعداً block-weather اضافه شود

GET /api/crop-simulation/growth/{task_id}/status/

وضعیت فعلی: وابسته به منبع run اولیه

نتیجه:

  • این endpoint فقط status/result job را برمی‌گرداند
  • اگر growth هنگام شروع از داده غیرمنطبق استفاده کرده باشد، status endpoint هم همان خروجی را reflect می‌کند

5.9) Economy

POST /api/economy/overview/

وضعیت فعلی: منطبق نیست و حتی منبع واقعی ندارد

منبع داده فعلی:

  • economy/services.py
  • پیام صریح دارد که منبع واقعی تنظیم نشده است

نتیجه:

  • این API فعلاً نه cluster-based است و نه حتی data-backed

نیاز به اصلاح:

  • اگر قرار است فعال شود، باید از snapshot مزرعه + هزینه‌ها + yield prediction + irrigation/fertilization plan استفاده کند

5.10) Farm Alerts

POST /api/farm-alerts/tracker/

وضعیت فعلی: نیمه‌منطبق

منبع داده فعلی:

  • farm_alerts/services.py
  • از load_farm_context(farm_uuid) و get_farm_details(farm_uuid) استفاده می‌کند

نتیجه:

  • چون farm context بخشی از snapshotهای block-based را می‌خواند، تا حدی با policy سازگار است
  • اما اگر context نهایی هنوز metric خام یا latest non-aggregated را ترجیح بدهد، کامل منطبق نیست

نیاز به اصلاح:

  • همه alert ruleها باید روی block/farm aggregated metrics سوار شوند
  • در صورت نیاز، هشدارهای spatial باید روی cluster-level breakdown هم نمایش داده شوند

6) جمع‌بندی سریع انطباق APIها

APIهایی که ماهیتاً باید cluster-aggregated باشند

این‌ها باید به policy جدید مهاجرت کنند:

  • POST /api/rag/chat/
  • POST /api/soile/anomaly-detection/
  • POST /api/soile/health-summary/
  • POST /api/weather/water-need-prediction/
  • POST /api/pest-disease/risk/
  • POST /api/irrigation/recommend/
  • POST /api/fertilization/recommend/
  • POST /api/crop-simulation/current-farm-chart/
  • POST /api/crop-simulation/growth/
  • POST /api/crop-simulation/harvest-prediction/
  • GET /api/crop-simulation/yield-harvest-summary/
  • POST /api/crop-simulation/yield-prediction/
  • POST /api/farm-alerts/tracker/

APIهایی که ماهیتاً parser/input/image/weather هستند و کامل cluster-based نیستند

  • POST /api/pest-disease/detect/ -> image-first
  • POST /api/irrigation/plan-from-text/ -> text parser
  • POST /api/fertilization/plan-from-text/ -> text parser
  • POST /api/farm-data/parameters/ -> data ingestion/upsert
  • POST /api/weather/farm-card/ -> location-centered weather
  • POST /api/economy/overview/ -> فعلاً منبع واقعی ندارد

APIهایی که spatial map هستند و نباید صرفاً average شوند

  • POST /api/soile/moisture-heatmap/

این API باید cluster/grid-aware بماند، نه فقط average-based.


7) مهم‌ترین mismatch فعلی در کد

مهم‌ترین mismatch فعلی این است که farm_data.services.get_farm_details() برای soil.resolved_metrics این کار را می‌کند:

  • latest_satellite = build_location_satellite_snapshot(center_location)
  • سپس latest_satellite.resolved_metrics را به عنوان soil_metrics می‌گیرد

این یعنی:

  • خلاصه اصلی soil بر اساس یک snapshot location/block واحد ساخته می‌شود
  • نه بر اساس build_farmer_block_aggregated_snapshot(center_location, sensor_payload=...)

در حالی که برای policy موردنظر شما بهتر است این کار انجام شود:

  • farm_level_snapshot = build_farmer_block_aggregated_snapshot(...)
  • soil.resolved_metrics = farm_level_snapshot.resolved_metrics
  • soil.satellite_snapshots = block-level snapshots
  • در صورت نیاز sub_block snapshots هم در context بمانند

8) پیشنهاد ساختار استاندارد پاسخ داده برای AI

برای همه سرویس‌های AI بهتر است یک snapshot واحد با این ساختار وجود داشته باشد:

{
  "farm_uuid": "...",
  "aggregation_policy": {
    "sensor": "cluster_mean_then_block_mean_then_farm_mean",
    "satellite": "cluster_mean_then_block_mean_then_farm_mean",
    "weather": "center_location_latest_forecast",
    "default_block_policy": "1_main_block + 1_default_sub_block_when_missing"
  },
  "farm_metrics": {},
  "block_metrics": [],
  "sub_block_metrics": [],
  "weather": {},
  "plants": [],
  "irrigation_method": {}
}

مزیت این طراحی:

  • همه APIها source of truth یکسان دارند
  • اختلاف بین RAG، crop simulation، irrigation و alerts کم می‌شود
  • تست‌پذیری ساده‌تر می‌شود

9) نتیجه نهایی

الان کدام APIها دقیقاً مطابق خواسته شما نیستند؟

تقریباً همه APIهای تحلیلی هنوز کاملاً مطابق policy شما نیستند، چون منبع canonical یکپارچه‌ای که صریحاً بر پایه cluster mean sensor + cluster mean satellite باشد هنوز همه‌جا enforce نشده است.

بیشترین فاصله با policy مطلوب در این بخش‌هاست:

  • crop_simulation
  • irrigation/recommend
  • fertilization/recommend
  • weather/farm-card
  • economy/overview
  • بخشی از soile و farm_alerts

الان کدام APIها از همین حالا تا حدی نزدیک هستند؟

  • rag/chat
  • farm_alerts/tracker
  • soile/health-summary
  • pest-disease/risk

چون به‌صورت مستقیم یا غیرمستقیم از snapshotهای block-based و get_farm_details() استفاده می‌کنند.

الان کدام APIها ماهیتاً نباید صرفاً average-based شوند؟

  • soile/moisture-heatmap
  • pest-disease/detect
  • parserهای plan-from-text

10) پیشنهاد اجرای فنی در کد

ترتیب پیشنهادی برای پیاده‌سازی:

  1. get_farm_details() را به farmer aggregated snapshot مهاجرت بده.
  2. یک service جدید مثل build_ai_farm_snapshot() بساز.
  3. default block policy را به 1 main block + 1 default sub-block ارتقا بده.
  4. همه APIهای تحلیلی را وادار کن فقط از snapshot جدید بخوانند.
  5. برای APIهای spatial مثل heatmap، علاوه بر average summary، خروجی cluster/grid breakdown نگه دار.