Files
aramesh-backend/chatbot/README.md
T

11 KiB

دستیار هوشمند سایت (چت‌بات)

سرویس گفتگوی محتوای سایت «گروه ساختمانی آرامش» — بازدیدکننده به فارسی سؤال می‌پرسد و دستیار فقط از روی محتوای خود سایت (مقاله‌ها، پروژه‌ها، خدمات، سؤالات متداول، اطلاعات تماس و…) پاسخ می‌دهد؛ اگر پاسخ در محتوا نباشد، به‌جای ساخت جواب، صادقانه اعلام می‌کند.

این سرویس پورت خط لولهٔ پروژهٔ 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
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 اجرا می‌شود — داخل کانتینر:

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):

# در .env ریشه:
LLM_BASE_URL=http://172.16.22.21:4460/v1   # یا هر سرویس سازگار با OpenAI
LLM_API_KEY=not-needed

اولین بوت مدل‌ها را دانلود می‌کند (~۱.۵GB: امبدینگ + ری‌رنکر) و در والیوم chatbot_cache کش می‌شود؛ بوت‌های بعدی چند ثانیه طول می‌کشد.

اجرای محلی بدون داکر:

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) است