145 lines
11 KiB
Markdown
145 lines
11 KiB
Markdown
# دستیار هوشمند سایت (چتبات)
|
|
|
|
سرویس گفتگوی محتوای سایت «گروه ساختمانی آرامش» — بازدیدکننده به فارسی سؤال میپرسد و
|
|
دستیار **فقط از روی محتوای خود سایت** (مقالهها، پروژهها، خدمات، سؤالات متداول، اطلاعات تماس و…)
|
|
پاسخ میدهد؛ اگر پاسخ در محتوا نباشد، بهجای ساخت جواب، صادقانه اعلام میکند.
|
|
|
|
این سرویس پورت خط لولهٔ پروژهٔ `llm-srerver-chatbot` (نسخه v13) با چهار تغییر اصلی برای این سایت است:
|
|
|
|
1. **پایگاه sparse بهجای SPLADE از BM25 استفاده میکند** — SPLADE انگلیسیمحور است و روی
|
|
فارسی عملاً بیفایده؛ BM25 با نرمالسازی فارسی (ی/ک عربی، اعراب، ارقام، نیمفاصله) جایگزین شده است.
|
|
2. **ذخیرهساز برداری pgvector است، نه FAISS** — بردارها در یک دیتابیس جدا (`aramesh_chatbot`)
|
|
روی همان نمونه Postgres نگهداری میشوند؛ همگامسازی افزایشی است (فقط سطرهای جدید/تغییریافته
|
|
امبد میشوند)، بکاپگیری با خود دیتابیس است و فایل ایندکس جدا وجود ندارد.
|
|
3. **تأخیر کمتر** — مسیریابی `alpha` با LLM و گسترش چندپرسشی (multi-query) بهصورت پیشفرض
|
|
خاموشاند (با متغیر محیطی قابل فعالسازی)؛ بازنویسی پرسش فقط وقتی تاریخچه گفتگو وجود دارد اجرا میشود.
|
|
4. **استریم واقعی توکنبهتوکن** با `astream_events` بهجای دیف پیامهای کامل.
|
|
|
|
## خط لوله هر پیام
|
|
|
|
```
|
|
پرسش کاربر
|
|
│
|
|
├─[اگر تاریخچه هست] بازنویسی پرسش با LLM (حل ضمیرها و اشارهها: «اون دومی» → موضوع واقعی)
|
|
│
|
|
├─ بازیابی ترکیبی: pgvector (معنایی، کسینوسی روی HNSW — MiniLM چندزبانه)
|
|
│ + BM25 (کلیدواژه، متن نرمالشده فارسی)
|
|
│ نمرهها min-max → ترکیب α×معنایی + (1-α)×کلیدواژه
|
|
│
|
|
├─ برش خوشهای (K-Means K=2 + silhouette ≥ 0.55) — مرز طبیعی کیفیت
|
|
│
|
|
├─ ریرنک با BAAI/bge-reranker-v2-m3 → نگهداشتن ۳ سند برتر
|
|
│
|
|
└─ تولید پاسخ فارسی با LLM (قواعد تکیه بر داده + معرفی صفحه مرتبط + پیام جایگزین مشخص)
|
|
```
|
|
|
|
ایندکس از محتوای PostgreSQL سایت ساخته میشود (`articles` منتشرشده + پرسشوپاسخهای
|
|
پایان مقاله، `projects` با مشخصات کامل، `services`، `faqs`، `brand`، `offices`،
|
|
`investment_models`، `process_steps`، `why_us`، `team`، `testimonials`، `timeline`،
|
|
`story`، و متنهای مدیریت محتوا: `stats`، `hero`، `page_heroes`، `section_headings`،
|
|
`footer`، `before_after`). سطرهای جایگاهخالی (متن زیر ~۳۰ نویسه) ایندکس نمیشوند.
|
|
متنهای بلند به قطعات ~۷۰۰ نویسهای همپوشان برش میخورند.
|
|
|
|
- **ایندکس برداری در pgvector**: جدول `chatbot_chunks` در دیتابیس اختصاصی
|
|
`aramesh_chatbot` (روی همان نمونه Postgres) با ستون `embedding vector(384)` و
|
|
ایندکس HNSW کسینوسی؛ هر قطعه `doc_id` پایدار (شامل url: `type:url-key:occ:chunk`)
|
|
و `content_hash` دارد و همگامسازی فقط سطرهای جدید/تغییریافته را امبد میکند.
|
|
دیتابیس و جدول در شروع سرویس خودکار ساخته میشوند.
|
|
- **تیتر در متن قطعه**: در سندهای تکقطعهای که تیترشان در متن نیست (مثلاً نام پروژه)،
|
|
تیتر به ابتدای متن میچسبد تا BM25 و جستوجوی معنایی پیدایش کنند.
|
|
- **بازسازی خودکار**: یک حلقهٔ پسزمینه هر `INDEX_REFRESH_SECONDS` ثانیه محتوا را چک
|
|
میکند و اگر ادمین چیزی را در پنل عوض کرده باشد، فقط تغییرات همگام میشود
|
|
(امبد اجباری همه: `POST /chat/reindex`).
|
|
- **ماندگاری گفتگو**: در حافظهٔ همان پروسه (MemorySaver لنگگراف) — با ریاستارت پاک میشود.
|
|
- **گزارش گفتگوها**: هر پاسخ (بههمراه تعداد سند بازیابیشده، تشخیص «پاسخی نبود» و تأخیر)
|
|
در جدول `chat_logs` همان دیتابیس ذخیره میشود تا پرسشهای بیپاسخ — یعنی شکافهای
|
|
دانش — قابل کشف باشند؛ `GET /chat/stats` جمعبندی میدهد.
|
|
|
|
## متغیرهای محیطی (در `.env` ریشه پروژه)
|
|
|
|
| متغیر | پیشفرض | توضیح |
|
|
|---|---|---|
|
|
| `LLM_BASE_URL` | خالی | **اجباری** — نشانی سرویس LLM سازگار با OpenAI. بدون آن چتبات بالا میآید ولی پاسخ نمیدهد |
|
|
| `LLM_API_KEY` | خالی | کلید API (برای سرور داخلی `not-needed` بگذارید) |
|
|
| `LLM_MODEL` | `openai/gpt-oss-120b` | نام مدل |
|
|
| `CHAT_REINDEX_TOKEN` | خالی | توکن `POST /chat/reindex` — اگر خالی باشد آن مسیر ۴۰۳ میدهد |
|
|
| `CHATBOT_DB_NAME` | `aramesh_chatbot` | نام دیتابیس اختصاصی چتبات روی همان نمونه Postgres (بردارها اینجا میروند) |
|
|
| `EMBEDDING_DIM` | `384` | بعد بردار مدل امبدینگ — با تعویض مدل امبدینگ باید هماهنگ شود |
|
|
| `RERANKER_ENABLED` | `true` | روی سرور کمرم (زیر ~۴GB) `false` بگذارید |
|
|
| `ALPHA_DENSE` | `0.5` | وزن جستوجوی معنایی در ترکیب نمرهها (۰ تا ۱) |
|
|
| `BEST_OF_K` | `3` | تعداد سند نهایی که به LLM میرود |
|
|
| `INDEX_REFRESH_SECONDS` | `600` | بازسازی خودکار ایندکس پس از تغییر محتوا (۰ = خاموش) |
|
|
| `RATE_LIMIT_MESSAGES` / `RATE_LIMIT_WINDOW` | `20` / `300` | سقف پیام هر IP در پنجره زمانی (ثانیه) |
|
|
|
|
`DATABASE_URL` در داکر از `docker-compose.yml` تزریق میشود — دیتابیس سایت فقط خوانده
|
|
میشود و نوشتن (بردارها) فقط در دیتابیس جدا `aramesh_chatbot` انجام میگیرد.
|
|
|
|
## پیشنیاز pgvector
|
|
|
|
سرویس `db` استک از ایمیج `pgvector/pgvector:pg17` استفاده میکند (همان postgres:17 +
|
|
افزونهٔ vector). اگر Postgres سایت روی نمونهٔ دیگری است، باید افزونهٔ pgvector روی آن
|
|
فعال باشد؛ در غیر این صورت شروع چتبات با خطای واضح متوقف میشود.
|
|
|
|
## مسیرها
|
|
|
|
| مسیر | توضیح |
|
|
|---|---|
|
|
| `GET /chat/stream?message=…&thread_id=…` | استریم SSE توکنبهتوکن پاسخ (`{"content": …}` و در پایان `{"done": true, "fallback": true|false}`) |
|
|
| `GET /chat/suggestions` | ۴ پیشنهاد پرسش (کش ۱۰ دقیقه؛ جایگزین: خود سؤالات متداول) |
|
|
| `GET /chat/eval?message=…` | مخصوص ارزیابی — پاسخ + اسناد بازیابیشده با thread تازه |
|
|
| `GET /chat/stats` | آمار گفتگوها (کل، ۷ روز اخیر، نرخ پیام جایگزین)؛ فهرست پرسشهای بیپاسخ اخیر فقط با هدر `X-Reindex-Token` |
|
|
| `GET /chat/healthz` | وضعیت ایندکس/LLM/تعداد اسناد |
|
|
| `POST /chat/reindex` | بازسازی اجباری ایندکس (هدر `X-Reindex-Token`) |
|
|
|
|
مسیرهای `GET /chat/{scope}/stream|eval|suggestions` هم برای سازگاری با اسکریپت
|
|
`eval_ragas.py` قدیمی موجود است — کافی است `--url` را به نشانی این سرویس بدهید.
|
|
|
|
## ارزیابی
|
|
|
|
مجموعه طلایی `eval_set.json` (پرسشهای واقعی از محتوای دیتابیس + یک کنترل مثبت برای
|
|
«نباید قیمت بسازد») با `eval_run.py` اجرا میشود — داخل کانتینر:
|
|
|
|
```bash
|
|
docker exec aramesh-site-chatbot-1 python /srv/chatbot/eval_run.py --out /tmp/report.md
|
|
docker exec aramesh-site-chatbot-1 cat /tmp/report.md
|
|
```
|
|
|
|
خروجی: وضعیت هر پرسش (`pass` / `miss` / `manual` / `fallback-expected` / `unexpected-fallback`)،
|
|
منابع بازیابیشده و زمان پاسخ — برای سنجش اثر تغییرات دانش و کشف پسرفتها.
|
|
|
|
## اجرا
|
|
|
|
با داکر (بخشی از استک اصلی است — `docker compose up -d --build`):
|
|
|
|
```bash
|
|
# در .env ریشه:
|
|
LLM_BASE_URL=http://172.16.22.21:4460/v1 # یا هر سرویس سازگار با OpenAI
|
|
LLM_API_KEY=not-needed
|
|
```
|
|
|
|
اولین بوت مدلها را دانلود میکند (~۱.۵GB: امبدینگ + ریرنکر) و در والیوم
|
|
`chatbot_cache` کش میشود؛ بوتهای بعدی چند ثانیه طول میکشد.
|
|
|
|
اجرای محلی بدون داکر:
|
|
|
|
```bash
|
|
cd chatbot
|
|
python -m venv .venv && source .venv/bin/activate # ویندوز: .venv\Scripts\activate
|
|
pip install -r requirements.txt
|
|
export DATABASE_URL="postgresql+psycopg://aramesh:PASS@localhost:5432/arameshplaza"
|
|
export LLM_BASE_URL="http://…" LLM_MODEL="openai/gpt-oss-120b"
|
|
uvicorn chatbot.app:api --port 8000 # از ریشه پوشه والد
|
|
```
|
|
|
|
## منابع مورد نیاز
|
|
|
|
- **رم**: با ریرنکر ~۴GB (مدل ۵۶۸M پارامتری روی CPU)؛ بدون ریرنکر ~۲GB
|
|
- **CPU**: امبدینگ و ریرنک روی CPU اجرا میشوند؛ GPU لازم نیست
|
|
- **شبکه**: دسترسی به PostgreSQL و به نشانی `LLM_BASE_URL`
|
|
|
|
## نکات امنیتی
|
|
|
|
- دیتابیس سایت فقط **خواندن**؛ نوشتن فقط در دیتابیس اختصاصی `aramesh_chatbot` انجام میشود
|
|
- محدودسازی نرخ per-IP فعال است (پیشفرض ۲۰ پیام در ۵ دقیقه)
|
|
- CORS باز است ولی کوکی/اعتبارنامهای رد و بدل نمیشود؛ در تولید همهچیز هممبدأ (پشت Traefik) است
|