هر ربات مشاورهای که پیدا میکردم یک قیف بود: منو، فرم، دکمهٔ «همین حالا رزرو کن». تا وقتی بازدیدکننده لازم داشته مشکلش را با کلمات خودش توصیف کند کار میکند — و دقیقاً همان مرحلهای که منو نمیتواند انجامش دهد.
پس برعکسش را ساختم: رباتی که یک مصاحبهٔ بالینی واقعی را در قالب گفتگو اجرا میکند، فرد را غربال میکند، و بعد مشاورانی را که به وضعیتش میخورند رتبهبندی میکند — با موتور رتبهبندی داخل خود ربات، نه پشت یک API فروشنده.
انتخاب پلتفرم، و اینکه چرا سازگار با تلگرام است
ربات روی بل، یک پیامرسان ایرانی، اجرا میشود و با python-telegram-bot==22.8 روی https://tapi.bale.ai/bot پیاده شده. این عمدی است: بل یک HTTP API سازگار با تلگرام ارائه میدهد، پس کل اکوسیستم python-telegram-bot — هندلرها، ConversationHandler، فیلترها، زمانبند — بدون تغییر کار میکند. محصول یک ربات بل است؛ کتابخانه، کتابخانهٔ تلگرام.
این بیشتر از آنچه به نظر میرسد اهمیت دارد. ConversationHandler همان قطعهای است که مصاحبهٔ چندمرحلهای را ممکن میکند، و یک مؤلفهٔ بالغ و جاافتاده است نه چیزی که دستی ساخته شده باشد. یعنی کد هم قابل حمل است: همان هندلرها با تغییر base URL و توکن روی API تلگرام هم اجرا میشوند.
دوازده وضعیت، یک گفتگو
مصاحبه یک ConversationHandler با دوازده وضعیت است: نام، تلفن، جنسیت، سن، موضوع، سابقهٔ درمان، جزئیات سابقهٔ درمان، انتظار، جنسیت مشاور ترجیحی، شعبه، پیشنهاد GHQ-28، و خود سؤال GHQ-28. هر وضعیت هندلر و اعتبارسنجی خودش را دارد.
جزئیات طراحی که ارزش گفتن دارد مسیر لغو است. /cancel و پیامی که با شروع مجدد م��خواند هر دو گفتگو را تمیز قطع میکنند، و هندلر fallback کاربر را به شروع برمیگرداند — همان چیزی که وقتی خراب میشود رباتها را ناخوشایند میکند.
غربال، پیشنهادی و تفسیرشده سمت سرور
مرحلهٔ GHQ-28 همان ابزاری است که ویجت هم استفاده میکند: ۲۸ سؤال در چهار زیرمقیاس — جسمانی، اضطراب، اجتماعی و افسردگی — که سمت سرور نمرهگذاری و به بازه تفسیر میشود. هشدار اصلی در نمرهٔ کل ۴۳ به بالا یا زیرمقیاس افسردگی ۱۵ به بالا فعال میشود؛ هشدار ملایمتر از نمرهٔ کل ۲۲ شروع میشود.
کاری که ربات انجام نمیدهد، تشخیص دادن است. خروجی بهعنوان یک سیگنال غربال ارائه میشود و پیشنهاد جنبهٔ مشورتی دارد — انتخاب نهایی با خود کاربر است. این تفکیک در کد هست، نه فقط در متن بیانیهٔ بیانیه.
موتور تطبیق، و اینکه چطور یاد میگیرد
SpiralMatchEngine دو پاس دارد. پاس اول فیلترهای سخت است: جنسیت، شعبه، بازهٔ سنی. پاس دوم امتیازدهی وزنی روی تناسب موضوع، مفاهیم بالینی استخراجشده و تعداد نوبت خالی زنده. خروجی یک فهرست کوتاه است و هر مورد با خط «چرا این پیشنهاد» میآید — تناسب موضوع، گروه سنی، و نوبتی که فرد واقعاً میتواند رزرو کند.
استخراج مفهوم متن آزاد را با فرهنگ لغت مترادفِ پالایششده به یازده مفهوم بالینی — اضطراب، افسردگی، وسواس، روابط و تعارض، زوج و ازدواج، والد و فرزند، کودک، نوجوان و جوان، توسعهٔ فردی، حقوقی، و پزشکی-روانپزشکی — نگاشت میکند و تنوعهای فارسی و عربی را هم پوشش میدهد. تطبیق نام فازی است، پس املای نام مشاور توسط بازدیدکننده لازم نیست دقیق باشد — فاصلهگذاری، تفاوت حروف عربی/فارسی و لقبها همه مدیریت میشوند. بازهٔ سنی از رشتهٔ موضوع آزاد استخراج میشود، پس «برای کودک ۷ ساله» و «نوجوان» روی درستترین ردیفهای فهرست مینشینند.
حلقهٔ یادگیری همان چیزی است که یک تابع رتبهبندی را به محصول تبدیل میکند. هر توصیه و هر امتیازدهی ادمین از طریق /feedback بهعنوان سیگنال مثبت یا منفی در ai_knowledge.db نوشته میشود، و این سیگنالها وزنهای آینده را بازتنظیم میکنند. /learning وزنهای فعلی را چاپ میکند، پس آموزش قابل بازرسی است نه جعبهٔ سیاه.
نوبت خالی بدون اسکرپ دستی صفحه
یک خزندهٔ زمانبندیشده هر دو ساعت تعداد نوبتهای خالی را با خواندن صفحهٔ عمومی تیم مرکز بهروز میکند، با fallback پروکسی، منطق retry، و یک فایل lock که از اجراهای همزمان جلوگیری میکند. این تعدادها وارد رتبهبندی میشوند، پس موتور کسی را ترجیح میدهد که کاربر واقعاً میتواند رزرو کند. /crawl هم آن را دستی اجرا میکند.
پنل مدیریت داخل خود گفتگو
این بخشی است که از همه بیشتر دوستش دارم، چون یک اپ وب کامل را از استقرار حذف میکند. ادمین با /admin و رمز عبور وارد میشود و یک منو میگیرد: /stats برای آمار زنده، /export برای خروجی Excel دو شیتی از کاربران و تاریخچهٔ درخواستها، /upload برای جایگزینی فایل Excel مشاوران — که موتور را بدون restart داغ بارگذاری میکند —، /broadcast برای پیام به همهٔ کاربران با مرحلهٔ تأیید، /crawl برای اجرای فوری خزنده، /feedback و /learning برای آموزش و بازرسی موتور، /myid برای گرفتن شناسهٔ کاربری بل، و /logout برای پایان نشست.
چون پنل مدیریت خودش یک ربات است، کل footprint عملیاتی در یک پروسه، یک مجموعه فایل SQLite و یک مجموعه متغیر محیطی خلاصه میشود. دسترسی با ADMIN_USER_IDS — فهرستی جداشده با کاما از شناسههای کاربری بل — محدود میشود، بهعلاوهٔ ADMIN_PANEL_PASSWORD و BOT_TOKEN جدا. هیچ secretی در کد ذخیره نمیشود؛ هر سه از محیط خوانده میشوند.
ذخیرهسازی، صادقانه
شش جدول در سه پایگاه دادهٔ SQLite: users، user_requests، consultants و upload_history در پایگاه کاربران، appointments برای توفّر نوبتها، و concept_feedback برای وزنهای یادگیری. فهرست مشاوران از یک فایل spreadsheet بارگذاری و به JSON تبدیل میشود و هر زمان قابل بازسازی از Excel است.
قدم بعدی چه باشد
قدم بعدیِ صادقانه باز هم ارزیابی است و اینجا هم همینطور. موتور را میشود دربارهاش بحث کرد؛ چیزی که لازم دارد مجموعهای برچسبخورده از سناریوهای مصاحبه همراه با رتبهبندیای است که یک مشاور خوب میداد، تا تغییر وزنها اندازهگیری شود نه فرض. اضافهٔ مفید دیگر مسیر یادآوری برای کسانی است که غربال شدند و بعد ساکت شدند — دادهاش از قبل هست.
کل مستندات فنی بهصورت ویکی منتشر شده است: معماری، موتور تطبیق، غربال GHQ، پنل مدیریت، راهاندازی و استقرار، مشارکت در توسعه، و عیبیابی. سورس روی GitHub با مجوز MIT.
مرتبط: همین موتور بهشکل یک ویجت وب تعبیهشدنی با پنل مدیریت چندمستاجری کامل هم عرضه میشود — آن مقاله هم در همین سایت هست.