From 3bd5a322d1c76725cad6de588ccc09060b6f532d Mon Sep 17 00:00:00 2001 From: Elshat Date: Thu, 24 Sep 2026 07:10:23 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20README=20=D0=B4=D0=BB=D1=8F=20Python=20?= =?UTF-8?q?MVP;=20robust=20seeding=20in=20run.py=20(only=20if=20DB=20empty?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++- src/dashboard/run.py | 7 ++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8208b9c..1e245e2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -# chto-ty-takoe-na-baze-kakih +# chto-ty-takoe-na-baze-kakih — AgroMarket Price Agent +MVP ИИ-агента, который ежедневно собирает открытые агро-цены (KZ/TJ/UZ/RU), +нормализует и категоризирует их, строит аналитику и отдаёт дашборд + дайджест. + +## Стек +- Python 3.12, SQLite (переносимо на PostgreSQL), APScheduler, FastAPI + Plotly +- ИИ (LLM) — только **выписывает число из фрагмента источника**, сверка посимвольно +- Каждая цена — со ссылкой на источник и датой сбора + +## Запуск +```bash +python3 -m pip install -r requirements.txt +export $(grep -v '^#' .env | xargs) # секреты в .env +python3 -m src.dashboard.run # дашборд на $PORT (дефолт 8000) +python3 -m src.scheduler --once # один прогон пайплайна + дайджест +python3 -m src.scheduler --daemon # демон (ежедневно 07:30) +python3 -m src.demo_data # синтетические данные (60 дней) +python3 -m pytest src/tests -q # тесты +``` + +## Структура +- `src/pipeline/` — fetch, extract, normalize, categorize, validate, llm_extract, raw, orchestrator +- `src/analytics/` — dynamics, spreads/arbitrage, seasonality, anomalies, alerts, forecast +- `src/dashboard/` — FastAPI: overview, products, product, countries, alerts, quality, sources +- `src/` — db.py (SQL), config.py, taxonomy.py, models.py, cache.py, scheduler.py, digest.py, demo_data.py +- `config/` — taxonomy.yaml, synonyms.yaml, sources.yaml + +## Принципы (железо) +1. **Искренность**: LLM не выдумывает цены — только цитирует число из фрагмента, + затем строковая сверка. Не прошло — запись отбрасывается (не в карантин). +2. **Корректность**: «нет данных» ≠ «цена 0». Подозрительные значения — в карантин, + не в аналитику. Ниже порога (floor по категории) → quarantine. +3. **Легальность**: tier-3 / новые источники — `legal_status: pending_review` + (выключены по умолчанию). Никакого обходa captcha/роботов; честный User-Agent, + robots.txt, rate-limit, кеш. +4. **Идентичность**: повторный запуск не дублирует записи (ключ по + source+region+product+price_type+as_of+фрагмент+snapshot). + +## Ограничения MVP +- КЗ-официальные источники (stat.gov.kz/uz/tj) недоступны из run-контейнера + (DNS) — только заглушки-адаптеры; нужен прогон из сети с доступом. +- Arbuz.kz / Sharyn — нет публичного API → `pending_review` + ручной CSV-дамп. +- Прогнозы — базовые (seasonal naive, SARIMA) с честным доверительным интервалом + и меткой «низкая уверенность». +- Модель арбитража (груз/тариф/комиссия) — предположения, помечены в UI как «assumptions». diff --git a/src/dashboard/run.py b/src/dashboard/run.py index 0d15b55..0354566 100644 --- a/src/dashboard/run.py +++ b/src/dashboard/run.py @@ -14,9 +14,14 @@ def main() -> None: format="%(asctime)s %(levelname)s %(name)s %(message)s") port = int(os.environ.get("PORT", "8000")) host = os.environ.get("DASHBOARD_HOST", "0.0.0.0") - # Make sure the DB exists and demo data is loaded (idempotent) + # Make sure the DB exists; seed demo data only if empty (idempotent) from .. import db from ..analytics.alerts import generate_alerts + rows = db.exec_sql("SELECT COUNT(*) AS t FROM prices") + if rows and rows[0]["t"] == 0 and os.environ.get("SEED_DEMO", "1") != "0": + logging.getLogger("dashboard").info("DB пуст — загружаю демо-данные") + from ..demo_data import generate + generate() _ = db.source_health() # touch schema generate_alerts() uvicorn.run("src.dashboard.app:app", host=host, port=port,