Files
ERP/bot/docs/DEPLOY-AND-MONITORING.md
T
whqreza 0adadd2cf8 feat(monitoring): bot observability dashboards — Grafana + Persian RTL ERP page
Grafana (full use of the existing monitoring/ stack, per OSS policy):
- datasources.yml: BotPostgres (uid bot-postgres) over the shared network;
  grafana service joins 'shared' + carries the datasource env
- db_init.py: idempotent read-only role obs_reader (USAGE on schema public +
  SELECT on bot_traces/bot_trace_events; skipped when
  OBS_READER_PASSWORD empty; ALTER ROLE for rotation) + composite index
  ix_bot_trace_events_stage_time (stage, created_at DESC) — senior-engineer
  review applied with the column-name fix (event_type -> stage); (trace_id)
  already covered by the existing (trace_id, created_at) index
- dashboards/bot-observability.json: KPI row (real traffic, events, Jev/
  fallback errors, Jev p95, tokens, commits, archive guard), events/day,
  per-stage p50/p95, mention statuses (arbiter+fallback aggregates + legacy
  rows), DeepSeek arbiter actions, tokens/day, model-drift vs jev-1.13.0,
  user-choice A/B, STT + audio.enhance health, archive size guard
- dashboards/bot-trace-explorer.json: trace table + full event drill-down
  via the trace_id variable

ERP page (admin-only, Persian RTL, recharts@^3 for React 19):
- backend bot_metrics.py: read-only require_role('admin') endpoints
  (summary/routing/quality/jev/stt/traces + trace detail); aggregates count
  real traffic only (calibration/demo/chat_id=0 excluded)
- frontend BotMonitorPage at /bot-monitor (KPI cards, daily events,
  status donut, arbiter actions, user-choice A/B, STT/enhance tables,
  stage latency, tokens chart, traces table + detail drawer with payload
  viewer); entry link from the admin panel; MobileTabBar hidden there
- api.ts: typed botMetrics client section

Tooling/docs:
- tools/seed_observability_demo.py: deterministic demo traces (source='demo',
  every current pipeline stage incl. audio.enhance/jev.failure/arbitration/
  fallback/user choices) — excluded from real-traffic aggregates
- monitoring/README.md + bot/docs/DEPLOY-AND-MONITORING.md: setup, seed and
  rollback notes; alerts deliberately deferred (owner decision 2026-10-24)

Verified locally: obs_reader SELECT ok / INSERT denied; panels' SQL run as
obs_reader with data; datasource health OK; both dashboards survive a grafana
restart; endpoints 200 as admin and 403 without role; tsc + vite build clean;
page renders in-browser (admin session); bot startup provisions role+index.
2026-10-24 15:09:01 +03:30

98 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# استقرار و پایش — فاز ۷ (Jev + Observability)
وضعیت به‌روزشده: 2026-10-12 · برنچ: `prod-v7` · **هیچ پوشی انجام نمی‌شود تا مالک دستور بدهد.**
## الف) وضعیت فعلی
- بات همین حالا **روی استک لوکال** اجرا می‌شود (long-polling بله؛ بدون پورت ورودی) و
`JEV_ENABLED` + `OBS_ENABLED` روی آن فعال است.
- استقرار بات روی سرور یک **تصمیم جدا** است (نه بخشی از مرج تیم). تا آن تصمیم، بات لوکال می‌ماند.
- ⚠️ **قانون یک‌پولره:** Bale فقط اجازه‌ی یک مصرف‌کننده‌ی `getUpdates` می‌دهد. اگر باتِ سرور و
باتِ لوکال هم‌زمان روشن باشند، پیام‌ها بین آن‌ها گم/تکراری می‌شوند. **قبل از روشن‌کردن بات سرور،
بات لوکال باید خاموش شود.**
## ب) پیش‌نیازهای سرور (وقتی تصمیم گرفته شد)
کلیدها و تنظیم‌های بات (در گیت نیستند — از طریق Coolify env یا فایل `bot/.env` روی سرور):
| متغیر | مقدار |
|---|---|
| `PROCUREMENT_BOT_BOT_TOKEN` | توکن بله (rotated — بخش امنیتی پایین را ببین) |
| `PROCUREMENT_BOT_AI_API_KEY` | کلید DeepSeek |
| `PROCUREMENT_BOT_TYPESAFE_API_KEY` | کلید Jev |
| `PROCUREMENT_BOT_API_BASE_URL` | `http://api:8000/api/v1` |
| `PROCUREMENT_BOT_DATABASE_URL` | `postgresql+asyncpg://postgres:***@postgres:5432/procurement` |
| `PROCUREMENT_BOT_JEV_CONF_EXACT` / `_FLOOR` | اختیاری — پیش‌فرض کالیبره: `0.85` / `0.65` |
| `PROCUREMENT_BOT_OBS_PROMPT_VERSION` | `nlu-v2-entity-mentions` (برای prompt_hash) |
ولوم‌ها: `bot/app` (کد)، `bot/tools` (ابزارها/pایش)، `bot/data` (**صدای خام — آرشیو، نه حذف**)،
`bot/.env:ro`.
نکته: **mount جدید فقط با recreate اعمال می‌شود، نه restart** → `docker compose up -d --force-recreate bot`.
بلوک آماده‌ی سرویس بات برای کامپوز دیپلوی Coolify (فقط «در صورت» تصمیم — عمداً به فایل کامپوز
اضافه نشده چون سرویس‌شدن خودکار = خطر دو‌پولری و تغییر رفتار سرور):
```yaml
bot:
image: v15-bot:latest
build: { context: ./bot, dockerfile: Dockerfile }
restart: unless-stopped
mem_limit: 512m
volumes:
- ./bot/data:/srv/bot/data
- ./bot/tools:/srv/bot/tools
environment:
PROCUREMENT_BOT_BOT_TOKEN: ${BALE_BOT_TOKEN}
PROCUREMENT_BOT_AI_API_KEY: ${AI_API_KEY}
PROCUREMENT_BOT_TYPESAFE_API_KEY: ${TYPESAFE_API_KEY}
PROCUREMENT_BOT_API_BASE_URL: http://api:8000/api/v1
PROCUREMENT_BOT_DATABASE_URL: postgresql+asyncpg://postgres:${POSTGRES_PASSWORD}@postgres:5432/procurement
```
## ج) ترتیب استقرار (فقط وقتی مالک گفت «پوش کن»)
1. مهندس ارشد `prod-v7` را با بقیه مرج و CI/Coolify دیپلوی ERP را انجام می‌دهد.
2. تصمیم بات سرور (بالا) → در صورت تأیید: خاموش‌کردن بات لوکال → روشن‌کردن بات سرور.
3. **Smoke test (چک‌لیست پلن §۷ بند ۱۲):** ۱۰ پیام نمونه شامل «منشیانه/دفتر/مصطفوی» + یک ویس؛
بررسی: اتصال دقیق‌ها، پیام ابهام با دکمه‌های کاندیدا، ساخت جدید، پیش‌نویس، ثبت نهایی.
4. راستی‌آزمایی پایش: `docker compose exec bot python tools/monitor.py --days 1`
→ قیف باید پر شود و رخدادهای همان پیام‌ها در `bot_trace_events` باشد.
5. چک سهمیه: `usage` در `jev.result` (توکن ورودی/خروجی) + latency p95.
6. **داشبوردها (۲۰۲۶-۱۰-۲۴):** `PROCUREMENT_BOT_OBS_READER_PASSWORD` را در `.env` ست کنید و
یک بار بات را restart کنید تا نقش فقط‌خواندنی `obs_reader` ساخته شود؛ سپس گرفانا را
(ری)استارت کنید — دیتاسورس `BotPostgres` و دو داشبورد «بات — Observability خط لوله» و
«بات — اکسپلورر ترس‌ها» خودکار provision می‌شوند (جزئیات در `monitoring/README.md`).
صفحه‌ی فارسی داخل ERP هم روی مسیر `/bot-monitor` (فقط ادمین) است.
برای دیدن همه‌ی پنل‌ها روی نصب تازه: `docker compose exec bot python tools/seed_observability_demo.py --days 7`
(داده‌ها با `source='demo'` از آمار ترافیک واقعی جدا می‌مانند).
## د) Rollback (بدون دیپلوی — فقط env + restart)
| سوئیچ | اثر |
|---|---|
| `JEV_ENABLED=false` | غیرفعال‌شدن Jev — mentionها مستقیم به فالبک DeepSeek می‌روند |
| `OBS_ENABLED=false` | خاموش‌شدن ثبت (داده‌های قبلی دست‌نخورده می‌مانند) |
حذف کامل داشبوردها (در صورت نیاز): دو JSON در `monitoring/grafana/dashboards/` + دیتاسورس در
`datasources.yml` + `backend/app/routers/bot_metrics.py` + `frontend/src/pages/BotMonitor/` + `DROP ROLE obs_reader`.
هیچ داده‌ای حذف نمی‌شود.
## ه) پایش هفتگی
```bash
docker compose exec bot python tools/monitor.py --days 7 # DIGEST
docker compose exec bot python tools/monitor.py --days 7 --check # exit 1 اگر هشدار
```
- خروجی: کنسول + فایل `bot/data/reports/monitoring_<ts>.md`
- آمار **فقط روی ترافیک واقعی** است (calibration/test حذف می‌شوند؛ `--include-calibration` برای دیدن همه).
- هشدارها (پلن §۴.۵): حجم Observability > ۵GB یا رخداد ماهانه > ۱۰۰هزار → **جلسه‌ی «آرشیو، نه حذف»**؛
انحراف نسخه‌ی مدل از پین `jev-1.13.0`؛ نسبت خطای Jev > ۲۰٪.
- پروکسی کیفیت: انتخاب کاربر در ابهام‌ها (گزینه اول مدل / گزینه دیگر / «هیچ‌کدام») — این عددِ A/B واقعی است.
## و) یادآوری امنیتی (از قبل علامت‌خورده — تصمیم مالک)
`docker-compose.yml` (نسخه لوکال) هنوز توکن بله و کلید DeepSeek را به‌صورت پیش‌فرض
(`:-...`) داخل خود دارد. قبل از هر پوش/جابه‌جایی سرور، چرخش کلیدها توصیه می‌شود.